@woosh/meep-engine 3.68.0 → 3.69.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/CHANGELOG.md +45 -0
- package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts +2 -17
- package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts.map +1 -1
- package/editor/ecs/component/editors/ecs/Trail3DEditor.js +3 -18
- package/package.json +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +33 -65
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.js +37 -245
- package/src/engine/graphics/ecs/trail3d/Trail3DFlags.d.ts +0 -4
- package/src/engine/graphics/ecs/trail3d/Trail3DFlags.js +4 -10
- package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.d.ts +13 -10
- package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/seed_trail_stroke.js +28 -37
- package/src/engine/graphics/ecs/trail3d/trail_knot_count.d.ts +10 -0
- package/src/engine/graphics/ecs/trail3d/trail_knot_count.d.ts.map +1 -0
- package/src/engine/graphics/ecs/trail3d/trail_knot_count.js +27 -0
- package/src/engine/graphics/trail/TrailKnots.d.ts +62 -0
- package/src/engine/graphics/trail/TrailKnots.d.ts.map +1 -0
- package/src/engine/graphics/trail/TrailKnots.js +137 -0
- package/src/engine/graphics/trail/tube/TubeXMaterialSpec.d.ts +1 -1
- package/src/engine/graphics/trail/tube/TubeXMaterialSpec.js +1 -1
- package/src/engine/graphics3/EntityRowHold.d.ts +48 -0
- package/src/engine/graphics3/EntityRowHold.d.ts.map +1 -0
- package/src/engine/graphics3/EntityRowHold.js +128 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +6 -2
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/GPUParticleEmitterSystem.js +11 -44
- package/src/engine/graphics3/Trail3DSystem.d.ts +69 -35
- package/src/engine/graphics3/Trail3DSystem.d.ts.map +1 -1
- package/src/engine/graphics3/Trail3DSystem.js +380 -81
- package/src/engine/physics/fluid/ecs/FluidObstacle.d.ts +17 -0
- package/src/engine/physics/fluid/ecs/FluidObstacle.d.ts.map +1 -1
- package/src/engine/physics/fluid/ecs/FluidObstacle.js +22 -2
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +6 -5
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts.map +1 -1
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.js +14 -3
- package/src/engine/physics/fluid/obstacle/FLUID_OBSTACLE_RECORD_STRUCT.d.ts +9 -1
- package/src/engine/physics/fluid/obstacle/FLUID_OBSTACLE_RECORD_STRUCT.d.ts.map +1 -1
- package/src/engine/physics/fluid/obstacle/FLUID_OBSTACLE_RECORD_STRUCT.js +11 -1
- package/src/engine/physics/fluid/obstacle/fluid_obstacle_record_create.d.ts +5 -0
- package/src/engine/physics/fluid/obstacle/fluid_obstacle_record_create.d.ts.map +1 -1
- package/src/engine/physics/fluid/obstacle/fluid_obstacle_record_create.js +2 -0
- package/src/engine/physics/fluid/obstacle/fluid_obstacle_write_gpu_record.d.ts +4 -3
- package/src/engine/physics/fluid/obstacle/fluid_obstacle_write_gpu_record.d.ts.map +1 -1
- package/src/engine/physics/fluid/obstacle/fluid_obstacle_write_gpu_record.js +11 -3
- package/src/shade/playground/trails/index.html +50 -0
- package/src/shade/playground/trails/main.d.ts +2 -0
- package/src/shade/playground/trails/main.d.ts.map +1 -0
- package/src/shade/playground/trails/main.js +258 -0
- package/src/shade/renderer/animation/animation_curve_write_keys.d.ts +18 -0
- package/src/shade/renderer/animation/animation_curve_write_keys.d.ts.map +1 -0
- package/src/shade/renderer/animation/animation_curve_write_keys.js +34 -0
- package/src/shade/renderer/deferred/tonemap/ColorGrade.d.ts.map +1 -1
- package/src/shade/renderer/deferred/tonemap/ColorGrade.js +13 -36
- package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +2 -2
- package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.d.ts +2 -13
- package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.js +6 -21
- package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.d.ts +17 -6
- package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.js +38 -178
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts +7 -9
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.js +17 -106
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.d.ts +7 -17
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.js +18 -118
- package/src/shade/renderer/fluid/forcing/FLUID_FORCING.d.ts +14 -0
- package/src/shade/renderer/fluid/forcing/FLUID_FORCING.d.ts.map +1 -1
- package/src/shade/renderer/fluid/forcing/FLUID_FORCING.js +18 -0
- package/src/shade/renderer/fluid/forcing/FLUID_FORCING_APPLY_STRUCT.d.ts +3 -1
- package/src/shade/renderer/fluid/forcing/FLUID_FORCING_APPLY_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/fluid/forcing/FLUID_FORCING_APPLY_STRUCT.js +4 -1
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +9 -0
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts.map +1 -1
- package/src/shade/renderer/fluid/forcing/GPUFluidForcing.js +20 -4
- package/src/shade/renderer/fluid/forcing/chunk_fluid_forcing_cell.d.ts +11 -2
- package/src/shade/renderer/fluid/forcing/chunk_fluid_forcing_cell.d.ts.map +1 -1
- package/src/shade/renderer/fluid/forcing/chunk_fluid_forcing_cell.js +59 -2
- package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.d.ts +4 -1
- package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.d.ts.map +1 -1
- package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.js +367 -341
- package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.d.ts +6 -2
- package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.d.ts.map +1 -1
- package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.js +339 -282
- package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts +3 -2
- package/src/shade/renderer/grass/press/shader_grass_press_mark.d.ts.map +1 -1
- package/src/shade/renderer/grass/press/shader_grass_press_mark.js +5 -3
- package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_draw.d.ts +49 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_draw.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_draw.js +144 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_splat.d.ts +71 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_splat.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/avboit_unlit_splat.js +152 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_unlit_varyings.d.ts +21 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_unlit_varyings.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_unlit_varyings.js +30 -0
- package/src/shade/renderer/rasterize/native/avboit/graph_avboit_unlit.d.ts +101 -0
- package/src/shade/renderer/rasterize/native/avboit/graph_avboit_unlit.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/native/avboit/graph_avboit_unlit.js +306 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.d.ts +9 -0
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.js +11 -4
- package/src/shade/renderer/trails/GPUTrailAVBOITChannel.d.ts +69 -0
- package/src/shade/renderer/trails/GPUTrailAVBOITChannel.d.ts.map +1 -0
- package/src/shade/renderer/trails/GPUTrailAVBOITChannel.js +129 -0
- package/src/shade/renderer/trails/GPUTrailSystem.d.ts +164 -0
- package/src/shade/renderer/trails/GPUTrailSystem.d.ts.map +1 -0
- package/src/shade/renderer/trails/GPUTrailSystem.js +725 -0
- package/src/shade/renderer/trails/data/TRAIL_DATABASE_SPEC.d.ts +14 -0
- package/src/shade/renderer/trails/data/TRAIL_DATABASE_SPEC.d.ts.map +1 -0
- package/src/shade/renderer/trails/data/TRAIL_DATABASE_SPEC.js +38 -0
- package/src/shade/renderer/trails/data/TRAIL_KNOT.d.ts +66 -0
- package/src/shade/renderer/trails/data/TRAIL_KNOT.d.ts.map +1 -0
- package/src/shade/renderer/trails/data/TRAIL_KNOT.js +154 -0
- package/src/shade/renderer/trails/data/TRAIL_RECORD.d.ts +19 -0
- package/src/shade/renderer/trails/data/TRAIL_RECORD.d.ts.map +1 -0
- package/src/shade/renderer/trails/data/TRAIL_RECORD.js +68 -0
- package/src/shade/renderer/trails/data/TRAIL_STATE.d.ts +18 -0
- package/src/shade/renderer/trails/data/TRAIL_STATE.d.ts.map +1 -0
- package/src/shade/renderer/trails/data/TRAIL_STATE.js +43 -0
- package/src/shade/renderer/trails/graph_trails.d.ts +58 -0
- package/src/shade/renderer/trails/graph_trails.d.ts.map +1 -0
- package/src/shade/renderer/trails/graph_trails.js +155 -0
- package/src/shade/renderer/trails/graph_trails_avboit.d.ts +126 -0
- package/src/shade/renderer/trails/graph_trails_avboit.d.ts.map +1 -0
- package/src/shade/renderer/trails/graph_trails_avboit.js +300 -0
- package/src/shade/renderer/trails/runtime/TrailFadeCurves.d.ts +56 -0
- package/src/shade/renderer/trails/runtime/TrailFadeCurves.d.ts.map +1 -0
- package/src/shade/renderer/trails/runtime/TrailFadeCurves.js +220 -0
- package/src/shade/renderer/trails/runtime/TrailKnotPool.d.ts +49 -0
- package/src/shade/renderer/trails/runtime/TrailKnotPool.d.ts.map +1 -0
- package/src/shade/renderer/trails/runtime/TrailKnotPool.js +162 -0
- package/src/shade/renderer/trails/runtime/TrailParameters.d.ts +52 -0
- package/src/shade/renderer/trails/runtime/TrailParameters.d.ts.map +1 -0
- package/src/shade/renderer/trails/runtime/TrailParameters.js +126 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_frame_transport.d.ts +23 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_frame_transport.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_frame_transport.js +57 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_knot_alpha.d.ts +14 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_knot_alpha.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_knot_alpha.js +40 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_seed.d.ts +18 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_seed.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_seed.js +64 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_segment.d.ts +14 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_segment.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_segment.js +51 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_update.d.ts +48 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_update.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_update.js +197 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_vertex.d.ts +34 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_vertex.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/chunk_trail_vertex.js +111 -0
- package/src/shade/renderer/trails/shaders/shader_trail_avboit.d.ts +24 -0
- package/src/shade/renderer/trails/shaders/shader_trail_avboit.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/shader_trail_avboit.js +78 -0
- package/src/shade/renderer/trails/shaders/shader_trail_avboit_segments.d.ts +23 -0
- package/src/shade/renderer/trails/shaders/shader_trail_avboit_segments.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/shader_trail_avboit_segments.js +130 -0
- package/src/shade/renderer/trails/shaders/shader_trail_draw_args.d.ts +29 -0
- package/src/shade/renderer/trails/shaders/shader_trail_draw_args.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/shader_trail_draw_args.js +78 -0
- package/src/shade/renderer/trails/shaders/shader_trail_seed.d.ts +18 -0
- package/src/shade/renderer/trails/shaders/shader_trail_seed.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/shader_trail_seed.js +79 -0
- package/src/shade/renderer/trails/shaders/shader_trail_update.d.ts +20 -0
- package/src/shade/renderer/trails/shaders/shader_trail_update.d.ts.map +1 -0
- package/src/shade/renderer/trails/shaders/shader_trail_update.js +101 -0
- package/src/engine/graphics/ecs/trail3d/advance_trail.d.ts +0 -18
- package/src/engine/graphics/ecs/trail3d/advance_trail.d.ts.map +0 -1
- package/src/engine/graphics/ecs/trail3d/advance_trail.js +0 -42
- package/src/engine/graphics/ecs/trail3d/seed_trail_tube.d.ts +0 -14
- package/src/engine/graphics/ecs/trail3d/seed_trail_tube.d.ts.map +0 -1
- package/src/engine/graphics/ecs/trail3d/seed_trail_tube.js +0 -75
- package/src/engine/graphics/ecs/trail3d/trail_head_position.d.ts +0 -17
- package/src/engine/graphics/ecs/trail3d/trail_head_position.d.ts.map +0 -1
- package/src/engine/graphics/ecs/trail3d/trail_head_position.js +0 -19
- package/src/engine/graphics/trail/tube/TubeX.d.ts +0 -186
- package/src/engine/graphics/trail/tube/TubeX.d.ts.map +0 -1
- package/src/engine/graphics/trail/tube/TubeX.js +0 -575
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts +0 -16
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts.map +0 -1
- package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +0 -34
- package/src/engine/graphics/trail/tube/tube_attributes_spec.d.ts +0 -27
- package/src/engine/graphics/trail/tube/tube_attributes_spec.d.ts.map +0 -1
- package/src/engine/graphics/trail/tube/tube_attributes_spec.js +0 -70
- package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts +0 -38
- package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts.map +0 -1
- package/src/engine/graphics3/trail/trail_tube_write_geometry.js +0 -119
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import { ALLOCATOR_NO_SPACE } from "../../../core/binary/allocator/OffsetAllocator.js";
|
|
3
|
+
import { GPUDatabase } from "../buffer/table/GPUDatabase.js";
|
|
4
|
+
import { ShadeGPUCommandContext } from "../../device/ShadeGPUCommandContext.js";
|
|
5
|
+
import { TRAIL_KNOT_WORDS, TRAIL_TICKS_PER_SECOND, trail_knot_write } from "./data/TRAIL_KNOT.js";
|
|
6
|
+
import { TRAIL_DATABASE_INITIAL_SIZE, TRAIL_DATABASE_SPEC, TRAIL_STATE_TABLE, TRAIL_TABLE } from "./data/TRAIL_DATABASE_SPEC.js";
|
|
7
|
+
import { TRAIL_FLAG } from "./data/TRAIL_RECORD.js";
|
|
8
|
+
import { TRAIL_STATE_UNSEEDED } from "./data/TRAIL_STATE.js";
|
|
9
|
+
import { TrailFadeCurves } from "./runtime/TrailFadeCurves.js";
|
|
10
|
+
import { TrailKnotPool } from "./runtime/TrailKnotPool.js";
|
|
11
|
+
import { TrailParameters } from "./runtime/TrailParameters.js";
|
|
12
|
+
import { graph_trails_simulate } from "./graph_trails.js";
|
|
13
|
+
import { TRAIL_FRAME_STRUCT } from "./shaders/chunk_trail_vertex.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Ticks past which an age is no longer exact: half the u32 range, so `max_age + a frame` stays under the
|
|
17
|
+
* wrap with room for the frame.
|
|
18
|
+
* @type {number}
|
|
19
|
+
*/
|
|
20
|
+
const TICK_AGE_LIMIT = 2 ** 31;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Scratch for an authored knot's frame and position.
|
|
24
|
+
* @type {Float64Array}
|
|
25
|
+
*/
|
|
26
|
+
const scratch_frame = new Float64Array(6);
|
|
27
|
+
const scratch_position = new Float64Array(3);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Everything the runtime holds for one trail. Plain bookkeeping: the trail's look is its
|
|
31
|
+
* {@link TrailParameters}, which the owner keeps.
|
|
32
|
+
*/
|
|
33
|
+
export class GPUTrailContext {
|
|
34
|
+
/** @type {number} row in the trail and state tables */
|
|
35
|
+
row = -1;
|
|
36
|
+
|
|
37
|
+
/** @type {number} the `transforms` row the head follows */
|
|
38
|
+
node = 0;
|
|
39
|
+
|
|
40
|
+
/** @type {import("../../../core/binary/allocator/OffsetAllocator.js").Allocation|null} the knot run */
|
|
41
|
+
knots = null;
|
|
42
|
+
|
|
43
|
+
/** @type {import("./runtime/TrailFadeCurves.js").TrailFadeCurve|null} */
|
|
44
|
+
fade = null;
|
|
45
|
+
|
|
46
|
+
/** @type {number} index of the trail's radial class */
|
|
47
|
+
draw_class = -1;
|
|
48
|
+
|
|
49
|
+
/** @type {number} what the record's `seed_epoch` says */
|
|
50
|
+
epoch = 1;
|
|
51
|
+
|
|
52
|
+
/** @type {boolean} the pending seed is a `clear()`, which keeps the trail's birth and frames */
|
|
53
|
+
reseed = false;
|
|
54
|
+
|
|
55
|
+
/** @type {TrailParameters} what was last staged */
|
|
56
|
+
staged = new TrailParameters();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A radial count in use, and the trails using it: a draw class, one indexed draw per pass.
|
|
61
|
+
*/
|
|
62
|
+
export class TrailDrawClass {
|
|
63
|
+
/** @type {number} */
|
|
64
|
+
radial = 0;
|
|
65
|
+
|
|
66
|
+
/** @type {number} trails in the class */
|
|
67
|
+
references = 0;
|
|
68
|
+
|
|
69
|
+
/** @type {number} the most segments the class's trails can show: their knots less one each */
|
|
70
|
+
segments = 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The frame uniform the trails' vertex stage reads: the clock, in ticks.
|
|
75
|
+
* @type {number}
|
|
76
|
+
*/
|
|
77
|
+
const FRAME_UNIFORM_BYTES = TRAIL_FRAME_STRUCT.size;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The GPU trails of one scene context: the trail and state tables, the knot pool, the fade curves, and
|
|
81
|
+
* the clock the knots are aged by.
|
|
82
|
+
*
|
|
83
|
+
* **The CPU registers and stages; the GPU simulates.** {@link add} claims a row, a run of knots and the
|
|
84
|
+
* trail's fade curve and radial class, and asks for a seed; {@link stage} writes the record when the
|
|
85
|
+
* trail's parameters change; {@link remove} lets everything go. No knot is written on the CPU, except
|
|
86
|
+
* the knots of an authored trail, which are uploaded once when it is added. Everything per frame — the
|
|
87
|
+
* head, commits, aging — is the GPU's.
|
|
88
|
+
*
|
|
89
|
+
* **Time is the owner's.** {@link advance} moves the clock by the simulation time the owner was given,
|
|
90
|
+
* so a paused game holds its trails still, and the knots age by ticks of it (`TRAIL_TICKS_PER_SECOND`).
|
|
91
|
+
*/
|
|
92
|
+
export class GPUTrailSystem {
|
|
93
|
+
/**
|
|
94
|
+
* @type {import("../GraphicsContext.js").GraphicsContext}
|
|
95
|
+
*/
|
|
96
|
+
#graphics;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @type {GPUDatabase}
|
|
100
|
+
*/
|
|
101
|
+
#database;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @type {TrailKnotPool}
|
|
105
|
+
*/
|
|
106
|
+
#knots;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @type {TrailFadeCurves}
|
|
110
|
+
*/
|
|
111
|
+
#fades;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @type {Map<number, GPUTrailContext>} by row
|
|
115
|
+
*/
|
|
116
|
+
#trails = new Map();
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Radial classes by radial count.
|
|
120
|
+
* @type {Map<number, TrailDrawClass>}
|
|
121
|
+
*/
|
|
122
|
+
#classes_by_radial = new Map();
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Radial classes by index; a hole is a class no trail uses, free to be handed out again.
|
|
126
|
+
* @type {(TrailDrawClass|undefined)[]}
|
|
127
|
+
*/
|
|
128
|
+
#classes = [];
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Trails the GPU is to seed in the next frame recorded.
|
|
132
|
+
* @type {GPUTrailContext[]}
|
|
133
|
+
*/
|
|
134
|
+
#seeds = [];
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* A static index buffer per radial count: the triangles of one segment, two rings of `radial + 1`
|
|
138
|
+
* vertices.
|
|
139
|
+
* @type {Map<number, GPUBuffer>}
|
|
140
|
+
*/
|
|
141
|
+
#index_buffers = new Map();
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The clock as the vertex stage reads it, written once a frame.
|
|
145
|
+
* @type {GPUBuffer}
|
|
146
|
+
*/
|
|
147
|
+
#frame_uniform;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @type {Uint32Array}
|
|
151
|
+
*/
|
|
152
|
+
#frame_words = new Uint32Array(FRAME_UNIFORM_BYTES / 4);
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Seconds of simulation time the clock has been advanced by. A double, so the tick count taken from
|
|
156
|
+
* it does not drift however long the game runs.
|
|
157
|
+
* @type {number}
|
|
158
|
+
*/
|
|
159
|
+
#time = 0;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @type {Object}
|
|
163
|
+
*/
|
|
164
|
+
#record = {
|
|
165
|
+
node: 0,
|
|
166
|
+
knot_base: 0,
|
|
167
|
+
knot_count: 0,
|
|
168
|
+
radial: 0,
|
|
169
|
+
flags: 0,
|
|
170
|
+
seed_epoch: 0,
|
|
171
|
+
commit_ticks: 0,
|
|
172
|
+
max_age_ticks: 0,
|
|
173
|
+
max_age: 0,
|
|
174
|
+
width: 0,
|
|
175
|
+
spawn_distance: 0,
|
|
176
|
+
fade_first: 0,
|
|
177
|
+
fade_count: 0,
|
|
178
|
+
draw_class: 0,
|
|
179
|
+
offset: new Float32Array(3),
|
|
180
|
+
color: new Float32Array(4),
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @param {import("../GraphicsContext.js").GraphicsContext} graphics
|
|
185
|
+
*/
|
|
186
|
+
constructor(graphics) {
|
|
187
|
+
assert.defined(graphics, 'graphics');
|
|
188
|
+
|
|
189
|
+
const device = graphics.device;
|
|
190
|
+
|
|
191
|
+
this.#graphics = graphics;
|
|
192
|
+
this.#database = new GPUDatabase({
|
|
193
|
+
device,
|
|
194
|
+
definition: TRAIL_DATABASE_SPEC,
|
|
195
|
+
label: "Trails",
|
|
196
|
+
initial_data_size_bytes: TRAIL_DATABASE_INITIAL_SIZE,
|
|
197
|
+
});
|
|
198
|
+
this.#knots = new TrailKnotPool(device);
|
|
199
|
+
this.#fades = new TrailFadeCurves(device);
|
|
200
|
+
this.#frame_uniform = device.createBuffer({
|
|
201
|
+
label: "Trails / frame",
|
|
202
|
+
size: FRAME_UNIFORM_BYTES,
|
|
203
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** @returns {import("../GraphicsContext.js").GraphicsContext} */
|
|
208
|
+
get graphics() {
|
|
209
|
+
return this.#graphics;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** @returns {GPUBuffer} the clock as the vertex stage reads it */
|
|
213
|
+
get frame_uniform() {
|
|
214
|
+
return this.#frame_uniform;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* The draw classes by index, a hole where no trail uses one.
|
|
219
|
+
* @returns {(TrailDrawClass|undefined)[]}
|
|
220
|
+
*/
|
|
221
|
+
get draw_classes() {
|
|
222
|
+
return this.#classes;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* The triangles of one segment of a tube of `radial` segments: two rings of `radial + 1` vertices,
|
|
227
|
+
* the head-ward ring first. Both faces are drawn, so the winding carries nothing.
|
|
228
|
+
*
|
|
229
|
+
* @param {number} radial
|
|
230
|
+
* @returns {GPUBuffer} `uint32` indices, `6 × radial` of them
|
|
231
|
+
*/
|
|
232
|
+
index_buffer(radial) {
|
|
233
|
+
let buffer = this.#index_buffers.get(radial);
|
|
234
|
+
|
|
235
|
+
if (buffer !== undefined) {
|
|
236
|
+
return buffer;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const indices = new Uint32Array(radial * 6);
|
|
240
|
+
const ring = radial + 1;
|
|
241
|
+
|
|
242
|
+
for (let r = 0; r < radial; r++) {
|
|
243
|
+
const a = r;
|
|
244
|
+
const b = ring + r;
|
|
245
|
+
const c = ring + r + 1;
|
|
246
|
+
const d = r + 1;
|
|
247
|
+
|
|
248
|
+
indices.set([a, b, d, b, c, d], r * 6);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const device = this.#graphics.device;
|
|
252
|
+
|
|
253
|
+
buffer = device.createBuffer({
|
|
254
|
+
label: `Trails / indices / ${radial}`,
|
|
255
|
+
size: indices.byteLength,
|
|
256
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
device.queue.writeBuffer(buffer, 0, indices.buffer, 0, indices.byteLength);
|
|
260
|
+
|
|
261
|
+
this.#index_buffers.set(radial, buffer);
|
|
262
|
+
|
|
263
|
+
return buffer;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** @returns {GPUDatabase} the trail and state tables */
|
|
267
|
+
get database() {
|
|
268
|
+
return this.#database;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** @returns {import("../buffer/table/GPUTypedTable.js").GPUTypedTable} */
|
|
272
|
+
get table() {
|
|
273
|
+
return this.#database.get(TRAIL_TABLE);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** @returns {import("../buffer/table/GPUTypedTable.js").GPUTypedTable} */
|
|
277
|
+
get state_table() {
|
|
278
|
+
return this.#database.get(TRAIL_STATE_TABLE);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** @returns {TrailKnotPool} */
|
|
282
|
+
get knots() {
|
|
283
|
+
return this.#knots;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** @returns {TrailFadeCurves} */
|
|
287
|
+
get fades() {
|
|
288
|
+
return this.#fades;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** @returns {number} trails registered */
|
|
292
|
+
get count() {
|
|
293
|
+
return this.#trails.size;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* @returns {number} the clock, in ticks, wrapped to a u32
|
|
298
|
+
*/
|
|
299
|
+
get now() {
|
|
300
|
+
return Math.floor(this.#time * TRAIL_TICKS_PER_SECOND) >>> 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Radial counts by class index, a hole where no trail uses one.
|
|
305
|
+
* @returns {(number|undefined)[]}
|
|
306
|
+
*/
|
|
307
|
+
get class_radials() {
|
|
308
|
+
return this.#classes.map(c => c === undefined ? undefined : c.radial);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* @returns {GPUTrailContext[]} trails waiting for the GPU to seed them; taken by the frame that seeds them
|
|
313
|
+
*/
|
|
314
|
+
get pending_seeds() {
|
|
315
|
+
return this.#seeds;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Move the clock on.
|
|
320
|
+
*
|
|
321
|
+
* @param {number} seconds simulation time since the last call
|
|
322
|
+
*/
|
|
323
|
+
advance(seconds) {
|
|
324
|
+
assert.greaterThanOrEqual(seconds, 0, 'seconds');
|
|
325
|
+
|
|
326
|
+
this.#time += seconds;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Register a trail whose head follows `node`.
|
|
331
|
+
*
|
|
332
|
+
* @param {TrailParameters} parameters
|
|
333
|
+
* @param {number} node the `transforms` row of the trail's entity
|
|
334
|
+
* @returns {GPUTrailContext|null} null when the knot pool cannot house the trail on this device
|
|
335
|
+
*/
|
|
336
|
+
add(parameters, node) {
|
|
337
|
+
assert.isNonNegativeInteger(node, 'node');
|
|
338
|
+
assert.notNull(parameters.fade, 'parameters.fade');
|
|
339
|
+
|
|
340
|
+
const knots = this.#knots.allocate(parameters.knot_count);
|
|
341
|
+
|
|
342
|
+
if (knots.offset === ALLOCATOR_NO_SPACE) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const context = new GPUTrailContext();
|
|
347
|
+
|
|
348
|
+
context.node = node;
|
|
349
|
+
context.knots = knots;
|
|
350
|
+
context.fade = this.#fades.acquire(parameters.fade);
|
|
351
|
+
context.draw_class = this.#acquire_class(parameters.radial, parameters.knot_count - 1);
|
|
352
|
+
context.staged.copy(parameters);
|
|
353
|
+
|
|
354
|
+
context.row = this.table.add(this.#write_record(context, parameters));
|
|
355
|
+
|
|
356
|
+
this.#trails.set(context.row, context);
|
|
357
|
+
|
|
358
|
+
this.#seed(context);
|
|
359
|
+
|
|
360
|
+
return context;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Bring a trail's record up to date with `parameters`. A change to a build-time parameter rebuilds
|
|
365
|
+
* the trail: a new knot run, and a seed.
|
|
366
|
+
*
|
|
367
|
+
* @param {GPUTrailContext} context
|
|
368
|
+
* @param {TrailParameters} parameters
|
|
369
|
+
* @returns {boolean} false when a rebuild could not be housed; the trail keeps its old build
|
|
370
|
+
*/
|
|
371
|
+
stage(context, parameters) {
|
|
372
|
+
assert.equal(this.#trails.get(context.row), context, 'the trail is not registered here');
|
|
373
|
+
|
|
374
|
+
const staged = context.staged;
|
|
375
|
+
|
|
376
|
+
if (!staged.same_build(parameters)) {
|
|
377
|
+
if (staged.knot_count !== parameters.knot_count) {
|
|
378
|
+
const knots = this.#knots.allocate(parameters.knot_count);
|
|
379
|
+
|
|
380
|
+
if (knots.offset === ALLOCATOR_NO_SPACE) {
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
this.#knots.free(context.knots);
|
|
385
|
+
context.knots = knots;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// acquired before the old is released, so a class the trail stays in is never let go
|
|
389
|
+
const draw_class = this.#acquire_class(parameters.radial, parameters.knot_count - 1);
|
|
390
|
+
|
|
391
|
+
this.#release_class(context.draw_class, staged.knot_count - 1);
|
|
392
|
+
context.draw_class = draw_class;
|
|
393
|
+
|
|
394
|
+
context.reseed = false;
|
|
395
|
+
context.epoch++;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (!context.fade.curve.equals(parameters.fade)) {
|
|
399
|
+
const fade = this.#fades.acquire(parameters.fade);
|
|
400
|
+
|
|
401
|
+
this.#fades.release(context.fade);
|
|
402
|
+
context.fade = fade;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const rebuild = !staged.same_build(parameters);
|
|
406
|
+
|
|
407
|
+
staged.copy(parameters);
|
|
408
|
+
|
|
409
|
+
this.table.set(context.row, this.#write_record(context, parameters));
|
|
410
|
+
|
|
411
|
+
if (rebuild) {
|
|
412
|
+
this.#seed(context);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return true;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Point a trail at another `transforms` row.
|
|
420
|
+
*
|
|
421
|
+
* @param {GPUTrailContext} context
|
|
422
|
+
* @param {number} node
|
|
423
|
+
*/
|
|
424
|
+
set_node(context, node) {
|
|
425
|
+
assert.isNonNegativeInteger(node, 'node');
|
|
426
|
+
|
|
427
|
+
if (context.node === node) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
context.node = node;
|
|
432
|
+
|
|
433
|
+
this.table.set(context.row, this.#write_record(context, context.staged));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Expire every knot and collapse the tube onto the head at the next frame, keeping the trail's birth
|
|
438
|
+
* and its knots' frames: `Trail3D.clear()`.
|
|
439
|
+
*
|
|
440
|
+
* @param {GPUTrailContext} context
|
|
441
|
+
*/
|
|
442
|
+
clear(context) {
|
|
443
|
+
context.epoch++;
|
|
444
|
+
context.reseed = true;
|
|
445
|
+
|
|
446
|
+
this.table.set(context.row, this.#write_record(context, context.staged));
|
|
447
|
+
|
|
448
|
+
if (!this.#seeds.includes(context)) {
|
|
449
|
+
this.#seeds.push(context);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* @param {GPUTrailContext} context
|
|
455
|
+
*/
|
|
456
|
+
remove(context) {
|
|
457
|
+
assert.equal(this.#trails.get(context.row), context, 'the trail is not registered here');
|
|
458
|
+
|
|
459
|
+
this.table.remove(context.row);
|
|
460
|
+
this.state_table.remove(context.row);
|
|
461
|
+
|
|
462
|
+
this.#knots.free(context.knots);
|
|
463
|
+
this.#fades.release(context.fade);
|
|
464
|
+
this.#release_class(context.draw_class, context.staged.knot_count - 1);
|
|
465
|
+
|
|
466
|
+
const pending = this.#seeds.indexOf(context);
|
|
467
|
+
|
|
468
|
+
if (pending !== -1) {
|
|
469
|
+
this.#seeds.splice(pending, 1);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
this.#trails.delete(context.row);
|
|
473
|
+
|
|
474
|
+
context.row = -1;
|
|
475
|
+
context.knots = null;
|
|
476
|
+
context.fade = null;
|
|
477
|
+
context.draw_class = -1;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Send what changed since the last frame: the records and states staged, the fade curves.
|
|
482
|
+
*
|
|
483
|
+
* @returns {GPUBuffer} the fade key buffer, as it now is on the device
|
|
484
|
+
*/
|
|
485
|
+
upload() {
|
|
486
|
+
const database = this.#database;
|
|
487
|
+
|
|
488
|
+
if (database.has_pending_upload) {
|
|
489
|
+
const cmd = ShadeGPUCommandContext.create(this.#graphics, "trails/upload");
|
|
490
|
+
|
|
491
|
+
database.update(cmd);
|
|
492
|
+
|
|
493
|
+
cmd.finish();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return this.#fades.flush();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Take the trails waiting for the GPU to seed them, as rows.
|
|
501
|
+
*
|
|
502
|
+
* @returns {Uint32Array}
|
|
503
|
+
*/
|
|
504
|
+
take_seeds() {
|
|
505
|
+
const seeds = this.#seeds;
|
|
506
|
+
const rows = new Uint32Array(seeds.length);
|
|
507
|
+
|
|
508
|
+
for (let i = 0; i < seeds.length; i++) {
|
|
509
|
+
rows[i] = seeds[i].row;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
seeds.length = 0;
|
|
513
|
+
|
|
514
|
+
return rows;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Upload what changed and record one frame of the trails into `graph`: the seeds asked for, the
|
|
519
|
+
* update, and the scan of live segments the per-view passes dispatch from. Once per frame, after
|
|
520
|
+
* the hierarchy has composed this frame's matrices.
|
|
521
|
+
*
|
|
522
|
+
* @param {import("../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph} graph
|
|
523
|
+
* @param {number} scene_database graph handle of the scene database
|
|
524
|
+
* @returns {import("./graph_trails.js").TrailSimulationHandles}
|
|
525
|
+
*/
|
|
526
|
+
record_simulation(graph, scene_database) {
|
|
527
|
+
const fade_keys = this.upload();
|
|
528
|
+
|
|
529
|
+
// a persistent buffer: written ahead of the frame's commands, read by its draws
|
|
530
|
+
this.#frame_words[0] = this.now;
|
|
531
|
+
this.#graphics.device.queue.writeBuffer(this.#frame_uniform, 0, this.#frame_words.buffer, 0, FRAME_UNIFORM_BYTES);
|
|
532
|
+
|
|
533
|
+
return graph_trails_simulate({
|
|
534
|
+
graph,
|
|
535
|
+
scene_database,
|
|
536
|
+
runtime: this,
|
|
537
|
+
fade_keys,
|
|
538
|
+
seeds: this.take_seeds(),
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
destroy() {
|
|
543
|
+
this.#database.destroy();
|
|
544
|
+
this.#knots.destroy();
|
|
545
|
+
this.#fades.destroy();
|
|
546
|
+
this.#frame_uniform.destroy();
|
|
547
|
+
|
|
548
|
+
for (const buffer of this.#index_buffers.values()) {
|
|
549
|
+
buffer.destroy();
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
this.#index_buffers.clear();
|
|
553
|
+
this.#trails.clear();
|
|
554
|
+
this.#seeds.length = 0;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Queue a GPU seed, or for an authored trail lay its knots down now.
|
|
559
|
+
*
|
|
560
|
+
* @param {GPUTrailContext} context
|
|
561
|
+
*/
|
|
562
|
+
#seed(context) {
|
|
563
|
+
const parameters = context.staged;
|
|
564
|
+
|
|
565
|
+
if (parameters.authored === null) {
|
|
566
|
+
this.state_table.set(context.row, TRAIL_STATE_UNSEEDED);
|
|
567
|
+
|
|
568
|
+
if (!this.#seeds.includes(context)) {
|
|
569
|
+
this.#seeds.push(context);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const authored = parameters.authored;
|
|
576
|
+
const count = authored.count;
|
|
577
|
+
const now = this.now;
|
|
578
|
+
|
|
579
|
+
assert.equal(count, parameters.knot_count, 'an authored trail has as many knots as its layout');
|
|
580
|
+
|
|
581
|
+
const words = new Uint32Array(count * TRAIL_KNOT_WORDS);
|
|
582
|
+
const floats = new Float32Array(words.buffer);
|
|
583
|
+
|
|
584
|
+
for (let i = 0; i < count; i++) {
|
|
585
|
+
authored.getPosition(scratch_position, i);
|
|
586
|
+
authored.getFrame(scratch_frame, i);
|
|
587
|
+
|
|
588
|
+
const age_ticks = Math.round(authored.getAge(i) * TRAIL_TICKS_PER_SECOND);
|
|
589
|
+
|
|
590
|
+
trail_knot_write(
|
|
591
|
+
words, floats, i,
|
|
592
|
+
scratch_position[0], scratch_position[1], scratch_position[2],
|
|
593
|
+
(now - age_ticks) >>> 0,
|
|
594
|
+
scratch_frame,
|
|
595
|
+
authored.getThickness(i),
|
|
596
|
+
context.row
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
this.#knots.write(context.knots.offset, words);
|
|
601
|
+
|
|
602
|
+
// laid down here, so seeded already: every knot is a candidate, the head is the first
|
|
603
|
+
this.state_table.set(context.row, {
|
|
604
|
+
head: 0,
|
|
605
|
+
live: count,
|
|
606
|
+
last_commit: now,
|
|
607
|
+
birth: now,
|
|
608
|
+
seeded_epoch: context.epoch,
|
|
609
|
+
distance: 0,
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
const pending = this.#seeds.indexOf(context);
|
|
613
|
+
|
|
614
|
+
if (pending !== -1) {
|
|
615
|
+
this.#seeds.splice(pending, 1);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* @param {GPUTrailContext} context
|
|
621
|
+
* @param {TrailParameters} parameters
|
|
622
|
+
* @returns {Object} the record, for the table to copy
|
|
623
|
+
*/
|
|
624
|
+
#write_record(context, parameters) {
|
|
625
|
+
const record = this.#record;
|
|
626
|
+
|
|
627
|
+
const max_age_ticks = Math.floor(parameters.max_age * TRAIL_TICKS_PER_SECOND);
|
|
628
|
+
|
|
629
|
+
assert.lessThan(max_age_ticks, TICK_AGE_LIMIT, 'maxAge past what a u32 tick count ages exactly');
|
|
630
|
+
|
|
631
|
+
let flags = 0;
|
|
632
|
+
|
|
633
|
+
if (parameters.spawning) {
|
|
634
|
+
flags |= TRAIL_FLAG.SPAWNING;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (parameters.distance_mode) {
|
|
638
|
+
flags |= TRAIL_FLAG.DISTANCE;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (parameters.authored !== null) {
|
|
642
|
+
flags |= TRAIL_FLAG.AUTHORED;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (parameters.warmup) {
|
|
646
|
+
flags |= TRAIL_FLAG.WARMUP;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (parameters.fade.evaluate(1) * parameters.color[3] > 0) {
|
|
650
|
+
flags |= TRAIL_FLAG.TAIL_VISIBLE;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (context.reseed) {
|
|
654
|
+
flags |= TRAIL_FLAG.RESEED;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
record.node = context.node;
|
|
658
|
+
record.knot_base = context.knots.offset;
|
|
659
|
+
record.knot_count = parameters.knot_count;
|
|
660
|
+
record.radial = parameters.radial;
|
|
661
|
+
record.flags = flags;
|
|
662
|
+
record.seed_epoch = context.epoch;
|
|
663
|
+
record.commit_ticks = Math.floor(parameters.max_age / parameters.knot_count * TRAIL_TICKS_PER_SECOND);
|
|
664
|
+
record.max_age_ticks = max_age_ticks;
|
|
665
|
+
record.max_age = parameters.max_age;
|
|
666
|
+
record.width = parameters.width;
|
|
667
|
+
record.spawn_distance = parameters.spawn_distance;
|
|
668
|
+
record.fade_first = context.fade.first;
|
|
669
|
+
record.fade_count = context.fade.count;
|
|
670
|
+
record.draw_class = context.draw_class;
|
|
671
|
+
record.offset.set(parameters.offset);
|
|
672
|
+
record.color.set(parameters.color);
|
|
673
|
+
|
|
674
|
+
return record;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* @param {number} radial
|
|
679
|
+
* @param {number} segments the trail's knots less one
|
|
680
|
+
* @returns {number} the class index
|
|
681
|
+
*/
|
|
682
|
+
#acquire_class(radial, segments) {
|
|
683
|
+
assert.greaterThanOrEqual(radial, 3, 'a tube ring needs at least 3 radial segments');
|
|
684
|
+
|
|
685
|
+
let draw_class = this.#classes_by_radial.get(radial);
|
|
686
|
+
|
|
687
|
+
if (draw_class === undefined) {
|
|
688
|
+
draw_class = new TrailDrawClass();
|
|
689
|
+
draw_class.radial = radial;
|
|
690
|
+
|
|
691
|
+
this.#classes_by_radial.set(radial, draw_class);
|
|
692
|
+
|
|
693
|
+
let index = this.#classes.indexOf(undefined);
|
|
694
|
+
|
|
695
|
+
if (index === -1) {
|
|
696
|
+
index = this.#classes.length;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
this.#classes[index] = draw_class;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
draw_class.references++;
|
|
703
|
+
draw_class.segments += segments;
|
|
704
|
+
|
|
705
|
+
return this.#classes.indexOf(draw_class);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* @param {number} index
|
|
710
|
+
* @param {number} segments the trail's knots less one, as it was acquired with
|
|
711
|
+
*/
|
|
712
|
+
#release_class(index, segments) {
|
|
713
|
+
const draw_class = this.#classes[index];
|
|
714
|
+
|
|
715
|
+
draw_class.references--;
|
|
716
|
+
draw_class.segments -= segments;
|
|
717
|
+
|
|
718
|
+
if (draw_class.references > 0) {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
this.#classes_by_radial.delete(draw_class.radial);
|
|
723
|
+
this.#classes[index] = undefined;
|
|
724
|
+
}
|
|
725
|
+
}
|