@woosh/meep-engine 3.16.0 → 3.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/editor/particles/effect/ParticleEffectDocument.d.ts +21 -6
- package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -1
- package/editor/particles/effect/ParticleEffectDocument.js +38 -17
- package/editor/view/node-graph/NodeGraphClipboard.d.ts +186 -0
- package/editor/view/node-graph/NodeGraphClipboard.d.ts.map +1 -0
- package/editor/view/node-graph/NodeGraphClipboard.js +231 -0
- package/editor/view/node-graph/NodeGraphEditorView.d.ts +59 -1
- package/editor/view/node-graph/NodeGraphEditorView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeGraphEditorView.js +248 -5
- package/editor/view/node-graph/NodeGraphView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeGraphView.js +84 -8
- package/editor/view/node-graph/actions/connection_bind_actions.d.ts +23 -0
- package/editor/view/node-graph/actions/connection_bind_actions.d.ts.map +1 -0
- package/editor/view/node-graph/actions/connection_bind_actions.js +39 -0
- package/editor/view/particles/effect/ParticleCodeEditorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleCodeEditorView.js +4 -3
- package/editor/view/particles/effect/ParticleCurveEditorView.d.ts +20 -0
- package/editor/view/particles/effect/ParticleCurveEditorView.d.ts.map +1 -0
- package/editor/view/particles/effect/ParticleCurveEditorView.js +574 -0
- package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleEmitterInspectorView.js +27 -1
- package/editor/view/particles/effect/ParticleGradientEditorView.d.ts +24 -0
- package/editor/view/particles/effect/ParticleGradientEditorView.d.ts.map +1 -0
- package/editor/view/particles/effect/ParticleGradientEditorView.js +472 -0
- package/editor/view/particles/effect/ParticleGraphEditorView.d.ts +5 -0
- package/editor/view/particles/effect/ParticleGraphEditorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleGraphEditorView.js +61 -31
- package/editor/view/particles/effect/ParticleNodeParametersView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleNodeParametersView.js +33 -0
- package/editor/view/particles/effect/particle-editor.css +123 -35
- package/package.json +1 -1
- package/src/core/binary/allocator/OffsetAllocator.d.ts +25 -16
- package/src/core/binary/allocator/OffsetAllocator.d.ts.map +1 -1
- package/src/core/binary/allocator/OffsetAllocator.js +10 -1
- package/src/core/collection/array/typed/float32_array_hash.d.ts +21 -0
- package/src/core/collection/array/typed/float32_array_hash.d.ts.map +1 -0
- package/src/core/collection/array/typed/float32_array_hash.js +38 -0
- package/src/core/graph/csr/CSRGraph.d.ts +8 -1
- package/src/core/graph/csr/CSRGraph.d.ts.map +1 -1
- package/src/core/graph/csr/CSRGraph.js +14 -6
- package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts +24 -0
- package/src/core/model/node-graph/visual/layout/LayoutProblem.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/LayoutProblem.js +27 -0
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts +26 -0
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.js +28 -0
- package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts +92 -3
- package/src/core/model/node-graph/visual/layout/RouteGrid.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/RouteGrid.js +405 -242
- package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts +50 -5
- package/src/core/model/node-graph/visual/layout/RouteSearch.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/RouteSearch.js +160 -24
- package/src/core/model/node-graph/visual/layout/RouteSet.d.ts +7 -0
- package/src/core/model/node-graph/visual/layout/RouteSet.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/RouteSet.js +8 -0
- package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.js +9 -3
- package/src/core/model/node-graph/visual/layout/layout_build_problem.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/layout_build_problem.js +7 -0
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts +9 -3
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/layout_insert_lanes.js +17 -3
- package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts +9 -6
- package/src/core/model/node-graph/visual/layout/layout_node_graph.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/layout_node_graph.js +49 -7
- package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts +11 -1
- package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/layout_order_layers.js +135 -16
- package/src/core/model/node-graph/visual/layout/route_connections.d.ts +29 -0
- package/src/core/model/node-graph/visual/layout/route_connections.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/route_connections.js +716 -309
- package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts +3 -2
- package/src/core/model/node-graph/visual/layout/route_grid_build.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/route_grid_build.js +7 -15
- package/src/core/model/node-graph/visual/layout/route_nudge.d.ts +6 -0
- package/src/core/model/node-graph/visual/layout/route_nudge.d.ts.map +1 -1
- package/src/core/model/node-graph/visual/layout/route_nudge.js +42 -13
- package/src/engine/animation/AnimationUtils.js +246 -246
- package/src/engine/animation/Animations.js +74 -74
- package/src/engine/control/first-person/FirstPersonPlayerControllerSystem.js +2078 -2078
- package/src/engine/ecs/EventType.d.ts +0 -1
- package/src/engine/ecs/EventType.js +1 -26
- package/src/engine/ecs/ik/ik_write_local_rotation.d.ts +1 -1
- package/src/engine/ecs/ik/ik_write_local_rotation.d.ts.map +1 -1
- package/src/engine/ecs/ik/ik_write_local_rotation.js +3 -7
- package/src/engine/ecs/parent/EntityNode.d.ts.map +1 -1
- package/src/engine/ecs/parent/EntityNode.js +4 -9
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +360 -364
- package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.d.ts +41 -0
- package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.d.ts.map +1 -0
- package/src/engine/ecs/transform/TRANSFORM64_EVENT_CHANGE.js +40 -0
- package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +8 -0
- package/src/engine/ecs/transform/t64_announce_change.d.ts +10 -7
- package/src/engine/ecs/transform/t64_announce_change.d.ts.map +1 -1
- package/src/engine/ecs/transform/t64_announce_change.js +12 -12
- package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.d.ts +30 -0
- package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.d.ts.map +1 -0
- package/src/engine/ecs/transform-attachment/TRANSFORM_ATTACHMENT_EVENT_CHANGE.js +29 -0
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.d.ts.map +1 -1
- package/src/engine/ecs/transform-attachment/TransformAttachmentSystem.js +13 -21
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
- package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +4 -8
- package/src/engine/graphics3/LightSystem.d.ts.map +1 -1
- package/src/engine/graphics3/LightSystem.js +4 -8
- package/src/engine/graphics3/MeshSystem.d.ts.map +1 -1
- package/src/engine/graphics3/MeshSystem.js +9 -19
- package/src/engine/graphics3/ParticleEmitterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/ParticleEmitterSystem.js +4 -8
- package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -1
- package/src/engine/graphics3/ShadedGeometrySystem.js +4 -10
- package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts +1 -1
- package/src/engine/grid/transform2grid/Transform2GridPositionSystem.d.ts.map +1 -1
- package/src/engine/grid/transform2grid/Transform2GridPositionSystem.js +4 -8
- package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.js +218 -218
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +2 -5
- package/src/engine/sound/ecs/SoundListenerSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/SoundListenerSystem.js +6 -8
- package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts +0 -4
- package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts.map +1 -1
- package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.js +5 -11
- package/src/shade/playground/particle_editor/README.md +43 -2
- package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_prototype.js +4 -5
- package/src/shade/playground/particle_system/particle_scene.js +4 -4
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +13 -1
- package/src/shade/renderer/particles/DESIGN.md +104 -19
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +64 -50
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +176 -78
- package/src/shade/renderer/particles/ParticleConstants.d.ts +59 -23
- package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
- package/src/shade/renderer/particles/ParticleConstants.js +66 -23
- package/src/shade/renderer/particles/ParticleRenderMode.d.ts +19 -0
- package/src/shade/renderer/particles/ParticleRenderMode.d.ts.map +1 -0
- package/src/shade/renderer/particles/ParticleRenderMode.js +33 -0
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +167 -166
- package/src/shade/renderer/particles/data/particle_emitter_record.d.ts +37 -0
- package/src/shade/renderer/particles/data/particle_emitter_record.d.ts.map +1 -0
- package/src/shade/renderer/particles/data/particle_emitter_record.js +109 -0
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.d.ts +3 -3
- package/src/shade/renderer/particles/graph/ParticleGroupBuilder.js +303 -303
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts +18 -0
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeDescription.js +152 -133
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts +0 -11
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/ParticleNodeRegistry.js +720 -621
- package/src/shade/renderer/particles/graph/compile_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/compile_particle_graph.js +8 -3
- package/src/shade/renderer/particles/graph/flatten_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/flatten_particle_graph.js +244 -213
- package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.js +6 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.js +294 -319
- package/src/shade/renderer/particles/graph/groups/particle_groups_noise.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/particle_groups_noise.js +524 -517
- package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.d.ts +22 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/groups/particle_groups_psrdnoise.js +517 -0
- package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/groups/simplex_reference.js +185 -159
- package/src/shade/renderer/particles/graph/make_particle_group.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/make_particle_group.js +2 -20
- package/src/shade/renderer/particles/graph/particle_compare_ops.d.ts +17 -0
- package/src/shade/renderer/particles/graph/particle_compare_ops.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_compare_ops.js +22 -0
- package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts +28 -8
- package/src/shade/renderer/particles/graph/particle_graph_diagnostic.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_graph_diagnostic.js +49 -25
- package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts +40 -0
- package/src/shade/renderer/particles/graph/particle_node_parameters.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_node_parameters.js +312 -234
- package/src/shade/renderer/particles/graph/particle_node_presentation.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/particle_node_presentation.js +59 -3
- package/src/shade/renderer/particles/graph/particle_ramp.d.ts +230 -0
- package/src/shade/renderer/particles/graph/particle_ramp.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph/particle_ramp.js +403 -0
- package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts +7 -1
- package/src/shade/renderer/particles/graph/validate_particle_graph.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph/validate_particle_graph.js +66 -10
- package/src/shade/renderer/particles/graph_particles.d.ts +23 -69
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +105 -262
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles_avboit.js +3 -2
- package/src/shade/renderer/particles/graph_particles_billboard.d.ts +37 -0
- package/src/shade/renderer/particles/graph_particles_billboard.d.ts.map +1 -0
- package/src/shade/renderer/particles/graph_particles_billboard.js +93 -0
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts +51 -22
- package/src/shade/renderer/particles/isa/InstructionStream.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/InstructionStream.js +98 -38
- package/src/shade/renderer/particles/isa/ParticleProgram.d.ts +49 -8
- package/src/shade/renderer/particles/isa/ParticleProgram.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleProgram.js +95 -150
- package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts +306 -64
- package/src/shade/renderer/particles/isa/ParticleVMISA.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/ParticleVMISA.js +505 -136
- package/src/shade/renderer/particles/isa/particle_assembly.d.ts +36 -12
- package/src/shade/renderer/particles/isa/particle_assembly.d.ts.map +1 -1
- package/src/shade/renderer/particles/isa/particle_assembly.js +354 -139
- package/src/shade/renderer/particles/isa/validate_particle_program.d.ts +9 -0
- package/src/shade/renderer/particles/isa/validate_particle_program.d.ts.map +1 -0
- package/src/shade/renderer/particles/isa/validate_particle_program.js +128 -0
- package/src/shade/renderer/particles/layout/ParticleLayout.d.ts +23 -0
- package/src/shade/renderer/particles/layout/ParticleLayout.d.ts.map +1 -1
- package/src/shade/renderer/particles/layout/ParticleLayout.js +68 -0
- package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.d.ts +77 -0
- package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/optimize_particle_program_vector.js +219 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.d.ts +83 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.js +283 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.d.ts +133 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_graph.js +334 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.d.ts +117 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_groups.js +70 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.d.ts +97 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lift.js +462 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.d.ts +116 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_lower.js +687 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.d.ts +57 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_pack.js +860 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.d.ts +115 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/dag/particle_vector_rules.js +264 -0
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts +81 -42
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/optimize_particle_program.js +477 -478
- package/src/shade/renderer/particles/optimizer/particle_integer_bounds.d.ts +100 -0
- package/src/shade/renderer/particles/optimizer/particle_integer_bounds.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/particle_integer_bounds.js +236 -0
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts +139 -59
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_analysis.js +764 -647
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts +40 -7
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_equivalence.js +376 -213
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts +30 -3
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_program_fuzz.js +337 -223
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts +24 -38
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +166 -206
- package/src/shade/renderer/particles/optimizer/pass_allocate_registers.d.ts +99 -0
- package/src/shade/renderer/particles/optimizer/pass_allocate_registers.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_allocate_registers.js +313 -0
- package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts +3 -2
- package/src/shade/renderer/particles/optimizer/pass_dedup_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_dedup_constants.js +198 -197
- package/src/shade/renderer/particles/optimizer/pass_eliminate_dead_code.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts +19 -0
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +678 -561
- package/src/shade/renderer/particles/optimizer/pass_inline_constants.d.ts +22 -0
- package/src/shade/renderer/particles/optimizer/pass_inline_constants.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_inline_constants.js +221 -0
- package/src/shade/renderer/particles/optimizer/pass_narrow_widths.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_narrow_widths.js +63 -72
- package/src/shade/renderer/particles/optimizer/pass_pack_registers.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_pack_registers.js +87 -86
- package/src/shade/renderer/particles/optimizer/pass_peephole.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_peephole.js +392 -271
- package/src/shade/renderer/particles/optimizer/pass_propagate_copies.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_propagate_copies.js +361 -268
- package/src/shade/renderer/particles/optimizer/pass_schedule.d.ts +4 -0
- package/src/shade/renderer/particles/optimizer/pass_schedule.d.ts.map +1 -0
- package/src/shade/renderer/particles/optimizer/pass_schedule.js +244 -0
- package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.d.ts.map +1 -1
- package/src/shade/renderer/particles/optimizer/pass_share_subexpressions.js +154 -151
- package/src/shade/renderer/particles/particle_test_fixtures.d.ts +20 -0
- package/src/shade/renderer/particles/particle_test_fixtures.d.ts.map +1 -1
- package/src/shade/renderer/particles/particle_test_fixtures.js +229 -203
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +99 -25
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/EmitterRegistry.js +438 -286
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts +109 -0
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts.map +1 -0
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.js +127 -0
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +87 -102
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ParticleEmitter.js +184 -163
- package/src/shade/renderer/particles/runtime/ProgramArena.d.ts +26 -0
- package/src/shade/renderer/particles/runtime/ProgramArena.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ProgramHeap.d.ts +130 -0
- package/src/shade/renderer/particles/runtime/ProgramHeap.d.ts.map +1 -0
- package/src/shade/renderer/particles/runtime/ProgramHeap.js +595 -0
- package/src/shade/renderer/particles/runtime/ProgramPlacement.d.ts +91 -0
- package/src/shade/renderer/particles/runtime/ProgramPlacement.d.ts.map +1 -0
- package/src/shade/renderer/particles/runtime/ProgramPlacement.js +106 -0
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts +5 -2
- package/src/shade/renderer/particles/runtime/create_particle_effect.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/create_particle_effect.js +22 -19
- package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.d.ts +28 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emit_step.js +136 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.d.ts +35 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_simulate_step.js +133 -0
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts +18 -9
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.js +20 -80
- package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.d.ts +33 -0
- package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_emit_wide.js +114 -0
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts +8 -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 +33 -101
- package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.d.ts +45 -0
- package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_simulate_wide.js +123 -0
- package/src/shade/renderer/particles/sort/graph_particle_sort.d.ts +51 -0
- package/src/shade/renderer/particles/sort/graph_particle_sort.d.ts.map +1 -0
- package/src/shade/renderer/particles/sort/graph_particle_sort.js +120 -0
- package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.d.ts +6 -0
- package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/particles/vm/PARTICLE_VM_CONTEXT_STRUCT.js +16 -0
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +4 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/ParticleVMReference.js +261 -140
- package/src/shade/renderer/particles/vm/chunk_particle_vm.d.ts.map +1 -1
- package/src/shade/renderer/particles/vm/chunk_particle_vm.js +217 -255
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.d.ts +43 -0
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.d.ts.map +1 -0
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_fast.js +55 -0
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.d.ts +62 -0
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.d.ts.map +1 -0
- package/src/shade/renderer/particles/vm/chunk_particle_vm_registers_wide.js +86 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.d.ts +22 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v3f.js +32 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.d.ts +9 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/math/chunk_mod_v4f.js +19 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/README.md +65 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.d.ts +24 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2.js +83 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.d.ts +25 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_hash.js +42 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.d.ts +31 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise2_kernel.js +80 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.d.ts +26 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3.js +109 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.d.ts +38 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_kernel.js +129 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.d.ts +21 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_permute.js +35 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.d.ts +19 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_skew.js +28 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_psrdnoise3_unskew.js +22 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.d.ts +22 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise2.js +59 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.d.ts +23 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/noise/psrdnoise/chunk_srdnoise3.js +74 -0
- package/src/shade/wgsl/emulator/ComputeShaderEmulator.d.ts +34 -1
- package/src/shade/wgsl/emulator/ComputeShaderEmulator.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/ComputeShaderEmulator.js +41 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts +3 -2
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +49 -10
- package/src/view/minimap/dom/MinimapCameraView.d.ts.map +1 -1
- package/src/view/minimap/dom/MinimapCameraView.js +4 -9
- package/src/core/model/node-graph/visual/layout/BoxLayoutSpec.d.ts +0 -15
- package/src/core/model/node-graph/visual/layout/BoxLayoutSpec.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/ConnectedBoxLayouter.d.ts +0 -45
- package/src/core/model/node-graph/visual/layout/ConnectedBoxLayouter.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/ConnectionEndpointLayoutSpec.d.ts +0 -24
- package/src/core/model/node-graph/visual/layout/ConnectionEndpointLayoutSpec.d.ts.map +0 -1
- package/src/core/model/node-graph/visual/layout/ConnectionLayoutSpec.d.ts +0 -19
- package/src/core/model/node-graph/visual/layout/ConnectionLayoutSpec.d.ts.map +0 -1
- package/src/engine/ecs/transform/__probe.d.ts +0 -4
- package/src/engine/ecs/transform/__probe.d.ts.map +0 -1
- package/src/engine/ecs/util/hideEntityGracefully.d.ts +0 -9
- package/src/engine/ecs/util/hideEntityGracefully.d.ts.map +0 -1
- package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts +0 -27
- package/src/shade/renderer/buffer/table/gpu_database_emulator_image.d.ts.map +0 -1
- package/src/shade/renderer/particles/cull/chunk_particle_cull.d.ts +0 -14
- package/src/shade/renderer/particles/cull/chunk_particle_cull.d.ts.map +0 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_LAYOUT.d.ts +0 -156
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_LAYOUT.d.ts.map +0 -1
- package/src/shade/renderer/particles/runtime/Emitter.d.ts +0 -219
- package/src/shade/renderer/particles/runtime/Emitter.d.ts.map +0 -1
- package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts +0 -77
- package/src/shade/renderer/particles/runtime/ParticleSpawnScheduler.d.ts.map +0 -1
- package/src/shade/renderer/particles/runtime/ProgramArena.js +0 -155
- package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts +0 -15
- package/src/shade/renderer/particles/runtime/emitter_database_image.d.ts.map +0 -1
- package/src/shade/renderer/particles/runtime/spawn_request_encoding.d.ts +0 -34
- package/src/shade/renderer/particles/runtime/spawn_request_encoding.d.ts.map +0 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts +0 -32
- package/src/shade/renderer/particles/shaders/chunk_particle_context.d.ts.map +0 -1
|
@@ -1,621 +1,720 @@
|
|
|
1
|
-
import { NodeRegistry } from "../../../../core/model/node-graph/node/NodeRegistry.js";
|
|
2
|
-
import { PortDirection } from "../../../../core/model/node-graph/node/PortDirection.js";
|
|
3
|
-
import { DataType } from "../../../../core/model/node-graph/type/DataType.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
*
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
*
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* @
|
|
97
|
-
*/
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
description.
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (
|
|
141
|
-
description.
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
* @param {
|
|
202
|
-
* @returns {
|
|
203
|
-
*/
|
|
204
|
-
function
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
*
|
|
216
|
-
* @
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
*
|
|
225
|
-
* the
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Lane names, in lane order: the input ports of the vector constructors, and how a swizzle is
|
|
314
|
-
* spelled.
|
|
315
|
-
* @type {string[]}
|
|
316
|
-
*/
|
|
317
|
-
const LANE_NAMES = ["x", "y", "z", "w"];
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* @param {object} parameters
|
|
321
|
-
* @returns {number[]} a literal node's value
|
|
322
|
-
*/
|
|
323
|
-
function literal_of(parameters) {
|
|
324
|
-
return Array.isArray(parameters.value) ? parameters.value : [];
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Lower a literal into the constant pool, at the lanes it is being read at.
|
|
329
|
-
*
|
|
330
|
-
* Only those lanes: a scalar literal costs one word of the pool — or, read wider, that word
|
|
331
|
-
* repeated, which is the broadcast for free — and a wide literal read narrow interns only its
|
|
332
|
-
* leading words. A lane the value does not have reads zero, which is what a `color` written with
|
|
333
|
-
* too few numbers gets; the validator reports the parameter, and the compiler does not also throw.
|
|
334
|
-
*
|
|
335
|
-
* @param {InstructionStream} s
|
|
336
|
-
* @param {object} c the emit context
|
|
337
|
-
*/
|
|
338
|
-
function emit_literal(s, c) {
|
|
339
|
-
const v = literal_of(c.parameters);
|
|
340
|
-
|
|
341
|
-
const components = [];
|
|
342
|
-
|
|
343
|
-
for (let i = 0; i < c.width; i++) {
|
|
344
|
-
components.push((c.type.is_scalar ? v[0] : v[i]) ?? 0);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
s.loadConst(c.out, c.asm.constant(...components), c.width);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* @param {object} parameters
|
|
352
|
-
* @returns {number[]} a swizzle's lane selectors, one per output lane
|
|
353
|
-
*/
|
|
354
|
-
function pattern_of(parameters) {
|
|
355
|
-
const pattern = parameters.pattern;
|
|
356
|
-
|
|
357
|
-
return Array.isArray(pattern) && pattern.length > 0 ? pattern : [0, 1, 2, 3];
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* @param {number[]} pattern
|
|
362
|
-
* @returns {number} how many lanes of a source the selectors reach into
|
|
363
|
-
*/
|
|
364
|
-
function pattern_span(pattern) {
|
|
365
|
-
return 1 + Math.max(...pattern);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* The attribute a node names, as a type; `null` where there is no layout to look in or the name is
|
|
370
|
-
* not in it — which the validator reports in its own words.
|
|
371
|
-
*
|
|
372
|
-
* An attribute the layout has tagged (`color`, say) is that type rather than the plain shape, which
|
|
373
|
-
* is how a colour reaches a graph without a node saying so. The tag has to be the shape the
|
|
374
|
-
* attribute actually is: three words tagged `color` is a mistake, and reading them as a four-lane
|
|
375
|
-
* colour would be a second one, so the shape wins and the tag is ignored.
|
|
376
|
-
*
|
|
377
|
-
* @param {object} parameters
|
|
378
|
-
* @param {ParticleLayout|undefined} layout
|
|
379
|
-
* @returns {ParticleValueType|null}
|
|
380
|
-
*/
|
|
381
|
-
function attribute_type(parameters, layout) {
|
|
382
|
-
if (layout === undefined || !layout.has(parameters.name)) {
|
|
383
|
-
return null;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
const attribute = layout.get(parameters.name);
|
|
387
|
-
|
|
388
|
-
const semantic = particle_semantic_type(attribute.type);
|
|
389
|
-
|
|
390
|
-
if (semantic !== null && semantic.components === attribute.components) {
|
|
391
|
-
return semantic;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return particle_value_type(attribute.components);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Which of a binary op's operands is a scalar meeting a vector —
|
|
399
|
-
* broadcast — or `null` where neither or both are.
|
|
400
|
-
*
|
|
401
|
-
* @param {Object<string, ParticleValueType>} source_types
|
|
402
|
-
* @returns {string|null} `"a"`, `"b"` or `null`
|
|
403
|
-
*/
|
|
404
|
-
function scalar_operand(source_types) {
|
|
405
|
-
const a = source_types.a.is_scalar;
|
|
406
|
-
const b = source_types.b.is_scalar;
|
|
407
|
-
|
|
408
|
-
if (a === b) {
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return a ? "a" : "b";
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// --- sources ---
|
|
416
|
-
// A source has a type of its own, and it is as wide as that: a literal is as wide as it was
|
|
417
|
-
// written, a builtin as wide as the host fills it, an attribute as wide as the layout says. A
|
|
418
|
-
// consumer wanting more lanes than that is a conversion the compiler makes after the value is
|
|
419
|
-
// produced — except where a source can fill lanes for free, which it then does.
|
|
420
|
-
source("const", parameters => particle_value_type_or_null(literal_of(parameters).length), emit_literal, {
|
|
421
|
-
operation_width: ({ type, demand }) => type.is_scalar ? demand : Math.min(type.components, demand),
|
|
422
|
-
});
|
|
423
|
-
source(
|
|
424
|
-
"builtin",
|
|
425
|
-
parameters => particle_value_type_or_null(VM_BUILTIN_COMPONENTS[parameters.id]),
|
|
426
|
-
(s, c) => s.loadBuiltin(c.out, c.parameters.id, c.width)
|
|
427
|
-
);
|
|
428
|
-
source("attribute", attribute_type, (s, c) => {
|
|
429
|
-
const a = c.layout.get(c.parameters.name);
|
|
430
|
-
|
|
431
|
-
s.loadAttr(c.out, a.offset, a.components, c.width);
|
|
432
|
-
});
|
|
433
|
-
// One uniform draw in `[0, 1)` per lane, as many lanes as the node says. Shaped distributions are
|
|
434
|
-
// compositions over it rather than modes of it — see ./particle_graph_distributions.js.
|
|
435
|
-
source("random", parameters => particle_value_type_or_null(parameters.components), (s, c) => s.random(c.out, c.width));
|
|
436
|
-
|
|
437
|
-
// --- sinks ---
|
|
438
|
-
// A store is exactly as wide as the attribute it writes, and that is how it reads its input.
|
|
439
|
-
sink("setAttribute", ["value"], (s, c) => {
|
|
440
|
-
const a = c.layout.get(c.parameters.name);
|
|
441
|
-
|
|
442
|
-
s.storeAttr(c.in("value"), a.offset, a.components);
|
|
443
|
-
}, {
|
|
444
|
-
infer_types: ({ parameters, layout }) => ({ output: null, inputs: { value: attribute_type(parameters, layout) } }),
|
|
445
|
-
operation_width: ({ parameters, layout }) => layout.get(parameters.name).components,
|
|
446
|
-
});
|
|
447
|
-
sink("kill", ["condition"], (s, c) => s.kill(c.in("condition")), {
|
|
448
|
-
infer_types: () => ({ output: null, inputs: { condition: PARTICLE_TYPE_F32 } }),
|
|
449
|
-
operation_width: () => 1,
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
// --- arithmetic (component-wise) ---
|
|
453
|
-
binary("add", VM_OP.ADD);
|
|
454
|
-
binary("sub", VM_OP.SUB);
|
|
455
|
-
// A vector times a scalar
|
|
456
|
-
//
|
|
457
|
-
define("mul", ["a", "b"], (s, c) => {
|
|
458
|
-
const scalar = scalar_operand(c.source_types);
|
|
459
|
-
|
|
460
|
-
if (scalar === null) {
|
|
461
|
-
s.emit(VM_OP.MUL, c.out, c.in("a"), c.in("b"), 0, c.width);
|
|
462
|
-
} else {
|
|
463
|
-
s.emit(VM_OP.
|
|
464
|
-
}
|
|
465
|
-
}, {
|
|
466
|
-
input_widths: ({ source_types }) => {
|
|
467
|
-
const scalar = scalar_operand(source_types);
|
|
468
|
-
|
|
469
|
-
return scalar === null ? {} : { [scalar]: 1 };
|
|
470
|
-
},
|
|
471
|
-
});
|
|
472
|
-
binary("div", VM_OP.DIV);
|
|
473
|
-
binary("min", VM_OP.MIN);
|
|
474
|
-
binary("max", VM_OP.MAX);
|
|
475
|
-
binary("pow", VM_OP.POW);
|
|
476
|
-
binary("step", VM_OP.STEP, MASK_RESULT);
|
|
477
|
-
define("mad", ["a", "b", "c"], (s, c) => s.emit(VM_OP.MAD, c.out, c.in("a"), c.in("b"), c.in("c"), c.width));
|
|
478
|
-
define("mix", ["a", "b", "t"], (s, c) => s.emit(VM_OP.MIX, c.out, c.in("a"), c.in("b"), c.in("t"), c.width), blend_controlled_by("t"));
|
|
479
|
-
define("clamp", ["x", "lo", "hi"], (s, c) => s.emit(VM_OP.CLAMP, c.out, c.in("x"), c.in("lo"), c.in("hi"), c.width));
|
|
480
|
-
define("smoothstep", ["e0", "e1", "x"], (s, c) => s.emit(VM_OP.SMOOTHSTEP, c.out, c.in("e0"), c.in("e1"), c.in("x"), c.width), MASK_RESULT);
|
|
481
|
-
// The scalar is one lane; the vector is however wide the result is.
|
|
482
|
-
define("scale", ["v", "s"], (s, c) => s.emit(VM_OP.
|
|
483
|
-
infer_types: ({ inputs }) => ({ output: inputs.v, inputs: { v: inputs.v, s: PARTICLE_TYPE_F32 } }),
|
|
484
|
-
input_widths: () => ({ s: 1 }),
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
// --- unary math ---
|
|
488
|
-
unary("neg", VM_OP.NEG);
|
|
489
|
-
unary("abs", VM_OP.ABS);
|
|
490
|
-
unary("floor", VM_OP.FLOOR);
|
|
491
|
-
unary("fract", VM_OP.FRACT);
|
|
492
|
-
unary("sqrt", VM_OP.SQRT);
|
|
493
|
-
unary("sin", VM_OP.SIN);
|
|
494
|
-
unary("cos", VM_OP.COS);
|
|
495
|
-
unary("exp", VM_OP.EXP);
|
|
496
|
-
unary("log", VM_OP.LOG);
|
|
497
|
-
unary("normalize", VM_OP.NORMALIZE, WHOLE_VECTOR);
|
|
498
|
-
unary("length", VM_OP.LENGTH, REDUCTION);
|
|
499
|
-
|
|
500
|
-
// --- vector ---
|
|
501
|
-
binary("dot", VM_OP.DOT, REDUCTION);
|
|
502
|
-
// A cross product is three lanes in and three out, whatever arrives.
|
|
503
|
-
binary("cross", VM_OP.CROSS, {
|
|
504
|
-
infer_types: ({ inputs }) => ({ output: PARTICLE_TYPE_VEC3, inputs: read_all_as(inputs, PARTICLE_TYPE_VEC3) }),
|
|
505
|
-
operation_width: () => 3,
|
|
506
|
-
});
|
|
507
|
-
binary("distance", VM_OP.DISTANCE, REDUCTION);
|
|
508
|
-
// A swizzle is as wide as its pattern, and its source has to cover the lanes the pattern names —
|
|
509
|
-
// which is unrelated to how wide the result is, and is the one place a source's extent is read out
|
|
510
|
-
// of a parameter rather than a width.
|
|
511
|
-
define("swizzle", ["x"], (s, c) => {
|
|
512
|
-
const p = pattern_of(c.parameters);
|
|
513
|
-
|
|
514
|
-
s.emit(VM_OP.
|
|
515
|
-
}, {
|
|
516
|
-
infer_types: ({ inputs, parameters }) => {
|
|
517
|
-
const p = pattern_of(parameters);
|
|
518
|
-
const output = particle_value_type_or_null(p.length);
|
|
519
|
-
|
|
520
|
-
if (output === null) {
|
|
521
|
-
return { output: null, inputs: { x: null } };
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
const span = pattern_span(p);
|
|
525
|
-
const source = inputs.x;
|
|
526
|
-
|
|
527
|
-
// A source that covers the lanes named is read as itself: picking `.x` out of a vec4 is not
|
|
528
|
-
// a narrowing. One that does not is read as wide as the pattern reaches, and the lanes it
|
|
529
|
-
// has not got read zero — a warning, since `.z` of a vec2 is more likely a slip than a plan.
|
|
530
|
-
const x = source !== null && source.components >= span ? source : particle_value_type_or_null(span);
|
|
531
|
-
|
|
532
|
-
return { output, inputs: { x } };
|
|
533
|
-
},
|
|
534
|
-
input_widths: ({ operation_width, parameters }) => ({
|
|
535
|
-
x: pattern_span(pattern_of(parameters).slice(0, operation_width)),
|
|
536
|
-
}),
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
// The vector constructors: a value per lane, from scalars. Lowered as one `MOV` per lane into the
|
|
540
|
-
// result's run, which is the only way a lane gets written on its own — every other opcode writes a
|
|
541
|
-
// run from its start.
|
|
542
|
-
for (const components of [2, 3, 4]) {
|
|
543
|
-
const inputs = LANE_NAMES.slice(0, components);
|
|
544
|
-
const output = particle_value_type(components);
|
|
545
|
-
|
|
546
|
-
define(`vec${components}`, inputs, (s, c) => {
|
|
547
|
-
const moves = inputs.slice(0, c.width).map((port, lane) => ({ from: c.in(port), to: c.out + lane }));
|
|
548
|
-
|
|
549
|
-
// The allocator lets one source sit where the result starts, and nowhere else inside it. A
|
|
550
|
-
// move into lane 0 would overwrite that source before a later move could read it into its
|
|
551
|
-
// own lane — so it goes first, and the rest are then free to land in any order.
|
|
552
|
-
moves.sort((p, q) => Number(q.from === c.out) - Number(p.from === c.out));
|
|
553
|
-
|
|
554
|
-
for (const { from, to } of moves) {
|
|
555
|
-
if (from !== to) {
|
|
556
|
-
s.mov(to, from, 1);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}, {
|
|
560
|
-
infer_types: () => ({ output, inputs: read_all_as(Object.fromEntries(inputs.map(port => [port, null])), PARTICLE_TYPE_F32) }),
|
|
561
|
-
input_widths: () => Object.fromEntries(inputs.map(port => [port, 1])),
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// --- logic ---
|
|
566
|
-
define("compare", ["a", "b"], (s, c) => {
|
|
567
|
-
const opcode = PARTICLE_COMPARE_OPCODES[c.parameters.op];
|
|
568
|
-
if (opcode === undefined) {
|
|
569
|
-
throw new Error(`Unknown compare op '${c.parameters.op}'`);
|
|
570
|
-
}
|
|
571
|
-
s.emit(opcode, c.out, c.in("a"), c.in("b"), 0, c.width);
|
|
572
|
-
}, MASK_RESULT);
|
|
573
|
-
define("select", ["a", "b", "condition"], (s, c) => s.emit(VM_OP.SELECT, c.out, c.in("a"), c.in("b"), c.in("condition"), c.width), blend_controlled_by("condition"));
|
|
574
|
-
|
|
575
|
-
// --- curves ---
|
|
576
|
-
// One lane of `t` in, one value out — which the opcode writes across every lane it is asked for, so
|
|
577
|
-
// a curve read wider than a scalar is the broadcast for free.
|
|
578
|
-
define("curve", ["t"], (s, c) => s.curve(c.out, c.parameters.handle, c.in("t"), c.width), {
|
|
579
|
-
infer_types: () => ({ output: PARTICLE_TYPE_F32, inputs: { t: PARTICLE_TYPE_F32 } }),
|
|
580
|
-
operation_width: ({ demand }) => demand,
|
|
581
|
-
input_widths: () => ({ t: 1 }),
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
// --- semantic sources ---
|
|
585
|
-
// Appended rather than filed with the other sources, because a node type's id is its position in
|
|
586
|
-
// this file and the ids are a wire format: inserting one up there renumbers every type below it.
|
|
587
|
-
// Where an author finds it is the palette's business, and the palette lists it among the sources.
|
|
588
|
-
|
|
589
|
-
// A literal colour. A `const` of four numbers in every respect the compiler sees — it interns the
|
|
590
|
-
// same pool words and loads them with the same instruction — and a colour to everything above:
|
|
591
|
-
// a swatch to edit rather than four fields, and its own colour along the wire to wherever it is
|
|
592
|
-
// written.
|
|
593
|
-
source("color", () => PARTICLE_TYPE_COLOR, emit_literal);
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
1
|
+
import { NodeRegistry } from "../../../../core/model/node-graph/node/NodeRegistry.js";
|
|
2
|
+
import { PortDirection } from "../../../../core/model/node-graph/node/PortDirection.js";
|
|
3
|
+
import { DataType } from "../../../../core/model/node-graph/type/DataType.js";
|
|
4
|
+
import {
|
|
5
|
+
pack_swizzle,
|
|
6
|
+
VM_BUILTIN_COMPONENTS,
|
|
7
|
+
VM_OP,
|
|
8
|
+
VM_SWIZZLE_BROADCAST,
|
|
9
|
+
vm_immediate,
|
|
10
|
+
vm_register,
|
|
11
|
+
} from "../isa/ParticleVMISA.js";
|
|
12
|
+
import { PARTICLE_COMPARE_OPCODES } from "./particle_compare_ops.js";
|
|
13
|
+
import { ParticleGroupBuilder } from "./ParticleGroupBuilder.js";
|
|
14
|
+
import { particle_node_presentation } from "./particle_node_presentation.js";
|
|
15
|
+
import { ParticleNodeDescription } from "./ParticleNodeDescription.js";
|
|
16
|
+
import {
|
|
17
|
+
PARTICLE_GRADIENT_COLOR_COMPONENTS,
|
|
18
|
+
roll_out_particle_curve,
|
|
19
|
+
roll_out_particle_gradient,
|
|
20
|
+
} from "./particle_ramp.js";
|
|
21
|
+
import {
|
|
22
|
+
PARTICLE_ALL_VALUE_TYPES,
|
|
23
|
+
PARTICLE_TYPE_COLOR,
|
|
24
|
+
PARTICLE_TYPE_F32,
|
|
25
|
+
PARTICLE_TYPE_VEC3,
|
|
26
|
+
particle_semantic_type,
|
|
27
|
+
particle_value_type,
|
|
28
|
+
particle_value_type_or_null,
|
|
29
|
+
particle_widest_type,
|
|
30
|
+
} from "./ParticleValueType.js";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Standard particle-node library, expressed as {@link ParticleNodeDescription}s in a
|
|
34
|
+
* {@link NodeRegistry}. Nodes are value-producing operations (constants, builtins, attribute reads,
|
|
35
|
+
* math, curves, random) or sinks (attribute writes, kill).
|
|
36
|
+
*
|
|
37
|
+
* These lower to the same opcodes the JS reference + WGSL interpreter agree on, so any effect
|
|
38
|
+
* authored here is executable and testable end-to-end on the CPU before it ever hits the GPU.
|
|
39
|
+
*
|
|
40
|
+
* ## Types
|
|
41
|
+
*
|
|
42
|
+
* A value is a float vector of one to four lanes, and which it is is a property of the node
|
|
43
|
+
* instance rather than of its type: a `builtin` is a scalar or a `vec3` by which builtin it reads,
|
|
44
|
+
* a `const` is as wide as the literal it holds, an `add` is as wide as the wider of what it adds.
|
|
45
|
+
* So every port is declared with the one {@link PARTICLE_VALUE_TYPE} — any output may be wired to
|
|
46
|
+
* any input — and each node type says how it is typed through
|
|
47
|
+
* {@link ParticleNodeDescription#infer_types}, which {@link ./infer_particle_graph_types.js}
|
|
48
|
+
* resolves over a whole graph. The types themselves are in {@link ./ParticleValueType.js}, and
|
|
49
|
+
* registered here so a view can colour by them.
|
|
50
|
+
*
|
|
51
|
+
* A type may also say what a value *means* — {@link PARTICLE_TYPE_COLOR} is four lanes that are a
|
|
52
|
+
* colour — and the rules below never ask. They compare `components` and take the widest, so a
|
|
53
|
+
* colour is accepted wherever a `vec4` is, produces whatever a `vec4` would, and survives the
|
|
54
|
+
* arithmetic in between. The two places a colour enters a graph are the `color` node and an
|
|
55
|
+
* attribute the effect's layout has tagged as one.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The declared type of every port in the library: a value goes here.
|
|
60
|
+
*
|
|
61
|
+
* Which value — one lane or four — is resolved per node instance, see the note on types above.
|
|
62
|
+
* Declared once for all ports because the graph model connects ports whose declared types are the
|
|
63
|
+
* same object, and any particle value may drive any particle input; where the widths differ, the
|
|
64
|
+
* compiler converts and the validator says so.
|
|
65
|
+
*
|
|
66
|
+
* @type {DataType}
|
|
67
|
+
*/
|
|
68
|
+
export const PARTICLE_VALUE_TYPE = DataType.from(0, "value");
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Name of the single output port carried by every value-producing node.
|
|
72
|
+
* @type {string}
|
|
73
|
+
*/
|
|
74
|
+
export const PARTICLE_NODE_OUTPUT_PORT = "out";
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @type {NodeRegistry}
|
|
78
|
+
*/
|
|
79
|
+
export const PARTICLE_NODE_REGISTRY = new NodeRegistry();
|
|
80
|
+
|
|
81
|
+
// The resolved types are the registry's too: a view colours a wire by a type's id, and looks the
|
|
82
|
+
// id up here.
|
|
83
|
+
for (const type of PARTICLE_ALL_VALUE_TYPES) {
|
|
84
|
+
PARTICLE_NODE_REGISTRY.addType(type);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Node descriptions are addressed by name while authoring; the registry itself is keyed by the
|
|
89
|
+
* numeric IDs assigned below.
|
|
90
|
+
*
|
|
91
|
+
* IDs are handed out in declaration order rather than by {@link NodeDescription}'s global counter,
|
|
92
|
+
* so they do not drift with unrelated node usage elsewhere in the engine. Appending new node types
|
|
93
|
+
* at the end of the library is therefore safe; reordering or removing existing ones renumbers every
|
|
94
|
+
* node that follows, which would invalidate already-serialized graphs.
|
|
95
|
+
*
|
|
96
|
+
* @type {Map<string, ParticleNodeDescription>}
|
|
97
|
+
*/
|
|
98
|
+
const descriptions_by_name = new Map();
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {string} name
|
|
102
|
+
* @param {string[]} inputs input port names, in operand order
|
|
103
|
+
* @param {function} emit see {@link ParticleNodeDescription#emit}
|
|
104
|
+
* @param {object} [options]
|
|
105
|
+
* @param {boolean} [options.produces_value] when false the node is a sink and gets no output port
|
|
106
|
+
* @param {function} [options.infer_types] see {@link ParticleNodeDescription#infer_types}
|
|
107
|
+
* @param {function} [options.operation_width] see {@link ParticleNodeDescription#operation_width}
|
|
108
|
+
* @param {function} [options.input_widths] see {@link ParticleNodeDescription#input_widths}
|
|
109
|
+
* @param {function} [options.result_width] see {@link ParticleNodeDescription#result_width}
|
|
110
|
+
* @returns {ParticleNodeDescription}
|
|
111
|
+
*/
|
|
112
|
+
function define(name, inputs, emit, {
|
|
113
|
+
produces_value = true,
|
|
114
|
+
infer_types,
|
|
115
|
+
operation_width,
|
|
116
|
+
input_widths,
|
|
117
|
+
result_width,
|
|
118
|
+
} = {}) {
|
|
119
|
+
if (descriptions_by_name.has(name)) {
|
|
120
|
+
throw new Error(`Duplicate particle node type '${name}'`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const description = new ParticleNodeDescription();
|
|
124
|
+
|
|
125
|
+
description.name = name;
|
|
126
|
+
description.id = descriptions_by_name.size;
|
|
127
|
+
|
|
128
|
+
// what an author reads is decided in one table, away from the lowering; see
|
|
129
|
+
// ./particle_node_presentation.js
|
|
130
|
+
const presentation = particle_node_presentation(name);
|
|
131
|
+
|
|
132
|
+
description.display_label = presentation.label;
|
|
133
|
+
description.icon = presentation.icon;
|
|
134
|
+
description.help = presentation.help;
|
|
135
|
+
|
|
136
|
+
for (const input of inputs) {
|
|
137
|
+
description.createPort(PARTICLE_VALUE_TYPE, input, PortDirection.In);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (produces_value) {
|
|
141
|
+
description.createPort(PARTICLE_VALUE_TYPE, PARTICLE_NODE_OUTPUT_PORT, PortDirection.Out);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
description.emit = emit;
|
|
145
|
+
|
|
146
|
+
if (infer_types !== undefined) {
|
|
147
|
+
description.infer_types = infer_types;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (operation_width !== undefined) {
|
|
151
|
+
description.operation_width = operation_width;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (input_widths !== undefined) {
|
|
155
|
+
description.input_widths = input_widths;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (result_width !== undefined) {
|
|
159
|
+
description.result_width = result_width;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
PARTICLE_NODE_REGISTRY.addNode(description);
|
|
163
|
+
descriptions_by_name.set(name, description);
|
|
164
|
+
|
|
165
|
+
return description;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** side-effecting sink (no output) */
|
|
169
|
+
function sink(name, inputs, emit, options = {}) {
|
|
170
|
+
return define(name, inputs, emit, { ...options, produces_value: false });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* A source: no inputs, and a type of its own that its parameters (and the layout) decide.
|
|
175
|
+
*
|
|
176
|
+
* @param {string} name
|
|
177
|
+
* @param {function(object, ParticleLayout|undefined):(ParticleValueType|null)} type_of
|
|
178
|
+
* @param {function} emit
|
|
179
|
+
* @param {object} [options]
|
|
180
|
+
* @returns {ParticleNodeDescription}
|
|
181
|
+
*/
|
|
182
|
+
function source(name, type_of, emit, options = {}) {
|
|
183
|
+
return define(name, [], emit, {
|
|
184
|
+
...options,
|
|
185
|
+
infer_types: ({ parameters, layout }) => ({ output: type_of(parameters, layout), inputs: {} }),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function unary(name, opcode, options = {}) {
|
|
190
|
+
return define(name, ["x"], (s, c) => s.emit(opcode, c.out, c.in("x"), 0, 0, c.width), options);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function binary(name, opcode, options = {}) {
|
|
194
|
+
return define(name, ["a", "b"], (s, c) => s.emit(opcode, c.out, c.in("a"), c.in("b"), 0, c.width), options);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Every input read at one type.
|
|
199
|
+
*
|
|
200
|
+
* @param {Object<string, *>} inputs
|
|
201
|
+
* @param {ParticleValueType|null} type
|
|
202
|
+
* @returns {Object<string, ParticleValueType|null>}
|
|
203
|
+
*/
|
|
204
|
+
function read_all_as(inputs, type) {
|
|
205
|
+
const read = {};
|
|
206
|
+
|
|
207
|
+
for (const port of Object.keys(inputs)) {
|
|
208
|
+
read[port] = type;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return read;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {Object<string, ParticleValueType>} input_types
|
|
216
|
+
* @returns {number} lanes of the first input, which for the reductions is the lanes of every input
|
|
217
|
+
*/
|
|
218
|
+
function first_input_width(input_types) {
|
|
219
|
+
return Object.values(input_types)[0].components;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* The same lanes, stripped of what they were for.
|
|
224
|
+
*
|
|
225
|
+
* A meaning travels through the operations that stay in the value's own space — a colour scaled,
|
|
226
|
+
* clamped or mixed is still a colour — and stops at the ones that leave it. A comparison of two
|
|
227
|
+
* colours is a mask, and the lane that chooses between two colours is a mask; calling either of
|
|
228
|
+
* those a colour would be the type saying something untrue, which is worse than it saying nothing.
|
|
229
|
+
*
|
|
230
|
+
* @param {ParticleValueType|null} type
|
|
231
|
+
* @returns {ParticleValueType|null}
|
|
232
|
+
*/
|
|
233
|
+
function plain_shape(type) {
|
|
234
|
+
return type === null ? null : particle_value_type(type.components);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* An op whose result is a mask or a ramp rather than another value of what it read: it compares in
|
|
239
|
+
* the operands' own space, and answers in none.
|
|
240
|
+
*/
|
|
241
|
+
const MASK_RESULT = {
|
|
242
|
+
infer_types: ({ inputs }) => {
|
|
243
|
+
const wide = particle_widest_type(Object.values(inputs));
|
|
244
|
+
|
|
245
|
+
return { output: plain_shape(wide), inputs: read_all_as(inputs, wide) };
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* An op that returns one of the values it was given, or something between them, under the control
|
|
251
|
+
* of a third: `mix`'s `t`, `select`'s `condition`.
|
|
252
|
+
*
|
|
253
|
+
* Two questions with different answers. *How wide* is what it has always been — every operand,
|
|
254
|
+
* widest wins, so a pair of scalars chosen between by a three-lane mask is still three lanes of
|
|
255
|
+
* result, which is how a vector was assembled before there were constructors for it. *What it
|
|
256
|
+
* means* comes only from the ends: the control says which side, not what kind, and a result is only
|
|
257
|
+
* a colour if the things being blended were.
|
|
258
|
+
*
|
|
259
|
+
* @param {string} control the port that steers rather than contributes
|
|
260
|
+
* @returns {object} description options
|
|
261
|
+
*/
|
|
262
|
+
function blend_controlled_by(control) {
|
|
263
|
+
return {
|
|
264
|
+
infer_types: ({ inputs }) => {
|
|
265
|
+
const width_from = particle_widest_type(Object.values(inputs));
|
|
266
|
+
|
|
267
|
+
if (width_from === null) {
|
|
268
|
+
return { output: null, inputs: read_all_as(inputs, null) };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const ends = particle_widest_type([inputs.a, inputs.b]);
|
|
272
|
+
|
|
273
|
+
const output = ends !== null && ends.semantic && ends.components === width_from.components
|
|
274
|
+
? ends
|
|
275
|
+
: plain_shape(width_from);
|
|
276
|
+
|
|
277
|
+
const read = read_all_as(inputs, output);
|
|
278
|
+
|
|
279
|
+
read[control] = plain_shape(output);
|
|
280
|
+
|
|
281
|
+
return { output, inputs: read };
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* A reduction: reads every lane of its inputs — which are read at one width, the widest among
|
|
288
|
+
* them — and writes one, whatever is asked of it. The result is a scalar.
|
|
289
|
+
*/
|
|
290
|
+
const REDUCTION = {
|
|
291
|
+
infer_types: ({ inputs }) => {
|
|
292
|
+
const wide = particle_widest_type(Object.values(inputs));
|
|
293
|
+
|
|
294
|
+
return { output: wide === null ? null : PARTICLE_TYPE_F32, inputs: read_all_as(inputs, wide) };
|
|
295
|
+
},
|
|
296
|
+
operation_width: ({ input_types }) => first_input_width(input_types),
|
|
297
|
+
result_width: () => 1,
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* An op that has to see the whole vector to compute any lane of it: a `normalize` feeding a
|
|
302
|
+
* one-component attribute still divides by the length of the whole vector rather than by the
|
|
303
|
+
* length of its first lane. Its type is its input's; only the width it runs at differs from the
|
|
304
|
+
* element-wise default.
|
|
305
|
+
*/
|
|
306
|
+
const WHOLE_VECTOR = {
|
|
307
|
+
operation_width: ({ type }) => type.components,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Lane names, in lane order: the input ports of the vector constructors, and how a swizzle is
|
|
314
|
+
* spelled.
|
|
315
|
+
* @type {string[]}
|
|
316
|
+
*/
|
|
317
|
+
const LANE_NAMES = ["x", "y", "z", "w"];
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @param {object} parameters
|
|
321
|
+
* @returns {number[]} a literal node's value
|
|
322
|
+
*/
|
|
323
|
+
function literal_of(parameters) {
|
|
324
|
+
return Array.isArray(parameters.value) ? parameters.value : [];
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Lower a literal into the constant pool, at the lanes it is being read at.
|
|
329
|
+
*
|
|
330
|
+
* Only those lanes: a scalar literal costs one word of the pool — or, read wider, that word
|
|
331
|
+
* repeated, which is the broadcast for free — and a wide literal read narrow interns only its
|
|
332
|
+
* leading words. A lane the value does not have reads zero, which is what a `color` written with
|
|
333
|
+
* too few numbers gets; the validator reports the parameter, and the compiler does not also throw.
|
|
334
|
+
*
|
|
335
|
+
* @param {InstructionStream} s
|
|
336
|
+
* @param {object} c the emit context
|
|
337
|
+
*/
|
|
338
|
+
function emit_literal(s, c) {
|
|
339
|
+
const v = literal_of(c.parameters);
|
|
340
|
+
|
|
341
|
+
const components = [];
|
|
342
|
+
|
|
343
|
+
for (let i = 0; i < c.width; i++) {
|
|
344
|
+
components.push((c.type.is_scalar ? v[0] : v[i]) ?? 0);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
s.loadConst(c.out, c.asm.constant(...components), c.width);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* @param {object} parameters
|
|
352
|
+
* @returns {number[]} a swizzle's lane selectors, one per output lane
|
|
353
|
+
*/
|
|
354
|
+
function pattern_of(parameters) {
|
|
355
|
+
const pattern = parameters.pattern;
|
|
356
|
+
|
|
357
|
+
return Array.isArray(pattern) && pattern.length > 0 ? pattern : [0, 1, 2, 3];
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @param {number[]} pattern
|
|
362
|
+
* @returns {number} how many lanes of a source the selectors reach into
|
|
363
|
+
*/
|
|
364
|
+
function pattern_span(pattern) {
|
|
365
|
+
return 1 + Math.max(...pattern);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* The attribute a node names, as a type; `null` where there is no layout to look in or the name is
|
|
370
|
+
* not in it — which the validator reports in its own words.
|
|
371
|
+
*
|
|
372
|
+
* An attribute the layout has tagged (`color`, say) is that type rather than the plain shape, which
|
|
373
|
+
* is how a colour reaches a graph without a node saying so. The tag has to be the shape the
|
|
374
|
+
* attribute actually is: three words tagged `color` is a mistake, and reading them as a four-lane
|
|
375
|
+
* colour would be a second one, so the shape wins and the tag is ignored.
|
|
376
|
+
*
|
|
377
|
+
* @param {object} parameters
|
|
378
|
+
* @param {ParticleLayout|undefined} layout
|
|
379
|
+
* @returns {ParticleValueType|null}
|
|
380
|
+
*/
|
|
381
|
+
function attribute_type(parameters, layout) {
|
|
382
|
+
if (layout === undefined || !layout.has(parameters.name)) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const attribute = layout.get(parameters.name);
|
|
387
|
+
|
|
388
|
+
const semantic = particle_semantic_type(attribute.type);
|
|
389
|
+
|
|
390
|
+
if (semantic !== null && semantic.components === attribute.components) {
|
|
391
|
+
return semantic;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return particle_value_type(attribute.components);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Which of a binary op's operands is a scalar meeting a vector — read through `.xxxx` with no
|
|
399
|
+
* broadcast instruction — or `null` where neither or both are.
|
|
400
|
+
*
|
|
401
|
+
* @param {Object<string, ParticleValueType>} source_types
|
|
402
|
+
* @returns {string|null} `"a"`, `"b"` or `null`
|
|
403
|
+
*/
|
|
404
|
+
function scalar_operand(source_types) {
|
|
405
|
+
const a = source_types.a.is_scalar;
|
|
406
|
+
const b = source_types.b.is_scalar;
|
|
407
|
+
|
|
408
|
+
if (a === b) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return a ? "a" : "b";
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// --- sources ---
|
|
416
|
+
// A source has a type of its own, and it is as wide as that: a literal is as wide as it was
|
|
417
|
+
// written, a builtin as wide as the host fills it, an attribute as wide as the layout says. A
|
|
418
|
+
// consumer wanting more lanes than that is a conversion the compiler makes after the value is
|
|
419
|
+
// produced — except where a source can fill lanes for free, which it then does.
|
|
420
|
+
source("const", parameters => particle_value_type_or_null(literal_of(parameters).length), emit_literal, {
|
|
421
|
+
operation_width: ({ type, demand }) => type.is_scalar ? demand : Math.min(type.components, demand),
|
|
422
|
+
});
|
|
423
|
+
source(
|
|
424
|
+
"builtin",
|
|
425
|
+
parameters => particle_value_type_or_null(VM_BUILTIN_COMPONENTS[parameters.id]),
|
|
426
|
+
(s, c) => s.loadBuiltin(c.out, c.parameters.id, c.width)
|
|
427
|
+
);
|
|
428
|
+
source("attribute", attribute_type, (s, c) => {
|
|
429
|
+
const a = c.layout.get(c.parameters.name);
|
|
430
|
+
|
|
431
|
+
s.loadAttr(c.out, a.offset, a.components, c.width);
|
|
432
|
+
});
|
|
433
|
+
// One uniform draw in `[0, 1)` per lane, as many lanes as the node says. Shaped distributions are
|
|
434
|
+
// compositions over it rather than modes of it — see ./particle_graph_distributions.js.
|
|
435
|
+
source("random", parameters => particle_value_type_or_null(parameters.components), (s, c) => s.random(c.out, c.width));
|
|
436
|
+
|
|
437
|
+
// --- sinks ---
|
|
438
|
+
// A store is exactly as wide as the attribute it writes, and that is how it reads its input.
|
|
439
|
+
sink("setAttribute", ["value"], (s, c) => {
|
|
440
|
+
const a = c.layout.get(c.parameters.name);
|
|
441
|
+
|
|
442
|
+
s.storeAttr(c.in("value"), a.offset, a.components);
|
|
443
|
+
}, {
|
|
444
|
+
infer_types: ({ parameters, layout }) => ({ output: null, inputs: { value: attribute_type(parameters, layout) } }),
|
|
445
|
+
operation_width: ({ parameters, layout }) => layout.get(parameters.name).components,
|
|
446
|
+
});
|
|
447
|
+
sink("kill", ["condition"], (s, c) => s.kill(c.in("condition")), {
|
|
448
|
+
infer_types: () => ({ output: null, inputs: { condition: PARTICLE_TYPE_F32 } }),
|
|
449
|
+
operation_width: () => 1,
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// --- arithmetic (component-wise) ---
|
|
453
|
+
binary("add", VM_OP.ADD);
|
|
454
|
+
binary("sub", VM_OP.SUB);
|
|
455
|
+
// A vector times a scalar reads the scalar through `.xxxx`, which is what keeps `velocity * dt` one
|
|
456
|
+
// instruction rather than a broadcast and a multiply.
|
|
457
|
+
define("mul", ["a", "b"], (s, c) => {
|
|
458
|
+
const scalar = scalar_operand(c.source_types);
|
|
459
|
+
|
|
460
|
+
if (scalar === null) {
|
|
461
|
+
s.emit(VM_OP.MUL, c.out, c.in("a"), c.in("b"), 0, c.width);
|
|
462
|
+
} else {
|
|
463
|
+
s.emit(VM_OP.MUL, c.out, c.in(scalar === "a" ? "b" : "a"), vm_register(c.in(scalar), VM_SWIZZLE_BROADCAST), 0, c.width);
|
|
464
|
+
}
|
|
465
|
+
}, {
|
|
466
|
+
input_widths: ({ source_types }) => {
|
|
467
|
+
const scalar = scalar_operand(source_types);
|
|
468
|
+
|
|
469
|
+
return scalar === null ? {} : { [scalar]: 1 };
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
binary("div", VM_OP.DIV);
|
|
473
|
+
binary("min", VM_OP.MIN);
|
|
474
|
+
binary("max", VM_OP.MAX);
|
|
475
|
+
binary("pow", VM_OP.POW);
|
|
476
|
+
binary("step", VM_OP.STEP, MASK_RESULT);
|
|
477
|
+
define("mad", ["a", "b", "c"], (s, c) => s.emit(VM_OP.MAD, c.out, c.in("a"), c.in("b"), c.in("c"), c.width));
|
|
478
|
+
define("mix", ["a", "b", "t"], (s, c) => s.emit(VM_OP.MIX, c.out, c.in("a"), c.in("b"), c.in("t"), c.width), blend_controlled_by("t"));
|
|
479
|
+
define("clamp", ["x", "lo", "hi"], (s, c) => s.emit(VM_OP.CLAMP, c.out, c.in("x"), c.in("lo"), c.in("hi"), c.width));
|
|
480
|
+
define("smoothstep", ["e0", "e1", "x"], (s, c) => s.emit(VM_OP.SMOOTHSTEP, c.out, c.in("e0"), c.in("e1"), c.in("x"), c.width), MASK_RESULT);
|
|
481
|
+
// The scalar is one lane; the vector is however wide the result is.
|
|
482
|
+
define("scale", ["v", "s"], (s, c) => s.emit(VM_OP.MUL, c.out, c.in("v"), vm_register(c.in("s"), VM_SWIZZLE_BROADCAST), 0, c.width), {
|
|
483
|
+
infer_types: ({ inputs }) => ({ output: inputs.v, inputs: { v: inputs.v, s: PARTICLE_TYPE_F32 } }),
|
|
484
|
+
input_widths: () => ({ s: 1 }),
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// --- unary math ---
|
|
488
|
+
unary("neg", VM_OP.NEG);
|
|
489
|
+
unary("abs", VM_OP.ABS);
|
|
490
|
+
unary("floor", VM_OP.FLOOR);
|
|
491
|
+
unary("fract", VM_OP.FRACT);
|
|
492
|
+
unary("sqrt", VM_OP.SQRT);
|
|
493
|
+
unary("sin", VM_OP.SIN);
|
|
494
|
+
unary("cos", VM_OP.COS);
|
|
495
|
+
unary("exp", VM_OP.EXP);
|
|
496
|
+
unary("log", VM_OP.LOG);
|
|
497
|
+
unary("normalize", VM_OP.NORMALIZE, WHOLE_VECTOR);
|
|
498
|
+
unary("length", VM_OP.LENGTH, REDUCTION);
|
|
499
|
+
|
|
500
|
+
// --- vector ---
|
|
501
|
+
binary("dot", VM_OP.DOT, REDUCTION);
|
|
502
|
+
// A cross product is three lanes in and three out, whatever arrives.
|
|
503
|
+
binary("cross", VM_OP.CROSS, {
|
|
504
|
+
infer_types: ({ inputs }) => ({ output: PARTICLE_TYPE_VEC3, inputs: read_all_as(inputs, PARTICLE_TYPE_VEC3) }),
|
|
505
|
+
operation_width: () => 3,
|
|
506
|
+
});
|
|
507
|
+
binary("distance", VM_OP.DISTANCE, REDUCTION);
|
|
508
|
+
// A swizzle is as wide as its pattern, and its source has to cover the lanes the pattern names —
|
|
509
|
+
// which is unrelated to how wide the result is, and is the one place a source's extent is read out
|
|
510
|
+
// of a parameter rather than a width.
|
|
511
|
+
define("swizzle", ["x"], (s, c) => {
|
|
512
|
+
const p = pattern_of(c.parameters);
|
|
513
|
+
|
|
514
|
+
s.emit(VM_OP.MOV, c.out, vm_register(c.in("x"), pack_swizzle(p[0] ?? 0, p[1] ?? 0, p[2] ?? 0, p[3] ?? 0)), 0, 0, c.width);
|
|
515
|
+
}, {
|
|
516
|
+
infer_types: ({ inputs, parameters }) => {
|
|
517
|
+
const p = pattern_of(parameters);
|
|
518
|
+
const output = particle_value_type_or_null(p.length);
|
|
519
|
+
|
|
520
|
+
if (output === null) {
|
|
521
|
+
return { output: null, inputs: { x: null } };
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const span = pattern_span(p);
|
|
525
|
+
const source = inputs.x;
|
|
526
|
+
|
|
527
|
+
// A source that covers the lanes named is read as itself: picking `.x` out of a vec4 is not
|
|
528
|
+
// a narrowing. One that does not is read as wide as the pattern reaches, and the lanes it
|
|
529
|
+
// has not got read zero — a warning, since `.z` of a vec2 is more likely a slip than a plan.
|
|
530
|
+
const x = source !== null && source.components >= span ? source : particle_value_type_or_null(span);
|
|
531
|
+
|
|
532
|
+
return { output, inputs: { x } };
|
|
533
|
+
},
|
|
534
|
+
input_widths: ({ operation_width, parameters }) => ({
|
|
535
|
+
x: pattern_span(pattern_of(parameters).slice(0, operation_width)),
|
|
536
|
+
}),
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// The vector constructors: a value per lane, from scalars. Lowered as one `MOV` per lane into the
|
|
540
|
+
// result's run, which is the only way a lane gets written on its own — every other opcode writes a
|
|
541
|
+
// run from its start.
|
|
542
|
+
for (const components of [2, 3, 4]) {
|
|
543
|
+
const inputs = LANE_NAMES.slice(0, components);
|
|
544
|
+
const output = particle_value_type(components);
|
|
545
|
+
|
|
546
|
+
define(`vec${components}`, inputs, (s, c) => {
|
|
547
|
+
const moves = inputs.slice(0, c.width).map((port, lane) => ({ from: c.in(port), to: c.out + lane }));
|
|
548
|
+
|
|
549
|
+
// The allocator lets one source sit where the result starts, and nowhere else inside it. A
|
|
550
|
+
// move into lane 0 would overwrite that source before a later move could read it into its
|
|
551
|
+
// own lane — so it goes first, and the rest are then free to land in any order.
|
|
552
|
+
moves.sort((p, q) => Number(q.from === c.out) - Number(p.from === c.out));
|
|
553
|
+
|
|
554
|
+
for (const { from, to } of moves) {
|
|
555
|
+
if (from !== to) {
|
|
556
|
+
s.mov(to, from, 1);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}, {
|
|
560
|
+
infer_types: () => ({ output, inputs: read_all_as(Object.fromEntries(inputs.map(port => [port, null])), PARTICLE_TYPE_F32) }),
|
|
561
|
+
input_widths: () => Object.fromEntries(inputs.map(port => [port, 1])),
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// --- logic ---
|
|
566
|
+
define("compare", ["a", "b"], (s, c) => {
|
|
567
|
+
const opcode = PARTICLE_COMPARE_OPCODES[c.parameters.op];
|
|
568
|
+
if (opcode === undefined) {
|
|
569
|
+
throw new Error(`Unknown compare op '${c.parameters.op}'`);
|
|
570
|
+
}
|
|
571
|
+
s.emit(opcode, c.out, c.in("a"), c.in("b"), 0, c.width);
|
|
572
|
+
}, MASK_RESULT);
|
|
573
|
+
define("select", ["a", "b", "condition"], (s, c) => s.emit(VM_OP.SELECT, c.out, c.in("a"), c.in("b"), c.in("condition"), c.width), blend_controlled_by("condition"));
|
|
574
|
+
|
|
575
|
+
// --- curves ---
|
|
576
|
+
// One lane of `t` in, one value out — which the opcode writes across every lane it is asked for, so
|
|
577
|
+
// a curve read wider than a scalar is the broadcast for free.
|
|
578
|
+
define("curve", ["t"], (s, c) => s.curve(c.out, c.parameters.handle, c.in("t"), c.width), {
|
|
579
|
+
infer_types: () => ({ output: PARTICLE_TYPE_F32, inputs: { t: PARTICLE_TYPE_F32 } }),
|
|
580
|
+
operation_width: ({ demand }) => demand,
|
|
581
|
+
input_widths: () => ({ t: 1 }),
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// --- semantic sources ---
|
|
585
|
+
// Appended rather than filed with the other sources, because a node type's id is its position in
|
|
586
|
+
// this file and the ids are a wire format: inserting one up there renumbers every type below it.
|
|
587
|
+
// Where an author finds it is the palette's business, and the palette lists it among the sources.
|
|
588
|
+
|
|
589
|
+
// A literal colour. A `const` of four numbers in every respect the compiler sees — it interns the
|
|
590
|
+
// same pool words and loads them with the same instruction — and a colour to everything above:
|
|
591
|
+
// a swatch to edit rather than four fields, and its own colour along the wire to wherever it is
|
|
592
|
+
// written.
|
|
593
|
+
source("color", () => PARTICLE_TYPE_COLOR, emit_literal);
|
|
594
|
+
|
|
595
|
+
// --- more math ---
|
|
596
|
+
// Appended for the same reason `color` is: these belong with the unary math above and cannot be
|
|
597
|
+
// written there without renumbering every type that follows. The palette files them where they
|
|
598
|
+
// read.
|
|
599
|
+
|
|
600
|
+
unary("rsqrt", VM_OP.RSQRT);
|
|
601
|
+
unary("tan", VM_OP.TAN);
|
|
602
|
+
unary("asin", VM_OP.ASIN);
|
|
603
|
+
unary("acos", VM_OP.ACOS);
|
|
604
|
+
// The one binary among them, and the one whose ports are not interchangeable: `atan2(y, x)` is the
|
|
605
|
+
// angle of the point, so the ports are named for the axes rather than `a` and `b`.
|
|
606
|
+
define("atan2", ["y", "x"], (s, c) => s.emit(VM_OP.ATAN2, c.out, c.in("y"), c.in("x"), 0, c.width));
|
|
607
|
+
|
|
608
|
+
// The `clamp` everybody writes, with the bounds written in rather than wired up. Both of them are
|
|
609
|
+
// literals of one lane, so they are immediates in the instruction word: the node costs one
|
|
610
|
+
// instruction and not one register, where a `clamp` between two `const` nodes costs three and as
|
|
611
|
+
// many registers as it is wide. Nothing else in the library takes an immediate this way, because
|
|
612
|
+
// nothing else in the library knows what its operands are before the graph is drawn.
|
|
613
|
+
define("saturate", ["x"], (s, c) => s.emit(VM_OP.CLAMP, c.out, c.in("x"), vm_immediate(0), vm_immediate(1), c.width));
|
|
614
|
+
|
|
615
|
+
// --- ramps ---
|
|
616
|
+
// A curve and a gradient: authored data, lowered by being written out.
|
|
617
|
+
//
|
|
618
|
+
// These are the library's expanding types. They are made of other nodes, like a
|
|
619
|
+
// {@link ./ParticleNodeGroup.js group} is, but the nodes they are made of come from their own
|
|
620
|
+
// parameters rather than from a body fixed when the library loaded — a gradient with five stops is
|
|
621
|
+
// different arithmetic from a gradient with two, and no one body could be both. So instead of an
|
|
622
|
+
// `emit` they carry an {@link ./ParticleNodeDescription.js#expand}, which
|
|
623
|
+
// {@link ./flatten_particle_graph.js} calls and splices in exactly as it splices a group. What each
|
|
624
|
+
// one expands *to* is in {@link ./particle_ramp.js}, which is also where the reasoning about the
|
|
625
|
+
// arithmetic lives; this is only the declaration.
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Declare a type whose body is built from its own parameters.
|
|
629
|
+
*
|
|
630
|
+
* @param {string} name
|
|
631
|
+
* @param {Array<{name: string, type: ParticleValueType|null}>} inputs
|
|
632
|
+
* @param {ParticleValueType} output what the node produces, and what the expansion's `out` is
|
|
633
|
+
* @param {function(ParticleGroupBuilder, object):*} body given a builder and the instance's
|
|
634
|
+
* parameters, returns the value to expose as `out`
|
|
635
|
+
* @returns {ParticleNodeDescription}
|
|
636
|
+
*/
|
|
637
|
+
function expanding(name, inputs, output, body) {
|
|
638
|
+
const description = define(name, inputs.map(port => port.name), () => {
|
|
639
|
+
throw new Error(
|
|
640
|
+
`'${name}' reached the compiler. An expanding type is written out before lowering; `
|
|
641
|
+
+ "see flatten_particle_graph.js."
|
|
642
|
+
);
|
|
643
|
+
}, {
|
|
644
|
+
infer_types: ({ inputs: wired }) => ({
|
|
645
|
+
output,
|
|
646
|
+
inputs: Object.fromEntries(inputs.map(port => [port.name, port.type ?? wired[port.name]])),
|
|
647
|
+
}),
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
description.expand = parameters => {
|
|
651
|
+
const g = new ParticleGroupBuilder({
|
|
652
|
+
name,
|
|
653
|
+
label: description.display_label,
|
|
654
|
+
icon: description.icon,
|
|
655
|
+
help: description.help,
|
|
656
|
+
inputs,
|
|
657
|
+
outputs: [{ name: PARTICLE_NODE_OUTPUT_PORT, type: output }],
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
g.out(PARTICLE_NODE_OUTPUT_PORT, body(g, parameters));
|
|
661
|
+
|
|
662
|
+
return g.build();
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
return description;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// One curve, written into the program. Not the `curve` node above: that one samples a row of the
|
|
669
|
+
// host's animation-curve table and this one *is* the curve, the way `color` is a colour rather than
|
|
670
|
+
// the name of one.
|
|
671
|
+
expanding(
|
|
672
|
+
"curveLiteral",
|
|
673
|
+
[{ name: "t", type: PARTICLE_TYPE_F32 }],
|
|
674
|
+
PARTICLE_TYPE_F32,
|
|
675
|
+
(g, parameters) => roll_out_particle_curve(g, g.in.t, parameters.curve)
|
|
676
|
+
);
|
|
677
|
+
|
|
678
|
+
// The two gradients. One body, read three lanes wide or four — an RGB gradient still *stores* four
|
|
679
|
+
// channels per stop, so that a gradient copied from one node to the other keeps the alpha it was
|
|
680
|
+
// authored with rather than losing it on the way through.
|
|
681
|
+
expanding(
|
|
682
|
+
"gradientRGBA",
|
|
683
|
+
[{ name: "t", type: PARTICLE_TYPE_F32 }],
|
|
684
|
+
PARTICLE_TYPE_COLOR,
|
|
685
|
+
(g, parameters) => roll_out_particle_gradient(g, g.in.t, parameters.stops, PARTICLE_GRADIENT_COLOR_COMPONENTS)
|
|
686
|
+
);
|
|
687
|
+
expanding(
|
|
688
|
+
"gradientRGB",
|
|
689
|
+
[{ name: "t", type: PARTICLE_TYPE_F32 }],
|
|
690
|
+
PARTICLE_TYPE_VEC3,
|
|
691
|
+
(g, parameters) => roll_out_particle_gradient(g, g.in.t, parameters.stops, PARTICLE_TYPE_VEC3.components)
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Every node type in the library, in declaration order.
|
|
696
|
+
*
|
|
697
|
+
* The registry is keyed by the numeric ids assigned above, so this is the only way to walk the
|
|
698
|
+
* library by the names authoring uses — which is what a node palette lists.
|
|
699
|
+
*
|
|
700
|
+
* @returns {ParticleNodeDescription[]}
|
|
701
|
+
*/
|
|
702
|
+
export function particle_node_descriptions() {
|
|
703
|
+
return Array.from(descriptions_by_name.values());
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Look up a node type by name.
|
|
708
|
+
* @param {string} name
|
|
709
|
+
* @returns {ParticleNodeDescription}
|
|
710
|
+
* @throws if no such node type exists
|
|
711
|
+
*/
|
|
712
|
+
export function get_particle_node_description(name) {
|
|
713
|
+
const description = descriptions_by_name.get(name);
|
|
714
|
+
|
|
715
|
+
if (description === undefined) {
|
|
716
|
+
throw new Error(`Unknown particle node type '${name}'`);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return description;
|
|
720
|
+
}
|