From 568288641ec9607ef3728ab07bd8205ded0d1fad Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Wed, 30 Sep 2026 21:57:11 +0300 Subject: [PATCH 001/141] The showcase shows what the bridge gained in 0.8.4 Three pages for flame_flutter3d 0.8.4. Two keyboard layouts claim seats by pressing, keys read from the keyboard rather than the focus. Ninety-six simulated monsters take slots in one instanced batch and each chases the nearer of two players, drawn between their steps. A game moves between two level scenes with replaceScene3d while a view camera keeps the whole party in frame. Each page has a guide and a test that fails when its feature is taken away. The showcase moves to 0.8.4+1 so its pages may name that release. --- ARCHITECTURE.md | 2 +- README.md | 2 +- .../flutter3d_showcase/lib/catalog/flame.dart | 36 +++ .../lib/pages/flame/flame_horde.dart | 269 +++++++++++++++++ .../lib/pages/flame/flame_horde.md | 48 +++ .../lib/pages/flame/flame_level_scene.dart | 276 ++++++++++++++++++ .../lib/pages/flame/flame_level_scene.md | 39 +++ .../lib/pages/flame/flame_seats.dart | 259 ++++++++++++++++ .../lib/pages/flame/flame_seats.md | 41 +++ .../lib/pages/flame/registry.dart | 6 + apps/flutter3d_showcase/pubspec.yaml | 2 +- .../test/pages/flame_test.dart | 187 +++++++++++- site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- 14 files changed, 1168 insertions(+), 9 deletions(-) create mode 100644 apps/flutter3d_showcase/lib/pages/flame/flame_horde.dart create mode 100644 apps/flutter3d_showcase/lib/pages/flame/flame_horde.md create mode 100644 apps/flutter3d_showcase/lib/pages/flame/flame_level_scene.dart create mode 100644 apps/flutter3d_showcase/lib/pages/flame/flame_level_scene.md create mode 100644 apps/flutter3d_showcase/lib/pages/flame/flame_seats.dart create mode 100644 apps/flutter3d_showcase/lib/pages/flame/flame_seats.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 235ff0b20..d68be0670 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2688,7 +2688,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10786 tests** across 41 packages and 10 applications | +| Unit tests | **10789 tests** across 41 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index f4f7fd4e7..0695c51c3 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10786 tests across forty-one packages and nine applications. The +There are 10789 tests across forty-one packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/apps/flutter3d_showcase/lib/catalog/flame.dart b/apps/flutter3d_showcase/lib/catalog/flame.dart index 493f93578..d19e9c5b0 100644 --- a/apps/flutter3d_showcase/lib/catalog/flame.dart +++ b/apps/flutter3d_showcase/lib/catalog/flame.dart @@ -107,4 +107,40 @@ const List flameFeatures = [ evidenceFile: 'packages/flame_flutter3d/CHANGELOG.md', packages: ['flame_flutter3d', 'flutter3d_particles'], ), + Feature( + id: 'flame-seats', + title: 'Two layouts, and whoever presses first is player one', + category: Category.flame, + summary: + 'PlayerSeats over two keyboard layouts read from the keyboard ' + 'itself: a layout joins on its own key, in the order people press.', + since: '0.8.4', + evidence: 'lets players claim one by pressing, in the order they join', + evidenceFile: 'packages/flame_flutter3d/CHANGELOG.md', + packages: ['flame_flutter3d', 'flutter3d_game', 'flutter3d_sim'], + ), + Feature( + id: 'flame-horde', + title: 'Ninety-six monsters in one draw, chasing two players', + category: Category.flame, + summary: + 'Simulated actors drawn as slots of one instanced batch, stepped ' + 'towards two players at once, each going for the one it can reach.', + since: '0.8.4', + evidence: 'draws a simulated actor as a slot of a shared', + evidenceFile: 'packages/flame_flutter3d/CHANGELOG.md', + packages: ['flame_flutter3d', 'flutter3d_sim'], + ), + Feature( + id: 'flame-level-scene', + title: 'A level is a scene, and the camera frames the whole party', + category: Category.flame, + summary: + 'A Flame game moving from one level\'s scene to the next, and a ' + 'camera eased towards a view worked out from four walkers at once.', + since: '0.8.4', + evidence: 'moves the game to the next level\'s scene with the camera', + evidenceFile: 'packages/flame_flutter3d/CHANGELOG.md', + packages: ['flame_flutter3d'], + ), ]; diff --git a/apps/flutter3d_showcase/lib/pages/flame/flame_horde.dart b/apps/flutter3d_showcase/lib/pages/flame/flame_horde.dart new file mode 100644 index 000000000..7b450a036 --- /dev/null +++ b/apps/flutter3d_showcase/lib/pages/flame/flame_horde.dart @@ -0,0 +1,269 @@ +/// A horde of `flutter3d_sim` actors drawn as slots of one instanced batch, +/// stepped by one `ActorSystemComponent` towards two players at once. +/// +/// Quoted by `flame_horde.md` and shown whole in the Source tab. +library; + +import 'dart:math' as math; + +import 'package:flame/components.dart' show Component; +import 'package:flame/game.dart'; +import 'package:flame_flutter3d/flame_flutter3d.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_showcase/src/demo/demo.dart'; +import 'package:flutter3d_showcase/src/demo/flame_layer.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; + +/// How many monsters there are: one draw for all of them. +const int _monsters = 96; + +// #region brain +/// Walks at whichever player the system says it can reach first, and +/// remembers which one that was. +final class _Chase extends Brain { + int attended = -1; + + @override + void act(Mind it) { + attended = it.focusIndex; + it.steerTowardsFocus(); + } +} +// #endregion brain + +final class FlameHordeDemo extends ShowcaseDemo { + late final DemoContext _context; + late final Scene _scene; + late final _Horde _game; + late final Widget _body = flameOrbit( + _context, + Flutter3dFlameWidget( + game: _game, + existing: (device: _context.device, renderer: _context.renderer), + ), + ); + + @override + void configureView(DemoContext context) { + context.orbit + ..distance = 22.0 + ..pitch = 0.95 + ..yaw = 0.2; + context.orbit.target.setValues(0.0, 0.0, 0.0); + } + + @override + Scene build(DemoContext context) { + _context = context; + _scene = Scene() + ..ambientColor = Vector3(0.45, 0.5, 0.6) + ..ambientIntensity = 0.3 + ..add( + MeshNode( + DeviceMesh.upload( + context.device, + CuboidShape(size: Vector3(28.0, 0.1, 28.0)).build(), + ), + Material(name: 'floor', baseColor: Vector4(0.34, 0.36, 0.33, 1.0)), + name: 'floor', + )..setPosition(0.0, -0.05, 0.0), + ) + ..add( + LightNode(name: 'sun', intensity: 2.2) + ..setLocalForward(Vector3(-0.3, -0.7, -0.4)), + ); + _game = _Horde(context.camera)..open3d(context.device, scene: _scene); + // In the scene from the start, empty until the monsters take their slots. + _scene.add(_game.batch); + return _scene; + } + + @override + void update(DemoContext context, double dt) => + context.orbit.syncProjectionDepth(context.camera); + + /// The Flame game the page runs, for a test that steps it without a window. + @visibleForTesting + FlameGame get game => _game; + + /// The one batch every monster is drawn through. + @visibleForTesting + InstancedMeshNode get batch => _game.batch; + + /// Every monster's body, and which player it went for on its last step. + @visibleForTesting + List<({Vector3 at, int attended})> get monsters => + <({Vector3 at, int attended})>[ + for (final (Actor actor, _Chase brain) in _game.monsters) + (at: actor.body!.position, attended: brain.attended), + ]; + + /// Where the two players are now. + @visibleForTesting + List get players => [ + for (final MeshNode hero in _game.heroes) hero.readPosition(), + ]; + + @override + Widget? customBody(BuildContext buildContext, DemoContext context) => _body; + + @override + void verify(Scene scene, FrameResult frame) { + if (frame.drawCalls < 1) throw StateError('the floor was not drawn'); + final batches = scene.meshes.whereType(); + if (batches.length != 1) { + throw StateError( + 'the horde should be one batch, found ${batches.length}', + ); + } + } +} + +/// The colour a monster wears for the player it is after. +final List _playerColours = [ + Vector4(0.95, 0.55, 0.2, 1.0), + Vector4(0.3, 0.7, 0.95, 1.0), +]; + +final class _Horde extends FlameGame with HasFlutter3d { + _Horde(this._eye); + + final CameraNode _eye; + final List heroes = []; + final List<(Actor, _Chase)> monsters = <(Actor, _Chase)>[]; + + // #region batch + /// Every monster's mesh and material, drawn in one call however many there + /// are; the colour is each slot's own. + late final InstancedMeshNode batch = InstancedMeshNode( + DeviceMesh.upload( + device, + CuboidShape(size: Vector3(0.6, 1.0, 0.6)).build(), + ), + Material(name: 'monster', baseColor: Vector4.all(1.0)), + capacity: _monsters, + name: 'horde', + ); + // #endregion batch + + double _clock = 0.0; + + @override + CameraNode createCamera3d() => _eye; + + @override + void onOpen3d() { + final CollisionWorld world = CollisionWorld() + ..addBox(Vector3(0.0, -0.5, 0.0), Vector3(28.0, 1.0, 28.0)) + ..update(); + final ActorSystem system = ActorSystem(world: world, random: GameRandom(7)); + final DeviceMesh ball = DeviceMesh.upload( + device, + SphereShape(segments: 24, radius: 0.5).build(), + ); + for (var i = 0; i < 2; i++) { + final MeshNode hero = MeshNode( + ball, + Material( + name: 'player $i', + baseColor: _playerColours[i], + emissive: _playerColours[i].xyz, + emissiveStrength: 1.5, + ), + name: 'player $i', + ); + heroes.add(hero); + scene.add(hero); + } + _walkHeroes(); + + // #region foci + // One system, stepped towards both players: each monster goes for the + // one it can reach first. The system is a step clock too, and every + // monster is drawn between its last two steps by it. + final ActorSystemComponent stepper = ActorSystemComponent( + system: system, + foci: () => [ + for (final MeshNode hero in heroes) + (at: hero.readPosition()..y = 0.9, body: null), + ], + ); + // #endregion foci + + // #region spawn + // Two rings of monsters, each an actor in the system and a slot in the + // batch; no scene node of their own. + final List slots = []; + for (var i = 0; i < _monsters; i++) { + final double radius = i.isEven ? 9.0 : 11.0; + final double angle = i * 2.0 * math.pi / _monsters; + final _Chase brain = _Chase(); + final Actor actor = system.spawn( + body: CharacterController( + world: world, + position: Vector3( + radius * math.cos(angle), + 0.9, + radius * math.sin(angle), + ), + tuning: const MovementTuning(walkSpeed: 2.2), + ), + brain: brain, + ); + monsters.add((actor, brain)); + slots.add( + _Monster(actor: actor, batch: batch, stepper: stepper, brain: brain), + ); + } + addAll([stepper, ...slots]); + // #endregion spawn + add(flameCaption('one draw, two players: each monster wears its target')); + } + + @override + void update(double dt) { + super.update(dt); + if (!has3d || heroes.isEmpty) return; + _clock += dt; + _walkHeroes(); + } + + /// Two players walking circles of their own on either side of the yard. + void _walkHeroes() { + for (var i = 0; i < heroes.length; i++) { + final double side = i == 0 ? -1.0 : 1.0; + final double turn = _clock * 0.5 + i * math.pi; + heroes[i].setPosition( + side * 3.5 + 2.5 * math.cos(turn), + 0.5, + 2.5 * math.sin(turn), + ); + } + } +} + +// #region monster +/// A monster drawn through its slot, between its steps, in the colour of +/// the player its brain went for. +final class _Monster extends InstancedActorComponent { + _Monster({ + required super.actor, + required super.batch, + required ActorSystemComponent super.stepper, + required this.brain, + }) : super(lift: -0.4); + + final _Chase brain; + int _shown = -1; + + @override + void update(double dt) { + super.update(dt); + final InstanceHandle? at = slot; + if (at == null || brain.attended == _shown || brain.attended < 0) return; + _shown = brain.attended; + at.setColor(_playerColours[_shown]); + } +} +// #endregion monster diff --git a/apps/flutter3d_showcase/lib/pages/flame/flame_horde.md b/apps/flutter3d_showcase/lib/pages/flame/flame_horde.md new file mode 100644 index 000000000..c79a0a26d --- /dev/null +++ b/apps/flutter3d_showcase/lib/pages/flame/flame_horde.md @@ -0,0 +1,48 @@ +# Ninety-six monsters in one draw, chasing two players + +A horde whose every step is decided by `flutter3d_sim` does not need a scene +node per monster. `InstancedActorComponent` draws a simulated actor as a slot +of a shared `InstancedMeshNode`, and `ActorSystemComponent` steps the whole +system towards several players at once, so each monster goes for the one it +can reach first. + +## Step 1: One batch for the horde + +The mesh and the material are the batch's; the colour is each slot's own, +which is how a monster shows who it is after. + +{{code batch}} + +## Step 2: A brain that chases a focus + +The system tells each monster which focus is its own on every step, and +`steerTowardsFocus` walks it there. The brain only remembers the answer so +the page can colour it. + +{{code brain}} + +## Step 3: Several foci + +`foci` is read fresh on every step: here, where the two players are now. With +several, each actor attends to the one nearest by walking where the level has +a flow field, and by straight line where it has none, as in this empty yard. +The component is also what steps the actors, so it is the clock their slots +draw between. + +{{code foci}} + +## Step 4: An actor per monster, a slot per actor + +Each monster is an actor in the system and a component in the game. The +component takes a slot when it is added, gives it back when it goes, and +goes itself when the simulation removes its actor. + +{{code spawn}} + +Drawn with `stepper`, a monster is placed between where it was before the +last step and where it is now, so the horde moves smoothly at any frame rate. +The slot's colour follows the player the brain last went for: orange for the +player on the left, blue for the one on the right, and a monster changes +colour as the two circle and the other one comes nearer. + +{{code monster}} diff --git a/apps/flutter3d_showcase/lib/pages/flame/flame_level_scene.dart b/apps/flutter3d_showcase/lib/pages/flame/flame_level_scene.dart new file mode 100644 index 000000000..60cafc301 --- /dev/null +++ b/apps/flutter3d_showcase/lib/pages/flame/flame_level_scene.dart @@ -0,0 +1,276 @@ +/// A Flame game that moves from one level to the next with +/// `replaceScene3d`, and a `ViewCamera` that frames the whole party as it +/// walks. +/// +/// Quoted by `flame_level_scene.md` and shown whole in the Source tab. +library; + +import 'dart:math' as math; + +import 'package:flame/components.dart' show Component, TextComponent; +import 'package:flame/game.dart'; +import 'package:flame_flutter3d/flame_flutter3d.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_showcase/src/demo/demo.dart'; +import 'package:flutter3d_showcase/src/demo/flame_layer.dart'; + +/// Seconds on each level before the party moves on. +const double _stay = 6.0; + +final class FlameLevelSceneDemo extends ShowcaseDemo { + late final _Tour _game; + late final Widget _body = Flutter3dFlameWidget( + game: _game, + existing: (device: _context.device, renderer: _context.renderer), + ); + late final DemoContext _context; + + @override + void configureView(DemoContext context) { + context.orbit + ..distance = 14.0 + ..pitch = 0.7 + ..yaw = 0.0; + context.orbit.target.setValues(0.0, 0.0, 0.0); + } + + @override + Scene build(DemoContext context) { + _context = context; + // #region levels + // Two levels, each a scene of its own: its floor, its light, its air. + // The first is the one the game opens on; the second waits unseen. + final List levels = [ + _level( + context.device, + floor: Vector4(0.36, 0.5, 0.3, 1.0), + air: Vector3(0.55, 0.6, 0.7), + rocks: Vector4(0.6, 0.58, 0.52, 1.0), + seed: 1, + ), + _level( + context.device, + floor: Vector4(0.3, 0.28, 0.34, 1.0), + air: Vector3(0.35, 0.3, 0.5), + rocks: Vector4(0.45, 0.4, 0.6, 1.0), + seed: 2, + ), + ]; + _game = _Tour(context.camera, levels) + ..open3d(context.device, scene: levels.first); + // #endregion levels + return levels.first; + } + + /// A floor, a sun and a scatter of rocks. + static Scene _level( + GraphicsDevice device, { + required Vector4 floor, + required Vector3 air, + required Vector4 rocks, + required int seed, + }) { + final math.Random random = math.Random(seed); + final DeviceMesh rock = DeviceMesh.upload( + device, + CuboidShape(size: Vector3(0.8, 1.2, 0.8)).build(), + ); + final Material stone = Material(name: 'rock', baseColor: rocks); + final Scene level = Scene() + ..ambientColor = air + ..ambientIntensity = 0.35 + ..add( + MeshNode( + DeviceMesh.upload( + device, + CuboidShape(size: Vector3(20.0, 0.1, 20.0)).build(), + ), + Material(name: 'floor', baseColor: floor), + name: 'floor', + )..setPosition(0.0, -0.05, 0.0), + ) + ..add( + LightNode(name: 'sun', intensity: 2.4) + ..setLocalForward(Vector3(-0.3, -0.6, -0.4)), + ); + for (var i = 0; i < 14; i++) { + level.add( + MeshNode(rock, stone, name: 'rock $i')..setPosition( + random.nextDouble() * 18.0 - 9.0, + 0.6, + random.nextDouble() * 18.0 - 9.0, + ), + ); + } + return level; + } + + @override + void update(DemoContext context, double dt) => + context.orbit.syncProjectionDepth(context.camera); + + /// The Flame game the page runs, for a test that steps it without a window. + @visibleForTesting + FlameGame get game => _game; + + /// The two levels, in the order the party visits them. + @visibleForTesting + List get levels => _game.levels; + + /// The level the game draws now. + @visibleForTesting + Scene get level => _game.scene; + + /// The camera the view camera moves. + @visibleForTesting + CameraNode get camera => _game.camera3d; + + /// The party's markers. + @visibleForTesting + List get party => _game.party; + + /// Where the view camera wants to be this frame. + @visibleForTesting + Vector3 get wantedEye { + final Vector3 eye = Vector3.zero(); + _game.frameParty(eye, Vector3.zero()); + return eye; + } + + @override + Widget? customBody(BuildContext buildContext, DemoContext context) => _body; + + @override + void verify(Scene scene, FrameResult frame) { + if (frame.drawCalls < 1) throw StateError('the first level was not drawn'); + if (!identical(scene, _game.levels.first)) { + throw StateError('the page should open on the first level'); + } + } +} + +/// The party's colours, one a member. +final List _colours = [ + Vector4(0.95, 0.55, 0.2, 1.0), + Vector4(0.3, 0.7, 0.95, 1.0), + Vector4(0.6, 0.9, 0.35, 1.0), + Vector4(0.9, 0.4, 0.75, 1.0), +]; + +final class _Tour extends FlameGame with HasFlutter3d { + _Tour(this._eye, this.levels); + + final CameraNode _eye; + final List levels; + final List party = []; + late final TextComponent _caption = flameCaption('level one'); + + int _level = 0; + double _clock = 0.0; + + @override + CameraNode createCamera3d() => _eye; + + // #region camera + @override + void onOpen3d() { + final DeviceMesh ball = DeviceMesh.upload( + device, + SphereShape(segments: 20, radius: 0.4).build(), + ); + for (var i = 0; i < _colours.length; i++) { + final MeshNode member = MeshNode( + ball, + Material(name: 'member $i', baseColor: _colours[i]), + name: 'member $i', + ); + party.add(member); + scene.add(member); + } + _walk(); + // Eased each frame towards wherever `frameParty` says, after the party + // has walked: no one member decides where the camera goes. + addAll([ + ViewCameraComponent( + ViewCamera(camera: camera3d, view: frameParty, stiffness: 2.5), + ), + _caption, + ]); + } + // #endregion camera + + // #region frame + /// Above and behind the party's middle, further back the more it spreads. + bool frameParty(Vector3 eye, Vector3 target) { + if (party.isEmpty) return false; + final Vector3 middle = party.fold( + Vector3.zero(), + (Vector3 sum, MeshNode m) => sum..add(m.readPosition()), + )..scale(1.0 / party.length); + final double spread = party.fold( + 0.0, + (double far, MeshNode m) => + math.max(far, m.readPosition().distanceTo(middle)), + ); + target.setFrom(middle); + eye.setValues( + middle.x, + middle.y + 3.5 + spread * 1.5, + middle.z + 5.0 + spread * 1.5, + ); + return true; + } + // #endregion frame + + @override + void update(double dt) { + // The party walks before the camera, which is one of the children + // `super.update` runs. + if (has3d && party.isNotEmpty) { + _clock += dt; + if (_clock >= _stay) _nextLevel(); + _walk(); + } + super.update(dt); + } + + // #region next + /// The next level: the party is carried across, then the scene is + /// replaced, and the camera goes with it. What the old level holds stays + /// there for the next visit. + void _nextLevel() { + _clock = 0.0; + _level = (_level + 1) % levels.length; + final Scene next = levels[_level]; + for (final MeshNode member in party) { + member.removeFromParent(); + next.add(member); + } + replaceScene3d(next); + _caption.text = _level == 0 ? 'level one' : 'level two'; + } + // #endregion next + + /// Each level has its own walk: across the first, round the second, the + /// party spreading and closing as it goes. + void _walk() { + final double t = _clock / _stay; + final Vector3 middle = _level == 0 + ? Vector3(-3.0, 0.4, 6.0 - 12.0 * t) + : Vector3( + 5.0 * math.cos(t * 2.0 * math.pi), + 0.4, + 5.0 * math.sin(t * 2.0 * math.pi), + ); + final double spread = 1.0 + 0.8 * math.sin(_clock * 1.3); + for (var i = 0; i < party.length; i++) { + final double angle = _clock * 0.8 + i * 2.0 * math.pi / party.length; + party[i].setPosition( + middle.x + spread * math.cos(angle), + middle.y, + middle.z + spread * math.sin(angle), + ); + } + } +} diff --git a/apps/flutter3d_showcase/lib/pages/flame/flame_level_scene.md b/apps/flutter3d_showcase/lib/pages/flame/flame_level_scene.md new file mode 100644 index 000000000..0e03dc632 --- /dev/null +++ b/apps/flutter3d_showcase/lib/pages/flame/flame_level_scene.md @@ -0,0 +1,39 @@ +# A level is a scene, and the camera frames the whole party + +A game with levels moves from one to the next, and in flutter3d a level is a +scene: its floor, its lights, its air. `HasFlutter3d.replaceScene3d` moves a +Flame game's 3D layer to the next scene, with the camera, on the same device +and renderer. `ViewCamera` eases a camera towards wherever a function says, +for a view no single component decides, such as a party of four. + +## Step 1: Two levels, two scenes + +Both are built up front. The game opens on the first, and the second waits +without being drawn. + +{{code levels}} + +## Step 2: Move on with replaceScene3d + +Every six seconds the party goes to the next level. The game carries its own +nodes across, since what was in the old scene stays there, and then replaces +the scene. The camera goes with it, and `Flutter3dFlameWidget` draws the +game's scene as it is now, not the one it opened with. + +{{code next}} + +## Step 3: Where the party should be seen from + +The view is worked out from every member at once: above and behind their +middle, further back the more they spread. It is asked once a frame and +writes the eye and the point looked at into the two vectors it is handed. + +{{code frame}} + +## Step 4: A camera eased towards it + +`ViewCameraComponent` runs the view camera after whatever moves what it +frames. On a new level the party starts somewhere else, and the camera eases +across to it rather than jumping. + +{{code camera}} diff --git a/apps/flutter3d_showcase/lib/pages/flame/flame_seats.dart b/apps/flutter3d_showcase/lib/pages/flame/flame_seats.dart new file mode 100644 index 000000000..d1d63560b --- /dev/null +++ b/apps/flutter3d_showcase/lib/pages/flame/flame_seats.dart @@ -0,0 +1,259 @@ +/// `PlayerSeats` over two keyboard layouts: nobody plays until they press +/// their join key, and the first to press is player one. +/// +/// Quoted by `flame_seats.md` and shown whole in the Source tab. +library; + +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; +import 'package:flame_flutter3d/flame_flutter3d.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter3d_showcase/src/demo/demo.dart'; +import 'package:flutter3d_showcase/src/demo/flame_layer.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; + +// #region layouts +/// What a layout presses to join, and to do nothing else yet. +const GameAction join = GameAction('join'); + +/// One way of holding the game: four keys to walk and one to join, in a +/// table and a state of its own. +FlameInputBridge _layout( + LogicalKeyboardKey up, + LogicalKeyboardKey left, + LogicalKeyboardKey down, + LogicalKeyboardKey right, + LogicalKeyboardKey joins, +) => FlameInputBridge( + bindings: Bindings() + ..bind(InputSource.key(up.keyId), GameAction.moveForward) + ..bind(InputSource.key(left.keyId), GameAction.moveLeft) + ..bind(InputSource.key(down.keyId), GameAction.moveBack) + ..bind(InputSource.key(right.keyId), GameAction.moveRight) + ..bind(InputSource.key(joins.keyId), join), + inputState: InputState(), +); + +/// WASD and Space on the left of the keyboard, the arrows and Slash on the +/// right: two players at one keyboard, neither of them a player yet. +PlayerSeats _twoLayouts() => PlayerSeats([ + _layout( + LogicalKeyboardKey.keyW, + LogicalKeyboardKey.keyA, + LogicalKeyboardKey.keyS, + LogicalKeyboardKey.keyD, + LogicalKeyboardKey.space, + ), + _layout( + LogicalKeyboardKey.arrowUp, + LogicalKeyboardKey.arrowLeft, + LogicalKeyboardKey.arrowDown, + LogicalKeyboardKey.arrowRight, + LogicalKeyboardKey.slash, + ), +]); +// #endregion layouts + +// #region claim +/// Seats every free layout whose join key went down this step, in the order +/// the seats have them, and answers the ones it seated. +List seatNewcomers(PlayerSeats seats) => [ + for (final FlameInputBridge layout in seats.free.toList()) + if (layout.inputState.pressed(join) && seats.claim(layout)) layout, +]; +// #endregion claim + +final class FlameSeatsDemo extends ShowcaseDemo { + late final DemoContext _context; + late final Scene _scene; + late final _Lobby _game; + late final Widget _body = flameOrbit( + _context, + Flutter3dFlameWidget( + game: _game, + existing: (device: _context.device, renderer: _context.renderer), + ), + ); + + late final bool _secondFirst; + + @override + void configureView(DemoContext context) { + context.orbit + ..distance = 9.0 + ..pitch = 0.8 + ..yaw = 0.0; + context.orbit.target.setValues(0.0, 0.0, 0.0); + } + + @override + Scene build(DemoContext context) { + _context = context; + _secondFirst = _run(); + _scene = Scene() + ..ambientColor = Vector3(0.5, 0.55, 0.65) + ..ambientIntensity = 0.3 + ..add( + MeshNode( + DeviceMesh.upload( + context.device, + CuboidShape(size: Vector3(9.0, 0.1, 9.0)).build(), + ), + Material(name: 'floor', baseColor: Vector4(0.36, 0.4, 0.38, 1.0)), + name: 'floor', + )..setPosition(0.0, -0.05, 0.0), + ) + ..add( + LightNode(name: 'sun', intensity: 2.5) + ..setLocalForward(Vector3(-0.3, -0.6, -0.4)), + ); + _game = _Lobby(context.camera, _twoLayouts()) + ..open3d(context.device, scene: _scene); + return _scene; + } + + @override + void update(DemoContext context, double dt) => + context.orbit.syncProjectionDepth(context.camera); + + /// Checks, with no window, that the second layout pressing first is + /// player one: seats go in the order people join, not the order listed. + static bool _run() { + final PlayerSeats seats = _twoLayouts(); + final FlameInputBridge arrows = seats.candidates[1]; + arrows.onKeyEvent( + const KeyDownEvent( + physicalKey: PhysicalKeyboardKey.slash, + logicalKey: LogicalKeyboardKey.slash, + timeStamp: Duration.zero, + ), + const {}, + ); + seatNewcomers(seats); + return seats.seated.length == 1 && identical(seats.seated.first, arrows); + } + + /// The Flame game the page runs, for a test that steps it without a window. + @visibleForTesting + FlameGame get game => _game; + + /// Who holds what, for a test that presses keys. + @visibleForTesting + PlayerSeats get seats => _game.seats; + + /// The marker each layout walks, in the order the seats list the layouts. + @visibleForTesting + List get markers => _game.markers; + + @override + Widget? customBody(BuildContext buildContext, DemoContext context) => _body; + + @override + void verify(Scene scene, FrameResult frame) { + if (frame.drawCalls < 1) throw StateError('the floor was not drawn'); + if (!_secondFirst) { + throw StateError( + 'the arrows pressed Slash first and should have been player one', + ); + } + } +} + +/// The colour of each seat, player one first. +final List _seatColours = [ + Vector4(0.95, 0.65, 0.2, 1.0), + Vector4(0.3, 0.75, 0.95, 1.0), +]; + +// #region lobby +/// The game: every layout fed from the keyboard itself and closed after each +/// frame, a marker for each, hidden until its layout joins. +final class _Lobby extends FlameGame with HasFlutter3d { + _Lobby(this._eye, this.seats); + + final CameraNode _eye; + final PlayerSeats seats; + final List markers = []; + late final TextComponent _caption = flameCaption( + 'Space joins WASD, / joins the arrows', + ); + + @override + CameraNode createCamera3d() => _eye; + + @override + void onOpen3d() { + final DeviceMesh cube = DeviceMesh.upload( + device, + CuboidShape(size: Vector3.all(0.8)).build(), + ); + for (var i = 0; i < seats.candidates.length; i++) { + final MeshNode marker = + MeshNode( + cube, + Material(name: 'player', baseColor: Vector4.all(1.0)), + name: 'layout $i', + ) + ..setPosition(-1.5 + 3.0 * i, 0.4, 0.0) + ..visible = false; + markers.add(marker); + scene.add(marker); + } + addAll([ + // From `HardwareKeyboard`, wherever the focus is. + seats.listenToKeyboard(), + _Seating(this), + // Every layout's step closed, the free ones too, so a press made to + // join is seen once. + ...seats.stepEnds(), + _caption, + ]); + } + // #endregion lobby + + // #region seat + /// A layout that has just joined shows its marker in its seat's colour; + /// every seated layout walks its own. + void seatAndWalk(double dt) { + for (final FlameInputBridge joined in seatNewcomers(seats)) { + final int seat = seats.seated.indexOf(joined); + final MeshNode marker = markers[seats.candidates.indexOf(joined)]; + marker + ..material.baseColor.setFrom(_seatColours[seat]) + ..visible = true; + } + for (final FlameInputBridge player in seats.seated) { + final Vector2 axis = player.inputState.moveAxis; + final MeshNode marker = markers[seats.candidates.indexOf(player)]; + final Vector3 at = marker.readPosition(); + marker.setPosition( + (at.x + axis.x * 3.0 * dt).clamp(-4.0, 4.0), + at.y, + (at.z - axis.y * 3.0 * dt).clamp(-4.0, 4.0), + ); + } + _caption.text = switch (seats.seated.length) { + 0 => 'Space joins WASD, / joins the arrows', + 1 => 'player one is in; the other layout can still join', + _ => 'two players, in the order they pressed', + }; + } + // #endregion seat +} + +/// Seats and walks between the keyboard's feed and the steps' ends, so a +/// join press is read in the frame it came in. +final class _Seating extends Component { + _Seating(this._lobby); + + final _Lobby _lobby; + + @override + void update(double dt) { + super.update(dt); + _lobby.seatAndWalk(dt); + } +} diff --git a/apps/flutter3d_showcase/lib/pages/flame/flame_seats.md b/apps/flutter3d_showcase/lib/pages/flame/flame_seats.md new file mode 100644 index 000000000..d7924eb32 --- /dev/null +++ b/apps/flutter3d_showcase/lib/pages/flame/flame_seats.md @@ -0,0 +1,41 @@ +# Two layouts, and whoever presses first is player one + +`PlayerSeats` keeps every way of holding a game at one machine, each a +`FlameInputBridge` with its own bindings and state, and lets the game claim +one as the next player when it sees it pressed. Here the two ways are two +halves of one keyboard. Nobody plays until they press their join key, and +seats go in the order people join, whichever layout is listed first. + +## Step 1: A table and a state for each layout + +Each layout is a bridge of its own: the same `Bindings` and `InputState` +pair a native `flutter3d_game` reads, one per player, so a key reaches the +player it is bound for and no other. + +{{code layouts}} + +## Step 2: Claim a seat by pressing + +What counts as asking to join is the game's to say. Here it is the join key +going down this step on a layout nobody has claimed. `claim` makes it the +next player, and `seated` keeps them in the order they came. + +{{code claim}} + +## Step 3: Keys from the keyboard, not the focus + +`listenToKeyboard` feeds every layout from `HardwareKeyboard`, so a key +reaches the game wherever the focus is, and a key let go while a button or a +menu had the focus is still let go. `stepEnds` closes every layout's step +after the frame, the free ones as well, so a press made to join is seen once +and not on every frame after. + +{{code lobby}} + +## Step 4: A marker for each player + +A layout that has just joined shows its cube in its seat's colour: amber for +player one, blue for player two. From then on it walks with its own four +keys. Press Slash before Space and the arrows are player one. + +{{code seat}} diff --git a/apps/flutter3d_showcase/lib/pages/flame/registry.dart b/apps/flutter3d_showcase/lib/pages/flame/registry.dart index 0cdb19c97..374860ca8 100644 --- a/apps/flutter3d_showcase/lib/pages/flame/registry.dart +++ b/apps/flutter3d_showcase/lib/pages/flame/registry.dart @@ -9,10 +9,13 @@ import 'package:flutter3d_showcase/src/demo/demo.dart'; import 'flame_camera_bridge.dart'; import 'flame_crowd.dart'; import 'flame_ecs_bridge.dart'; +import 'flame_horde.dart'; import 'flame_input_bridge.dart'; +import 'flame_level_scene.dart'; import 'flame_overview.dart'; import 'flame_owned_world.dart'; import 'flame_physics_bridge.dart'; +import 'flame_seats.dart'; import 'flame_transform_bridge.dart'; final Map flameDemos = { @@ -24,4 +27,7 @@ final Map flameDemos = { 'flame-camera-bridge': FlameCameraBridgeDemo.new, 'flame-owned-world': FlameOwnedWorldDemo.new, 'flame-crowd': FlameCrowdDemo.new, + 'flame-seats': FlameSeatsDemo.new, + 'flame-horde': FlameHordeDemo.new, + 'flame-level-scene': FlameLevelSceneDemo.new, }; diff --git a/apps/flutter3d_showcase/pubspec.yaml b/apps/flutter3d_showcase/pubspec.yaml index 135b81e4d..cf1da6dc7 100644 --- a/apps/flutter3d_showcase/pubspec.yaml +++ b/apps/flutter3d_showcase/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter3d_showcase description: "Every capability of the flutter3d engine on a page of its own, with the version it appeared in and a step-by-step guide." publish_to: 'none' -version: 0.8.3+1 +version: 0.8.4+1 resolution: workspace environment: diff --git a/apps/flutter3d_showcase/test/pages/flame_test.dart b/apps/flutter3d_showcase/test/pages/flame_test.dart index bbb69f274..8669ec3c1 100644 --- a/apps/flutter3d_showcase/test/pages/flame_test.dart +++ b/apps/flutter3d_showcase/test/pages/flame_test.dart @@ -2,21 +2,33 @@ library; import 'package:flame/components.dart' show Vector2; +import 'package:flame/game.dart' show FlameGame; import 'package:flame_flutter3d/flame_flutter3d.dart'; +import 'package:flutter/services.dart' + show + HardwareKeyboard, + KeyDownEvent, + KeyUpEvent, + LogicalKeyboardKey, + PhysicalKeyboardKey; import 'package:flutter3d/flutter3d.dart' hide Material; import 'package:flutter3d_showcase/pages/flame/flame_camera_bridge.dart'; import 'package:flutter3d_showcase/pages/flame/flame_ecs_bridge.dart'; +import 'package:flutter3d_showcase/pages/flame/flame_horde.dart'; +import 'package:flutter3d_showcase/pages/flame/flame_level_scene.dart'; import 'package:flutter3d_showcase/pages/flame/flame_overview.dart'; import 'package:flutter3d_showcase/pages/flame/flame_physics_bridge.dart'; +import 'package:flutter3d_showcase/pages/flame/flame_seats.dart'; import 'package:flutter3d_showcase/pages/flame/flame_transform_bridge.dart'; import 'package:flutter3d_showcase/src/demo/demo_run.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart' show Matrix4, Vector3; import '../support/page_harness.dart'; /// Mounts [game] at 800 by 600 and steps it [frames] times, with no window: /// what a `GameWidget` does, minus the widget. -Future _run(TransparentFlameGame game, int frames) async { +Future _run(FlameGame game, int frames) async { game.onGameResize(Vector2(800.0, 600.0)); // ignore: invalid_use_of_internal_member await game.load(); @@ -32,8 +44,181 @@ Future _run(TransparentFlameGame game, int frames) async { } } +/// A key going down or up on the keyboard itself, where `listenToKeyboard` +/// hears it, with nothing holding the focus. +void _key( + LogicalKeyboardKey logical, + PhysicalKeyboardKey physical, + bool down, +) => HardwareKeyboard.instance.handleKeyEvent( + down + ? KeyDownEvent( + physicalKey: physical, + logicalKey: logical, + timeStamp: Duration.zero, + ) + : KeyUpEvent( + physicalKey: physical, + logicalKey: logical, + timeStamp: Duration.zero, + ), +); + +/// Steps [game] one frame at a time: a key pressed between two of them is +/// read in the next. +void _frames(FlameGame game, int frames) { + for (var i = 0; i < frames; i++) { + game.update(1 / 60); + } +} + void main() { + // `HardwareKeyboard` and the input step's lifecycle listener both need a + // binding, and a plain `test()` never makes one. + TestWidgetsFlutterBinding.ensureInitialized(); + group('flame pages', () { + test('a layout joins on its own key, in the order it pressed', () async { + // Mutation: seat the free layouts in the order they are listed rather + // than on their own key, and WASD is player one with no press at all. + final FlameSeatsDemo demo = FlameSeatsDemo(); + final DemoRun run = await DemoRun.start(cpuDevice(), demo); + try { + await _run(demo.game, 5); + final FlameInputBridge wasd = demo.seats.candidates[0]; + final FlameInputBridge arrows = demo.seats.candidates[1]; + expect(demo.seats.seated, isEmpty); + expect(demo.markers.every((MeshNode m) => !m.visible), isTrue); + + // The second layout presses first, through the keyboard alone. + _key(LogicalKeyboardKey.slash, PhysicalKeyboardKey.slash, true); + _frames(demo.game, 1); + _key(LogicalKeyboardKey.slash, PhysicalKeyboardKey.slash, false); + expect(demo.seats.seated, [arrows]); + expect(demo.markers[1].visible, isTrue); + expect(demo.markers[0].visible, isFalse); + + // Held, the join key seats nobody else on later frames. + _frames(demo.game, 5); + expect(demo.seats.seated, hasLength(1)); + + _key(LogicalKeyboardKey.space, PhysicalKeyboardKey.space, true); + _frames(demo.game, 1); + _key(LogicalKeyboardKey.space, PhysicalKeyboardKey.space, false); + expect(demo.seats.seated, [arrows, wasd]); + // Player one wears the first seat's colour, whichever layout it is. + expect(demo.markers[1].material.baseColor.x, greaterThan(0.9)); + expect(demo.markers[0].material.baseColor.z, greaterThan(0.9)); + + // A seated player walks with its own keys, and only its marker goes. + final double arrowsX = demo.markers[1].readPosition().x; + final double wasdX = demo.markers[0].readPosition().x; + _key( + LogicalKeyboardKey.arrowRight, + PhysicalKeyboardKey.arrowRight, + true, + ); + _frames(demo.game, 30); + _key( + LogicalKeyboardKey.arrowRight, + PhysicalKeyboardKey.arrowRight, + false, + ); + expect(demo.markers[1].readPosition().x, greaterThan(arrowsX + 1.0)); + expect(demo.markers[0].readPosition().x, wasdX); + } finally { + run.dispose(); + } + }); + + test('the horde is one batch, split between the two players', () async { + // Mutation: step the system towards the first player alone, and no + // monster ever goes for the second. + final FlameHordeDemo demo = FlameHordeDemo(); + final DemoRun run = await DemoRun.start(cpuDevice(), demo); + try { + double nearest() { + final List players = demo.players; + return demo.monsters + .map( + (m) => players + .map((Vector3 p) => (p..y = m.at.y).distanceTo(m.at)) + .reduce((double a, double b) => a < b ? a : b), + ) + .reduce((double a, double b) => a + b) / + demo.monsters.length; + } + + await _run(demo.game, 1); + final double before = nearest(); + _frames(demo.game, 90); + + // Every monster is a slot of the one batch. + expect(demo.batch.count, demo.monsters.length); + expect(demo.monsters, hasLength(96)); + // Both players are chased, each by some of the horde. + final Iterable attended = demo.monsters.map((m) => m.attended); + expect(attended.where((int i) => i == 0), isNotEmpty); + expect(attended.where((int i) => i == 1), isNotEmpty); + // And the horde closed in on whichever was nearer. + expect(nearest(), lessThan(before - 2.0)); + // The slot draws the body, not somewhere of its own. + final Matrix4 placed = Matrix4.zero(); + demo.batch.readTransform(0, placed); + final Vector3 drawn = placed.getTranslation(); + final Iterable gaps = demo.monsters.map( + (m) => Vector3(m.at.x - drawn.x, 0.0, m.at.z - drawn.z).length, + ); + expect( + gaps.reduce((double a, double b) => a < b ? a : b), + lessThan(0.2), + ); + } finally { + run.dispose(); + } + }); + + test( + 'the level moves to its next scene and the camera finds the party', + () async { + // Mutation: leave out `replaceScene3d`, and the second level is never + // the one drawn. + final FlameLevelSceneDemo demo = FlameLevelSceneDemo(); + final DemoRun run = await DemoRun.start(cpuDevice(), demo); + try { + await _run(demo.game, 90); + expect(demo.level, same(demo.levels[0])); + // The camera keeps up with the party as it walks the first level. + expect( + demo.camera.readPosition().distanceTo(demo.wantedEye), + lessThan(2.0), + ); + + // Past six seconds: the second level, with the camera and the party. + _frames(demo.game, 6 * 60 - 90 + 2); + expect(demo.level, same(demo.levels[1])); + expect(demo.levels[1].cameras, contains(demo.camera)); + expect(demo.levels[0].cameras, isNot(contains(demo.camera))); + expect( + demo.party.every((MeshNode m) => demo.levels[1].meshes.contains(m)), + isTrue, + ); + // The party starts the new level elsewhere; the camera eases after. + final double moved = demo.camera.readPosition().distanceTo( + demo.wantedEye, + ); + expect(moved, greaterThan(5.0)); + _frames(demo.game, 90); + expect( + demo.camera.readPosition().distanceTo(demo.wantedEye), + lessThan(moved / 2.0), + ); + } finally { + run.dispose(); + } + }, + ); + test('the transform bridge carries each side across, live', () async { // Mutation: give both components the same direction and one of the // two cubes stays where it started. diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 0e27d5493..704817f60 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10786 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10789 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index aa436f1c9..7a11e8a6d 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10786 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10789 tests need a GPU. --- # Testing -10786 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10789 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -36,10 +36,10 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_mcp_kit` | 2 | | `flutter3d_build` | 127 | | | | | `apps/flutter3d_lesson_viewer` | 48 | | | | | `apps/flutter3d_stereo_lesson_viewer` | 6 | -| `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 85 | +| `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10760 rather than 10786: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10763 rather than 10789: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. From 775c232d5022bd658759fa1117db61ba1f6ac3cc Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Wed, 30 Sep 2026 23:01:32 +0300 Subject: [PATCH 002/141] 0.9 has a plan: parity first, then past it tasks/0.9-engine-roadmap.md records the owner's twenty decisions and the research behind them: hot reload of shaders, assets and levels with the game kept alive, rotating bodies and joints in pure Dart, a declarative widget API, the renderer's missing rows, user materials compiled for every backend, an editor that runs the game, and twelve features the simulation makes possible, from an animation graph to replay tests for game developers. Hot reload and editor Play come first. --- tasks/0.9-engine-roadmap.md | 346 ++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 tasks/0.9-engine-roadmap.md diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md new file mode 100644 index 000000000..ae8ed7c7e --- /dev/null +++ b/tasks/0.9-engine-roadmap.md @@ -0,0 +1,346 @@ +# flutter3d 0.9 — parity and past it + +Date: 2026-09-30. Branch `0.9.0`, cut from `river-0.8.3` at `56828864` +(0.8.4 published and tagged at `a796ecab`). Written from four parallel +research passes, each made to read code rather than READMEs: a feature +inventory of flutter_scene at its published 0.23.0 and at `master` (`b473543`, +an unreleased 0.24.0), an inventory of this repository, a study of hot reload +in flutter_scene, in Flutter 3.47 and here, and a search for features +flutter_scene does not have. Then narrowed by the owner's answers to twenty +questions (§1). + +The previous plans stay the record of 0.8: [`0.8-engine-roadmap.md`](0.8-engine-roadmap.md) +and [`engine_0_8_plan.md`](engine_0_8_plan.md). What they left open is folded +in below (§8), and `doc/engine-gap-analysis.md`'s milestones M1–M5 are the +source of several items here. + +## 0. In short + +1. **0.9.0 catches up with flutter_scene as it will be when it ships its + 0.24**, not with the 0.23 on pub.dev: hot reload of everything, rotating + bodies and joints, a declarative widget API, the renderer's missing rows, + user materials, and an editor that runs the game. +2. **And it adds twelve things flutter_scene does not have**, most of them in + the simulation, where four backends agree without trying: an animation + graph, navmeshes, behaviour trees, a sequencer, a time-travel debugger, + replay tests for game developers, saves, telemetry, a photo mode, colour + accessibility, sharing, procedural generation. Plus voxels and networking + past two players, which were out of scope until now. +3. **Hot reload and Play in the editor come first.** It is the most visible + gap and the one with the cheapest start: a shader edit today updates the + library and never the picture, and fixing that is one call. +4. **One release.** 0.9.0 goes out when all of it is in (the owner's call; §10 + says what that costs). Every effect still lands off by default until its + goldens exist in all four sets. + +## 1. Decisions (2026-09-30) + +| # | Question | Decision | +|---|---|---| +| 1 | Which flutter_scene to match | Its `master`, i.e. 0.24 | +| 2 | Hot reload scope | All five tiers (§3) | +| 3 | Physics | Rotation, inertia, joints and CCD in pure Dart; no native backend | +| 4 | Declarative widget API | Yes, full | +| 5 | Renderer gaps | SMAA, lens flare and distortion, `.cube` LUT; projected decals; planar reflections and render-to-texture; physical sky; debug views and render stats | +| 6 | User materials | Compile our own material language to every GPU backend | +| 7 | Editor | Play with hot reload; Windows and Linux; docking panels, inspector, console, palette, render-graph view; published | +| 8 | Agent tooling | Render inspection in a running game; build, run and reload from MCP; skills in packages; desync bisect by an agent | +| 9–11 | New features | All twelve candidates (§5) | +| 12 | Old exclusions lifted | Behaviour on objects, level generators, voxels, networking past two | +| 13 | Release | One large 0.9.0 | +| 14 | First in line | Hot reload and editor Play | +| 15 | Networking past two | Both models: rollback for N players and an authoritative server | +| 16 | Platforms checked live | Windows and Linux; Android with measured numbers on the Galaxy A55; WebGPU as the browser default | +| 17 | Branch | `0.9.0` from `river-0.8.3`, now | +| 18 | Four backends agree | Kept, no exceptions | +| 19 | Comparison page | Fix what is stale now; revise it once for 0.9.0 | +| 20 | Voxels | A `flutter3d_voxel` package plus a demo | + +Not chosen, and so not in this plan: iOS on a device (still simulator only). + +## 2. Constraints, unchanged since 0.8 + +- No compute on flutter_gpu 3.47 (flutter#188474, #188480), none on WebGL2. + Compute runs on WebGPU and the CPU backend; `FieldPass` is the substitute + elsewhere. +- Shaders are compiled ahead of time and a bundle is tied to its SDK. There + is no runtime shader compiler on device, which shapes §4.2 and §3.4. +- One render pass per command buffer; depth cannot be sampled; MRT is probed; + the surface buffer turns MSAA off; readback is one or two frames late and + RGBA8. +- No GPU timers on Impeller or WebGL. +- The simulation step uses `Portable` maths and no isolates, so everything in + §5 that runs in the step is deterministic by construction and tested by + state digest. +- Four backends agree, and every effect has a CPU mirror (decision 18). + +## 3. Hot reload — first + +flutter_scene's hot reload (`lib/src/hot_reload/hot_reload_coordinator.dart`) +rides Flutter's own: `SceneView.reassemble()` calls a debug-only coordinator +that re-hashes registered assets and reloads, in order, shader bundles, `.fmat` +sidecars, scenes (patched in place by a node-id diff), textures and +environments. New bytes arrive because `flutter run` reruns build hooks on hot +reload (flutter_tools `run_hot.dart:486-500`). Its editor saves a scene and +calls `ext.flutter_scene.reloadScene`; on a device, where the source root is +not visible, that falls back to a hot restart and the game state is lost. Its +inspector sliders preview only inside the editor. + +What this repository already has: + +- `LoadedShaderLibrary.refresh` keeps handle identity + (`flutter3d_hardware/lib/src/shader.dart:133-175`), implemented on Impeller + with `reinitializeFromBytes` and on WebGL with mutable handles. + `Renderer.relinkShaders()` clears the pipeline caches + (`flutter3d_core/.../render/renderer.dart:855-900`). +- The engine bundle is loaded with `ShaderLibrary.fromAsset` + (`flutter3d_impeller/lib/src/gpu_device.dart:113-119`), which Flutter 3.47 + already reinitializes on hot reload. **Nothing calls `relinkShaders` then**, + so the library changes and the frame does not. +- `flutter3d_build` declares every source as a hook dependency + (`build_assets.dart:337-390`), so hooks already rerun on hot reload; the + runtime just never re-reads their output. +- `ext.flutter3d.timeline.*` (pause, step, rewind, branch, history) and the + editor's `timeline_client.dart` are a live link to a running game already. + +**HR1. Shaders and Dart code without losing the world (S).** A debug-only +`ReloadCoordinator` in `flutter3d` with WeakReference registrations and +injected file access, so its logic is plain unit tests. The game widget +overrides `reassemble()` and calls it; everything is also reachable as +`ext.flutter3d.reload.*`. On a changed bundle: `relinkShaders()`, plus a relink +hook for contributors that keep their own pipelines (`ParticleContributor`). +Custom `.f3dshaders` loaded from bytes are refreshed by the coordinator, which +also covers the web, where flutter_tools does not reinitialize bundles. +Documented limit: a stage newly added under a name already looked up needs a +restart (`gpu_loaded_shaders.dart:62-69`). +*Tests:* refresh then relink draws the new colour on every backend; a refused +bundle keeps the old frame. + +**HR2. Models, textures, environments (M).** On reassemble the coordinator +evicts and re-hashes registered `flutter3d_generated/*.f3d` and texture keys; +textures are re-uploaded in place through a new HAL `updateTexture` that keeps +handle identity (conformance-checked like shaders); meshes and materials are +swapped inside live `ModelInstance`s by node name, keeping transforms and +animation state. Two faster routes: the editor calls `ext.flutter3d.assets.reload` +after a save on desktop (reading `assets_src` directly under a debug +`FLUTTER3D_SOURCE_ROOT`), and **`ext.flutter3d.assets.put` sends cooked bytes +over the VM service, so a phone gets the new model with no restart** — where +flutter_scene restarts. +*Tests:* hash change reloads, identical bytes are a no-op; texture identity +across update on three backends; a macOS integration run that overwrites a +`.glb`, reloads and compares a golden. + +**HR3. A level from the editor into a running game (L).** The editor sends +`ext.flutter3d.level.apply {document or patch, hash}`. `diffLevel` keyed by +entity id splits the change: presentation (lights, decor, materials, +visibility) is patched in place; anything the simulation reads (colliders, +entities, signals) is applied as a timeline branch: rewind to a keyframe, +swap the level, replay the recorded input to now. A `TimelineLevelSwapped` +command and the level hash per `Demo` segment keep a replay reproducible, which +is the condition `ROADMAP.md` set for level reload. It travels over the VM +service, so it works on a device, and the editor MCP drives the same path. +*Tests:* diff classifier units; a swap at step K replays to the same final +digest; an editor ↔ game run over a real VM service. + +**HR4. Live parameters and replay after a reload (S–M).** Inspector drags send +`ext.flutter3d.material.set` and `ext.flutter3d.cvar.set` at about 30 Hz, +applied next frame without a save; runtime overrides survive later file +reloads. Tunables that affect the simulation are recorded into the input tape +as commands. **Replay after reload:** with a `RunTimeline` attached, a code +reload can rewind N seconds and re-simulate to now with the same input and the +new code, and report where the new run diverges from the old +(`SnapshotDivergence`). flutter_scene has no simulation layer to offer this. +*Tests:* a tape with cvar commands reproduces; swapping an injected step +function reproduces the new outcome and names the first differing step. + +**HR5. Editor Play (M), with §6.** The editor runs the user's project through +`flutter run --machine`, attaches the timeline and reload extensions, and +shows the console. Hot reload and restart from a toolbar and from MCP. + +## 4. Parity with flutter_scene 0.24 + +### 4.1 Renderer + +| Id | Item | Theirs | Ours today | Effort | +|---|---|---|---|---| +| P1 | SMAA 1x | `render/smaa_pass.dart` | FXAA, TAA/TAAU | M | +| P2 | Lens flare, lens distortion, `.cube` LUT import | `bloom_pass.dart` flares, `screen_distortion.dart`, `post_process/color_lut.dart` | halation, strip LUT grading (`doc/postprocessing-parity.md`, `gfx-30n` partial) | S–M | +| P3 | Projected box decals painting a material | `decal.dart` (0.24) | none | M | +| P4 | Planar reflections; public render-to-texture camera | `components/planar_reflector_component.dart`, `render_texture.dart` | SSR, probes; no public RTT | M | +| P5 | Physical sky (atmospheric scattering), stars, height fog | `sky_sources.dart` `PhysicalSkySource` | gradient sky, cubemap, `Atmosphere` presets | M | +| P6 | Debug views (material channels, UV, NaN, split wipe) and render stats | `render/debug_view.dart`, `render_stats.dart` (0.24) | viewport shading, NaN scan in `sim_mcp` | S | +| P7 | Orthographic everywhere, per-draw ranges, alpha to coverage, render order | 0.24 commits | orthographic exists; check every effect | S | + +Already ours and not theirs, to keep: volumetric fog, weighted blended OIT, +motion blur, local exposure and HDR out, EVSM, impostors, Hi-Z and software +occlusion, generated LOD, WebGPU, the CPU backend, retargeting. + +### 4.2 Materials + +**P8. User materials compiled for the GPU (L).** flutter_scene's answer is +`.fmat`: GLSL stages with parameters, varyings and, since 30 September, +lighting hooks, compiled by a build hook and hot-reloaded. Ours: +`formats/material_language/` already emits GLSL and evaluates on the CPU, but +`flutter3d_build` never compiles it into GPU bundles. Make the build hook +compile material-language sources into Impeller, WebGL and WebGPU bundles +(the last through the existing translation), load them through +`GraphicsDevice.loadShaders`, and let the CPU backend evaluate the same +source. Parameters, instance attributes and hot reload through HR1. Custom +lighting hooks come after surface outputs work. +*Tests:* one material-language source, one golden per backend. + +### 4.3 Physics + +**P9. Rotation, inertia, joints, CCD in pure Dart (L).** The M1 milestone of +`doc/engine-gap-analysis.md`: orientation and angular velocity on +`RigidBody`, inertia tensors per shape, contact manifolds with friction that +turn bodies, joints (fixed, hinge, ball, slider, distance), swept shapes for +fast bodies, a triangle-mesh shape and a 3D broadphase (today's grid is XZ +only). Snapshot and restore for rollback come free with determinism. +Ragdoll in §5 depends on it. +*Tests:* stacking, tipping and pendulum scenes compared by digest on two +operating systems; energy drift bounds. + +### 4.4 Declarative widget API + +**P10. Scene as widgets (L).** `Scene3D`, `SceneNode`, `SceneMesh`, +`SceneModel` (async, with a placeholder), lights and cameras as widgets over +the imperative graph, reconciled by key, with animation control as +properties. Games keep the imperative API; examples and the site move to +whichever reads better. +*Tests:* widget tests that a rebuild with the same keys keeps node identity, +and goldens. + +### 4.5 Editor and tools + +**P11. Editor (L).** Play (HR5); Windows and Linux; docking shell, outliner, +component inspector, console, command palette, render-graph inspector; +published like the modeler (web build plus desktop downloads). flutter_scene's +editor is not published, which is the chance to be first. + +**P12. Agent tooling (M).** In a running game, not only the 320×200 CPU +render: `list_draws`, `get_draw`, `list_passes`, `pass_output`, `read_pixel`, +`scan_nan`, `render_stats`. `build`, `run`, `stop`, `hot_reload`, +`reload_level`, console and event subscription from MCP. A `skills` command +installing per-package skills (idioms, performance, looks, verification). +Desync bisect as MCP tools on top of N4. + +## 5. New features flutter_scene does not have + +Checked absent in a clone of flutter_scene by grep, and in its open issues. + +| Id | Feature | Builds on here | Effort | +|---|---|---|---| +| N1 | **Animation graph:** state machines with typed parameters and crossfades, 1D/2D blend spaces, layers with masks, clip events as `GameEvent`s, IK nodes (foot plant, look-at, hand target), root motion into `CharacterController`; stored in `.f3d`; modeler panel and MCP tools | `engine/animation/{animation_layer,inverse_kinematics,skin_blend}.dart`, `model_core/{ik_constraint,root_motion_commands}.dart` — "called from tests only" | L | +| N2 | **Navmesh and crowds:** a Recast-like bake in Dart at build time (voxelise, regions, contours, polygons), tile rebake at runtime, A* with a funnel, off-mesh links from `JumpReach`, RVO avoidance; flow fields stay for hordes | `sim/nav/{nav_grid,flow_field,jump_links}.dart` | L | +| N3 | **Behaviour trees and utility AI** as data, leaves registered by kind, blackboard in the ECS store so it is in snapshots and rewind; a debug overlay of the active path | `sim/actors/{brain,actor_system}.dart` | M | +| N4 | **Time-travel debugger:** per-entity tracks, scrubber, branch from here, divergence bisect between two tapes to the first differing step and component | `game/run/run_timeline.dart`, `sim/save/{rewind,state_digest,tape}.dart`, `net/snapshot_divergence.dart` | M | +| N5 | **Replay tests for game developers:** `testReplay('level.tape', digestAt: …, goldensAt: …)` on the CPU backend, so a game's CI needs no GPU | `flutter3d_testing/{golden,replay_golden}.dart`, `sim/headless_run.dart` | S | +| N6 | **Saves:** autosave on pause and at checkpoints, schema version and migrations, cloud sync adapters (HTTP, Play Games, iCloud) resolving conflicts by step and digest | `app/storage/atomic_write.dart`, `sim/save/snapshot.dart`, `game/screens/save_file.dart` | M | +| N7 | **Telemetry and heatmaps from tapes:** opt-in upload of input, metrics by re-simulation on a server, a heatmap in the editor | `sim_mcp/playtest.dart` (`Playtest.heatmap`) | M | +| N8 | **Photo mode:** pause, free camera held inside the level, filters, tiled capture at any resolution, share | `sim/loop/pause_gate.dart`, `camera_rig.dart`, `cpu_png.dart` | S–M | +| N9 | **Colour accessibility:** daltonise and simulate passes, semantic colour roles a player can remap, high contrast with outlines, a lint for hue-only cues | `game/config/accommodations.dart`, `app/surface/scene_semantics.dart` | S–M | +| N10 | **Sharing and ghosts:** content-addressed {level, version, tape} bundles, short codes, a web viewer, ghosts for any genre from pose tracks | `game_racing/ghost.dart`, `sim/save/{tape,replay,demo}.dart`, `cloud/` | M | +| N11 | **Procedural generation:** WFC over brush-kit tiles, hydraulic and thermal erosion on `Heightfield`, `(seed, rules) → Level` in an isolate, a reachability rule, an MCP `generate_level` | `sim/level/{heightfield,level_recipes}.dart`, `editor_core/tool/levels/` | M | +| N12 | **Cinematic sequencer:** camera on a spline, clips and graph parameters, audio, signals, subtitles, fades; in the fixed step, so it replays and rewinds; skippable to the same state | `game/run/demo_timeline.dart`, `model_core/key_table.dart`, `sim/math/spline.dart` | M–L | + +Lifted exclusions (decision 12), each its own item: + +- **N13. `flutter3d_voxel` plus a demo (M–L):** chunks, greedy meshing, + collision against chunks, edits saved as deltas, a small sandbox demo. +- **N14. Networking past two (L):** rollback for 4–8 players in + `flutter3d_net` and `flame_multiplayer`, rooms, matchmaking and a relay, and + spectators fed by the tape; plus an authoritative-server model with state + replication and client prediction for larger matches (decision 15). +- Behaviour on objects and level generators are N3 and N11. + +## 6. Platforms (decision 16) + +- **Windows and Linux:** runners for the demos and the editor; native sides of + `pad_input` and `pointer_lock`; CI desktop jobs already exist and become + required. +- **Android:** the frame numbers on the Galaxy A55 that 0.8 left out + (`doc/pacing/`), committed and gated. +- **WebGPU by default in the browser**, with WebGL2 as the fallback when no + adapter is offered. The 376 KB of JavaScript this costs (measured on the + strategy demo) is accepted; it brings compute to the web, which H11 (GPU + splat sort) and GPU particles can then use. + +## 7. Quality rules (decision 18) + +Four backends agree; every effect has a CPU mirror; a new effect is off by +default until its golden exists in all four sets. Everything in §5 that runs +in the step is tested by digest across operating systems. Each new test moves +the counts `tool/structure.dart` holds README, ARCHITECTURE.md and the site +to. + +## 8. Carried over from 0.8 + +- **Upstream-triggered:** H9 push constants, H10 indirect draw and GPU + culling, H12 subgroups and bindless, H13 Slang, C10 neural texture + compression. **H11, GPU splat sort on WebGPU compute, has its trigger met** + (H6 landed) and moves into 0.9.0, helped by WebGPU becoming the default. +- **Partly done:** H2 GPU timings on WebGL (`EXT_disjoint_timer_query`); N2's + quality table measured per device class instead of on the CPU rasteriser; + N3 pacing on Android (§6). +- **From `plan-status.json`:** `fmt-27` USDZ writer, `gfx-05n` lights by + importance, `gfx-08n` SSAO on by default, `gfx-30n` lens flare (now P2), + `gfx-25n` one switch for the post chain. +- **From the gap analysis still open:** M2 reversed-Z, sea and rivers, terrain + layers and geomorphing, LOD cross-fade, clip planes, outline/matcap/toon + ramp; M3 ribbons, stretched billboards, particle collision, sub-emitters, an + effect format, Doppler; M4 streaming with a VRAM budget, prefab assets with + sockets, a quaternion on `EntityDef`; M5 sockets, more than 64 joints and 8 + morphs, world-space text. These are not all promised for 0.9.0; each is + pulled in when an item above needs it (ragdoll needs P9, the sequencer needs + sockets, and so on). +- **Lab (phase 9):** unchanged, not in 0.9.0. + +## 9. Order + +``` +HR1 ── HR2 ── HR5 ── HR3 ── HR4 (first: decision 14) +P9 ─── N1 (ragdoll, root motion into bodies) +P8 ─── HR1 (materials reload through the coordinator) +P10 independent, touches every example: early enough to write the rest in it +P1 P2 P3 P4 P5 P6 P7 independent, parallel +N2 ── N3 ── N12 (NPCs that walk, decide and act in cutscenes) +N4 ── P12 (desync bisect for agents), N5, N7 +N6 ── N10 +N8 N9 N11 N13 N14 H11 independent +§6 platforms run alongside from the start +``` + +## 10. What one release of all of this costs + +This is larger than 0.8, which was the largest release so far. The risk 0.8 +named is sharper here: packages on pub.dev stay at 0.8.x for months while +flutter_scene ships 0.24 and whatever follows. Three things keep it honest. +Every item lands behind a setting that is off by default, so nothing half +done reaches a user of the branch. Items are merged into `0.9.0` as they +finish, so the branch is always releasable minus what is not in yet. And +§9 puts the visible parity first, so if the owner revisits decision 13, a +parity-only 0.9.0 is the natural cut. + +## 11. Stale now, fixed before the release + +- `site/content/reference/comparison.md` was read against 0.8.1/0.8.2; fix + versions, the bridge's 0.8.4, `flame_multiplayer`, and flutter_scene's new + input package now; revise the whole table for 0.9.0 (decision 19). +- The README and ARCHITECTURE §8.4/§15 say `convert` has no texture encoder; + `formats/ktx2/encode/` has BC1, BC3, ETC2 and ASTC. +- The README package table omits `flame_flutter3d`, `flame_flutter3d_audio`, + `flame_multiplayer*` and `flutter3d_lti`. +- ARCHITECTURE says the dungeon demo has no Windows or Linux runner; it has + both directories. + +## 12. Open questions + +- Custom lighting hooks in P8 (flutter_scene added them on 30 September): in + 0.9.0 or after surface outputs are proven? +- N14's authoritative model: our own replication, or an adapter to an existing + one? `flame_multiplayer_dashwire` already speaks dashwire's transport. +- N10 and N7 need a hosted service: `cloud/` on our own server, or adapters + only? +- Where the editor is published (web, desktop downloads, both), and whether it + needs signing on Windows and macOS. From 7a1143ecffb081b34d7603954ac5dc223b885812 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Wed, 30 Sep 2026 23:08:26 +0300 Subject: [PATCH 003/141] The 0.9 plan answers its own open questions Four more decisions: lighting hooks in user materials land in 0.9.0 after surface outputs, the authoritative network model replicates through flame_multiplayer's own wire and snapshots, sharing and telemetry get client adapters plus a reference server in cloud/, and the level editor ships as a web build and as desktop downloads with full Play. --- tasks/0.9-engine-roadmap.md | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index ae8ed7c7e..aa54e222a 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -7,7 +7,7 @@ inventory of flutter_scene at its published 0.23.0 and at `master` (`b473543`, an unreleased 0.24.0), an inventory of this repository, a study of hot reload in flutter_scene, in Flutter 3.47 and here, and a search for features flutter_scene does not have. Then narrowed by the owner's answers to twenty -questions (§1). +questions (§1), and four more the same day (decisions 21–24). The previous plans stay the record of 0.8: [`0.8-engine-roadmap.md`](0.8-engine-roadmap.md) and [`engine_0_8_plan.md`](engine_0_8_plan.md). What they left open is folded @@ -55,6 +55,10 @@ source of several items here. | 18 | Four backends agree | Kept, no exceptions | | 19 | Comparison page | Fix what is stale now; revise it once for 0.9.0 | | 20 | Voxels | A `flutter3d_voxel` package plus a demo | +| 21 | Lighting hooks in user materials | In 0.9.0, after surface outputs work on all four backends | +| 22 | Replication for the authoritative model | Our own, in `flame_multiplayer`, over `PeerWire` and our snapshots | +| 23 | Service for sharing and telemetry | Adapters in the packages plus a reference server in `cloud/`; our demos use our instance | +| 24 | Editor publication | Web build (without Play of another project) plus desktop downloads with full Play | Not chosen, and so not in this plan: iOS on a device (still simulator only). @@ -186,7 +190,8 @@ compile material-language sources into Impeller, WebGL and WebGPU bundles (the last through the existing translation), load them through `GraphicsDevice.loadShaders`, and let the CPU backend evaluate the same source. Parameters, instance attributes and hot reload through HR1. Custom -lighting hooks come after surface outputs work. +lighting hooks (a BRDF or toon inside the light loop) follow in 0.9.0 once +surface outputs pass on all four backends (decision 21). *Tests:* one material-language source, one golden per backend. ### 4.3 Physics @@ -215,8 +220,10 @@ and goldens. **P11. Editor (L).** Play (HR5); Windows and Linux; docking shell, outliner, component inspector, console, command palette, render-graph inspector; -published like the modeler (web build plus desktop downloads). flutter_scene's -editor is not published, which is the chance to be first. +published like the modeler: a web build, which cannot Play another project, +and desktop downloads for macOS, Windows and Linux with full Play (decision +24). flutter_scene's editor is not published, which is the chance to be +first. **P12. Agent tooling (M).** In a running game, not only the 320×200 CPU render: `list_draws`, `get_draw`, `list_passes`, `pass_output`, `read_pixel`, @@ -251,7 +258,9 @@ Lifted exclusions (decision 12), each its own item: - **N14. Networking past two (L):** rollback for 4–8 players in `flutter3d_net` and `flame_multiplayer`, rooms, matchmaking and a relay, and spectators fed by the tape; plus an authoritative-server model with state - replication and client prediction for larger matches (decision 15). + replication and client prediction for larger matches (decision 15), built + in `flame_multiplayer` over `PeerWire` and our snapshots rather than on + someone else's replication (decision 22). - Behaviour on objects and level generators are N3 and N11. ## 6. Platforms (decision 16) @@ -334,13 +343,10 @@ parity-only 0.9.0 is the natural cut. - ARCHITECTURE says the dungeon demo has no Windows or Linux runner; it has both directories. -## 12. Open questions +## 12. Services -- Custom lighting hooks in P8 (flutter_scene added them on 30 September): in - 0.9.0 or after surface outputs are proven? -- N14's authoritative model: our own replication, or an adapter to an existing - one? `flame_multiplayer_dashwire` already speaks dashwire's transport. -- N10 and N7 need a hosted service: `cloud/` on our own server, or adapters - only? -- Where the editor is published (web, desktop downloads, both), and whether it - needs signing on Windows and macOS. +N7 and N10 need somewhere to send tapes and bundles to. The packages define +the client side and the formats; `cloud/` gets a reference server anyone can +run, and the demos on the site use our instance (decision 23). Moderation of +shared levels and consent for telemetry are that server's job, and the client +never uploads without an explicit opt-in. From 389d3773af38e1f41401d625b60deb05957d0679 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Wed, 30 Sep 2026 23:36:34 +0300 Subject: [PATCH 004/141] A hot reload reaches the picture Flutter reinitializes the engine's shader bundle on hot reload, but the pipelines linked from the old code kept drawing it. HotSwap in flutter3d_app relinks every registered renderer from SceneSurface's reassemble (the Flame bridge draws through it too), refreshes bundles an application loaded from bytes, keeps the last bundle that loaded when a new one is refused, and answers ext.flutter3d.hotSwap. Renderer.relinkShaders now reaches contributors through PassContributor.relinkShaders: particles, mesh particles, splats and the debug overlay drop their pipelines too. Named a swap because reload is a weapon's word in the packages. --- ARCHITECTURE.md | 2 +- README.md | 2 +- packages/flutter3d_app/CHANGELOG.md | 18 ++ packages/flutter3d_app/lib/flutter3d_app.dart | 1 + .../lib/src/hot_swap/hot_swap.dart | 225 ++++++++++++++++++ .../lib/src/surface/scene_surface.dart | 45 +++- .../flutter3d_app/test/hot_swap_test.dart | 204 ++++++++++++++++ packages/flutter3d_core/CHANGELOG.md | 10 + .../engine/render/contributor_registry.dart | 9 + .../lib/src/engine/render/mesh_overlay.dart | 3 + .../lib/src/engine/render/renderer.dart | 7 +- .../src/engine/render/splat_contributor.dart | 7 + packages/flutter3d_particles/CHANGELOG.md | 6 + .../lib/src/mesh_particle_contributor.dart | 3 + .../lib/src/particle_contributor.dart | 10 + site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- tasks/0.9-engine-roadmap.md | 12 +- 18 files changed, 559 insertions(+), 15 deletions(-) create mode 100644 packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart create mode 100644 packages/flutter3d_app/test/hot_swap_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index d68be0670..e52234843 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2688,7 +2688,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10789 tests** across 41 packages and 10 applications | +| Unit tests | **10795 tests** across 41 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 0695c51c3..2c02cc28b 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10789 tests across forty-one packages and nine applications. The +There are 10795 tests across forty-one packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/packages/flutter3d_app/CHANGELOG.md b/packages/flutter3d_app/CHANGELOG.md index 0305647c7..dcbb808b2 100644 --- a/packages/flutter3d_app/CHANGELOG.md +++ b/packages/flutter3d_app/CHANGELOG.md @@ -1,3 +1,21 @@ +## Unreleased + +**A hot reload shows the shaders it reloaded.** Flutter 3.47 reinitializes an +asset-loaded shader bundle on hot reload, which the engine's own bundle is, +but a pipeline linked from the old code went on drawing it: the library +changed and the picture did not. `HotSwap` relinks every renderer +it knows of on a reload, and refreshes the bundles an application loaded +from bytes and registered with `registerLibrary`, which Flutter's reload +never reaches. A bundle that does not load keeps the last one that did and +is named in the report. `SceneSurface` registers its renderer and reloads +from `reassemble`, so a game drawn through it, the Flame bridge's included, +needs nothing more; a tool reaches the same reload as +`ext.flutter3d.hotSwap`. Debug builds only: in profile and release it holds +nothing. + +`SceneSurface` is a `StatefulWidget` now, for `reassemble`. Its constructor +and parameters are unchanged. + ## 0.8.1 **`SceneSurface` draws more than one view.** `moreViews` are drawn into the diff --git a/packages/flutter3d_app/lib/flutter3d_app.dart b/packages/flutter3d_app/lib/flutter3d_app.dart index 3e2e526c0..c59cd17d2 100644 --- a/packages/flutter3d_app/lib/flutter3d_app.dart +++ b/packages/flutter3d_app/lib/flutter3d_app.dart @@ -43,6 +43,7 @@ export 'src/backend_native.dart' if (dart.library.js_interop) 'src/backend_web.dart' show kFixedResolution, openDevice, presentFrame; export 'src/diagnostics/issues.dart'; +export 'src/hot_swap/hot_swap.dart'; export 'src/level/level_loader.dart'; export 'src/level/model_visuals.dart'; export 'src/level/prop_visuals.dart'; diff --git a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart new file mode 100644 index 000000000..df01491a6 --- /dev/null +++ b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart @@ -0,0 +1,225 @@ +/// What a hot reload does to a running 3D world, in debug builds. +library; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:developer' as developer; + +import 'package:flutter/foundation.dart'; +import 'package:flutter3d/flutter3d.dart'; + +/// Reads the current bytes of a shader bundle an application loaded itself, +/// or null when they cannot be read now. +typedef ShaderBundleSource = Future Function(); + +/// The renderers and shader libraries a running application has, and what to +/// do with them when its code or its shaders change under it. +/// +/// **Flutter already swaps the bundle; nothing redrew with it.** The engine's +/// own bundle is loaded from an asset, and Flutter 3.47 reinitializes a +/// library loaded that way on every hot reload whose shaders changed. A +/// library keeps its handles through that, but a pipeline linked from the old +/// code keeps drawing it until it is linked again, so the new shaders reached +/// the library and never the picture. [swap] relinks every renderer this +/// knows of, which is the engine's half of the reload, and refreshes the +/// bundles an application loaded from bytes, which Flutter's half never +/// reaches. +/// +/// **Relinked every time, not only when a bundle changed.** Whether Flutter +/// reinitialized the engine's bundle is not observable from here, and a +/// relink costs one link per pipeline on the next frame — the cost of the +/// first frame — once per hot reload, in a debug build. +/// +/// **A bundle that does not load keeps the last one that did.** A refused +/// refresh leaves its library as it was (`LoadedShaderLibrary.refresh` +/// promises that), so a shader with a mistake in it costs a message in the +/// report, not the picture. +/// +/// Debug builds only: in a profile or release build every method returns at +/// once and holds nothing. What reaches it: `SceneSurface` and the Flame +/// bridge's widget register their renderer and call [swap] from +/// `reassemble`, which is what a hot reload runs; an application registers +/// the bundles it loads itself with [registerLibrary]; and a tool reaches the +/// same thing through the VM service as `ext.flutter3d.hotSwap`. +/// +/// A swap and not a reload in its names because `reload` is a weapon's here: +/// CONTRIBUTING.md keeps that word out of the packages, and the structure +/// check holds them to it. +final class HotSwap { + /// A coordinator of its own, for a test. Everything else uses [instance]. + @visibleForTesting + HotSwap({this.enabled = kDebugMode}); + + /// The one every widget and application registers with. + static final HotSwap instance = HotSwap(); + + /// False in profile and release builds, where nothing reloads. + final bool enabled; + + final List> _renderers = >[]; + final List<_Library> _libraries = <_Library>[]; + + /// Counts the reloads that finished, for a host that draws only on demand + /// and has to draw once more to show the new shaders. + final ValueNotifier swaps = ValueNotifier(0); + + Future? _running; + static bool _extensionRegistered = false; + + /// Remembers [renderer] until it is garbage, to relink it on [swap]. + void registerRenderer(Renderer renderer) { + if (!enabled) return; + _renderers.removeWhere((WeakReference r) => r.target == null); + if (_renderers.any((WeakReference r) => r.target == renderer)) { + return; + } + _renderers.add(WeakReference(renderer)); + _registerExtension(); + } + + /// Remembers [library], loaded from bytes by the application, to refresh + /// it on [swap] from whatever [read] returns then. + /// + /// A bundle loaded from an asset by `flutter_gpu` itself needs no + /// registration: Flutter reinitializes those. One loaded through + /// `GraphicsDevice.loadShaders` from bytes the application read does, + /// because nothing else knows where the bytes came from. + void registerLibrary( + LoadedShaderLibrary library, { + required ShaderBundleSource read, + ByteData? loadedFrom, + }) { + if (!enabled) return; + _libraries.removeWhere((_Library l) => l.library.target == null); + _libraries.add( + _Library( + WeakReference(library), + read, + loadedFrom == null ? null : _fingerprint(loadedFrom), + ), + ); + _registerExtension(); + } + + /// Refreshes the registered bundles whose bytes changed, then relinks every + /// live renderer. + /// + /// Several surfaces reassembling in one hot reload share one run: a call + /// while one is in flight gets that one's report. + Future swap() { + if (!enabled) return Future.value(HotSwapReport.none); + return _running ??= _swap().whenComplete(() => _running = null); + } + + Future _swap() async { + final refreshed = []; + final refused = []; + for (final entry in List<_Library>.of(_libraries)) { + final library = entry.library.target; + if (library == null) continue; + final ByteData? bytes; + try { + bytes = await entry.read(); + } on Object catch (error) { + refused.add('${library.name}: could not be read ($error)'); + continue; + } + if (bytes == null) continue; + final fingerprint = _fingerprint(bytes); + if (fingerprint == entry.fingerprint) continue; + try { + library.refresh(bytes); + entry.fingerprint = fingerprint; + refreshed.add(library.name); + } on ShaderBundleRefused catch (refusal) { + // The library is as it was; the next reload tries these bytes again + // only if they change, since the same bytes would be refused again. + entry.fingerprint = fingerprint; + refused.add('${library.name}: ${refusal.reason}'); + } + } + _renderers.removeWhere((WeakReference r) => r.target == null); + for (final renderer in _renderers) { + renderer.target?.relinkShaders(); + } + final report = HotSwapReport( + renderers: _renderers.length, + refreshed: List.unmodifiable(refreshed), + refused: List.unmodifiable(refused), + ); + for (final line in refused) { + debugPrint('flutter3d: kept the last shaders that loaded, $line'); + } + swaps.value++; + return report; + } + + void _registerExtension() { + if (_extensionRegistered || !identical(this, instance)) return; + _extensionRegistered = true; + developer.registerExtension('ext.flutter3d.hotSwap', ( + String method, + Map parameters, + ) async { + final report = await instance.swap(); + return developer.ServiceExtensionResponse.result( + jsonEncode(report.toJson()), + ); + }); + } + + /// FNV-1a over the bundle's bytes: enough to tell a changed file from an + /// unchanged one, which is all a reload asks. + static int _fingerprint(ByteData bytes) { + final data = bytes.buffer.asUint8List( + bytes.offsetInBytes, + bytes.lengthInBytes, + ); + var hash = 0x811c9dc5; + for (final byte in data) { + hash = ((hash ^ byte) * 0x01000193) & 0xffffffff; + } + return hash ^ data.length; + } +} + +/// What one [HotSwap.swap] did. +@immutable +final class HotSwapReport { + const HotSwapReport({ + required this.renderers, + required this.refreshed, + required this.refused, + }); + + /// What a profile or release build reports: nothing was touched. + static const HotSwapReport none = HotSwapReport( + renderers: 0, + refreshed: [], + refused: [], + ); + + /// How many renderers were relinked. + final int renderers; + + /// The bundles that changed and loaded. + final List refreshed; + + /// The bundles that changed and did not load, each with the reason. Their + /// libraries kept the last bundle that did. + final List refused; + + Map toJson() => { + 'renderers': renderers, + 'refreshed': refreshed, + 'refused': refused, + }; +} + +final class _Library { + _Library(this.library, this.read, this.fingerprint); + + final WeakReference library; + final ShaderBundleSource read; + int? fingerprint; +} diff --git a/packages/flutter3d_app/lib/src/surface/scene_surface.dart b/packages/flutter3d_app/lib/src/surface/scene_surface.dart index 4feb16142..b35ae2806 100644 --- a/packages/flutter3d_app/lib/src/surface/scene_surface.dart +++ b/packages/flutter3d_app/lib/src/surface/scene_surface.dart @@ -1,6 +1,10 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter3d/flutter3d.dart' hide Material; +import '../hot_swap/hot_swap.dart'; + /// The shape of `presentFrame`, and of every presenter a backend registers. /// /// A parameter of [SceneSurface] rather than a call it makes: `presentFrame` @@ -46,7 +50,12 @@ typedef FramePresenter = /// /// So the two games do the same thing in two different places, and only one of /// the two places works for both. A function called here is that place. -class SceneSurface extends StatelessWidget { +/// +/// **A hot reload relinks the renderer.** In a debug build the surface +/// registers its [renderer] with [HotSwap] and runs a reload from +/// `reassemble`, so an edited shader is drawn on the next frame with the world +/// as it was — see [HotSwap] for what that does and why. +class SceneSurface extends StatefulWidget { const SceneSurface({ super.key, required this.renderer, @@ -85,8 +94,42 @@ class SceneSurface extends StatelessWidget { /// parameter rather than an import. final FramePresenter presentFrame; + @override + State createState() => _SceneSurfaceState(); +} + +class _SceneSurfaceState extends State { + @override + void initState() { + super.initState(); + HotSwap.instance.registerRenderer(widget.renderer); + } + + @override + void didUpdateWidget(SceneSurface oldWidget) { + super.didUpdateWidget(oldWidget); + if (!identical(oldWidget.renderer, widget.renderer)) { + HotSwap.instance.registerRenderer(widget.renderer); + } + } + + @override + void reassemble() { + super.reassemble(); + unawaited(HotSwap.instance.swap()); + } + @override Widget build(BuildContext context) { + final SceneSurface( + :renderer, + :scene, + :view, + :settings, + :onBeforeFrame, + :presentFrame, + :moreViews, + ) = widget; final dpr = MediaQuery.devicePixelRatioOf(context); return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { diff --git a/packages/flutter3d_app/test/hot_swap_test.dart b/packages/flutter3d_app/test/hot_swap_test.dart new file mode 100644 index 000000000..113564942 --- /dev/null +++ b/packages/flutter3d_app/test/hot_swap_test.dart @@ -0,0 +1,204 @@ +/// What a hot reload does to a running world: relink every renderer, refresh +/// the bundles an application loaded itself, and keep the last bundle that +/// loaded when a new one does not. +/// +/// flutter test test/hot_swap_test.dart +library; + +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter3d/flutter3d.dart' hide Material; +import 'package:flutter3d/flutter3d.dart' as engine show Material; +import 'package:flutter3d_app/flutter3d_app.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart' show Vector3; + +/// A renderer in software with one cube in front of it, so a frame links a +/// material pipeline a reload has to drop. +({Renderer renderer, Scene scene, RenderView view}) _stage() { + final device = CpuDevice( + width: 16, + height: 9, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final camera = CameraNode()..setPosition(0.0, 0.0, 4.0); + final scene = Scene() + ..add(camera) + ..add( + MeshNode( + DeviceMesh.upload(device, CuboidShape(size: Vector3.all(1.0)).build()), + engine.Material(name: 'cube'), + ), + ); + return ( + renderer: Renderer.create(device: device), + scene: scene, + view: RenderView(camera: camera), + ); +} + +void _draw(({Renderer renderer, Scene scene, RenderView view}) it) => + it.renderer.render( + width: 16, + height: 9, + scene: it.scene, + views: [it.view], + ); + +/// Counts the times a renderer asked it to drop what it linked. +final class _Contributor extends PassContributor { + int relinks = 0; + + @override + bool get isActive => false; + + @override + void encode(ContributorFrame frame) {} + + @override + void relinkShaders() => relinks++; +} + +/// A bundle an application loaded from bytes, refreshed from what [bytes] +/// says now; refuses whatever starts with a zero byte. +final class _Library implements LoadedShaderLibrary { + final List refreshes = []; + + @override + String get name => 'game'; + + @override + ShaderHandle? operator [](String name) => null; + + @override + void refresh(ByteData bytes) { + if (bytes.getUint8(0) == 0) { + throw const ShaderBundleRefused(name: 'game', reason: 'not a bundle'); + } + refreshes.add(bytes); + } +} + +ByteData _bytes(List values) => + ByteData.sublistView(Uint8List.fromList(values)); + +void main() { + test('a reload drops every pipeline the renderer and its contributors ' + 'linked', () async { + final it = _stage(); + final contributor = it.renderer.addContributor(_Contributor()); + final swap = HotSwap(enabled: true) + ..registerRenderer(it.renderer); + _draw(it); + expect(it.renderer.pipelineCount, greaterThan(0)); + + final report = await swap.swap(); + + expect(report.renderers, 1); + expect(it.renderer.pipelineCount, 0); + expect(contributor.relinks, 1); + // And the next frame links them again: the reload costs one frame's + // links, not the picture. + _draw(it); + expect(it.renderer.pipelineCount, greaterThan(0)); + }); + + test('a bundle is refreshed when its bytes change, and only then', () async { + final library = _Library(); + var current = _bytes([1, 2, 3]); + final swap = HotSwap( + enabled: true, + )..registerLibrary(library, read: () async => current, loadedFrom: current); + + expect((await swap.swap()).refreshed, isEmpty); + expect(library.refreshes, isEmpty); + + current = _bytes([1, 2, 4]); + expect((await swap.swap()).refreshed, ['game']); + expect(library.refreshes, hasLength(1)); + + expect((await swap.swap()).refreshed, isEmpty); + expect(library.refreshes, hasLength(1)); + }); + + test('a bundle that does not load keeps the last one that did, and is ' + 'not tried again until it changes', () async { + final library = _Library(); + var current = _bytes([1, 2, 3]); + final swap = HotSwap( + enabled: true, + )..registerLibrary(library, read: () async => current, loadedFrom: current); + + current = _bytes([0, 9]); + final refused = await swap.swap(); + expect(refused.refused, hasLength(1)); + expect(refused.refused.single, contains('not a bundle')); + expect(library.refreshes, isEmpty); + + expect((await swap.swap()).refused, isEmpty); + + current = _bytes([1, 9]); + expect((await swap.swap()).refreshed, ['game']); + }); + + test('two surfaces reassembling in one hot reload share one run', () async { + final it = _stage(); + final contributor = it.renderer.addContributor(_Contributor()); + final swap = HotSwap(enabled: true) + ..registerRenderer(it.renderer) + ..registerRenderer(it.renderer); + + await Future.wait(>[swap.swap(), swap.swap()]); + + expect(contributor.relinks, 1); + }); + + test('outside a debug build nothing is held and nothing reloads', () async { + final it = _stage(); + final contributor = it.renderer.addContributor(_Contributor()); + final swap = HotSwap(enabled: false) + ..registerRenderer(it.renderer); + + final report = await swap.swap(); + + expect(report.renderers, 0); + expect(contributor.relinks, 0); + }); + + testWidgets('a hot reload of the app relinks the renderer a surface draws ' + 'with', (WidgetTester tester) async { + final it = _stage(); + final contributor = it.renderer.addContributor(_Contributor()); + await tester.pumpWidget( + MaterialApp( + home: SceneSurface( + renderer: it.renderer, + scene: it.scene, + view: it.view, + onBeforeFrame: () {}, + settings: () => const RenderSettings(), + presentFrame: + ( + GraphicsDevice device, + TextureHandle frame, { + BoxFit fit = BoxFit.fill, + FilterQuality quality = FilterQuality.none, + }) => const SizedBox.shrink(), + ), + ), + ); + final before = HotSwap.instance.swaps.value; + + // Not awaited: it completes at the end of a frame, and a widget test has + // frames only when it pumps them. + unawaited(tester.binding.reassembleApplication()); + await tester.pump(); + await tester.pump(); + + expect(contributor.relinks, 1); + expect(HotSwap.instance.swaps.value, before + 1); + }); +} diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index e58ed2a77..25bfb626c 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -1,3 +1,13 @@ +## Unreleased + +- **A reload reaches the contributors too.** `Renderer.relinkShaders` asks + every `PassContributor` to drop what it linked, through the new + `PassContributor.relinkShaders`, which does nothing by default. + `SplatContributor` and `MeshOverlay` drop their pipelines; particles, whose + contributors live in `flutter3d_particles`, do the same there. Before this a + reloaded shader reached the scene's own pipelines and not the splats, the + debug lines or the particles. + ## 0.8.3 - **`Renderer.renderPost` gives its bloom chain back.** Its pooled targets diff --git a/packages/flutter3d_core/lib/src/engine/render/contributor_registry.dart b/packages/flutter3d_core/lib/src/engine/render/contributor_registry.dart index abb97ee56..f53e5ec28 100644 --- a/packages/flutter3d_core/lib/src/engine/render/contributor_registry.dart +++ b/packages/flutter3d_core/lib/src/engine/render/contributor_registry.dart @@ -49,6 +49,15 @@ abstract base class PassContributor { /// particles, splats, anything without a depth of its own — redraws it here /// through [ReactiveFrame.spriteStage] or a stage of its own. void encodeReactive(ReactiveFrame frame) {} + + /// Drops every pipeline this contributor has linked, so its next frame + /// links them again from whatever the stages are now. + /// + /// `Renderer.relinkShaders` calls it on each contributor after dropping its + /// own: a hot reload swaps the code behind a stage and keeps the handle, + /// and a pipeline built from the old code would go on drawing it. Nothing + /// by default, for a contributor that links nothing of its own. + void relinkShaders() {} } /// The contributors a renderer draws, and the order it draws them in. diff --git a/packages/flutter3d_core/lib/src/engine/render/mesh_overlay.dart b/packages/flutter3d_core/lib/src/engine/render/mesh_overlay.dart index f60cd913c..b5fa721a6 100644 --- a/packages/flutter3d_core/lib/src/engine/render/mesh_overlay.dart +++ b/packages/flutter3d_core/lib/src/engine/render/mesh_overlay.dart @@ -306,6 +306,9 @@ final class MeshOverlay extends PassContributor { PipelineHandle? _pipeline; + @override + void relinkShaders() => _pipeline = null; + /// The index sequence 0, 1, 2, … that every draw here is made through. /// /// **Not an optimisation and not a formality: `CommandEncoder.draw` in this diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer.dart b/packages/flutter3d_core/lib/src/engine/render/renderer.dart index daedd0b6a..6b51491a8 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer.dart @@ -873,9 +873,12 @@ final class Renderer implements RenderServices { /// renderer to reload. /// /// Contributors keep their own pipelines — `ParticleContributor` links once - /// and holds — and are not reached from here; a contributor that wants to - /// follow a reload exposes its own way to drop what it linked. + /// and holds — so each is asked to drop what it linked, through + /// [PassContributor.relinkShaders]. void relinkShaders() { + for (final contributor in contributors.all) { + contributor.relinkShaders(); + } _pipelineCache.clear(); _fragmentShaders.clear(); _fullscreenPipelines.clear(); diff --git a/packages/flutter3d_core/lib/src/engine/render/splat_contributor.dart b/packages/flutter3d_core/lib/src/engine/render/splat_contributor.dart index bf08c4d88..732014d80 100644 --- a/packages/flutter3d_core/lib/src/engine/render/splat_contributor.dart +++ b/packages/flutter3d_core/lib/src/engine/render/splat_contributor.dart @@ -496,6 +496,13 @@ final class SplatContributor extends PassContributor { PipelineHandle? _pipeline; PipelineHandle? _hashedPipeline; + @override + void relinkShaders() { + _pipeline = null; + _hashedPipeline = null; + _reactivePipeline = null; + } + /// The index sequence 0, 1, 2, … every draw in this engine needs — see /// `MeshOverlay._identityIndices`, which keeps the same sequence for the /// same reason: `CommandEncoder.draw` has no unindexed path, and a draw diff --git a/packages/flutter3d_particles/CHANGELOG.md b/packages/flutter3d_particles/CHANGELOG.md index 5928bad77..896afd647 100644 --- a/packages/flutter3d_particles/CHANGELOG.md +++ b/packages/flutter3d_particles/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +* **Particles follow a shader reload.** `ParticleContributor` and + `MeshParticleContributor` drop their pipelines when the renderer relinks, + through `flutter3d_core`'s `PassContributor.relinkShaders`. + ## 0.8.1 * **A mesh particle can take light away.** `MeshParticleContributor` takes a diff --git a/packages/flutter3d_particles/lib/src/mesh_particle_contributor.dart b/packages/flutter3d_particles/lib/src/mesh_particle_contributor.dart index 5bf3064e3..9d79f5627 100644 --- a/packages/flutter3d_particles/lib/src/mesh_particle_contributor.dart +++ b/packages/flutter3d_particles/lib/src/mesh_particle_contributor.dart @@ -247,4 +247,7 @@ final class MeshParticleContributor extends PassContributor { final vm.Vector3 _eye = vm.Vector3.zero(); PipelineHandle? _pipeline; Float32List? _instances; + + @override + void relinkShaders() => _pipeline = null; } diff --git a/packages/flutter3d_particles/lib/src/particle_contributor.dart b/packages/flutter3d_particles/lib/src/particle_contributor.dart index b3f0dd582..de68a9c75 100644 --- a/packages/flutter3d_particles/lib/src/particle_contributor.dart +++ b/packages/flutter3d_particles/lib/src/particle_contributor.dart @@ -464,6 +464,16 @@ final class ParticleContributor extends PassContributor { int? _reactiveDevice; PipelineHandle? _reactivePipeline; + /// Forgets the device the pipelines were keyed on, which drops them all: + /// the next frame links them again from the reloaded stages. + @override + void relinkShaders() { + _pipelines.clear(); + _pipelineDevice = null; + _reactivePipeline = null; + _reactiveDevice = null; + } + final Set _missing = {}; final Float32List _fog = Float32List(4); diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 704817f60..d7934b0ca 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10789 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10795 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 7a11e8a6d..1a756b9fc 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10789 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10795 tests need a GPU. --- # Testing -10789 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10795 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -26,7 +26,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 28 | | `apps/flutter3d_demo_river` | 53 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 90 | -| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 118 | +| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 124 | | `flutter3d_game` | 255 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10763 rather than 10789: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10769 rather than 10795: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index aa54e222a..a0f1ee9fa 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -108,11 +108,13 @@ What this repository already has: editor's `timeline_client.dart` are a live link to a running game already. **HR1. Shaders and Dart code without losing the world (S).** A debug-only -`ReloadCoordinator` in `flutter3d` with WeakReference registrations and -injected file access, so its logic is plain unit tests. The game widget -overrides `reassemble()` and calls it; everything is also reachable as -`ext.flutter3d.reload.*`. On a changed bundle: `relinkShaders()`, plus a relink -hook for contributors that keep their own pipelines (`ParticleContributor`). +`HotSwap` in `flutter3d_app` (not `Reload…`: CONTRIBUTING.md keeps `reload` +out of the packages as a weapon's word) with WeakReference registrations and +injected file access, so its logic is plain unit tests. `SceneSurface`, which +the Flame bridge draws through too, overrides `reassemble()` and calls it; it +is also reachable as `ext.flutter3d.hotSwap`. On every swap: `relinkShaders()`, +which now reaches contributors through `PassContributor.relinkShaders` +(particles, mesh particles, splats, the debug overlay). Custom `.f3dshaders` loaded from bytes are refreshed by the coordinator, which also covers the web, where flutter_tools does not reinitialize bundles. Documented limit: a stage newly added under a name already looked up needs a From 2d1e8086ca2ab6f3206a0be8572caf4c3fa3377e Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Wed, 30 Sep 2026 23:46:36 +0300 Subject: [PATCH 005/141] An edited model lands in the nodes the game holds ModelInstance.adopt matches surfaces by node name and slot and hands each MeshNode the new mesh and material, so transforms, animation and anything hung from the nodes survive; what the new file adds or drops is reported. HotSwap.loadModel reads and fingerprints the converted file, and a swap after it changed rebuilds the asset and has every tracked instance adopt it. ext.flutter3d.assets.put does the same from bytes sent over the VM service, for a device the editor cannot write to. A model that does not build keeps the version that did. --- ARCHITECTURE.md | 2 +- README.md | 2 +- packages/flutter3d/CHANGELOG.md | 9 + packages/flutter3d/README.md | 4 +- .../lib/src/engine/assets/model_instance.dart | 131 +++++++- packages/flutter3d/test/model_adopt_test.dart | 186 +++++++++++ packages/flutter3d_app/CHANGELOG.md | 7 + .../lib/src/hot_swap/hot_swap.dart | 312 +++++++++++++++++- .../flutter3d_app/test/hot_swap_test.dart | 113 ++++++- site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- tasks/0.9-engine-roadmap.md | 30 +- 12 files changed, 769 insertions(+), 39 deletions(-) create mode 100644 packages/flutter3d/test/model_adopt_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index e52234843..4d75772c4 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2688,7 +2688,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10795 tests** across 41 packages and 10 applications | +| Unit tests | **10802 tests** across 41 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 2c02cc28b..31d3dfc05 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10795 tests across forty-one packages and nine applications. The +There are 10802 tests across forty-one packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/packages/flutter3d/CHANGELOG.md b/packages/flutter3d/CHANGELOG.md index 90ff97a3f..37121ff1a 100644 --- a/packages/flutter3d/CHANGELOG.md +++ b/packages/flutter3d/CHANGELOG.md @@ -1,3 +1,12 @@ +## Unreleased + +- **`ModelInstance.adopt` draws a new version of a model in the nodes an + instance already has.** Surfaces are matched by the name of the node that + draws them and their place there; each matched `MeshNode` takes the new + mesh and material and the nodes stay, with their transforms, animation and + children. `ModelSwap` reports what matched, what the new version dropped + (left drawn as it was) and what it added (needs instantiating again). + ## 0.8.3 * **`ModelWardrobe` puts models on nodes made before they arrived.** A game diff --git a/packages/flutter3d/README.md b/packages/flutter3d/README.md index ad02f2f60..fe9ee3f4e 100644 --- a/packages/flutter3d/README.md +++ b/packages/flutter3d/README.md @@ -104,7 +104,7 @@ What works today: `CUBICSPLINE` with authored tangents), slerped rotations, an `AnimationPlayer` with play/pause/seek/speed and once/loop/ping-pong, and the decoded node hierarchy rebuilt on instantiation so an animated parent carries its subtree; -- 1616 tests covering projection, scene, sorting, debug draw, raycasting, +- 1620 tests covering projection, scene, sorting, debug draw, raycasting, animation, skinning, lighting, render targets, BVH, LOD, glTF, OBJ and `.f3d`, plus a real frame drawn through `flutter3d_cpu`'s software rasteriser for the ones that need one, all without a GPU. The geometry the engine is written in @@ -322,7 +322,7 @@ lib/src/engine/assets/ glTF, OBJ and .f3d decoders, isolate loading, ca example/lib/ the demo, and the frame capture hook skills/ the conventions, as agent skills — see below bin/skills.dart what copies them into a project that uses this -test/ 1616 tests, all runnable without a GPU +test/ 1620 tests, all runnable without a GPU ``` The GLSL is not here. Every shader this package draws with lives in diff --git a/packages/flutter3d/lib/src/engine/assets/model_instance.dart b/packages/flutter3d/lib/src/engine/assets/model_instance.dart index cbc3feed7..c469da3b5 100644 --- a/packages/flutter3d/lib/src/engine/assets/model_instance.dart +++ b/packages/flutter3d/lib/src/engine/assets/model_instance.dart @@ -25,10 +25,12 @@ final class ModelInstance { required this.skeletons, required this.player, this.variants = const [], - this._drawn = const <(MeshNode, ModelPart)>[], + List<(MeshNode, ModelPart)> drawn = const <(MeshNode, ModelPart)>[], + this._slots = const {}, Material Function(Material)? materialFor, PointerTargets? pointerTargets, - }) : _materialFor = materialFor ?? _same, + }) : _drawn = List<(MeshNode, ModelPart)>.of(drawn), + _materialFor = materialFor ?? _same, pointerTargets = pointerTargets ?? PointerTargets(); static Material _same(Material material) => material; @@ -37,8 +39,15 @@ final class ModelInstance { final List variants; /// Each mesh node with the part it was built from, for [selectVariant]. + /// + /// Its entries are replaced by [adopt], so a variant chosen after a swap + /// dresses the new parts. final List<(MeshNode, ModelPart)> _drawn; + /// Where in [_drawn] each drawn surface sits, by [ModelAssetInstantiate]'s + /// slot key: the node's name and which of its surfaces or levels this is. + final Map _slots; + /// The instance's own copy of an asset material, or the material itself /// when materials are shared — see [ModelAssetInstantiate.instantiate]. final Material Function(Material) _materialFor; @@ -79,6 +88,54 @@ final class ModelInstance { return true; } + /// Draws [next]'s meshes and materials in this instance's nodes, keeping + /// the nodes themselves: where the game put them, what the animation is + /// doing to them, what hangs from them. + /// + /// **For a model edited while the game runs.** The file changed on disk, or + /// arrived over the VM service; instantiating it again would build new + /// nodes, and every reference the game holds to the old ones — a hand a + /// sword hangs from, a node a camera follows — would point at a model no + /// longer drawn. So the surfaces are matched instead, by the name of the + /// node that draws them and their place among that node's surfaces, and + /// each matched [MeshNode] is handed the new geometry and material. + /// + /// What does not follow: a surface under a node [next] names differently, + /// or one it adds, is reported in [ModelSwap.added] and needs the model + /// instantiated again; one [next] no longer has is left drawn as it was and + /// reported in [ModelSwap.kept]. A node moved in the file stays where it + /// was here, since here it is the game's. The skeleton stays the one the + /// instance was built with, bound to the same nodes. + /// + /// The variant chosen with [selectVariant] is chosen again from [next]'s + /// parts. The old asset is not released: other instances may still draw it + /// until they adopt too, and the caller that loaded it knows when none do. + ModelSwap adopt(ModelAsset next) { + final nextSlots = next._slotParts(); + final kept = []; + var swapped = 0; + for (final MapEntry(key: slot, value: index) in _slots.entries) { + final part = nextSlots[slot]; + if (part == null) { + kept.add(slot); + continue; + } + final (mesh, _) = _drawn[index]; + mesh.mesh = part.mesh; + _drawn[index] = (mesh, part); + swapped++; + } + selectVariant(_variant); + return ModelSwap( + swapped: swapped, + kept: List.unmodifiable(kept), + added: List.unmodifiable([ + for (final slot in nextSlots.keys) + if (!_slots.containsKey(slot)) slot, + ]), + ); + } + /// Lets animation pointer tracks aimed at the file's light [index] drive /// [light]. Instantiating a model creates no lights, so a clip that /// animates one reaches only a light the caller has put in its place. @@ -103,6 +160,28 @@ final class ModelInstance { void removeFromScene() => root.removeFromParent(); } +/// What one [ModelInstance.adopt] did, by slot key: a node's name, then `/` +/// and which of its surfaces (`s0`, `s1`, …) or level surfaces (`l0`, …). +final class ModelSwap { + const ModelSwap({ + required this.swapped, + required this.kept, + required this.added, + }); + + /// Surfaces drawn from the new asset now. + final int swapped; + + /// Surfaces the new asset does not have, still drawn as they were. + final List kept; + + /// Surfaces only the new asset has; instantiate it again to draw them. + final List added; + + /// Whether the instance now draws everything the new asset has. + bool get complete => added.isEmpty; +} + /// `instantiate` and its helpers, added to [ModelAsset]. /// /// Named and public — unlike the `part`s in the gltf/OBJ/f3d decoders, where @@ -179,6 +258,7 @@ extension ModelAssetInstantiate on ModelAsset { final created = List.filled(nodes.length, null); final meshNodes = []; final drawn = <(MeshNode, ModelPart)>[]; + final slots = {}; final materials = {}; // Skeletons are attached after the walk: a joint may be created later than // the mesh that references it, so binding as we go would capture nulls. @@ -218,7 +298,7 @@ extension ModelAssetInstantiate on ModelAsset { parentNode.add(node); created[index] = node; - MeshNode? addSurface(int surfaceIndex) { + MeshNode? addSurface(int surfaceIndex, String slot) { if (surfaceIndex < 0 || surfaceIndex >= parts.length) return null; final part = parts[surfaceIndex]; final mesh = MeshNode( @@ -227,6 +307,9 @@ extension ModelAssetInstantiate on ModelAsset { name: part.name, ); meshNodes.add(mesh); + if (_slotKey(model.name, slot) case final String key) { + slots.putIfAbsent(key, () => drawn.length); + } drawn.add((mesh, part)); if (part.skinIndex != null) pendingSkins.add((mesh, part.skinIndex!)); @@ -262,11 +345,11 @@ extension ModelAssetInstantiate on ModelAsset { (lod) => lod.surfaceIndices.length == 1 || lod.impostor != null, ); if (model.lods.isNotEmpty && singleSurfaceLevels) { - final baseMesh = addSurface(model.surfaces.single); + final baseMesh = addSurface(model.surfaces.single, 's0'); if (baseMesh != null) { final levels = [ LodLevel(node: baseMesh, maxScreenFraction: 2.0), - for (final lod in model.lods) + for (final (level, lod) in model.lods.indexed) // `C4`: the card a chain ends in, drawn from the atlases the // asset uploaded once for every instance. if (impostors[lod.impostor] case final ImpostorPart part) @@ -282,7 +365,8 @@ extension ModelAssetInstantiate on ModelAsset { maxScreenFraction: lod.maxScreenFraction, ) else if (lod.surfaceIndices.length == 1) - if (addSurface(lod.surfaceIndices.single) case final MeshNode m) + if (addSurface(lod.surfaceIndices.single, 'l$level') + case final MeshNode m) // The group sits at the node with no transform of its // own, so the node's units the error is measured in are // the group's. @@ -295,8 +379,8 @@ extension ModelAssetInstantiate on ModelAsset { node.add(LodGroup(levels: levels, name: model.name)); } } else { - for (final surfaceIndex in model.surfaces) { - final mesh = addSurface(surfaceIndex); + for (final (slot, surfaceIndex) in model.surfaces.indexed) { + final mesh = addSurface(surfaceIndex, 's$slot'); if (mesh != null) node.add(mesh); } } @@ -348,6 +432,7 @@ extension ModelAssetInstantiate on ModelAsset { ), variants: variants, drawn: drawn, + slots: slots, materialFor: materialFor, pointerTargets: pointerTargets, ); @@ -392,6 +477,36 @@ extension ModelAssetInstantiate on ModelAsset { }; static Material _copyMaterial(Material source) => source.copy(); + + /// The part each slot of this asset draws, keyed as [instantiate] keys + /// them, for [ModelInstance.adopt] to match against. + Map _slotParts() { + final found = {}; + void put(String? node, String slot, int surface) { + if (surface < 0 || surface >= parts.length) return; + if (_slotKey(node, slot) case final String key) { + found.putIfAbsent(key, () => parts[surface]); + } + } + + for (final node in nodes) { + for (final (slot, surface) in node.surfaces.indexed) { + put(node.name, 's$slot', surface); + } + for (final (level, lod) in node.lods.indexed) { + if (lod.surfaceIndices.length == 1) { + put(node.name, 'l$level', lod.surfaceIndices.single); + } + } + } + return found; + } + + /// A drawn surface's name across two versions of one file: the node's name + /// and its place there. An unnamed node matches nothing, since two + /// unnamed nodes cannot be told apart. + static String? _slotKey(String? node, String slot) => + node == null ? null : '$node/$slot'; } /// A weights track reaching every primitive of one split mesh. diff --git a/packages/flutter3d/test/model_adopt_test.dart b/packages/flutter3d/test/model_adopt_test.dart new file mode 100644 index 000000000..3e05564ef --- /dev/null +++ b/packages/flutter3d/test/model_adopt_test.dart @@ -0,0 +1,186 @@ +/// A model edited while the game runs is drawn in the nodes the game holds. +/// +/// flutter test test/model_adopt_test.dart +/// +/// `ModelInstance.adopt` matches surfaces by the name of the node that draws +/// them and their place there, so the nodes survive and only what they draw +/// changes. Drawn on the CPU device, because "the new model is on screen" is +/// a claim about pixels. +/// +/// Mutation: drop the `selectVariant` call at the end of `adopt` and the +/// frame stays red; key the slots on the part's name instead of the node's +/// and the renamed-part case finds nothing to swap. +library; + +import 'dart:typed_data'; + +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +const int _size = 32; + +MeshData _quad(double half) => MeshData( + layout: VertexLayout.standard, + vertices: Float32List.fromList([ + // position, normal, texcoord, tangent, colour + -half, -half, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, + half, -half, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, + half, half, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + -half, half, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, + ]), + indices: Uint32List.fromList([0, 1, 2, 0, 2, 3]), +); + +/// A hull and, when [withFlag], a flag: one quad each, unlit in [colour]. +PlainModelDocument _ship( + Vector4 colour, { + bool withFlag = false, + String hull = 'hull', + String? partName, +}) => PlainModelDocument( + surfaces: [ + ModelSurface(mesh: _quad(1.0), materialIndex: 0, name: partName), + if (withFlag) ModelSurface(mesh: _quad(0.1), materialIndex: 0), + ], + materials: [SurfaceMaterial(baseColor: colour, unlit: true)], + nodes: [ + ModelNode(name: hull, surfaces: [0]), + if (withFlag) ModelNode(name: 'flag', surfaces: [1]), + ], +); + +({CpuDevice device, Renderer renderer}) _engine() { + final device = CpuDevice( + width: _size, + height: _size, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final flat = device.createTextureFromPixels( + width: 1, + height: 1, + format: TextureFormat.r8g8b8a8UNormInt, + pixels: ByteData.sublistView(Uint8List.fromList([128, 128, 255, 255])), + )!; + return ( + device: device, + renderer: Renderer.create(device: device, fallbackNormal: flat), + ); +} + +Future> _centre( + ({CpuDevice device, Renderer renderer}) engine, + Scene scene, +) async { + final camera = CameraNode() + ..setPosition(0.0, 0.0, 3.0) + ..lookAt(Vector3.zero()); + final frame = engine.renderer.render( + width: _size, + height: _size, + scene: scene, + views: [RenderView(camera: camera)], + settings: const RenderSettings(bloom: BloomSettings(enabled: false)), + ); + final pixels = (await engine.device.readPixels( + frame.frame, + ))!.buffer.asUint8List(); + final i = (_size ~/ 2 * _size + _size ~/ 2) * 4; + return pixels.sublist(i, i + 4); +} + +void main() { + test('the new model is drawn in the nodes the game already holds', () async { + final engine = _engine(); + final red = await ModelAsset.fromDocument( + _ship(Vector4(1.0, 0.0, 0.0, 1.0)), + device: engine.device, + ); + final blue = await ModelAsset.fromDocument( + _ship(Vector4(0.0, 0.0, 1.0, 1.0)), + device: engine.device, + ); + final scene = Scene(); + final ship = red.instantiate(scene); + final hull = ship.nodes.single..setPosition(0.0, 0.0, 0.5); + final mesh = ship.meshes.single; + expect((await _centre(engine, scene))[0], greaterThan(200)); + + final swap = ship.adopt(blue); + + expect(swap.swapped, 1); + expect(swap.complete, isTrue); + expect(ship.nodes.single, same(hull)); + expect(ship.meshes.single, same(mesh)); + expect( + hull.localMatrix.getTranslation().z, + 0.5, + reason: 'where the game put it stays', + ); + expect(mesh.mesh, same(blue.parts.single.mesh)); + final pixel = await _centre(engine, scene); + expect(pixel[2], greaterThan(200)); + // The tone map lifts the other channels as full blue nears white, so + // red is held to a third of blue, as the variants test holds it. + expect(pixel[0], lessThan(pixel[2] ~/ 3 + 1)); + }); + + test('a part renamed in the file is still found by its node', () async { + final engine = _engine(); + final before = await ModelAsset.fromDocument( + _ship(Vector4(1.0, 0.0, 0.0, 1.0), partName: 'Cube'), + device: engine.device, + ); + final after = await ModelAsset.fromDocument( + _ship(Vector4(0.0, 1.0, 0.0, 1.0), partName: 'Cube.001'), + device: engine.device, + ); + final ship = before.instantiate(Scene()); + + expect(ship.adopt(after).swapped, 1); + expect(ship.meshes.single.material, same(after.parts.single.material)); + }); + + test('what the new file adds is reported, and what it drops stays', () async { + final engine = _engine(); + final plain = await ModelAsset.fromDocument( + _ship(Vector4(1.0, 0.0, 0.0, 1.0)), + device: engine.device, + ); + final flagged = await ModelAsset.fromDocument( + _ship(Vector4(0.0, 1.0, 0.0, 1.0), withFlag: true, hull: 'keel'), + device: engine.device, + ); + final ship = plain.instantiate(Scene()); + final old = ship.meshes.single.mesh; + + final swap = ship.adopt(flagged); + + expect(swap.swapped, 0); + expect(swap.kept, ['hull/s0']); + expect(swap.added, unorderedEquals(['keel/s0', 'flag/s0'])); + expect(swap.complete, isFalse); + expect(ship.meshes.single.mesh, same(old)); + }); + + test( + 'an unshared instance adopts copies, not the asset\'s materials', + () async { + final engine = _engine(); + final red = await ModelAsset.fromDocument( + _ship(Vector4(1.0, 0.0, 0.0, 1.0)), + device: engine.device, + ); + final blue = await ModelAsset.fromDocument( + _ship(Vector4(0.0, 0.0, 1.0, 1.0)), + device: engine.device, + ); + final ship = red.instantiate(Scene(), shareMaterials: false)..adopt(blue); + + final material = ship.meshes.single.material; + expect(material, isNot(same(blue.parts.single.material))); + expect(material.baseColor.z, 1.0); + }, + ); +} diff --git a/packages/flutter3d_app/CHANGELOG.md b/packages/flutter3d_app/CHANGELOG.md index dcbb808b2..6b7dfd8ed 100644 --- a/packages/flutter3d_app/CHANGELOG.md +++ b/packages/flutter3d_app/CHANGELOG.md @@ -13,6 +13,13 @@ needs nothing more; a tool reaches the same reload as `ext.flutter3d.hotSwap`. Debug builds only: in profile and release it holds nothing. +**A model edited while the game runs is drawn in the nodes the game +holds.** `HotSwap.loadModel` hands back a `SwappableModel`; instances made +through it adopt the new file on the next hot reload, keeping their nodes, +transforms and animation. `ext.flutter3d.assets.put` sends a model's bytes +over the VM service instead, for a device whose disk the editor cannot write. +A model that does not build keeps the version that did. + `SceneSurface` is a `StatefulWidget` now, for `reassemble`. Its constructor and parameters are unchanged. diff --git a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart index df01491a6..2fdd5769a 100644 --- a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart +++ b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart @@ -6,12 +6,20 @@ import 'dart:convert'; import 'dart:developer' as developer; import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart' show rootBundle; import 'package:flutter3d/flutter3d.dart'; /// Reads the current bytes of a shader bundle an application loaded itself, /// or null when they cannot be read now. typedef ShaderBundleSource = Future Function(); +/// Reads the current bytes of a model file, or null when they cannot be read +/// now. +typedef ModelBytesSource = Future Function(); + +/// Builds a model from a file's bytes, on the device the game draws with. +typedef ModelBuild = Future Function(Uint8List bytes); + /// The renderers and shader libraries a running application has, and what to /// do with them when its code or its shaders change under it. /// @@ -58,6 +66,7 @@ final class HotSwap { final List> _renderers = >[]; final List<_Library> _libraries = <_Library>[]; + final List _models = []; /// Counts the reloads that finished, for a host that draws only on demand /// and has to draw once more to show the new shaders. @@ -101,6 +110,116 @@ final class HotSwap { _registerExtension(); } + /// Loads the model the build hook converted [sourcePath] into, as + /// `loadModelAsset` does, and hands it back ready to be swapped when the + /// file changes. + /// + /// **The one call a game makes.** Instantiate through the result, and a + /// hot reload after the model was edited draws the edit in the nodes the + /// game already holds; see [ModelInstance.adopt] for what follows and what + /// needs the model instantiated again. Outside a debug build this is + /// `loadModelAsset` and nothing is watched. + Future loadModel( + String sourcePath, { + required GraphicsDevice device, + }) async { + final generated = generatedAssetPathFor(sourcePath); + Future load(String path) async { + try { + final data = await rootBundle.load(path); + return data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + } on FlutterError { + return null; + } + } + + // The file `loadModelAsset` would read: this device class's own first, + // then the one every class shares. + Future read() async => switch (assetDeviceClass) { + final DeviceClass reading => + await load(deviceClassPath(generated, reading)) ?? + await load(generated), + null => await load(generated), + }; + + Future build(Uint8List bytes) async => ModelAsset.fromDocument( + await decodeModelInIsolate( + ModelLoadRequest(source: _MemorySource(generated, bytes)), + ), + device: device, + ); + + // Read once and built from what was read, so the bytes the swap compares + // against are the ones on screen. A project with no converted file yet + // goes through `loadModelAsset`, which decodes the source, and its first + // swap takes the converted file as it finds it as the starting point. + final bytes = enabled ? await read() : null; + final asset = bytes != null + ? await build(bytes) + : await ModelAsset.fromDocument( + await loadModelAsset(sourcePath), + device: device, + ); + return registerModel( + sourcePath, + asset, + read: read, + build: build, + device: device, + loadedFrom: bytes, + ); + } + + /// Wraps [asset], loaded from the file [read] reads, so that a [swap] after + /// the file changed rebuilds it with [build] and every instance made + /// through the result adopts it. + /// + /// [path] names it for `ext.flutter3d.assets.put` and in the report. + /// [loadedFrom] are the bytes [asset] was built from, when the caller has + /// them; without them the first swap reads the file and takes it as the + /// starting point rather than as a change. [device] is what an old asset is + /// released from once nothing draws it. + SwappableModel registerModel( + String path, + ModelAsset asset, { + required ModelBytesSource read, + required ModelBuild build, + GraphicsDevice? device, + Uint8List? loadedFrom, + }) { + final model = SwappableModel._( + path, + asset, + read, + build, + device, + loadedFrom == null + ? null + : _fingerprint(ByteData.sublistView(loadedFrom)), + ); + if (!enabled) return model; + _models + ..removeWhere((SwappableModel m) => m.path == path) + ..add(model); + _registerExtension(); + return model; + } + + /// Draws [bytes] as the model registered at [path] in every instance of it, + /// without the file on disk changing: what `ext.flutter3d.assets.put` + /// does for a tool on another machine, a phone that has no disk the + /// editor can write. + /// + /// The file's own bytes win again the next time they change. Null when no + /// model is registered at [path]. + Future put(String path, Uint8List bytes) async { + if (!enabled) return null; + for (final model in _models) { + if (model.path == path) return model._adopt(bytes); + } + return null; + } + /// Refreshes the registered bundles whose bytes changed, then relinks every /// live renderer. /// @@ -138,6 +257,29 @@ final class HotSwap { refused.add('${library.name}: ${refusal.reason}'); } } + final models = []; + for (final model in List.of(_models)) { + final Uint8List? bytes; + try { + bytes = await model._read(); + } on Object catch (error) { + refused.add('${model.path}: could not be read ($error)'); + continue; + } + if (bytes == null) continue; + final fingerprint = _fingerprint(ByteData.sublistView(bytes)); + if (model._fingerprint == null || fingerprint == model._fingerprint) { + model._fingerprint = fingerprint; + continue; + } + model._fingerprint = fingerprint; + final report = await model._adopt(bytes); + if (report.refused case final String reason) { + refused.add('${model.path}: $reason'); + } else { + models.add(report); + } + } _renderers.removeWhere((WeakReference r) => r.target == null); for (final renderer in _renderers) { renderer.target?.relinkShaders(); @@ -146,9 +288,10 @@ final class HotSwap { renderers: _renderers.length, refreshed: List.unmodifiable(refreshed), refused: List.unmodifiable(refused), + models: List.unmodifiable(models), ); for (final line in refused) { - debugPrint('flutter3d: kept the last shaders that loaded, $line'); + debugPrint('flutter3d: kept the last version that loaded, $line'); } swaps.value++; return report; @@ -166,6 +309,29 @@ final class HotSwap { jsonEncode(report.toJson()), ); }); + developer.registerExtension('ext.flutter3d.assets.put', ( + String method, + Map parameters, + ) async { + final path = parameters['path']; + final bytes = parameters['bytes']; + if (path == null || bytes == null) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + 'assets.put takes a path and its bytes in base64', + ); + } + final report = await instance.put(path, base64Decode(bytes)); + if (report == null) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + 'no model is registered at $path', + ); + } + return developer.ServiceExtensionResponse.result( + jsonEncode(report.toJson()), + ); + }); } /// FNV-1a over the bundle's bytes: enough to tell a changed file from an @@ -190,6 +356,7 @@ final class HotSwapReport { required this.renderers, required this.refreshed, required this.refused, + this.models = const [], }); /// What a profile or release build reports: nothing was touched. @@ -205,14 +372,18 @@ final class HotSwapReport { /// The bundles that changed and loaded. final List refreshed; - /// The bundles that changed and did not load, each with the reason. Their - /// libraries kept the last bundle that did. + /// The bundles and models that changed and did not load, each with the + /// reason. They kept the last version that did. final List refused; + /// The models that changed and were adopted. + final List models; + Map toJson() => { 'renderers': renderers, 'refreshed': refreshed, 'refused': refused, + 'models': [for (final model in models) model.toJson()], }; } @@ -223,3 +394,138 @@ final class _Library { final ShaderBundleSource read; int? fingerprint; } + +/// A model a game draws, and every instance of it, kept together so a +/// changed file reaches all of them. +/// +/// What [HotSwap.loadModel] and [HotSwap.registerModel] hand back. Make +/// instances through [instantiate], or hand ones made elsewhere to [track]; +/// an instance the game drops is forgotten with it. +final class SwappableModel { + SwappableModel._( + this.path, + this._asset, + this._read, + this._build, + this._device, + this._fingerprint, + ); + + /// The file this model was loaded from, as the game named it. + final String path; + + /// The version drawn now; a new one after each swap. + ModelAsset get asset => _asset; + ModelAsset _asset; + + final ModelBytesSource _read; + final ModelBuild _build; + final GraphicsDevice? _device; + int? _fingerprint; + final List> _instances = + >[]; + + /// [asset]'s `instantiate`, remembering the instance for the next swap. + ModelInstance instantiate( + Scene scene, { + SceneNode? parent, + String? name, + bool shareMaterials = true, + }) => track( + _asset.instantiate( + scene, + parent: parent, + name: name, + shareMaterials: shareMaterials, + ), + ); + + /// Remembers [instance], made from [asset] some other way — + /// `instantiateFitted`, say — for the next swap. + ModelInstance track(ModelInstance instance) { + _instances + ..removeWhere((WeakReference i) => i.target == null) + ..add(WeakReference(instance)); + return instance; + } + + Future _adopt(Uint8List bytes) async { + final ModelAsset next; + try { + next = await _build(bytes); + } on Object catch (error) { + return ModelSwapReport(path: path, instances: 0, refused: '$error'); + } + _instances.removeWhere( + (WeakReference i) => i.target == null, + ); + final swaps = [ + for (final instance in _instances) + if (instance.target case final ModelInstance live) live.adopt(next), + ]; + final previous = _asset; + _asset = next; + // Released only when no instance still draws a surface of it: a surface + // the new file dropped stays drawn from the old asset's meshes. + if (_device case final GraphicsDevice device + when swaps.every((ModelSwap s) => s.kept.isEmpty)) { + previous.release(device); + } + return ModelSwapReport( + path: path, + instances: swaps.length, + added: {for (final swap in swaps) ...swap.added}.toList(), + ); + } +} + +/// What a changed file did to one [SwappableModel]. +@immutable +final class ModelSwapReport { + const ModelSwapReport({ + required this.path, + required this.instances, + this.added = const [], + this.refused, + }); + + final String path; + + /// The instances that adopted the new version. + final int instances; + + /// Surfaces the new version has that its instances could not take on + /// (see `ModelSwap.added`); instantiate the model again to draw them. + final List added; + + /// Why the new bytes did not build, when they did not; the model is drawn + /// as it was. + final String? refused; + + Map toJson() => { + 'path': path, + 'instances': instances, + 'added': added, + if (refused != null) 'refused': refused, + }; +} + +/// A model file's bytes, already read, as the decoders read a file. +final class _MemorySource extends AssetSource { + const _MemorySource(this.path, this.bytes); + + final String path; + final Uint8List bytes; + + @override + String get key => 'memory:$path'; + + @override + Future read() async => bytes; + + @override + AssetUriResolver get resolveUri => + (AssetRequest request) async => throw StateError( + '$path was sent as bytes and has no files beside it', + ); +} diff --git a/packages/flutter3d_app/test/hot_swap_test.dart b/packages/flutter3d_app/test/hot_swap_test.dart index 113564942..64d7e2db6 100644 --- a/packages/flutter3d_app/test/hot_swap_test.dart +++ b/packages/flutter3d_app/test/hot_swap_test.dart @@ -14,7 +14,7 @@ import 'package:flutter3d/flutter3d.dart' as engine show Material; import 'package:flutter3d_app/flutter3d_app.dart'; import 'package:flutter3d_cpu/flutter3d_cpu.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:vector_math/vector_math.dart' show Vector3; +import 'package:vector_math/vector_math.dart' show Vector3, Vector4; /// A renderer in software with one cube in front of it, so a frame links a /// material pipeline a reload has to drop. @@ -85,13 +85,38 @@ final class _Library implements LoadedShaderLibrary { ByteData _bytes(List values) => ByteData.sublistView(Uint8List.fromList(values)); +/// A one-quad model whose colour is its file's first byte: 1 red, 2 green, +/// 3 blue; 0 does not build. +Future _model(GraphicsDevice device, Uint8List bytes) { + if (bytes.first == 0) throw const FormatException('not a model'); + final colour = Vector4.zero() + ..[bytes.first - 1] = 1.0 + ..w = 1.0; + return ModelAsset.fromDocument( + PlainModelDocument( + surfaces: [ + ModelSurface( + mesh: CuboidShape(size: Vector3.all(1.0)).build(), + materialIndex: 0, + ), + ], + materials: [ + SurfaceMaterial(baseColor: colour, unlit: true), + ], + nodes: [ + ModelNode(name: 'hull', surfaces: [0]), + ], + ), + device: device, + ); +} + void main() { test('a reload drops every pipeline the renderer and its contributors ' 'linked', () async { final it = _stage(); final contributor = it.renderer.addContributor(_Contributor()); - final swap = HotSwap(enabled: true) - ..registerRenderer(it.renderer); + final swap = HotSwap(enabled: true)..registerRenderer(it.renderer); _draw(it); expect(it.renderer.pipelineCount, greaterThan(0)); @@ -159,8 +184,7 @@ void main() { test('outside a debug build nothing is held and nothing reloads', () async { final it = _stage(); final contributor = it.renderer.addContributor(_Contributor()); - final swap = HotSwap(enabled: false) - ..registerRenderer(it.renderer); + final swap = HotSwap(enabled: false)..registerRenderer(it.renderer); final report = await swap.swap(); @@ -201,4 +225,83 @@ void main() { expect(contributor.relinks, 1); expect(HotSwap.instance.swaps.value, before + 1); }); + + group('a model', () { + late CpuDevice device; + late Uint8List file; + late HotSwap swap; + late SwappableModel model; + + setUp(() async { + device = CpuDevice( + width: 16, + height: 9, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + file = Uint8List.fromList([1]); + swap = HotSwap(enabled: true); + model = swap.registerModel( + 'assets_src/ship.glb', + await _model(device, file), + read: () async => file, + build: (Uint8List bytes) => _model(device, bytes), + device: device, + loadedFrom: file, + ); + }); + + double blue(ModelInstance ship) => ship.meshes.single.material.baseColor.z; + + test('is drawn anew in every instance when its file changes, and ' + 'only then', () async { + final scene = Scene(); + final a = model.instantiate(scene); + final b = model.instantiate(scene); + + expect((await swap.swap()).models, isEmpty); + + file = Uint8List.fromList([3]); + final report = await swap.swap(); + + expect(report.models.single.instances, 2); + expect(blue(a), 1.0); + expect(blue(b), 1.0); + expect(model.instantiate(scene).meshes.single.material.baseColor.z, 1.0); + expect((await swap.swap()).models, isEmpty); + }); + + test( + 'put draws bytes the file never had, until the file changes', + () async { + final ship = model.instantiate(Scene()); + + final put = await swap.put( + 'assets_src/ship.glb', + Uint8List.fromList([3]), + ); + expect(put!.instances, 1); + expect(blue(ship), 1.0); + + expect((await swap.swap()).models, isEmpty, reason: 'file unchanged'); + expect(blue(ship), 1.0); + + file = Uint8List.fromList([2]); + await swap.swap(); + expect(ship.meshes.single.material.baseColor.y, 1.0); + expect(await swap.put('assets_src/boat.glb', file), isNull); + }, + ); + + test('that does not build keeps the version that did', () async { + final ship = model.instantiate(Scene()); + final before = model.asset; + + file = Uint8List.fromList([0]); + final report = await swap.swap(); + + expect(report.refused.single, contains('not a model')); + expect(model.asset, same(before)); + expect(ship.meshes.single.material.baseColor.x, 1.0); + }); + }); } diff --git a/site/content/quickstart.md b/site/content/quickstart.md index d7934b0ca..80303c5cf 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10795 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10802 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 1a756b9fc..97d58117c 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,14 +1,14 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10795 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10802 tests need a GPU. --- # Testing -10795 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10802 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| -| `flutter3d` | 1616 | | | | +| `flutter3d` | 1620 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | | `flutter3d_sim` | 579 | | `pad_input` | 67 | @@ -26,7 +26,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 28 | | `apps/flutter3d_demo_river` | 53 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 90 | -| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 124 | +| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 127 | | `flutter3d_game` | 255 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10769 rather than 10795: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10776 rather than 10802: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index a0f1ee9fa..ab860eea7 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -122,19 +122,23 @@ restart (`gpu_loaded_shaders.dart:62-69`). *Tests:* refresh then relink draws the new colour on every backend; a refused bundle keeps the old frame. -**HR2. Models, textures, environments (M).** On reassemble the coordinator -evicts and re-hashes registered `flutter3d_generated/*.f3d` and texture keys; -textures are re-uploaded in place through a new HAL `updateTexture` that keeps -handle identity (conformance-checked like shaders); meshes and materials are -swapped inside live `ModelInstance`s by node name, keeping transforms and -animation state. Two faster routes: the editor calls `ext.flutter3d.assets.reload` -after a save on desktop (reading `assets_src` directly under a debug -`FLUTTER3D_SOURCE_ROOT`), and **`ext.flutter3d.assets.put` sends cooked bytes -over the VM service, so a phone gets the new model with no restart** — where -flutter_scene restarts. -*Tests:* hash change reloads, identical bytes are a no-op; texture identity -across update on three backends; a macOS integration run that overwrites a -`.glb`, reloads and compares a golden. +**HR2. Models, textures, environments (M).** *Models done:* +`HotSwap.loadModel` reads the converted `.f3d` itself (the device class's +file first, as `loadModelAsset` does), fingerprints it, and a swap after the +file changed rebuilds the asset and has every instance made through the +returned `SwappableModel` `adopt` it: `ModelInstance.adopt` matches surfaces +by node name and slot, hands each matched `MeshNode` the new geometry and +material and keeps the nodes, so transforms, animation and whatever the game +hung from them survive. What the new file adds or drops is reported, not +guessed at. `ext.flutter3d.assets.put {path, bytes}` does the same from bytes +sent over the VM service, so a phone gets the new model with no restart; the +file on disk wins again once it changes. A bundle or model that does not build +keeps the last version that did. +*Left:* a texture of a new size in place. `overwriteTexture` already keeps a +handle across a same-size write; a new size needs `TextureHandle`'s fields +mutable across four backends and the conformance suite, and waits until a +texture registry gives it a caller. Environments follow textures. The editor's +save hook arrives with HR5. **HR3. A level from the editor into a running game (L).** The editor sends `ext.flutter3d.level.apply {document or patch, hash}`. `diffLevel` keyed by From eac8d3f3a88a2441941d6e79682a7c0c4913ae54 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Wed, 30 Sep 2026 23:54:06 +0300 Subject: [PATCH 006/141] The editor plays the project it is editing A play button runs the project above the open level with flutter run --machine. PlayScreen shows the console and drives start, stop, hot reload and hot restart through the daemon protocol, and opens the timeline panel on the VM service the run reports. The run belongs to the editor, so closing the panel leaves the game running. The protocol shapes were checked against a real run on macOS; the tests drive a fake process that speaks them. --- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_editor/lib/main.dart | 94 ++++-- .../lib/src/play/flutter_run.dart | 285 ++++++++++++++++++ .../lib/src/play/play_screen.dart | 110 +++++++ .../test/flutter_run_test.dart | 249 +++++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- tasks/0.9-engine-roadmap.md | 14 +- 9 files changed, 737 insertions(+), 29 deletions(-) create mode 100644 apps/flutter3d_editor/lib/src/play/flutter_run.dart create mode 100644 apps/flutter3d_editor/lib/src/play/play_screen.dart create mode 100644 apps/flutter3d_editor/test/flutter_run_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 4d75772c4..1a03e40d5 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2688,7 +2688,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10802 tests** across 41 packages and 10 applications | +| Unit tests | **10808 tests** across 41 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 31d3dfc05..265758fc7 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10802 tests across forty-one packages and nine applications. The +There are 10808 tests across forty-one packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/apps/flutter3d_editor/lib/main.dart b/apps/flutter3d_editor/lib/main.dart index 87e46015a..70911b370 100644 --- a/apps/flutter3d_editor/lib/main.dart +++ b/apps/flutter3d_editor/lib/main.dart @@ -49,6 +49,8 @@ import 'src/editor_theme.dart'; import 'src/fly_camera.dart'; import 'src/light_plan_dialog.dart'; import 'src/open_run_channel.dart'; +import 'src/play/flutter_run.dart'; +import 'src/play/play_screen.dart'; import 'src/playtest_report_screen.dart'; import 'src/recent_projects.dart'; import 'src/run_info.dart'; @@ -144,6 +146,11 @@ class _EditorScreenState extends State /// The documents this editor has had open, kept between launches. final RecentProjects _projects = RecentProjects(); + /// `HR5`: the project this level belongs to, run with `flutter run`. Kept + /// here rather than by [PlayScreen] so closing the panel leaves the game + /// running; replaced when a level from another project is played. + FlutterRun? _run; + /// What [_projects] said when the chooser last needed it. /// /// Read once rather than in `build`, because `_onTick` rebuilds this screen @@ -1142,6 +1149,7 @@ class _EditorScreenState extends State @override void dispose() { + unawaited(_run?.dispose()); _openRunChannel?.dispose(); _shaders?.dispose(); _ticker?.dispose(); @@ -1433,6 +1441,19 @@ class _EditorScreenState extends State bottom: 0, child: EditorLegend(state: state), ), + // `HR5`: runs the project this level belongs to. + Positioned( + top: 4, + right: 92, + child: IconButton( + icon: const Icon( + Icons.play_circle_outline, + color: Color(0xFFE6EAF0), + ), + tooltip: 'Play the project', + onPressed: () => _play(state), + ), + ), // `rp-02`: attaches to a game already running elsewhere, // over the same VM service channel DevTools uses — this // editor still edits no simulation of its own. @@ -1542,6 +1563,59 @@ class _EditorScreenState extends State /// Asks for a running game's VM service address, connects, and opens the /// timeline panel on it — `rp-02`'s door, from the editor's side. + /// Opens the play panel on the project [state]'s level belongs to, + /// starting a run of it when none is going. + Future _play(EditorReady state) async { + final root = projectRootFor( + state.editing.path, + hasPubspec: (String directory) => + File('$directory${Platform.pathSeparator}pubspec.yaml').existsSync(), + ); + if (root == null) { + _changed( + 'this level is not inside a Flutter project, so there is ' + 'nothing to run', + ); + return; + } + final run = switch (_run) { + final FlutterRun same? when same.projectRoot == root => same, + final other => () { + unawaited(other?.dispose()); + return _run = FlutterRun(projectRoot: root); + }(), + }; + if (run.state.value case PlayIdle() || PlayStopped()) { + unawaited(run.start()); + } + await Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => PlayScreen(run: run, onTimeline: _openTimeline), + ), + ); + } + + Future _openTimeline(String uri) async { + final TimelineClient client; + try { + client = await VmServiceTimelineClient.connect(uri); + } catch (error) { + if (!mounted) return; + _changed('could not attach: $error'); + return; + } + if (!mounted) { + await client.dispose(); + return; + } + await Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => TimelineAttachScreen(client: client), + ), + ); + await client.dispose(); + } + Future _attachToRunningGame() async { final controller = TextEditingController(text: 'http://127.0.0.1:8181/'); final uri = await showDialog( @@ -1571,25 +1645,7 @@ class _EditorScreenState extends State ); controller.dispose(); if (uri == null || uri.isEmpty || !mounted) return; - - final TimelineClient client; - try { - client = await VmServiceTimelineClient.connect(uri); - } catch (error) { - if (!mounted) return; - _changed('could not attach: $error'); - return; - } - if (!mounted) { - await client.dispose(); - return; - } - await Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => TimelineAttachScreen(client: client), - ), - ); - await client.dispose(); + await _openTimeline(uri); } } diff --git a/apps/flutter3d_editor/lib/src/play/flutter_run.dart b/apps/flutter3d_editor/lib/src/play/flutter_run.dart new file mode 100644 index 000000000..b8a5a6884 --- /dev/null +++ b/apps/flutter3d_editor/lib/src/play/flutter_run.dart @@ -0,0 +1,285 @@ +/// `HR5`: the project a level belongs to, run from the editor. +/// +/// `flutter run --machine` and not a game built into the editor: the level +/// belongs to another application, with its own code, packages and entry +/// point, and the only thing that runs it the way its author does is the +/// author's own `flutter run`. `--machine` makes it a daemon the editor talks +/// to in JSON lines — the same protocol an IDE uses — so a hot reload from the +/// toolbar is the same hot reload the terminal would have done, and the game +/// answers it through `HotSwap` the same way. +library; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; + +/// Where a run is: not yet started, starting, running with a VM service to +/// attach to, or over. +sealed class PlayState { + const PlayState(); +} + +final class PlayIdle extends PlayState { + const PlayIdle(); +} + +final class PlayStarting extends PlayState { + const PlayStarting(this.message); + + /// What the tool says it is doing: "Launching lib/main.dart…". + final String message; +} + +final class PlayRunning extends PlayState { + const PlayRunning({required this.appId, required this.vmService}); + + final String appId; + + /// The game's VM service, for the timeline panel and `ext.flutter3d.*`. + final String vmService; +} + +final class PlayStopped extends PlayState { + const PlayStopped(this.exitCode); + + final int exitCode; +} + +/// Starts `flutter run --machine` in [workingDirectory]. A parameter of +/// [FlutterRun] so a test hands it a process of its own making. +typedef StartFlutter = + Future Function(List arguments, String workingDirectory); + +Future _startFlutter( + List arguments, + String workingDirectory, +) => Process.start( + 'flutter', + arguments, + workingDirectory: workingDirectory, + runInShell: Platform.isWindows, +); + +/// One `flutter run --machine`, from start to exit. +/// +/// **Its console is the tool's, line for line.** What the game prints, what +/// the tool reports and any line that is not the protocol at all — a build +/// error comes out as plain text before the daemon has said anything — all +/// go to [console] in the order they arrived, because the moment somebody +/// needs this panel is the moment something went wrong, and a filtered +/// console hides exactly that. +final class FlutterRun { + FlutterRun({ + required this.projectRoot, + this.device, + this._start = _startFlutter, + }); + + /// The directory with the project's `pubspec.yaml`. + final String projectRoot; + + /// `-d` for `flutter run`; the tool's own choice when null. + final String? device; + + final StartFlutter _start; + + /// Where the run is; the panel rebuilds from it. + final ValueNotifier state = ValueNotifier( + const PlayIdle(), + ); + + /// Every line the run has printed, oldest first. Capped at [consoleLimit], + /// dropping the oldest, so a game that logs every frame costs a bounded + /// list and not the editor's memory. + final ValueNotifier> console = ValueNotifier>( + const [], + ); + + static const int consoleLimit = 2000; + + Process? _process; + String? _appId; + var _nextId = 0; + final Map> _pending = >{}; + + /// Starts the run. Does nothing while one is already going. + Future start() async { + if (_process != null) return; + state.value = const PlayStarting('Starting flutter run…'); + final Process process; + try { + process = await _start([ + 'run', + '--machine', + if (device case final String id) ...['-d', id], + ], projectRoot); + } on ProcessException catch (error) { + _print('could not start flutter: ${error.message}'); + state.value = const PlayStopped(-1); + return; + } + _process = process; + process.stdout + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen(_line); + process.stderr + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen(_print); + unawaited( + process.exitCode.then((int code) { + _process = null; + _appId = null; + for (final pending in _pending.values) { + pending.completeError(StateError('flutter run exited ($code)')); + } + _pending.clear(); + state.value = PlayStopped(code); + }), + ); + } + + /// A hot reload: the game keeps its state, and `HotSwap` relinks shaders + /// and swaps changed models from `reassemble`. + Future hotReload() => _restart(fullRestart: false); + + /// A hot restart: the game starts over from `main`, with the new code. + Future hotRestart() => _restart(fullRestart: true); + + /// Asks the game to stop, and the tool with it. + Future stop() async { + final appId = _appId; + if (appId == null) { + _process?.kill(); + return; + } + try { + await _send('app.stop', {'appId': appId}); + } on StateError { + // Already gone. + } + } + + /// Stops the run if there is one and lets go of the process. + Future dispose() async { + await stop(); + state.dispose(); + console.dispose(); + } + + Future _restart({required bool fullRestart}) async { + final appId = _appId; + if (appId == null) return; + final result = await _send('app.restart', { + 'appId': appId, + 'fullRestart': fullRestart, + 'pause': false, + 'reason': 'manual', + }); + // "Reloaded 3 of 812 libraries" as well as a refusal: after a press of + // the button, the console is where somebody looks to see it happened. + if (result case {'message': final String message} when message.isNotEmpty) { + _print(message); + } + } + + Future _send(String method, Map params) { + final process = _process; + if (process == null) { + return Future.error(StateError('nothing is running')); + } + final id = _nextId++; + final reply = Completer(); + _pending[id] = reply; + process.stdin.writeln( + jsonEncode([ + {'id': id, 'method': method, 'params': params}, + ]), + ); + return reply.future; + } + + void _line(String line) { + // The protocol wraps each message in a one-element list; anything else + // on stdout is the tool talking before or around the daemon. + final Object? decoded; + try { + decoded = line.startsWith('[{') ? jsonDecode(line) : null; + } on FormatException { + _print(line); + return; + } + switch (decoded) { + case [final Map message]: + _message(message); + default: + _print(line); + } + } + + void _message(Map message) { + switch (message) { + case {'id': final int id, 'error': final Object? error}: + _pending.remove(id)?.completeError(StateError('$error')); + case {'id': final int id}: + _pending.remove(id)?.complete(message['result']); + case {'event': 'app.start', 'params': {'appId': final String appId}}: + _appId = appId; + case { + 'event': 'app.debugPort', + 'params': {'appId': final String appId, 'wsUri': final String uri}, + }: + _appId = appId; + state.value = PlayRunning(appId: appId, vmService: uri); + case {'event': 'app.log', 'params': {'log': final String log}}: + _print(log); + case { + 'event': 'daemon.logMessage', + 'params': {'message': final String m}, + }: + _print(m); + case { + 'event': 'app.progress', + 'params': {'message': final String progress}, + }: + _print(progress); + if (state.value is PlayStarting) { + state.value = PlayStarting(progress); + } + default: + // `app.started`, `app.stop`, `daemon.connected`, `device.*` and + // whatever a later tool adds: nothing the panel shows. + break; + } + } + + void _print(String line) { + final lines = console.value; + console.value = [ + ...lines.length >= consoleLimit + ? lines.skip(lines.length - consoleLimit + 1) + : lines, + line, + ]; + } +} + +/// The directory of the nearest `pubspec.yaml` above [levelPath], or null +/// when the level is not inside a project at all. +/// +/// [hasPubspec] answers for a directory; injected, like +/// `Documents.assetRootFor`'s own check, so this is tested with no disk. +String? projectRootFor( + String levelPath, { + required bool Function(String directory) hasPubspec, +}) => _nearest(File(levelPath).parent, hasPubspec); + +String? _nearest(Directory directory, bool Function(String) hasPubspec) => + switch (directory.parent) { + _ when hasPubspec(directory.path) => directory.path, + final parent when parent.path == directory.path => null, + final parent => _nearest(parent, hasPubspec), + }; diff --git a/apps/flutter3d_editor/lib/src/play/play_screen.dart b/apps/flutter3d_editor/lib/src/play/play_screen.dart new file mode 100644 index 000000000..005e74301 --- /dev/null +++ b/apps/flutter3d_editor/lib/src/play/play_screen.dart @@ -0,0 +1,110 @@ +/// `HR5`'s panel: the project running, its console, and the four things +/// done to it — start, hot reload, hot restart, stop — plus the timeline of +/// the game it started. +library; + +import 'package:flutter/material.dart'; + +import 'flutter_run.dart'; + +/// Shows [run] and drives it. +/// +/// **A view of the run, not its owner.** The editor keeps [run] for as long +/// as the project is open, so closing this screen leaves the game running and +/// opening it again finds the same console — the way a terminal tab with +/// `flutter run` in it would behave. +final class PlayScreen extends StatelessWidget { + const PlayScreen({super.key, required this.run, required this.onTimeline}); + + final FlutterRun run; + + /// Opens the timeline panel on the running game's VM service. + final void Function(String vmService) onTimeline; + + static const Color _background = Color(0xFF14161A); + static const Color _text = Color(0xFFE6EAF0); + + @override + Widget build(BuildContext context) => ValueListenableBuilder( + valueListenable: run.state, + builder: (BuildContext context, PlayState state, _) { + final running = state is PlayRunning; + final going = state is PlayStarting || running; + return Scaffold( + backgroundColor: _background, + appBar: AppBar( + title: Text(run.projectRoot, overflow: TextOverflow.ellipsis), + actions: [ + IconButton( + key: const ValueKey('play.start'), + icon: Icon(going ? Icons.stop : Icons.play_arrow), + tooltip: going ? 'Stop' : 'Run the project', + onPressed: going ? run.stop : run.start, + ), + IconButton( + key: const ValueKey('play.reload'), + icon: const Icon(Icons.bolt), + tooltip: 'Hot reload', + onPressed: running ? run.hotReload : null, + ), + IconButton( + key: const ValueKey('play.restart'), + icon: const Icon(Icons.restart_alt), + tooltip: 'Hot restart', + onPressed: running ? run.hotRestart : null, + ), + IconButton( + key: const ValueKey('play.timeline'), + icon: const Icon(Icons.podcasts), + tooltip: 'Timeline', + onPressed: switch (state) { + PlayRunning(:final vmService) => () => onTimeline(vmService), + _ => null, + }, + ), + ], + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(12, 8, 12, 4), + child: Text( + switch (state) { + PlayIdle() => 'Not running', + PlayStarting(:final message) => message, + PlayRunning(:final vmService) => 'Running, $vmService', + PlayStopped(:final exitCode) => 'Stopped ($exitCode)', + }, + key: const ValueKey('play.status'), + style: const TextStyle(color: _text), + ), + ), + Expanded( + child: ValueListenableBuilder>( + valueListenable: run.console, + // Reversed, so the newest line sits at the bottom and the list + // stays there as lines arrive, as a terminal does. + builder: (BuildContext context, List lines, _) => + ListView.builder( + reverse: true, + padding: const EdgeInsets.symmetric(horizontal: 12), + itemCount: lines.length, + itemBuilder: (BuildContext context, int index) => + SelectableText( + lines[lines.length - 1 - index], + style: const TextStyle( + color: _text, + fontFamily: 'monospace', + fontSize: 12, + ), + ), + ), + ), + ), + ], + ), + ); + }, + ); +} diff --git a/apps/flutter3d_editor/test/flutter_run_test.dart b/apps/flutter3d_editor/test/flutter_run_test.dart new file mode 100644 index 000000000..6a784d601 --- /dev/null +++ b/apps/flutter3d_editor/test/flutter_run_test.dart @@ -0,0 +1,249 @@ +/// `HR5`: the editor runs a project through `flutter run --machine` and +/// drives it — against a process of the test's own making, which speaks the +/// daemon protocol the way the tool does. +/// +/// flutter test test/flutter_run_test.dart +library; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter3d_editor/src/play/flutter_run.dart'; +import 'package:flutter3d_editor/src/play/play_screen.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// A `flutter run --machine` that says what [say] is given and records what +/// it is sent. +final class _Tool implements Process { + final StreamController> _out = StreamController>(); + final StreamController> _err = StreamController>(); + final Completer _exit = Completer(); + final List> sent = >[]; + late final IOSink _in = IOSink(_Sent(this)); + + void say(String line) => _out.add(utf8.encode('$line\n')); + + void event(String event, Map params) => say( + jsonEncode([ + {'event': event, 'params': params}, + ]), + ); + + void answer(int id, Object? result) => say( + jsonEncode([ + {'id': id, 'result': result}, + ]), + ); + + void exit(int code) => _exit.complete(code); + + @override + Stream> get stdout => _out.stream; + + @override + Stream> get stderr => _err.stream; + + @override + IOSink get stdin => _in; + + @override + Future get exitCode => _exit.future; + + @override + int get pid => 1; + + @override + bool kill([ProcessSignal signal = ProcessSignal.sigterm]) { + if (!_exit.isCompleted) _exit.complete(-15); + return true; + } +} + +final class _Sent implements StreamConsumer> { + _Sent(this.tool); + + final _Tool tool; + + @override + Future addStream(Stream> stream) => stream + .transform(utf8.decoder) + .transform(const LineSplitter()) + .forEach((String line) { + final [Object? message] = jsonDecode(line) as List; + tool.sent.add(message! as Map); + }); + + @override + Future close() async {} +} + +({FlutterRun run, _Tool tool, List> started}) _run() { + final tool = _Tool(); + final started = >[]; + return ( + run: FlutterRun( + projectRoot: '/game', + device: 'macos', + start: (List arguments, String directory) async { + started.add([directory, ...arguments]); + return tool; + }, + ), + tool: tool, + started: started, + ); +} + +void main() { + test('starts the tool in the project and follows it to running', () async { + final it = _run(); + await it.run.start(); + expect(it.started.single, [ + '/game', + 'run', + '--machine', + '-d', + 'macos', + ]); + expect(it.run.state.value, isA()); + + it.tool + ..say('Resolving dependencies...') + ..event('app.start', {'appId': 'a1'}) + ..event('app.progress', {'message': 'Building macOS'}) + ..event('app.debugPort', { + 'appId': 'a1', + 'port': 8181, + 'wsUri': 'ws://127.0.0.1:8181/ws', + }) + ..event('app.log', {'log': 'flutter: hello'}); + await pumpEventQueue(); + + final running = it.run.state.value as PlayRunning; + expect(running.appId, 'a1'); + expect(running.vmService, 'ws://127.0.0.1:8181/ws'); + expect(it.run.console.value, [ + 'Resolving dependencies...', + 'Building macOS', + 'flutter: hello', + ]); + }); + + test('a hot reload and a hot restart are the tool\'s own commands', () async { + final it = _run(); + await it.run.start(); + it.tool.event('app.debugPort', { + 'appId': 'a1', + 'wsUri': 'ws://x', + }); + await pumpEventQueue(); + + final reload = it.run.hotReload(); + await pumpEventQueue(); + expect(it.tool.sent.single, { + 'id': 0, + 'method': 'app.restart', + 'params': { + 'appId': 'a1', + 'fullRestart': false, + 'pause': false, + 'reason': 'manual', + }, + }); + it.tool.answer(0, {'code': 0, 'message': ''}); + await reload; + + final restart = it.run.hotRestart(); + await pumpEventQueue(); + expect((it.tool.sent.last['params']! as Map)['fullRestart'], isTrue); + it.tool.answer(1, { + 'code': 1, + 'message': 'Reload rejected: a const changed shape', + }); + await restart; + expect(it.run.console.value.last, 'Reload rejected: a const changed shape'); + }); + + test('the tool exiting stops the run and fails what was waiting', () async { + final it = _run(); + await it.run.start(); + it.tool.event('app.debugPort', { + 'appId': 'a1', + 'wsUri': 'ws://x', + }); + await pumpEventQueue(); + + final reload = it.run.hotReload(); + await pumpEventQueue(); + it.tool.exit(1); + + await expectLater(reload, throwsStateError); + expect((it.run.state.value as PlayStopped).exitCode, 1); + }); + + test( + 'the console keeps the newest lines when a game logs every frame', + () async { + final it = _run(); + await it.run.start(); + for (var i = 0; i < FlutterRun.consoleLimit + 5; i++) { + it.tool.say('line $i'); + } + await pumpEventQueue(); + expect(it.run.console.value, hasLength(FlutterRun.consoleLimit)); + expect(it.run.console.value.first, 'line 5'); + }, + ); + + test('a level is run as part of the nearest project above it', () { + final projects = {'/work/game'}; + bool hasPubspec(String directory) => projects.contains(directory); + + expect( + projectRootFor( + '/work/game/assets/levels/one.json', + hasPubspec: hasPubspec, + ), + '/work/game', + ); + expect( + projectRootFor('/elsewhere/one.json', hasPubspec: hasPubspec), + isNull, + ); + }); + + testWidgets('the panel offers a reload only once the game is running', ( + WidgetTester tester, + ) async { + final it = _run(); + final timelines = []; + await it.run.start(); + await tester.pumpWidget( + MaterialApp( + home: PlayScreen(run: it.run, onTimeline: timelines.add), + ), + ); + + IconButton button(String key) => + tester.widget(find.byKey(ValueKey(key))); + expect(button('play.reload').onPressed, isNull); + expect(button('play.timeline').onPressed, isNull); + + it.tool.event('app.debugPort', { + 'appId': 'a1', + 'wsUri': 'ws://game', + }); + await tester.pump(); + await tester.pump(); + + expect(find.text('Running, ws://game'), findsOneWidget); + await tester.tap(find.byKey(const ValueKey('play.reload'))); + await tester.pump(); + expect(it.tool.sent.single['method'], 'app.restart'); + + await tester.tap(find.byKey(const ValueKey('play.timeline'))); + expect(timelines, ['ws://game']); + }); +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 80303c5cf..26bba71cc 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10802 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10808 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 97d58117c..613f6927c 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10802 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10808 tests need a GPU. --- # Testing -10802 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10808 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -20,7 +20,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 63 | | `apps/flutter3d_demo_platformer` | 199 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 328 | | `apps/flutter3d_demo_strategy` | 50 | -| `apps/flutter3d_editor` | 215 | | `apps/flutter3d_demo_arcade` | 20 | +| `apps/flutter3d_editor` | 221 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 28 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10776 rather than 10802: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10782 rather than 10808: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index ab860eea7..ce221bba9 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -163,9 +163,17 @@ new code, and report where the new run diverges from the old *Tests:* a tape with cvar commands reproduces; swapping an injected step function reproduces the new outcome and names the first differing step. -**HR5. Editor Play (M), with §6.** The editor runs the user's project through -`flutter run --machine`, attaches the timeline and reload extensions, and -shows the console. Hot reload and restart from a toolbar and from MCP. +**HR5. Editor Play (M), with §6.** *Done on desktop:* the toolbar's play +button finds the project above the open level (the nearest `pubspec.yaml`) +and runs it with `flutter run --machine`; `PlayScreen` shows the console and +drives start, stop, hot reload and hot restart through the daemon protocol, +and opens the timeline panel on the VM service the run reports. The run +belongs to the editor, not the panel, so closing the panel leaves the game +running. The protocol was checked against a real run of +`packages/flutter3d/example` on macOS (`app.start`, `app.debugPort` with +`wsUri`, `app.restart` answering `{code, message}`, `app.stop`). +*Left:* the same from MCP, a device picker (the tool's own default today), and +the web editor, which has no process to start and will attach instead. ## 4. Parity with flutter_scene 0.24 From 4afad3f9343ab7efe7747605f5f2001f4f3d10f7 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 00:12:42 +0300 Subject: [PATCH 007/141] A level edit knows whether the run has to replay diffLevel splits a change between two versions of a level: lights, materials, fog and music can be patched into a running scene; brushes, entities, the ground, recipes and the next level are the simulation's. RunTimeline.swapLevel takes the simulation's half: it restores the last keyframe, lets the caller swap the level, replays the recorded input to the present and rebases the rewind buffer there. TimelineLevelSwapped records the step and the level digest, so a replay that swaps at the same step arrives where the run did. --- ARCHITECTURE.md | 2 +- README.md | 2 +- packages/flutter3d_game/CHANGELOG.md | 9 ++ .../lib/src/run/run_timeline.dart | 71 ++++++++ .../lib/src/run/run_timeline_extensions.dart | 2 + .../test/run_timeline_level_swap_test.dart | 151 ++++++++++++++++++ packages/flutter3d_sim/CHANGELOG.md | 11 ++ packages/flutter3d_sim/lib/flutter3d_sim.dart | 1 + .../lib/src/level/level_diff.dart | 132 +++++++++++++++ .../flutter3d_sim/lib/src/save/rewind.dart | 19 +++ .../flutter3d_sim/test/level_diff_test.dart | 150 +++++++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- 13 files changed, 554 insertions(+), 8 deletions(-) create mode 100644 packages/flutter3d_game/test/run_timeline_level_swap_test.dart create mode 100644 packages/flutter3d_sim/lib/src/level/level_diff.dart create mode 100644 packages/flutter3d_sim/test/level_diff_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 1a03e40d5..6e91d2a87 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2688,7 +2688,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10808 tests** across 41 packages and 10 applications | +| Unit tests | **10818 tests** across 41 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 265758fc7..aa229fa96 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10808 tests across forty-one packages and nine applications. The +There are 10818 tests across forty-one packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/packages/flutter3d_game/CHANGELOG.md b/packages/flutter3d_game/CHANGELOG.md index 7466d2686..e8aa354ab 100644 --- a/packages/flutter3d_game/CHANGELOG.md +++ b/packages/flutter3d_game/CHANGELOG.md @@ -1,3 +1,12 @@ +## Unreleased + +- **`RunTimeline.swapLevel` replaces the level under a running game without + the run jumping.** It restores the last keyframe, lets the caller swap the + level, replays the recorded input to the present with the devices muted, + and rebases the buffer there. `TimelineLevelSwapped` records the step and + the level's digest, so a replay that swaps at the same step arrives where + the run did. + ## 0.8.0 **Moves with the stack to 0.8.0**, whose `flutter3d_hardware` changes diff --git a/packages/flutter3d_game/lib/src/run/run_timeline.dart b/packages/flutter3d_game/lib/src/run/run_timeline.dart index 81bf7766f..b047712d5 100644 --- a/packages/flutter3d_game/lib/src/run/run_timeline.dart +++ b/packages/flutter3d_game/lib/src/run/run_timeline.dart @@ -29,6 +29,29 @@ final class TimelineBranched extends TimelineCommand { final int step; } +/// The level under the run was replaced, taking effect before [step]: the +/// run was replayed from there under the level whose document digests to +/// [levelDigest] (`Level.digestHex`). +/// +/// **What makes a run with an edit in it reproducible.** A tape replayed +/// against the old level from the start would part from this run at [step]; +/// with this command in hand, a replay swaps the level at the same step and +/// arrives where the run did. +final class TimelineLevelSwapped extends TimelineCommand { + const TimelineLevelSwapped(this.step, this.levelDigest); + final int step; + final String levelDigest; + + @override + bool operator ==(Object other) => + other is TimelineLevelSwapped && + other.step == step && + other.levelDigest == levelDigest; + + @override + int get hashCode => Object.hash(step, levelDigest); +} + /// Pause, step, rewind and branch, built on a live [RewindBuffer]. /// /// **What `rp-02`'s editor panel is a face for, not the panel itself.** The @@ -155,6 +178,54 @@ final class RunTimeline { _history.add(TimelineBranched(point.step)); } + /// Replaces the level under the run without the run jumping: [swap] puts + /// the new level in place, and the steps since the last keyframe are lived + /// again under it, so the present is one the new level could have led to. + /// + /// **For the half of an edit the simulation reads** — brushes, entities, + /// the ground (`LevelDiff.simulation`). Swapped in place, a crate moved in + /// the editor would teleport mid-run, and nothing replaying the tape could + /// reach that state. Swapped here, the keyframe is restored, [swap] runs, + /// and the recorded input plays forward with the live devices muted, as + /// [releaseAt] plays it; at most one keyframe interval is replayed. The + /// buffer is then rebased on that keyframe (`RewindBuffer.rebaseAt`), + /// since nothing held on either side of it belongs to the new level. + /// + /// [swap] must replace only what the snapshots do not carry — the level, + /// its colliders, what it spawned at load — and leave what they do carry to + /// the restore. With no keyframe yet held, [swap] runs at once, as at the + /// first step of a run. + /// + /// Returns the step the new level took effect before, which is what + /// [TimelineLevelSwapped] records. + int swapLevel(void Function() swap, {required String levelDigest}) { + final point = rewind.rewindTo(rewind.step); + if (point == null) { + swap(); + _history.add(TimelineLevelSwapped(rewind.step, levelDigest)); + return rewind.step; + } + restore(point.snapshot); + swap(); + final replay = InputTapePlayback(point.tapeToPoint); + final wasMuted = input.muted; + input.muted = true; + try { + while (!replay.isFinished) { + replay.applyTo(input); + input.beginStep(); + stepSim(stepSeconds); + input.endStep(); + } + } finally { + input.muted = wasMuted; + } + rewind.rebaseAt(point); + final at = point.step - point.replayed; + _history.add(TimelineLevelSwapped(at, levelDigest)); + return at; + } + /// [releaseAt], given the step directly rather than a [RewindPoint] — /// what a caller across a wire has, since a step number survives being /// sent as a string and a [RewindPoint] does not. Returns whether the diff --git a/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart b/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart index ed3ea7ba0..bcdb35508 100644 --- a/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart +++ b/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart @@ -191,4 +191,6 @@ String _describe(TimelineCommand command) => switch (command) { TimelineResumed() => 'resumed', TimelineStepped() => 'stepped', TimelineBranched(:final step) => 'branched:$step', + TimelineLevelSwapped(:final step, :final levelDigest) => + 'level:$step:$levelDigest', }; diff --git a/packages/flutter3d_game/test/run_timeline_level_swap_test.dart b/packages/flutter3d_game/test/run_timeline_level_swap_test.dart new file mode 100644 index 000000000..edbd42aae --- /dev/null +++ b/packages/flutter3d_game/test/run_timeline_level_swap_test.dart @@ -0,0 +1,151 @@ +/// `HR3`: a level edited under a running game, swapped through the timeline +/// so the run stays one a replay can reach. +/// +/// flutter test test/run_timeline_level_swap_test.dart +/// +/// The toy walks into a wall the snapshots do not carry — the level's part of +/// the world — so a swap that forgot to replay would leave the walker where +/// the old wall stopped it, and one that replayed from the wrong keyframe +/// would stop it somewhere a fresh run never does. +/// +/// Mutation: skip the replay loop in `swapLevel` and the first test's states +/// part; drop `rebaseAt` and the rewind through the old level's keyframe +/// succeeds. +library; + +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// A walker that stops at [wall], with a die it rolls at every stop. +final class _Walker { + _Walker(this.wall) : dice = GameRandom(3); + + /// The level: not in [save], as a level is not in a game's snapshot. + double wall; + final GameRandom dice; + double x = 0.0; + int bumps = 0; + + void step(InputState input) { + x += input.moveAxis.x * 0.1; + if (x > wall) { + x = wall; + bumps += dice.nextInt(100); + } + } + + Snapshot save() => + Snapshot({'x': x, 'bumps': bumps, 'random': dice.state}); + + void restore(Snapshot snapshot) { + x = snapshot.data.number('x'); + bumps = snapshot.data.integer('bumps'); + dice.state = snapshot.data.integer('random'); + } + + String get state => '$x/$bumps/${dice.state}'; +} + +void _play(InputState input, int step) => + input.setStickAxis(step % 7 == 0 ? -1.0 : 1.0, 0.0); + +({_Walker walker, InputState input, RewindBuffer rewind, RunTimeline timeline}) +_run() { + final walker = _Walker(5.0); + final input = InputState(); + final rewind = RewindBuffer(stepsPerSecond: 60, history: 10.0); + return ( + walker: walker, + input: input, + rewind: rewind, + timeline: RunTimeline( + rewind: rewind, + input: input, + stepSim: (dt) => walker.step(input), + restore: walker.restore, + ), + ); +} + +/// Steps [from] to [to] the way a game loop does: record, keyframe, step. +void _steps( + ({ + _Walker walker, + InputState input, + RewindBuffer rewind, + RunTimeline timeline, + }) + it, + int from, + int to, +) { + for (var step = from; step < to; step++) { + _play(it.input, step); + it.rewind.recorder.record(it.input); + it.input.beginStep(); + if (it.rewind.keyframeDue) it.rewind.keyframe(it.walker.save()); + it.walker.step(it.input); + it.input.endStep(); + } +} + +void main() { + test('a run with a level swapped at step K arrives where a fresh run with ' + 'the new level from step K does', () { + final live = _run(); + _steps(live, 0, 150); + final at = live.timeline.swapLevel( + () => live.walker.wall = 9.0, + levelDigest: 'b', + ); + _steps(live, 150, 400); + + // The keyframe before step 150, a second apart. + expect(at, 120); + expect(live.timeline.history.last, const TimelineLevelSwapped(120, 'b')); + + final fresh = _run(); + _steps(fresh, 0, at); + fresh.walker.wall = 9.0; + _steps(fresh, at, 400); + + expect(live.walker.state, fresh.walker.state); + }); + + test('without the swap the run ends elsewhere, so the check above has ' + 'something to say', () { + final kept = _run(); + _steps(kept, 0, 400); + final swapped = _run(); + _steps(swapped, 0, 150); + swapped.timeline.swapLevel( + () => swapped.walker.wall = 9.0, + levelDigest: 'b', + ); + _steps(swapped, 150, 400); + + expect(swapped.walker.state, isNot(kept.walker.state)); + }); + + test('nothing from before the swap can be rewound into afterwards', () { + final live = _run(); + _steps(live, 0, 150); + live.timeline.swapLevel(() => live.walker.wall = 9.0, levelDigest: 'b'); + + expect(live.rewind.rewindTo(100), isNull, reason: 'the old level\'s'); + expect(live.rewind.rewindTo(130), isNotNull); + expect(live.rewind.step, 150, reason: 'the tape since is kept'); + }); + + test('before the first keyframe the level is simply swapped', () { + final live = _run(); + final at = live.timeline.swapLevel( + () => live.walker.wall = 9.0, + levelDigest: 'b', + ); + + expect(at, 0); + expect(live.walker.wall, 9.0); + }); +} diff --git a/packages/flutter3d_sim/CHANGELOG.md b/packages/flutter3d_sim/CHANGELOG.md index 09c73a462..e60e2fa5b 100644 --- a/packages/flutter3d_sim/CHANGELOG.md +++ b/packages/flutter3d_sim/CHANGELOG.md @@ -1,3 +1,14 @@ +## Unreleased + +- **`diffLevel` says who has to act on an edit.** It compares two versions + of a level part by part and splits the change: lights, materials, fog and + music can be patched into a running scene; brushes, entities, the ground, + recipes and the next level are the simulation's, and go through a timeline + branch. Conservative on purpose: a brush that only changed material is + still the simulation's, since its surface falls back to its material. +- **`RewindBuffer.rebaseAt`** makes one keyframe the oldest thing held, for a + change to the world the snapshots do not carry. + ## 0.8.1 **Several things to chase, and one sweep to chase them by.** diff --git a/packages/flutter3d_sim/lib/flutter3d_sim.dart b/packages/flutter3d_sim/lib/flutter3d_sim.dart index d808a5931..2f5b45ea6 100644 --- a/packages/flutter3d_sim/lib/flutter3d_sim.dart +++ b/packages/flutter3d_sim/lib/flutter3d_sim.dart @@ -66,6 +66,7 @@ export 'src/level/heightfield_tiles.dart'; export 'src/level/json_reader.dart'; export 'src/level/level.dart'; export 'src/level/level_collision.dart'; +export 'src/level/level_diff.dart'; export 'src/level/level_issue.dart'; export 'src/level/level_validator.dart'; export 'src/level/level_visibility.dart'; diff --git a/packages/flutter3d_sim/lib/src/level/level_diff.dart b/packages/flutter3d_sim/lib/src/level/level_diff.dart new file mode 100644 index 000000000..b1d29b7ae --- /dev/null +++ b/packages/flutter3d_sim/lib/src/level/level_diff.dart @@ -0,0 +1,132 @@ +import '../save/state_digest.dart'; +import 'level.dart'; + +/// What changed between two versions of one level, split by who has to act +/// on it. +/// +/// **The split is the whole point.** An editor saves a level while the game +/// is running it, and the game has two ways to take the change. What only the +/// picture reads — a light, a material, the fog, the music — can be patched +/// into the running scene as it is, and nothing the simulation computed is +/// disturbed. What the simulation reads — brushes it collides with, entities +/// it spawned, the ground — cannot: patched in place, the run would carry on +/// from a state no run of the new level could have reached, and a replay of +/// it would disagree with the run it recorded. That half goes through a +/// timeline branch instead (`RunTimeline.swapLevel`). +/// +/// **Conservative on purpose.** A brush whose only change is its material +/// looks presentational, and usually is — but a brush's surface falls back to +/// its material, and a surface is what footsteps and friction read. A +/// classifier that is sometimes too careful costs a replay; one that is +/// sometimes too bold costs a run that cannot be reproduced. So anything +/// under [simulation] is judged by the whole part, not by a guess at which of +/// its fields the game reads. +final class LevelDiff { + const LevelDiff({ + this.lights = const [], + this.lightCountChanged = false, + this.materials = const [], + this.fog = false, + this.music = false, + this.simulation = const [], + }); + + /// Lights changed in place, by index in the level's list. + final List lights; + + /// Lights were added or removed, so indices past the shorter list mean + /// nothing; a presenter rebuilds the lights rather than patching them. + final bool lightCountChanged; + + /// Materials added, removed or changed, by name. + final List materials; + + /// The fog's colour or density changed. + final bool fog; + + /// The music changed. + final bool music; + + /// Which parts the simulation reads changed: `brushes`, `entities`, + /// `heightfield`, `recipes`, `next`. Empty when the change can be patched + /// into a running scene without touching the run. + final List simulation; + + /// Whether nothing changed at all. + bool get isEmpty => + lights.isEmpty && + !lightCountChanged && + materials.isEmpty && + !fog && + !music && + simulation.isEmpty; + + /// Whether the change can be patched in without a timeline branch. + bool get presentationOnly => simulation.isEmpty; + + Map toJson() => { + 'lights': lights, + 'lightCountChanged': lightCountChanged, + 'materials': materials, + 'fog': fog, + 'music': music, + 'simulation': simulation, + }; +} + +/// Compares [before] and [after], part by part, through their documents. +/// +/// Through `toJson` rather than field by field: the document is what a level +/// *is* to the editor that wrote it, and a field added to a part later is +/// compared the day it is added, with nothing here to remember. +LevelDiff diffLevel(Level before, Level after) { + String digest(Object? json) => contentDigestHex({'v': json}); + bool differs(Object? a, Object? b) => digest(a) != digest(b); + + final oldLights = before.lights; + final newLights = after.lights; + final shared = oldLights.length < newLights.length + ? oldLights.length + : newLights.length; + + final names = {...before.materials.keys, ...after.materials.keys}; + return LevelDiff( + lights: [ + for (var i = 0; i < shared; i++) + if (differs(oldLights[i].toJson(), newLights[i].toJson())) i, + ], + lightCountChanged: oldLights.length != newLights.length, + materials: [ + for (final name in names) + if (differs( + before.materials[name]?.toJson(), + after.materials[name]?.toJson(), + )) + name, + ], + fog: + before.fogColor != after.fogColor || + before.fogDensity != after.fogDensity, + music: before.music != after.music, + simulation: [ + if (differs( + [for (final b in before.brushes) b.toJson()], + [for (final b in after.brushes) b.toJson()], + )) + 'brushes', + if (differs( + [for (final e in before.entities) e.toJson()], + [for (final e in after.entities) e.toJson()], + )) + 'entities', + if (differs(before.heightfield?.toJson(), after.heightfield?.toJson())) + 'heightfield', + if (differs( + [for (final r in before.recipes) r.toJson()], + [for (final r in after.recipes) r.toJson()], + )) + 'recipes', + if (before.next != after.next) 'next', + ], + ); +} diff --git a/packages/flutter3d_sim/lib/src/save/rewind.dart b/packages/flutter3d_sim/lib/src/save/rewind.dart index 36d1f609f..e840ea60c 100644 --- a/packages/flutter3d_sim/lib/src/save/rewind.dart +++ b/packages/flutter3d_sim/lib/src/save/rewind.dart @@ -180,6 +180,25 @@ final class RewindBuffer { _keyframes.removeWhere((k) => k.step > point.step); } + /// Makes [point]'s keyframe the oldest thing held: the keyframes on either + /// side of it and the entries before it are forgotten, the entries after it + /// kept. + /// + /// For a change to the world the snapshots do not carry — a level edited + /// while the run goes on. The run is replayed from [point]'s keyframe under + /// the new world; the keyframes before it describe states the old world + /// led to, and the ones after it states the replay has just replaced, so a + /// rewind through either would put one world's state under the other. + void rebaseAt(RewindPoint point) { + final base = point.step - point.replayed; + _keyframes.removeWhere((k) => k.step != base); + final drop = base - _dropped; + if (drop > 0) { + recorder.tape.frames.removeRange(0, drop); + _dropped = base; + } + } + /// Forgets everything, for a new level. void reset() { recorder.tape.frames.clear(); diff --git a/packages/flutter3d_sim/test/level_diff_test.dart b/packages/flutter3d_sim/test/level_diff_test.dart new file mode 100644 index 000000000..6887a9b4f --- /dev/null +++ b/packages/flutter3d_sim/test/level_diff_test.dart @@ -0,0 +1,150 @@ +/// `diffLevel` splits an edit into what can be patched into a running scene +/// and what has to go through a timeline branch. +/// +/// dart test test/level_diff_test.dart +/// +/// Mutation: drop `'entities'` from the simulation list and the moved crate +/// patches in place; compare lights by count alone and the recoloured lamp +/// is missed. +library; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; + +Map _document() => { + 'version': 1, + 'name': 'yard', + 'fogDensity': 0.02, + 'materials': { + 'stone': { + 'color': [0.5, 0.5, 0.5, 1.0], + }, + }, + 'brushes': [ + { + 'at': [0, -0.5, 0], + 'size': [20, 1, 20], + 'material': 'stone', + }, + ], + 'lights': [ + { + 'type': 'point', + 'at': [0, 3, 0], + 'color': [1, 1, 1], + }, + ], + 'entities': [ + { + 'type': 'crate', + 'at': [2, 0, 2], + }, + ], +}; + +/// The level, after [edit] has changed its document. +Level _edited(void Function(Map json) edit) => + Level.fromJson(_document()..let(edit)); + +extension on Map { + void let(void Function(Map) edit) => edit(this); +} + +void main() { + final before = Level.fromJson(_document()); + + test('the same document is no change', () { + final diff = diffLevel(before, Level.fromJson(_document())); + expect(diff.isEmpty, isTrue); + expect(diff.presentationOnly, isTrue); + }); + + test('a recoloured lamp, a material and the fog patch in place', () { + final after = _edited((json) { + ((json['lights']! as List).single! + as Map)['color'] = [ + 1, + 0.5, + 0.2, + ]; + json['materials'] = { + 'stone': { + 'color': [0.3, 0.3, 0.3, 1.0], + }, + 'moss': { + 'color': [0.1, 0.4, 0.1, 1.0], + }, + }; + json['fogDensity'] = 0.05; + }); + + final diff = diffLevel(before, after); + + expect(diff.lights, [0]); + expect(diff.lightCountChanged, isFalse); + expect(diff.materials, unorderedEquals(['stone', 'moss'])); + expect(diff.fog, isTrue); + expect(diff.music, isFalse); + expect(diff.presentationOnly, isTrue); + }); + + test('a lamp added is a count change, not a patch of index one', () { + final after = _edited((json) { + (json['lights']! as List).add({ + 'type': 'point', + 'at': [5, 3, 0], + }); + }); + + final diff = diffLevel(before, after); + + expect(diff.lightCountChanged, isTrue); + expect(diff.lights, isEmpty); + expect(diff.presentationOnly, isTrue); + }); + + test('a moved crate and a wider floor go through the timeline', () { + final after = _edited((json) { + ((json['entities']! as List).single! + as Map)['at'] = [ + 3, + 0, + 2, + ]; + ((json['brushes']! as List).single! + as Map)['size'] = [ + 30, + 1, + 20, + ]; + }); + + final diff = diffLevel(before, after); + + expect(diff.simulation, ['brushes', 'entities']); + expect(diff.presentationOnly, isFalse); + }); + + test('a brush that only changes material is still the simulation\'s', () { + // Its surface falls back to its material, and footsteps read surfaces. + final after = _edited((json) { + ((json['brushes']! as List).single! + as Map)['material'] = + 'moss'; + }); + + expect(diffLevel(before, after).simulation, ['brushes']); + }); + + test('the next level is the run\'s, the music the picture\'s', () { + final after = _edited((json) { + json['next'] = 'cellar'; + json['music'] = 'rain.ogg'; + }); + + final diff = diffLevel(before, after); + + expect(diff.simulation, ['next']); + expect(diff.music, isTrue); + }); +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 26bba71cc..9c60b5c07 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10808 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10818 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 613f6927c..5c3579b92 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,17 +1,17 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10808 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10818 tests need a GPU. --- # Testing -10808 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10818 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| | `flutter3d` | 1620 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | -| `flutter3d_sim` | 579 | | `pad_input` | 67 | +| `flutter3d_sim` | 585 | | `pad_input` | 67 | | `flutter3d_lab` | 15 | | `flutter3d_core` | 735 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | @@ -27,7 +27,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `apps/flutter3d_demo_river` | 53 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 90 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 127 | -| `flutter3d_game` | 255 | | `flutter3d_shaders` | 5 | +| `flutter3d_game` | 259 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | | | | | `flutter3d_net` | 15 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10782 rather than 10808: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10792 rather than 10818: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. From a0cb0585c8431e05190352998b26aa09b16a99f1 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 00:16:30 +0300 Subject: [PATCH 008/141] A level saved in the editor lands in the running game Saving while the project runs sends the document and its digest to ext.flutter3d.level.apply. The game's LiveLevel refuses a document whose digest does not match, patches the look in place, and sends brushes, entities and the ground through RunTimeline.swapLevel so the run replays under the new level instead of jumping. A game with no timeline is rebuilt in place and the answer says so. Checked end to end over a real VM service in the timeline extensions test. --- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_editor/lib/main.dart | 29 +++- .../lib/src/play/level_push.dart | 79 +++++++++ .../test/level_push_test.dart | 70 ++++++++ packages/flutter3d_game/CHANGELOG.md | 7 + .../flutter3d_game/lib/flutter3d_game.dart | 1 + .../lib/src/run/live_level.dart | 156 ++++++++++++++++++ .../test/fixtures/timeline_target.dart | 20 +++ .../flutter3d_game/test/live_level_test.dart | 145 ++++++++++++++++ .../test/run_timeline_extensions_test.dart | 57 +++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- tasks/0.9-engine-roadmap.md | 31 ++-- 14 files changed, 585 insertions(+), 26 deletions(-) create mode 100644 apps/flutter3d_editor/lib/src/play/level_push.dart create mode 100644 apps/flutter3d_editor/test/level_push_test.dart create mode 100644 packages/flutter3d_game/lib/src/run/live_level.dart create mode 100644 packages/flutter3d_game/test/live_level_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 6e91d2a87..f819f0bb5 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2688,7 +2688,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10818 tests** across 41 packages and 10 applications | +| Unit tests | **10828 tests** across 41 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index aa229fa96..dbe73f811 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10818 tests across forty-one packages and nine applications. The +There are 10828 tests across forty-one packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/apps/flutter3d_editor/lib/main.dart b/apps/flutter3d_editor/lib/main.dart index 70911b370..f67ca3569 100644 --- a/apps/flutter3d_editor/lib/main.dart +++ b/apps/flutter3d_editor/lib/main.dart @@ -50,6 +50,7 @@ import 'src/fly_camera.dart'; import 'src/light_plan_dialog.dart'; import 'src/open_run_channel.dart'; import 'src/play/flutter_run.dart'; +import 'src/play/level_push.dart'; import 'src/play/play_screen.dart'; import 'src/playtest_report_screen.dart'; import 'src/recent_projects.dart'; @@ -1112,20 +1113,34 @@ class _EditorScreenState extends State // since they were written. A crash or a full disk halfway through left a // truncated level where the good one had been, so one lost session // became every future one. - await writeFileAtomically( - path, - // A copy of a generated document takes ownership of itself. One that - // still named the generator would invite somebody to run it again, and - // running it again is exactly what throws the work away. - editing.write(claiming: copy ? kAuthor : null), - ); + // A copy of a generated document takes ownership of itself. One that + // still named the generator would invite somebody to run it again, and + // running it again is exactly what throws the work away. + final document = editing.write(claiming: copy ? kAuthor : null); + await writeFileAtomically(path, document); editing.history.saved(); _cubit.say('written to $path'); + // `HR3`: the game this level is playing in takes it too. Not a copy: + // the running game plays the original, not the file beside it. + if (!copy) unawaited(_pushToRunningGame(document)); } catch (error) { _cubit.say('could not write $path: $error'); } } + /// Sends a saved level to the game [_run] started, when it is running, + /// and says in the status strip what the game did with it. + Future _pushToRunningGame(String document) async { + if (_run?.state.value case PlayRunning(:final vmService)) { + try { + final answer = await pushLevel(vmService, document); + _cubit.say(describeLevelApplied(answer)); + } catch (error) { + _cubit.say('saved, but the running game did not take it: $error'); + } + } + } + /// A name beside [path] that nothing is using yet. /// /// **It used to be one name.** Shift-save always wrote `foo.edited.json`, so diff --git a/apps/flutter3d_editor/lib/src/play/level_push.dart b/apps/flutter3d_editor/lib/src/play/level_push.dart new file mode 100644 index 000000000..0f634386b --- /dev/null +++ b/apps/flutter3d_editor/lib/src/play/level_push.dart @@ -0,0 +1,79 @@ +/// `HR3`'s editor half: a saved level, sent to the game running it. +library; + +import 'dart:convert'; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:vm_service/vm_service.dart'; +import 'package:vm_service/vm_service_io.dart'; + +/// The parameters `ext.flutter3d.level.apply` takes for [document]: the text +/// as saved, and the digest of the level it parses to. +/// +/// The digest is taken from the parsed level, not from the text, because the +/// game computes it the same way from what it receives: two documents that +/// differ only in whitespace are the same level and digest the same. +Map levelApplyArguments(String document) => { + 'document': document, + 'hash': Level.fromJson( + jsonDecode(document) as Map, + ).digestHex, +}; + +/// What the game said it did with a level, in a line for the status strip. +String describeLevelApplied(Map answer) { + final diff = answer['diff'] as Map? ?? const {}; + final simulation = (diff['simulation'] as List? ?? const []) + .cast(); + return switch (answer) { + {'swappedAt': final int step} => + 'the game replayed from step $step with the new ' + '${simulation.join(', ')}', + {'rebuiltInPlace': true} => + 'the game rebuilt ${simulation.join(', ')} in place ' + '(it has no timeline to replay)', + _ when simulation.isEmpty && diff.values.every(_unchanged) => + 'the game already had this level', + _ => 'the game took the new look', + }; +} + +bool _unchanged(Object? value) => switch (value) { + false => true, + final List list => list.isEmpty, + _ => false, +}; + +/// Sends [document] to the game whose VM service is at [vmService], to the +/// isolate that answers `ext.flutter3d.level.apply`. +/// +/// Throws when no isolate there has registered it: a game that never called +/// `registerLevelExtension` cannot take a level, and saying so is better +/// than a silence the person reads as "sent". +Future> pushLevel( + String vmService, + String document, +) async { + const method = 'ext.flutter3d.level.apply'; + final service = await vmServiceConnectUri(vmService); + try { + final vm = await service.getVM(); + for (final ref in vm.isolates ?? const []) { + final isolate = await service.getIsolate(ref.id!); + if (!(isolate.extensionRPCs ?? const []).contains(method)) { + continue; + } + final response = await service.callServiceExtension( + method, + isolateId: ref.id, + args: levelApplyArguments(document), + ); + return response.json ?? const {}; + } + throw StateError( + 'the running game does not take levels: nothing registered $method', + ); + } finally { + await service.dispose(); + } +} diff --git a/apps/flutter3d_editor/test/level_push_test.dart b/apps/flutter3d_editor/test/level_push_test.dart new file mode 100644 index 000000000..01b2d48d0 --- /dev/null +++ b/apps/flutter3d_editor/test/level_push_test.dart @@ -0,0 +1,70 @@ +/// `HR3`'s editor half: what a saved level is sent as, and how the game's +/// answer reads in the status strip. +/// +/// flutter test test/level_push_test.dart +library; + +import 'dart:convert'; + +import 'package:flutter3d_editor/src/play/level_push.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; + +const String _document = '{"version": 1, "name": "yard", "fogDensity": 0.02}'; + +void main() { + test('the hash is the level\'s digest, as the game computes it', () { + final arguments = levelApplyArguments(_document); + + expect(arguments['document'], _document); + expect( + arguments['hash'], + Level.fromJson(jsonDecode(_document) as Map).digestHex, + ); + // Whitespace is not a different level. + expect( + levelApplyArguments( + '{\n "version": 1,\n "name": "yard",\n "fogDensity": 0.02\n}', + )['hash'], + arguments['hash'], + ); + }); + + test('the game\'s answer, in a line', () { + Map answer({ + List simulation = const [], + bool fog = false, + int? swappedAt, + bool rebuiltInPlace = false, + }) => { + 'diff': { + 'lights': [], + 'lightCountChanged': false, + 'materials': [], + 'fog': fog, + 'music': false, + 'simulation': simulation, + }, + 'swappedAt': ?swappedAt, + 'rebuiltInPlace': rebuiltInPlace, + }; + + expect( + describeLevelApplied( + answer(simulation: ['brushes'], swappedAt: 120), + ), + 'the game replayed from step 120 with the new brushes', + ); + expect( + describeLevelApplied( + answer(simulation: ['entities'], rebuiltInPlace: true), + ), + contains('in place'), + ); + expect( + describeLevelApplied(answer(fog: true)), + 'the game took the new look', + ); + expect(describeLevelApplied(answer()), 'the game already had this level'); + }); +} diff --git a/packages/flutter3d_game/CHANGELOG.md b/packages/flutter3d_game/CHANGELOG.md index e8aa354ab..9c270ff1c 100644 --- a/packages/flutter3d_game/CHANGELOG.md +++ b/packages/flutter3d_game/CHANGELOG.md @@ -1,5 +1,12 @@ ## Unreleased +- **`LiveLevel` takes a level edited under a running game.** The game hands + it `present` and `rebuild`; `apply` patches the look in place and sends the + simulation's half through `swapLevel`. `registerLevelExtension` puts it on + the VM service as `ext.flutter3d.level.apply {document, hash}`, which the + editor calls after a save; a document whose digest does not match its hash + is refused. + - **`RunTimeline.swapLevel` replaces the level under a running game without the run jumping.** It restores the last keyframe, lets the caller swap the level, replays the recorded input to the present with the devices muted, diff --git a/packages/flutter3d_game/lib/flutter3d_game.dart b/packages/flutter3d_game/lib/flutter3d_game.dart index e3c2eec9e..a661ec84d 100644 --- a/packages/flutter3d_game/lib/flutter3d_game.dart +++ b/packages/flutter3d_game/lib/flutter3d_game.dart @@ -44,6 +44,7 @@ export 'src/input/playing.dart'; export 'src/input/touch_controls.dart'; export 'src/run/bug_report.dart'; export 'src/run/demo_timeline.dart'; +export 'src/run/live_level.dart'; export 'src/run/run_session.dart'; export 'src/run/run_timeline.dart'; export 'src/run/run_timeline_extensions.dart'; diff --git a/packages/flutter3d_game/lib/src/run/live_level.dart b/packages/flutter3d_game/lib/src/run/live_level.dart new file mode 100644 index 000000000..d1712127f --- /dev/null +++ b/packages/flutter3d_game/lib/src/run/live_level.dart @@ -0,0 +1,156 @@ +import 'dart:convert'; +import 'dart:developer' as developer; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; + +import 'run_timeline.dart'; + +/// The level a running game is playing, and how to change it under the game. +/// +/// **The game says how; this says when.** Only the game knows what its level +/// became once loaded — which scene nodes are its lights, which colliders +/// its brushes, what its entities spawned — so it hands over two functions: +/// [present] patches what only the picture reads, [rebuild] replaces what the +/// simulation reads. [apply] asks [diffLevel] which of the two an edit needs +/// and calls them in the order that keeps the run honest: the simulation's +/// half through [timeline] when there is one, so the run is replayed under +/// the new level rather than jumping, then the picture's. +/// +/// Without a [timeline] the simulation's half is rebuilt in place, which is +/// what a game with no rewind buffer can do and what it then gets: a run no +/// replay can reach. The report says which happened. +final class LiveLevel { + LiveLevel({ + required this._level, + required this.present, + required this.rebuild, + this.timeline, + }); + + /// Patches the running scene to [next]'s look. [diff] names what changed, + /// so a presenter can touch one lamp rather than rebuild them all. + final void Function(Level next, LevelDiff diff) present; + + /// Replaces what the simulation reads — colliders, spawns, the ground — + /// with [next]'s. Called inside `RunTimeline.swapLevel` when there is a + /// [timeline], so it must leave to the snapshot restore what snapshots + /// carry. + final void Function(Level next) rebuild; + + /// The run to branch when an edit reaches the simulation. + final RunTimeline? timeline; + + /// The level being played now. + Level get level => _level; + Level _level; + + /// Makes [next] the level being played, as little disturbed as the change + /// allows. + LevelApplied apply(Level next) { + final diff = diffLevel(_level, next); + if (diff.isEmpty) { + _level = next; + return LevelApplied(diff: diff); + } + final int? swappedAt; + if (diff.presentationOnly) { + swappedAt = null; + } else if (timeline case final RunTimeline run) { + swappedAt = run.swapLevel( + () => rebuild(next), + levelDigest: next.digestHex, + ); + } else { + rebuild(next); + swappedAt = null; + } + present(next, diff); + _level = next; + return LevelApplied( + diff: diff, + swappedAt: swappedAt, + rebuiltInPlace: !diff.presentationOnly && swappedAt == null, + ); + } +} + +/// What one [LiveLevel.apply] did. +final class LevelApplied { + const LevelApplied({ + required this.diff, + this.swappedAt, + this.rebuiltInPlace = false, + }); + + final LevelDiff diff; + + /// The step the timeline branched at, when the edit reached the + /// simulation and there was a timeline to branch. + final int? swappedAt; + + /// The edit reached the simulation and there was no timeline: the run + /// carries on from a state a replay cannot reach. + final bool rebuiltInPlace; + + Map toJson() => { + 'diff': diff.toJson(), + 'swappedAt': ?swappedAt, + 'rebuiltInPlace': rebuiltInPlace, + }; +} + +/// Answers `ext.flutter3d.level.apply` for [live]: [parameters] carry the +/// level's whole `document`, as JSON, and its `hash`, `Level.digestHex`. +/// +/// **The hash is checked, not trusted.** It is what the editor computed from +/// the level it saved; the document is what arrived. When the two disagree, +/// something between them changed the bytes — an encoding, a truncation — +/// and applying the document would put a level under the run that the editor +/// never showed anybody. So a mismatch is refused, naming both. +/// +/// A plain function over the parameters, and [registerLevelExtension] a thin +/// door onto it, so the answers are tested without a VM service. +({Map? result, String? error}) answerLevelApply( + LiveLevel live, + Map parameters, +) { + final document = parameters['document']; + final hash = parameters['hash']; + if (document == null || hash == null) { + return (result: null, error: 'level.apply takes a document and its hash'); + } + final Level next; + try { + next = Level.fromJson(jsonDecode(document) as Map); + } on Object catch (error) { + return (result: null, error: 'the document is not a level: $error'); + } + if (next.digestHex != hash) { + return ( + result: null, + error: + 'the document digests to ${next.digestHex}, not $hash; ' + 'it changed on the way', + ); + } + return (result: live.apply(next).toJson(), error: null); +} + +/// Puts [live] on the VM service as `ext.flutter3d.level.apply`, beside the +/// timeline's own extensions: the editor that saved a level sends it here, +/// to a game on this machine or on a phone, and the running game takes it. +void registerLevelExtension(LiveLevel live) { + developer.registerExtension('ext.flutter3d.level.apply', ( + method, + parameters, + ) async { + final (:result, :error) = answerLevelApply(live, parameters); + if (error != null) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + error, + ); + } + return developer.ServiceExtensionResponse.result(jsonEncode(result)); + }); +} diff --git a/packages/flutter3d_game/test/fixtures/timeline_target.dart b/packages/flutter3d_game/test/fixtures/timeline_target.dart index d98b93173..808ac5546 100644 --- a/packages/flutter3d_game/test/fixtures/timeline_target.dart +++ b/packages/flutter3d_game/test/fixtures/timeline_target.dart @@ -47,6 +47,26 @@ void main() { ); final frameTimes = StepTimeTrace(); var stepNumber = 0; + // `HR3`: a level the toy plays on, taken from outside as the editor + // sends it. The toy reads nothing from it; what is checked from outside is + // that a brush change branches the timeline and a look change does not. + registerLevelExtension( + LiveLevel( + level: Level.fromJson(const { + 'version': 1, + 'brushes': [ + { + 'at': [0, 0, 0], + 'size': [1, 1, 1], + 'material': 'stone', + }, + ], + }), + present: (next, diff) {}, + rebuild: (next) {}, + timeline: timeline, + ), + ); registerTimelineExtensions( timeline, frameTimes: frameTimes, diff --git a/packages/flutter3d_game/test/live_level_test.dart b/packages/flutter3d_game/test/live_level_test.dart new file mode 100644 index 000000000..87982d838 --- /dev/null +++ b/packages/flutter3d_game/test/live_level_test.dart @@ -0,0 +1,145 @@ +/// `HR3`: a level saved in the editor reaches the running game through +/// `ext.flutter3d.level.apply`, and the game takes it as little disturbed as +/// the change allows. +/// +/// flutter test test/live_level_test.dart +/// +/// What the extension does is `answerLevelApply`, tested here over the same +/// string parameters the VM service hands it. +library; + +import 'dart:convert'; + +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; + +Map _document({double wallAt = 5.0, double fog = 0.0}) => + { + 'version': 1, + 'name': 'corridor', + 'fogDensity': fog, + 'brushes': [ + { + 'at': [wallAt, 0, 0], + 'size': [1, 4, 4], + 'material': 'stone', + }, + ], + }; + +Level _level({double wallAt = 5.0, double fog = 0.0}) => + Level.fromJson(_document(wallAt: wallAt, fog: fog)); + +/// What a game did with each call, in order. +({LiveLevel live, List calls}) _game({RunTimeline? timeline}) { + final calls = []; + return ( + live: LiveLevel( + level: _level(), + present: (next, diff) => calls.add('present fog=${diff.fog}'), + rebuild: (next) => calls.add('rebuild ${next.brushes.single.centre.x}'), + timeline: timeline, + ), + calls: calls, + ); +} + +Map _send(Level level) => { + 'document': jsonEncode(level.toJson()), + 'hash': level.digestHex, +}; + +void main() { + test('a look-only edit is presented and the run is left alone', () { + final game = _game(); + final applied = game.live.apply(_level(fog: 0.04)); + + expect(game.calls, ['present fog=true']); + expect(applied.swappedAt, isNull); + expect(applied.rebuiltInPlace, isFalse); + expect(game.live.level.fogDensity, 0.04); + }); + + test('a moved wall goes through the timeline, then the picture', () { + final input = InputState(); + final rewind = RewindBuffer(stepsPerSecond: 60); + final restored = []; + final timeline = RunTimeline( + rewind: rewind, + input: input, + stepSim: (dt) {}, + restore: restored.add, + ); + for (var step = 0; step < 30; step++) { + rewind.recorder.record(input); + if (rewind.keyframeDue) rewind.keyframe(Snapshot({})); + } + final game = _game(timeline: timeline); + + final applied = game.live.apply(_level(wallAt: 8.0)); + + expect(game.calls, ['rebuild 8.0', 'present fog=false']); + expect(applied.swappedAt, 0); + expect(restored, hasLength(1), reason: 'the keyframe the replay starts at'); + expect( + timeline.history.single, + TimelineLevelSwapped(0, _level(wallAt: 8.0).digestHex), + ); + }); + + test('with no timeline the wall is rebuilt in place, and says so', () { + final game = _game(); + final applied = game.live.apply(_level(wallAt: 8.0)); + + expect(game.calls.first, 'rebuild 8.0'); + expect(applied.rebuiltInPlace, isTrue); + }); + + test('the same level again touches nothing', () { + final game = _game(); + expect(game.live.apply(_level()).diff.isEmpty, isTrue); + expect(game.calls, isEmpty); + }); + + group('ext.flutter3d.level.apply', () { + test('applies a document whose hash matches, and reports how', () { + final game = _game(); + final (:result, :error) = answerLevelApply( + game.live, + _send(_level(fog: 0.04)), + ); + + expect(error, isNull); + expect((result!['diff']! as Map)['fog'], isTrue); + expect(result['rebuiltInPlace'], isFalse); + }); + + test('refuses a document that changed on the way', () { + final game = _game(); + final parameters = _send(_level(fog: 0.04)) + ..['hash'] = _level().digestHex; + + final (:result, :error) = answerLevelApply(game.live, parameters); + + expect(result, isNull); + expect(error, contains('changed on the way')); + expect(game.calls, isEmpty); + }); + + test('refuses what is not a level, and what is missing', () { + final game = _game(); + expect( + answerLevelApply(game.live, { + 'document': '[1, 2]', + 'hash': 'x', + }).error, + contains('not a level'), + ); + expect( + answerLevelApply(game.live, {}).error, + contains('takes a document'), + ); + }); + }); +} diff --git a/packages/flutter3d_game/test/run_timeline_extensions_test.dart b/packages/flutter3d_game/test/run_timeline_extensions_test.dart index 8f67e78a1..664658e82 100644 --- a/packages/flutter3d_game/test/run_timeline_extensions_test.dart +++ b/packages/flutter3d_game/test/run_timeline_extensions_test.dart @@ -22,6 +22,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; import 'package:test/test.dart'; import 'package:vm_service/vm_service.dart'; import 'package:vm_service/vm_service_io.dart'; @@ -238,4 +239,60 @@ void main() { expect(bugReportJson.containsKey('x'), isTrue); expect(bugReportJson.containsKey('step'), isTrue); }, timeout: const Timeout(Duration(seconds: 60))); + + test('a level sent from outside the process branches the run when it ' + 'moves a brush, and is refused when it changed on the way', () async { + final target = await _startAndConnect(); + addTearDown(() { + target.service.dispose(); + target.process.kill(); + }); + // A keyframe or two to branch from. + await Future.delayed(const Duration(seconds: 2)); + + final moved = { + 'version': 1, + 'brushes': [ + { + 'at': [3, 0, 0], + 'size': [1, 1, 1], + 'material': 'stone', + }, + ], + }; + final document = jsonEncode(moved); + final hash = Level.fromJson(moved).digestHex; + + final applied = _decode( + await target.service.callServiceExtension( + 'ext.flutter3d.level.apply', + isolateId: target.isolateId, + args: {'document': document, 'hash': hash}, + ), + ); + expect(applied['swappedAt'], isA()); + expect((applied['diff']! as Map)['simulation'], [ + 'brushes', + ]); + + final history = _decode( + await target.service.callServiceExtension( + 'ext.flutter3d.timeline.history', + isolateId: target.isolateId, + ), + ); + expect( + (history['commands']! as List).last, + 'level:${applied['swappedAt']}:$hash', + ); + + await expectLater( + target.service.callServiceExtension( + 'ext.flutter3d.level.apply', + isolateId: target.isolateId, + args: {'document': document, 'hash': 'deadbeef'}, + ), + throwsA(isA()), + ); + }, timeout: const Timeout(Duration(minutes: 3))); } diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 9c60b5c07..5377724db 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10818 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10828 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 5c3579b92..ca3766860 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10818 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10828 tests need a GPU. --- # Testing -10818 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10828 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -20,14 +20,14 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 63 | | `apps/flutter3d_demo_platformer` | 199 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 328 | | `apps/flutter3d_demo_strategy` | 50 | -| `apps/flutter3d_editor` | 221 | | `apps/flutter3d_demo_arcade` | 20 | +| `apps/flutter3d_editor` | 223 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 28 | | `apps/flutter3d_demo_river` | 53 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 90 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 127 | -| `flutter3d_game` | 259 | | `flutter3d_shaders` | 5 | +| `flutter3d_game` | 267 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | | | | | `flutter3d_net` | 15 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10792 rather than 10818: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10802 rather than 10828: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index ce221bba9..fde91767a 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -140,17 +140,26 @@ mutable across four backends and the conformance suite, and waits until a texture registry gives it a caller. Environments follow textures. The editor's save hook arrives with HR5. -**HR3. A level from the editor into a running game (L).** The editor sends -`ext.flutter3d.level.apply {document or patch, hash}`. `diffLevel` keyed by -entity id splits the change: presentation (lights, decor, materials, -visibility) is patched in place; anything the simulation reads (colliders, -entities, signals) is applied as a timeline branch: rewind to a keyframe, -swap the level, replay the recorded input to now. A `TimelineLevelSwapped` -command and the level hash per `Demo` segment keep a replay reproducible, which -is the condition `ROADMAP.md` set for level reload. It travels over the VM -service, so it works on a device, and the editor MCP drives the same path. -*Tests:* diff classifier units; a swap at step K replays to the same final -digest; an editor ↔ game run over a real VM service. +**HR3. A level from the editor into a running game (L).** *Done:* saving in +the editor while `PlayScreen` has the project running sends the document and +its `Level.digestHex` to `ext.flutter3d.level.apply`, on the isolate that +registered it. The game's `LiveLevel` checks the digest (a mismatch is +refused, naming both), and `diffLevel` splits the change: lights, materials, +fog and music go to the game's `present`; brushes, entities, the ground, +recipes and `next` go through `RunTimeline.swapLevel`, which restores the last +keyframe, runs the game's `rebuild`, replays the recorded input to now and +rebases the rewind buffer there. `TimelineLevelSwapped(step, digest)` lands in +the history (`level::` over the wire). A game with no timeline +is rebuilt in place and the answer says so. There are no entity ids in the +format, so parts are compared by document, conservatively: a brush that only +changed material is still the simulation's. +*Tests:* classifier units; a run swapped at step K arrives where a fresh run +with the new level from K does; the extension end to end over a real VM +service (`run_timeline_extensions_test.dart`). +*Left:* patches instead of whole documents; the swap written into a `Demo` +segment so a saved `.f3drun` replays it (the history has it, the file format +does not yet); the editor MCP driving the same path; a demo game wired to +`LiveLevel` so the loop is visible in one of ours. **HR4. Live parameters and replay after a reload (S–M).** Inspector drags send `ext.flutter3d.material.set` and `ext.flutter3d.cvar.set` at about 30 Hz, From 52dc84a36c2fef333ca50da0bc7edb7fc2bcca1a Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 01:04:12 +0300 Subject: [PATCH 009/141] A tuned number is on the tape, and new code can relive the last seconds InputState.tune sets a tunable for one step; the recorder writes it and playback applies it, and Tunables takes it before the step reads it and keeps it in the snapshot. ext.flutter3d.cvar.set goes through the same door, so a run tuned while played replays like any other. RunTimeline.replayUnderNewCode lives the last seconds again from the nearest keyframe under the code running now, compares each keyframe the old run left and the present, and names the first field that parted and the steps it parted between. firstDifferingPath moves to flutter3d_sim for it; flutter3d_net still exports it. --- ARCHITECTURE.md | 2 +- README.md | 2 +- packages/flutter3d_game/CHANGELOG.md | 10 ++ .../lib/src/run/run_timeline.dart | 152 ++++++++++++++++++ .../lib/src/run/run_timeline_extensions.dart | 72 +++++++++ .../test/fixtures/timeline_target.dart | 15 +- .../test/run_timeline_extensions_test.dart | 57 +++++++ .../run_timeline_new_code_replay_test.dart | 141 ++++++++++++++++ packages/flutter3d_net/CHANGELOG.md | 6 + packages/flutter3d_net/lib/flutter3d_net.dart | 2 + .../lib/src/snapshot_divergence.dart | 40 ----- packages/flutter3d_sim/CHANGELOG.md | 9 ++ packages/flutter3d_sim/lib/flutter3d_sim.dart | 2 + .../lib/src/input/input_state.dart | 23 +++ .../lib/src/input/input_tape.dart | 17 ++ .../flutter3d_sim/lib/src/input/tunables.dart | 66 ++++++++ .../lib/src/save/first_differing_path.dart | 41 +++++ .../flutter3d_sim/lib/src/save/rewind.dart | 10 ++ .../flutter3d_sim/test/tunables_test.dart | 106 ++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- 21 files changed, 735 insertions(+), 50 deletions(-) create mode 100644 packages/flutter3d_game/test/run_timeline_new_code_replay_test.dart create mode 100644 packages/flutter3d_sim/lib/src/input/tunables.dart create mode 100644 packages/flutter3d_sim/lib/src/save/first_differing_path.dart create mode 100644 packages/flutter3d_sim/test/tunables_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f819f0bb5..23ec0478d 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2688,7 +2688,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10828 tests** across 41 packages and 10 applications | +| Unit tests | **10839 tests** across 41 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index dbe73f811..d486d56a9 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10828 tests across forty-one packages and nine applications. The +There are 10839 tests across forty-one packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/packages/flutter3d_game/CHANGELOG.md b/packages/flutter3d_game/CHANGELOG.md index 9c270ff1c..e74d2708a 100644 --- a/packages/flutter3d_game/CHANGELOG.md +++ b/packages/flutter3d_game/CHANGELOG.md @@ -1,5 +1,15 @@ ## Unreleased +- **`RunTimeline.replayUnderNewCode` shows what a code reload changed.** It + lives the last seconds again under the new code from the nearest keyframe, + compares the state at each keyframe the old run left and at the present, + and names the first field that parted and the two steps it parted between. + The replay is kept and the buffer rebased there. +- **`registerTuningExtensions`** puts `ext.flutter3d.cvar.set` and + `cvar.list` on the VM service; a set goes through `InputState.tune`, so it + lands on the tape. `registerTimelineExtensions(capture:)` adds + `ext.flutter3d.timeline.replayUnderNewCode`. + - **`LiveLevel` takes a level edited under a running game.** The game hands it `present` and `rebuild`; `apply` patches the look in place and sends the simulation's half through `swapLevel`. `registerLevelExtension` puts it on diff --git a/packages/flutter3d_game/lib/src/run/run_timeline.dart b/packages/flutter3d_game/lib/src/run/run_timeline.dart index b047712d5..41c359317 100644 --- a/packages/flutter3d_game/lib/src/run/run_timeline.dart +++ b/packages/flutter3d_game/lib/src/run/run_timeline.dart @@ -52,6 +52,86 @@ final class TimelineLevelSwapped extends TimelineCommand { int get hashCode => Object.hash(step, levelDigest); } +/// The last seconds were lived again under new code, from [fromStep] to the +/// present — `RunTimeline.replayUnderNewCode`. +final class TimelineReplayed extends TimelineCommand { + const TimelineReplayed(this.fromStep); + final int fromStep; + + @override + bool operator ==(Object other) => + other is TimelineReplayed && other.fromStep == fromStep; + + @override + int get hashCode => fromStep.hashCode; +} + +/// Where the run new code makes first parts from the one old code made. +/// +/// **Between two keyframes, not at one step.** The old run is held as a +/// snapshot a keyframe interval apart and the tape between; what it was at +/// the steps between keyframes is gone. So this names the last step the two +/// runs were seen to agree ([agreedAt]) and the first they were seen to differ +/// ([step]), and the first field that differed there. Closer keyframes narrow +/// it; `RewindBuffer.keyframeEvery` is the dial. +final class ReplayDivergence { + const ReplayDivergence({ + required this.agreedAt, + required this.step, + required this.path, + required this.before, + required this.after, + }); + + /// The last step both runs were compared at and agreed. + final int agreedAt; + + /// The first step they were compared at and differed. + final int step; + + /// Where in the snapshot, as `firstDifferingPath` spells it. + final String path; + + /// The value under the old code. + final Object? before; + + /// The value under the new code. + final Object? after; + + Map toJson() => { + 'agreedAt': agreedAt, + 'step': step, + 'path': path, + 'before': before, + 'after': after, + }; + + @override + String toString() => + 'between step $agreedAt and $step, `$path` went from $before to $after'; +} + +/// What `RunTimeline.replayUnderNewCode` did. +final class CodeReplay { + const CodeReplay({ + required this.fromStep, + required this.toStep, + this.divergence, + }); + + final int fromStep; + final int toStep; + + /// Null when the new code made the same run as the old. + final ReplayDivergence? divergence; + + Map toJson() => { + 'fromStep': fromStep, + 'toStep': toStep, + 'divergence': divergence?.toJson(), + }; +} + /// Pause, step, rewind and branch, built on a live [RewindBuffer]. /// /// **What `rp-02`'s editor panel is a face for, not the panel itself.** The @@ -226,6 +306,78 @@ final class RunTimeline { return at; } + /// Lives the last [seconds] again under the code running now, and says + /// where that run parts from the one the old code made. + /// + /// **What a code reload is worth to a simulation.** A hot reload swaps the + /// step function and keeps the state, so the run carries on under new code + /// from a present the old code made. That is enough to see the new code + /// running and not enough to see what it changes. This goes back to the + /// keyframe at or before [seconds] ago, replays the recorded input to the + /// present under the new code with the devices muted, and compares the + /// state at each keyframe the old run left — and at the present, which + /// [capture] reads before anything moves — against what the replay reaches + /// there. The first that differs is the [ReplayDivergence]; none differing + /// means the change did not touch these seconds. + /// + /// The replay is kept: the present is the new code's afterwards, and the + /// buffer is rebased on the keyframe it started from, since the keyframes + /// after it are the old code's. Null when the buffer does not reach back. + CodeReplay? replayUnderNewCode({ + required double seconds, + required Snapshot Function() capture, + }) { + final now = rewind.step; + final point = rewind.rewindBy(seconds); + if (point == null) return null; + final from = point.step - point.replayed; + final before = { + ...rewind.keyframesAfter(from), + now: capture(), + }; + + restore(point.snapshot); + final replay = InputTapePlayback( + InputTape(seed: point.seed, frames: point.frames), + ); + var agreed = from; + ReplayDivergence? divergence; + void compare(int step) { + final old = before[step]; + if (old == null || divergence != null) return; + final differs = firstDifferingPath(old.data, capture().data); + if (differs == null) { + agreed = step; + } else { + divergence = ReplayDivergence( + agreedAt: agreed, + step: step, + path: differs.path, + before: differs.a, + after: differs.b, + ); + } + } + + final wasMuted = input.muted; + input.muted = true; + try { + for (var step = from; !replay.isFinished; step++) { + if (step != from) compare(step); + replay.applyTo(input); + input.beginStep(); + stepSim(stepSeconds); + input.endStep(); + } + } finally { + input.muted = wasMuted; + } + compare(now); + rewind.rebaseAt(point); + _history.add(TimelineReplayed(from)); + return CodeReplay(fromStep: from, toStep: now, divergence: divergence); + } + /// [releaseAt], given the step directly rather than a [RewindPoint] — /// what a caller across a wire has, since a step number survives being /// sent as a string and a [RewindPoint] does not. Returns whether the diff --git a/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart b/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart index bcdb35508..001248cbc 100644 --- a/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart +++ b/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart @@ -57,6 +57,12 @@ import 'run_timeline.dart'; /// button the game itself draws. What the callback returns is the /// caller's business; a `Demo.toJson()` is the obvious shape, and this /// does not require it. +/// * `replayUnderNewCode` — registered only when [capture] is given; +/// `seconds`, a number as a string (three when absent); returns +/// [CodeReplay.toJson], or `{"reached": false}` when the buffer does not +/// reach that far. `HR4`: called after a hot reload, it lives the last +/// seconds again under the new code and names where that run parts from +/// the old one. /// /// Registered once per [RunTimeline]; registering the same [timeline] twice /// throws, the same way [developer.registerExtension] itself refuses a @@ -65,6 +71,7 @@ void registerTimelineExtensions( RunTimeline timeline, { StepTimeTrace? frameTimes, Map Function()? bugReport, + Snapshot Function()? capture, }) { developer.registerExtension('ext.flutter3d.timeline.pause', ( method, @@ -167,6 +174,24 @@ void registerTimelineExtensions( }); } + if (capture != null) { + developer.registerExtension('ext.flutter3d.timeline.replayUnderNewCode', ( + method, + parameters, + ) async { + final seconds = double.tryParse(parameters['seconds'] ?? '') ?? 3.0; + final replay = timeline.replayUnderNewCode( + seconds: seconds, + capture: capture, + ); + return developer.ServiceExtensionResponse.result( + jsonEncode( + replay?.toJson() ?? const {'reached': false}, + ), + ); + }); + } + if (bugReport != null) { developer.registerExtension('ext.flutter3d.timeline.bugReport', ( method, @@ -193,4 +218,51 @@ String _describe(TimelineCommand command) => switch (command) { TimelineBranched(:final step) => 'branched:$step', TimelineLevelSwapped(:final step, :final levelDigest) => 'level:$step:$levelDigest', + TimelineReplayed(:final fromStep) => 'replayed:$fromStep', }; + +/// Puts [tunables] on the VM service, so an inspector can change them while +/// the game runs: `ext.flutter3d.cvar.set {name, value}` tunes one through +/// [input] — so the change is on the tape with the step that takes it — and +/// `ext.flutter3d.cvar.list` answers every tunable, its value and its default. +/// +/// A name the table does not declare, or a value that is not a number, is +/// refused rather than dropped, so the inspector that sent it hears why. +void registerTuningExtensions(InputState input, Tunables tunables) { + developer.registerExtension('ext.flutter3d.cvar.set', ( + method, + parameters, + ) async { + final name = parameters['name']; + final value = double.tryParse(parameters['value'] ?? ''); + if (name == null || value == null) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + 'cvar.set takes a name and a numeric value', + ); + } + if (!tunables.defaults.containsKey(name)) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + 'no tunable is called $name; there are ' + '${tunables.defaults.keys.join(', ')}', + ); + } + input.tune(name, value); + return developer.ServiceExtensionResponse.result('{}'); + }); + developer.registerExtension('ext.flutter3d.cvar.list', ( + method, + parameters, + ) async { + return developer.ServiceExtensionResponse.result( + jsonEncode({ + for (final MapEntry(key: name, :value) in tunables.values.entries) + name: { + 'value': value, + 'default': tunables.defaults[name], + }, + }), + ); + }); +} diff --git a/packages/flutter3d_game/test/fixtures/timeline_target.dart b/packages/flutter3d_game/test/fixtures/timeline_target.dart index 808ac5546..025812034 100644 --- a/packages/flutter3d_game/test/fixtures/timeline_target.dart +++ b/packages/flutter3d_game/test/fixtures/timeline_target.dart @@ -22,15 +22,24 @@ import 'package:flutter_test/flutter_test.dart'; final class _Toy { _Toy(int seed) : dice = GameRandom(seed); final GameRandom dice; + final Tunables tunables = Tunables(const {'speed': 1.0}); double x = 0.0; - void step(InputState input) => x += 1.0; + void step(InputState input) { + tunables.take(input); + x += tunables['speed']; + } - Snapshot save() => Snapshot({'x': x, 'random': dice.state}); + Snapshot save() => Snapshot({ + 'x': x, + 'random': dice.state, + 'tunables': tunables.toJson(), + }); void restore(Snapshot snapshot) { x = snapshot.data.number('x'); dice.state = snapshot.data.integer('random'); + tunables.restore(snapshot.data['tunables']! as Map); } } @@ -67,8 +76,10 @@ void main() { timeline: timeline, ), ); + registerTuningExtensions(input, toy.tunables); registerTimelineExtensions( timeline, + capture: toy.save, frameTimes: frameTimes, bugReport: () => {'x': toy.x, 'step': stepNumber}, ); diff --git a/packages/flutter3d_game/test/run_timeline_extensions_test.dart b/packages/flutter3d_game/test/run_timeline_extensions_test.dart index 664658e82..9fb2059a8 100644 --- a/packages/flutter3d_game/test/run_timeline_extensions_test.dart +++ b/packages/flutter3d_game/test/run_timeline_extensions_test.dart @@ -295,4 +295,61 @@ void main() { throwsA(isA()), ); }, timeout: const Timeout(Duration(minutes: 3))); + + test( + 'a tunable set and a replay after reload, from outside the process', + () async { + final target = await _startAndConnect(); + addTearDown(() { + target.service.dispose(); + target.process.kill(); + }); + await Future.delayed(const Duration(seconds: 2)); + + Future> call( + String method, [ + Map? args, + ]) async => _decode( + await target.service.callServiceExtension( + method, + isolateId: target.isolateId, + args: args, + ), + ); + + expect(await call('ext.flutter3d.cvar.list'), { + 'speed': {'value': 1.0, 'default': 1.0}, + }); + expect( + await call('ext.flutter3d.cvar.set', { + 'name': 'speed', + 'value': '2.5', + }), + {}, + ); + await Future.delayed(const Duration(milliseconds: 200)); + expect( + ((await call('ext.flutter3d.cvar.list'))['speed']! + as Map)['value'], + 2.5, + ); + await expectLater( + call('ext.flutter3d.cvar.set', { + 'name': 'gravity', + 'value': '1', + }), + throwsA(isA()), + ); + + // Nothing reloaded, so the replay makes the same run: the tune is on the + // tape and the tunables are in the snapshot. + final replay = await call( + 'ext.flutter3d.timeline.replayUnderNewCode', + {'seconds': '1'}, + ); + expect(replay['fromStep'], isA()); + expect(replay['divergence'], isNull); + }, + timeout: const Timeout(Duration(minutes: 3)), + ); } diff --git a/packages/flutter3d_game/test/run_timeline_new_code_replay_test.dart b/packages/flutter3d_game/test/run_timeline_new_code_replay_test.dart new file mode 100644 index 000000000..ead08928d --- /dev/null +++ b/packages/flutter3d_game/test/run_timeline_new_code_replay_test.dart @@ -0,0 +1,141 @@ +/// `HR4`: after a code reload, the last seconds lived again under the new +/// code, and the first place that run parts from the old one named. +/// +/// flutter test test/run_timeline_new_code_replay_test.dart +/// +/// The toy's step is a function the test swaps, the way a hot reload swaps +/// the code behind a step: the new rule only differs once the walker is past +/// a mark, so the old and new runs agree for a while and then do not. +/// +/// Mutation: skip `compare(step)` inside the loop and the divergence is +/// found only at the present; replay without restoring the keyframe and the +/// state after the replay is not the fresh run's. +library; + +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; + +typedef _Rule = double Function(double x, double push); + +double _old(double x, double push) => x + push * 0.05; + +/// Walks at double speed once past 8: the same run as [_old] until then. +double _new(double x, double push) => x + push * (x > 8.0 ? 0.1 : 0.05); + +final class _Walker { + _Rule rule = _old; + double x = 0.0; + + void step(InputState input) => x = rule(x, input.moveAxis.x); + + Snapshot save() => Snapshot({'x': x}); + void restore(Snapshot snapshot) => x = snapshot.data.number('x'); +} + +({_Walker walker, InputState input, RewindBuffer rewind, RunTimeline timeline}) +_run() { + final walker = _Walker(); + final input = InputState(); + final rewind = RewindBuffer(stepsPerSecond: 60, history: 10.0); + return ( + walker: walker, + input: input, + rewind: rewind, + timeline: RunTimeline( + rewind: rewind, + input: input, + stepSim: (dt) => walker.step(input), + restore: walker.restore, + ), + ); +} + +void _steps( + ({ + _Walker walker, + InputState input, + RewindBuffer rewind, + RunTimeline timeline, + }) + it, + int from, + int to, +) { + for (var step = from; step < to; step++) { + it.input.setStickAxis(step % 5 == 0 ? 0.0 : 1.0, 0.0); + it.rewind.recorder.record(it.input); + it.input.beginStep(); + if (it.rewind.keyframeDue) it.rewind.keyframe(it.walker.save()); + it.walker.step(it.input); + it.input.endStep(); + } +} + +void main() { + test('the replay arrives where a run with the new code from the keyframe ' + 'does, and names where the two first part', () { + final live = _run(); + _steps(live, 0, 300); + // x passes 8 at about step 200, well inside the last three seconds. + live.walker.rule = _new; + + final replay = live.timeline.replayUnderNewCode( + seconds: 3.0, + capture: live.walker.save, + )!; + + expect(replay.fromStep, 120); + expect(replay.toStep, 300); + + final fresh = _run(); + _steps(fresh, 0, replay.fromStep); + fresh.walker.rule = _new; + _steps(fresh, replay.fromStep, 300); + expect(live.walker.x, fresh.walker.x); + + final divergence = replay.divergence!; + expect(divergence.path, 'x'); + // The keyframes are 60 steps apart: agreed at 180, parted by 240. + expect(divergence.agreedAt, 180); + expect(divergence.step, 240); + expect( + divergence.after! as double, + greaterThan(divergence.before! as double), + ); + expect(live.timeline.history.last, const TimelineReplayed(120)); + }); + + test('code that changes nothing here says so', () { + final live = _run(); + _steps(live, 0, 300); + final x = live.walker.x; + live.walker.rule = (x, push) => push * 0.05 + x; + + final replay = live.timeline.replayUnderNewCode( + seconds: 3.0, + capture: live.walker.save, + )!; + + expect(replay.divergence, isNull); + expect(live.walker.x, x); + }); + + test('the old code\'s keyframes are not rewound into afterwards', () { + final live = _run(); + _steps(live, 0, 300); + live.walker.rule = _new; + live.timeline.replayUnderNewCode(seconds: 3.0, capture: live.walker.save); + + expect(live.rewind.keyframesAfter(120), isEmpty); + expect(live.rewind.rewindTo(100), isNull); + }); + + test('a buffer that does not reach back replays nothing', () { + final live = _run(); + expect( + live.timeline.replayUnderNewCode(seconds: 3.0, capture: live.walker.save), + isNull, + ); + }); +} diff --git a/packages/flutter3d_net/CHANGELOG.md b/packages/flutter3d_net/CHANGELOG.md index f5c2d7a69..bec21056e 100644 --- a/packages/flutter3d_net/CHANGELOG.md +++ b/packages/flutter3d_net/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +- `firstDifferingPath` lives in `flutter3d_sim` now; this package exports + it from there, so imports of it keep working. + + ## 0.8.1 **Any transport here carries `flame_multiplayer`.** `NetTransportWire` makes diff --git a/packages/flutter3d_net/lib/flutter3d_net.dart b/packages/flutter3d_net/lib/flutter3d_net.dart index 5978bf876..41f7cf595 100644 --- a/packages/flutter3d_net/lib/flutter3d_net.dart +++ b/packages/flutter3d_net/lib/flutter3d_net.dart @@ -15,6 +15,8 @@ /// against a mock that cannot lie about timing. library; +export 'package:flutter3d_sim/flutter3d_sim.dart' show firstDifferingPath; + export 'src/loopback_transport.dart'; export 'src/net_session.dart'; export 'src/net_transport.dart'; diff --git a/packages/flutter3d_net/lib/src/snapshot_divergence.dart b/packages/flutter3d_net/lib/src/snapshot_divergence.dart index 73b4840b1..368d6313c 100644 --- a/packages/flutter3d_net/lib/src/snapshot_divergence.dart +++ b/packages/flutter3d_net/lib/src/snapshot_divergence.dart @@ -79,43 +79,3 @@ SnapshotDivergence? diffRuns({ found: path.b, ); } - -/// A path into JSON-shaped values ([Map], [List], or a primitive) leading -/// to the first leaf where [a] and [b] differ, or null if they are equal — -/// exported so a caller that already has two full snapshots and already -/// knows which step to compare (a golden test disagreeing on one recorded -/// step, say) can ask this question directly, without going through -/// [diffRuns]'s digest-narrowing at all. -({String path, Object? a, Object? b})? firstDifferingPath( - Object? a, - Object? b, [ - String prefix = '', -]) { - if (a is Map && b is Map) { - final keys = {...a.keys, ...b.keys}.toList() - ..sort((x, y) => x.toString().compareTo(y.toString())); - for (final key in keys) { - final childPrefix = prefix.isEmpty ? '$key' : '$prefix.$key'; - final result = firstDifferingPath(a[key], b[key], childPrefix); - if (result != null) return result; - } - return null; - } - if (a is List && b is List) { - final shorter = a.length < b.length ? a.length : b.length; - for (var i = 0; i < shorter; i++) { - final result = firstDifferingPath(a[i], b[i], '$prefix[$i]'); - if (result != null) return result; - } - if (a.length != b.length) { - return ( - path: prefix.isEmpty ? '' : '$prefix.', - a: a.length, - b: b.length, - ); - } - return null; - } - if (a != b) return (path: prefix, a: a, b: b); - return null; -} diff --git a/packages/flutter3d_sim/CHANGELOG.md b/packages/flutter3d_sim/CHANGELOG.md index e60e2fa5b..757f2f0ce 100644 --- a/packages/flutter3d_sim/CHANGELOG.md +++ b/packages/flutter3d_sim/CHANGELOG.md @@ -1,5 +1,14 @@ ## Unreleased +- **A number tuned while the game runs is on the tape.** `InputState.tune` + sets a tunable for one step, `InputFrame.tunes` records it, playback + applies it, and `Tunables` is the step's side: named values with defaults, + taken from the input before the step reads them, saved into a snapshot so + a rewind comes back with the old value. A run tuned as it was played + replays like any other. +- **`firstDifferingPath` moved here from `flutter3d_net`**, which still + exports it. **`RewindBuffer.keyframesAfter`** reads the snapshots held. + - **`diffLevel` says who has to act on an edit.** It compares two versions of a level part by part and splits the change: lights, materials, fog and music can be patched into a running scene; brushes, entities, the ground, diff --git a/packages/flutter3d_sim/lib/flutter3d_sim.dart b/packages/flutter3d_sim/lib/flutter3d_sim.dart index 2f5b45ea6..d73f3dfc7 100644 --- a/packages/flutter3d_sim/lib/flutter3d_sim.dart +++ b/packages/flutter3d_sim/lib/flutter3d_sim.dart @@ -56,6 +56,7 @@ export 'src/ecs/entity_remap.dart'; export 'src/input/game_action.dart'; export 'src/input/input_state.dart'; export 'src/input/input_tape.dart'; +export 'src/input/tunables.dart'; export 'src/level/breaches.dart'; export 'src/level/brush_geometry.dart'; export 'src/level/data_source.dart'; @@ -97,6 +98,7 @@ export 'src/nav/navigation.dart'; export 'src/physics/layers.dart'; export 'src/save/data_source_trace.dart'; export 'src/save/demo.dart'; +export 'src/save/first_differing_path.dart'; export 'src/save/game_random.dart'; export 'src/save/replay.dart'; export 'src/save/rewind.dart'; diff --git a/packages/flutter3d_sim/lib/src/input/input_state.dart b/packages/flutter3d_sim/lib/src/input/input_state.dart index 0fd928dd0..856a8ca4e 100644 --- a/packages/flutter3d_sim/lib/src/input/input_state.dart +++ b/packages/flutter3d_sim/lib/src/input/input_state.dart @@ -54,6 +54,7 @@ final class InputState { final Vector2 _lookDelta = Vector2.zero(); int? _slotRequest; + final Map _tunes = {}; // MARK: - Reading, from inside a step @@ -137,6 +138,10 @@ final class InputState { /// arriving at the one the player pressed most recently is what they meant. int? get slotRequest => _slotRequest; + /// The tunables set for this step, by name — see [tune]. + Map get tunesThisStep => + Map.unmodifiable(_tunes); + // MARK: - Writing, from a device /// Whether the devices are ignored. @@ -309,6 +314,22 @@ final class InputState { _slotRequest = slot; } + /// Sets the tunable called [name] to [value] from the next step on. + /// + /// **Through the input, because the tape is where a run is reproduced + /// from.** A jump height dragged in an inspector while the game runs + /// changes what the simulation does; set on the simulation directly, it + /// would change the run and leave the tape describing a run with the old + /// height, and every replay of it would disagree with what was played. As + /// an input it is recorded with the step that took it, replayed with that + /// step, and muted with the devices during a replay — so a drag arriving + /// while the timeline replays does not land in the past. `Tunables.take` + /// is the step's side of it. + void tune(String name, double value) { + if (muted) return; + _tunes[name] = value; + } + /// Drops everything, held state included. /// /// For losing focus. A key that was down when the window went away never @@ -324,6 +345,7 @@ final class InputState { _moveAxis.setZero(); _values.clear(); _slotRequest = null; + _tunes.clear(); } // MARK: - Step boundaries @@ -341,6 +363,7 @@ final class InputState { _releasedLatch.clear(); _lookDelta.setZero(); _slotRequest = null; + _tunes.clear(); } void _recomputeMoveAxis() { diff --git a/packages/flutter3d_sim/lib/src/input/input_tape.dart b/packages/flutter3d_sim/lib/src/input/input_tape.dart index 4b57f7ce5..341b84d9d 100644 --- a/packages/flutter3d_sim/lib/src/input/input_tape.dart +++ b/packages/flutter3d_sim/lib/src/input/input_tape.dart @@ -23,6 +23,7 @@ final class InputFrame { this.lookY = 0.0, this.values = const {}, this.slot, + this.tunes = const {}, }); factory InputFrame.fromJson(Map json) => InputFrame( @@ -47,6 +48,13 @@ final class InputFrame { entry.key! as String: (entry.value! as num).toDouble(), }, slot: (json['slot'] as num?)?.toInt(), + tunes: { + for (final entry + in (json['tunes'] as Map? ?? + const {}) + .entries) + entry.key! as String: (entry.value! as num).toDouble(), + }, ); /// Action names rather than the actions themselves. @@ -73,12 +81,16 @@ final class InputFrame { /// release, so a tape of transitions had nowhere to put it. final int? slot; + /// Tunables set by this step, by name — `InputState.tune`. + final Map tunes; + /// Whether this step is worth writing down at all. bool get isIdle => pressed.isEmpty && released.isEmpty && values.isEmpty && slot == null && + tunes.isEmpty && stickX == 0.0 && stickY == 0.0 && lookX == 0.0 && @@ -93,6 +105,7 @@ final class InputFrame { if (lookY != 0.0) 'ly': lookY, if (values.isNotEmpty) 'values': values, if (slot != null) 'slot': slot, + if (tunes.isNotEmpty) 'tunes': tunes, }; } @@ -187,6 +200,7 @@ final class InputTapeRecorder { entry.key.name: entry.value, }, slot: input.slotRequest, + tunes: input.tunesThisStep, ), ); } @@ -242,5 +256,8 @@ final class InputTapePlayback { } final slot = frame.slot; if (slot != null) input.requestSlot(slot); + for (final MapEntry(key: name, :value) in frame.tunes.entries) { + input.tune(name, value); + } } } diff --git a/packages/flutter3d_sim/lib/src/input/tunables.dart b/packages/flutter3d_sim/lib/src/input/tunables.dart new file mode 100644 index 000000000..10357092d --- /dev/null +++ b/packages/flutter3d_sim/lib/src/input/tunables.dart @@ -0,0 +1,66 @@ +import 'input_state.dart'; + +/// The numbers a game lets somebody change while it runs — a jump height, a +/// gravity, an enemy's reaction time — each with the value it starts at. +/// +/// **Changed through the input, read by the step.** Somebody drags a value in +/// an inspector; the editor calls `ext.flutter3d.cvar.set`, which calls +/// `InputState.tune`; the next step calls [take] before it simulates and +/// reads the new value through `[]`. The change is on the tape with that step, +/// so a replay changes it at the same moment, and a run tuned while it was +/// played reproduces like any other. +/// +/// **Part of the state, so part of the snapshot.** A rewind to before a +/// change has to come back with the old value, or the replay from there runs +/// with a value it did not have. Put [toJson] into the game's snapshot and +/// hand it back to [restore]. +/// +/// A name the table does not declare is ignored, not added: a stale +/// inspector naming a tunable an edit has since removed should not grow the +/// state of every run it is sent to. +final class Tunables { + Tunables(Map defaults) + : defaults = Map.unmodifiable(defaults), + _values = Map.of(defaults); + + /// What each tunable starts at. + final Map defaults; + + final Map _values; + + /// The value of [name] now. Throws for a name the table does not declare, + /// which is a typo in the step rather than something to default past. + double operator [](String name) => + _values[name] ?? + (throw ArgumentError.value(name, 'name', 'no such tunable')); + + /// Every tunable and its value now. + Map get values => Map.unmodifiable(_values); + + /// Takes the tunables [input] set this step. Call once per step, before + /// anything reads a value, the same place a step reads the rest of its + /// input. + void take(InputState input) { + for (final MapEntry(key: name, :value) in input.tunesThisStep.entries) { + if (_values.containsKey(name)) _values[name] = value; + } + } + + /// The values that differ from [defaults], for a snapshot: a run nobody + /// tuned adds nothing to its saves. + Map toJson() => { + for (final MapEntry(key: name, :value) in _values.entries) + if (value != defaults[name]) name: value, + }; + + /// Puts back what [toJson] wrote; anything it does not name returns to its + /// default. + void restore(Map json) { + for (final MapEntry(key: name, value: fallback) in defaults.entries) { + _values[name] = switch (json[name]) { + final num saved => saved.toDouble(), + _ => fallback, + }; + } + } +} diff --git a/packages/flutter3d_sim/lib/src/save/first_differing_path.dart b/packages/flutter3d_sim/lib/src/save/first_differing_path.dart new file mode 100644 index 000000000..b2b7ce40f --- /dev/null +++ b/packages/flutter3d_sim/lib/src/save/first_differing_path.dart @@ -0,0 +1,41 @@ +/// A path into JSON-shaped values ([Map], [List], or a primitive) leading +/// to the first leaf where [a] and [b] differ, or null if they are equal. +/// +/// Here rather than in `flutter3d_net`, where it was written for +/// `diffRuns`: the timeline asks the same question after a code reload — +/// where does the run the new code makes first part from the one the old +/// code made — and the timeline does not depend on networking. `flutter3d_net` +/// exports it from here, so nothing that reached it there has moved. +({String path, Object? a, Object? b})? firstDifferingPath( + Object? a, + Object? b, [ + String prefix = '', +]) { + if (a is Map && b is Map) { + final keys = {...a.keys, ...b.keys}.toList() + ..sort((x, y) => x.toString().compareTo(y.toString())); + for (final key in keys) { + final childPrefix = prefix.isEmpty ? '$key' : '$prefix.$key'; + final result = firstDifferingPath(a[key], b[key], childPrefix); + if (result != null) return result; + } + return null; + } + if (a is List && b is List) { + final shorter = a.length < b.length ? a.length : b.length; + for (var i = 0; i < shorter; i++) { + final result = firstDifferingPath(a[i], b[i], '$prefix[$i]'); + if (result != null) return result; + } + if (a.length != b.length) { + return ( + path: prefix.isEmpty ? '' : '$prefix.', + a: a.length, + b: b.length, + ); + } + return null; + } + if (a != b) return (path: prefix, a: a, b: b); + return null; +} diff --git a/packages/flutter3d_sim/lib/src/save/rewind.dart b/packages/flutter3d_sim/lib/src/save/rewind.dart index e840ea60c..2e1322add 100644 --- a/packages/flutter3d_sim/lib/src/save/rewind.dart +++ b/packages/flutter3d_sim/lib/src/save/rewind.dart @@ -180,6 +180,16 @@ final class RewindBuffer { _keyframes.removeWhere((k) => k.step > point.step); } + /// The snapshots held after [step], by the step each was taken before. + /// + /// What the run looked like at those moments — for comparing a replay of + /// the same tape against, after something that should not have changed + /// the outcome (or should have) has changed the code. + Map keyframesAfter(int step) => { + for (final keyframe in _keyframes) + if (keyframe.step > step) keyframe.step: keyframe.snapshot, + }; + /// Makes [point]'s keyframe the oldest thing held: the keyframes on either /// side of it and the entries before it are forgotten, the entries after it /// kept. diff --git a/packages/flutter3d_sim/test/tunables_test.dart b/packages/flutter3d_sim/test/tunables_test.dart new file mode 100644 index 000000000..1f03db549 --- /dev/null +++ b/packages/flutter3d_sim/test/tunables_test.dart @@ -0,0 +1,106 @@ +/// `HR4`: a number changed while the game runs is an input, so a run tuned as +/// it was played replays like any other. +/// +/// flutter test test/tunables_test.dart +/// +/// Mutation: drop `tunes:` from the recorder and the replay runs at the old +/// speed; skip `tunes` in `InputFrame.toJson` and the saved tape forgets it. +library; + +import 'dart:convert'; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; + +/// A walker whose speed is a tunable. +final class _Walker { + final Tunables tunables = Tunables(const {'speed': 1.0}); + double x = 0.0; + + void step(InputState input) { + tunables.take(input); + x += tunables['speed'] * 0.1; + } +} + +/// Plays [steps] steps, tuning the speed to 3 at step 40, and returns the +/// walker and the tape. +({_Walker walker, InputTape tape}) _played(int steps) { + final walker = _Walker(); + final input = InputState(); + final recorder = InputTapeRecorder(seed: 1); + for (var step = 0; step < steps; step++) { + if (step == 40) input.tune('speed', 3.0); + recorder.record(input); + input.beginStep(); + walker.step(input); + input.endStep(); + } + return (walker: walker, tape: recorder.tape); +} + +double _replayed(InputTape tape) { + final walker = _Walker(); + final input = InputState(); + final playback = InputTapePlayback(tape); + while (!playback.isFinished) { + playback.applyTo(input); + input.beginStep(); + walker.step(input); + input.endStep(); + } + return walker.x; +} + +void main() { + test('a tape with a tunable changed half way reproduces the run', () { + final run = _played(100); + // 40 steps at 1, 60 at 3. + expect(run.walker.x, closeTo(4.0 + 18.0, 1e-9)); + expect(_replayed(run.tape), run.walker.x); + }); + + test('and still does after the tape has been saved and read back', () { + final run = _played(100); + final saved = InputTape.fromJson( + jsonDecode(jsonEncode(run.tape.toJson())) as Map, + ); + expect(saved.frames[40].tunes, {'speed': 3.0}); + expect(saved.frames[40].isIdle, isFalse); + expect(_replayed(saved), run.walker.x); + }); + + test('a tune lasts one step on the input and for good on the table', () { + final walker = _Walker(); + final input = InputState()..tune('speed', 2.0); + walker.step(input); + input.endStep(); + expect(input.tunesThisStep, isEmpty); + walker.step(input); + expect(walker.x, closeTo(0.4, 1e-9)); + }); + + test('a muted input takes no tune, as it takes no key', () { + final input = InputState()..muted = true; + input.tune('speed', 9.0); + expect(input.tunesThisStep, isEmpty); + }); + + test('a name the table does not declare is ignored', () { + final tunables = Tunables(const {'speed': 1.0}); + tunables.take(InputState()..tune('gravity', 5.0)); + expect(tunables.values.keys, ['speed']); + expect(() => tunables['gravity'], throwsArgumentError); + }); + + test('a snapshot holds what was tuned, and a restore puts it back', () { + final tunables = Tunables(const {'speed': 1.0, 'jump': 2.0}) + ..take(InputState()..tune('speed', 4.0)); + final saved = tunables.toJson(); + expect(saved, {'speed': 4.0}); + + tunables.take(InputState()..tune('jump', 7.0)); + tunables.restore(saved); + expect(tunables.values, {'speed': 4.0, 'jump': 2.0}); + }); +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 5377724db..a6eb6c64f 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10828 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10839 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index ca3766860..aa82fb9fb 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,17 +1,17 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10828 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10839 tests need a GPU. --- # Testing -10828 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10839 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| | `flutter3d` | 1620 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | -| `flutter3d_sim` | 585 | | `pad_input` | 67 | +| `flutter3d_sim` | 591 | | `pad_input` | 67 | | `flutter3d_lab` | 15 | | `flutter3d_core` | 735 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | @@ -27,7 +27,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `apps/flutter3d_demo_river` | 53 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 90 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 127 | -| `flutter3d_game` | 267 | | `flutter3d_shaders` | 5 | +| `flutter3d_game` | 272 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | | | | | `flutter3d_net` | 15 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10802 rather than 10828: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10813 rather than 10839: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. From ff151e44e6d65ccce188bc0c43d7526dc072a265 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 01:07:19 +0300 Subject: [PATCH 010/141] A material dragged in the inspector changes the next frame HotSwap.setMaterial, and ext.flutter3d.material.set over the VM service, sets colour, emissive, roughness, metallic, normal scale or alpha cutoff on every material of that name in the scenes SceneSurface registers. The values stay as overrides and are put back after every model swap, so a colour still being dragged does not snap back when the model is saved. --- ARCHITECTURE.md | 2 +- README.md | 2 +- packages/flutter3d_app/CHANGELOG.md | 7 + .../lib/src/hot_swap/hot_swap.dart | 162 +++++++++++++++++- .../lib/src/surface/scene_surface.dart | 7 +- .../flutter3d_app/test/hot_swap_test.dart | 77 ++++++++- site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- tasks/0.9-engine-roadmap.md | 28 +-- 9 files changed, 275 insertions(+), 20 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 23ec0478d..027327e6c 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2688,7 +2688,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10839 tests** across 41 packages and 10 applications | +| Unit tests | **10842 tests** across 41 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index d486d56a9..788c3e023 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10839 tests across forty-one packages and nine applications. The +There are 10842 tests across forty-one packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/packages/flutter3d_app/CHANGELOG.md b/packages/flutter3d_app/CHANGELOG.md index 6b7dfd8ed..3d8d9f215 100644 --- a/packages/flutter3d_app/CHANGELOG.md +++ b/packages/flutter3d_app/CHANGELOG.md @@ -20,6 +20,13 @@ transforms and animation. `ext.flutter3d.assets.put` sends a model's bytes over the VM service instead, for a device whose disk the editor cannot write. A model that does not build keeps the version that did. +**A material dragged in an inspector changes the next frame.** +`HotSwap.setMaterial` (`ext.flutter3d.material.set` over the VM service) +sets base colour, emissive, roughness, metallic, normal scale or alpha cutoff +on every material of that name in the scenes registered — `SceneSurface` +registers its own — and keeps them as overrides that a model swap does not +undo. `clearMaterial` lets them go. + `SceneSurface` is a `StatefulWidget` now, for `reassemble`. Its constructor and parameters are unchanged. diff --git a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart index 2fdd5769a..fe2f62dd4 100644 --- a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart +++ b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart @@ -67,6 +67,9 @@ final class HotSwap { final List> _renderers = >[]; final List<_Library> _libraries = <_Library>[]; final List _models = []; + final List> _scenes = >[]; + final Map> _overrides = + >{}; /// Counts the reloads that finished, for a host that draws only on demand /// and has to draw once more to show the new shaders. @@ -215,11 +218,140 @@ final class HotSwap { Future put(String path, Uint8List bytes) async { if (!enabled) return null; for (final model in _models) { - if (model.path == path) return model._adopt(bytes); + if (model.path == path) { + final report = await model._adopt(bytes); + _applyOverrides(); + return report; + } } return null; } + /// Remembers [scene] until it is garbage, for [setMaterial] to find + /// materials in. `SceneSurface` registers the scene it draws. + void registerScene(Scene scene) { + if (!enabled) return; + _scenes.removeWhere((WeakReference s) => s.target == null); + if (_scenes.any((WeakReference s) => identical(s.target, scene))) { + return; + } + _scenes.add(WeakReference(scene)); + _registerExtension(); + } + + /// The fields [setMaterial] understands, and how many numbers each takes. + static const Map materialFields = { + 'baseColor': 4, + 'emissive': 3, + 'emissiveStrength': 1, + 'roughness': 1, + 'metallic': 1, + 'normalScale': 1, + 'alphaCutoff': 1, + }; + + /// Sets [fields] on every material called [name] in the registered + /// scenes, from the next frame on, and keeps them as overrides. + /// + /// **For an inspector dragging a slider.** Nothing is saved and nothing is + /// reloaded; the material the frame is drawn with changes. What the + /// simulation reads is not a material's business, so nothing here goes + /// near the tape — a tunable is the door for that. + /// + /// **The overrides outlive the file.** A model swapped after the drag + /// brings its materials as the file has them, which is the value before + /// the drag; every swap puts the overrides back on, so the colour somebody + /// is still dragging does not snap back each time the model is saved. + /// [clearMaterial] drops them. + /// + /// [fields] maps a name from [materialFields] to a number or a list of + /// that many numbers. Throws [ArgumentError] naming the field otherwise. + /// Returns how many materials took the change. + int setMaterial(String name, Map fields) { + if (!enabled) return 0; + for (final MapEntry(key: field, :value) in fields.entries) { + _numbers(field, value); + } + (_overrides[name] ??= {}).addAll(fields); + return _applyOverrides(only: name); + } + + /// Forgets the overrides for [name], or for every material when null. The + /// materials keep what they were set to until their file is loaded again. + void clearMaterial([String? name]) { + if (name == null) { + _overrides.clear(); + } else { + _overrides.remove(name); + } + } + + int _applyOverrides({String? only}) { + _scenes.removeWhere((WeakReference s) => s.target == null); + final seen = Set.identity(); + for (final scene in _scenes) { + scene.target?.root.traverse((SceneNode node) { + if (node is! MeshNode) return; + final material = node.material; + final name = material.name; + if (name == null || (only != null && name != only)) return; + final fields = _overrides[name]; + if (fields == null || !seen.add(material)) return; + for (final MapEntry(key: field, :value) in fields.entries) { + _write(material, field, _numbers(field, value)); + } + }); + } + return seen.length; + } + + static List _numbers(String field, Object? value) { + final count = materialFields[field]; + if (count == null) { + throw ArgumentError.value( + field, + 'field', + 'not a material field; there are ${materialFields.keys.join(', ')}', + ); + } + final numbers = switch (value) { + final num one => [one.toDouble()], + final List many => [ + for (final item in many) + if (item is num) item.toDouble(), + ], + _ => const [], + }; + if (numbers.length != count || + (value is List && value.length != count)) { + throw ArgumentError.value( + value, + field, + 'takes $count number${count == 1 ? '' : 's'}', + ); + } + return numbers; + } + + static void _write(Material material, String field, List v) { + switch (field) { + case 'baseColor': + material.baseColor.setValues(v[0], v[1], v[2], v[3]); + case 'emissive': + material.emissive.setValues(v[0], v[1], v[2]); + case 'emissiveStrength': + material.emissiveStrength = v[0]; + case 'roughness': + material.roughness = v[0]; + case 'metallic': + material.metallic = v[0]; + case 'normalScale': + material.normalScale = v[0]; + case 'alphaCutoff': + material.alphaCutoff = v[0]; + } + } + /// Refreshes the registered bundles whose bytes changed, then relinks every /// live renderer. /// @@ -280,6 +412,7 @@ final class HotSwap { models.add(report); } } + if (models.isNotEmpty) _applyOverrides(); _renderers.removeWhere((WeakReference r) => r.target == null); for (final renderer in _renderers) { renderer.target?.relinkShaders(); @@ -309,6 +442,33 @@ final class HotSwap { jsonEncode(report.toJson()), ); }); + developer.registerExtension('ext.flutter3d.material.set', ( + String method, + Map parameters, + ) async { + final name = parameters['name']; + final fields = parameters['fields']; + if (name == null || fields == null) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + 'material.set takes a name and its fields as JSON', + ); + } + try { + final touched = instance.setMaterial( + name, + jsonDecode(fields) as Map, + ); + return developer.ServiceExtensionResponse.result( + jsonEncode({'materials': touched}), + ); + } on Object catch (error) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + '$error', + ); + } + }); developer.registerExtension('ext.flutter3d.assets.put', ( String method, Map parameters, diff --git a/packages/flutter3d_app/lib/src/surface/scene_surface.dart b/packages/flutter3d_app/lib/src/surface/scene_surface.dart index b35ae2806..ae1fa8042 100644 --- a/packages/flutter3d_app/lib/src/surface/scene_surface.dart +++ b/packages/flutter3d_app/lib/src/surface/scene_surface.dart @@ -102,7 +102,9 @@ class _SceneSurfaceState extends State { @override void initState() { super.initState(); - HotSwap.instance.registerRenderer(widget.renderer); + HotSwap.instance + ..registerRenderer(widget.renderer) + ..registerScene(widget.scene); } @override @@ -111,6 +113,9 @@ class _SceneSurfaceState extends State { if (!identical(oldWidget.renderer, widget.renderer)) { HotSwap.instance.registerRenderer(widget.renderer); } + if (!identical(oldWidget.scene, widget.scene)) { + HotSwap.instance.registerScene(widget.scene); + } } @override diff --git a/packages/flutter3d_app/test/hot_swap_test.dart b/packages/flutter3d_app/test/hot_swap_test.dart index 64d7e2db6..0c3a83f91 100644 --- a/packages/flutter3d_app/test/hot_swap_test.dart +++ b/packages/flutter3d_app/test/hot_swap_test.dart @@ -101,7 +101,7 @@ Future _model(GraphicsDevice device, Uint8List bytes) { ), ], materials: [ - SurfaceMaterial(baseColor: colour, unlit: true), + SurfaceMaterial(baseColor: colour, unlit: true, name: 'paint'), ], nodes: [ ModelNode(name: 'hull', surfaces: [0]), @@ -304,4 +304,79 @@ void main() { expect(ship.meshes.single.material.baseColor.x, 1.0); }); }); + + group('a material', () { + late CpuDevice device; + late HotSwap swap; + + setUp(() { + device = CpuDevice( + width: 16, + height: 9, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + swap = HotSwap(enabled: true); + }); + + test('is set in every scene registered, once however many nodes wear ' + 'it', () async { + final asset = await _model(device, Uint8List.fromList([1])); + final scene = Scene(); + final a = asset.instantiate(scene); + asset.instantiate(scene); + swap.registerScene(scene); + + final touched = swap.setMaterial('paint', { + 'baseColor': [0.0, 0.5, 1.0, 1.0], + 'roughness': 0.25, + }); + + expect(touched, 1, reason: 'the two instances share it'); + final material = a.meshes.single.material; + expect(material.baseColor.z, 1.0); + expect(material.roughness, 0.25); + expect(swap.setMaterial('chrome', {'metallic': 1}), 0); + }); + + test('refuses a field it does not know or a value of the wrong size', () { + expect( + () => swap.setMaterial('paint', {'shine': 1}), + throwsArgumentError, + ); + expect( + () => swap.setMaterial('paint', { + 'baseColor': [1, 0, 0], + }), + throwsArgumentError, + ); + }); + + test('keeps what was dragged when the model is swapped under it', () async { + var file = Uint8List.fromList([1]); + final model = swap.registerModel( + 'assets_src/ship.glb', + await _model(device, file), + read: () async => file, + build: (Uint8List bytes) => _model(device, bytes), + loadedFrom: file, + ); + final scene = Scene(); + final ship = model.instantiate(scene); + swap + ..registerScene(scene) + ..setMaterial('paint', {'roughness': 0.1}); + + file = Uint8List.fromList([3]); + await swap.swap(); + + final material = ship.meshes.single.material; + expect(material.baseColor.z, 1.0, reason: 'the file\'s new colour'); + expect(material.roughness, 0.1, reason: 'and the drag, kept'); + + swap.clearMaterial('paint'); + file = Uint8List.fromList([2]); + await swap.swap(); + expect(ship.meshes.single.material.roughness, isNot(0.1)); + }); + }); } diff --git a/site/content/quickstart.md b/site/content/quickstart.md index a6eb6c64f..33d6f68ba 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10839 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10842 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index aa82fb9fb..69d44dda3 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10839 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10842 tests need a GPU. --- # Testing -10839 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10842 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -26,7 +26,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 28 | | `apps/flutter3d_demo_river` | 53 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 90 | -| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 127 | +| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 130 | | `flutter3d_game` | 272 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10813 rather than 10839: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10816 rather than 10842: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index fde91767a..310b690fd 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -161,16 +161,24 @@ segment so a saved `.f3drun` replays it (the history has it, the file format does not yet); the editor MCP driving the same path; a demo game wired to `LiveLevel` so the loop is visible in one of ours. -**HR4. Live parameters and replay after a reload (S–M).** Inspector drags send -`ext.flutter3d.material.set` and `ext.flutter3d.cvar.set` at about 30 Hz, -applied next frame without a save; runtime overrides survive later file -reloads. Tunables that affect the simulation are recorded into the input tape -as commands. **Replay after reload:** with a `RunTimeline` attached, a code -reload can rewind N seconds and re-simulate to now with the same input and the -new code, and report where the new run diverges from the old -(`SnapshotDivergence`). flutter_scene has no simulation layer to offer this. -*Tests:* a tape with cvar commands reproduces; swapping an injected step -function reproduces the new outcome and names the first differing step. +**HR4. Live parameters and replay under new code (S–M).** *Done:* +`ext.flutter3d.material.set {name, fields}` sets base colour, emissive, +roughness, metallic, normal scale or alpha cutoff on every material of that +name in the scenes `SceneSurface` registers, next frame, without a save; the +values are kept as overrides and put back after every model swap, and +`clearMaterial` drops them. `ext.flutter3d.cvar.set {name, value}` goes +through `InputState.tune`, so a tunable changed mid-run is on the tape +(`InputFrame.tunes`), taken by `Tunables` before the step reads it and kept +in the snapshot; `cvar.list` answers values and defaults. +`RunTimeline.replayUnderNewCode` (over the wire, +`ext.flutter3d.timeline.replayUnderNewCode {seconds}`) lives the last seconds +again from the nearest keyframe under the code running now and names where +that run first parts from the old one: the field, and the two keyframes it +parted between, since the old run is only held at keyframes. +(`reload` stays out of the names: CONTRIBUTING.md keeps it for weapons.) +*Left:* the inspector sending at 30 Hz (the editor has the transport, not the +sliders), and calling the replay from `reassemble` on its own rather than +from a tool. **HR5. Editor Play (M), with §6.** *Done on desktop:* the toolbar's play button finds the project above the open level (the nearest `pubspec.yaml`) From 8f03266cb6009aee66b04e5d1f5eb6ed71d1d637 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 01:18:06 +0300 Subject: [PATCH 011/141] The river's land reaches the sky, and its gunners are seen firing The grass was drawn only 46 units either side of the river's middle, while the stretches ahead reach a few hundred units up it; on a wide window the sky showed in both top corners. The outer quads of each row, and the roads to the bridges, now run to the camera's far plane. Trees keep to the old band, so the course is laid out as before. A helicopter's shot was a cube the size of an explosion shard and came once per pass: it is now a red streak along its flight with a flash at the nose, and a gunner fires from 46 units ahead every 1.1 seconds, which is three shots at cruise rather than one. More tankers and helicopters move once woken, from 55% on the first level to 90% on the last; the rest still hold still. --- apps/flutter3d_demo_river/lib/src/course.dart | 10 +++++-- apps/flutter3d_demo_river/lib/src/craft.dart | 2 ++ apps/flutter3d_demo_river/lib/src/levels.dart | 12 ++++---- apps/flutter3d_demo_river/lib/src/models.dart | 17 +++++++---- apps/flutter3d_demo_river/lib/src/pieces.dart | 15 ++++++++-- .../lib/src/river_game.dart | 29 ++++++++++++++++--- 6 files changed, 64 insertions(+), 21 deletions(-) diff --git a/apps/flutter3d_demo_river/lib/src/course.dart b/apps/flutter3d_demo_river/lib/src/course.dart index 7cb00f80a..b5b4cf123 100644 --- a/apps/flutter3d_demo_river/lib/src/course.dart +++ b/apps/flutter3d_demo_river/lib/src/course.dart @@ -23,10 +23,16 @@ const double sectionLength = 180.0; /// How far either side of the valley's middle the water may ever reach. const double riverReach = 17.0; -/// How far either side the land is drawn: past what the camera sees at the -/// far end of the view, so the valley has no edge on screen. +/// How far either side of the middle the trees and houses stand. const double valleyReach = 46.0; +/// How far either side the grass is drawn: as far as the camera sees at +/// all. The stretches ahead reach a few hundred units up the river, and a +/// wide window sees about as far across up there, so land that stopped at +/// [valleyReach] left the sky showing in both top corners. Only the outer +/// quads of each row get wider; the valley has no more vertices for it. +const double landReach = 400.0; + /// Half the water's width under a bridge, and where each stretch starts. const double narrowHalf = 4.5; diff --git a/apps/flutter3d_demo_river/lib/src/craft.dart b/apps/flutter3d_demo_river/lib/src/craft.dart index bc86ff743..52e1bcfb2 100644 --- a/apps/flutter3d_demo_river/lib/src/craft.dart +++ b/apps/flutter3d_demo_river/lib/src/craft.dart @@ -56,6 +56,7 @@ final class _Kit { rotor = DeviceMesh.upload(device, rotorMesh()), depot = DeviceMesh.upload(device, depotMesh()), shot = DeviceMesh.upload(device, shotMesh()), + bullet = DeviceMesh.upload(device, bulletMesh()), shard = DeviceMesh.upload(device, shardMesh()), puff = DeviceMesh.upload(device, puffMesh()), water = DeviceMesh.upload(device, waterMesh()); @@ -68,6 +69,7 @@ final class _Kit { final DeviceMesh rotor; final DeviceMesh depot; final DeviceMesh shot; + final DeviceMesh bullet; final DeviceMesh shard; final DeviceMesh puff; final DeviceMesh water; diff --git a/apps/flutter3d_demo_river/lib/src/levels.dart b/apps/flutter3d_demo_river/lib/src/levels.dart index dd8fc4066..d0bbdd9a8 100644 --- a/apps/flutter3d_demo_river/lib/src/levels.dart +++ b/apps/flutter3d_demo_river/lib/src/levels.dart @@ -102,7 +102,7 @@ const List campaign = [ tanker: 0.45, helicopter: 0.2, depot: 0.3, - moving: 0.35, + moving: 0.55, speed: 0.8, islands: 0.25, narrowest: 7.0, @@ -114,7 +114,7 @@ const List campaign = [ briefing: 'Sink six tankers before the last bridge.', bridges: 3, task: {TargetKind.tanker: 6}, - mix: Mix(tanker: 0.55, helicopter: 0.15, depot: 0.25, moving: 0.6), + mix: Mix(tanker: 0.55, helicopter: 0.15, depot: 0.25, moving: 0.7), ), Level( name: 'Rotor Alley', @@ -126,7 +126,7 @@ const List campaign = [ helicopter: 0.45, depot: 0.22, jet: 0.08, - moving: 0.65, + moving: 0.75, speed: 1.1, gunners: 0.4, ), @@ -141,7 +141,7 @@ const List campaign = [ helicopter: 0.3, depot: 0.2, jet: 0.18, - moving: 0.7, + moving: 0.8, speed: 1.2, gunners: 0.5, islands: 0.45, @@ -159,7 +159,7 @@ const List campaign = [ helicopter: 0.32, depot: 0.12, jet: 0.14, - moving: 0.75, + moving: 0.85, speed: 1.3, gunners: 0.6, islands: 0.45, @@ -176,7 +176,7 @@ const List campaign = [ helicopter: 0.33, depot: 0.14, jet: 0.16, - moving: 0.8, + moving: 0.9, speed: 1.4, gunners: 0.7, islands: 0.5, diff --git a/apps/flutter3d_demo_river/lib/src/models.dart b/apps/flutter3d_demo_river/lib/src/models.dart index db8b8e2f2..21e1a64f4 100644 --- a/apps/flutter3d_demo_river/lib/src/models.dart +++ b/apps/flutter3d_demo_river/lib/src/models.dart @@ -80,7 +80,7 @@ MeshData valleyMesh(Section section) { final grown = row.islandGrown; final crest = bedDepth + (landHeight - bedDepth) * grown; return [ - p(-valleyReach, landHeight), + p(-landReach, landHeight), p(row.left - bankTop, landHeight), p(row.left + bankUnder, bedDepth), p(row.islandLeft - bankUnder * grown, bedDepth), @@ -89,7 +89,7 @@ MeshData valleyMesh(Section section) { p(row.islandRight + bankUnder * grown, bedDepth), p(row.right - bankUnder, bedDepth), p(row.right + bankTop, landHeight), - p(valleyReach, landHeight), + p(landReach, landHeight), ]; } @@ -120,21 +120,21 @@ MeshData valleyMesh(Section section) { if (section.hasBridge) { final row = section.rowAt(section.bridgeAt); final z = -section.bridgeAt; - final leftLength = row.left - 0.3 + valleyReach; - final rightLength = valleyReach - row.right - 0.3; + final leftLength = row.left - 0.3 + landReach; + final rightLength = landReach - row.right - 0.3; parts ..add( _part( CuboidShape(size: Vector3(leftLength, 0.06, 1.8)), _road, - _at(-valleyReach + leftLength / 2.0, landHeight + 0.03, z), + _at(-landReach + leftLength / 2.0, landHeight + 0.03, z), ), ) ..add( _part( CuboidShape(size: Vector3(rightLength, 0.06, 1.8)), _road, - _at(valleyReach - rightLength / 2.0, landHeight + 0.03, z), + _at(landReach - rightLength / 2.0, landHeight + 0.03, z), ), ); } @@ -307,6 +307,11 @@ MeshData shotMesh() => CuboidShape( size: Vector3(0.14, 0.14, 0.9), ).build().withColor(linearFromSrgb(1.0, 0.9, 0.4)); +/// A helicopter's bullet: a long rod along Z, white, for its material to +/// colour. Long so it reads as a streak coming at the jet from eleven +/// metres up; a cube the size of a shard was lost against the water. +MeshData bulletMesh() => CuboidShape(size: Vector3(0.3, 0.3, 1.6)).build(); + /// One shard of an explosion, white: its material gives it its colour. MeshData shardMesh() => CuboidShape(size: Vector3.all(0.32)).build(); diff --git a/apps/flutter3d_demo_river/lib/src/pieces.dart b/apps/flutter3d_demo_river/lib/src/pieces.dart index 1082f852e..068fcf739 100644 --- a/apps/flutter3d_demo_river/lib/src/pieces.dart +++ b/apps/flutter3d_demo_river/lib/src/pieces.dart @@ -128,10 +128,19 @@ final class TargetComponent extends Object3dComponent } /// Seconds between a gunner's shots. - static const double fireInterval = 1.8; + static const double fireInterval = 1.1; /// A gunner fires only at a jet this far ahead of it, and no nearer. - static const (double, double) fireRange = (7.0, 36.0); + /// + /// **From almost as far as it wakes.** Starting at 36 with a shot every + /// 1.8 seconds, a jet at cruise went through the whole window in about + /// one interval and drew a single shot, and on the throttle often none. + /// With the first shot soon after waking, it now draws three at cruise + /// and two on the throttle. + static const (double, double) fireRange = (7.0, 46.0); + + /// Seconds from waking to a gunner's first shot. + static const double firstShot = 0.2; final TargetPlan plan; @@ -148,7 +157,7 @@ final class TargetComponent extends Object3dComponent double _spin = 0.0; double _dying = 0.0; double _smokeIn = 0.0; - double _fireIn = fireInterval / 2.0; + double _fireIn = firstShot; /// Made with the component rather than on load, so a target hit before /// Flame has loaded it has a hitbox to take away. diff --git a/apps/flutter3d_demo_river/lib/src/river_game.dart b/apps/flutter3d_demo_river/lib/src/river_game.dart index 9ed26cab0..144b73bc4 100644 --- a/apps/flutter3d_demo_river/lib/src/river_game.dart +++ b/apps/flutter3d_demo_river/lib/src/river_game.dart @@ -474,21 +474,41 @@ final class RiverGame extends FlameGame } } - /// A helicopter at [from] fires at where the jet is now. + /// A helicopter at [from] fires at where the jet is now: a red flash at + /// its nose, and a streak laid along the way it flies. void enemyFire({required Vector2 from}) { final aim = (jet.position - from)..normalize(); + final muzzle = from + aim * 1.4; _sayAt(Sounds.tracer, river.to3d(from, at: flightHeight)); + _muzzleFlash(river.to3d(muzzle, at: flightHeight)); add( EnemyShotComponent( - node: MeshNode(_kit.shard, _kit.tracer, name: 'tracer') - ..setUniformScale(0.7), + // The rod turned inside a node of its own: the component writes + // the outer node's place, and the streak keeps its heading. + node: SceneNode(name: 'tracer') + ..add( + MeshNode(_kit.bullet, _kit.tracer) + ..setRotation(_facing(aim.x, aim.y)), + ), scene: _scene, - position: from + aim * 1.4, + position: muzzle, velocity: aim * EnemyShotComponent.speed, ), ); } + void _muzzleFlash(Vector3 at) => blasts.system.burst( + ParticleEffect( + count: 10, + emitter: const SphereEmitter(speed: Range(1.5, 3.5)), + lifetime: const Range(0.2, 0.3), + size: const Range(0.5, 0.8), + color: _muzzle, + affectors: const [ParticleSizeOverLife()], + ), + at, + ); + /// Fire: glowing shards thrown up and out, falling, shrinking, dimming /// from orange to a dull red, round the flash of the blast itself. void fireball(Vector3 at, {double size = 1.0}) { @@ -540,6 +560,7 @@ final class RiverGame extends FlameGame static Vector4 get _flame => Vector4(4.0, 2.2, 0.6, 1.0); static Vector4 get _ember => Vector4(1.2, 0.2, 0.05, 1.0); static Vector4 get _spark => Vector4(4.0, 3.4, 1.6, 1.0); + static Vector4 get _muzzle => Vector4(4.0, 0.9, 0.4, 1.0); static Vector4 get _spray => Vector4(0.7, 0.8, 0.9, 1.0); /// How much of what is behind it a puff of smoke takes away, fresh and From d3ea84af11d6af30c4c07cb4ea11e7b4efa9d7a6 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 01:37:44 +0300 Subject: [PATCH 012/141] A river craft only moves where there is water to cross A tanker or helicopter put on a channel barely longer than itself turned at each bank several times a second and looked like it was shaking in place. One now gets a speed only when its channel leaves it at least three metres to travel, and a course test holds every mover to that. The test that a woken target moves measured where it ended a second later, and a mover that met a bank and turned back could end where it began; it now looks at the furthest it got. --- apps/flutter3d_demo_river/lib/src/course.dart | 17 +++++++++++++++-- .../flutter3d_demo_river/test/course_test.dart | 18 ++++++++++++++++++ apps/flutter3d_demo_river/test/game_test.dart | 12 ++++++++++-- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/apps/flutter3d_demo_river/lib/src/course.dart b/apps/flutter3d_demo_river/lib/src/course.dart index b5b4cf123..ed483cce5 100644 --- a/apps/flutter3d_demo_river/lib/src/course.dart +++ b/apps/flutter3d_demo_river/lib/src/course.dart @@ -307,15 +307,24 @@ final class Section { while (distance < section.end - 32.0) { final kind = mix.kindFor(random.nextDouble()); final heading = random.nextBool() ? 1 : -1; - final moves = random.nextDouble() < mix.moving; + final rolledMoving = random.nextDouble() < mix.moving; final gunner = kind == TargetKind.helicopter && random.nextDouble() < mix.gunners; + final row = section.rowAt(distance); final double? x = switch (kind) { // A jet comes in from off the side and crosses the whole valley. TargetKind.jet => -heading * (riverReach + 6.0), - _ => _placeOnWater(section.rowAt(distance), kind.halfLength, random), + _ => _placeOnWater(row, kind.halfLength, random), }; if (x != null) { + // **A mover needs water to move across.** One put on a channel + // barely longer than itself turned at each bank several times a + // second and read as a craft shaking in place, not moving. + final room = switch (row.channelAt(x)) { + (final from, final to) => to - from - 2.0 * kind.halfLength, + null => 0.0, + }; + final moves = rolledMoving && room >= _roomToMove; targets.add( TargetPlan( kind: kind, @@ -339,6 +348,10 @@ final class Section { return targets; } + /// How far a tanker or a helicopter must be able to travel across its + /// channel to be given a speed at all. + static const double _roomToMove = 3.0; + /// Somewhere on one of the row's channels, clear of both banks, or null /// when the channel picked is too narrow for it. static double? _placeOnWater( diff --git a/apps/flutter3d_demo_river/test/course_test.dart b/apps/flutter3d_demo_river/test/course_test.dart index 888a93f98..e19b2aa8d 100644 --- a/apps/flutter3d_demo_river/test/course_test.dart +++ b/apps/flutter3d_demo_river/test/course_test.dart @@ -48,6 +48,24 @@ void main() { } }); + test('a tanker or a helicopter that moves has water to move across', () { + final course = Course(); + var movers = 0; + for (var i = 0; i < _sections; i++) { + for (final target in course.section(i).targets) { + if (target.kind == TargetKind.jet || target.speed == 0.0) continue; + movers++; + final (from, to) = course.rowAt(target.distance).channelAt(target.x)!; + expect( + to - from - 2.0 * target.kind.halfLength, + greaterThanOrEqualTo(3.0), + reason: '${target.kind} at ${target.distance}', + ); + } + } + expect(movers, greaterThan(0)); + }); + test('there is always a channel a jet fits through', () { final course = Course(); for (var d = -sectionLength; d < sectionLength * _sections; d += 0.5) { diff --git a/apps/flutter3d_demo_river/test/game_test.dart b/apps/flutter3d_demo_river/test/game_test.dart index d2c51a78f..d5b1e9405 100644 --- a/apps/flutter3d_demo_river/test/game_test.dart +++ b/apps/flutter3d_demo_river/test/game_test.dart @@ -4,6 +4,8 @@ /// Flame's collision detection finding two hitboxes overlapping. library; +import 'dart:math' as math; + import 'package:flame/collisions.dart' show ShapeHitbox; import 'package:flame/components.dart' show TextComponent, Vector2; import 'package:flame_test/flame_test.dart'; @@ -363,9 +365,15 @@ void main() { // Still on the water, only nearer: it is distance that wakes a target, // not the jet being in the air. game.jet.position.y = -(mover.plan.distance - RiverGame.wakeRange + 5.0); - await _run(game, 60); + // The furthest it got rather than where it ended: a mover that meets a + // bank turns back, and a second later can be where it started. + var furthest = 0.0; + for (var i = 0; i < 60; i++) { + await _run(game, 1); + furthest = math.max(furthest, (mover.position.x - mover.plan.x).abs()); + } expect(mover.awake, isTrue); - expect((mover.position.x - mover.plan.x).abs(), greaterThan(0.5)); + expect(furthest, greaterThan(0.5)); }); test('every model loads and takes its primitive\'s place', () async { From e7bd72943bb2231539e38d5c39209c436f490aec Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 01:37:44 +0300 Subject: [PATCH 013/141] A level saved in the editor lands in the platformer as it runs The platformer builds the edited document ahead of the swap, textures, meshes and a simulation of its own, then puts it in the running level's place. When the edit reaches the simulation the swap happens inside the timeline's replay, so the run is lived again to now under the new level; the widget hears of it only afterwards, like any load, and starts a new demo there. A document for another level, or one that does not build, is refused and the level on screen stays. LiveLevel gains prepare and applyWhenReady for levels that cannot be built synchronously, and a settable level for a game that moved on by itself. RunSession.replaceLevel swaps a new build of the level being played in and carries the run over through the game's own snapshot and restore. --- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_demo_platformer/lib/main.dart | 32 ++++- .../lib/src/run.dart | 110 +++++++++++++-- .../test/run_test.dart | 129 +++++++++++++++++- packages/flutter3d_game/CHANGELOG.md | 10 ++ .../lib/src/run/live_level.dart | 43 ++++-- .../lib/src/run/run_session.dart | 20 +++ .../flutter3d_game/test/live_level_test.dart | 65 +++++++-- .../flutter3d_game/test/run_session_test.dart | 28 ++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 12 +- tasks/0.9-engine-roadmap.md | 10 +- 13 files changed, 418 insertions(+), 47 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 027327e6c..82045ac8f 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2688,7 +2688,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10842 tests** across 41 packages and 10 applications | +| Unit tests | **10851 tests** across 41 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 788c3e023..6bf1436bd 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10842 tests across forty-one packages and nine applications. The +There are 10851 tests across forty-one packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/apps/flutter3d_demo_platformer/lib/main.dart b/apps/flutter3d_demo_platformer/lib/main.dart index bd116d785..c8e1c7a81 100644 --- a/apps/flutter3d_demo_platformer/lib/main.dart +++ b/apps/flutter3d_demo_platformer/lib/main.dart @@ -408,8 +408,37 @@ class _GameScreenState extends State ); } - /// Where the run was standing when it was last written to disk. + /// `HR3`: the level on screen, as the editor sees it. + LiveLevel? _live; + + /// Lets a level saved in the editor into this run, or tells the door + /// which level is up now. /// + /// **Registered with the first level rather than in [initState]**, because + /// a `LiveLevel` compares every edit with the level it holds and there is + /// none before one loads; a VM service extension cannot be registered + /// twice, so later levels move the one door along. Everything the edit + /// takes is the run's: it builds the edited level ahead, swaps it in inside + /// [_timeline]'s replay when the simulation has to be lived again, and only + /// then tells this widget, through [PlatformerRun.onLevelBuilt] like any + /// load. + void _takeEdits(LevelReady level) { + final live = _live; + if (live != null) { + live.level = level.loaded.level; + return; + } + final run = _run.run; + registerLevelExtension( + _live = LiveLevel( + level: level.loaded.level, + timeline: _timeline, + prepare: run.prepareEdit, + rebuild: (Level next) => run.installEdit(), + present: (Level next, LevelDiff diff) => run.announceEdit(), + ), + ); + } @override void initState() { @@ -571,6 +600,7 @@ class _GameScreenState extends State onLevelBuilt: (String asset, LevelReady level, GraphicsDevice device) { setState(() => _levelArrived(level, device)); _beginDemo(asset, level); + _takeEdits(level); }, ), ); diff --git a/apps/flutter3d_demo_platformer/lib/src/run.dart b/apps/flutter3d_demo_platformer/lib/src/run.dart index 11156e92d..ef1221d6f 100644 --- a/apps/flutter3d_demo_platformer/lib/src/run.dart +++ b/apps/flutter3d_demo_platformer/lib/src/run.dart @@ -48,15 +48,29 @@ typedef Carried = ({int lives, int deaths, double elapsed, int coins}); /// /// Deliberately *not* in `staging.dart`: everything here needs a /// [GraphicsDevice], and that file exists to be callable without one. +/// +/// [document] builds that level instead of reading [asset]: a level edited +/// in the editor, which exists as a document and not yet as an asset. Future<({EntityRegistry kinds, LoadedLevel loaded, FixtureVisuals fixtures})> -openLevel(String asset, {required GraphicsDevice device}) async { +openLevel( + String asset, { + required GraphicsDevice device, + Level? document, +}) async { final kinds = platformerRegistry(); - final loaded = await LevelLoader().load( - asset, - device: device, - registry: kinds, - rules: platformerRules(), - ); + final loaded = document == null + ? await const LevelLoader().load( + asset, + device: device, + registry: kinds, + rules: platformerRules(), + ) + : await const LevelLoader().build( + document, + device: device, + registry: kinds, + rules: platformerRules(), + ); final fixtures = FixtureVisuals( loaded.scene, loaded, @@ -118,7 +132,21 @@ final class PlatformerRun extends RunSession { Future open(String asset) async { final device = await openDevice(); _device = device; - final (:kinds, :loaded, :fixtures) = await openLevel(asset, device: device); + final level = await _build(asset, device); + onLevelBuilt(asset, level, device); + return level; + } + + Future _build( + String asset, + GraphicsDevice device, { + Level? document, + }) async { + final (:kinds, :loaded, :fixtures) = await openLevel( + asset, + device: device, + document: document, + ); final staged = stage( loaded.level, @@ -132,13 +160,65 @@ final class PlatformerRun extends RunSession { elapsed: _carried?.elapsed ?? 0.0, ); - final level = LevelReady( - loaded: loaded, - staged: staged, - fixtures: fixtures, - ); - onLevelBuilt(asset, level, device); - return level; + return LevelReady(loaded: loaded, staged: staged, fixtures: fixtures); + } + + /// An edit of the level being played, built and waiting for + /// [installEdit], with the build it was made to replace. + ({LevelReady edited, LevelReady replaces})? _edit; + + /// Whether an edit went in and the widget has not been told yet. + bool _editUntold = false; + + /// Builds [next], an edit of the level being played, without touching the + /// run: textures, meshes, colliders and a simulation of its own, so the + /// swap itself can be synchronous. Throws, and changes nothing, when + /// nothing is being played, when [next] is another level, or when it does + /// not build. + Future prepareEdit(Level next) async { + final playing = status; + if (playing is! RunPlaying) { + throw StateError('no level is being played'); + } + final name = playing.level.loaded.level.name; + if (next.name != name) { + throw StateError('the game is playing $name, not ${next.name}'); + } + final device = await openDevice(); + final edited = await _build(playing.asset, device, document: next); + final waiting = _edit; + if (waiting != null) close(waiting.edited); + _edit = (edited: edited, replaces: playing.level); + } + + /// Swaps in what [prepareEdit] built, the run carried over. Called inside + /// the timeline's replay, so nothing here may tell the widget: that is + /// [announceEdit]'s, once the run has been brought back to now. + /// + /// A build made for a level that has since been left is let go. + void installEdit() { + final edit = _edit; + if (edit == null) return; + _edit = null; + if (!identical(level, edit.replaces)) { + close(edit.edited); + return; + } + _editUntold = replaceLevel(edit.edited); + } + + /// Installs the edit if [installEdit] has not, then hands the new build to + /// [onLevelBuilt] as a load would: the runner's node, the camera, a new + /// demo from here. + void announceEdit() { + installEdit(); + final playing = status; + final device = _device; + if (!_editUntold || playing is! RunPlaying || device == null) { + return; + } + _editUntold = false; + onLevelBuilt(playing.asset, playing.level, device); } @override diff --git a/apps/flutter3d_demo_platformer/test/run_test.dart b/apps/flutter3d_demo_platformer/test/run_test.dart index 340889a54..4c6840b72 100644 --- a/apps/flutter3d_demo_platformer/test/run_test.dart +++ b/apps/flutter3d_demo_platformer/test/run_test.dart @@ -47,6 +47,7 @@ final class _Storage implements Storage { /// exists so the loader has somewhere to upload the level's textures. ({PlatformerRun run, _Storage storage}) _game({ void Function(String asset)? onLevelBuilt, + InputState? input, }) { final device = CpuDevice( width: 16, @@ -59,7 +60,7 @@ final class _Storage implements Storage { run: PlatformerRun( firstLevel: _first, saves: SaveFile(appName: 'platformer', storage: storage), - input: InputState(), + input: input ?? InputState(), openDevice: () async => device, // The widget's half, which this test does not have: a camera, a box for // the runner, the interpolators — `onLevelBuilt` itself, `rp-01`'s own @@ -245,6 +246,132 @@ void main() { expect(await again.run.begin(), isTrue, reason: 'did not resume'); expect((again.run.status as RunPlaying).asset, _first); }); + + group('a level edited in the editor', () { + test('goes in under the run, which carries on where it stood', () async { + final input = InputState(); + final built = []; + final it = _game(input: input, onLevelBuilt: built.add); + await it.run.begin(); + final before = it.run.level!; + final timeline = _played(it.run, input, steps: 90); + final elapsed = before.sim.elapsed; + final standing = before.runner.body.position.clone(); + final lastBrush = before.loaded.level.brushes.last.centre.y; + built.clear(); + + final applied = await _live( + it.run, + timeline, + ).applyWhenReady(_raised(before.loaded.level)); + + final after = it.run.level!; + expect(applied.swappedAt, isNotNull, reason: 'a brush is the sim’s'); + expect(after, isNot(same(before))); + expect(after.loaded.level.brushes.last.centre.y, lastBrush + 40.0); + expect( + after.sim.elapsed, + closeTo(elapsed, 1e-9), + reason: 'the run was lived again up to now', + ); + expect(after.runner.body.position.distanceTo(standing), lessThan(1e-6)); + expect(built, [ + _first, + ], reason: 'the widget hears of it once, after the replay'); + expect((it.run.status as RunPlaying).asset, _first); + }); + + test('a look-only edit swaps the build and replays nothing', () async { + final input = InputState(); + final it = _game(input: input); + await it.run.begin(); + final before = it.run.level!; + final timeline = _played(it.run, input, steps: 30); + final document = before.loaded.level.toJson()..['fogDensity'] = 0.03; + + final applied = await _live( + it.run, + timeline, + ).applyWhenReady(Level.fromJson(document)); + + expect(applied.swappedAt, isNull); + expect(it.run.level, isNot(same(before))); + expect(it.run.level!.loaded.level.fogDensity, 0.03); + expect( + timeline.history, + isEmpty, + reason: 'the timeline was not branched', + ); + }); + + test('another level is refused, and the one up is kept', () async { + final input = InputState(); + final it = _game(input: input); + await it.run.begin(); + final before = it.run.level!; + // Another level differs in more than its name: a name alone is not + // something `diffLevel` compares, and an edit that changes nothing + // else is taken as the same level. + final document = _raised(before.loaded.level).toJson() + ..['name'] = 'elsewhere'; + + await expectLater( + _live( + it.run, + _played(it.run, input, steps: 1), + ).applyWhenReady(Level.fromJson(document)), + throwsA(isA()), + ); + expect(it.run.level, same(before)); + }); + }); +} + +/// The door the game opens for the editor, as `main.dart` builds it. +LiveLevel _live(PlatformerRun run, RunTimeline timeline) => LiveLevel( + level: run.level!.loaded.level, + timeline: timeline, + prepare: run.prepareEdit, + rebuild: (Level next) => run.installEdit(), + present: (Level next, LevelDiff diff) => run.announceEdit(), +); + +/// Plays [steps] steps of the level that is up the way `GameLoop` does, +/// keyframes and all, and hands back the timeline over them. +RunTimeline _played(PlatformerRun run, InputState input, {required int steps}) { + final rewind = RewindBuffer(stepsPerSecond: 60, history: 10.0); + for (var i = 0; i < steps; i++) { + rewind.recorder.record(input); + input.beginStep(); + if (rewind.keyframeDue) rewind.keyframe(run.level!.sim.save()); + run.level!.sim.step(1.0 / 60.0); + input.endStep(); + } + return RunTimeline( + rewind: rewind, + input: input, + stepSim: (double dt) => run.level!.sim.step(dt), + restore: (Snapshot snapshot) => run.level!.sim.restore(snapshot), + ); +} + +/// [level] with its last brush forty metres up, out of the runner's way. +Level _raised(Level level) { + final document = level.toJson(); + final brushes = [...document['brushes']! as List]; + final last = Map.of( + brushes.removeLast()! as Map, + ); + final at = (last['at']! as List).cast(); + last['at'] = [ + at[0].toDouble(), + at[1].toDouble() + 40.0, + at[2].toDouble(), + ]; + return Level.fromJson({ + ...document, + 'brushes': [...brushes, last], + }); } /// Finishes the level that is up, and moves on. diff --git a/packages/flutter3d_game/CHANGELOG.md b/packages/flutter3d_game/CHANGELOG.md index e74d2708a..fbae3bb27 100644 --- a/packages/flutter3d_game/CHANGELOG.md +++ b/packages/flutter3d_game/CHANGELOG.md @@ -1,5 +1,15 @@ ## Unreleased +- **A level that has to be built before it can be swapped in.** + `LiveLevel(prepare:)` is awaited by `applyWhenReady`, which the extension + now calls, before anything changes; a level whose `prepare` throws is + refused with what it threw and the game keeps the one it had. + `answerLevelApply` returns a `Future`. `LiveLevel.level` is settable, for a + game that moved to another level by its own means. +- **`RunSession.replaceLevel` puts a new build of the level being played in + its place** and carries the run over through the game's own `snapshotOf` + and `restoreInto`, without loading anything or starting the level again. + - **`RunTimeline.replayUnderNewCode` shows what a code reload changed.** It lives the last seconds again under the new code from the nearest keyframe, compares the state at each keyframe the old run left and at the present, diff --git a/packages/flutter3d_game/lib/src/run/live_level.dart b/packages/flutter3d_game/lib/src/run/live_level.dart index d1712127f..47b88c29f 100644 --- a/packages/flutter3d_game/lib/src/run/live_level.dart +++ b/packages/flutter3d_game/lib/src/run/live_level.dart @@ -21,12 +21,19 @@ import 'run_timeline.dart'; /// replay can reach. The report says which happened. final class LiveLevel { LiveLevel({ - required this._level, + required this.level, required this.present, required this.rebuild, this.timeline, + this.prepare, }); + /// Builds ahead what [rebuild] and [present] will swap in, for a game + /// whose level cannot be made synchronously: textures to upload, meshes, + /// a scene. Awaited by [applyWhenReady] before anything changes, so a + /// level that will not build leaves the one being played untouched. + final Future Function(Level next)? prepare; + /// Patches the running scene to [next]'s look. [diff] names what changed, /// so a presenter can touch one lamp rather than rebuild them all. final void Function(Level next, LevelDiff diff) present; @@ -41,15 +48,24 @@ final class LiveLevel { final RunTimeline? timeline; /// The level being played now. - Level get level => _level; - Level _level; + /// + /// Set by a game that moved on by its own means — the next level, a + /// restart — so the next edit is compared with what is on screen. + Level level; + + /// [prepare], then [apply]: what the extension does with a level that + /// arrived. An edit that changes nothing prepares nothing. + Future applyWhenReady(Level next) async { + if (!diffLevel(level, next).isEmpty) await prepare?.call(next); + return apply(next); + } /// Makes [next] the level being played, as little disturbed as the change /// allows. LevelApplied apply(Level next) { - final diff = diffLevel(_level, next); + final diff = diffLevel(level, next); if (diff.isEmpty) { - _level = next; + level = next; return LevelApplied(diff: diff); } final int? swappedAt; @@ -65,7 +81,7 @@ final class LiveLevel { swappedAt = null; } present(next, diff); - _level = next; + level = next; return LevelApplied( diff: diff, swappedAt: swappedAt, @@ -108,12 +124,15 @@ final class LevelApplied { /// and applying the document would put a level under the run that the editor /// never showed anybody. So a mismatch is refused, naming both. /// +/// A level whose [LiveLevel.prepare] throws is refused the same way, with +/// what it threw: the game keeps the level it had. +/// /// A plain function over the parameters, and [registerLevelExtension] a thin /// door onto it, so the answers are tested without a VM service. -({Map? result, String? error}) answerLevelApply( +Future<({Map? result, String? error})> answerLevelApply( LiveLevel live, Map parameters, -) { +) async { final document = parameters['document']; final hash = parameters['hash']; if (document == null || hash == null) { @@ -133,7 +152,11 @@ final class LevelApplied { 'it changed on the way', ); } - return (result: live.apply(next).toJson(), error: null); + try { + return (result: (await live.applyWhenReady(next)).toJson(), error: null); + } on Object catch (error) { + return (result: null, error: 'the level did not build: $error'); + } } /// Puts [live] on the VM service as `ext.flutter3d.level.apply`, beside the @@ -144,7 +167,7 @@ void registerLevelExtension(LiveLevel live) { method, parameters, ) async { - final (:result, :error) = answerLevelApply(live, parameters); + final (:result, :error) = await answerLevelApply(live, parameters); if (error != null) { return developer.ServiceExtensionResponse.error( developer.ServiceExtensionResponse.invalidParams, diff --git a/packages/flutter3d_game/lib/src/run/run_session.dart b/packages/flutter3d_game/lib/src/run/run_session.dart index 8599453c6..244111a0e 100644 --- a/packages/flutter3d_game/lib/src/run/run_session.dart +++ b/packages/flutter3d_game/lib/src/run/run_session.dart @@ -311,6 +311,26 @@ abstract base class RunSession { saves.write(playing.asset, snapshotOf(playing.level)); } + /// Puts [next], another build of the level being played, in its place, and + /// carries the run over: [snapshotOf] the old one, [restoreInto] the new, + /// then the old one is [close]d. + /// + /// **For a level edited under a running game**, which has to be built + /// before it can be swapped in and must not start the level again when it + /// is. The asset stays the same: it is the same level, changed. False, and + /// [next] closed, when no level is being played — a load under way wins. + bool replaceLevel(L next) { + final playing = _status; + if (playing is! RunPlaying) { + close(next); + return false; + } + restoreInto(next, snapshotOf(playing.level)); + _emit(RunPlaying(playing.asset, next, outcome: outcomeOf(next))); + close(playing.level); + return true; + } + void _emit(RunStatus next) { _status = next; onChanged?.call(next); diff --git a/packages/flutter3d_game/test/live_level_test.dart b/packages/flutter3d_game/test/live_level_test.dart index 87982d838..ffa9e3f33 100644 --- a/packages/flutter3d_game/test/live_level_test.dart +++ b/packages/flutter3d_game/test/live_level_test.dart @@ -32,7 +32,11 @@ Level _level({double wallAt = 5.0, double fog = 0.0}) => Level.fromJson(_document(wallAt: wallAt, fog: fog)); /// What a game did with each call, in order. -({LiveLevel live, List calls}) _game({RunTimeline? timeline}) { +({LiveLevel live, List calls}) _game({ + RunTimeline? timeline, + bool prepares = false, + String? prepareFails, +}) { final calls = []; return ( live: LiveLevel( @@ -40,6 +44,13 @@ Level _level({double wallAt = 5.0, double fog = 0.0}) => present: (next, diff) => calls.add('present fog=${diff.fog}'), rebuild: (next) => calls.add('rebuild ${next.brushes.single.centre.x}'), timeline: timeline, + prepare: prepares || prepareFails != null + ? (next) async { + await Future.delayed(Duration.zero); + if (prepareFails != null) throw StateError(prepareFails); + calls.add('prepare'); + } + : null, ), calls: calls, ); @@ -103,9 +114,9 @@ void main() { }); group('ext.flutter3d.level.apply', () { - test('applies a document whose hash matches, and reports how', () { + test('applies a document whose hash matches, and reports how', () async { final game = _game(); - final (:result, :error) = answerLevelApply( + final (:result, :error) = await answerLevelApply( game.live, _send(_level(fog: 0.04)), ); @@ -115,29 +126,65 @@ void main() { expect(result['rebuiltInPlace'], isFalse); }); - test('refuses a document that changed on the way', () { + test('refuses a document that changed on the way', () async { final game = _game(); final parameters = _send(_level(fog: 0.04)) ..['hash'] = _level().digestHex; - final (:result, :error) = answerLevelApply(game.live, parameters); + final (:result, :error) = await answerLevelApply(game.live, parameters); expect(result, isNull); expect(error, contains('changed on the way')); expect(game.calls, isEmpty); }); - test('refuses what is not a level, and what is missing', () { + test('builds the level ahead, then swaps it in', () async { + final game = _game(prepares: true); + final (:result, :error) = await answerLevelApply( + game.live, + _send(_level(wallAt: 8.0)), + ); + + expect(error, isNull); + expect(game.calls, [ + 'prepare', + 'rebuild 8.0', + 'present fog=false', + ]); + expect(game.live.level.brushes.single.centre.x, 8.0); + }); + + test('keeps the level it had when the new one does not build', () async { + final game = _game(prepareFails: 'no texture stone.png'); + final (:result, :error) = await answerLevelApply( + game.live, + _send(_level(wallAt: 8.0)), + ); + + expect(result, isNull); + expect(error, contains('did not build')); + expect(error, contains('no texture stone.png')); + expect(game.calls, isEmpty); + expect(game.live.level.brushes.single.centre.x, 5.0); + }); + + test('the same level again prepares nothing', () async { + final game = _game(prepares: true); + await answerLevelApply(game.live, _send(_level())); + expect(game.calls, isEmpty); + }); + + test('refuses what is not a level, and what is missing', () async { final game = _game(); expect( - answerLevelApply(game.live, { + (await answerLevelApply(game.live, { 'document': '[1, 2]', 'hash': 'x', - }).error, + })).error, contains('not a level'), ); expect( - answerLevelApply(game.live, {}).error, + (await answerLevelApply(game.live, {})).error, contains('takes a document'), ); }); diff --git a/packages/flutter3d_game/test/run_session_test.dart b/packages/flutter3d_game/test/run_session_test.dart index 5355a511d..999517bed 100644 --- a/packages/flutter3d_game/test/run_session_test.dart +++ b/packages/flutter3d_game/test/run_session_test.dart @@ -420,6 +420,34 @@ void main() { ], reason: 'the level left behind, then the one the race lost'); }); }); + + group('an edited level', () { + test('takes the run over from the build it replaces', () async { + final game = _game(); + await game.begin(); + final old = (game.status as RunPlaying<_Level>).level; + final edited = _Level('one')..outcome = RunOutcome.won; + + expect(game.replaceLevel(edited), isTrue); + + final now = game.status as RunPlaying<_Level>; + expect(now.level, same(edited)); + expect(now.asset, 'one', reason: 'the same level, changed'); + expect(now.outcome, RunOutcome.won, reason: 'read off the new build'); + expect(edited.restored, {'where': 'one'}); + expect(game.closed, <_Level>[old]); + expect(game.opened, ['one'], reason: 'nothing was loaded again'); + }); + + test('is let go when no level is being played', () async { + final game = _game(); + final edited = _Level('one'); + + expect(game.replaceLevel(edited), isFalse); + expect(game.closed, <_Level>[edited]); + expect(game.level, isNull); + }); + }); } const Snapshot snapshotOfNothing = Snapshot({}); diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 33d6f68ba..fa996ca5f 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10842 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10851 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 69d44dda3..34f03f71f 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10842 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10851 tests need a GPU. --- # Testing -10842 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10851 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -18,16 +18,16 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_shooter` | 340 | | `flutter3d_audio` | 59 | | `flutter3d_game_racing` | 223 | | `flutter3d_webgl` | 62 | | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 63 | -| `apps/flutter3d_demo_platformer` | 199 | | `flutter3d_impeller` | 64 | +| `apps/flutter3d_demo_platformer` | 202 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 328 | | `apps/flutter3d_demo_strategy` | 50 | | `apps/flutter3d_editor` | 223 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 28 | -| `apps/flutter3d_demo_river` | 53 | | `flutter3d_testing` | 27 | +| `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 90 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 130 | -| `flutter3d_game` | 272 | | `flutter3d_shaders` | 5 | +| `flutter3d_game` | 277 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | | | | | `flutter3d_net` | 15 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10816 rather than 10842: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10825 rather than 10851: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 310b690fd..57f4bface 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -156,10 +156,16 @@ changed material is still the simulation's. *Tests:* classifier units; a run swapped at step K arrives where a fresh run with the new level from K does; the extension end to end over a real VM service (`run_timeline_extensions_test.dart`). +The platformer demo takes edits: `PlatformerRun.prepareEdit` builds the +edited document ahead (textures, meshes, a simulation of its own, through +`LiveLevel(prepare:)`), `RunSession.replaceLevel` swaps it in inside the +replay with the run carried over, and the widget hears of it once the run is +back at now. A document for another level, or one that does not build, is +refused and the level on screen stays. *Left:* patches instead of whole documents; the swap written into a `Demo` segment so a saved `.f3drun` replays it (the history has it, the file format -does not yet); the editor MCP driving the same path; a demo game wired to -`LiveLevel` so the loop is visible in one of ours. +does not yet; the platformer starts a new demo at the swap instead); the +editor MCP driving the same path. **HR4. Live parameters and replay under new code (S–M).** *Done:* `ext.flutter3d.material.set {name, fields}` sets base colour, emissive, From 1b393e6fc368c27b9ba6e3586c23898ec4764c28 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:05:14 +0300 Subject: [PATCH 014/141] A dragged slider shows in the running game, and a reload replays itself The editor's material panel, which had never been mounted, opens from a palette button beside the step panel. Every value one of its sliders passes through, and every value written, goes to the game Play is running as ext.flutter3d.material.set, in the engine's words: a level's emissive is emissiveStrength, and a new base colour is the glow's too. One VM service connection is kept for it, and values are merged and sent at most thirty times a second, the last one always. RangeSliderField and FieldRow gain onPreview for the values between the start and the end of a drag. replayAfterHotSwap lives the last seconds again under the new code after every HotSwap and says where the two runs part; the platformer calls it. --- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_demo_platformer/lib/main.dart | 11 ++ apps/flutter3d_editor/lib/main.dart | 62 +++++++++ .../lib/src/material_panel.dart | 39 ++++++ .../lib/src/play/live_material.dart | 122 ++++++++++++++++++ .../test/live_material_test.dart | 88 +++++++++++++ .../test/material_panel_test.dart | 61 +++++++++ .../flutter3d_editor_widgets/CHANGELOG.md | 7 + .../lib/src/field_row.dart | 6 + .../lib/src/range_slider_field.dart | 23 +++- .../test/range_slider_field_test.dart | 36 ++++++ packages/flutter3d_game/CHANGELOG.md | 5 + .../flutter3d_game/lib/flutter3d_game.dart | 1 + .../lib/src/run/replay_after_swap.dart | 57 ++++++++ .../run_timeline_new_code_replay_test.dart | 45 +++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 12 +- tasks/0.9-engine-roadmap.md | 12 +- 19 files changed, 574 insertions(+), 19 deletions(-) create mode 100644 apps/flutter3d_editor/lib/src/play/live_material.dart create mode 100644 apps/flutter3d_editor/test/live_material_test.dart create mode 100644 packages/flutter3d_game/lib/src/run/replay_after_swap.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 82045ac8f..51ccc1555 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2688,7 +2688,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10851 tests** across 41 packages and 10 applications | +| Unit tests | **10860 tests** across 41 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 6bf1436bd..7b9e5dd49 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10851 tests across forty-one packages and nine applications. The +There are 10860 tests across forty-one packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/apps/flutter3d_demo_platformer/lib/main.dart b/apps/flutter3d_demo_platformer/lib/main.dart index c8e1c7a81..15928b365 100644 --- a/apps/flutter3d_demo_platformer/lib/main.dart +++ b/apps/flutter3d_demo_platformer/lib/main.dart @@ -411,6 +411,13 @@ class _GameScreenState extends State /// `HR3`: the level on screen, as the editor sees it. LiveLevel? _live; + /// Stops the replay [replayAfterHotSwap] starts after every hot reload. + late final VoidCallback _stopReplays; + + /// The run as it stands, for the replay to compare against. Empty before + /// a level is up, when there is also nothing recorded to replay. + Snapshot _present() => _sim?.save() ?? const Snapshot({}); + /// Lets a level saved in the editor into this run, or tells the door /// which level is up now. /// @@ -475,6 +482,9 @@ class _GameScreenState extends State // `rp-02`: harmless where the VM service is off — `registerExtension` // just adds an entry nothing ever asks for. registerTimelineExtensions(_timeline, bugReport: _remoteBugReport); + // `HR4`: after every hot reload, the last three seconds lived again under + // the new code, and the console says whether they came out the same. + _stopReplays = replayAfterHotSwap(_timeline, capture: _present); _view = RenderView(camera: _camera); unawaited(_openGraphics()); } @@ -1087,6 +1097,7 @@ class _GameScreenState extends State @override void dispose() { + _stopReplays(); // Null if the device never opened: nothing ran, so there is nothing to // keep — and the cubit to close was never built either. _runOrNull?.save(); diff --git a/apps/flutter3d_editor/lib/main.dart b/apps/flutter3d_editor/lib/main.dart index f67ca3569..dc2b81154 100644 --- a/apps/flutter3d_editor/lib/main.dart +++ b/apps/flutter3d_editor/lib/main.dart @@ -48,9 +48,11 @@ import 'src/editor_palette.dart'; import 'src/editor_theme.dart'; import 'src/fly_camera.dart'; import 'src/light_plan_dialog.dart'; +import 'src/material_panel.dart'; import 'src/open_run_channel.dart'; import 'src/play/flutter_run.dart'; import 'src/play/level_push.dart'; +import 'src/play/live_material.dart'; import 'src/play/play_screen.dart'; import 'src/playtest_report_screen.dart'; import 'src/recent_projects.dart'; @@ -198,6 +200,18 @@ class _EditorScreenState extends State /// `edu-01`: whether the step panel is open. bool _showSteps = false; + /// `HR4`: whether the material panel is open. + bool _showMaterials = false; + + /// `HR4`: what the material panel drags, on its way to the game [_run] + /// has running, thirty times a second at most. + late final ThrottledMaterials _liveMaterials = ThrottledMaterials( + send: _sendMaterial, + ); + + /// The connection [_liveMaterials] sends through, to the game running now. + GameMaterials? _gameMaterials; + /// `rp-04`: the macOS side of file association calls back through this — /// a double-click on a `.f3drun` in Finder, or a drop on the dock icon. OpenRunChannel? _openRunChannel; @@ -1141,6 +1155,30 @@ class _EditorScreenState extends State } } + /// Sets [fields] on [material] in the game [_run] has running, when one + /// is. Nothing is sent, and nothing said, when no game is running: the + /// panel edits the level either way. + Future _sendMaterial( + String material, + Map fields, + ) async { + if (_run?.state.value case PlayRunning(:final vmService)) { + final link = _gameMaterials; + final GameMaterials game; + if (link != null && link.vmService == vmService) { + game = link; + } else { + unawaited(link?.dispose()); + game = _gameMaterials = GameMaterials( + vmService, + onError: (Object error) => + _cubit.say('the running game did not take the material: $error'), + ); + } + await game.set(material, fields); + } + } + /// A name beside [path] that nothing is using yet. /// /// **It used to be one name.** Shift-save always wrote `foo.edited.json`, so @@ -1164,6 +1202,8 @@ class _EditorScreenState extends State @override void dispose() { + _liveMaterials.dispose(); + unawaited(_gameMaterials?.dispose()); unawaited(_run?.dispose()); _openRunChannel?.dispose(); _shaders?.dispose(); @@ -1441,6 +1481,15 @@ class _EditorScreenState extends State _changed('$what — ${state.editing.says}'), ), ), + // `HR4`: a value dragged here reaches a running game + // at once; a value written is the level's, as any. + if (_showMaterials) + MaterialPanel( + editing: state.editing, + onChanged: (String what) => + _changed('$what — ${state.editing.says}'), + onLive: _liveMaterials.push, + ), EditorInspector( state: state, onChanged: (String what) => @@ -1508,6 +1557,19 @@ class _EditorScreenState extends State onPressed: () => setState(() => _showSteps = !_showSteps), ), ), + Positioned( + top: 4, + right: 172, + child: IconButton( + icon: Icon( + _showMaterials ? Icons.palette : Icons.palette_outlined, + color: const Color(0xFFE6EAF0), + ), + tooltip: 'Open the material panel', + onPressed: () => + setState(() => _showMaterials = !_showMaterials), + ), + ), // `rp-04`: the same screen file association and the open // panel both land on. Positioned( diff --git a/apps/flutter3d_editor/lib/src/material_panel.dart b/apps/flutter3d_editor/lib/src/material_panel.dart index 833017359..fa8e50094 100644 --- a/apps/flutter3d_editor/lib/src/material_panel.dart +++ b/apps/flutter3d_editor/lib/src/material_panel.dart @@ -153,6 +153,31 @@ bool setLevelMaterialField( return true; } +/// What a running game's `ext.flutter3d.material.set` takes for [key] of a +/// level material set to [value], or null for a key a frame cannot show +/// without a reload: a texture path, the tiling, a material file. +/// +/// **The level's words turned into the engine's**, the way the loader turns +/// them (`LevelScene.materialFrom`): a level's `emissive` is a strength over +/// the base colour, so it is `emissiveStrength`, and a new base colour is +/// the glow's colour too. +Map? liveMaterialFields(String key, Object? value) => + switch ((key, value)) { + ('baseColor', final List colour) + when colour.length >= 3 && colour.every((it) => it is num) => + { + 'baseColor': [ + ...colour.take(3).cast(), + colour.length > 3 ? colour[3]! as num : 1.0, + ], + 'emissive': colour.take(3).cast().toList(), + }, + ('roughness', final num at) => {'roughness': at}, + ('metallic', final num at) => {'metallic': at}, + ('emissive', final num at) => {'emissiveStrength': at}, + _ => null, + }; + /// The materials of a level, and the fields of the one that is open. final class MaterialPanel extends StatefulWidget { const MaterialPanel({ @@ -161,11 +186,17 @@ final class MaterialPanel extends StatefulWidget { required this.onChanged, this.documents = const {}, this.onMaterialWritten, + this.onLive, this.offers = nothingToOffer, }); final Editing editing; + /// Called with a material's name and [liveMaterialFields] for every value + /// a slider passes through and every value written, for a running game to + /// show before anything is saved. Keys a frame cannot show are not sent. + final void Function(String material, Map fields)? onLive; + /// Called after a field was actually written, naming it, so the screen can /// rebuild and the scene can be rebuilt from the document. The inspector's /// callback, with the same contract: a refused write tells nobody. @@ -264,6 +295,7 @@ class _MaterialPanelState extends State { value: row[key], hint: levelMaterialHints[key], offers: widget.offers, + onPreview: (Object? value) => _live(name, key, value), onWrite: (Object? value) { if (setLevelMaterialField( widget.editing, @@ -271,6 +303,7 @@ class _MaterialPanelState extends State { key, value, )) { + _live(name, key, value); widget.onChanged('$name.$key'); } }, @@ -334,6 +367,12 @@ class _MaterialPanelState extends State { ]; } + void _live(String material, String key, Object? value) { + final send = widget.onLive; + final fields = liveMaterialFields(key, value); + if (send != null && fields != null) send(material, fields); + } + void _writeFile( String name, MaterialDocument document, diff --git a/apps/flutter3d_editor/lib/src/play/live_material.dart b/apps/flutter3d_editor/lib/src/play/live_material.dart new file mode 100644 index 000000000..43c56bce7 --- /dev/null +++ b/apps/flutter3d_editor/lib/src/play/live_material.dart @@ -0,0 +1,122 @@ +/// `HR4`'s editor half: a material dragged in the panel, shown in the game +/// that is running before anything is saved. +library; + +import 'dart:async'; +import 'dart:convert'; + +import 'package:vm_service/vm_service.dart'; +import 'package:vm_service/vm_service_io.dart'; + +/// Passes on what the panel sends at most [interval] apart, merging what +/// arrives in between, and always sends the last of it. +/// +/// **A drag is sixty values a second and the game wants the last one.** Each +/// is a round trip over the VM service, and a phone on the far end of a +/// cable falls behind at that rate: the colour then trails the thumb and +/// keeps changing after the hand has stopped. So the first value goes at +/// once, what comes during the next [interval] is merged by material and +/// field, and whatever is left when it ends goes then. The last value is +/// never dropped, which is the one somebody let go of the slider on. +final class ThrottledMaterials { + ThrottledMaterials({ + required this.send, + this.interval = const Duration(milliseconds: 33), + }); + + /// Sends one material's fields to the game. + final Future Function(String material, Map fields) + send; + + /// Thirty a second by default. + final Duration interval; + + final Map> _pending = + >{}; + Timer? _quiet; + + void push(String material, Map fields) { + (_pending[material] ??= {}).addAll(fields); + if (_quiet == null) _flush(); + } + + void _flush() { + if (_pending.isEmpty) { + _quiet = null; + return; + } + final sending = Map>.of(_pending); + _pending.clear(); + for (final MapEntry(key: material, value: fields) in sending.entries) { + unawaited(send(material, fields)); + } + _quiet = Timer(interval, _flush); + } + + /// Drops what has not been sent yet. + void dispose() { + _quiet?.cancel(); + _quiet = null; + _pending.clear(); + } +} + +/// One VM service connection to a running game, kept for as long as the +/// panel sends to it, to call `ext.flutter3d.material.set`. +/// +/// **Connected once, not per value.** `pushLevel` connects for each save, +/// which is fine for one call a save; thirty a second would spend most of +/// each on a handshake. +final class GameMaterials { + GameMaterials(this.vmService, {this.onError}); + + final String vmService; + + /// Told when a send fails, once per connection: the game has gone, or + /// does not draw through a `SceneSurface`. Every later send in the same + /// drag would say the same thing. + final void Function(Object error)? onError; + + static const String method = 'ext.flutter3d.material.set'; + + Future<({VmService service, String isolate})>? _link; + bool _told = false; + + Future set(String material, Map fields) async { + try { + final (:service, :isolate) = await (_link ??= _connect()); + await service.callServiceExtension( + method, + isolateId: isolate, + args: {'name': material, 'fields': jsonEncode(fields)}, + ); + } on Object catch (error) { + final link = _link; + _link = null; + unawaited(link?.then((it) => it.service.dispose()).catchError((_) {})); + if (!_told) onError?.call(error); + _told = true; + } + } + + Future<({VmService service, String isolate})> _connect() async { + final service = await vmServiceConnectUri(vmService); + final vm = await service.getVM(); + for (final ref in vm.isolates ?? const []) { + final isolate = await service.getIsolate(ref.id!); + if ((isolate.extensionRPCs ?? const []).contains(method)) { + return (service: service, isolate: ref.id!); + } + } + await service.dispose(); + throw StateError( + 'the running game takes no live materials: nothing registered $method', + ); + } + + Future dispose() async { + final link = _link; + _link = null; + if (link != null) await (await link).service.dispose(); + } +} diff --git a/apps/flutter3d_editor/test/live_material_test.dart b/apps/flutter3d_editor/test/live_material_test.dart new file mode 100644 index 000000000..816add65f --- /dev/null +++ b/apps/flutter3d_editor/test/live_material_test.dart @@ -0,0 +1,88 @@ +/// `HR4`: what the material panel drags reaches a running game at most +/// thirty times a second, merged, and never without its last value. +/// +/// flutter test test/live_material_test.dart +library; + +import 'package:flutter3d_editor/src/play/live_material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + const interval = Duration(milliseconds: 33); + + /// What reached the game, in order. + ({ThrottledMaterials sender, List<(String, Map)> sent}) + sender() { + final sent = <(String, Map)>[]; + return ( + sender: ThrottledMaterials( + send: (String material, Map fields) async => + sent.add((material, Map.of(fields))), + ), + sent: sent, + ); + } + + testWidgets('the first value goes at once', (WidgetTester tester) async { + final it = sender(); + it.sender.push('stone', {'roughness': 0.1}); + expect(it.sent, hasLength(1)); + it.sender.dispose(); + }); + + testWidgets('a drag inside one interval sends its last value once', ( + WidgetTester tester, + ) async { + final it = sender(); + it.sender.push('stone', {'roughness': 0.1}); + for (final at in [0.2, 0.3, 0.4]) { + it.sender.push('stone', {'roughness': at}); + } + expect(it.sent, hasLength(1), reason: 'the rest wait for the interval'); + + await tester.pump(interval); + + expect(it.sent, hasLength(2)); + expect(it.sent.last.$1, 'stone'); + expect(it.sent.last.$2, {'roughness': 0.4}); + + await tester.pump(interval * 3); + expect(it.sent, hasLength(2), reason: 'nothing new, nothing sent'); + it.sender.dispose(); + }); + + testWidgets('fields and materials are merged, not dropped', ( + WidgetTester tester, + ) async { + final it = sender(); + it.sender.push('stone', {'roughness': 0.1}); + it.sender + ..push('stone', {'metallic': 0.5}) + ..push('moss', {'roughness': 0.9}); + + await tester.pump(interval); + + final merged = it.sent.skip(1).toList(); + expect(merged.map((it) => it.$1), ['stone', 'moss']); + expect(merged.first.$2, {'metallic': 0.5}); + expect(merged.last.$2, {'roughness': 0.9}); + it.sender.dispose(); + }); + + testWidgets('at most one send per material per interval', ( + WidgetTester tester, + ) async { + final it = sender(); + // Two seconds of a sixty-hertz drag. + for (var frame = 0; frame < 120; frame++) { + it.sender.push('stone', {'roughness': frame / 120}); + await tester.pump(const Duration(microseconds: 16667)); + } + await tester.pump(interval); + + expect(it.sent.length, lessThanOrEqualTo(2 * 1000 ~/ 33 + 2)); + expect(it.sent.length, greaterThan(40)); + expect(it.sent.last.$2['roughness'], 119 / 120); + it.sender.dispose(); + }); +} diff --git a/apps/flutter3d_editor/test/material_panel_test.dart b/apps/flutter3d_editor/test/material_panel_test.dart index 4427c818a..2bf4f79b5 100644 --- a/apps/flutter3d_editor/test/material_panel_test.dart +++ b/apps/flutter3d_editor/test/material_panel_test.dart @@ -54,6 +54,7 @@ Widget _panel( Map documents = const {}, void Function(String)? onChanged, void Function(String, MaterialDocument)? onMaterialWritten, + void Function(String, Map)? onLive, }) => MaterialApp( home: Scaffold( body: MaterialPanel( @@ -61,6 +62,7 @@ Widget _panel( documents: documents, onChanged: onChanged ?? (String _) {}, onMaterialWritten: onMaterialWritten, + onLive: onLive, ), ), ); @@ -91,6 +93,65 @@ void main() { ); }); + testWidgets('a drag reaches a running game before the level is written', ( + WidgetTester tester, + ) async { + // `HR4`: every value the thumb passes goes out live in the engine's + // words, and the one the drag ends on is written to the level as well. + final editing = openTestDocument(); + setLevelMaterialField(editing, 'stone', 'roughness', 0.4); + final live = <(String, Map)>[]; + + await tester.pumpWidget( + _panel( + editing, + onLive: (String m, Map f) { + live.add((m, f)); + }, + ), + ); + final gesture = await tester.startGesture( + tester.getCenter(_in('field:roughness', Slider)), + ); + await gesture.moveBy(const Offset(20, 0)); + await tester.pump(); + expect(live, isNotEmpty, reason: 'sent while the thumb was still down'); + expect(editing.level.materials['stone']!.roughness, 0.4); + + await gesture.moveBy(const Offset(20, 0)); + await gesture.up(); + await tester.pumpAndSettle(); + + expect(live.every((it) => it.$1 == 'stone'), isTrue); + expect(live.last.$2.keys, ['roughness']); + expect( + live.last.$2['roughness'], + editing.level.materials['stone']!.roughness, + reason: 'the last value sent is the one written', + ); + }); + + test('a level material field in the engine’s words', () { + expect(liveMaterialFields('roughness', 0.3), { + 'roughness': 0.3, + }); + expect(liveMaterialFields('emissive', 2.0), { + 'emissiveStrength': 2.0, + }); + expect( + liveMaterialFields('baseColor', [0.1, 0.2, 0.3]), + { + 'baseColor': [0.1, 0.2, 0.3, 1.0], + 'emissive': [0.1, 0.2, 0.3], + }, + ); + expect( + liveMaterialFields('albedo', 'stone.png'), + isNull, + reason: 'a texture needs a reload, not a frame', + ); + }); + testWidgets('and a hinted range is a slider', (WidgetTester tester) async { // **The hint reaching a UI, which is the whole task.** `roughness` is a // number in the document and nothing but a number, so the panel that typed diff --git a/packages/flutter3d_editor_widgets/CHANGELOG.md b/packages/flutter3d_editor_widgets/CHANGELOG.md index 85413f395..047f29366 100644 --- a/packages/flutter3d_editor_widgets/CHANGELOG.md +++ b/packages/flutter3d_editor_widgets/CHANGELOG.md @@ -1,3 +1,10 @@ +## Unreleased + +- **A slider can show its value live while it is dragged.** + `RangeSliderField.onPreview` and `FieldRow.onPreview` are called with every + stepped value the thumb passes; `onChanged`/`onWrite` still come once, where + the drag ends. The editor uses it to show a material in a running game. + ## 0.8.0 **Moves with the stack to 0.8.0**, whose `flutter3d_hardware` changes diff --git a/packages/flutter3d_editor_widgets/lib/src/field_row.dart b/packages/flutter3d_editor_widgets/lib/src/field_row.dart index bc0d52b24..372ebfb85 100644 --- a/packages/flutter3d_editor_widgets/lib/src/field_row.dart +++ b/packages/flutter3d_editor_widgets/lib/src/field_row.dart @@ -40,6 +40,7 @@ final class FieldRow extends StatelessWidget { required this.name, required this.value, required this.onWrite, + this.onPreview, this.hint, this.faded = false, this.offers = nothingToOffer, @@ -49,6 +50,10 @@ final class FieldRow extends StatelessWidget { final Object? value; final void Function(Object? value) onWrite; + /// Every value a slider passes through while it is dragged, before + /// [onWrite] gets the one it stops on. See [RangeSliderField.onPreview]. + final void Function(Object? value)? onPreview; + /// What a control for this value should look like, when anything knows. /// /// Null for every field of a level, and that is the whole reason @@ -116,6 +121,7 @@ final class FieldRow extends StatelessWidget { step: it.step, editable: true, onChanged: onWrite, + onPreview: onPreview, ), (final ColorHint it, final List at) when at.length >= 3 && at.every((Object? e) => e is num) => diff --git a/packages/flutter3d_editor_widgets/lib/src/range_slider_field.dart b/packages/flutter3d_editor_widgets/lib/src/range_slider_field.dart index 58b8894e7..a1efee5b1 100644 --- a/packages/flutter3d_editor_widgets/lib/src/range_slider_field.dart +++ b/packages/flutter3d_editor_widgets/lib/src/range_slider_field.dart @@ -33,6 +33,7 @@ final class RangeSliderField extends StatefulWidget { required this.max, this.step, required this.onChanged, + this.onPreview, this.enabled = true, this.editable = false, }); @@ -61,6 +62,12 @@ final class RangeSliderField extends StatefulWidget { /// this engine keeps strikes for a drag. final ValueChanged onChanged; + /// Called with every value the thumb passes through while it is dragged, + /// stepped as [onChanged]'s would be: for something that shows the value + /// live, a running game, without writing it anywhere. [onChanged] still + /// comes once at the end. + final ValueChanged? onPreview; + final bool enabled; /// Whether the slider is paired with a text box that shows the exact @@ -89,14 +96,13 @@ class _RangeSliderFieldState extends State { if (widget.value != old.value) _dragging = null; } - void _commit(double raw) { + void _commit(double raw) => widget.onChanged(_stepped(raw)); + + double _stepped(double raw) { final double? step = widget.step; - if (step == null || step <= 0.0) { - widget.onChanged(raw); - return; - } + if (step == null || step <= 0.0) return raw; final double stepped = (raw / step).roundToDouble() * step; - widget.onChanged(double.parse(stepped.toStringAsFixed(4))); + return double.parse(stepped.toStringAsFixed(4)); } @override @@ -186,7 +192,10 @@ class _RangeSliderFieldState extends State { max: widget.max, divisions: divisions, onChanged: widget.enabled - ? (double v) => setState(() => _dragging = v) + ? (double v) { + setState(() => _dragging = v); + widget.onPreview?.call(_stepped(v)); + } : null, onChangeEnd: widget.enabled ? (double v) { diff --git a/packages/flutter3d_editor_widgets/test/range_slider_field_test.dart b/packages/flutter3d_editor_widgets/test/range_slider_field_test.dart index e80537dcd..6b606c2e2 100644 --- a/packages/flutter3d_editor_widgets/test/range_slider_field_test.dart +++ b/packages/flutter3d_editor_widgets/test/range_slider_field_test.dart @@ -18,6 +18,7 @@ Future pump( double max = 1.0, double? step, ValueChanged? onChanged, + ValueChanged? onPreview, bool enabled = true, bool editable = false, }) => tester.pumpWidget( @@ -32,6 +33,7 @@ Future pump( enabled: enabled, editable: editable, onChanged: onChanged ?? (double _) {}, + onPreview: onPreview, ), ), ), @@ -72,6 +74,40 @@ void main() { expect(said, hasLength(1)); }); + testWidgets('a drag previews every stop on the way, and writes once', ( + WidgetTester tester, + ) async { + // `HR4`: a running game shows the value while the thumb moves; the + // document still gets one write, where the drag ends. + final previewed = []; + final written = []; + await pump( + tester, + label: 'Metallic', + value: 0.2, + step: 0.05, + onPreview: previewed.add, + onChanged: written.add, + ); + + final gesture = await tester.startGesture( + tester.getCenter(find.byType(Slider)), + ); + for (var i = 0; i < 4; i++) { + await gesture.moveBy(const Offset(15, 0)); + await tester.pump(); + } + await gesture.up(); + await tester.pumpAndSettle(); + + expect(previewed.length, greaterThan(1)); + for (final at in previewed) { + expect((at / 0.05 - (at / 0.05).round()).abs(), lessThan(1e-9)); + } + expect(written, hasLength(1)); + expect(written.single, previewed.last); + }); + testWidgets('a null step writes what the drag produced, unrounded', ( WidgetTester tester, ) async { diff --git a/packages/flutter3d_game/CHANGELOG.md b/packages/flutter3d_game/CHANGELOG.md index fbae3bb27..a4412ffe6 100644 --- a/packages/flutter3d_game/CHANGELOG.md +++ b/packages/flutter3d_game/CHANGELOG.md @@ -1,5 +1,10 @@ ## Unreleased +- **`replayAfterHotSwap` asks the question a hot reload leaves open on its + own.** After every `HotSwap` it lives the last seconds again under the new + code and says whether they came out the same or where they parted, in the + console and as a `flutter3d.timeline.replayedUnderNewCode` VM service + event. Returns the call that stops it. - **A level that has to be built before it can be swapped in.** `LiveLevel(prepare:)` is awaited by `applyWhenReady`, which the extension now calls, before anything changes; a level whose `prepare` throws is diff --git a/packages/flutter3d_game/lib/flutter3d_game.dart b/packages/flutter3d_game/lib/flutter3d_game.dart index a661ec84d..914a45977 100644 --- a/packages/flutter3d_game/lib/flutter3d_game.dart +++ b/packages/flutter3d_game/lib/flutter3d_game.dart @@ -45,6 +45,7 @@ export 'src/input/touch_controls.dart'; export 'src/run/bug_report.dart'; export 'src/run/demo_timeline.dart'; export 'src/run/live_level.dart'; +export 'src/run/replay_after_swap.dart'; export 'src/run/run_session.dart'; export 'src/run/run_timeline.dart'; export 'src/run/run_timeline_extensions.dart'; diff --git a/packages/flutter3d_game/lib/src/run/replay_after_swap.dart b/packages/flutter3d_game/lib/src/run/replay_after_swap.dart new file mode 100644 index 000000000..704e9f514 --- /dev/null +++ b/packages/flutter3d_game/lib/src/run/replay_after_swap.dart @@ -0,0 +1,57 @@ +import 'dart:developer' as developer; + +import 'package:flutter/foundation.dart'; +import 'package:flutter3d_app/flutter3d_app.dart' show HotSwap; +import 'package:flutter3d_sim/flutter3d_sim.dart'; + +import 'run_timeline.dart'; + +/// Lives the last [seconds] of [timeline] again under the new code after +/// every hot reload, and says where the new code parts from the old. +/// +/// **A reload answers what the code does now, not what it changed.** The +/// world survives a hot reload and carries on under the new step, which +/// shows the new code running and hides whether the last seconds would have +/// gone differently under it. `RunTimeline.replayUnderNewCode` answers that; +/// this asks it on its own each time `HotSwap` finishes a swap, which a +/// `SceneSurface`'s `reassemble` starts, so nobody has to remember to. +/// +/// [capture] is the simulation's present, as `registerTimelineExtensions` +/// takes it. What happened goes to [onReplayed] — by default a line in the +/// console — and to the VM service as a `flutter3d.timeline.replayedUnderNewCode` +/// event, for an editor listening there. Nothing happens in a build where +/// `HotSwap` is off, which is every build but a debug one. +/// +/// Returns the call that stops it. +VoidCallback replayAfterHotSwap( + RunTimeline timeline, { + required Snapshot Function() capture, + double seconds = 3.0, + void Function(CodeReplay replay)? onReplayed, + @visibleForTesting HotSwap? hotSwap, +}) { + final swaps = (hotSwap ?? HotSwap.instance).swaps; + void replay() { + final done = timeline.replayUnderNewCode( + seconds: seconds, + capture: capture, + ); + if (done == null) return; + developer.postEvent( + 'flutter3d.timeline.replayedUnderNewCode', + done.toJson(), + ); + (onReplayed ?? _say)(done); + } + + swaps.addListener(replay); + return () => swaps.removeListener(replay); +} + +void _say(CodeReplay replay) => debugPrint(switch (replay.divergence) { + null => + 'flutter3d: steps ${replay.fromStep}–${replay.toStep} replayed under the ' + 'new code and came out the same', + final ReplayDivergence parted => + 'flutter3d: the new code parts from the old $parted', +}); diff --git a/packages/flutter3d_game/test/run_timeline_new_code_replay_test.dart b/packages/flutter3d_game/test/run_timeline_new_code_replay_test.dart index ead08928d..4b1e9cac0 100644 --- a/packages/flutter3d_game/test/run_timeline_new_code_replay_test.dart +++ b/packages/flutter3d_game/test/run_timeline_new_code_replay_test.dart @@ -12,6 +12,7 @@ /// state after the replay is not the fresh run's. library; +import 'package:flutter3d_app/flutter3d_app.dart' show HotSwap; import 'package:flutter3d_game/flutter3d_game.dart'; import 'package:flutter3d_sim/flutter3d_sim.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -138,4 +139,48 @@ void main() { isNull, ); }); + + group('after a hot reload, on its own', () { + test('the swap replays the last seconds and names the parting', () async { + // Mutation: drop the listener in `replayAfterHotSwap` and nothing is + // replayed when the swap finishes. + final live = _run(); + _steps(live, 0, 300); + final hotSwap = HotSwap(enabled: true); + final replays = []; + replayAfterHotSwap( + live.timeline, + capture: live.walker.save, + hotSwap: hotSwap, + onReplayed: replays.add, + ); + + live.walker.rule = _new; + await hotSwap.swap(); + + expect(replays, hasLength(1)); + expect(replays.single.toStep, 300); + expect(replays.single.divergence?.path, contains('x')); + }); + + test('stopped, a swap replays nothing', () async { + final live = _run(); + _steps(live, 0, 300); + final hotSwap = HotSwap(enabled: true); + final replays = []; + final stop = replayAfterHotSwap( + live.timeline, + capture: live.walker.save, + hotSwap: hotSwap, + onReplayed: replays.add, + ); + + await hotSwap.swap(); + expect(replays.single.divergence, isNull, reason: 'the same code'); + + stop(); + await hotSwap.swap(); + expect(replays, hasLength(1)); + }); + }); } diff --git a/site/content/quickstart.md b/site/content/quickstart.md index fa996ca5f..09ca4cdd8 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10851 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10860 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 34f03f71f..7f6ca4249 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10851 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10860 tests need a GPU. --- # Testing -10851 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10860 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -20,14 +20,14 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 63 | | `apps/flutter3d_demo_platformer` | 202 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 328 | | `apps/flutter3d_demo_strategy` | 50 | -| `apps/flutter3d_editor` | 223 | | `apps/flutter3d_demo_arcade` | 20 | +| `apps/flutter3d_editor` | 229 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 28 | | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 27 | -| `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 90 | +| `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 130 | -| `flutter3d_game` | 277 | | `flutter3d_shaders` | 5 | +| `flutter3d_game` | 279 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | | | | | `flutter3d_net` | 15 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10825 rather than 10851: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10834 rather than 10860: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 57f4bface..1f6350507 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -182,9 +182,15 @@ again from the nearest keyframe under the code running now and names where that run first parts from the old one: the field, and the two keyframes it parted between, since the old run is only held at keyframes. (`reload` stays out of the names: CONTRIBUTING.md keeps it for weapons.) -*Left:* the inspector sending at 30 Hz (the editor has the transport, not the -sliders), and calling the replay from `reassemble` on its own rather than -from a tool. +The editor's material panel (a palette button beside the step panel; it had +never been mounted) sends every value a slider passes, and every value +written, to the game Play is running: in the engine's words +(`liveMaterialFields`), over one kept VM service connection, merged and at +most thirty times a second with the last value always sent. +`replayAfterHotSwap` replays the last seconds after every `HotSwap` and +reports where the new code parts from the old; the platformer calls it. +*Left:* the same live path for `.fmat` parameters, which the engine material +does not carry as fields. **HR5. Editor Play (M), with §6.** *Done on desktop:* the toolbar's play button finds the project above the open level (the nearest `pubspec.yaml`) From 0c6916b2e56419616dcd961b782ae29a77aa4314 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:13:56 +0300 Subject: [PATCH 015/141] The Medium article on the Flame bridge has its pictures An English write-up of how flame_flutter3d joins Flame and flutter3d, with a step-by-step tutorial (Buoy Run) checked against the published packages and run on macOS. The frame diagram is redrawn in English, without the claim that the 3D layer draws a frame late. --- .../lib/src/play/flutter_run.dart | 285 -------------- .../lib/src}/level_push.dart | 0 .../test/flutter_run_test.dart | 0 .../test/level_push_test.dart | 0 .../articles/medium-flame/01_river_sortie.png | Bin 0 -> 1384170 bytes .../articles/medium-flame/02_two_layers.png | Bin 0 -> 72555 bytes .../articles/medium-flame/03_frame_loop.png | Bin 0 -> 91108 bytes .../medium-flame/04_transform_bridge.png | Bin 0 -> 109505 bytes .../medium-flame/05_physics_bridge.png | Bin 0 -> 107428 bytes .../articles/medium-flame/06_input_bridge.png | Bin 0 -> 112348 bytes .../articles/medium-flame/07_buoy_run.png | Bin 0 -> 1240896 bytes .../medium-flame/08_river_bridge_down.png | Bin 0 -> 1418301 bytes site/assets/articles/medium-flame/article.md | 361 ++++++++++++++++++ .../medium-flame/buoy_run/lib/main.dart | 172 +++++++++ .../medium-flame/buoy_run/pubspec.yaml | 14 + 15 files changed, 547 insertions(+), 285 deletions(-) delete mode 100644 apps/flutter3d_editor/lib/src/play/flutter_run.dart rename {apps/flutter3d_editor/lib/src/play => packages/flutter3d_editor_play/lib/src}/level_push.dart (100%) rename {apps/flutter3d_editor => packages/flutter3d_editor_play}/test/flutter_run_test.dart (100%) rename {apps/flutter3d_editor => packages/flutter3d_editor_play}/test/level_push_test.dart (100%) create mode 100644 site/assets/articles/medium-flame/01_river_sortie.png create mode 100644 site/assets/articles/medium-flame/02_two_layers.png create mode 100644 site/assets/articles/medium-flame/03_frame_loop.png create mode 100644 site/assets/articles/medium-flame/04_transform_bridge.png create mode 100644 site/assets/articles/medium-flame/05_physics_bridge.png create mode 100644 site/assets/articles/medium-flame/06_input_bridge.png create mode 100644 site/assets/articles/medium-flame/07_buoy_run.png create mode 100644 site/assets/articles/medium-flame/08_river_bridge_down.png create mode 100644 site/assets/articles/medium-flame/article.md create mode 100644 site/assets/articles/medium-flame/buoy_run/lib/main.dart create mode 100644 site/assets/articles/medium-flame/buoy_run/pubspec.yaml diff --git a/apps/flutter3d_editor/lib/src/play/flutter_run.dart b/apps/flutter3d_editor/lib/src/play/flutter_run.dart deleted file mode 100644 index b8a5a6884..000000000 --- a/apps/flutter3d_editor/lib/src/play/flutter_run.dart +++ /dev/null @@ -1,285 +0,0 @@ -/// `HR5`: the project a level belongs to, run from the editor. -/// -/// `flutter run --machine` and not a game built into the editor: the level -/// belongs to another application, with its own code, packages and entry -/// point, and the only thing that runs it the way its author does is the -/// author's own `flutter run`. `--machine` makes it a daemon the editor talks -/// to in JSON lines — the same protocol an IDE uses — so a hot reload from the -/// toolbar is the same hot reload the terminal would have done, and the game -/// answers it through `HotSwap` the same way. -library; - -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/foundation.dart'; - -/// Where a run is: not yet started, starting, running with a VM service to -/// attach to, or over. -sealed class PlayState { - const PlayState(); -} - -final class PlayIdle extends PlayState { - const PlayIdle(); -} - -final class PlayStarting extends PlayState { - const PlayStarting(this.message); - - /// What the tool says it is doing: "Launching lib/main.dart…". - final String message; -} - -final class PlayRunning extends PlayState { - const PlayRunning({required this.appId, required this.vmService}); - - final String appId; - - /// The game's VM service, for the timeline panel and `ext.flutter3d.*`. - final String vmService; -} - -final class PlayStopped extends PlayState { - const PlayStopped(this.exitCode); - - final int exitCode; -} - -/// Starts `flutter run --machine` in [workingDirectory]. A parameter of -/// [FlutterRun] so a test hands it a process of its own making. -typedef StartFlutter = - Future Function(List arguments, String workingDirectory); - -Future _startFlutter( - List arguments, - String workingDirectory, -) => Process.start( - 'flutter', - arguments, - workingDirectory: workingDirectory, - runInShell: Platform.isWindows, -); - -/// One `flutter run --machine`, from start to exit. -/// -/// **Its console is the tool's, line for line.** What the game prints, what -/// the tool reports and any line that is not the protocol at all — a build -/// error comes out as plain text before the daemon has said anything — all -/// go to [console] in the order they arrived, because the moment somebody -/// needs this panel is the moment something went wrong, and a filtered -/// console hides exactly that. -final class FlutterRun { - FlutterRun({ - required this.projectRoot, - this.device, - this._start = _startFlutter, - }); - - /// The directory with the project's `pubspec.yaml`. - final String projectRoot; - - /// `-d` for `flutter run`; the tool's own choice when null. - final String? device; - - final StartFlutter _start; - - /// Where the run is; the panel rebuilds from it. - final ValueNotifier state = ValueNotifier( - const PlayIdle(), - ); - - /// Every line the run has printed, oldest first. Capped at [consoleLimit], - /// dropping the oldest, so a game that logs every frame costs a bounded - /// list and not the editor's memory. - final ValueNotifier> console = ValueNotifier>( - const [], - ); - - static const int consoleLimit = 2000; - - Process? _process; - String? _appId; - var _nextId = 0; - final Map> _pending = >{}; - - /// Starts the run. Does nothing while one is already going. - Future start() async { - if (_process != null) return; - state.value = const PlayStarting('Starting flutter run…'); - final Process process; - try { - process = await _start([ - 'run', - '--machine', - if (device case final String id) ...['-d', id], - ], projectRoot); - } on ProcessException catch (error) { - _print('could not start flutter: ${error.message}'); - state.value = const PlayStopped(-1); - return; - } - _process = process; - process.stdout - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen(_line); - process.stderr - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen(_print); - unawaited( - process.exitCode.then((int code) { - _process = null; - _appId = null; - for (final pending in _pending.values) { - pending.completeError(StateError('flutter run exited ($code)')); - } - _pending.clear(); - state.value = PlayStopped(code); - }), - ); - } - - /// A hot reload: the game keeps its state, and `HotSwap` relinks shaders - /// and swaps changed models from `reassemble`. - Future hotReload() => _restart(fullRestart: false); - - /// A hot restart: the game starts over from `main`, with the new code. - Future hotRestart() => _restart(fullRestart: true); - - /// Asks the game to stop, and the tool with it. - Future stop() async { - final appId = _appId; - if (appId == null) { - _process?.kill(); - return; - } - try { - await _send('app.stop', {'appId': appId}); - } on StateError { - // Already gone. - } - } - - /// Stops the run if there is one and lets go of the process. - Future dispose() async { - await stop(); - state.dispose(); - console.dispose(); - } - - Future _restart({required bool fullRestart}) async { - final appId = _appId; - if (appId == null) return; - final result = await _send('app.restart', { - 'appId': appId, - 'fullRestart': fullRestart, - 'pause': false, - 'reason': 'manual', - }); - // "Reloaded 3 of 812 libraries" as well as a refusal: after a press of - // the button, the console is where somebody looks to see it happened. - if (result case {'message': final String message} when message.isNotEmpty) { - _print(message); - } - } - - Future _send(String method, Map params) { - final process = _process; - if (process == null) { - return Future.error(StateError('nothing is running')); - } - final id = _nextId++; - final reply = Completer(); - _pending[id] = reply; - process.stdin.writeln( - jsonEncode([ - {'id': id, 'method': method, 'params': params}, - ]), - ); - return reply.future; - } - - void _line(String line) { - // The protocol wraps each message in a one-element list; anything else - // on stdout is the tool talking before or around the daemon. - final Object? decoded; - try { - decoded = line.startsWith('[{') ? jsonDecode(line) : null; - } on FormatException { - _print(line); - return; - } - switch (decoded) { - case [final Map message]: - _message(message); - default: - _print(line); - } - } - - void _message(Map message) { - switch (message) { - case {'id': final int id, 'error': final Object? error}: - _pending.remove(id)?.completeError(StateError('$error')); - case {'id': final int id}: - _pending.remove(id)?.complete(message['result']); - case {'event': 'app.start', 'params': {'appId': final String appId}}: - _appId = appId; - case { - 'event': 'app.debugPort', - 'params': {'appId': final String appId, 'wsUri': final String uri}, - }: - _appId = appId; - state.value = PlayRunning(appId: appId, vmService: uri); - case {'event': 'app.log', 'params': {'log': final String log}}: - _print(log); - case { - 'event': 'daemon.logMessage', - 'params': {'message': final String m}, - }: - _print(m); - case { - 'event': 'app.progress', - 'params': {'message': final String progress}, - }: - _print(progress); - if (state.value is PlayStarting) { - state.value = PlayStarting(progress); - } - default: - // `app.started`, `app.stop`, `daemon.connected`, `device.*` and - // whatever a later tool adds: nothing the panel shows. - break; - } - } - - void _print(String line) { - final lines = console.value; - console.value = [ - ...lines.length >= consoleLimit - ? lines.skip(lines.length - consoleLimit + 1) - : lines, - line, - ]; - } -} - -/// The directory of the nearest `pubspec.yaml` above [levelPath], or null -/// when the level is not inside a project at all. -/// -/// [hasPubspec] answers for a directory; injected, like -/// `Documents.assetRootFor`'s own check, so this is tested with no disk. -String? projectRootFor( - String levelPath, { - required bool Function(String directory) hasPubspec, -}) => _nearest(File(levelPath).parent, hasPubspec); - -String? _nearest(Directory directory, bool Function(String) hasPubspec) => - switch (directory.parent) { - _ when hasPubspec(directory.path) => directory.path, - final parent when parent.path == directory.path => null, - final parent => _nearest(parent, hasPubspec), - }; diff --git a/apps/flutter3d_editor/lib/src/play/level_push.dart b/packages/flutter3d_editor_play/lib/src/level_push.dart similarity index 100% rename from apps/flutter3d_editor/lib/src/play/level_push.dart rename to packages/flutter3d_editor_play/lib/src/level_push.dart diff --git a/apps/flutter3d_editor/test/flutter_run_test.dart b/packages/flutter3d_editor_play/test/flutter_run_test.dart similarity index 100% rename from apps/flutter3d_editor/test/flutter_run_test.dart rename to packages/flutter3d_editor_play/test/flutter_run_test.dart diff --git a/apps/flutter3d_editor/test/level_push_test.dart b/packages/flutter3d_editor_play/test/level_push_test.dart similarity index 100% rename from apps/flutter3d_editor/test/level_push_test.dart rename to packages/flutter3d_editor_play/test/level_push_test.dart diff --git a/site/assets/articles/medium-flame/01_river_sortie.png b/site/assets/articles/medium-flame/01_river_sortie.png new file mode 100644 index 0000000000000000000000000000000000000000..d894769a373190f9053d2a1e8e875fd0483d34df GIT binary patch literal 1384170 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%0DD!!B9zfKE4HzdO^-XosBr4CC$~gn>Nb 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 z zM3^XRb=>vgZk4J8jH43KWNMhgIxYd#VXX`$Vcjf&j;4?ajW>9xSBH0uI}}mM8K_ii z3u!Sdm9aFvqHNDPZm7l91Bnc&)g6eyikh5?R3Za$T`Dmz=XIQmBJu-3Q0@92=Q7Ng zh=z^f5YVa^P(btBazhTM>qXlaHz9JJtQnM*CbOaqS7Hy%0ET4;wev;l#^1})gbis> zo;1Z+y^h1GdgFrw^;!eS6r(E(bTCy&6yt|G@z06ZLdjNA1?c_DyF1%!J4enpyoxw+c5c|{7Rv@7!ES`$EPkny7=hgql+J%>d

tX%Mc{k%3N7=Oe_t1tfQ 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 z


6Tx0S)k>!17WkI}#IVOYaV$W;g>V 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

$(ZEBu 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

)4c9^j{Sl@po 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

-%J4^`{yIJ8-okiT;kfRl(o*1VOAm-0(YL&EPh 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 z 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%0Afs~Lyf(K{DGTr+L{(nW#GANFJK%C8YS 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 ZYR>u4tzYCMRJD^RD{3edJ$MrMe*rZka?1b! literal 0 HcmV?d00001 diff --git a/site/assets/articles/medium-flame/03_frame_loop.png b/site/assets/articles/medium-flame/03_frame_loop.png new file mode 100644 index 0000000000000000000000000000000000000000..9cd7452629a296540d1816bf82b62d0b75dc6ea3 GIT binary patch literal 91108 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 literal 0 HcmV?d00001 diff --git a/site/assets/articles/medium-flame/04_transform_bridge.png b/site/assets/articles/medium-flame/04_transform_bridge.png new file mode 100644 index 0000000000000000000000000000000000000000..8ac44f07a1cb90c55f3617066b90dd3306772357 GIT binary patch literal 109505 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

6e&kiH_Dw_T@*G&S+*M8ynfoH%!OFt&NQbvr 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 literal 0 HcmV?d00001 diff --git a/site/assets/articles/medium-flame/05_physics_bridge.png b/site/assets/articles/medium-flame/05_physics_bridge.png new file mode 100644 index 0000000000000000000000000000000000000000..d9df761ab16867f438c8f0cf2f629c60b11e5122 GIT binary patch literal 107428 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 literal 0 HcmV?d00001 diff --git a/site/assets/articles/medium-flame/06_input_bridge.png b/site/assets/articles/medium-flame/06_input_bridge.png new file mode 100644 index 0000000000000000000000000000000000000000..cd0b3e0d578138466c2630d5d7eb68c96952c03e GIT binary patch literal 112348 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 g( zd?udX>@5!Kp5x{sTHQpr+x-MSfguSvo?eB1&*;`IuiCBXM?&m*xv*gHHKlxU7y~Px z9`T;Fk*od0Da4?=qKydNNPN+^Zx{na1E^f9ux-K@A(~IxQMY#Ps5xH@ zmtj>#o67C*F-$dWgkS~Dl`$7jX+6V67xrukP>t>}XnBk=VHK z-uLA&$`1Kl3%vO`-PISQ(dZcFaT8_X+deF13|J4v%K1F-2<<3?#IiV9Sneqi^E0i2 zCxlsqZjQmOEZ638(5+4V#Q&VgxDx(sYMfFR9pE&cytLe#=O~uBQ-TFr4^b%Vpa8Sh zIh>o_W{0Kfh_U2`<>p6nuem;hIer@_Snjj$!Y3x|_P927zU`gn$hRcZPB1pHH|Rvx zT@ZbgUqn()2>kUcyo$?m-fOAGC_X;EKLlBZOEGIY2@#tfhhG0|c)MTf;Q~gKjbmb( z!wQdV)V)eC-1i~sMxbzx1zDki!+Jq(_AVTgdZWv?-SN-h&sH%MbxA;$4d(ermg3dD zW<+Ees5YT+G@02SoR~#9U4%M7>1W}HKH>{=TFTZ@B;7cKh5!(uQbX=td&p6P{lR-Z zL;};R&GwNr@}5cpg=0yKDiup$DUUfz@N@zpWp61h?di$mL;X+<9{yszJro$GL0_1J z2_1DEbH2KZkpM;dhvcU|^W^>P*3#k$SnB-yf$RWu?r^MLl@@*^{fy zc1Giw8!&pltPn>}WwX1n!b#oS8W+P*05l!dmvVX18+ce_rz z8tJIgd@Kgt7UO{k_sex*k1YTWRg#z6?}>AVG(S;)jcT%Qf4-qO!#>$Bw{f0~d|_Bc zR_EkEVS&x*_PU7Lar#LHcGY%+)zSK|^7;Asag^2y#R*yP0I+(7aQ7IgC{31ebMvQ> z^_xgTg1vf-KT#s^0SiHF@J|J6QAS3&+;22ZpTuIDnWU1ZQQ}P-{w_2?Pzr-qm83iw zrDixJ`2UH0kZ4JJ#n1<58@FoqMTTHz^J^_BB#=QR$0^uW)Gt5@pIP7 z?~7=%hJGgaBFH=PV`5?zi{H0og%m>*W;%IOR- zxZ($~Dhp7ZcHCd2$viS=e}A6X;>7&6-tJJ=zhw_ns}{$04-T0q=diE$TzNdDjuFE< zWKdeNsm+(fVZ;Hzk0y4{)?8ld^E7&Bb-&^&;8tk`@A3i&(x^9IH$Req_cjBIx%Qc? zLgu~sFr#W$4{RW0<&@3$7b!i>)YuQ3(>cdNYfg)qLs>HD+l~RdM>oM@QROqLfyzWm-EMMV0@V0?Y`k@{#$ zuTon_IVOUv+~hWqRx7IJ_$B6)<&HlFS0m=#=XPo{seH3#dB{LX@0`U{*Wp9#lsZaR z=tWg*coiyiM$H~k0^$E*>@35g`ob+ffP@MHN=phTAl*HL2n^lQB_Q3M3L@RzodOEd zB~sE20uJ5XH4NPC|9-e%ua6%vW**q*%zO5`-nD*v8T}Yu^$M-xVO_85YHw=-T->!<6?>ry1K;2+&PNx6T9rv60X?O}qkDmc(h)es$ zw)_!H5zJ^3l{$4s6@q8+aiH)VuNH(xB_tfS-?tfke;PaxYvq{UiY_3*FcmvI>V1CD zYPLK5`7zPjQ2gd93fkV?o!D=rP`&gM3a9;@DLCSSkX19|Jx@`(7O--V7dPu?KOytE zyS+Yt&UY_tIo?23{9!nOyz%s6ckwWMq@6_6=SoS~5SZ(JrhJ%d+t-C|FSY}X%bAEp zQQ=G6ONBJ~0iJW98zqbrvFM^wcLGm~*T&YeKH`GUuSUGJp6n@0`0p~(uu!}gO{}v> zj3?wC7iZg4ZTAZ!BUKvdyeXTd#u*}hKMb=)uTR$JznPWOg%rvqVq-u0TVpX&r15(0 zhdnMN`fwqY`wju7dYE$+8-AaQEJ5LeUM&UY8sd=hIISgEP5_Gx7R22iervMyYD`51 z2o!A4;uk-e?EikF;pRR&?Jz{gxUx>Gvjg@Wusp+rIiqlj}ftt8b0?a1# z8XAkfha!D!OY3z?=CfaLDEc2utiDuWACVS`ygL4JIFc!}2uyKSwN}r%1NkdWlijr{ z-!;1J&jFS2$?b|=D+ zcI*YK!-f3bj=(Q3UWn%kA{hz!o9xvv=9c7h+0daf4mV*Cg9Ycz~~FT=j7*hZ}mF&%NFuKiH1fY z55DN-3ZPJPu(~bZUUN7;q?b$TBHDfJ^AYbwnAOO0of4})wQ%=ZvYBLMbd=;}${iIa zErxNw9Sg{V05r+MGSx=Y!2Zm|yflNm{kiq|>B2Ncjlvs*0)(dQxw^`U*6Yl6oyJKf z<-cO3401eB8{JdMi6ez#%5G~hDIPf42Ydc2UkGY|xK>FK&=a>$5O_I3Oq8IP(f6l% zkf18X!@^W5zCbof2q`JU*BH{MQ!YP5q}tJ_yIPQ{1*k&0GIf5GyW}&O7(pBb#td`S zk3Pk;yIfbhKF3%a96Hu>`g^){go(?V<>_|s)^U<(-8#{|L%byu7Tx4OGKv#qI2{_& ztMT_zXfx?}yd%>&dognWes zGPQF^CQU~n)wLXQbv2&oFHDaL-!5DK^DuV>}-t&7i z4mRzQ%8!*(`=gO~fmURNYwM&rWlweT2EW8xG{?ukzlT5y_|+I_)Mo;nHkGnDKC7ap zZDez4^{PA*2|XqsDK?mK^^RI{lWbRuTVbS63LqDIs8GW%g{7h}6x*(xY+vN@^ckLn zsuQ|1L_N)yhXMeCt>TIms^-7!N zpbw_jeRX@anknRNKA57i9myxyGNa%wAi6if#>wP$GXkx*g*Ta3=y;xP_#Q2F{PW5s zF<*;U85BthNQ5wCnH0jbstkN*D&c1R36&bnyd-20NH3Lu)BC@;tFF<_e(~n&8k`oL z?ASvx7Uh8gQ674>g07VJXWQBS4hY{5h>x`Gk+#pAew=8SbF zEz+y);%B|lhoD?tmjc4W`R!~?cNk&0A#hdf3uV8315u7z4OuyW7as!OS~837LbYW) zlSbK2u;amADpO*40*lr`2NJPWxLc~2j0iN)=dCns_gMbbuTkFfM%u8^Nh+@3_}QyA zm3*S(0!1+#1!7x2g*09f_X9m6BcloPAp=W>6}M(28miSy>8V{ZE4^G|e>&ek=NwhA zNSXTgTX*O!bx2;WM@c0D6MZN{q*>{3VbO=nZfIvJ@wHZz=iTjE=rfXBN9gTnrJUA^ zqjA=&AcyJ-apaAV`yM%{xK-_UbCvqdY+s`3x{L*7Ek4K<6^mOv|7}(C>!`tcvi$z^ zhb8S7T&mIgc(C}%jJmuV*Qelennd`g@Q<-E{N^SaKWDC+8T3##zaj6%k1m%?uGFg3 z%M!KqLbxvg!+9Bvtu}VC}%Oa9;zL(;`B(OeJkLBnmIDSpOA)N5J7rd3kx* z9PsXK$G@_kDYvNCsnTooIlcH7Ti~^wjr+hP!EUwp=iAW{`YAExpZ0bjiDBiw)?!_3 zMC2I2=L~(W>3Je%-f2kXSi2&AXwO-zc70C2E>Gggh*Eb)H-vXqq+s|4DF@+8gpfUO z={)=T`9~@d4_1obODz2QUY``zFoB7QWW1qJc2tR>9Ib7G+~?}9FCt1V37f=S;pHhR z8pUFU2_dR0DlPu>c7$S{$4!HCcq>vSPd91N*j1 z8jJH&U)@Q#?!`~?3q4PtL#_B{9x6VvC5A!ljzai7L{NW7PTJehPwW_Clk6qNA-qD$ zFk!{97wJVG^Q08vLEATYgG5g(#F**vh$_cc|N#%U(&N{#d z66iC()De3~6n>o-q4LF5vKoOxlc3tSA9&4hw4V46bI)M2mkQKRux2qEA8AOld33*J_*cw{|$QwP+i%%!LvZ%WdWQM%treAWcxe2_IlA}-*q-h7OVrK*LnL-*HKna64S=M=KNF9cj5AW#(EiO>G)7?Rjn6ToD(*IF>2mys`56F__Zt3%U7R!rgcI zEHTm~WT(sDfWJdQLD>)#?0I+D@Q9G@y7THd`w2O}@6DOjR5>w&UeMND%3)i%R{cQ_ z&U0`tyB+M!qM|>JqU8D~N^Y>|B@|>{8*K^Nf3NdG?1K%`l>I0*wFPVEz66#EtU|M# z8VlH_W@USOBUJZz7HPcjZizg6-Pxa9m!Tcf`%My~esBh9qy5ZA7|3%b@3LZDeCB1I997qDym{P!L4Hz)YlbYQ2b?Q}2^E?dRlusr^BhH=qfk$Ou zE+qQ$^z^jt_v-Qc-w~wGw&^tjIe3MKS>fJERm#TQ)n>`8`huW3$;ilz7_>II9vT#C zQsgzDphz%KQ$v;`rZt5}Ffq_)_uUL17asbN7&f`2?$6hWcpUv)T)YQ{I!wRg?it#* zdW}xq=^|XFMB&}(ISgUqety-=7L}P|P^fI2e6xR9DUASduQ}b44wT)?eiiqbFvhXGu zlcNR@XHERxoE>a858(|~gWIZT-gOndjm0DSe%jm(q~f?hw9xs_?00e*)%4{!3_OG1 z@UQ|Ue`AM0_sZj(l&8T>ZL+wmpaR1@8rP=+i+tWrfLRc zwn0HC^?+*eLiRBR30v&bib|GsW=wyyGj^hBLgrkmf-Q zS>X=-*i=i}PI#t#o_D>1Pt9@vKO1MyJ?GX&Unf12=JNdyAx4^@Ren4c$Qx%toX!}! zhq9ay5=$!cUBojSAk4|pXN2!7+x#PL}KJ0!payxV7nRz;#1s`>e#3k zga<|jz92Mv+A^@Ltf3J{7tWk+_{QdYn!u#1e)T)Cm;kci-c{dGc9(@6KJT6%#*$y` zW}9k_!!^yZ7#FPW-PVp|p^65zPErW>#gTC$a8rFqdpY2=kt#y}pqTK@1h)!7EFBtN z=RVG`BqKF*pV5afFc%PhH6K8hV@VCmvF;L50kAYYmK-kvDOr}!xs7r0k!KRW1M(Qk z%5<2C#ttw8ug*Hzd96+jdFo@c-WZXi_;- zF@nRs)&kGo3WR4JP`Aobc++>8f3h)G}qXq^5)6d5vx#+887`9@4NTKc+c5 zSyBnYQ>;S;b0Q55!UrAml!KEDg^I<`P~Q2@6)@t}4|E^5RB;;Bxs4KZugWU)kLvK) zdVvNA;0H7-HF_O{hI&Hrs&y*NKcy9U3WM>90+Pmg-y=~KhQ-@V!o!^$uF9lS5#TvW zkhfbpK_S6D5=f0Qrjh&@@+KVJ10Q=xS>twYf*QtH_YPM<-_d*V^W_H4)3dX4E#KkAtpN9@Z5cy< zN2j#Y8a9n~reiR>qOZPYH4lsKpcWd9mu595Hmych0ti5iznEB+FMg8_qxhBb^kju` zLYO{1?c98XTVR!c7+W>9jH)OZYF~?sSgG-J-UQ{Vz$7xQX`rw5Enz{}WHDBog{ejA z{Up=y1~06XbmP1`&05pdwx@gKZ&Gz7I+sFF^`LQ*?Ngcun~zTELU!wAR4v* z`p~oW0Q=7is%x{MsiEF2A9J}GQ4WzGxCdP@xu-xJe@%S z?qP?{;HO6WxgyY>E$C}LxTR8;W7N5x0|_Bk%HVed(}h{*jkVvIy}JQfjONiOv%24l z=kDL1??lYX86GiHO!yw2A+PDxWMLte@9$r{5|wTJ9mtHf^tf2@t6}}Y5Z@xR);3O= zmqDe=9VywBBkwOWll1&xkclvrZ(efwB#++=|_H=5bO2bv05@8`LwW3GZljH ztI9`9f`46!(QWU(`t6JuWA`V#XQTqAH)lsjNBTnUKxOsWt=n%rU6+eI35prmZgEDq ztutv15+2-}s0^kFoQ|&#NaIpPDari#W;v0rkWR|_>Q3@>@?I04#-PC&F%3KhlpU}GTPozpc3KKeZ8 ze055+Or=ipD5p_F+D7$PJjwroR33b6fDdwJ{YTFLIi*b4XS&ae&6noL8@u{u;7)`p zrmB9o;#r&s!SA7#QFpwbkz38Iv&E$leplwQ4eSQMgxx7~=sa@dMcokpbo#3Sh{3c0 z(eN!6`Ww5&PukWtM339s9QB*PWBw&(ht<=NN)x zteEQ~sZ|nVT0MPbj2|hMfPVeIo17) z5)3aaDk35>d>|X@Z&dl0?K1k_Fc|MwD&_dULK+lfRExSIb{?aZZHmNwxAQ8<=n1Xt zm9_25`+fAW(lY$OTlz15IsUurjbPo@jUfRHsMN}Y6I18@>Q6pFQZV&-+El2>YBallm9?OOVGb6h<1mlEw|#&!S{=q^3OC1)vu|D ze^FtnAAJ!ixbGYY;pb(v_@}uAb|gHyN*?Yto7kF6k&}ONKSxRYABG1!8z+CHh6en1 zMgDu+!v9@`|M%B-_X%NISuWxerUu-g`e9$ylPg^udg9;4D>)#9-n=Klpjm4#;yd0G&qYKZ?b)SVn?VHFiMh%ANEeb4`S~b=J_WyA*R_ zLVN{eYyE|n>_xThd+?ugDWmNP(+fSWC%bNU+tn9{U*sjk+ulbNohM&VA3d682Rb2E zYHBaZ{bOfkm~Zn``^8@SxY3~L9Rp3u=1}MTU0amVUH`E#^&1!XrBao19g^xpy?^UZ zER^g!#OChjA`NZ`x&on%Uk)Q%6}q7|k|PyQg1y%`D&MkKsIBF?!+(|Z?fp9Xd*G4= zm+Dba%*t`g8br?{TJ)a={oe#zc#uA+^W>^ewIPqJ#y=QA9rsRm=Ej|L0gz1|~ z?GglWLG;^(OjxgUIYyp!LJasDyf-&^$^-6uGl&jlcWL_`KEwb~eMu7FLCc*vq)1R* z`i%2BnQFFqp1Jw>@{Bp?`^s@j>%zK&$KQkY@3aMXR%(RZsd1lJh!pEoV5+#J2^Cu! zNHVZB(Y?=>)(uAw&#S*rdvufyQzp2yUA*yZ7bRBAau6ivD!xmI%fKd2)(?~>ajxb$ zFZ9o0LsNzc%mzbLSHg#XJ{ckKsi4Cb` zc^}8o#3!htaO0ND9RfRg!`P^hnY(0WLD|R z!)~JIg_GMZ1|sgf0*s;W<1HSEL9(Q!7aKgpCw^cb_E)pC=Iw3Eo%IhV>c~qRz#ERY zI?ysz>H2!3D7>4^94_S0hMH>W@W8rEF2q$-B?k!5!zmPA32P^9QuOBN<#Xt1d=fY7u`vj&yDrzo1#D@z+@0@jAH{t#}o;=z0+qjzu^6Ph`Q0OE{RtEx^tzFc>qY` zrSAQln18qlse=l-Q(F91XXNE`%}pohcaLaW^LS2=}8G|me6x?dOlFX4#8_QfVs z3wY|U`CHYQ4HPjoZ2e7Uq6pPzw3L_O^(15tsL6Nx+iz??1oxsrs;8{N7-|EypV2jC z33$^vCxXO$w{W&5F&+sC1A;lkB%-vk)_P`m+et;glY~}wI7U_i%f6OPuYp~F(eh@m zQQa}UPPy%nlY9R&4JOUIIvCMza<;$dezM`G(>1hzJ^FnBBuMP!F*_BR^;@6K zmrKrQC6vx80yXm0Utyj5K91?R_zD9O8C}*|;6@=3K5Vp&|K9^cj zqCqBQhSIgDgN#(VGK?u_xpubFy*#`*Wd==)u*Q+{6vv_oer^zFSU8% zCZD#x-Q;|>X*RC}MXEPAEXOYTS$OD~4J08!=vkH^mf>L@y{pGDsV!C^8|f&X1o_C} zVtti;yI(dADF&pT)esv8XPy5E1J7&0sjJA%@7*wAR>e4qsA8(KJVtwDXa%jSTV<2IT<9TiR$MYAhX7Swz&$kgY4rQ(jT4STwpIs(#jo^Mj^o zn!Eh(cVe&!X5pIsnSZ80C@SAkzClN>XSF(-fqM;_Ddg&I-u~ud*B$hT)rGJP7WF?u zu`py1)=C?^8N1V?%7P7=KDD?ArDl++dl#@;Z#Tyvs~S%Pa4F72*H<5ryP6d~4u^Kj z5})5gpM882)X*aG6hOS-%fW!!?*N2fx9P1gno^B#G19D^ehs*D+oqTTR z5?@D=|Djs47)fo1Oc}~=e4{pK_Sx=< z(FW&chg^xIfSCFz?M~-- z$R(71QL-9V^Vd=fw|lMk*OqGm-~JnEsYE92UzB)aE)?M=XEhe>^66e1XWK{L*!3rv zT8?`S@X|?li($Ma7k4QA5b~PPQ_g6C$lAxH{ za?QDl>6%wcHVfafK3FZs6;eSP+D-aaUK6vejUlua+fq1)MR~`=wlQPCpfurB7GDTTQ(kWPXFJ$1M`MXf-~$>XHHMax$?m zG@BPc>I-!qEY@KCTDd+wxOw!ZPBhx5&Z%$ZvBk*0dHByPcpL*p@LE?M3G!^a%uFtt zQrIZ5OZanGKx|V{6uKi=EixV}^;{Sa!cb>-6J&<%-)1O#26A|iju{D+-6BEN2{}POg4nZ4N-K|>S;mE9Q zlK*0+QqR%okdjjT?$>7&=lMDtgBAy4k9Y6hcg6p@e*ECU7l%0dyuC*_($3cZ-oz?_>-7Z3>%C(Ix#Qo;SJ(H8T^-jiD6L((O<7@$CoFw zH9M|5f%QgyaxS8KDWA=|-}OBoA0OW?xg1t&C3}4QFP?V)cD|jkk9(%?S#L6n_Q~0{nsdqbYs4xx z=fg*+K~)AV9?NHp>=9C@8%eBc8$Gqwz#X|WS@DJWh_HO>PrbE8K)e5SzeY)}Y1Dk9 z>p;=@u8kX__O+Iag7s`s#q2tl6+v7=TuOsm_X#Z~?%HtT?pf$+WkSMe%QgGR^RznG z)nGt|dD-1d2r3_*M2mybWhJJ&otlDf(+BZCNN;*r!jh+7XWCSAo&7U*&hp@=D?%?V zO4OWRx%C!B#wyWfi9UjUvvBW*CGyXvkrUUuO~I%hmZz-7yqCQsJ-IAS3l^X>Uegs* zn9!-Ue4GF&-0pcKUJ%@I_6Q>oW$<c&h&A9OrdYmGSHuJO4(0ozCX&M!)-!ldDGbh3F2CK0x7 z>4z2%Y2Vk*viF9@(P`5$hkzO5-B#xUR58P2cSFIT+Gf6?O|Dq+@?g;)j7eR)D@v+#9x=(QGvdMY3Y zk6D|yN*`Y6Y{At5*bcV(?UZuWTk#@~0z4d-_p}YRU3dN*d#V#0N!ZBCZz^W;+Wy6( zc*bS9(gpHt+RRBVw!y^Hl?Eaf7dBmwF=*OS_ZF)bL1f!=)++4Nrf2#P21F+2r5|EH zrXx^|e7N%q?mxrXpUim~yw)OOEYtF-Fpea%SsP2<^!-1_&nEmb2%eq#9Op@a{-c6{ z$)ROF-{)-k%L3W8@!mT+S%U#SF&VB1aYH`=`vbk?c`o-0P#T0@z5;Nnv&2xX-@(R% zSvJLXk1NS8NwZmZT`F1EXDtI7|a0s>!(!1=41 zmW=nIJ_Hwl=mk7Ts`}?R{cjkDM%Ok*QUg84wq5+7c7Jo&+8P{=$B~Fjp&!=uW^h3| zZ5M^~>v?;C9RLY-k7dA!It6zGB(D3#Bh(%cGEmVUiRI!}$`a=CzlSRm+I&YHRRJF` zG9e-cU}wgnDW76`Buv!x?8}oMwi6q_y2BlhkHH`_^Ad6R4v${;$2(?l35VV?c6@5E z9tgjiZ_{r^@Yz#~bK4pXa_mpWO15ne-Xq;7)JNw3=&{aRQTbi1oQ_13i;`1PF68Uh zQ|Q)FtK^Yi!nb1o^~0)8C!jP6{jk5^y3OsA{qNbf@-);2Z?C}8Y)_==wgDZm37n%rIK&A(6a4T&@xkg zDUG~Afjnh#j8)1O_EatSpaS_x4&4f8>$yLY1mAA;=f?ogW?MgBQ#q*Sfv}X<5)ryHw$o*gsUOr>rjwA!y9FQqK$OOd1g5+-+ zQ<2!&xeZ$%me9jglF%oy5y}Oss)U>get=r$2t!e zDm5Ba&*G&w0)4&Q?w5DR62>ou>cGg%L1MdgS8I_$ZD%{sJAov4-m)Q6UG1r9-oJhS z_L=Nysbl`zv-=}7*!GG*jbN>vlxrlJSF-yYa|Tbm>dooZVY91^{lc~)tVE^sSHK7)o&E&$TU9AVD zU;3Q7B6lBlWQ8uNeBKGc@J4;1*0Z+fB5|rW z`@ff2p0~*|<|g#K6|FG%-jZqyE|M_oRIE*1s;Ee_8Vskt#w=K#*II(RyJa)n&v(!Z z>bf@iyiiWm(_y5=_IGji+1B6Krywm6(rea0zOhR-RqDD9xR_Z~ZIt@EkzumOjtS!y z#<(zo-|p9gjIiw=p{o;9GTkTjReGGeTJ8c2^q`Yuh~eTF;O%M2wI4YEe=JBCOc7?3 zqv7+fxwD5V9dAIDV(#<3-3Q6OYPIeJCk`+c7C)h~QO+mR5Zh z?TXj2s!d0ii&k2dYff}(Fpz&4N}C!=x@F!IPpmGi$`G-ARA4vN--gOjb-6t@^g2dJ zi!GT^b!%-oFk!q3{F`s>b>o1C+{=NLW# z-&E}`@i0%RRKlcAN7YK8~6$*ZREfj;fOd3HuXvoxbINsls_DcH47uq0mxU+ z5n?1riPv%!a?>ANZ&hJP3AflA_*Wr?7Nf|`@BUr}Qi4<)$NXt*YqM_4l$fk+UV@u% z@z5`Au7`zuZ2Bd-wN5^3T(C$`C%aZT|0=?^G4+MmGWou!!f^Jd*TV_ri1-%g!Q142 z!7fTrRe0F!Tn?KZ2YZ|R1kon%X4TTC;B`U+)z{8y^zvteoTNv~U~mY;Vl779Ir(Yw zBOC?&8kb`WU#Q)5&O*zgU+!d10_9vyIVcV)(Vau%u2!cPIZ8L)>F&Yy9hatWdAw^x z=`jkqFSOqb!3cRPb;KD(Jhy%pI4-7;%7le7X|vx~3=j_ua{Je`)msDZ6`WE99a?`vl8y?tA81KkFvtO)ktn^@@PatU>muql@&y~#+K0E&4p_!eF zCRA_1u#}8MsKTlY`IC*m9zA*KzZc(s_uYSw3UN7tytMFT`o_={_cV%1=;Cy;l)Oh_ z(e8BMY#a0P&>sH1CRdQ3s+=h(rgArJeqDt?Lj%q_6Andzh#mM`zf?^B?!S5Qe5TSe z=p`LpisoEcUQkdF0kiT?2Lb+a5&w^zmT6m z3R$#+lR-S=o&`!uh7+)UEwgg_B9o%RIAQjbMp6TR<*!|R?<$8&=IS+Q2eir!MEu8A zvI-uIL36YY7et!fqobqxW4T_9af^kNg$-53rZQ$z)|399Q3#hS{eGDgmPox3KVSe|Pvf8Slx>C$xZ-~6NUS2-R(x2ni^XJ?#wtfd)9s=yr%P*w36#~zW(ezlg8c)`dflefr;1CHogpQec zZ=fSk0lms0_63OSZG?FMI%69p*G4l({-W3J5ZD6n*iEH1WTtF}6p;gi)G1q=%)Hqk zi9NTZZJO$eXPZapQ^4qk&AV^0?_Mr~0g3vLHxJ>xo@vTfBpB51NrLNXjXK2=lFC(W zFBe)77l(_r&YNJk+@@04pHQl{e}^(Q1|IO6BiWdneR70KBcjN>Y+)UKYjX)zr>jm( zvC<-jjBYY2sw&GgA;yjsDv#5*J$LCbF=86%rr6*RjncCBiqcwunP)lYU``Uu5C%xN zFPmT-hTn`jQc_4D-)8rrx7_EG%Yn~ls ziQ_5=;rac!j|URPKPxALdsahr}!aa_p76Aa-bdS0|8QW7-VK;mqr4L9N z3F9eSTY9;<>Ue7OUm07d&5~qoY?vVoWjbJ9?IRUNYWM0_3Mw@WT|9;*X=!OGBm_&# zsd@@}v~-7@D${t(qRdcxO~|^}$3#NiLlR&BJnH-Pix@#7eW4wmCj?A-e{9NY7p>k^ zc%9b{B)@(024r?lg%2-0AU{`+37FNf9_JmnVLZS94!mMFcvH|8fdjxv0#`03q~7kY zN0vgSmu~XFI+I!*^D9`_DgcBu`CL~IN&n)rU+{T)k@F$-B!1jKI79IjWN)#a%@hT0 z2l=h_&4|eK^N#U`63}T=p&jjB5WJOcwO2|Y`RF_L>vLz@mCayG2k5cp0O)8mV%Ul` zx!f&Na8c>nv!AVfC`IJ5hNe^1p51o|lEJEVc)6%w=Tgc-O>Ms*{E6YlingrGNf0{`4AV^-(xfB zLm~<_EAR9R*|4&woyz)AbFMpQS2?dZ&j1X8pFFA=$VvSEZ6Li7AmE&4=9dk08USx| z)t_b;AR|s2YeMjPwh-XqikE6hY4freLt(B}94?T6 z=MMWB^OA$$a~`X+A>7qog*0K;jj#VGcatIbX=M(gnyOd;lR+T0%ulIe z^@tSWq7YBl$BsSd7;FsAZ_wT__~>AX%27N<@N>kjI4H$7jwCp>ygsd7ugQiq@BLki zCxE>Rv{(=m(c^VF7b~j3tS3t)36+wH$=_EhsKM;&pPSk|c|zXH7`9UDH;8BQr@jzH zBCXOIu%jHqc@GCucq|4g;q~_LoAu3X{N7?W%m{qt;TQIc4R@`T`lco(PMducBqSf^ zYK$X5rXKM`g2DM7xfF=Yz~S@4QM+3HRSie9_KPQ`o$P~-5y~VN-4<&8ciKPRove?r z$v&li5YTXjoY9rfDF9ee{E@6~DbZGkWeH$DWfX&{j=#p_wh!gc8qpgw7cWs-<5t76 ztVWISrOTK~x#+;8N(oL*D^u0)CZUBaSak}|+`%vXGmdw|YIhaMp=~~WPiwqkmT<1- zjpQ^A7@hf`GlMiC;E(UrTUV@BgoK1yJv7Troe zq4YUAT8^Et7(#$R^EO37>%^Pv{Cwp(`;aX!WruG==7_7+=*1Fkgw&l&Vv)l3pd^Gz z{cD49Z{?Vw@AY;7%Dmg*G6W2?H#avg;n8a2jB<7s4hJo@yOXJ)MYplBDc7tBAlaBC z0(o%YFqXnfWjA}YM2tEKS9ErC-<__=qyAwt`xJu07WPTG5SEy0+H=z)hL*$jC{Yvd zP+p2aD#$UZm#MFMkMMIU>d2`T>|}4zKbzOrD>FC}<)>X@GOMgeY$xBk`p3r-CVQvO zo5QW}+=+P#L6pLM-P)9!38i?8+cd%QRdgPD&x^0x+fVhHCZCgmH|`3DDCdazLH+Ns z$I&3rW@OL#F0h6Yk@9D~C+2~cuORMhSTWMulUVh=W}+y_dosFFQ`+yB#Knw^WGeNz zSr!r)g@a<42e1k?ld3*_#N(u3f1~pCU8!ml8I9o7c#DzL_FbekD0jZ1NOH)^i)>!& zzYU52bEjLE2Be(!+ne3jpEGV)VZK$s>LvvCmWv8E9PqE|j>V@LvJ+ey($ya>K|J7O z1d0Sv{^Q43zjKUl+%RkQ8c)|e$?!WI$t`Ln<|2dxGq^tJ75Slt)M!@#{o8KzhkyeS zbV3M~2EzZtLhJL%dOIZ)A2Q9X&AnD$6_v?PgZaRAp{21S-QGl;^VJQhL9D*5Ds10P zNP_%n19*Tx4~_+}cLs$Dx}d?3w{5Y;QYvp!B#Cxo_jN6X(rO*&c_Gwx79SRT{-rql zmp}2TCu=0(Z*+`q45yxs^-{U-A~_&>%?<}w-%a_4G?F(x`^?b-fE!u`#&u>`<^d_W zRPGtveqUN}ZNPo%ySkGIj_S~FbW?bOg&M&1O6h~;yi3tYgg|f$|GkRF2@#WOuFo}N z{Htd80ocdTNaFS-?k1w?Y&g@X%%Mh-24IElebJ6lqlz7;!;AyLgVN!_hvhdZFr$sn?pliuwUgolkn&= zBd5ad+)Lr|M*w7p56}Z9$mxJ6xXVJU_#D@_oubrV;`bo5uSl`gw+q3{5wR2#!%T<$ z4YY(`jE&o}X`Hv2oKc40GeTNbbgy1{_l9jKq)nD-*I4e>k@G+t)g22h-~KalxiKx7 zvCd_@_awVm0xK2ock4J6x|4e|k?WbRGl| zlNrw77#q`_sqpkT9Nsf50>-&cZvPkCxIRabjEam?IFeJ`eH%02xrgh0F6emn`_uwo zZey5IVLJV$QDa*VtIege?CSH{q`Jn)0bb${iSw=5)4kc6 zaw4{fsIa*vzx}aJ>6YQ+w&LdZcp=x;h>LxHV?Uq% zX51O$bN!L{WOIPe{@`zM^e?sy0pvVeJBcN}faJK~rxp29of=Iay}!x-gwwgH-z6m? zLUkZM15R_8h8We(V(UWz?_1S}oSOV8d5bSd;MLn_@;iB){RWhQ*tAMCF(WZCk9U8j z9uxoV3neVqe63NY9(R!j!T3@mJ9qfmy>47ra zYpDdd=DK|LZ$%m3G%ov?NZd<^J z85pbV4Vs+}FDgo*_ZbOsCYmL1J9mC^i;*l>I(ihbDDH>es&PzaaeaHF6A&>zB~cDs z-&Ot`m#(Ab-e__dK(f~v!vQNu&~;maaeK6MVj?7DJN_jY?Nj}2b-t|BHz%+ZC-^=B z1t`ZQ77@X1Xj=fSdm)KN8wBBD z(x}k9S$9oySxbKsi*B{N_E2J;KpbdNf{k4c#@9g| z3NE|4QU6_S6P(`{FlEJq`?W;7eEKMs7O)FMmsX?o6*mKvFjA(O;}~wPuRRaz=l2&R zarvVjA6;(pKMA>8U+VC^xyU?&H)Mh1biimY({2A}ZyFJVP8K8t)Mwtvrh(*NTPq@J z557-~_rqnD6S8%$awwkdyK#7E*led?R+_^IFoYogW6~LngR)GlVu*(-!SR;MVuV$* zVJI-7OCT-}{nvhvF+S@-3+Ofh zMkcIf&*kD$HQH>EU3flX<9ru-d(;Vx!~pxt-s1*^ipxaSvSEG7&Zg3s`bpja4k&p1 z_%R|!x1J43Ito0DN>Ie0{KSYIrpZyuk@Lm_eOC6fmkWDR!r!kknf2?i@lIe>c7Yh` z=iGKvR%W+6Pfij+iQmwDLg5jF8)iD(u0{tX2fPG|ygjX^muqTA9Ds7O!&7po5eRxag~z1Y zNfo@h<{!^)u2%h!h-h-&y$L^Gd=#IzF4lAx%1t9KfnAhxVz^an9de=7wgG}ev?(% z%Z})q!^rgAjTqsJEV3mrK6m_jEcJbPegvY!>U7{V%7o z295}cA{W)G(PW8Q1)N+JmG7~!Y%yHwI6t_22=XtUtS!H(X#T^fsajC8LC*e z9}+;4xz@tj+9(C`-t@)o3%Rf(Njd9vMTb`82&9_fPZC}{(2nYBLk?Kgrd1F)>zO~S z2Wb5ro!T@W=?Ic#1nE;Le1<+Xc2kt0w+1zK@a$s6^C-f=wwp4yRxileWM4d(X`>H* zmj#=wt3?#JuxG=YZl)IM+wXWDPrgVJ04(cxcZ}5U*f^6KZG5j|9NtK6mQ>BJpg|&C z9F5=wyTr7jJ``~=oX%Aa+UnO~E148&ZwEJ!L3lt4cq_wA#rIbcd^Hk_2C?|#HO;Ls zgTejomM$=q9UNmC z@JzTbG(lB>`nPb)L%cl5MoV5Qy`WiiD6XGRx7WcsL{fHIMB{#&eZ9KmW5I5sMVl}J` zKiY8v=)&RWsJ>{)l9-In$NO<)ZB5bco(uzw>M6q~+h`{E8HoV(}NU!c4d#@P_ zURH6CQ8=LsA^-4^2UF7jQ1;eQQN3@!|Bwz!3_Tzcf=a_kH-aD_EsaPFAxKDf>Ci(6 zh%^Gyr65w$Ff@p?fHX*V*YDQv^E>ODKh9at^I5LX+Dm88-m~Mrulu^L_v_8m=+&2e zbHqlyJ(5M=m8GcAW`LA1Uj1!v#-aCVIOVGm|2S6rGR?V34n750Z3;sZljkljz;RB< z_LtUI7n&zNsm8DShqJI46|t3`k&#_sV+X9H5fJDU2~B*x$U_^zVv!WJ z4f4@Tu@)2G^9u`2;pMh?N-^z9lvMyVj)#Y%-n!WW$6{m&5AMij4d*me5qf2uqpLka z=)1Utlg#F}4WAc--$g%)NhNTMW!crKGS8V+^dG~Ga=9!*roT^&cIpC8w z;3@;{*bj)wv|cX%BGQbFt{>u=5wbjqGlh!}-~czcXKCtuaJsx{+p{$+hydZxpCEEv z1?Qp>*Gqku-gtrqE~=gZJ}Vk~D@%vr;XEIw^OjzrXNrBFWkMJ7O%tUWKQR`3JE42R zcY3O!)EDHbq!3)?VQiW(z5XjgnD}R^fbUGKjjP>R!}{5+rxqEv%awU$k64)~nsmK* zRI8SRo5p|e54B3l;_UbNyCo@?wDo%42h2+DBJ$hDckj+rnd>vhFolz1mYIV~Q>?Fw zWcn{OjgyeT&6cT;XS9W2_kEsF+6R{qmxn9D3j0i;7yo`BJQ>q9Dq&bA~ui5muYfz92|5QEo-!G5APOul|t?^ zlRIB>s{&~`LF(g8CqP&hau%_r3>5%ZGD!hAXvAZGujV?xT9?oeLyB6{-&$4LiQ?JnW`>gk9XMnu9uR*d1rV)GN|gyJ|yT<__@GW%PUuXl*P zvg{BY@srv(Jt#pZjn~ptlCfp)iOtClm|(9T9X+pwGXH_@)IV^}U5B$v=D3g9_Y}5g*GN z`-Cb9*{rSvdaAywiY}A<^+zK2c3G#Qpg0&hSZ!uvqDf^ou2L_nJ>j`bUbXaj(Q|1tDumJ6`ud-r)O)Uan(rRlDiFCgf(@4(jG(IIp0hrRPvn}?5f0ms^&A0HPn0DE~j+270!C3bp?n>%XyW^4F36ug>@HZvUNa z-v6l(E>ZJ;eLny|{9m_E27-PL`%5SNRf1u-RoQa3PPj(694V4sYryzn7w-cO zc!dBb{(_WN8U)h28xZhEbiIVoM2r%RZbg~0Mp39a#fph2>S}XN@(xtELh% zf-KPfc#7!BzAiydXaOJ2qo(qUyj<$lCJw3#V+g_NE4vRJOg%)nF{^Nm?k=jJ6_hUJdG)o9m(C zvfXui)w|!uD;&kgz)y&icTWZ=!C(L0RQLE26kGV)1P!|j2}2+TkY?fUS(||wGuLv{ z8((||=hUv(X+Uh&M;mmov6~GVaV3dKaHN@PoLtujD{S8vwk^m*H8Mm3+&}KPX*w7S ziWQr}3#Jn^dmWRWyrnk$0FV>b4+A!}c(3AsmmpiZ+uM$#)DuHMR02eMeMz^Nc_*f* z?08M3&+Xm%o2esFBER{QX3cLW*p1(H))9L!i3M?dz9vz=VO0Au-fSYlD{Y1Cp7Jk3 zOrtZ|>+9u6hK4(anVLC{LBrzD`|IvGv_{!_B0;E6Z|Vl`Yc$ z!&No5wUu5qWiQ)gUpCgf^ofb0QhmpiNhZ)srrNerMI7sY)9E(3n0`wzFRZK7U5&_Q zWopGlou8dED1Bh#a~lo?)e5A>LqeSW?-Tb#CK7(#8}j1#M62Rx8G@(eS(FxMq592H zr>;QkJDlhz;XT%z#D!`?>xWl{v-p0)9K!r0{a=5zojf6-RDi{1GdrXUYc)EnU5lUM z$nNXd{#@%#7%6Q-WsR(nY0sHY4y}Law=+B!-+1x*H+I$teqx>D%PMAW4C(d7MuK+N z;JccL1oi3C{mHT460TbDp8}>%n|@%Ka)y~ws~Sz!AeQIq0rED?59{N4QvxLo+CQ2j z=;cJMpPi=W&(7@!CwRDvok>Ni)4wLGY`HbR{sGO_N87(B8K|j`#dA#pv330YIiVl= zQ-4)jJUbZ5!@EVjEJq3<-uk8Jve}$wSoh7=NsJEst!my^sM%wW@%H@{o8iKGtI1K0 zm4RhKg56tPNpuW#p3})co@atDYnsYFKL$t8$kJl#*W&w0#ivFO%3+fUR?!Sg1IE&O zBp??$5XF^}Ar&1K=9A1}#eF?%!WkaDJu~yoae8xoOvX9r7d8&o-2Q_oIh53auu;lb zsn^koBWNx(-ElMdIylb4 zB21>o-?;Lk=rF^b&+=gcgl)F~v@^_;>Rp*SDI_^HvD$ax49xCN1iS{T0*M!S1SXXzsgtVrp3Vex&jwh-KaI|Q?(ID*Gc&OfrPx=dV^PB`dPI<{w79B zx}e(6<@&bD?{DLqwL{H{RMVTap7ZMOb4x@fylkd_*5C5K)tPj@+s9#5RmP*8oqmt; z&yVtn2cuj=lyYNV_r-)HY8_9vR*|Lgp8&^`xrl0+OXuN}z`M|F3Fq9jsqgHp?hRJQ z4jJR0vt9;uf16F+*dH*t@NRlDtEvcq_OHHV1?^LE-wY5&r&PV9NfvYW(DN97drn_f zr0@l_;Jh!#!|ywHJ;S)J%Q&xyxXzDf;@qI+w`EcISxm$C&0(UrpfIa&U8fA~OK+HM zFJbd1w-Se{?T-~HFgEf#nU$1d1wtf&A|0*L=l7nJ{vep~V}|;)+X#>@C-kQHuC@eK ziMeFe+OtzM?KY}q)LN|^J+Ik+F6{0$6nt2@HXIQh;p=hz!c0cyT~>QIH4l9v-@(pS znv_a13?9GGmHnhhw=`cb(=2Mr^INg8nE%l>o1$Xq0`bPA_x$)5{771YET}D0TysDp zPx>uJw7cDmC0ss8h@I&LE~D$(Fq>fm9Z&ci$yPMIVB5lx0#(nMKvBk%C%Q%2w|85L zSDzs>q{=2gBeGaTA39xBJJ1Tf`g;b)*3YVJf9-JIZLzG-F3`4`_%6bcz)jqja{PN! z?Dl9$!G-)%03FlrKt?pJ=%S~~*O%{rKe!Tqz0b&+*4X$3R-zi-0s|@bO(v8GaR5H( za?N46t2ECbA^gR$y+f*xs8<&b(TTjAU(9{aB}vnNOY`dI(hSWW7bBhD?(iF?OHsnp zT&E(StaR$N0zgY1o$Ra2p$?wyncdxCyyl>AtfpLsC)>$-G(gk?5degYvnwu0qjry1Ee%0P#11;F=BmRt#=aRmAZG+kSAGlXa z+#o0O3opFgZM|2fx1ibVBSj?imqZYLF@MY9&zYcN!ETVtxm!I%bsxfdS?zFbw!{u; zs*=6IqtELw6>n|#+jHfhAa48ks#ARgbfLI=9{aZK2G2LcnI9>HhW)NYo`csgfz_BG zRB|=t3uvSjc=@yUZrH7r-mU$16NrKzeLawinPzur?->s`K=h+S+^~`W-sl&~g)>h{#A+bBTwRwegMK-C0h3!w{H>6w=&D>A;N*kt1){~cd z(${_@;a-~cJ!L$J7{)1F58A4&+irT4p^{W?*RyOxB7UNGiN92&rA?TIIBJjYI@T@N z8=XCdd<0&n4h_HRFtQ}t{-2_HmYiywqEwV(DX6JCe2Z)a4M)~SLVSeDSWf>0ev$*w z#uVX#Uf=f+6TRyy|MdNZ$!+5E<)VW2rxXl3S5U~yL#?%0tjpVi*1$I8QP1QvM)b|* zMl3>oo;4YWFvpYa86#?@G|c()IU7P|GyfLKuV7%U(K4C$2H6UA=LX+ve6UpdD)c zn132RmFBoA4x#&9SqU;dr%w|Ee3Hs1&($NWmI)c)3UwRZ#?s0+v72YI2_ZQHZvJ2V zT7y&kj2c1O5)fD1Jg-jk(ygDe5#V0rTF1VIO{Se6Zy^X5&bJ$gPA}c6#JIRNrIos$ zL2^ywSzo-tO1#=o8Y!gac`+}oq?80A4`hO@8$i1iJhV{w5uLUL-c?^QBtBnc$C{9y zu>bERXRAv||EBFSjiURow{-@Zt*<+li*|p=W~e3)e^ea0QKZYj??ds&RLQz&J1;)? z;m{_+F@)sK-uWe%t|n?d$#sT7)6NTVe;_-yrmA1(Zr}P*&NuCdXCryB6S%mTccNJ+ z4en+x8M^rgz28L``$(a~#8=0TBfD)P+MU;%qP}NDJ2}Q8u6u1yCA=TX6E&83)a)JW z7Jf%gep`zkW zlqq}T7B%<9NZzyjOeSe--Lh11KThL!b%~|7E+4@u-}pZfy{#0AQXkE^}W z5Z?DgK#73pL`l2UZA{{L_q3hg71E?GI68PfCHS=mT7MLcAqX~1gv^OO)q*k=vgL~2Q!xhJ+b=5`C{?KJX2N;8U z<@;xhZL|4rBcsQxAcZzo@r-CTq+1@JRhIO-oguWmw*fN>I96a_GR@NJl|^VxPQ_t< z@zYx#AcV8@5~hfyygk^GYmNOT%BYT#&5IiL4DDj71--&cNh!}_RMZehH2t}9<=86N zWrcX`QLAfJt`_0!+nY;kxrw8k?)Atf8o%2vH7l4DWJH;3<8gwgK}M6p{kb1Zk`lZc zlxl8M=iAfT#V5DQj!X@p1xH?yDXQ0d`{W%_+TcoOY9&~hS71ro+CEoSwv84#U#4A+ zhu~;yBQWE0ye)GP)I3wJx`UyGF?<(nImC516WS1;xfPL-J)0OxQuwqhMr0&55==|ZM}v_2fo3h$*Dj8 z9;W4eu-FQZV_RYeqrG!uSN!MgjYc$OjKs-M4{+xWmp z(x2UNz#Yd6Sm9O4x?9px+KhdmoJ>8QQ4(|BTkFc+mnWWT^pS+XhD;6&eDsuh|FltD zOdFSI^u+LZ_YFtmOeE!zxtArKPRRCUBpA`Z;*e#8U5wN?tdBZ;cA0x~&w1r}gUjwl z*TS<0Q6#Sn-mi`P?VQ`XsHw<{rW=)Q1`Tu1Ydz+MzAzZoyx#iL9DjV?Sn0igzo}eB zd#vExmmB(A#Zvd}b$84vqcay-tV27IN*|Fzr|Y9qTntr+dKM zwWXo=yikivzjk=C>{!xqaVN()Pl|@m=IFrsuX76+OeVkiMGkzZKUiUiBO{{)}&Dmz5VW-%BR%+!ukF`wyAI3V9b&txSAWxxUkmMtg;jS z>g?Q7`~Cr$8zr0bz%vITS1)8@CWoqS%?%s+=$ZOgT~o4?Ld$hU9*6Q=+EvG$r7DF( zu4;Xv(u$oQ(rWEwmpW2}k!Dy=A1t_&4(01rWv;or2N%eCA=xNmB|UWJQaVnjGGkRq zMbBYR3|GpQxzYK0rg{5D~y4OtL+i(hwew@!cf7F2N z?>jKw4b#k7zSx=0H69bP`E@YCJq<$?Sl2G><=nqVQD3L`c9|-@?0jO8hJ?5i0}JU;XMQ z7pw=@3KD%zkGIB`k8eOTX9h0LW&?*m0C00YsPG2K*>eWYmZd1e5y+cVg&NtIbYPXq zWmIc9(#@Br=IXa~`XE^#&#PML@P-T5#eQ_fSg`>Cv|hl(o?i>tTy1W-kfcN&In)KUbAi`fh zxX-eprru;N!0}qwTgsrSe<-ci!l(gG{(;0i%*NoL7Q%#rWqFykOe8@}jxi4<2;=Xj z5PiPrU$0LA)<=4A%a%VF(lo^KbaJ-_x;3@69lals7_NMHabjUONQ8iP+eXYizR^!?@gm`*gF(9uv7+U6RjaQQbZ$BInKWy z3Iu*&88Y>PhgV((3k@ruV9g1X2JS!z1ey%=+1o;a#Tv&=0=#OAmc~xV!_pT<^|p#$ z@@K7;>%6jYP7aRy2Bzp~1VW_Uln{!L3DAA7iGd5K@DhdqPd$D*78GlFB!Y@Kga%1p zRA^>K3cZTz!X0`IhmGGMmNY03ID<212AEZ_6)IKo@&@0HBYeI87C}oan#xU2j+t}W zV{jTqzlNVI%32?U-p>qRQA|%vd~QL2(qKBto|M~99rv2#?c_KS|gTcBPe3 zdd6+B2YtHFKn~!nk?*lg!IAjf>UVPLYw4wT;Ek-Zh-xESBU zVDNLe#dO3{wy^uF7@gd&AFkbrg@yC0P+V-R(6pO@(qLrU|MDbFGe8R1!nz9wfU7aH z%cW$v7?9&33cd-1{Qo|5>!ZJ5k#bb)i1BN)M~UR%Um$nYg23+3*%`_Y;Fp7M1hLNF zga1Fb19{p1*Ju7a+gPEC?r*Lc{rRoAFOohUXQkl%=Lmmq36BAJyqO}l;#pB7l~Sle z-o3vk@$ZzeE{-*q%|haW-+1xvjIo^)O2?YFZ(@O=fWnaKzfQ3wA9nG?1k%Llx71S4 zL1k%Fb3nazhMu77-vhY-`>{S6@*d^8z5jlGQ=i2rwH)5t*JMUO9L;g#{n5+#-Y8>ynzI5g3@6S)R;J z*+b;N>tEf+8cc}dVuDQ(JT9jHuLt7aefghl!{q-xVE?}JzqJj`mihPa{X5M6b$j0Z z%~g5C>ER!#W9U^}4S=C+!yr_b_lKln@b;0O>hO|HWH`G!&kV)`0YC%YW$UgX6w^ zgnIcn?1I`lTtwSqBbligE}X`Vdd&-8^!k>AxUMT^xe6s90OxKC?E;3KgbWbIMRW9B z6aj&}A|7KK=dGbyIMS-^epmtxH%TwQug20ZULDq)?_dOnVlny2>?MkFW2${_4*k2# z?!mW9qm0T1ptvphC-t|s67YNBUnY$R2)r>-mnS_udEy7^X=jHF83?4M*BK>xDO9PW z5kH=+ZkmRn(Q2wXy76Ldu>BS~Xx z?%pn8G*}Zx3Te~T*u{C%n?MqU7LR@UwGCg5nr!@{^<4CFX$`~Fa$&|dk6tF*!)cM2 zuhugFI#ld;85}RaZ8BBZui^Un%3MIiY9f6mOg9ekd32BT%~Zz96z`gDf|c3#!4=lR z3{r>!S*})*euXNq^T|_5@dF+4kRU{bZkbSjx&&D0o9XKi5J>Gbwe}?ooURqEf)Kq$ zFU+0-iE$pN=j_=-)9~Fr7aTJ{!FGI0^3rYGuP-SP3Ea(j8g-6c}55tazcBugatliW=eC3>t_W6k!?9u5Hs6dTcZ17ms+0 z$aYzMNdCa@;fc5m^B zH(K_sIWFA4`#9TH4q^Oni2P<7Zo>F?!27y! z89;EAM^}l!R}#RqsOI%-Mm>Up7DEA(R$h=i2w?C_)XtR2L7{L_8@V*O{C{2xOVF3G zm<}B{`vOlc8D*e+4YAN?SN|AVD0=q{QT@EMy1E*$OMoy`Buy0}4Op4YwOPSdeb**OHc40Y6~@t8U)Y_B@b#EYJX6Onfk$?>Tq$w0SQsP;KMh99tratj5kCS|RqwY94zBM@2qog?~6t>~arUpOBt(UQgs&p10l zZ{XFlc}Tj4oc3z1p;ZHb;eqHQMVyB32A-bsbOjJT<{ zoqxZ0p@vEvyM06Yo}!cJ^z&PJ{If2#U{w3f%Ck7lko$OanQ{r0>Eqm`Q)>3N)DVr7nHeu zQB(8z zp6SA$PQ7|F(AsPQc6b&oq0^|E_Jm!#_8NeRx5l>n{4Vj;Y1{PLM9D!|(QNmKO-|7D zXIohGaN$Une#IHsp-MFk_(BNT{`lxxq%$!wfg1pT+H==&`OG!B)&}=+b0`X;9^u@G zqC;R85F3p>TUg*pmw3t?B8~gz`kTf(XP0!u!d6`b7)*fD3?U-o*AZ3Xx+dP77zLAu z_LLJs1|%o_`jWK)1iUFUY*#t`;xrJaD=Z91h(QDYdT(O0tGj*z4HB?7CaWAkQPO|m zP_&v}ku6DNKja;A2vA3y7L_#sXA`zuYxQn3B+_!=>a;0v5exv07fywfn-G(Dd;pQE zu^K3CYdaYp{$$!J?_?+7)c?Y)7pO4yk_8$+ePRI)#rET$L91>NU+y#7hL!Yl(3+f( zni`Xuda_=&&Y@GRYMuCVN8(uPd8LiElhZsnOQ|d2m94s`ke34zV|FY3o3bIqYRXgo ze|`j(7z$|@fN|xmX13m!dFSN|pIK=qC8hc9_Z;2ti<%OtCY)!vdmiQhOitYcmJJeI zY=?$Z%zQfsfcC_X+ji?3_bIEWi1-M*0*#DZI0YVKeDHRC)qJ5ghyRm1^ybPPa6Sx+ zq8aCKi6Ll*`KFeB;pybm0t765W%#~N zPL7SIX!P92fFVb{P=nTfFslz(7DRuhzA7*|pLGm2hf^y4F6VE-#*KgEgwp!0)q3Waa4n)$S_n;Qr7P2f043Z|2_q5@FTq7nD#1c&eM?|)J2qLK8F zlAp``&N*0M0tB867`eg4_-0sD3SdM)Cp*^Qd+v9!d;WyMmy==-NXCxO_m?=TeNM?z zR5qvUj^A@o*F^Kkyqs;iB7dL?Y`|rM3GWElpg1`))&B(EpjS{+^InJ?Yq<8)iKd^< z@T=uQ&V?|Piuqqn#m6tEOBkn%9u^({zCBf>>wtL^i#k24Wl=QtJ+uhKJ~j5gJoLN# z4Z8Wq3$=SFXHBOJ`ex_5omRDsP|&=LRu& zyPHu249;)Y8qT+@r>f^}Fc9A6H8Dwgm;*4XeUuYPe2oQQ_63TuL(FZ5crHV?vjgi` z7Cw32<(^G6>`hGV>$w&|yI;R-zkvt&+mmQrII^}>w^*;)^4V{{^S-V)DeIx!k^KR8 zD`g&ouVA2W)DOV|90Q;ZXZADb5(RE&g?hWcA6h5@ua4O?_uzQ8XUpqlc|GNknnUY{ zf-ltpge{1{mtS+NHSU4h>W|FJ7$(^lzLiy3tj;S}f1Es(;#tLs(fOea{G$4j*LL0+83vcEfptZW+DQwYWc+X@TQ&yIzbTw44(QzCLzs(BR{lH*~MZ zX1dnZ|HSkNn7F10IS@kxUP+o)O~%E>dZ^zooq8IJiUH>0)85SujtA4Eu@Ojy%WSw;pjhHgJ*wV}iPhh`8h@a;xx^%F+ zFeI5)?zzd+L)B|oZ%(Rd3z3EH?LF*Zj#YfcYNf zF*J09-xbw|Tw6b}^Q*n{Y_05Bo+%JE)VcZEZ%r|P6wVe3bjxcM>1>|t%8n9{D}`An zXR7aP)%Cb+O|4!YO97=|0pzpS;TibtRX(RT!Q1mtWudfgD_PJEA5*Pgk?=qji=`+dOh1(?AR z1eCm}F9%LT;QF6}aY1FMP0y@1p2d5j+yZ>`n|T*DWE{w2LWu9M5D3^$eR5h-ik28P zIX@zTyxyIE@6ed0pR1hYy~XA}AhER7*w{UoeFM_8ei%i6NlIc%NxaU}c+uVbRuQ5O z1UTUMSTUGLL-DHkV=de9(I1SYc$(f{(@$;fP=|6qYePOFed=7 z_avGg7nFD|$G?ydNX;jjc8WK+3c$+Cv;MEcMk>Gjs{G+~hx=)Q| zE|Xt*!N8CKG^JNyrZGh?%+G^=&RPEzO4>@=LdmXG4I~yvn{|-VzC5s`{rD^|s-WP; z@zyk+^uofi`{pmtrSA4ofp|^ShM1eJ!`RjYiIfiz&Yc3ADg7F!5j4#Z5Io?RG|u(o zAjQ);hxmyC+S}X12D!nq?u{6>0HIlOxh)^jTg~o#MFAoXB~}Vrgjw5#=RQS>L9NSb z_LC^i=bt}nR4k9@(oZ#RXKQ4y3B7ER_)de=(*Zm)o&48xg9}n_AjpihEiC-C*-|32 zBSm@{R)(5!RufSfBEBQaq6m!1d5mh+E0&p2FIBCS4<;U=5ljYve*lEy)IH@ryyy=4 zBDK&hw~m}VtM3=cet7lEH!yAlrH0EObOOJT&iIh?Z(_T*#G+ zSdOIO!-gn*;06!wlJEJEQtTZjYhaT$QYevc-2fi7pw?dxGL*kfXvy?~7MJ#RPhebT z53en~ZEbBGyNLVa=g*(!`dB7lG<-;i{P8gm%n7;iqIASnlP|u^16|r>IAXpQal9+7 z0IU;09isq_q~B$Ofka{&;!Vt_bx|oPx9iK}_1dfRHO8edw;>^u1?F{_?11WX`xr#54bp+wK(!0Fc%A7I z^l}<&V-8?2DOp^kXoWJGSF8cPiF)Qdm*KY)@Nye*-;m*T`28jj=LQ9b&e6t1T&#Ij zF$u&Oh=PD>qC~gM?JYi$7@!5_JY7kb@a{|6f6F4mR&V-JN}80DT<%{2qbGjO`%m7aqIq)SlE}6)k=^90%MC3780_Jq`-J4rxJ)t%SFm z!&J530_HI8zDgpW)z*(p*_8p~mvDSc&Do>wM}mYp#pBb#reLBOkG=6PyLELF$|RUl z0{TM`@_i<=TbdT^@c>wPx_8cy|HX4C*GkM~c5?gOetpwr4OSB&a8?}X?!Jo(2xaFu zG3I5jmhssbC#ai#k=atOsOSyd^{mHhxbo`%V5|z*f37SNKzFWGz%sG0K(0K*YTR%U zP8k91!f%@K6Z0O-YQII@zwcEKB9yDGI)|gp2tbD|0urd;R$5GW&Z*-HvT;U@tvxa1 znZ@Ayxi60a-2BgObqLVeX+Oh*FtH%fPy*dTtrE42u?`SGn6``)kYkQEPS2=~l|8R7 zy8*hBODq2e4vE&x7bH28LC{TY@VTKXF#UcI3~c$|eY8`I+(0bni>p*wn(*AG$97+l{0lyNO>;8@+2EWmM>>SK4}Xtg6iR-_!S z4xNt~`_o8X)azIH0gV0e$u9YfS5b7I8o`mem;+mLQ8algo=`3w-9L8+<#)zhvK21oQ^WlfR4C z;ew%+41dMYz!=;nSQZk41|bHR0u}`NI;(4elNmU?VyrFPEBy!ElrRg+^N&qNW%WmE zQFp{h?uZ7&c(oD{{d-(y`#qndT1`>BNJ5zX@A9R2xd>GTiPVII(P~BVkIO`|q}}Dc zAJh?;$1Yj3{;l~Rp(5XGCqCOxpY1MmD8;6xr443382}z~B=q81v9{%$`-5*l%CpP9 zDnrcOr$04g#(R4jh&v;w2c-6u>+GjR`xAEs8X-tFX66#_;}3qP7N?!>80s(G7}Ce1 zc)sN2S#_3AecR+KN4sr|bM!cwo6=)3f`qjkRsgv0pgg$~!r%w`3Zte=8%`}}z`8@~ z+NY|sy8)i&MVFi*nW2&zu|y z_04&dknbM5SyjKi7LJD>CEQ!ry^>;TTXF(~%m+Zvnl`jb!?&Cb)?xNj)xYiCk?5Lx zxvc$tDgH*4HUMr@flos3bhNQ*P)YB*nnMj@PI}@HWB+>H=g+66YNRGM`vHZTPRcL! zCnzMG&c0=Gt3$5z+W{Y5Zp}}xot%`~^lB6Gxg{mPyd2CbC@26!@SILJ2=dPlz0ZGS zwt`pt#%F{)792yy6c|EZXaxEasEvIr+QTgp5L4?7ss~s5z z(l4i{YW9~0u0>!_APp~Yn5kMGFwVEevJc%Grh+nk=o!h9dj-+?q%bG++k^Q&X0l*N z8l}f_QmjzO^*0Y*{q{-bY;Bx|NC#H_U$+qkt3Fy0v&UdPadX-TjglRd$CF|8GH#iB z{jWlVm;FZW)FuT4vU>m)hC!c(YPx}(RWMNsog{EDSW;6{BMA|JKtoz-Xp$fSG2$lD zjjx3-uFhLoEs~OGxD396gOs$Aen?25x*0^;a$B(2ZzU}ggc}BpS7R1ERF8=ti3r-B zxNc2l82g>(0HVCz^e+i9vB~XY!!vgsorIa~8v$UM034FRA0{x$nGymb5*UG^V{ijl z@4*4bm>xJP250y@^xPQVMK}Ld;QBY5w?|lE{iqm_6a&&;@DM-52jke<=;rCyuTLQx zT@NzA#MlBjr39HE&h$K_uGMNw=&4W=TFrKQViPl^887P*U7Gpzl&XNVzi5VnE%Pk_f3NhE5Jau zw0_;>bH~-=puv*~eQ7W^e-8wu2dh@boG;3u>c5{=qhQ@>8WwU1pFqpZh{N#sf=g%= zAZ34|b-P;`)4w>kk8KEk&T0*}0qG&qe?lQ&&@d_Nd*1Atm>sYseV zMvs+Z@jZxkbFFO*pb$Q)$DMOdHLN+!CW4Shoy~AHygpb98V`%&L(f|nDDz@kpdt_$ z(iF1cjg6A+nCYJUXQ5Swv0l_h8pxy-Ci7KD46-?p!iQ$Fp5EJ0PjSq%= zbl(;NI(LJd?zbPf(^~bG`pLvsQ+k#% zla~T2#sldN;4b^sI33#fPgjqHA(`#YdWIqTcQ`i?q z40qm8Kl~Q14+NJ9^kqm_OnetiiG=Y{bYrW-Vwq`1o!_iHl=lQ_OudH-PBl;_pq7Ck zL#=kCLp{amhgh0Fl4QP0;(%uphxAxJE3c?1IoO0Get`tcyLiE5a;;w)2nM`~iA30F zgEV&v*Mr)4>*TrDf{@nnF4?VcyMIOA#|{+gHW&}2+MTE9u!!v7-E*Ovq1N5p17pU1 z4Sv(L49opXgBd@|o?B0VMc+`KuGj1%vYQVvG1E0}r$60qki4k#^Hpx}(IEwvw{u?E zD;!@C2nIAwM`(@pX7bhL4SxHnmu-Ir(V=|mZ?3B~uo6@;hvG^q5X`h0vwkFjG2Y6P z=heBL??se|{JwDZOlgeQCT>${7UGzs=e4khBJfS7;~)V!zVg0lPm=GxP6>njwX z5h%opoy`)-NVD;1)M{im)vj#k1=G|K2;o$+GO8P&v6D7dg7uEuC3H>@5T-L3Q6bh4htfh~2 z!E>UeXQOZaN-d0~>Dyh}u6Kd#OM--@mtnBvmHE_wzHtl=LXNWSiO{jU$>0{1Mh@Rr zegF(Vrr0>!-fRRWr=2KYlfFMNmyl5*9y$NZZSjiJsh-Z)H<}($$GwS}8lD6?J-GjY zxudJpK`Y(+cP;Ulft~e9ZK;*HIjjYy$qQ_{nD6TxTsqr0wXP_zRT+K~f7ifTuQQh) zLV1ZZ6RN)F1OGy~-%Zx|Qxqf)CqlO-UsW!}lJ9y|J^b^?KCo*<3jJuSpZZ@GH_EhP z(hjPB`xca`YU~yeG9fS^DDxah2Q&zu#e9tzG45L(pl5}pEC5CnLYbm6*LV|mhhZS! z_t|Y`o02ojIhB3FPq!^OkV3;^*;MKK@tvi0lprT%sC#l|^D)II{S`uT&3?nO4=W~$ zUWF(`7E(@G#CCkgel*{3YM;v0%BRgN}0X*}wB9Ze`7>@nz4qeu!bs`Irx|&mQ}gx^=GWM;;3pty}u< z%*%|F6}~v$$$DZu5$mVK^PUu zhAL|h*X+i>>L0eLbGfv>)fT!a{o9>_HT80PX`4GPBTPmioZU!|;@@r;(+=ix8W1M3 zn^U|(po;)>v3$1rPb0y_QC*@2JLgR-XvV+S9-K55$S`SO{~zrr(s0WE9RF`ZoxxV? zqu8Q(uhWgJOEDp$L8%jaq3`=MQ&QK;ergXwqs}hOC+zW~y_uIrRKp&H5>5_O&?^RY69uhYI zTLC)l_uFFK-v6ttUvv-km!BqlmRFQ=O^v;HTW6}XY-9uK-&oIlSTAYXA89s6 zy42;0SfI&%w~BokNn7$>;edqD2UWV%c5$75biM6i3Q>qAjs_vPLrEu5Ur zF~#^Jw}`AVMQlu>W}GIM(gSCC)S`iz3aV*o@3AH2K?=5IvgEqXV6d%uEkN>}RODjVMYOu$H)0U zB7X*#8{Q;Xn298lacj+s@qQfoToxPj*2Z;Yz#xpcPy}G7?Rwl(#D%hKBu&V6s~ ziqqXteeoMKz!Z%oQ)x3I?8H*(y8ggnf~m&lK7+%ltu0$O^|KWNEuW@8#P~fo;`x%D7w>aA2p!3IJkL%cU z5*7plWn%C7Ia+f2w`U%(j*88VXKJI61Hq2=2nNrxD>evZ3vigo?m;|r=9i67PGd*G zzPAQ187{jbamliD4jjg>}eZ3UXRKJ7PugJ5uRT>I1GuN9{#yGI2F4^APUbD=&pl(!*vW^SDjym9 zO)4by=)gN7itrkq(iaSJSYKa_oi2Uwc1}48-j4WF!|E=euB8v!UyO^N*Pw2ZVOWn9 zjeet2&-cgXoQ5Pfl8knQX-TXqgG9mnhSIiu>5B8OH2G`jd z*}}p2w=;lXcT(lhpamHNQ0y|K$ri7AYHM3OUHTA1*!nU4@f{OsC|1t>959^~CNafV z>>Pnr^wHMz`ECaTpebr(%d7F*{i@a}Nrub)URfe%kE7!k&^F{AvSYz+>l>s=E8@~Gl?my!VK(lF91ej81KO(S9Rvb#5e@$5 zR+n~-{?g|C_U9jMM2mmU@Zt@Ftg0adCZ0^mRY~P4VZLq?Zn!B>!ngabRQ_G*H>z|j zWEg_%@TJqYSH+TH!)>!-(0JNWy!hmcAK{0%B~jf*Nw88yw@RB+@ zI)-OGE~YI~vxh}WYHG`~%}KzLa7&4j6lM%$e}?wPT+lFK60O(J2mnf%p9dnfHnTR3 z3Xbr8jaF}u)t^Cpmdbo+804Au3%ewQCH#V5eo6u+0d~!exJynt<%8jMB7ub_`&7s5 zckzume>JdEVbs9s`1QjQ0!3CYnOp3YvviRyzvVLE&*l$>t^KO(c~w^5eI8S^sxib9 z)jNk$ll;!dg@{NX|Il-Xoct|}H<#S(aoU$fm1@DGQ+GlaP58jv2g^K0|5wx$q#LGvBQt_argxdk^$9N zhaVpsAm2J0a9j15PPA+$aSNf$?2PQ0y^@ekcCz>8cYMCz z-+DZLzy5d+aeIyD^E}VF&UIY}Q4dw6yh}?QZ$`=_<};5&t!)00MP4!~ES$Gld4<%d z+Q0nwd%im%UmGTY_-$c96BQR?;p~x<+!}EpRy{7FH_%|nRhTvNSiOIKQ%m+ajd8mC z4LWiP!t=Srp9P8PKK^(n|GobCyKtnLxEY+W{Tt#8&*|ogAlC@?pd^dsQ^+(ca?|BD zmyv0m-+VQKhdk;{jmj<>BC}3$%)Kx*$Cvbvf2x22~0#1UZu+uJqVK=R(@xo zD(+mFwdkL2%9Luv$=6J9aoCz(T^$^yi>=X1$k8A4Eio}AENn0d+TTRX@4^WY^d8RB zQZau%_~q2h1ZM^8m<`E=w`fveH93zOCI1(OGT#E=1Ey_x_m{R!FJWi2-#RMV@va8Cu@K^_^I~#S#+! zmM=NWxq+8D<+o94$81n=aByg7SCZ6^1xL$k`D#~CgA0!MZvjVpz>Y@UANMR3lL6!H z0kZD{nimm8a;&LzuVY@_SSI~+MO0Q6700+NJ9J{Z_BTOhC7Bc(+vY{SJW%vY>3vi6SM~1FX)5 z;bJqD0W#&-EQg^a^m_!$`K=oCfpk!h@qYerUuAABbDd8F%cljTd6etZ{zaj@nK zBCWRX3gn#SiAzhI(gkgWsTQK}xZ0NA2NuJ?yy*uQnPg3{n|w0+&lp+GeR8cHw-e!@ zB;1H4L>@gcf8-!)%-m1RI1?g%>1GEwJ-rVN?H3$d483%YpDiuj;=gGN3eZd6e?8>ps zMfa!&WMyM3KqQ1o6|kaBO;Twtth*Z*6&10{YpCmpQ96Kk6v-hAbV2 zQHY;*@rxpl-S>NYUYVKlm#%Pfs%LQ;Pl`HOu8x#iHHYK_H0Zqc=QWV7UJg5g&9t1Q zrJAMX1|)d&9yw&j7m(1Dr#FcGErncPoV1M1 zwqohX`Fk`11|G|~Z}B5Bkrco4#vTKI@i}kn)xn(0HcFf7`6p}8B%+|8Af^}RHEoaK z@s(q(t1Ey>hW&+B-5JjBmLYjRbtIb^_KH9m^6yV7y(kD(_Iun2QYF91oe zv1L`lj?tnue3#59!~!aaVPFNyGK7x7?bW}<2Zi6*7gd2~$L0_KOsz*spvNgGB;?kH zVV!MPH{l$uaLR_cYkfHY%3L#^1mG2V(Ack5Gi8#y&!-x|pSKe(P5YkKN`|QtZ zK2alAQ^Syx?*NAC2Xx}b59@RBcxPXt*u8agWwCF;O~DD>3A~%`I3egI^Z`)|qEL3$ zSm7+dax?(WelNCo0xfcJqsD*IybU=I_=o-a zR4fkO0pAsjX>9(>?vMKoFnby<{it8_NlRD~Q5}pW+&tc$uz?55&R)Ol%%fjrcd*4n zBS6hu$hkULFx#)`!FMJ3snzIS+o*bS2n|#^0!)TUUFrP5Q4eb?w2nKOPxP3LRJoav zmgciFlmSg5$(zlWe9szO^NEWUKBqR7WA90gIk$$bs-e|3WA_Efmu{1OSI<~V2`~>; zx5(^e^lf$EN`;4i&6f_o5_N&&>6c>5{`Q%fMtG(^N3VX2xm?Bnsa>b_XXjD63vsA+n>o{W^8L>B`toPLp}*UtM%?3t1Qh(Wmz4fI30D+E4Tb1+%@j$ z0GfQD+j$yIjVmW4W(bCm^7`{)sV_-w$mPPc@24NL5fmRj~(W=l&4 zkNc4ta*if9^;WvrhJ{VU#Kl!TuYW_%s(|+)%AZ5i`#H?v!6!*)VM*OuKGYET=r9QH zW-#+fZAlh%NHqZlXOhRTT|Q%FN_wlj#yt;2C?n6KnvL1)7ckG_x2V&(pC^t9%r@;ny;a%bP;G zn>l7fRd5ToVV$oFSu#{p?(;u*U?+!$O}PAh+`hN|WY19o!V(Zk==qHHb|+37sbhH! zk6b3YqD-$FPr?)Ej@@)nP(Z1veTN#2-lytf+WqEE;1qJ}l)wPJbOw=d$C=i8$mOrF ztD*TM!i1}m*$ljd1cZcU;kBN9om-a@E-u9K`)l?mHEZs45e>?BN3L+kc>PN4CZ?;n zi6+4D9r~i$P+YtT&GZl=w;;YmDW3%$+iEg`a~KSHqDP)s+VIYD{CsC@bX2iV2UZEH zEugin41W_aHf%1IrFYwFjpzg3Fz*$k&Lya?8yk-U0}mmB9BwVNBy{BBX`{x-KutD% zU4}jMV@y54#EuktpWb$+#D_*qgDZ6X%7w>#&P+2^`%+g?%1A)hT=nkwA^7ng^<@@? ziK}%lhGsv>xbN=1Q~y@hW5+XBDZT{G04aw#RQ($loVs^p#tnubUM`J?!8?u%?TRtK z+OgFn_pmHio0XCHt~pRQkCwfn*iXIei(`UN86W;g@Bo%39s+oXX^7Y9aTu@!9#%Rh za2qI}uD7@$KR?}nQY%#ji)J8?2kLOckrKVv_W%~DP7srtD0$h^*O%h4d-HY>1>&{Y zwUn(X07b)ivUE0`*bcpjod|axukU5!49-fNPcKK5E0h_$%WS}t7Wef}6{)V$p^pf! zQR9cd2N`LmSJ>@c0*Jf$n&IV=AyFKfd>kCSFMfI;j2-mer7iSUQCKY17?XLoerHTR z!5Oh;H}lCnZ1Is#_fw>Uv3eCz8adBs&{3h+OV8sDXC!$KRXD}>q}<|du5k9ch@2f> z1o4u`BO1AhFzIxyDOi(&g5*mUzVRV^4#Z_Vq-;JhX#R(uZqeKTG~TL?>{p*ZW)QaF zN4A!R0{BWyJKmR$|J(iZHVl^yGczaTIq8>}-UfB!K-sGzAeHkskGoqjB2~vbjHto2 z$rRw!K*PP^A`mS@5#RT8l@`~ADupNa=M8KI@}^(o1?;Y>$jGUyt62Vva9X@0WIJ96 z(~N%g7EMn_;s7^q-YMaC_Q1Q^HhrJt<*hC2_^xTYMi>%%0#tsU+R;jQ`5iI&P>QFf z_QmM`8>-kA&?#9G_d4)=jq8H*X+Ym&L~sbsfosAxqQ=cG^`A7dHS~}@V0h!6M zpsRyei{2V0G-yKDkZ@CBlyV4NQ&6<$dQ)O->-6K>S8hP5^WEe@`*Yq#u@1FOCb9O+mNF}L8DgF-7Wv(|ri@!sMJYW2+ zcer~C)@Az35%VxQF{hoS^Oa*yQsn|6{EnrzJwY%mm~6)d`{P5BADvsZdq&+; zO^SDE3)a`!y?tIxHL3u?b$s&OSf%i*HVhTQ&!7bBhgNj!0pdCn;qIor`!Cye9h6+t zDlBb*;dsUa35$GJn|F@(FV&v7Pvmf(g0#S`TY~E2k3j)*ZX)_4`m4Fc#hd}m#K!nY z)!_<0e4JxB(E76>qq&-)oZ#{Ivn-fU=oA=58+dEg+8P*zUJGk%qGEftmNzr=_Hc(4 z?x^BHKwb=Y8qkLskVLPNY(r>Y15w86-sYT&10RQ81q%MdX=zW_({m#uNxA76PPj}6 z^#k_nqK>ZF8Y(Iu_beDmAvRs~4GMp|%Zpr-a7W4b=R3@=bj3@RKdmu($xjsfkxv%}{fm89<=LZ?E zf-U3f{p^nR+GuEMxbvO|J+6wWRJgluJ@#$S%Cu$nuxfp!=_n^?)cBI6Z z_si-~mER6pEH#rOL5gk0^m{vDUkO_LvhMtFG33>&H|x{41e)Eq z|2@gqKki9siNEP_u&{&h!h#DafTiqquwZ_$LkCR9{t%*xt%V8r3^U-UMDa_SV>vX} zN6Qthejg3zXemm%cy0BxARlTOS=zdiloM|xx@XZLfa59jc6Lj~|4xPsrftt~M1O={^$hT3ehV@m#JP;O^`5gjNH~YUCtUUm z0Wt?(9oQIb5KO)~uZUx}zoG8EH<tKmvXdj`LuTY-U;npLNqf7e+hJ&27Ru-T&D6$GKOf)QHT5eJ_^wsD z&Lj3<*RxV9Xe@2t;%tXH2%ZGpz z8!`EWQggOl=)2)2gxk3!&Fr1ERIi+40J_0!tU?9(l#yg( zkku#Qsa5T?=o}~G>IdZ;oCH^_Hs?BHFX;;DmRPGRwBJYkP7NF1@xkzd7#95Io!*S@ zO)HtOM7w?aik8SEgH4clB#66RK=fgiF_Pvz{!BO_IT=P$_rAb*q6Xa*9HZku3q6m6 zlf%2zVxTwKhv2OPwjx_YX^Ixq98d%a6AeSw7*XTJ%*CasUd_kncXYz5xrp;gAsQdX zX)zA${7!|UA@Mu$ha0n7!sCBi@?q9^poLm?%5do_?E;&K`&J_) z?%IbIE$n78tGRI67KepZsjCYK*al261@+G9=CqcJA8%Mi5LM>hR^GuG(v)}B~fA>>g@C32&MF^BvGdo z(3arTsYr2^$uEjH0~zJ+?dj!RYSi6Q%U?5Xc7tF>&ziqHRCKj`WgF@|P}BUgYupfp z;T>NiZ*_~(7E}oUF~@v4j$6Cf8qy$yywv;c{c7Fvpd*$1strqbckICR5!hf2jM#fw zS(?~0(|#`IhO2}$a6HT!JMYi>jhfe7ap$>>JL`b4rIx|zwt7+`f0m$nD8e}q*yYcF> z)E8k#WwBKa|0tcHf0nDwg=k^Q)H(6$;Ly0rrQKXRlBZ{`mjN8I5}8--?j|I8X@MLa%*;!?ndVN!;9oRTkojx z3j7^O2Pe1W9mpZ|MXc=&FMQ?FEekP#XeQQM^*fSY<#mxck8;9+5W=ZjwpI7~0ruR= zP$cM0>=AYaxzzp57+Mj#!3wk0@x6GVNeG(Q@U?Flup8&ezMFPv;I-0t98K;wQt&?5dI_*7` zA))78f9sN=a;?Uffgjji=8;_9HH2^wJTU;vi{)l!RJj zt|CA;jAY4_kA&$D16Wgl!l~FuHEWI}RNoKZtr>>2ZM4_|ubB`96caL|a`z{&NaXB6 z%?0xuu2WE4C)fk?G*8e2Al5GK%i%<0T>eZN4=*FGH)gt%|J+pRu$K!_cgpC@x+HP}?CbHDCfn3>> zKQRL79FX9Y+Kt(dbDK|aCMdU<(v0dRJV7tb5r7ya}g~9*or^s+=VFCAI zmH$5Yo|KxS4NO){zQz%96D~s;#NsK>1sba(h$t{u-ku9%x$hqGmfL=IsX6 z`^gB;BdPDEndkbSsH?C=!3$r3l4HaEO?~(TL%69rb{W#Uf)R%?;0oLh0=vLkpN%$b zTbn#|vGqV4TWld9$aUS=wDSWW03j>?TecF$a7yMIQV<#J==etw9iEkShNHeFO^#v@ z@?4*i4k_TQEJGd|bdDX^_J}Isg_xpVJbfA~_R|<^eqJg(J?r+>r7wCKSLk%#F%VBm zbgD>YW$6qw5a2YEqXx|!uKAyy9IgrzO#{+%2Kt)8rA$p#sb@=mSrknpB?OqSc`(z0 z2|0oZrNC4Eq?Sg=EE57SS1d5W;X*56q+hLZ3voz6a@nKaJ}2heD30eL7eB*r*@NpiU{@S~;52VSy!ErU(fA*2{q`PrTV-jdA+TIm)tmbg{^ zLH3I@k2@9oSrIV=ZSj1sGEzb3o^ACx!> zn4p5GP1B_b27_E}b)*xhiKs$rbMVRlek*0oORz0Q5kO-^t3*JC6eGB-p|s{YMZiSO z+`t=5$sTbAXridey!GW*IKpn6H;h+0kCcwg?plrZuKr5nz_2jQybBt4=6b^*)dM47 z@AnD<&|qAe9hV->V<72s0?@ml5}tA3#Tf8mVelFcG?JGITK=9%;ZgEZNl9K_5%@5# zL_TJ9`KOJv^E_nVJS>JkeNiUzn2mWJ*NNlZ0oP*Z9!y&OPjqLlBK}{ zm_ivXdE$Bl1!A6FmL3Y1rvXUwpx{N6D;y#Sh=Xx}Ywo!>ISiqTE{)S(!i)%>dq)q6 z$S~RdK_`NI%Gz04TA~wk20jKUAR$(aq%(0eD8`N;i#X~I)ydQi|GWIqP*$b%i+`YM z%Sl^6_ByG*RO_TvT_}@j{7*5%8j|I_mC9V1rysSqAEgOs=U?KYXjAz$G0)=GtENVBtF1Q9Dp63#!#Q2u6C4zlsa zAYV8pgKpR7x&yfE;j=OGD&%EVVU-oLp`}Bi;7sBd2hMDr&TtTGMn&Et zfwz#MSGf}=zVQN@0*Bj+BjpYQPKz4eC;OcxFOwEVN>@Ry#cfdY@dI`DKzz;@4d@OB zk-nFsoV`m}ezh!X*h|u&Z}b}%2SysqUjV3TD48DEiJ~rAa485L7#Ak*^ zi8IEtcJ{nZwa*blb$vo zDj%o&g$syu#6>28SfF5DEXapPln#>$-(L444%Sbch;0FsW*B&phg#c$Z7li~pXbPF)003jtL*P?D4Ghirq zYiUFW=0{hk2o_fAKJ3jS(c9C!PBlPDXSFRs+WIxU=|wk5&Eu#K)FgGFumMCgLzO9& z7p2hG^lpTzkh7yOm^6s=mlKx}2_xdVR*x?rAaJHM`B8qkG9kZ7KgL;Dh09m%c1L@G z@&DFAai*O2Wi&~NS{IVa>m_2499`W7g`x4_*IfXJBg08akbwb)doVI<3}izPG}0Fj z05qbG-1}v_k~Qt5r5S_h`VeOA2|5*f~@DUtH8-k8xyZvfgdV95{$+GcLrAGp4| z6AY%MBY<&^LGlB-;$6rRh|m4P`4b^a|D&^0)RAo)I8D_j8n5qtGV2oMsot9UbbmGE zy0HCdSp=NQ3%&E-z*^v!Q^Gv!pMSv(hG8qQUno`Gl4zLv%!=rwyxAxTAsTG~S{=I5 zkSwie5vNgZAKer@PezV}mp$0rUiC)S4v(YAs3lvZ% zoDGfp0vxQ+gBRb?1(2OB4{dG(g0r!Zk+eGeEmqC@sEwXWv5K>HLY95P%7{!%pk<-m z#)UK)4>5M;NN8#bzN*KqxpI3b=VxgIqvq0&ZR+6nX;;$fpJ@$#fjbQhEO%WOL?}6* z)$z*pIdm@=BC+#Qzl*IAIRyoqk)7D}X{kBY?}#r?h18MVOB8|67Wy2Fb_FnkD@!9+ z*@BHcCKC6PV$6^9v@}qt!NldvxVh>2l-z%>8t!4D3XG*d;I<6M&U(M5!PBg#5|Z8q zz3ECB%Eyhrc%G;c10YfTD3%{oDPN5oZ?|`yqgubGgMBV@@vahFz(C$h$6%JW`*L5r z4wV-c7J7<4T96|mFxe8MK~@9aeQ`A`>@7Ew_-BLFvo)l%j_-LMje~Z}d%ej-zK{Y_ zRQH^m#7()@8}B{s-6Ih0!pc#6dtn<2 zLUD&V!7~idq-e;sRZ_Azx29d7lg1dsf)@@B3~;`MCRQR(^#u`TbZcReYRUyDID)v} zO#B1=4_t>~$TfckMBlT(B|$XId3D@x{>`87M2xSd2@&OwBI;ir*TWd;JiSU>WOT(7 z9vW2sWJt6ryL;x1)!>Aw_x$E8eIh~Kl{N+*h%uR0{2JU~y3l@+kI1W~4;{;S zFe@Fd{SqbHCpJQ+nfGNHVzlo;IvNe>5}=MC>xD=NOAx0h0HG0KmyW(}1Ofp@XOrf4Wfh^l61$x#&YzO^xzBIt?HUcVmyf!b%an(AQ3 z3((mCNHiJcS6az0b>9N6$T`NNcf?6Lrdo4c;@(HDpHchikM@lge=9-W@9{5F16IK- z{d&+1+34VX?aH)U^5i?N;$Vyen!Nv8Cbv9(T<*Lq3cV>UjIOEzm3`${yl_4U*9ZUh zFsuv}?Y1z~CV+#0)3(pc{SE2KueNO%Q`nG+LW>y`w}_3c!#HovZ{2fLB1MU>^?gk(YLiulFV1c%s7g=8t1;76UP#tZfEDGbLoqp!R7iT)~`S_U%k=Z0Rbz* zbcmNo3tOHg{Bn2;LUfK!>B45W)aNK@Rx4&%0$?PG6b?oGb)hl_o&?hg5NbTZ^96F( z%2(3|0rc*!gI`@Pka$lBzBTaVbL#v+2B8B|w+Fe}VKqD2t0a=thH-|KdayRKY+0ov z_^^eAh5DZB`xsV5@BP_~kk8=GCgpv^f+)+$5d}1WJBf^SPR6qW44l;${ZP6I>XfWE z$_R|dReMyz18|MOlmQxs4_M^^{Wgxc;X-o#4&;!k0%o2y&+C(2#%aQ1V_$(1@d{TW zr`E=b+^C_I6plfe-D}bf*ziXuIpB>Vhl#4RWv%%j`f4gQP4Vir+Qms z0{Y${axVazl?VP?E~0t?BpFa~kNrnxDoIHwzWp>CigCzcZ%`9KCu!+dS_cHu+q!#s zU~JnNyV(n3UDh_k+@b#nOpiJRy5-1+i*D&-O>G{v7v?P1K>V)Ro4N!T7JN<&2qK>p zzhJU?_2>hyMH}XPPp+8v(X$wC=)?RwyHWU;%)7SjXt6%yfm8|Yz5TxzeEa$yaK~89 zw30ysy`4pL8v=aFfPw`oWt=(?8*iQ* zzU@wwi1|=fd;hIJz#{t4c~RF%6v0m{&}W0^shlFU!Tvx=LUa z(DIwCVA*OHnoLG8-vUOXlY+*;H*iF%+8NG<4PHEQ2FQG;<_HQU+0U;%;b)%CrsBW? zNwH@0M>w3=BsBbp@zpex0|miJAg=;z<>vRcX(?}k>0fDzX$mnm(6$Efg)PnBklLTD zEg{H8>LcaM%*zKbfE31|8iM8vh%7!+dj+c*UPJ5qSA(&f4;t<}jyN|l+__D_glxd} zfdFvab}O>d#RqK5?s9M_4me3(TC9NC(Dh-V#I%DvVq#}{S5GFN<`CQ8xjJvdnT=E# zk*MfsDkA@!kFvj;ZJkSa1OzVK)B5`SWNk7UKB)$JnLr&EcKTNg=ZS0ezK~K!Yis8d zZjyn^zTXYGBqZLt%qj9z168QiWx{JfP)Tp#=A#eAKBxL2%aA=`aPAF+c6Ji!36p&{ zZt;FvZlAV0;bRNDbx@hH;7 z#N=FziZ#Px6WX0Y6_*{7wKR#=Sn=Ju*fTd^aMcwc01Fm zK>Kz)qgw|IPN2~>1j+&Ew!b<(jxMt^F*5_00C^WSvD^3LrR2ejKG6Nsq(Jzg5&TPs z@DedgNC>po&9axWq2AL7qGJssDt7yUxvp*#bMwPv;lCsQPbGAB z5#PffOuXvqQ1e3p)pl}xC?JDR;_U;XY6Of+OiWGrE`*SUBmk$Y=0t;fWZe5OG$*GM zqF|)C8LgyeA2@S4N$5g@ll{rfP&%I5 zUcYk$NNqRB!qrZW&X?HwuySw!FI4*Xa2lkhz>ir1)68B$FR=N^%#>B?MSRt>H%e@l z2E%ZkfzcpnWUN@JYo17$y}Y+>-cIscz@J~B2=6r`l4DToJouFa!01HZmzdX%Y@M@5mZB# z4k{itR?m0q_1qml;L$lQA3%|D3{1o@Qb?)*2CMP^Y`8=XhG1bWW7(P{y(};a8)|g1 zRfd}G2CH%Kg5fiksofoatPpDvhj)Pp7#-y-LjwH#K=H(h0xu&dC115i1wKjl#A2}} zF01bN+McRN#)5en1GRs--6X1kwaT_t=*weCjfi=58m0izop*0Df)@X_^;^sDI{Y z354{tFsOK*9vfmzq+HjKt8*|B8RCB1{HCd?sWE2&s-9b~;yr^DEDt_(I2`1X+fU=8 zqK**-4ULk4_?P{6-0zf(%X-)LpIh9H^3tOiT!+vk2{ldl(k0xYJPK z@dJR_Z>c0w6P$y}00avA1x)%W`|oZ)tUVK znRb%$8G+~?9{Nioqp2_fb4QOf z^Vi8Ps!FnZC4|zzzL4(nPP6emflCuyku|TsfwM0u5i=ihQ@*fwp-esi&C)h& zH+ZJ5uWcYxBoHDRTW}PR0lxXFNlb@n+=W$&W|a8#1SpO^>D%UX&*KATODFH(&vqI2 z3wO+Q-}AVV2p&^FGFz4}$@K65{vrPCht;6c7Ax(S{kV zywp>P^$}u+IA?nM%8_Lwq8rj9IAH#l1Ji7K*H7?bJ-WfUm$J#j5D+J!VhJ5 zq84(y={XVd{Z4#~*%PJ6TO$}!HPE2dEo>p5p7VB0t|5#k&7sb&Q^&?}@Jthwg|DW5 zsSzr-;xWFDrsa+aR=Y-IhDVB~JsPD;d#D-(DTn9o*zRhr6w93wJCPhe&?VeKe}yRq zL)P9tb)q{B&q$dvI%ZR&F1*!3D4Fn$%^o_MHvGw~FFOiAz(*jIl=vKOh##pvzYNGT zW1p!8G_s+<-uIgS=f${=Sw5u)y=8{}pzCKjv!T9pq3GieYK6db3tjsgxy)5gg-e59 zCkKE*D|zg=`0nR;1U(+dKp7_oaR4(=^yyd>6=;l+iiXK(mZ8;M9N&S(t{*#_lud<8 zUq_b3*?~*ZH3tj)F&V7BoSp$)<)GUAXL-w#PR^B}lJV1oCUUOYhM zKCvukvj}Nx$q;Q&GMHjpSzTfp59phG7r`RmE7-7x^M$|=jAvz88H|qhEwu-E1;cLL zp(G?T8kc))inhiszKjZA^z@0&kaR2;*9a!H-*6vZ<}2isHYw6D^eQ z%NcSKeX?^S@Z&cfO3Sk3r7j+N8sF9YQnhCDJ>Og~BLdcjGpV8N)9>2hoB_ zLIwd7?1Q@%1c{^+zI<1(EHMp}?})gmn7qRS%g5;9rY67kE<7Z_f9s8OKBGqNCg_J0 zOsr0OFNgFjX3Yo1g-)sn)kk;?-%>?=FfF=UPRf5_w0dNNBxI|ET|TH`@~2>z;N{Yh zY>PO7jk<~M%9Lr`$E>6 z6GgGq6jx=Ltr~Fa4H5!zzouGE+SVK`H5N?wXK7lESKT!zD`nxGvU|rB)-Fd&>?nJ? zPPiKS9b~mqHI764-MgOs>BFxvBju+_f&izQyxdOyaNUQ2fmz_5@)!Smt+Pq$A2?*eY&D z%?-M$CNZygT(dxp=gsxMTl)2(M|wW*jts%X*J0s`m&&rzmYCj6OzOiXIUI${$UV70 zfjn0TLb06_TugZUpikIr+2J+Y*VFNWJYst z=gMbKwS8ph!)flCvC8UN$vq_499-j6lc&`?{qlC>rW7~%es-f`K`ILKv>E!Onve{x zdThHxYI+$n(>|@}bzQ=2>>Qs=GQCmW>WxML;rZwpxxF}A`FC+TERrU9iyRnP`x{i# z_VJ3u<*?*QV&hdJ(wRE$6ZmRTXs@(HIJ|n=;4pJn&As@OkvxWZNhIq zN*n)fYf$2>N#5UwO4lgs&fTN#_&r~qA!J24A17U&FMyo?i zj-&_ExhS*$v4OfEeL`HqZS%b}f&5XpN#8Y`*}R<)8dd49Y!+2_w50F$&eM<$v>S!2 z4>e%YuB-EC%Q0sCSSr0Bow%?YCueXA9pZ6=PX?J0e;|3V-7evi9N?KqA57k)OZTRL zeD)7Z3H!;d5W+<&_B|K0hZ>#L6&`@9Of!F_L5sVCP9$Z{{Ogc40aMLr^E@YvA)}rS z4s)4#6vZN6k=GqBygCy|aXJ2u)z;JNS$;ORsQVZsP8yRHf`Gl#O(6P*P;ZHBxnK|CKA{v0e7Uoy+A`eb69k zj{+*$kh~tm-kD+KuS@Q8JpoT4PlEKP$LO zjJ`NX@cd$OyHlgpC(jg^eT^*m4a3^oFW0oY*#>g$OW(;1U+M`Harwri_A=W-*wcjV zfc@b_eU>4MZ?yf`llL}sL0c+$3^u|U9ws^O^SHbfi@0;xzhL)oiV>N=CG zO)!Y>IW_$pR4$d%yR3Y6RKhSvUM#_yab64|$mrK3<)gBrA9{aHtMvd&Mc#K?c=xSw zPxk%aXk#zo3I=-;zAF+|(&wUME|rIlR2ALt>zxlHA}HVHY)KAywqo_dQTO#1i%{w1 zr@lg+I6u2?{hRo}v~T@TS;~c3+y^Uz1TPo80QKVMn_Kms}xBHjsDoKT>+gx0*vANXW_= zR7;wND^ppjf7-TfNz24CRlGTpq(jEO_tG|9c8=4cga4?1A9LQNH5xbjUs~@i7wfSp z`^rVI?sU1y{CSYZMe(fb4mBarrCMFQ#%x?7Zb-KSvYGtwVy|HCURtTKYJB>Fod~#n z^6q9NIa`mZr5)h;1;>1f+sR1am>F4$UG#2E8L1EDelA>o@u;S|XSIgvFn7_`>(#iu zQY7Qd5zv;w&iF3rnpU=w@WGYCx0lN&HGg*1zOk{7oEQoiFz~gZ-&ou&{j2gc@OUM| z*T(IEvtofs?StESG!?oM{7Pz-JI4NoxPvL9^*v6kh1b>bj#qvd`aG5#O2X(P_t1}P zrNk^%Fk^xAZ$~J+9Xn3S{aC(^VD~3B?;egtn+vbf?C6jOpFBPNRO_1TFQxx-kJ;g5 zHG*yDkmcj??|1h~t9*}N$v-a@J}w-g-zZHi^>GL%T&YtlT5`lgF5&0OEf@A)u6rhO zPPlJ}x}h*+S(#VxidS9zfiB5B-$hq4?2z1z^4yJa4b^)XTpF6JECmc_qS(>2a4Rwi zW3SB2P5U!-N?i+#ChZ0b*<6dap!>nX!b0s2nh&baz-@I13=D({1rqN(&W;}Qv=drt zW2N4ps~j3Y+~tDpJzDZjJwVOw`flUYmT&%55sk_M29 zAB|}b)ILvPVA9Gl24j!DvhYbIX& z$aewrF%i3S(7{t`J?^=hDAAg({rR8m72Lo6N%Q-?mnSHf4qW%&NO6ll!+Ke05~s0v zRF8ufp^gW!XV`baF2I4H!*j`a$xg(z^HHJ`nBoB=#Aqz-W*&0h-euFWg`!w~fkUQ9!CJiS4e7_s$d#=oCh;G@6NO+)2xNXhp26a0i`?=Sdq+`?s* z>r{oVbv~j~`S-+MoY@~9*^*OS?|3^Mo8CsM_f8TlycJTqjTnCW_Y{Eflm zBbs^s6KVU8AIkE|c~8p=|2eji8$~G)-{Or+z|MsRMm)v8e4eyfTqAYL{yaZFR1%I_ z_4n!)wsDzolDes`Uy{R>0mksk2WolDGm*~j23Rs8Vid^I__K6BG&1UMb6+`0RQgfT z!$5Lz5f+0CP+FS*_$V(Y2U%U|*HN}K$h?`DKA#!x249_5*}0=mr-fCFVvG0?Zg)N~f31`mR@ZL{3{S85cs)e2XAaZh1$BdHN+xrhICHZm`z&Xm7n{$yy8Rv9+`h zE<(Pk+u54k2a^G)^Oq9uI?Nqv6+Nv?*r);{&qKTmgVrUFdCSbZ6M=u=_-}YQV6_G) zGqC?@Bt2dC6{0iq6oZb2(CaX`fmf->Fh!<%!u2hUfB0*u*J!@pVlc@U`S=6(JmJGb z9cDBzKU<5gPq-^Z+VaqfyMBd7YG-crsR#ZZz6-vWS1FR$3}#84c^|=4(d%}VW7!&c zeMG%|LTFO#EEm9CS}A@u^kSeh3Ij8$3W>JV)QVnbR1(!b7M#f z$Ie|lu6zEvfZ>hiy>E#U`pZMcQ!{N;w>9^luZbB0{rc{rY}@3K=Pxw0)|3<0zgfib zI4-ND%0=9v6N|rpdU=u4S4UJfJ`S4@oi=Q(pC6<1`RL4?2SAbb+J>~(t6aKOu00!T z{v*VubKCAG2KFKYL#eN8Q%Rd$_<)0y7aI z`peR1GsN(m7SV~C9C|+g_wW*zURBxQB_0up#1EeP*M%(#t%nN5qWzYANEI^E7ciXJ zS(Z-BE)4^i#JM6p)U{RYXe zl6O501iPEOq5V9?s_9H-;_XsTF znpdBHNayT0dZhfUx2%6kMX?fEV;KPb32iH)?VL3Uv4LN{q{j|tGp1_=(UNrE`a&Mq1fvbX&^VdxmdeL)Q<{6wVinfk zmIdWFI=o@|0vgMyuM4wU>d4as4@OnDTsorIi(WNc(891((zR{G3Yde@v=5htX!BCV zo5yG?J{9JcY*N&{%--4dB&pN22xf*XJ=tB;kujT3QM0MG|rd`TeAFSe{*+r_|^8WCfk}5Q86(cOlsBp zVKiXb@Fz+9m62R+DL|?2RXd^r+v1iCOlT|Y(@PTl7268}=4Ra=R|d8WG;>G4Ihe=T zjO9EL7Jez0s;gk(h`DSO*T_p5lL0zhismpWmxJQ_LoPs=S&1>oK~v-Iy$wi$+i7lm zQIWX$lezjO4B(+;H_|VSb}TX(1hU-Zoi@6HaDZ@y^mdyYTMLPNcG`meYiu8Q%1il! z!;}bZPZC6DZ-wz$XP>(-hg&Bbl2!**Bhmm|7q;1FdHe(mr#wdo{|A|AEEgQEz2IQ9 z2|m%e?%PI9!8($aq^bhb8?*8JZX4hwE5WVl7erN?Bq4T_8|&Wy4u6LIje;S#(yIqs zy(-}DUi-WrrLHbT%a`1aM}h!Rm%H@P>~g`7^^ti%Lw(BB#>a~`rPmERiE_I~Y*i9@ zCqu`pzp_NLsU`!xwrE6o{z%F)Snc-EvR@FTMEFNfz&rU(+t(5ZZ8V>LdCbJex4ygg zlr2c@Gs;iQNOi3`6LSN~ZIFjRQOa54`^q zj7a$vetdJ%j+&`4mq3|~5@V{RN-4u$GJ+#tlN~cgPohRANIN5|i~0=w&xZtjNRFtZ z&mH4O+l}Y=e{H0O{gJk_#n0%?bT{&id)oWcX;|M;i8^JFzU>Lw^-G1J^O?L#y}Tjf z8sXbc@|ZmwF8z;VHuGX&&$*e;h^{jm@YM4ET)9bg?vus|A0mWAHBqTu&**@~ z@W+23Yk?_y|2dOi{|TG?KCy@y2Ig zC3PHMG%j~o!0b>aNIw#YYpUJyfbI0nC3;()g;3sYhKyLZzBNeO^_n+JXZa|w1A+P#4=$QhM3u&!NHMD$aj)_oIVS)Qxw1>FCEOUKpQ7 zc!L#v?Sc@IcURZbYH^`SO|^#BKw+vLx&JyFrRU$N3U zlmQKF;FH2A_Kg`HIiG+h_(w6$xA*p;TaZwl;gqBF-Wa4DcW8sNaaI30URT1ogZcWueP>mW(5?J24C9gXYN5$m0?w)<7d>lH z1KXe-kK)vV4AX(cTOQ+@b4zbv_1)_hJ$BURpZSXe_(v!HO7T?OszH`aTeW0rSJo5G z#ppPRSYtwZ=%g_P4TcUC%LJQib>C_K<5>xDchoHbdrfBS*0pGEot>G$Na$tEnms5! zMCUEWr2{gT1%EIsa70NIt-h14PZ0X6&ZA+sfQY3A{iPdEK}MnJNu6v<6} z?ti>}A&O7_J8{M}H|_6v|D9-&jxH2Apa=ExN)jq4VPX;=->aq|s*1eqECu$^J6zPp zqZw~_awf>dxLt~OE{`tWxRV8WKyIIwJ`3(4HC<5Bg;X_cKs8C$Bwe2{`;4y zq+xIwd_6(fR^RjJ1nL3Dr4ysql|fIG9+WO?FrEhMvYTifWIK7CtD|l$Onq(lzi#75 z{eLj^-GNkx-~ZPxGRjV(5+Wj-5SiIKdq(yq^P*%FitH6KGt#y92-(SASLU^E=EZgI z{XOdQ`Fwx=!~6ZbpXWU1yv}*fbH)%Y+l~81C90Fj0YllC&M0toeYQ~uawGK1P3l4C zi0IruCL6UCXBS;4~nR3|TO5aGJIg_rCYi*Zmr)RUk!YF+e3G}+4QX%>^OkTz+8O_} zEofR8u|rzo=+oG&wPnYp2elZkDJ#AQetM&#E>lG4R8N0pn=jCkxjf<}NkjTH{-ZRs zeUoycXz#^N?p>8vIXb3|uT+lrTRBTyS04RZ(7OIWE9JkvTJcZF48HN-d7avx+okeo zx^m1K^x6>hIUFcXNai*B^ZdDeTCfDiCq4!BLf8vc)y3QLZZ^U)$B|hNq%W$2)`_Vn|dgVU<|4|t!9jKv}z(~BH zpy{;9{7lQPjUmtIM7=8?btgU zDp8W)eeKzjzZ50s`yU%Q3_4Ii^ewC%Ud;I!S}eA=z|X}e`%&9aA4GC>g%Ry@a1@r} zjtu0rIP;@(57(zhcRb3T)$flL>y(rb6NE-Mj1?JIxY#PJ4Iix8H^lN2e@Ogam(&xM zg<=TgsXeI0JsDiJ2D<);xWM0m1Qgk7M``iJekg6d0t4AfWO;8izAW)JVMi zvwluQ=Eo&zsr_H5A^ejK3eRr+(EN7a&h9zLVHdw4d;kLDmXEZbs&y7==X3E7zPn-$ zYUu1xL|8I_Z34Za7Qfbm820ULRYSU7*nj%^UUU(b6AfOfE3gMf6G*-%!34L0e(9Ax zn_Atfqo5~Xng}~P4e5nJ0b;{1$;is)(ox(2ms9_nN=Ff-FJ*pls@8V_z-Y6X#)fu2 z=SVdD1AQz!fV|V<-8_i(6}lPt6SRtWbOIy{oUtELGE1>>3)9hZJpGUOgY!QnQkRdt ze;=R*D1sbUsSDFrgXn_To7?iyCm1Yfc#^_^#GniaLjw8|Jw>+>4@{JlRCV?AGJ-($ zw|~3$TI&O~CrH_P+*TVfeDn41ERR*)#xD-zCA`TOjToNyC(kZFw3K3)m<AF`<1s_}o76Oa&B-HxEuhj(bN4Gs{I-ky3a6}6g@2p3ii{0W+>rE9ji=?h@qfLVsU&FKc#J{&7I67o_i_=xTRZQK2F;Ib z-1n%2-d>Q^&IlwOY-{kVo_XHMa54mNH-WI~;;DlFZOm(}t2+j_l(jh-qBWGNrk!|` zf$8Hi+cJnig?HwFwSY>h?g6@u7!dOp`WW1J@16XAOtIo}rQ1lfN?Izb(M9Je7(GAS zJ6#QgoPuN6B~Xy8#&u!w;JHdX+y6IdOWl<)t&*uR5GrQ-{Q13TkBwjX;DQ_6Tc;1! zKyg?Qc-Z^x25ETgO^N9DKm0iUH`Tl)g-}rT2#nr0FmPY{bK6B`Q>OS`?r&`UjEox~ zH}#&0D3p(34rW*3_TRHV)uqRB(1si>5V=Ecee&lj2y=i}J?FklIPKKNAp3IW(4Ip* zOBA|$4%)xi7}C{O{eP>QgS@8V6SmtmQSeIN1UCI6m(tEfCDo_!W=9f>uB|=R*WY3P zRk4>|Yq{%KSzA#76nJ_s+Q~|E+b4Onm*LLq^8)AFY)}5nz$e+lTrMw!-Ixf1U4M~T z8vd6j3I8m3jwwLxWRyfbF1nij#c$l*Jp5ZfrXPp23iF_npGLy2%;X<>dZ1gW(fh)=s|5w@xbF z5x>2f%g*nm^F4$1zZ1b)D-b=o#Q8iYMB$SBrTNoNWl+ic^?OaFw@#Xo|7x;dJPN_8 z+vnAC&F3SFzN37dF9Tqv;j7`N!8$)r~j$=44!Oi4>0TcG9XU>+sHpeb-}-$WVzT+^E>R~iK;mEXH%DQI7@l0R4CFX+?`CM- zDXTbz-utgD!Fr;>Nc#@2J5qB~bCs98U+{}wk7fT zJE%lVm+$I{v$xTqmH)6?>Y;H@;EL6{h}Rev$Mt^$KDbWHaObJ=o@{x=nUv;Mi|F8| zH_Fq@eeMxdbBPOkFRR0xH|NKfmxBs%70uFdHCvB{CE8J#W4K)t0Wm)7rEBj+BXjQ> zCn+h@2z{XA&|*_kX1m)@m-*2;IbpLZvqYUMv%R=P+P}+QBC%Tgi$nr@~IhNLJAEHdlMfp(ZHFm?Q%2i3b5cHpB?sqLNe| z-Mm(ubko7StE9jEU6-m~Y}H;G!OxREPrS%a))zc4Oq+ZFnQP~^M;SJy^*eUbL=XzZcDY9IiL`|q*dgQR*>(p0mWwH3N@gc7flLz5_3h> z&4P|Pq;UvbPaN*MMp21)ZLQb~y}>5Ry!8jTIOqnrEiz0>d@7UF3krWuwj5VO|4`x< zr7?xLQte{5%)cy}D_cG+vd)H;J|9|jxaU-Zd4QSZc9pK3+O$EBUqQIS~{q|i7~+S;hK%gu!*f_2K4mwn5^jN;M38*KG2y_(Hum# zEp@%EcGyW0C|o$Msl`-4(XOC8!)8zLw(IzoH&W7o>4E^4=CgfbMT$Sp8JO9hA5Tu` z-tFH`#%`@9DqJIOkfyFP{W9)9*^Knxowl145ylR-o{F#pEk)T$VWZ+C{jdlq7BM+7 z*@_5|@&xHmLg_s7waPyI{VJ^|WUVLPd!&xXP7gGsys^o_8_7$eSR}5){N>7`;>6^n zDU~MvUlxrdogRF4`K>Gp?DA2}mpuvOv%szCGy2xbJ#(B*nGxwjS2VvMuW~%-jw*TO zi5_MHiV}Fxd~(EksRdhIffnzD8*AOLGHDqP^d?aP)Zd*QhB^8EP2*|BWI$xqMFWOb% zWItAdp~n$$3>?*y`AjYt;Tw#=opnl|+u`Q2af#-b@u$@_3(uAO zL}u?OCYc9r&ZHaGn_&A4TK40D_gOG8ESM3>;6JZ$Yv!0Q(r0-OFq??r zBRl9Z0*lH%N8dIo<4Npa4RZZHhQ=Dk2XCmD3&TL~NMlfB5F>r^it|cw$X^U;FMgld zRgl^D;Cy$rTj8t{HRrXdbP9;>kE>-qJ*&N*jm!Iz=-fvj-slu;2YvZ>S0#vstZ zmi0=i{y#dOCTtfW6ZHBP5y1yb{jOF0AJ8M&XAP;5(bR?}m!JpzU(B0-&K-o(`Z-Sa zut3iT%Ix5vS_T5TNDtjC4Bj-yY(X(=o~~zL-MHhi1dZU8#+$&m77@IQ2zGI|2bC7h zkL0pS`3^YLGa_@{yuFV=NzlUM%NNU~qWyNTN8=uk>7eHgc;G6gBVj}-@PPdq@&4fC zta(875ew86Nun=0F1p+Fr^7Bg;5hX`@LpW-9zC?!s7#+L@j=iA3X5I9k+1|TMX_>b zH}BLq;YNSeTY=c4K4#zfAmIIH>&5=2#JU9qVOW=LTznPs5VNWQ|7_v`R(3upZDw3b zgzHDa_u_D9R~#COoyEelTM>oWCEs(AIQVUN@E)ps`?M{Z^^(!xS_Kq4IlFUMknVEU z4?TsN7@290>l6+LpXp?K3_UkIE;BH5M?29MITV}t3^i|=BF{*mNF1iX6EU-M+<#vSio#4SyDc!?>F7!=+A+?;Jh-`b&wRu;iEWHyB4BvN_P zNcqhA!ds@cPlEF}XnUK^>p5HlRwUvcjK5OgCeN6G^XxXGj*Kvl=I9wz;4usti12M$ zo^8GJE*IPuA5}mxBEicddimcdi-`q1OTc~1APc%OKR;hkZ4l(M7??Nzacn(41q7Yg zI%?XYNxptD16D4dG1QL$;}QjpCiLsbDy!_X_V*HcAgJRYX=Zka$HvSkbKp35J?nu1 zm(_9j0;axXZGQowtA*m)zdv)iTkVMIC1M!#a&E6RM~*pEx5!)A!qH8LZI_nOU9TE}7gh{&B>3=M*h@lI%0nIEZEWk`|9o;*-<|&# zC19z=Kbnf(;Fglj&te{!*4x*A{5&699B|SfprS3&Vsi1vQ+iOp)gi@u{4Kk3+Vj-e zVciIwBm1Q9_Brn3mKc+Z?}r%D(ApN1FqN%D1TT@b9<6@ii2b-V&POVAMevI2iNPnW zq9ccwOG+EHErCZDk1&;e77-iTzr|3dz|r4SvK#c_eRPSf;e>;?ze4*h?B78lse?)1 zN~~;$h#r_a1urQ=mjhVD&?BgfN4_O=;Vv3xYVl{TE%0+3W}++mEIipLbF99tR9b3S zcRaw+sY)fE?+O9^_A35DVUn%koU*(E>3fPT#NRy{*v%kC4vI}~e#ZT=YdNEYt}z`s z{ML1SR8z(UFCFQV)BeyVR%=gOvszLx8nyfkb^DQX$z}#Mt-w_Q`nt-kvvZJbp(4;) zYZl(?b78?gK5m`2O7fEmhy4B|FF#zNS*W#EHu4lDSY8_XwkdV`1YS~tX}=oq=R^P| zXo?UABs(oAy`IYr`Ax;{kgjZdy0Xzc-MTMg|4t05rzD%|7QCN*s82(-RU>G?ke90`9+i93g%7q2JYH4-R7^ zN^#T*z1*2!=h_qW3&{wJsg9w9t~_boC(904&36?w;75j2%3s(me+*d&L7~emE`yT6 z7zGBx(s&BXAebVi+>I+FQ6yyUy`d#2_C6du%z@oqIFqpIj)6eFEqHR-LIm80{1yU# zw^O#_Iw=K(uMdgQbcO13BXDz#sW-Y@>kAhc){uQGze4Ljjt$kv#B?{Z)FCsGezhnb z1b!GTn0Va;lh0<*bpbvZ4IYZTn^=204|`}l~|@t1`i_F^}~Wx z=pf$%LlV4#e#OdX&Ea9@^A>fh4$mY20z=#1TWZFw3&cqn94B4gQH4OX_|H?W45T5U ze?no1As_Kd&lMPg|9LyHAf!#`+U$@wh$}$Wqt6Sogf&j6fZ>pL0Gq9y^FfaeXS-I< zs3$rrY126=5I~$(cYMV&d$Ix#PohSp9P_xr*jlRg92(N7!`{)tcu{?)XumSALT(yJ z8^yKR_=4$v-o3Niop+Kv__?Iw1mbev4hCm|i#ASgKry zSkOo96yB5!-mfx@V4Q{U#pULTNK2efaY*^>kM*)C)1-$r+nkd^LQDsz)JMa+x(c%c z`tDnH3quC-iq?mQgZpO}(Cug@ra@M>N0*6OW&Z$L_=s3HjynXc-(m6ZuWjx`3xh2l zNG;M7I?yUUF2ps`%hIPX8vi_@JpnL33ENs46hm!DA7wOf);5i~yv?PXiORgw;}X1|pz+~@LBon+)mo>{5vDk+SZAntx1&Dc0*@a%5UFx&2HHM9RJDFX*ZYD z12+!`yD>)SQCsCv4j*mKUWWnRzj#zYTs~CWWy7{vgN{b;3M6faLzIT_(S;$8<+X@8@{>&Lup*;K$T!w9SFX9NCvl-Xmbs;_WY}#DOf{ z+X;GUE_HAeenuvJA~_UpoAn*ANMzCa8VneN1e8IaMqV_81PWiQ@Ab!H?BJA(kTEcw zCxa-PW=jVzIFv{$Ck+AHijzKiQAo{w0JE%+kXh7)K%C%SdvNH!Kz8KQxA4Muodh9_ zQ>L1*mFeN&{ll`_@1U4LHaRdz&{Ecopwf}gagCjnH@vhgg%&_p@$Sjz>n|4?j^=ja z9_@VqjIWI66^0KH4Iz5nU<*6_L(L-Qd8`gNBP4yZgX1d8n~~9BX~S~eprp!e0w9l% zpBhuxw#tv&1zTD!P9rg|$?&f(-@6=Almb7UE%6^Pkf1Y!YMMnSdE$W`p`z6hla`348Q5krI?X8>EL5E*QfE%UIz+8Aev~e--$nlyk~YKkSzzrh~a<*E$_d}v%jut zInlCOBXKim|ATsCS=Bd!u&WT(F)8LD2|e!}TXZD{OZ~FK;mc)E>!DmISymxY#~UrI6ki54Ob1j^|NxLDf{05Rfh+1 zDO+_FK*Is9w0I?$vAuYV>JY#YG!3@95eAG z?okRyO(xk(I6f;EyJK)o>Vt%fy~;VZ|CKMlhEdG+yMN^i{NV)v=32!&MYolnwve!h z2JF_T7cQsxkU(gD;8klRh+8+#JIo&(2S}a&c(Rrr3!pVJx;Hg@CkPE#RvL4zQ>Nb| zw%SX6@x1D`>B?BOaVB6}>`AY2a~wc*z+azEr!|6?q7n+#A4)Sp2n+E6DNnxp#D4~d z`Jl7<16y}9uM59iXLfcF+7va1`wPQuVp%G{a`uZ3(BC|WhUFK1It_WezyZD`V2^7FgA=825WRddG zA+}qgb9H$aGWCO4o#%bQd;9xg9)M`&weTQt7q}YbTtW$im*nT3a-R*!|Nnvf<3X*ju;)3*B-0J%(=| zZK&k5;IWnzY{dk8$Xb=z73@C_rhOkBk6CB>^o#^h0Kc~Iwb@WdCqb$650sgW%USmw ztuF{{!y*J3)g`=Dpvr0@YUzaudqOX;wbhW>YIBw;r6gp+xD^{3z}K#?1aj zGaqICcUf??*A8dul${Z^i^KuC~J{)Fm^<}FfaBE zFo$*UIHMUK1;f!Az(UiqA3=X~e+kyr3>`?hI~;VJumS_x;fLL@OTe^xq37)MY}V3qwKaVES0p5P zq2jph9DrViKw+MS;8cDFkb7`feobi55cA+g(t zlw^6SkvC!!Ec~^Y933oP{f7ziized36#SWM6FLa#hX{c01oeY_(F+~Gl$q_E^hRq( z=fNX{fb|v?*mrG24Fy*oc3zY%Wsm)`jgM>34Wid0|@NPmK|)bF`f+35^R5($UdnpB|2$ zHjxgHg1HI|GDH2b*1$of z&&-C`&4B%pWe#<8X-qjlmmje@Grgn3nbLj-#cbUL05E962TtGZTr7|z${9Ri;u&0X z&=S;R4EF*0W9FGTM?BHzdyqK%^;`XTNcI|*4R(OQqL;zW?I7&X+V$91(isStp9g_G zDXSVDr2eFK039cF@Qgu!@IOH8n4X(H+;TMM3?!}087%te6S`;t5b-);ybwGL+)+Q! z1=fEdJH4HzcJ6HLU|4e8#ruX{0whEcK+2~&UCl{*Z@|W<%N)Zm7yiL}KLFssC2I~> zsa{|`so}k(3CRr^6sSpHlLWQYfSKT&^w63KXg+vB9i}`0j`}YEU;7U=@zSX;0GNQX z1vltBzZ~>e==n36uaSkV*V9W~mH&{6s3S0*eSDu-lZMai*-Mt!i>N?c;kbN#P&+@~ zW8D>Dd5f04oCuclW8XkU*tAEq8lICg0VEo~CaZGZVFBapyMSv)`7O8&bPzn&J^T*{ zTW1H}j->{w(w?*}4?;s^;(7F*ed8b-v$98R*Yr=lP+jF$$@V|))hPt(H-(~YnuLaE z`*U+?w|n5hMmmcmuE;QgS^47hQ8I*{m=>8oGK~u|_#!_?`iC>**_BV677e@uSxUuNfYYG9}~`U{*Fw^iD2MM z5nuo84`FFg&<&i&Ffm7O@?!3PttQVRN>;#P z{ivOKkNfu5`!eiz)!HY7ZxTUN`(rXWYFx!U`|~s;9a{2AfIa2O#+A{3fyY>Y$HHn% zkJ%k6(#o74ky=cX>Udc4 zq{bh)MRr6V{3jrTZ17Q(b4WcEt)CP+ltlU*OTzL(Fl_}U17z)jC~gEIJ|dMtK$S}V zae)a7)Zp2g>lUq@Zl6)JK$}J_gN6AL|H>s^fV=gVmq_W;R1LX&d87^<14IC+3k>{i zaJE^SFiv=VI?cMRM}S}kgD#8Ilx%s4gR}iksz$bmBR81vmA?lX^n_=v^!|56@k|Pl zN2>Le|5*BRLuDpe+Thv+HkToG`@YfjUkPD+0FZg^A2Mexu{yaC6jFg*^cC?*It4cw zK!B7_soR{QNd>gXn_QchThQcjI?|$T{KXUr|+0SZHNgQH$mDcw5 z(!fj{^gORNvymstx+{_JFRcN)cE*%}bdcjr-vv+@bDl;CJ|s+sEbo!`MiVl*~*dFp>06;jko8IaNY zT==jx0!k^j<2j&VJI|e{E|whf?= zfG{qrv5vUVadG%cnr$-kQ|!697pC&XF@LFQcxIh6#NL~0ARcU?_x_rWACqC$7Asis z#SVymWil30frW?5STR8gp6WiEET~)Ft-ZM31-3Htsng$igRKo+nA$Y?GUC7f!NqE} zK4c5u!e1_2M}Q^K0eUvvOr>yZ@86uKK4=ot5Q!WIn^3I%b;g8Sj{ns`DWt&luTB74 zL$d__R;z6-p5GN+v+E2Epy^`f6Rs-^ruID6xYcj|_hqcEcbWcHR_(&UqTCt%GYUvE{Z|~natw?xiseBK;3@L>5k+4JUl6l8I6*v~q0WL?fOL z0&jE4+h%o=M67CktyU9Yc&r5xIsU=D2Nd-_>b%YO3t7OE2|SB^xo}T#?gQR{vwujh z4NfoZ7d-M<%gtKjN6gMxb$VGX+!KYCI*~#$v2jD#LHI!ls+g<mY1O7T_Z z64RX1^NywlYEDv@?fEp?>pgqLqaxD&Sgix*n#XwTD!N7DNMpMNuO4d+$q9SkpP05P zr%(N(bjZ{%-mY7jnlvwI8tKG`hz1o7I>Bk9UShZRTTRKbIDQJ%f4m~#SU?zx%IbN< z$HSB&S1F%3^t#pZt_vyyQpbN{&80n;wF!HaO!0kS&1b&2&Cv>z!tEbx+N@B;$ z${QTDEscaN_)NFT=qi>5TPaq5t_x;dqaj*bzPU*{7=PIKKg&Jh9@l`v{xtsumxqX5 zYIi~k%$?BoCC28q<%wR>yUMY*Ay9irJqt8N1Ur`r-^XEF(Yu!tI4yY97muYnV*)7B zTXA?d9zG&XX=IwM|5Q10{zp;jBS#HCH-rMvFoj4a1pyP-p9a)R$<(Pk80Z`_eao~b zxHi$K3HZEb+taGitfCbhxRy;`tew8Z6D8F8fPk^Q{L^_6*qArs6$@bgB@G{Up<6_1 z#8S~~rt@k{ju;IxOqqrcy_To*Pig*DCjtYc7fx}23#>)Z?d7DDl4yc zZo0&M?l}zInQKhxm3R_vc4cmgM96y94x5mDYNKoPN9wsefm@q;c-mi+AJ~76@$ZL; z3!Zmg5qvehx$3ekzrE<;0Hd$OP+**VWjZr|cT4?boXu1R@dC!u*p0$LhTmjvA-S^S zeha`f+o6=+y9!Ja(<}>djYa(j?xhbZ6X`1}DtfvxqL~Df_&`>J{s6C+h%u$)@o%+4 zmz^QtZ6e_QzXL+edzlnDyiq$o=S1zPZIv4n-;|#h!n^Lz^I!1o6F-AA+v#7w_Bo^v z+)J_{6tcQ0Q4YU@4w!)_CX2F(W7y6Ug?E!kwYT7uLWjD>)#v+B^IGKtr+`T-wCWE+ zxL$NFSi)Q!`(O7)dxX@Va`eJh(CFV&1e%j8LkCu>YjzHF*4aRj7fDV(wd&}-Ib{c{ zczV@_UiInT%PHqWT3&uj-BRbi`L}c9LA^Y?>?>gn$R=Sp{0NE|!0JJ4-I|Iuymo)QWezJ>V(OrVcb zfo@wnJt)(k)$+b1o6M1t6dycm<>;|LAC*~0mrmI*-v$M;1!VugIC#j%aMZHCqD8(w1&?VbwnyNZOO>q z4hNpuAFJaXE9<^Us{ZSupNAs5&-|$f18G8ju3j!E=pS7hQiriw_)07{{EPM%OC+a@4!M6KL3)&OEuLYI0+Oxj2lwH%4-ANF{4(7bs5X?7Rl_%pOMJ z{0VaW!oPVGgex5IxQgS73ysQZawkIxiHuVd0!V8&_H9I#h~BM7WOTUJU>)?XYV8CS zQ($)s-~wnaZ}!4|YJkn{Hsszj_C0ZuM?dx}M>Bo4fvNhHLtXw_kA|c#c5G0Dqv`|E zt&0gJ5r)(BGy2pQeXSg{LzCug1r@EVekw7>KHS23u1wZFA0~fEpAaykqzQ)2KCqhq z?$c2B_3-dO12j`0W@x(^8Lj>Wvx7W8M(;umvWC|pfmM^9LhZMI+?ThGF{RbwSd!DL zntd`mpgS<|UA6IHCpqEwy5)FWm$R`Te)Mu|GQ|DVi;2M`vy^p`20nJ^vQg zc<@WF6W+mjGKP{gFHX99U?Kt$VdHwb`fLBg9rrZx!o#4j9F1(}TGpOm)f~=(BNL6o zdt+)}rk057cbzmwP5-w?|t&P{{0^Q`BE}MPL@Y#>mvi*Z;O`ztuUQRv-xUwjJ1O`rwH-H${CkQl(Wan+->@fLVH=y?zNFek@E8k?A~F3HCOLw+a;me*M3I*+L%dhDo`9T4|rLrs;4S@ zc~eT>`wHgoBOp4Tl3w#p$UnZWTLgFr;I zk*&R=3Fq}lasFAa;dkalM+pp=2b?P+W986ScC8rF3!CRPRIqm!Jzj)@Pw5d&5oqCk z?dMT?L@LZriQb%3=q@QKwy{c0))f4<&tvE{mupRT-$7HGJkv&9tt+FTDR0yCuh^$)-n@p0lDh`53Hd$ds~p!$N>UfRY(TH#GsCwgrjaj( zE9%;h&Vo;98z=+5x5Y@_+f6z0ufuDDN0Qd00?7!Upl-^Tic-ueDM(^3_OVg!@A>Ru zl$Up1H-jwmAT&kC^ZJ)H(+$(wNu64$-l|6DXBwr)qEuq++5xTKchw0DqdmZT&wn_ z1+L#qmr3U?*F2=O6sv4j{Lo0P%yD`0myYtOli#9Tu?ZQ0`%)(Cv2`J@$lWTY*Xw$M z-aMqE!7PPgpVq7AlVr$T`t>5Bqf?{q-d_7|Xs^}hTAN>MJ4j}~rMD2+_3KT8HZDEd z*}DE+(c6})sV}>#lg(tRVPQ-GKG$f#Z7bv&&EuRShPVzn-LYbN6$$0KU03C&h^LC` z_mlH}raD|NaxhR?!F}B2Yc!!btbQF?B&xS1Uszib{?jrhhl_oN=lMg|q|_4QIvyj@ zDTb1cW~}ko40qc-Nk89hE}(=bjjkNb~RoG6aGBTfdY=r<+pP!WjI!Zw3f%iORh}H zm@cICl{9M9$_izYd1MH1^Sthc;=G8UkSgvBka{`i(T`n@nPh>SAm@A!I+ zcm67GhDv%x{uXrGm|iVI+z#%l%~^9qDph-rxVuYgxZ#2h>4X=z#36)410NOn8=U$i z_@bWEPbBHDsJ&HG=N<3YTE|aTx#V8LyFM7F*&ku+EA+~1bc%~hy(;@B?~ROLAzfdJ z7cQKKSS6C{Hab2zD60dvH??!Ce{2muEp6I`&7_v@4W8^Hqcz6Dt<3bwB+7HMSKtqy zTTV*%clhw3j8Oj9Slp}=J^=?-$oo&MGBPgb~C;)m&v3Om}==(bXKed)=PEy*XW| znmhcnrmK2iORv_AwK|^ZMuYQIg@IxY z$|{7`6-D;w9>qrI?C9rOn@$6kWA3OA;u~>kKTKnGZOEvp>5Ig&Pm7k+n;!5otLfvW z?(6XsgIJ-;CNGCCCHsqpbRsYCdaK!q@kNBeWgZ~MZ*{kFyEPi0gkLhOhSyuY>fyyL zM?Z>}s~yCygt58}MU@bj)XU^F-!3v+H)6|9a&FG?3ICRxwaxi1mwHRRZM37ETj74y zZ`Jh)HIst(Kfxw5uZOC~lN$Ypa{EdBQz4U#j z+dvG;KT@nC1rg~fqyD5l18#gHKa{^@274ZT9z%+Hc!{AnOg@E<&V6*N-}$uJAS-*| zq7hWvv7s4eMU}zui@`Mf){}T^x0h%=omWrb##<=!+O1YRo|CY&Nf~dUx6#jA3`-WS z>1$N+21_|M-M(JZ;L>NM>RKeZ$6bnR=^6S>@Q4&hsoN`2iqM}6%2V8FST)u3P#sZm z&lx$DP*XGImKy(be4F_KGe+{Ia2MI~9PwF% zGtW+T>cyZQPCC^|K;uabxAV8udXtxt2S4$F(EbRX$c{5aIpM-3PCQb!JtZ9rzs|}D zk*=(&>>pLnK7Cs|zO`DUOWMim#(Qb3SRCQcePnj3qouWyw->FaL4=cuxGRgRP#S4x zxT&D!B-A)))l$SO7onk5J5pAp^oduKx<5Buf|~=G+(cbQcGi$v$S9nw7q$80B-Yea zaBgpID&ibow-0%}&p`$7lPQHji)P6F_qNKyy9ak`GaFV_%gkb)u5w=KXd?DqDh~K|u;%+4GJKAbz5b5N>5iP*Ik6-w~=<_*Qx#Obgx>h_Dmqm+dPt?#|{Gr0ZC2Mk^;c3oNJ z_BVZT)Uf$$$G>nHZ-f)D3hPJd-bV)G$P=G^MUy*d1uo*Z3PCHF&OL z$wQYnxr6V=6B=Ghj-qJMacrxPkgRV}W(iG?+7%8~uk(^_+Wu^>s_x>7XT;a7w}d_& zxAbbZ>*bW>&!TGVq6jzfH$H9^_7_Us(A7xO$nc!lkrRrq_^MXW_c*|$LZ>|;gee_G z3~t17PV{}grOJne$!^_y7MMa5YHW@?hvJs|jl}%WnZEb$Gn1*T^7(R(t#Zj`iYGRW z>g?(qE{~o5nu+nJ3NnyE$7B&_y*Yfd8gpaA>Sg2lORw?aB#GWT=AXoM&GPjHpAyTE z>Cr3Qcr(5qg{t3=Kb13W?c7-l45t|&Wi*3b_SV!1P#GSIXgUB5?+~E#v?I>wRx0>bYGnYO8 z78d+Im+xne{I9jHPc+r-KS;Gif(#eU(Mj^ix28q*4C8ruNE&a9k|Hk$spnpIPv@Ww znouk{j5!d_^zY+N&xelh3A$=W&$*t%x9BHTT$>SD;j8ISSD(J``IhOaPEa^RzCStt zN=r%e5UUqKnoW9n7b_<*oE;%7qIDd<#q}hI2-Jjr|zg9FHx25?(+j9T)FhUzE#3Ux(}HZGBAMjZ8wuSLJUK8&WZoyIg^K>Wl~4z+KKs z<7ua2n?wMjU)uftNN+>?g=Bi6<{;aZx$(q_ionG_;2Re8KbND?%Pm?7W4&bZ`*Zqd zQ1UvNEPUw^3FFFLg$98m!cba0xsk{3EB$sCYt%andQNmI_51^-*naD%mm z+q}$}V~fSo=geBQu)cikXXoZDV%VcwZX8M%T1|GUl5XzuT$FuKXUTQqGwxQ;k>uFy zN8S&k`&OR{z3XrhH#Yh0af@HcTw**}s!`fY_RKGezn^(t@UaUg1;4)0JKi%9rt5b) zEM7JoPh|^PnbVjjQJl#XN538#cL`=Ky-A;~R7&0Aknv`JS?8Vlx64h9(uNUcD_;kGj$DeMcIQ&pV_IA;l{ z?k=i_(AAy)sVl*FQ>P5%Tv(8_GKB1^&HZwJduzIsQz2MD8!6ZnvWc1SuV~jTJxMn! zyf>k~4j2H$`u6S3R8mjfbO z07}(SMZA=Hvx|_1=9m~iduCFi>#*t}#FZGvn(f80)Icj!Ub|5to-*|b16Opo zbGiY7*v|;=L^r2&Yp_jf$@A++6!ope7nPCdzw^r9#5m8g3B2jjHgGD^S%{(=s^7fV zyY$FJE5(E#Yg7qu+MQ?PUdIcUknzC>cbROQ<(TltUp^{7i<$aS)W}I1jG%mAYBCsm zQ%t*jq7P((nULrGT-CXkA>PYjLrIdUn6%w6Khf+`f1JeMzH6zL`)aB9s+v`^kG5&L zm6wHl&aUHcTopx7T zoGIV9tt-7PbnhjtkF_Dvxa#6mpm8AwZX6*yEyZp&s(M}9(&sbWw*D(ql~41{igojf zmcm$(C!gmvYYr9XkDN$hGDaOBxoYa8&s0fUukbX@w+)HI;5SFm-L}kiY(0fq3PaPi z@v^f4in+_wLe;zXU3p&M&40XOP`J7LYri(UI$;Th)nX7f?!L<{ys8YD{qR>a0P`~Q z(@%c|I!^X{O(;So+1$Um@~bAe6X7)g$zDR2o=lbnZtkv0XzeJbqMvJa3Cg-cgZAuD4vwS^`%wAwgP-@BR-G!k8T|rjk-EpQ6mL-qpWjj;=_~{mH}2GfAKH zH1R2e{80U_lKqD!e1{@6CwEu)n>UvI3?JS*LK?fRUPyS4XuS0Gyz2LaBN_K>JXD82 zWoO<%aGp>eoL;7UecsK4C{swht$p2)d>s)^!Y znvE%Wc~$HxqRQ)#xul$xzNRUjI3n_<=G;^rHN@>}ZkY3>xyu%Lt_yeNX&OZIXBJ*o zUllI{B}kK!Uh#SGviKn8ETS{Tc?4s4)GdCN84oz}tK}?9G;b!66@Rew4WFoeqPeHN`?&m8|!P z#?!l+cGpX^w{R)*rY$CQb<_tQ3LvJq-O1^bUfH@xcSwPvn_p&$Fl&z5rg)kx_b zvWhgBugCG^wZG=K=JNV-Q~i~9ymRxLsr!unm`u{U&s=`i)rd$~&EX4Zr1+xcifJU~ z(~kUwH{O4)41JYjmENkdROy+()oNPb)pVGU zFx^zByr)?y7Qy$eYJx{so`3d}eDN>jRmW3ZWY`|x3wMc=hrr0FYsux?qQvYPUK1WW zxBv3~FiFTrDw6Ki{#97jq_KX0v`xmRx>#k}vGHZ~fl)@H5J@(8U9DiC2P3 z4-KhYf+B;tVdwKl&`3k1-I31Pd^^ojCU;_N(_P0_%Vh(#n&*_9HxK82HDX^Zy~A(g zl_%QXvyCJ-FrBJyeLobd11(3K(WDiTWeC6o2jvFpS!0)Dj*j$U<4(e}J3eUz_H*?O zS*Av<_pW@e=ev@}y6#8g_f%qGnI%h4vLJipchRhpc>M_ygFz2gYY?2~+-Zj%UZ2ryY@0JX&L56XTXXw@$&3x$i&}Z}GG4nY`4XtNc#_L1H z3?*m$2_S9&=bj13yhOo>$?(>Pp)%B7EgY4ta4dCGl-<2Lx$!yL)HusX24zd(P=I&@LhGwKZidUK-m7VDww~z{9-ttStS_{f~-L(hP;O zNrhJ-VIIFGm~td9VdjDqE_Ja*E;=}$L~gv#ZAr$eewOpZZ@b);9>PB+Kob@g9X(0~ zYjRxfm5<3zpR%l+N){vxaIX7ZF%rF@Qe}@0jnY@gkr3PCbQG0Sq2?GoW;pN|OHbsd z(SRd)(GocyB{I|B{QZ4PZTBEO`<`FMG$U^fNBwm( zi`t+DWlUgn6b#C(IK`aab2C`f~>-wHi3#Gk9RNnX!jjCGaN}b#=)U zj9T;3Y}(jpl@vDt3c|e4qs=R#MTnoI4@ApH#K(u6TGjcs;ZpROtXAq>lH)*cx(SAR&@VO9D09e5YIIrJv;5LnuiVJIPEhY-X^_!Zgi1YGv(%Ln8E8NmN9i5{T+YDTxf2?gWbGPZ-?syk6;6)FRz7B`INVzgu=>JY3sxE0>$lQ zwO)R9dt#x+PPE#Tow_*^%Tc{=h;tWNe|e(294Yne=KU;dNz}hE_J`)TmcE;u(Wriu zk2QmbS#wsU>?U&ZCQzAX{3nVJ?%GvN$IiW5>Ro07ECwZ~J+7+8=W4w*svgBp_#UUP zRH$A_QEMF9fpqKuZdZx#14Kn9p?Fqg!zPCev`SLi`{6ai0n_ZIg^!9JZr{+L9x6&) zu~_w+h@!OT=5sjHdHjmYFVi8~eYyKruo!JIx?IW6{>o;ku{JU$JTxodhL2vOmPY*U z;~Z4i%4h8N^BAi`3;QI*Fm0jJFx=$5e(+CO>OI|-LG@Cv{VW2TO(KK_0daq z)^WZ&9AVNUb@Z5EkFLHbG2BRlgRr1<=T4|^w6fE%PeJF$FMV3JRV$_FcMX5+3N~qo zHt;J}5!WNFj-u;7%PP>_+i5}2SAopcS0r0b4}a#4t%Y4__W4`t81Vif`n*0_OO~Vp z_OM@*v3i{$7P-j?xdO0VsCNc+2}4UjF8DHm-$8%ulHfl2wnXXhP19^n_HpIhU@Edf zu%)HWC-9{FL{WXvpn}iFKB&>)EJ^UH>QgzDuy9oUS-PtQc6*0@noH5fnBBe@NQ^v~J6l%jOoxdDGN-0jtG_-ny3`|er3sO|I$_i(_>X`A%F%u){O=WVX zH7%I!H1Kb{?vI#!0(%b3*H(=pI9@L3w7ojY4mfL%vXO>chzeZ^bvp2}!+bTpKz z=RXoiUCwV2qQXJ$8eQc>Hiamy1O1sf6mv^W_*=WS=NyEio;E+i)3QC^Ab^=JP0iTf z4JNkEs?uU~OHq9^f>ryGSX0y}fSB)rS-2I>9V@aNqP2zoLhx?25Mlsm)>1uJp zbPRN^x&&YsL$S>dAt7q;Fvp{_4ajbKg0-b6#rJ#<6EBza+BXj}H)O_-Iu`Fx*ek;?dbmhvZn2gO4w7O<*}yWepA~E9$tV{|@A&or zUxYd1*`dmD@GX@l^PgAxuR z9VBhPNX4BHh4|J*gcYJ+|7amvB|!Y`nkOz|leFE_KqEHK(C;n06Zoxh3c#AP-wozI@7(m!^M3 zVa~H?()RWM1#7sw=!WD7?s7djA^~(l_Fe%EeQk%Qnp1~&SZ{7}%y|_{{Uxq&Vl#_M zlem{JEvn93OS6rib;YlK^{`BnIkdcZT;+50rT^_kFW7vRb`PIwfJV|At45zB)#@$GS6o4(6qZWSf)(@Q>f(ca-xs}-G*PF@{t@U&_GKYra;AI(0C@3 zz<_MeoknHGaG2bv3!m4gj)_HCTseF*%R7xfM_pDXPV6`gC*}zazQ`*YdsM=~I(yG0 z|LH;6*If6ZaGlUi<={0cjXXiRshvlP?%XQh#{xx3l0XfDi$atOcB5GYx zs;B|hF}nrc8PH`+I3pF#c|rVifslzGo_xMjW$)^>_sr$T_f6f;YUGB9P8DnOB{TmX^UXGXD?W}8VVT~<9i~0QK@M)+LVN7C(}RQ~bIPFBlq14kk%#eS zau!+QClb=gQ=J&k-RG(2-bR5x&7=hkj6L+9TsIrSdwj#k8*|i*am95xM|W3793pv5 z)YfW0x;l9p?81uXbuBrGQ7Eb>lvmA*0&c-H>cT`y=(W)&I!=F+Pa0!+>;rnTAIR>~ z<|ZI&2KV1FC9>17ZhPqO``-8`|3*1EQDh!hEOwzwf_H9efW7q`P7P99BiC}wD&chY zkAMDYjCuUeA^Y^Ua+Jjf+}U&Tb41;EKu^7&+@;^dBZr)TYV|-rT7322ut}RI8j1qs zC`6h#NWQA)Ye^9xhgu`c>OyH{U+mA>ZjebFgf785qmNQ+vZCZCuX zGzbuJlHDbOV`K8c;Mhz@jV@14@9z2}Fj&Q8>G1om9UU{UN~uTH8jArZW7IpwT&x16 z7o#Iq-lkV`f5b5=qaT@us@~z(kQH3p6T9l(7B4okT3aweX8E4t%8Hw%nQurVgX34q z5RFx8c zC{Y;#asGKBo)<4fWvY{qjXtq-7<9IIV&$Q5xr$O8Vm}n?^Nxu>6{}+J_v%ZKb4aQt z8rz@ELK4NkAX;%c)jZ5dym~cUMc_9qbJQh!Prc-zhe}4-HEIfJHRo~C4u48E`$gmY z(FwY(MQ)7+QQSy9kVLGMURsp>VeQQ-jJEOK^If=G_P6yORQt;3#gu=W4-n2Bt`pC+ znzFAMA0IzN1;i7Dxz#?dTP)9vU@S7!6d|_GX%f&B=__a71zw;v5 z!tNch;I)GAiwhku*f0gXa3trO`!sASe;NWMGbA4m8F5R){_wXKy}rE;F>4jX#&}G~bgyI5^j=EW9MYUQ#aFHc$RS4!+F4g4{1YC%F-40T$24 zz-=AyIoIE=6afj&gO;|14z|1r$bE7zx08`ZWs&~I#He7w)gfNF;mX9Ey}VN^fX>UQ zzWVOjeQW3CTr4$ut0t}6pF9fxCfUo-sWk0%23)<&Qb#VrM%oS9>0vU_R3pnbusPJ) zI)2m#q;Vi=zAwFx;CUbPq@6<4&>WRlxhfB~sqm&V{E(l)B3k28= z&)`8<(SD69$hdMSWXE+X%zUY>Aa-m#@ql^xg$K8>tIKH0cfdFH7HVFk}*QY)h-IkjloB0-ksP`}Di^iO)D zluw+6s|rWDy&JzyM|iH{gG@j4%1*p>C6|WRrW|Jg(b zU+dQXN?AaJMVy3Bn|IYf_$abkFPWgE32j}a-leQCp<`_*5I#a#mWkvK#?D2^<_trl z82ZW^k%d29racI9i$&AS7p3(}$kfptc_J%Q^~u@|?evu*LJi^pChv}>yImL_eUI>4 zZH3)WC^N9l=WTrO=rfX88;3twotU_)VnKXV;?=}r?RLa*|9s+lcPe~U=l7G>RU)}2 z(>mmyvIUS8w8b`!u_F|WWH^-1w{9-;(S#k+$FoAj>5!H4IJY)oEp8$&PYx1@t}Mp}BD>gRj+EJ~6>ASA1>>Oxx~kAxn*adoFo5Woj<(;oReat*z?HJ}0#K zvgqFsS6Is9--^F^*)RG%D8C3_w+-=7Qbl}X&$+XsFZSXMP4PJ7#%IuGfCWUac)sJ z0|U=$Ypd^;#DB6wq$E)*_HJHMFfHM9F(sP((yP8c=6^>gU6C%x`L?5h84)FMIb|Hi z^NvWES1x0E(Vf0v#KJDLM4rS|0o676MpVN1w)5-tY#smJr-;)&TCfcT`{SLCKg`1Y z<(2XKI!!w`k%(u0y&k(?0SUBU8$ezDC6szGKa;QhqIyy!KM8~Zw11) zY4sAa&J#~5FHUHmtn+VXOPC{JeIQf)O9kEUcOFvvz9BxqXfqkT21&EVY=kFK^p4B& zLWJ$Ad9J1&pdy#6#oYGJw~3SM#|l%RT+s@ffrip><*2p$qWjP~gHvR7GP?ppU&WMx zw6S^9*EDKZC%VF)xAVWhN#PCFiz5nrP&j_35hUXpIzeB3`3d-<)4sMfY79`e>(^Zb{l_wcJduW@%Kjyv;OsdgL#9==qsrTXKsm_WK@6 ziiQ~#BN4p``~aVw8x%Okq8jh3578pDWHo~0cNwFvy7~PEHST{dQ=^UlZ1L*+)unIj$cjlxHF-^dMv z5V!l(HiJDxE|MvkSE-y~b4;AV8*k3#7CxVfLD--crDoj9e9@z>hufW|xCT7bF0)=$ z=pX}JT9#!R!U@jafc{ZUeyC8lWsA4^tlh)o)Ygzjf-2uG5YmJj4Tr`${9Owzm_{!z+nLyJAyC;hy7Rg=FR2nxa zh7GBhpIgiXY;PcV4K`z!eKEPDA zZf7Mn@K0Jc8uRLxipd9c_GHgdHT;c$l&tA4EQgw5}&bg0jmN_X2`WW7T!DN`;o%L#ae0=xaEC`6O^U?_zv)r{eVPinnnJ8{?V_0lMn zZe#LhE!b=A*7(xLeUbz{_>(W3($}7oU-a}ch4C~6Y?r2gO+oE3q3ffBJx$u5sqKmk z)mbifRI|TgMO{pXHz{M(w?iLmo?aSPG=esrKGpQAnT zX>Grv!B^L@DcV@xJGTpPDW2QZBsk?zz^CMfK1n!jKj;E7!j(S%wq5M4(a;~lE5Mc@Hv<)RitMl&>KS5C7)F7XZqNHriE?7dwji{M=bW@J6=x$uC>P4szV<(8Z5n5&AU&R0|*jme3Zo z8HEQ@P3)e-n|q@*T>KUk7aWG2UD`N-WfY<;PtJwCbJ^6DZ3v^eDK^_tUAc(C2&$z* z;$ailhJp&u_7Cwr!D%^fAM+Iiuc8eb3 zwy|$+OcK+(+?kb?=pY6JllaKKv{-&Ds}s7hoQpT401!2 zQQ2k6!>H9sggslXg#L68HfbP--==bJ#ov}%v@)x^$Futa(dn07+f|huGD6jHm7)(C z(fKH=-0HmXJjx4xk+3cl*LEA|@*d3Vz@glQ#jFn}%tuaDJ4fPVd~`J4DoOOS zc1|M2$HObaxe`>6h=m1;^BNVdS<4@5m12FM!E{KWTk8~6r}SH1y_%({yG>OCCAD!i zs@|T${XPz+?^!4)Q0=#%8(YPSZQQJ}y`c&U5PM;FQ;)Q-hoi_k$0Tzrud78ZshebM zOl-o*Cl?piIm2I;1Vcou9!`CUV(&jN3OtQDmng~E`EAE$`SEYDU$dzC3mtB}|7}->?Uvepedtc(v-}d)!d*Ucx4^Ih zDb@{yiLU2&#fx~m{(hXz@aEEmj?OByyM!)bI+^+GDC%W)+_^~!^#|l2PHzM8S>sw2U+BCK=7?YhKsm7m~JJlRDPUs;d@8(s{Nf zp$efkHa74^keD~swcN)hL2V14hCS6OV<{`*>{~n@|C?LyDu0-e>wT{9xK^-BRvP2Y^P!50&!0mQ1agnl^A`^&WwX;~C#R8ge`e@a zHv65rb3UzJrx1@&(+v*7?^pUgkGtzXi(6t;@N}?%fvuZjyPiyV#;=fWiwu(~nAh6+8^#@8{+q&4>1&9o#a+7p91I)M}KfhYH zziKpEHE&DZMS4MhK1t1OLtR5!F z^cmXeiVteCS;vYNOFeuA?}r;hDXi;B{jyISv_Q-J(PrUZq6za)sAP}NP})Fv2DrE09d*&v)dbC0fm&SUSBbh64H zEm|Rln>RpXk0V6J$mNGh5(V3RN~*(ksA3Hl2!?}7Y9?GWc$BHnyrxZ(M$T2gQOc+k8HS7O1G>}I zq!y^NHXlznuOjPSs26!HXvJBP6d^AzN3--;6(Bw_M{a?^wzjr7#+gu!WXm08OT(L4 z&_k;sQo-XVtmtF#)N-YNnN(8o(<@R|^P99LU!mhKrs-c5OT*yr=#FyA7O=nBZ%)!Z zxW#52-S#moJ$G;RP3@@pP%VRNfiQ3pM=+8^qaM+A6S?hGK< z>j)9dqhCaF0ZSMfD%N)pq_A7JOLx5tZ(8e58m7CXm}X+D4*@5oHh$_lFGjhPdt*^L zG4_Qe!s7+L&QoM$!oTjD$LAu1)VLNq8uK@EzPqfy4KRbklw`#p&rXk@TI(iB4h|~? z?73d|ugxRR?w6)Zt7@M%Q}gk411 z4y%_%MRsry*Z_+(gVze=+1ViXU8;snm2;hbvHAg23e=CfjBP$3+I4wuOdLsJjt%o2mbK9mWdUICUlQEG$K0+QV9+3b7)$ij;D7s{-!EVr%>Pn)tv2n(}YP#q|=?Cb{ zIQ#B+RKY_hV!PAcbceaH@gQ)hO=)>5!sOn!e0SkweXZITni1E2FkMULC!~V=i~L9mR92;sJk61l{Mao5*29>WoME4-Hd@FHhUdAx1%z$@K;CFmM3znVwHV8G;qJ@ST6#4+oC@lKs%v$|G;q z-hr34ME~7v;6ToLcG2I>6E@&MGKL8>{bkG9@f8ftU#ZwhhiPa`7gcW{zMN4+UdyoK zC(EuS<=q;*;RWOw8q)oUmt=$C^`1zT4#(;3^85YzWgRIp~#zOm!Q3?MCew@ zuEG&<9EG|0^gIE3xGF=uxm-o#U)lu2Z7Q*_eP(`+6<6!=ceP|kuGsJ6DJTdbfS%kH z+=NjKhT6KIze=|OyvZYDu-C?PZ$oaKvZnou{Xx_$JHeO%W zO2>_oEiZ9n%aE*B(JHguHHi4*RW(b2B+d)TGROI(^;jw6SzqPsUnau+`QjuecsMlL^ZRDvhf*(Y8X}vL{>fLZZ~$R7hzU)V4oC?|6+* zOEa9Z{Xw5oNN5}ST@klAUv+cLrDP?i!xs7-YzwX6h3<3tj;yntwK@Q<(ShwUkoOKu zpZ#$0KpS2xSjnBv8A~uL=YiB3OLpSUcKFbB)_H{FT3-o*E) z!uSvYGBO8q^E{!{>hvnu_z}V?^X&_SKb}d(`ZtyX<`s*+#1R}URC4Ywr_>X65hsXI z6Gi7s?zo=)MEm?vz5IjT8qS&1;+pG|<1>o#e*az+Z!kdZz(#Nta(4E0USzU0!t@OI zDh#ipT=JhR_Ip+Qif!xekR)IrdA^Fhi`*RO)1=5r;98lLh3D9NW@v@+GJlGUD4O)` zN>81yHk8v4hZ|qjGe<4lBS-q}2RveX5`XL(SfiNqQ+RM{m9Nf(W_crH0%ENOpJu_h zM_&`-vZHXY#yMDJQjY0s$+&lQOGfAB>bHsm-DstW;31=F&~l$m$shI#(`yTBZqdi+j>NcKri$ly9W@!F zut&@Y3FUnB?!{P$-TICDdgvFPWnbNoq37B-6C(cNmpQO^MT2|DDYKoDJlOq}>j&qZ zd4UGZ2#zM1PA|4-5Ev9nm4*MA7}}V+=!V|!ZBWms%mv(2 z*_m|)xy7VgoNtJ1jcCc`Kd67gkF8weIWk|eGfcAa3+jxkDv|#w_)Eh08`%6#QJ;RN z{mJRe=CE?;2}xD1tr$r&^nrnHG!l1%K z;6J|&pP|6H@#q$o04pmexfHI^ZrdNLH`D8ijm5>x(ejKM9I+(4(hJd69ZR8B zDD>{AEi;Kh3b&<6h~mA-jl)bvcfDuB!xYQT)VI(U(eh@-X*BT2=x9C_Cuj}SYTJH& z*}t^PJ*~niuilj@Sbn8qQNNKFAP}Oi-FBaERw=VK(sBsi%AeXD{g#tkby;OFEx|7E z0^ctyHoUZ-=Ix&Hc2AH*aT@|xtx4eVv{a*2F{t>5P83jRJlxl<+E3&}>b@aw|0JDr zpu#&Hp3345Dg7!l$Cr+6q%7T808)Ot-^=G1Y#`+89(**8Id`sWtW@WUO&{lKTIgRr zTzCJ)JU`_dNYIv9HDu_AtoF7c(O~bpSP96_wxWRxo13Fi7GlpG;^R&MCi48Z$hPt? zemYi_a{so{#Ww3VKY6v?f5R3DXxiagPmyu%S}$C$NA=Llaz(NI)#!;WROsnqr*Ixq ze;+~7HR;sF*Yt_j1(g^059><>Ab2V_K*`TPch|OX4kOxQW+4wWMu*r7MmNtNdF*Or zs^)p0hc`1l*`y7#f1S0D-Q`Yg%2z>O*bydRc)h`u+IX0z>Nc4}w+XjZ4O<$B23-mC ziUPB;bUC2Ix(N)+3_ZQkg-KF6jELSQ^7ueb60&}XMFFDkEmeMYfbScm5~}e}F6^Va z<@HFSz5vN%^K_G@0yf@~_cVk(J4*F^puX!$XNLuvKf!9u`zrG?qq_guw{a08&IN>y%^QtfoSYi?@KDXVYub?hToC;g5�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 dS!$zG)380{QN`7rRj}hX=3wi%Dc{>)4ew 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 c&K{Fccb(oD?7tNt@Bb_zLE= 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�%!Cc=%rXIC}y* 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⁡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⋙zhtDxP?d9L9vdf{@ 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

IJ3;?aTdheT%|V1 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

t8!lFI^L+$P7MfyMcr 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 `ZaF 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 z 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 z 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

  • 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 z%0D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

    CrZG_I)2vk zzb3x@%Hylmoyr8{7)b2XmLE3VPWicAsgR@dbHmn;x^j6lOjt|{<|k$=SKJk&p=yYI zODbXY{l2Wtl_+O_3znw+DSm)8??-0Cu5`bAE$`#<5SL5L%e-9XamnK$9*!9^X5^eR zW*kSvk@J{wU>=z>XYs8j4N8jbJ|j7&DZ$hpEG6o*NUdCnK4`lTHNg8T^`@%6;7&6! zlZ|r<#h2_qhVU=Us`Q}S1QqH-XCHdMvoZ{>KZuOC= z|HD>U?|&Y`Yc{fQ|EVNWPZV;o}$=y4e7U$#;tLtbAQ%gu(Vw3-Lp_fPJw?b0X-6WNXT8k+pDIL3Edj4z=sQqLBS#M>4 zQpH7@d2j@)N+t_pIA&bxKN^nAI~))6Z?rdZJb__ezA3-w{IC3(FaP=9kGpq1&+{jw z@m5Fb^c*$$nDOwj0XFVp2|a73{o-%_fc?VNwJup?_#@lY%8lu0nmJDIZeFOD(is(8FQ{ml>Z+aKY4V;LCm%!p&e%p(yoZ*Na6!<`cBnzOVu za}3s0r*ZzC@Bgu{{o+rbPX1fs+y4B7lI9Ae+c@3Ea2tu6%*S___s0i6<)^zZ<}-U# z+1=^?I^7a>dwWJ+GSB(s=Kd1jIKKQhemcMF<}*L^rFqP6efhn}()P-jfegPn$?hik z4I+_#I$_S)y7fQyBR~GrKl4*PQY;Ppso}$m4?n7Pxq1A!+A7Dq0BJdA-9bKzM`%8% zlsv1#e#p}~BlG@Y8W0m{r^k1*rkb&6^33LiOBL~ex*qk3&c>M*FW+E@i~rD>j!`Uu z%55DWW>fl&%H!+`z8(t|%kv+7{!^KW?A>c80`Ue+pwQYqwkE_h?PzN|j+{IHkph$` zipp_TezgRJmsjMP*jIkV^Pl;Z9F{8kF`dteR{TQqUaD>-xnWU8HZ<2OoM{? zTYVw;oUJ7hrJ&FLHsWg6(jJUtD=vsU(iU-Ub~=qXF2@MnL2&xJ=ai8W)*?|krq=X= z$qI^IcT$isX#mp+3+?1(mSc=|7cQJ{|8A#YGmp+GW@*cim=RQH(P7(65qHcI8KE?F z&y=xz3RDQkvU-spU%lTe?KNEGpUzuV@$)*qZSjfsq;YtA?Br5w`Q_rp@pK~-~N7WyO!2E1r2Y&9;mJNx)&Ftzf7beJnrY2{T=Daj_*8;Fp?(tm7|1_N zLbuUCu+%B;@R?08wTL24rl3UR7_VwHhq|@&YEvCca)G3zsdLEw*Xof;VH4I0`EHh& zMjiQKx_2y>)}$O5S|l^;y0!4Iu+;?WDnf);-d)p6J17QeAx?!6rBmKkzzHmUtcu?$ zd6CBC9PLI-Y+5jWj#PXve-8?uxd|*uM-p~l-s0FkIjd*iQYf#rZtPcWsW-ENttof3`M7W zU9&CTvl{0XF0F(Vl&9aXdc&|Z?VoVn_N^cTHSKfhgjX8jW_uk^D+mauL!S$m^G`YV znf&EE8&;lfS`XsHb6C0cc`R)-7dtFV6rsM}QTw0PZi6_fir-F8?FS)O3#qS(%U$M? zLYnaRHELI(_77bdJ%k8*WCv0cJ zk<)CDQ;Eqlw9e0U{xDIk^9@UKQ0GtfI`Y#Mxx|sPzH(>o zP}S7m@4{Ly27B-I!5q?CZ7;X|FV6xEcDO#$zv}aiY!HrWq*#J-MU2Xty_*U;AIz2_ zv2&W~>Ky~XktQ89^NesWu4pT;YNxH7enGrKZj!CSZi(~|O*-MRT%h<%iy|2)j#>{o zH)j=bfvGCO{m*7G9kq+g-#Tb?Xk8%KR(mCCHO$N(XFNk3lgoPc^W%B>kch&}JiTGZ z(;z|`Pa{1$jmMc zv%M3D?_RSablX{^teP;LOl`S{fAgr0Pg9yv4-yP z0&_cAUM{d4W@h=lKk(}e7mshazeBddsg9N5<>2`Y{>(CN&uc>V_N3A_CJxcBe> zwKpRpXN29HF89wa^WyMgv%met&tB&7@bL?bF)wksk8&A)_N}LQC&w5^94N&Y^Sj&A z^B1!fP=D;le*FjE`sO#j{?(7Z`gODM=68LL?Dfpsx8E}0^!d+w`h4*ed4D+`A~L@6 z)8onGJPz{c&jU3bLE!!&j^n-eKlsv@9%XpUgWhis^Wpx*c>LJ>hKIWWiOc7|f0bS= zA|=tc*zV4`$*DSLnA0*ZmbdL%O`3R{CQWAVo;C?Y5d^Cym*m9p{HLo4>)ukN%%zOF z%5^tv=bQ2cZ@)DO2Qdvy;;&QvFsDVPtD(I4p9^c1w(=80JFaCTW~1a{GzU>fVC$rX z6F8&zjpd^C$m`-`S)Iuks@QZrJn2}KKl5Y}@enWNSDn9^^7EePNTeZ~r zDYG~kR{2|<2{Y4O{-F`(PWPnpYpROU+VN9BfI3M6**X$J*Yp08!UnAcH596%u2p#SH5;(=jS1vBR#*AWAk$Ab5aJ~K&f72+udcTZsFULo#2wumxc0|?s5G?q~>tD!vQHZza zufh))x>s5m{H6Dc)t>?C`(3);j?rD3()@_O9IV!CX(MCP!FerJXdYUK#cS!`x%|SrI=Pa)hMtY6>P9MlyUb)aFhQrVBIybnHlSaq zxHyo$HVnK7%zkvKMgqbQ5v-uvs`uT1Cq4*m9Sid88%UMXpV40r%{ECk) zzA72{=%(1|E@QMF#2M8Pmw}{}hhSQR!BjG`8N&FvrZmzK;Qr|zA5SPJn|+~zOYf?` z=bksb{C;Ws((hNEBg^*FXU1?%Zy+B)G2?VbM{h=G?6LU9M5IiIi%T4;Lp?LxTZm6u zvuPb)|C~dts)WVsPWReBBXidKE%h`y_0;#}n_?7o)~x;I)CC56dRiZ3?F~@ANK)bK zXh?smoK4>D|= zGAB!dk`2)?k*d{sBs_Ccz)^0J`S3K$8`Lz zc8+Rs)LgfRHr2rs+AvyCBs1C$d;7km>qqiokQ8e>dRFHT1rh?@ZQxL!1WV@&o%n%V zcPn=)x14Ss)v9gf3$r8g>~_!)5)0{K~KMOT|Y z5t-#QY(baUh55?tkp(38qVr+B@k&%;xJQ&rL$!#&ti>N?eB{W8@NqY1+Uf0h_!gNd zxXJjo)h7)H;`jW|e({h0&SyXV>wi#!H>J}bLxQe|i7}9wmxnpuv)i}fCoJ3AUa(Eq zY~3%p%t+BA*81JO&riBU5S`>}n!FAq$gGLp0$cuISP16l8svtX;W+YmF~)auH>SQU zb<1VPj0o6h8ytc@>oY#-zRzj}Gf@mlYodD(*Zu^HXY`N%U0*%fzlTk(`Qi8hKYj0d z{zcWtpZhD1O1q|-3q#2HoRLSu!t6w3q0xxP&GK{xrbTJA$Jv3HvZ64Wvtbg_Y?WUD zG3_*X%0(`e?tGYE`|!o{%aQYP;Kf_xyTAE`{QA@1`Xir-%%A@o=g9o|_rE-6K79NF zdE<}H^AeG#W6qq1!5<#(Z*FeN%5)5T_^odR@aXO_5bwP6?l->vOXD;QzQ*J6-e3QtJMkr8z8=`|7<^y#)uF18W1gsq;cdWTmzn;vNIGTDu~n1C6Jvy-k6+V z&ovoEiaZRDJXy1(#Kuy1P?F$eu>kP+h~uP4qFOIdZAWge7KAQZ|lBjCtc zf;-VH(VgQnGe)^g*wVQ&YT0XkNkRqMX=>AwtMAJt0V|!XmA_T|B|j`6=DL3s-tA{NwLZDd)sFA&_$qY{_6N57 zWS5)T@>>qV7{3j|t<5P{d~EUUI(~jaeCo>e`nQE`$2Z2;`MaiWtG?cwVwwtU`G42f zua6(w`sY5?b3T82fY%O_4BIEhZ~ZKayehtZLVRrTP5l9-n6~wQ^nSJfJHBUjle*=# zSNxOlo*1Pwno&yc%mT66%JiquhV@J%l+CLbW1f-jh%kD@xb#5kf?^tijmHjOK7E{V zX_B^t_1$uY6|d3VA{-u-0nm>ms?V2Ns+83PyP12z*a}A`CqwRgpcFk?HX4*h4^5MZIa$twZrhF6B{9V_eP6EmhQTXA z?yXQGKYB<3!#O>Y*9n9lEU_}`wXd9A%<4$90bJZ&L`I4;#{IKIntMD1yyz1^XAH1b zGgWzBlYvt6wQiWvZ%bWGziqbkf};Czje*j|s~dj&fy^Xsqum^?eOZ8q5}qR|g2k8Q$3;(Q+EW zF8qzThHZ*32(z+7QPSI7s8z13Vd(`bqo`gz|Kx`vkzBJ&)Yn}t)TpYS1Z?=u_o*y@ zidzk_vl~GyF<6sAXTqFvJB_Dvy^*$eqH;g)L}4B$cbzmJ1Pw{|G65-P2%iyNK8EW3 zEkEz^X2NoQ6eSyzVdF{6r**$zyqU*GRCh6(ng7Fo^_%vuy!RLW@Q;u4psP9YP zuXb!T7w5&yW6t_0hk00Jo{~lba^qbr);yVXOfqq@%0NoHWUsf4FoPW9oLYKt1U=O9 zYBe`DvCfZv+E0(RK9!C9dBZPcD=(_nLgs(tw_tsD*5+c||Co1Ja?g&4%!AqGc!CUE zp2BYs7klT6<~IbN4B$BEUP39&k(o9sH#rEgcMwp4JYSCUb`@NT$ zKlfKGA|fx5@!j$Hgyn4}ZLtIHKl2~_e?!`T)Wgn&XKXUACH^13>37rt^mqPe-_Fbz zg;#|C)L;79&)uAU&8^uZ`;GsJyg7a2_}Z7!(thrzJTJ(wo3mw(G3N6Zwf_I<_x=Na z@k{=}FV;sm?jB3cBLxemtZhon$uiuA=3ULgDnUxeOfx@no^MW-oX9`1_h;336Bw0S zRu2p1w9ILc)Af>rR5gm~9tZP>0_3>2u%p0=e zHIXMH)vvu8d1UJ>s{F^r6j9f{ZGabyNO>1~LOI6Co%Y3iccN7`14kH=<=e&lTnYtB z3#{BcTa5+HiV_R74=2UXRzI=LZ@PIBnO6!OatYK>x1FOG2Da^LEf9KsA*Y$2zZ74c zU$h6`TIY>Vu)H|4y{E;wQC};FR}**)5iw=fo(b5c*Z@ab;;3(#!@=pxMMv#DnZUIf zL1}9MSGGw_egW(u!_R%M&a{D;aAZs~nv-VBBxfu6&bn63r2a1V8!xw+Q|YmmPgI*@ zVndgT}ra))_)bX9la7{E^9*d#) zRQX$-xYxvQxAagN`%--F|7za98b4njU-=1y;gYz0pP#RbzdwJquv4N-cUdy&6;hM3k>^Lcj5Z9L!dlf%Mr5Q?~nb~KeafGk?uC-6)M~WQ`-JK zX$v8DRKaBHREoV5iCxV{m46^MJgnjqUn=>zT<)m0Slg0&c_n^NjXFNvj#u?)B2tPz zC{>!p87I33eNTw{-(GdprSg6zy3QABptP~L4A@l#mRsaHO_slW|1~NBTP$mxzqweP z*YV|J@^XA!{je3^+DUh5o-9{HZ!_rVTk?w3pKB7+a8hgUxlj=||6PiX#8_&#C z|HxX`jPEyXd#vpjX#0oWKkZ)sN&Cm7Eg()-@Mc&5gZ1QCCP+;-t1cC0d(gr*Wrd%A- z{s(@;wzuJKGjp77bKn1xaI@b3nK>gdZNZCIXV zs_YkN(9B`s%?!=vU(?;$HgS`N%oH)Mac*kl(j!Laf9WDMW3oE$p~{4wgIH#$>-3}M z+Dqq;Fc*cE-awDh`M1!Z)xzbxWXl!Ocxm^f+i-l8eE=OFW*OL4TkQClN-S|tiE7|q z{)69t^Y#~LYv!uB+Um-UBFhFJx6H&P&u`;Y2Du?*6L!A9IA60xWO-fv94v(lvrTnn z8Eq-=U-Gh8c~sAt$jLh7r!wMl`O9DZ@!9G`xm+aVM};kW3EMJ~_5H1JkTp!XfpePz zjdc3Z=Q9%c&wuy7Z|6tNaQ|hWUtKG@vo?L5Ytc`#+}WBcbU1zPFGZ}qK^>i9D*n~}$94MqtBb!33T^$2S1(BP{j%=I zc-{TDq;V}X4&>$U`;GpO`~y!cE4S(IH+L&C2q2fLFZuzR)Ue{C-@jTB?PsU1c3+lb z_!u?NEuwk+?DyY)csIE$YpuFH(z05uTGKspdBj}3;9Px;?uc#oNT7oTdKEV12RVaK%SC*(i9|Me){|5yYT!+w6Eh5Qyk@00VZRlmOy|Ek^aw;KOy{dZMg z>*6_G`aJvkWLp$#eS$3Kt&)0a{lz(sUpD>;J#Ya)+UW>F4eAF+H}z69vyvHC637du znL(=TTX02kwQK^83{qBs8Wv+&r!>&HzYdw=B*Tlp-Mx1R~e-78`|_b zguWz#j`-x<$Wcjm5-FY4^e?3jmVIxjT`LQneQIIrlnlKYUmGkn8bqM_$Kupj$d!X| z8R60kF3^`?q3ZcmE~bV)kd9B4P?c;(`2ZG@+4K(8V4YPY8n!9HHf$Tv7LYd0#!K;; zTnTRSlFv@x!(`7Je4Uo*mF_@>`zYnMS|!ssatyP~;x?KS7Sg99{lEMB z627`-3l0zc23Oyg0lIfNEzG{3V(BX3`;nh($I_K|zf)6&OTd)45fHj>~J!dpqv1FbP~`RKjiT~y5K)%|q$8OaU? z=pYqQDXY34mJKILZ@Rd0WvQA=_BR6FcdLZT95YK5AeUyEQ3xlLH>pqY2>FZ< zcz=R?y2?_&&3T`I*Wg?|D!?|h+3aA9)20UNEhHjf8)%&q?#^8(~t z(}SKmm?2|!uM}cob#C5ws0MZP<;=wSs>+058aO0P zd84JaJj|Acbz~c+xM)3{l4eGsNu!T#PS_1V4wx66$(wnhx}?vX5J0pYS0N4wr-V8m zl&KdYOcY1o=wc#VN)?H9^RjcZcCt?EAry1Kt}P4{(ukbqgD_}+;s_k`JVw2?h&+eQ z%rOu%5;yHtGJvR^S)D9|VJLgjG$px2z5Zh}L6;&^p;oC#Qc2n4-~1LXPf}j3n+h)f zSA6qh2?mC_M;y2QxPTGg{;Ti#Y0L;8fMGNpx&$_Si8g_&V5d4y#jcCv`N^Cbt}O>p zc2dS!U8+DgAv8C&8YW3}*s#6sZkt}|#MJ$p0)tADN%^7nsX(XQ+0Z0fEF-kHpehif zPn=cD*hOoWga{TKrwXFg-s$*;ttV&e+v)RJXsm8|g%V~C&e_rT^%@xe8l@w<(@%-d z{MOpkmGQB~ZyludjdD>}mydU&#rf`Z zxVmS1{7u=j6%2I$k{QtPm(f5-t#?!P-s7io^==X7@|0I&N_WBDpIWQETu6j_Yt)J2 zuajej|gOY;we8fcf# zrP6@y?@2g0n>9txMU4rd>{@MWVC$=!^g$!qC-pr9xohFR>T6?@(AMl{eOQqTj2f!b zRsYICDhX?Jbzp+$e^H4nBuSHOT{V|gPO4|f-SV)-DD z8?X9WWi|i8ch&JJkFmwq0Hr`$zdfw@tX!mHUuRDDA2!FM)MZeN_^}m0MbVHe=vx0h zzA1kEa`A18Po=>8i9l%8{CSYT`P5( z8Ij)nP?aPu2W6=#rL>^y@yba@?VJFdeQc&V)V!cS}`aEqsd}5 z>Az@ww*3>^{#gdi(mX7DgfCXGdLCG~fkl#`SfOP$MF&!@+1y&>UfbPcYn7a9ASa){ zY6{j5M&%#l6rsM6%{#NDy4*^My11XU&jE|EQ@XiJUFd-DHr)*J5NSxNZO5x8o7S){0`m%}#hi(5;*~5DO6C$9CRB+C$9voULG3r)}=F3z$n6U_8O*W&J1lf+# z7tBO>I7SkZ5K?+NBICukPdAT*j;w;afEWRD3rp|!D~^3NN5`1+aO@5M@o))b$@1FF zNJEq}Vn!=Jl&p8ue-}p9SQ4;ZE!b)^wAE7&pK`m;wt1NC=MM{@dj15wTqA|FZEHXm z4}$qW4ZgaMpE54B-k$~Zju26uX;jFfH#NPo=58B70peL$B5;z$DrcW%E2^KR(Ltwy;S?s(f2;SKoXP84Ny13PKW(i#p8V|9rd%1b zFdg)g5K7n(QNk3-@9Sizx8r!O@deB?=DisVXT+t_wcqupe&H|tlem5Bi)@z``^l9o zC9^MnLHd4qzodo09+-h)Swbb*IH6cT6Ye?Fo@;&P;;~vrV3{jaH`AhZc#a3SrVk?F--iU^kEJ^kB-n#Eo?I##UYEi&Kr7QcL87psp#2hdbXd} z8l*ki6kd4TG9x^H4hUo*j(q+;PIuLrLlK7IfA;s?{+Isw3#9eN-?i2dCJEN&OUI>QWr4=U+^X5Hd^pT-*H{uGg0tUFqdA|JM1_ zZGrT(94BX+GNUo9h_F6MgegO_*|K>$h}KbV7#u}AAaoG*-&>z4y9an3-!#G7w0fBu z#8Rlwe0}_Ow$61IKza()N0dJKUTq`(=~dz8-`Tp ztL|Bg?#OZCR`J4FA)cIV3j9sHk{Y%zXXZ(e%nxP5iYA- z)B11glbc#lU}K?f@%svIk>T6{B!UwP6UF>C*7>|NtSv8@U0;Mn*Vi+`mJb)!{?twO zApBEq7>42(;$bUM*o*8o>lnplJ0!o+8bskIv0`X!0E&kQ|JBYm=sNHT2DYkZT@HjPNajZSL5ggxqhq4iX& zP_=5kEa)`x;w`=_^jRfuRE}rqu$0$%GOmWU3&az?5$ieo^hT^LB;1N`Ld$MLiV%%h z_67C))*3^(;2>#3wbYtC&{}xUeJxgZU&Q(4ab8M5g}Tp`-c@{-u^SUG2@~kS2Vj#I zj+uzzn+uq%>9XT5YrR2xHepi;6d?C+nOFdAMki2*j#zJwuC>vy5-A%N(VYHfZkKU3 zUCcOBv(`rhY-4%DO-N(6&6Tl~H@$hYyQ@9DnE8JDi0VP9sR*KX2vZtQYY#@EXorKywXd#3&$_!0*bE_4Ba`zJ z7s9Y-C+VHc?v9yg<$m_s*o4=uZ<>CJCCT4c?FthTsMddQOw-weWg;ZHmTB|A<=Mk{ zn@YWKGdg?2&HLCIZSq=@nzVx8xzRwYpZLkI&a|Tn|EjAeR|9Gu=f(?eXKPyUwo61N z%e0+S3GC+&w0}~yWNhaT%PDpRb^a`m^^i006*r9(bSXsi`PTZVJ=4g=Qsw)_8tAX{ z?Ha?C+w&W?_~h!_>hxvH=v&=K(O0)_in)Y6ddh0;l$RwRSdYQ%M9fX%ttwS$g^tK1jWa4$({YV>C@9U7_E$Pw0-QY^JuB2Cp6ZZzDM&uUUNk4s86#7g!5STdM7 zWj;=^3#{p|kQZpxkJ8lPtB82-+Ttl-{s} ztw=X9vjK@c`V~f>#|NjN?akG#*EWvh#jeZV&lZWul8vw|Nj(n=5+cV^1+V>IvlF5O zJf&j*D!XO9*;98XPNg8zc3v9iy1sq0Ax}Ha`Hg^o)=KOq`^M~W{hB$b^^>ibR9T1T z3o2yEJe}vcBkuoZn^xe|Ig)QaP!1y#e{&vI{^lEo{rddx^Wo}z?rmh!Hln^&d$u$p z$*k^|&1p_qS-}LlKw!tF4_YZkLHaM1(eWl$324mUa>-SR0>SuK`J3&EMb%tvwB>8# zqvLm0)q+v|{8;guZ~waZR`DextP6&1lFeCmk>vXMbK@#)@te=_>G5N>TBUve7f)Cw z6~g0Z6)USoP!oVS9dYn;nC584lvgNVTK8s~{A?JS)9|5>Y$L1GjuM)Xy)M`2x}OTd zhorZ*5Z!HsGuO$zu^oBC$MQ^8FKt^Nd|LcsSyum;B22AcEhj?T;#)HR+*s*Ux*$u; zmvmCcujhBYtzLic{By%+g{KRq&ygux+!fy?4b=S$A~<|$l5{>GW)r{a(nPNKYZm@$ z{62;?)vgn4N5SxXTdbVMKzcFF`}@shk6m}=7GL)-F_~H)5erv6#rTE{*CpM8Bho_E zc@<6hLu+Pjnk~5;hgC*dC3~-lZ(Dq4Eg$_MUon$j7r%Wo-GpkeE&iM^%yi33?dJ)z zm^HGrm$Yx1tLlaezrWbjumY=tqtRNn3nLg0`gmhG}8*6`P(C z*88>Xe9eBp>d$ae+Z>q`hO#aH#Fy=nLMXnq6VBfM=@#FLzo7kEA3|eF8`Wdkjc80z zbGDq!GwJz2Mai6D8$PhT1&gRgm!jOX!RT;Yq#b#nV<0Cnl|*=?c_K+a>uw=7WtBRC zOGhnjWNXKWA;pd*0u{gR0{Ur0>8Rw4N&s5Hf$60fA8tk+cjXf<5!fmB4b4%DPhlyO z3!qf(`=Ev}ZloE=rK@KtCe1eW-L35$CYF1$$%xo;ruTn}9{)FlmVW0~@n7k?vu#<- zIuaF%q-K~6pDalkj+f*bMjySj(Y^ylNx_?Vn{g zMZHF>_ls$DzlbMSD*5!8vTJ`sp|9Hshyn$v^`W5ZhDxpYLa8^)4b?r#X@5))~HZ-EGGo>`PY%Jd;c_~U&v|MN=HO#CDz{sl7eNFS~hYjUtGvfr$%p>tn z|L?!~C;y4>ZLOs(ljHj((xXzZYTgrfW zbN|PRf3^N<=VXrZc_G?fS8`@`Nu)fqoSW2^EClcM{)ggEcHV>b3R3TqO$qh#yKlZ! z0$1Df`h3H7z9mFWuDp86_1mq_7vuTgemb`BjBvM!aCjo74b0dyFT=B(ifr=xZB0fe z!T=f2{UTs$qWwv<{_uGV_>3?=&$u`uaYHEBcr%V~yV-y5fA{s_@BN?u$dBjETSP;U zwNxBfTMh%8Iv#ix04axG;ZM}}=|v)vJht}>GmF`}6^RA3AVa`X|KUQO3tPB!5DZk= zOC<8+fBj$of3oXs_V(NFAl0d<^~bdIH{W`Dso+!<5$rw=$KyBNKywE&m-hwo$&&m$0lA>Y{C&s+HkNGYl7JtiO{ zj|-VZT%V;$ytybwmT``hzmVyPBPQlU5Z*K6xXg_5-<)~Lv`C8#WCjl|lecNh29%?7 zSaqhdYrJXMZhy;%i23wXCK3yCH%8^((rq#Z1low=h7(5<-?V&u)vS|rTo+O{8P$VSNY+ZMl6WNc0MRR z&C4r4q!p9mBd-wp9eOLGb@8bj`JKdvYNf)ymkCT-i?=C% zn4Ipg{;w~R-YL?0ERL?w1uH)>eY+JO*_~yRrPtCsV+MH|g^HBpP%Hpke|7v-Ux+}qe6f4aq0hIT{AKGzwfdO(`dR&_NeW!+e-$6UT>MRSVa2!Y zPuu26qJDR4N((`?IjK52zq(%krRT-?PK2PSNv&YOJbJCq?xgx^{3W>ey7-zdcYSK= zLu~Q)^~r60jw9B*h}=tlIdk?6C`bENLSSaQ#TU9LVYtsG95+w4>9#hkXPg9-i&Z^b zy2<5yQ?5|;sFMhZG|z+8T>HNn7L%$glw95jtA1B!oA!LI31P|Roz{P8+hG%mrf&nk zuR%ZhDh#gFQMUfiil3ZJMQl2W*Od6{_$T&&#Tk^kP1;hMU+RZg?o0KSmVlcGr9Ppxwn3JPCAx0OA*Iv0&6yp86-Y>d_a@HMb;l3di+aZ24 zen9UE@XS?yR9#xLtd5k!Dso}O!r^7D=32dS>swl5Da^>Q1Ws;&9BIj{2;pMU_q?b8 zEAsZL{h92VsrFv#utui>YdmwXzHE@5X((^ZE3vvK>-%!9gJ*yB#E-fZ>#!8xc|AtL z6PD=_2*=Er@OXIUcaMd&_Dusj(ZU{frT8egttV!Ed4J;1e=TN4WF#`31T0}U3NwuK zSe~AiOVW7_U!`X+?Y%-+b$@~Hd@R?Sq;q*;2RGiaFZaE)yxO1i=l1M>8(V&;__oE@ z`iF(Il28W-*07|VKQNl1aeg0mjwe$>45p94f;}wh%3$P_CqAy zn)9E^FjM*_F~XE}xmY39GX?pTLNBWJwwDyf$~RjrpJpRgC7-_fN8Y5sAZxlxV{uF2 zCxm4l0MgH9Pckm0fa9lkV2R_I>W8zPJ{$9WOP`Vd#sAU2|F8a`AGm$zyEib#5?puq zVi`ywGg|>b$f;O9?lnE!^nR#!j+HaO1B>-ZbgCw|n;s-H$C@_r{I7dl)u!MkG3QZ< zaU#F@{`eI|SEscK)Y1}7i`Q)K;OBJewrx#A?#wzEUYm`N+dFUNFP z#=$cI1|QPrQNx1M6sZ^0poLUh-uT(Jw=EKo%ibfGUZ&2S*7Wf*2k!hzy*e!KptfpS zbs9%qm0$IAGj!(<=zJ!&*_XFk`|v71Y*a+6+N4laSRC>Osqgs}QRutN7!9d5h05lH zZ2CJckp0W?h0t4?M-AN&@0*uH-zjK)z?Hwf8oy0J6klJ&7N1%Sl7#H|xy8>dKKA&= zY`-$8)^=IM8=bw3@B4kLCsd7A6wmsi!@l@kH;{5j`HYF`8cSNQFr%xV;ArJ>GN4iz z!#3^oT+qqo1?N7T>l;+t@Xe*CcreTy==Qs2ur12MUP;YYH|(mZ<4{$JFRPBOM8$<^ zoDr70p5{`q;!^>T3?DOg9BN&c7!e^Pir=~A;z5LUWiFwi)&HZmL$*6JpI>S)y(VlV zw6E9N31Mg{-=Kz9to$UpQj?oG&!3czt#>8O?{~%5oM)l-%a+ zTU+S2jY+gTwU$DytZM^H7*Sa(^nQ!N(oIcUN?f@TaAKO2L)un5EzlhnZ3l|qhWB8@ za(UO-c9Wvxli{&kk6L@{Dt=HS#l_`h{9GrVC9Y(&J=I=y^Vk^-jKAcHrDd6|sn_vc zEUUFYt<7E7*r6W|*2J#$2wxW;>~DCYZ~&l)Z%c6`ls_>3rl{L$7_gi+g{L&bF=1|b zB;fN1bKp|Xd6-#Xw$|5$4vZHz394TK{+XJ^ZX~Qs-tTrg^ZaRbOT{n0U!LTuH>TcJ z>4ie_!6J!QJ>$+!OQ?RSe5^9bLU^?MZFf=E{a)`#XcaMN6EF-_C~;!`+RZxD^mt^J z4ASl*#9Xbq=c^*xwXOL!C$FI9xOVpno;eKzISXUH>sYCxYF1!icYQ_UGp2eTtzsU_ zBTO79*p=HswwCcS^o)hDUDKmFU5%fWYHy)J=#rC?kJq%PO~y4#S@HD}+6;EZ&)l3@ zhzFrld!*#Wo0{w;vq9izi_@-`>{z~~Jw@4kDyS7utMrP8QM(MfCx6!){dJi=c6D;_ioD3^GZrya~S!$C84XEJ_Xu0)FQ;P zlcJ3#&$Vw04WQ7Bf8Eeb-L#e z{W(hhI*Zveqv(lgEoS9b{-I>d6?ZQ~+OwbQ6OmxzEzSbC{TO1GYp@4|k%TWr1+h$wzhX}hiIDllz;AG;BV&}v7bo^S9^K?n;X^i ztG}fEZ|=6%KZ44W%yFs3P%d~Qa~73OQor(Ae)W9Y(1Y#Br}z>ejcEDM5azBgaIJi(a;FSD8E5tM+V;^A6<$_TCb zp^%BjdYkNw;BU?8-7Gu$G}x4sqDC{(-oTqPueX9y@Bdo8o-xI$E{oXAA?56Dqj+;+ zPGmuJinC>A61^`i1at}_^1-v-|^4>!vFCP+0EPEEnVB$ z9bpKhXS-vZEExZN8}ftqqh%hK zOJtw?m&Ec@OW-ulwx|i2Gp5ct0}^u+kgwOU$+=@;6Y{8R_)?QShlj=c@4xr&{MGqe z|DH3QAIL+QB6%ZYX3v3WK1xyTgvHWM&Rpif+jZZX^)~M-K z8PaO0K{Zc4MB#2DgQ8>+Q3xXdGY>&b`@WuaZOr;}4Q!i47_q<4^WV`bqQU~mH4bh# zm`fSvi_A-`!X*F?H2Hwl<}|krp9QoAZlBkAnjVB|2So-uRglq%q{W$uM&+!l_)9NZY0lDkl2AOU^%Rv~ z`R5j2`eI8(&U%D|+gA+Jk z-d!e?w6!Mh%76BCXfhtilQ+L-(Wgy45EN`~N z>*8nEcgdcjZiv>FDjnLF<9GfNqNED%BNDQHrdX0!@jE|R&gNKdsA_WbB(Z%Pbv|$L z4X=NG~-dGgq&z}D@^FZ}Q$6ww1yw1-P+wkJ$_%6XHEOqo;$npLCE{7UgOICg1 z%X!K$FZB~A3SCu9vubV~(x65<>=2XJfr+DfWyq_OO!d2U@}m3eFgdvR$h|&oUe}TE z!3y%oLw1^`q45O$b4qFwr$VWjkDe8KDm#%}6(t9TX^|%A-D}H?r4&DyO!?-4WtkO! zYP#yu+V88~dHu36PVD8zQmpvamUf#1F{f25kS&cgINu2J`j1|TuRgb^Ng~56kG$*n z!rjniYyp@GP+WWqsryex>*84?^pfve0LsqZhnJRzamGqdlR=0pbupBNNhrfTB(FNs<`PINW}gM%WvJjDG2KQY z1}v-fz3k&FoZ7S*;=E|STA0kWK0_E$ffd{{r`gc=%PwbkNUaDHT8EGeaGu9W{AebsiBrZXMM9y@tY60`bw=WmW#0-nbfX_l_ z1SA*kF-bcyb))L2jdGb>=|Hku5v~4H=onk)PqsC7pFabbZeDuaW;#EKQFNAK{9tAw z&O{l_IyrGxQx9xl;urynUgGis)U5a?VNtV+Wy@uz%;u8Z|BH?J2IMIcv zzi!@^s8kjpwYgiK%R&*QDNFtxbQHp@|_f_7SjRn_rtP69QUF4>j*BA)l$OlzX+YOke zt=@_QOV6lIKRW|Qlv?uX4YWQZr;j=kA#`O!FX#PAJwe#AN-Slb_m5| zGmbgyvN@1GhGYGo#k^1=BN78MRw_g({I*CUQ@#Z+>tENx7bgF(w6KG^Srm?+z`5kF ztTncxZAh->*xM-1_nILR=B=wx_d7L=N1JT~Y&0w^?R3)p%k}Z*SxC!IyDPc>rNxOB z6|Y{-<>9CP?f>_~C63G^Y(~tP0h=@5eET!yWf3WQFfs#gy#4lOVFy+AVQKF62 zl%Y-%$uoHJ_zjibHbWpJi|Q|1d$v)Nfg;@zaQB(hkwCF!X8|rp(H7nExVIsgT0r@g zWy_1Dr%@_Dc6V4TO#m6NBav}=$V~UVObcxC*QxwxN)jh|{$tBOHxx#_qY*qm!&-Zo zIfh4Zc82KIt?_4?lIl#9uca)W$k(paYvqTEuZBydR5XumL=WWR+CG16*yofa_O#7G$P%6G*6{TKtybQ|zDZpK%() ztX04phQj*`W0QikmA@@ZGRSn2ZfIf_2!|yk{Nh$&Qo?l;CgT*o$hwAKQAvfH)xE0pf$ zf>@w?KXvgEm!Q>O&U9$G%N1V`Q`q8%3TG#?ptVct`RTgWa}#R`VqJVLfivVQ@fS|b zv_6fE(9mIzpIdzTOG%)X1W0L+0XxRA&7PT`==0V4ZH=v7{|p+45;B#vlU01VZefYd ziU5gNF8DYh0%LUEILd#Fh6@Bjq#fs`=)r`VK8HDKBqML^_1LfUn~( zfSKak);#=__^u0~fzUPo7Q<}CZvnQE;^dd1nsC> zuh~x>^Ap`|$UG4c(9RLW7E#kGn}}JDUHi~oXjYj8CM*ALOxm&umU+AE4_qMv{BG-|J*UF|a-x7tq%vaDhvGTbbZ~=> z6<=v7ou2r8&F%&g`jeZIg00qm>2u3uC6zx)6Dqgo&-MKBN!iqnGmC%69#G0zg5SKE4s@l>I2n2F#1Cx7mL^?N`5;UD{vECpw3pM{LY zh)>0od~5^8bB!H{VPg`WL?w5=+?sJc6A^p2VF@^>|HI)=6=Bs2SSY~)z%jm`?oyti z#RolR%8E71X>@TeE?s7~vmVDvAaVx{lz=?5=RxBls$UiG&)Hgg@BQ}@-y_*2PRxC0 zG}KT)o;?&Ejo1_brB0>ROSaEc+XB%}Qx&GQd7~IB)LBE1q219Kg#IFE^O;FEvBDE2 z<~$L2c{60;f(#6A^FTPyA99wfp;f|(OFOUbms%>_O>%O~<)dNz99MU&i;RU3pXQ!3 z(oVBLlGVivrZ!Y4xAvc1=fA$+^p~`#8v9wtD?6}-yht=Jqq2waXm*TDEy>YC<7E*YNn z2uo5qH&aqRzaGt)-@-RCWLp>#ai^xul5Nol70uG@yOo@EL}idr{QG9m^wcsj~$^>KpJix6Y5X?~&j* z(k_wCN}ISWRw5|P4nK6DF6`R#^*Z^5kfZ?N?JafOY7Z5EyA?-x6~N}@5=ETnu*d@*8aqjQ~~ zuk^t5M_USf&3U)@vVXRP%4D|zjlEh3IJD6E%ki5&fSpC9gAC0r?e*W{TN{_|&)+@}~x2ha;PT)4Vm-U(N5)47GsnF#TPij9-69Y*Ta+$-zBGmS$qaycb3bkyTo(2&E zKN_*@5XC%W96j4fMapYmckjLsd&_)hsTmpki9%&$RcR)gq9-*nvI*3Xg%!LYbC^ejx#BBn_odRk z>>1k@ipMAu$v&Osj8%2wkmP@rZw)s0zH{sSLZ$?bB%_d+;7qM8C6!lXiIFZlQ`7fU zk`g@Esa2`!sMTqgPqVy41L7!X^hPf*PvwjB5X@E;&>_tIBdhTG&#=54$WHPa+y=|4 z6m3rc3|pdYRySf!FCV>HQgg&KA5M}Pf79w=4RZqqEg5Yj>|_QEI+TsH9E9~WRwE2P zBjH0@;~B7wl1xXKMdIY8x6NMOQ8R7{yj$%b^MEi8#X3%6Z9-U3iQCuT*ZY;`aeco+ zN(1Zr8cA~e>Rh_84q5qGv7oCj!7nZ>GZAL96jCxh{4x^{&-3n0+w7O+0O&pDC?OO2 zOuMIAcgKhC-(Th>Y-UbiMovQnA`u*j2CQ7_QZ1vk|JOat@5>{n5&O2zw?2P1x!{-1 zAMVc_?V+1oA8C*m7qs^$L0Mh2aep>;+e3KOEq-W!uJw27WVuvDqjXPOE=BXeZkN%g zwa+*9fUBv%`f6MM`sal+QvA7eURphag!?|H)4 zzmYH;Fta@SC|}o;Sw@Ds=F&_I3t!*-cK`C7*8A0XD$&~Qp!{_vO4$&7zgI&ey*f38 zg{_KF-=O8ioO4Fl5fRg7%!C~?1C|-(9bPS1A7`aS z2G}tVupw5eB7$|Li`k8U%bZ)_Lo7epJKx60@8bZ?=!K0ldb zEPbZLAleR`3@zpcvNhm{>+eg0$q_A(X=b6XzV;iXhI4pkJDGaw!Kv;ht%KPJ%C@W6 z;?ps)744N&8?sD)z2~*IF~Pf?)w(%Aqi&USy*7R$7+)7ZxA?Ia(bw_Y)`Mni?M0WH zU~^(neQJxpdCTGKjM0k8L))EAQTiBv3lkLALDwPS_+b&#ji ziMB%7zzL!L>l`rce=qGMVsh!_)`&|`pCr0GqQjDw6&1*ph)wf`gFMlW7Si{C8=q#WTN)xC$B{I4JNToltPf|UvDsB zYxOYxW>?%&1rUXLX~SishG}5Y@!eJx%?nlr_-YfD zrTq0u{H?6SQj4e5gz5{FSB&oW>-dDC6z=J|f1+lodwbosKNu4ctPw`yb)4A5AuZGCK8A4{#jyOflqA5^^{#ox5X zXv~~1#XpfdnSwSIKsXVllOo<}N&}Brq)>LnK9sl>vq@joFf({8?fXrOG%Xri7~AR4 zEmTzzwIo9gKrFdjgl!%*#g5e&Gy@^*edWrwSUL6lBIsvii+pax?kcy7Fd!2I!A=Yr zhy~V*mS{suAok%_rhtlHLbokjjIonJ9eD0OjO540etmZ0CIDu7XXBI0k z%nY+AQYDE2b2y@0`G%Pb-Rv;8bT=<41gFkG$qw7_rA%y|VW*Qt&V)x~S)T{xjmrfi zCyI$glonkE1v2L;nT^b|8Zi^A7%MBvnd!u=+Nvs3;MWK_Kna;qd0Z^t6AALC&?r&+ zlTc0q5?G*((m1R+gB^$$myv0%4^O7qe1E^G}RXm^oo977_gT4|l}gq|6EzsfcegCf}M4g^-qFk0sUf(=kB zq53kePRBx#7+EJXY&?$RnbrN9ZHbw%o5UG;iSjf{81VQ0#9#Zr{NW#>8m_@gN3Y*3 zaJqdWm^_u0RMnZ^Jnr%mXtF2ZGpb>!)ErtLQ5A`7E;vMeCo#>2Wnpb*NVW(R4PJ7{ z_*1{<`!*fF#X+mfJ06}c;h=qH56sK+?C)YxS*!%sKlnI$rb5cNd?)_+U;2m}x$f(b zyHfS65cRw1%CekdK(J9+3hAn7jb2)|A=@kwHbo}m_bXAdFbv8w)Py#gOo&YF+KumX-pCzq4kiusi(e_Q7#k^|>;K2u+FJO4>$U4rwfk@y~G+Q@ZTT0_$N zY{#~La=TyF`fTz0&eE2du%Y}ml)S{k&9#~lD7^L%zb^R>c6urmjP7C`FL99?pU1IeInyk9w3v8w5&+fNIz7oI6Q|df!@wfVS zd|L5$D!$EKcB_S9(j@4Daj=ilQO=LXyJ7gx&Lz6vPKnMTfb z{Ox{OwN*+Z&!|J9_37`6{K06f78wtE{>^c8#n0>b;+a}1KI`#16Mg&b$_<%~EJ+_% z{8N>-jXjfcO|)xfvql$Sm*N(a-8*l8E8S1Fn1je+hzssp-}Vlx=QwCNO1sJ1*4zCupzb^T~Ctr7=^-MrjF`Z)lWz>uNIZLrNiCx zWH8T@!41{JGQ%*Kxm&v3BhnUfVoS5`c}nH(GJ0nKIoBVY}9!gsA4JS}O=h z^}wxRNgN_NDt=WmB+X)3%JuhI?^g%XcK*LEzCa8qZ$2mKtsCGiYauRP_BePcI<>I^ zH+^aCwMGk3f5I_Urz{1$mKc_DzS0fJnVSTZxY%E&fHO-QB3ye|Iy0Jl}Y$Rd;K;UtjM} zbp!p6{q}d3yBQI4tf)Wx_Ko%LuclznO>8{$9f(Qww`~8)$rW(RmdonL`ZsGUW*mE z+z)L@;P8I5%#{p9vkptE%YjdgU-@Un$8M?Ee~gH;%|MUwjlXV9SZs?=c-c$w@hS0_ zW(@#bW?YJG{e$0oyX+NPq>EW9Z;ASrJlpUayI-q5XbwM%rJmA7xw#o=;?%kylih(F zU4O}sY-z(9Hvel08iyPVd?{^Z|H9cKOZ=X^f$huBadW*>PGz?6Q7LOyETiUZ5%JB0 z%{uSd3KT5yYQ@jZ4Y}*!Tuog`_ip)r)g`Um)o$WxgirVVtLGPCB#4=k%2;&_E9;dK zMSUGaI57TVaW>1oW!HiXii#gwd^+5vGI-TT!juWlB(M8={@ilBW?f3S#MS}R$SEdH ztNxOaXDN~F3HTz0Nhk|w|2%nZZlL5XeiA;2h1?VzadiT?KG@XgS}nb~d;9vcMbz=P zFSwOpbq5zBit)3p4GCXMM-qv ze&c(}K5VrC)VZ>7j0NiMAMJHF1&fF6ASxd4_I@{y)z(pN^!A2shAl^A!GwaOWwcUK z!TPM&F{0zBo{#QuYf?s^d&$`^0Vd&EIU2LgWuacC)A?Jx23N*l^b89(H_PGf8Sd_m zQ&9*FV_;aZ3(XBStaBnYaz|lvX6ETszEFsXnP!nOC}(#mz4lB8rX|__LQqShOnRU7 zi&Yu1`s~& zzx$K#-JHI8b~`)FG29GrGqZFvr%npmIx{jNa0HH-Paon)95ZKNMod^lMA*zocoC37 z^N*5zp#+Yy+HUmMujaYFFVienBb((^9u}_sxmXXc?9W`47||=`F=sX+sPobqoJ$yw zyl;AIw8pIdxl4tSWoI9{sSd~QYmN&XH~6noD#N0vfkEOgL;d$UW7=(*E@7uC~A7^0srVvCcw$ikYVKhnS|^ zyu7x_HNuj@fV9H{UJ=tf_QiN(@Q=t3x|_*UO9O5saqF+2Amb&dRo|BhQ& z#^pau8#p);aN)x|!^yB($>%Z;K$Z(8SYE%4mmIhTN>1-8!zLALI zv#|`!9~$;nyCMu@jXs!5+5TQQGM-$Y+Di0-DBWw$1DeFuRJOW*M{fLF;Yngm?91zdc~IwO>ch+_X14y6pCYbY5c?_x*}ai4DZNm+1g$Zs z=K3yc?%51_+?)p_0jn3g_75`wmrw5U4)exCC2|wsPAZ4@Bx6m~a^0!QpM5Fd^!<|Q zbi-^t>z)}>5L}L9%QtYT=GzuuU+v5BvBkG7K0YCSenR{(%`3y!{CcT!wR**B!qock zc4qw8`a~(w!Sv`npYge_N!Dq{SJ6gG$5eb@aJtQIy~r z1z9C3&iG4#W>ZCH&XxQ6Gk)e;f4*9!zh;9FGTGomWlkVG+XoGU zz{9(Em{-&bh}h}65CZv@Y;`>`F9bwyD5vHi!R8f zNmWb!R+G@}*Zw{KQ(l@am_erHKr{GUn)Z=vAe7-=_-`xAT(-p}^B@QJ#&AaV>^Fm^ zL|3m)rHr6*tJYrG38!}a&TP9?G_|(t_?oc&%HpqC>;4@|yE-Gyhgd_+-V}3+W7x@X za&yBNUMMY-IuhmiO5Le|x3HNJ<*b)>nGwh%b51{IM&gp0z?_+BM@E>%B&N4?rjzy0 z;sR@+Ca#Kl7gDcWA{>3sZ2xwBU$%X}TA%V3V7s*7L{$KTKzzTu88Dpd;|@1Fp)^f# z9vGHqA8zTR{yEIx?k?+rhI~aL&0=Pl%^CGu&xtv6!Y)Tn#U#?U!u-`m&Q;xs9Y$W zI)7B?m_+%3E8FGGHAywU$z71=8>Rkaeruked_LG!6(Ob7euM48fd2o~;OG&D?@*8) z?(oZPRfWY+rb-%x&LpF%;^w9GM+nle2BR{y)B5u+hNSA%gNqB|MX;31c|5ncn4d|V zNg$7$_i)3!S2p87=6p!dv&`iOq|&?h4>Hroqs&C!EAMSi3-84g}#{Krs^U)(`4Wv4tZI*7&vM;9M$w*dKho`z6 z=%CtQk+?jMAnlxzSCt>akonQG2&poekFMq2{g3^nZ~u|M_l?hf*B7Mvq5Fkzr>9EI zw-pA>eN9c8KN#2@82J3H<{3m@@A17+d#$bl%Z#u6;?v*#XYT=&)DL#tEiS# zW&g{9jzq@Y8k&Ndppu#|*SMBX3F%uwZU2`>uFE2h`Qc&)(E(y9dT&5PEOMT`KE7_} zm*eluGAv`%#O?^TuD-h5CMRHqYvacB)m=p0|<|<+#(n{@MF4fA&zzfkq>)T0<3G;SiSsskp4I)D2eH5?^V# zA^A%GM-pG^=EB~-HhvGzjP_U+-?O{h_0L7KXOGWHFj-wgU!o@^p~9hltLJo_Iv;)| z@d;*%r4uCHT)?;dZNDF1jxQ4dDf!cTxJRHXe(PkRl}Q)ivW@tB(Lrge#$-i)(zN%y zj_(bh!)7`yz`g6UXN?(>=q%srMNVwfuj-0$1iD!Hs_(KrJ>>84Y4KMQ{I?e0+=IVk z`Nw_t;d>#yf;dAf=( zF3aoJXOEBT^}(+!{*V6FKV|9V(y(~DE5UCAo|pga_22tC2I}Q6Bu%l5QtFjNpC=lc zV3^B;JhzE`uGZ>)KW%?6=15q}Yc_ph@%1oUt{uBI;`aB7**<{n#egl-Z4H$k#azeV zF;LeYJw3Za8#KBY`SO5XGAL#Xvd{RoG5GQ7-}f;<4_ZjGkE`F^|AmxNGu{^ddULs7 zf4{w7)#^GD&~H~RAFJ<)!dYBO>Mj*Lhzl-2|?ZOTOrW;0kDcZrr0lAl;WNz>4G z8?6v>v!#UGpK<|eU6Rc#n6^dNd;X=JEY=veo<pNQu$;pC*1|%{nfQ=|JvjY@mpJV+~}XxI_Mi)%GZ1- zMXqlp-9A6Dxu&wkp2yS7gOnThjKhFDp7rgEe8}VCUTofnHZGFBiKdj*QECksyT#6m zl}a~X`F!AQZef0>nMWEd=Zlpztg8vI8(blb$}aU`$*@a185AAbglVe3EO%rq(cYF8 zNZ73*Gai<6<+lF)PU~eox)BwC=Nlad-15=wFfUIaP8SS0#wgXo+Z!M9%CvUeGaJCo z?WxMUL6L5hujVi_Uvr0wod38!e>l;cxzVxmbx*GOF6AMn?e44J;r(*Gd}cc`x8+Rg ze4cC2DlZo(|7rJannIPWqA%Yxv)v`i)U^rxO5&@Yto-(>?~m8TUzFz8<>&oBu5gzt zJ~lD)%T>Uu%ued!xyjo^ci6&(S#Gc@w7qiQG`K23TKQx3 zO#}+fO^G(e3d8baTV=$R*#E}wO#H_de?R{hb#T+QZ1=QALSXYd$5qMl3G2VyD_@T9 zc-{K{s^gnt3u zU7N(F?Xn%d=?r1H-DVp|MP9$(E&dXQ{#%N#HqXoPTRE+7zE6t3)@Q#Fuj6OFlApX1 z9}6kpr1h)#pO_!onqO4@c76WXj{it|wcjEK-4>ActMjKRJgNArKk#z?@XN)wmWpjz z#J!wYb4MU?a&j^HOb?d~qb)kJwKamJn@zf#SA(Tk{mo~A{$4pyO#)7_j41B>lPClw%$w-a;C-mBOgxQ z!K=uXh{YORT%{JYE6K){V6X1d7t)wkusy~Fhj8=(RUsCUJ2*+VzSr6DZ8R`{&7e9`ia%%((Ngd39E55Z&G&?n9)Vn*V*A5%* zz{zcR-nhBpX4v^;fBL`k{dn{yF|jW`UXVa^FO&D{@$tuKQ53^Z{Q37@T>SZECOl`k z5n;41+k}}PIfsuaG56Bbg=evNqrF*|RRLW>+qF=BjGX^y|1^?OZl23iiE~j6Gh`tv z*2A#q4=jgId}>=tc|$aVpeJKyB%&Z!X)_{VMalh*lGzQUW8k;&qy&(Bsw)9tg>9!Xm_3#aSZdj6g)wItHIJ9s{1 zD^Qd-BwF!3ESC+qSAT51%?8YHo$4TIg50+ElqIPT4CO4$c~w-WJ@hEv!0 zW+%tZ7^f4?CwqK%x;fcA`@i!4y1e_w_=kRE${m|>A28rJ`m(4*YPI!JBG(ys_N{;U zFaP|DOWt4R<-q-nha-;}mzkHD5f%{#Fe4-INB_ak(BW>K0G#E#{mz|jBkIV`B`lDq z+c!6`a$=&VO?LCB`G#pbOC*GYbK&~8nEL4rbENP1)*aAELJk6%Qk5a!*>s>Ih|*86aE;l-kW5%e%o*G1^dm|oO$_5eDwz^ zvbbFoJ*gzMnLkuwI-keZaHGk0MsZd`{m1M5PtCg+b|u7{olIxDZ$zdypU$NUTCcDe z%OzgBHSsuXiB(N5UG{E1D1J5-bYsKneqWB%wl@hi^XvFcNqs4PS9Da7>E|!}R58A4 zM2so6|Du5Ee$S;wrb?5}^bp<=8o*_<{tGiz`99;fW>$)oFaBW4fpf(t|1wv7O}!zx z@A2i6Ho&A?uraxs?^rpH?HGb!|SsRU^P}!$4l{rFH2UMsP}8o z$*}5+J^yTitK>sH_x=f4j)FG5CcaLd*y8sTd8wGGT2y=?dvw3A_`N$Cd)PdS?oN)`lvZ4wr5)mZK8;T+R=z}#m`*v*@xdeFGve8v0l}t z1D2{zwvvc#{kXb+qnpA@2VV*=#xT-rT>vt(%_pQWTOm%4i`Q6|i&siOmXICl`di-zp%|t3u|pd zuEANi-3e|PH6R}bOAPaoG2Go^m=CklfDe1~ZrB^2^>O0ie*2i0I+kIp{Ae-W`aBk{ zB>vey`1$|-Klf8du)C2nlG!t9*AwX&M@02VAe3VRO8tx z-7h*H#QCjkpk^XOTk!s>xf6T+y-4I44SA;Y`QVA*Extp=>fEDxFLK?F!aX6Bifj`O z>dLd9t{Xk*n?`iE)?dV`J-)T&$4j{yXP+ZmVF!hrHz0hRXT&(&#pSuVMTGeoc><1V z&dxmCZUX7!Nya6|jWly@UOsM=gpa$lbEl3$+<3q({D~XlxY&U71jKjtn+dbLkH~R) z6Nx!Lrg8Lm1LKVVkPn+n%uV$t*;AM=;H7tVmR@_}bQkeLDpZ($=S_3xa3L^m+;3RL z#)Gnrur!P_G<8UXq5du4H|-S#yJ=h!TP1dO81lZl)71-$w6q+4Hxt99Ad3VpbA}JE z?|0%n3{jQpjKZ%CO5@nG`-ijRxXhE=aGS&8Waspl7tB1)9yb=3cyywQwG=$7fSqaH z_#+;~*EsC@yUWM#fB4O3&mZEFxIgB@B_ED_IPzh}5jYN-R7Bd0{DXh)D@TTR3o3rMkS2Zcdr8O|xRt+$5F9*LbU4w_S~>_*1{@bE&Zy2=Xx)-hSg&z4_ep zpBiDD?%u5DIFgoq;^JDIA4`?5lx1Z#9_0hL6#art^-|We`hLUck4ct&xpOYINb>ww z*G%Tv7L=Zn&i(cIS#Cdp)=ygfzTaj%6!ZFT`@x&!`t*(5%s$bvT9eJ!D7kS#rtYb-Z$pboa3- z<@@ss;j&JV2v)3VP<+6w;;YJDPz1&AQ~7HBv+=M}Q>bje%?sN}tgeJM+ru(hkj+P% zQ#K@SJ%7hnF2|{4ye_^iK+#ovkzCLibTJapla7k-TYO>Gw)iW5t1oWnlPtefv(J_rA5T zv6(?}>eV`U&0(Yij+&Novb41{<$uEX)f1&9PH|>zu}iq0g0LmDaw)ZPFn)QAQk4t41(~fHRq!2?=Z_)def2GSMv30T%NUw$&mSbrH zb$RC-6|d?S9U$nUz7$xL%7Mci=71r6)QsKTaXyW+aq zp_3H0W|oZ_INjWx#WS`IiDg8 zaj62$#~W|}=T^}Tq>o!5Y}EPltneKXfpMFeIGqzP9G02+P~VRE6JA|zW>4I1B$iUk zb2eO(pim(i#&U2*Tmor+E5vm8xC1icKEuZOjm#Kw-l-u>*(`EUZ8s-x99DhsFT_Fd z^xKgJKiAUN!QvQ@*ki^cNT?E ztF~SthF*=2UvYe6vS^&XReZ|*b#1odTleqnN<;%N#wWM4-`uM@uOS@vXeb8M%l zs+8)HLMk+}n>_>>j4%g~I(Vb%q>aUjdja*(PK-4pv*iRwu6i>@Q|9!74a?q)I*ev} zTdV0Mk`+f=CZ~Q0yV8$q<1+#ZR(xtt_aJ2%;4L#?*_rFeSn)N<=vKD?3)oevEgRxt zxtD{?84|tz(F#ydd?HM(riPv?zArz_&JQ>H9}pIw*+zFuVMkFmzim&@u%BN;m zhM?H_TU-A`Or2nL(XMC&mcV^v8%=?;^ROja(*#d<@uhh8$=}ddH`iNdKwXs6GOT1( zCf}CrSH$YPuzoY2q{2y^CRE@H zToSp|Smdp?x@eggL^Q_S20%9DSJgD|*u}=PX^0D$fzA65t&LA37ejEUM!7l0UuqT6 zw8qtZBh&^M6oZ8B5iNHZX&ks|>1^avuNzJb2#rSoD|{bOu4fGfV#Etm3Wjdkm^ z@Aqq>IueQ1IuuL$9Gk1IW>(og%G>+ZB#rS|i&hsDLV7L9S#rKs=M)JHP9ajRk%O?; zoSQ){Cb^tSV3v0e^&Qw$iEaP%L`#T$V(^IsiJFgvnR&%WYW=1t6 z-0U*b396=a?2L5hIMp&R29AIsk1ATFM;{jKWFaFXGt2{#z|8t*R@ji-yaUAz6o`FG ziDRx2%)UNU^VV;qb`I~4-k;4-7GtGd`S``fgtW2-6$alqFn_C@1%+^eY(klaw+NsD{U6nECgt+ zwrmpR{TjBPp8yJ!PzJ; zz5Z;H@cH{=-T9T2zxEzps((@Y$1`H7v~Y*WEP(oPwmf*4ta&ywmmF-b&OxO0>59TT zHxNY*Jy!WpX=Ki6ju``a0B5uIim%@C6EkE!%U1UvPfR?p%61aLD-wV|%AD{M42Cl- z^LP$J#u=8tji1lN;a;9P9H+OGDi0v?cot#iXPnNe@jde)kz`-tUn)Cyt3@V#awIW<+Ej({=Bzc5prc4*cehRaI{hR6;#bU1i803M`Q_&M ze1k6C>JYauEE~%y+&9r@3xCt+!^%H(e&XuWUae*Actk@OC~+ zACBIIOX<1u+o|}lQMlX8SXw$QU%a26>-o3I4eu9?Ifk(RJfEi-S&WL${IJNYm0{2_Ux{xEU-oqqOwQS(RP! zELybn$_rKcvyJ*F1Vrlzu)m{1D&#I`X_*t8n$`6}DyE#3s=go*zbCGAc+GB^;5t6_ zHmkmqe;%(dRT0rWqh5dC{BVS9x1NXjS|1`sJ|TWy$6xPvsnZlQXt~OgGekE^;lNP! zOWLX**ZPM#>elPO#qafJv%ERln&rP5pX)ElqZXaj`%P(5msCPz}v2j~Pr{-AY21Uj%259%Hfe&v6mt0;wsBIBbqGTCL! zE}#lOwTmyyq|@8=CbDgR0o#4MVJ!u~*3_uJ(?_5C+}Yos@G%jdo-y3w2-$a-6c$>C+bq&sVN&Zu7N=DL#;H{1_I=HC zb`y5v$EDBhE?L-28Lhd?)BUG zLvCyd+RIPSCCM0$(W=!e{wz5|cI*ECto_@|uWPd=23=M6^LxkKtncg9tM`dbOeY~ZG2kYO8(Jf#Ba)zk z!5xT#Xh0;01|$Zx-Jl)?L{L#k6x{p?LIf2dD8vJe2ZDMangr9Uvt7NuZ;to(RC!R> zRaL)tyz^VDV_41CbB;Mijpx0e`>DI`s%yjVw{8er;hv2b{vWq#PE%tqV$ELc^i~?p z)c#TaG2MmhYpvOM#qX&*(h1dY`Zgkbj2o=gmrc@!6+vhs=AS4JN{Sx!oQ>;42e&)z z)QR~_Tq!^>>_R*<-l>Up{b(6>{Sf!B^ohKcfgB>tue@P6vpziy3E=#Q{E2R8=MnK< zt9O0>r}O?5eJ6{X1IzP+0qz&zasLE=;Qk7KGyfhLz-{MC##c{^_zE-N&CeSw{g~O6 zfL}j|$Ni`@e82w{lRqP_A9y^WZo9wA(aipWw;xow*4XYX@AQWy0>^QG&HPF@g+Gox zjGluJ;2rl*0C2uw@y-4EB`>_iK(vRMSv_+m+~fIsgie|P51dCB{Lrf_;;S7?yd{(J zTfp-auGPc7lZueg?oxHsIYCKA%f1QJ_@r6Gk3s(g5}(&+pgqv>ws7>4XpJ3oi9ezOeNamg5tYic$w zj28jVXp#-Nb=65oY3m3H+tYF?Ta`Kw6u-~+B|iaYe44LTeQPk5t){O2R<0R$4#qbu z2l~E0)M2amCWSBal4bZTeqrAR8a46rHGXw->U_^0+4FqoWg+6Ll}E7Q&;IGt=hvz~ zTM9m`E%b<(uWnMAi0TA4!2I!$M?uIVxR~9g58!kD;&|TKFc*k~DqlAbs1*-(~vb64&ASGl%{>(H1iPEbyBP8=Rk{=w>>xXfUO1mDju2SXpsqlAAM zUw1>EUp`LRJe$6W|FS;BXw36NFT8rk-nzQX7~_;(@oR9L*SLN(;k+v4xTWS!KLzO< zw~{U=(gjUPUO_?}4eQCX(1Y2k^=XK>PsJBuy->U{x-i34w!=E(&gQVN;!k#YOjlZd zlyTq3=dQawWm?BdT+OgUrs&NRC24|W@&s)=ED15k9dwMc)_S*QAuXpiD-V~Ly*u$+ z0t)J6dDNuE;pJpXTOwRx>pncv6G529xR1d(u}oJ3J5r0j~EOiP`^Q zT%XtPE3!?K;*R6&wXxr$B1;T%M7y8LC;pjvRIxpU1$B43Q?9PXr^vk1&2Q~TB2V1n zj^ZiVLkg34KG;d$2Q=EB zeDMd@^EXdWW=cz`y?Q0#hxk;;ydyVBTcjoLP}_|^E34$dw(|iV6Fd1 z-{*Mal;X_o6wjo-RZ{Uvz68ZsWS`DgeCE#_3rhV5^BWbz(Tdcig-*S&@|LDz8qXJ^v+Uw!1&S0hjs10N~kn)+PzGh&bm*q6^QlyFT|Pb-T~^$KT{tmB{CPfA#A} zDlP#y$apXKyIpUNA7sl?Or774i~~!1_~S>&LlgLYo+E+l?YH|PP?qb6`x$ZSYr1|+ zAn<l%J1BJz3Lbv>S85&C|kJBgm-ze(yW_XdHWC4@#k}A;-fZ+bYjSWs@(MBBnWri!x5{sKq?z9p|C{`&Z&`q|CZ0=$HW(S zgl&f%-xMG1vu>>r)Q%9#i4Gt0F z`cdkqq4g;oF{=h+{JyK|`<>1>_n%YXW7daeNodoo#5U@b@^GtpEnPD#a7Cg!QH*a( zIBEdl(oFjpKS!y5DQxNDDpX3C2wyp44paQ09*lMvb@quXXnxq{!FBw)pH}?H1xyIK zH1zwexuBju$VCO`h4I3JQ!Smm{}O~PVJd4sw`IP@IIX0_Y(hl8;#K&_kuk*1-kg93huVckW#9euY`@c0J**6H)_SD2!@oRpg z{sZ-1_hd-%tN%0cYamtkccFkeW2OebaD}N)3g?giO@C*$9)x*obDFyty2P_+vLq~F zi>|d2zb`r|Rj%7ww;gGgzJL@yymFl~j_&VmXD4>EY{l2LMZhMKUY%1I#@BIdu3d%p zQzv%4huf(5kY)CCQZ;@k*nqgc?xM0nzE@%;H21Z@r@Gu1>XU8LQVSN5xuKb*87w17n+=iF)nEQe!puAJ89arZ|L74>H07` z5;vfRA}2FWZ$yWc1(h4UsE*@0ZsrfSWBIuJa(g>=9eca%zw)2_YtDDSWrL+Wb0#-7 zwPdOK%}ah0@vLviugAab&%OWe{)t~dLp@4U?^5U%$$d~Muho?B?f!@Mf2pPM8#Li` zI0s8wafEaK*CHOyrG$4A6SK*%Z4iq2lzqqw^}4HP%HAjk0k|`>x}T}8Rv;30>4p}C zCJ;cm5DwBxsrVkVl`<1(2Sy_u#m9!*KU5JlhEaY4VS-%wx$Rin>9)}j9iCQx)B0=8 zYf-3G@p^vk+aP(@EgNbAR7;lyoJ<&5 zF^(xlQB4S&5r#-OD}IVG(gwt~a5trcrj}qTSqjTwbE(nRy9z!tDBA2NaI;fpsuJRr zaFo5dUy<nBxWkV5@#{xx zn|E`+zB9Y*yqP`hT*WSg!R%pXUcRq(GT`S*JMLeQwagEKiA?DF%)t{=>f)k)7U_u=QwA3vCXud5WyA22t&>>M*sJI7DrxF6WVPyIw+ z;MXyCyUY*p@bks|ar`oWT<5pkzxesi@i^!0KmB+AsmE{sNgF#~TRF)(!&<9m*w}I! z{QkYa{J;Od{0q;x-UH9)^BEp_?i-PKJ~L6)TUX*O{Q-Q*^Cj+QgG>?+^8@?N&KvxT zpD%%9=fe5U{muM~`6B`IN1B;kW)DB#!5((LWCFOeJ&4a+;=rtEhJO8M*LP`e?qA?< zu;RNB+i%x*us7HRyE2~zh?NhcCzWt7^DFH#{{i*}zhEcrf%Bc&7o11rd&x8f-{YJ4 z3ID;)n{ee9hkE|VRRS)WuWGH9)*u9@8|$kc z{+L8**a~Z;K%1et#AkBF$IAa*e->DC>ciPTw0!9JkPl-{)im|zI@2kJq@scDGv3I= zI?zYDs->IoGx6JI=C3L~wn?>y@1Dwm%b2c}Ol{e`wX#V(&G4W2cYois16=T%*ixMx}%!caKzGBClK%8)-LZWzBy6SXlxyr$@l)~3Q}aXopD`9;jHE^{@Dav|8)aqHTV9^ZD>Cn!08K z^n8hr_54WSiWys+$eW*G9z>Xnm%;|P5Pj|LjUHOKctovnJMcp_q)J4(Hn(S_9X9T3 zu0?4!k*Xzjkn);EDch*X9IsAFx0~XZ7D;3YWx_gs5vRx>7F2DBZW-+DwR(1Ge##DX z;#1_uNf6{*B^4hNe>&Xk*?x>431nDUDN^eKQMfh~{C0HLoVM_E7tR)pvZ%hra72x+ z3>Yz_ichcT;JZk=*@|(-E;T6dB&}k_ubX)`n_QeM#pk)`7J@ z4fuZhwkGy5j||2~0yv1U1KAY@xkwJVEx=y(kAL%i1zSN)r4m}QFro92u*y=#<%c9q=u(6&RFp(>io3)c1tM@-oLb+P zbN|aQXZGjJTMCm6ixxH!Q1DrUhAO!3EIA0k?m}FS7qBclYRT!AMaM2!-k|#iETi0z z41h=N&-#`r0#K$v0vI?=I|>v(qMzoyKcW38T4rih4)=e;xPQ<*kv3?|h=m_6P(|T# zfnaBf`#;#L1)T~`?|&l=x~_^p8O5AErQb}UT<9U7^@&oa(fUx)kLMR= zf0~?qLmKckwDq*$On96CEtd;;`M?^Ts@D6p!;-V}|6(+gk&5PJsDsv?4`|1W)YZ*~ zm4GFl8T?t$N^S{w!#INa)2%f@wn%ak4%t~JJdcF8xoD3R)I(sw_o9-Sjlf4`yePdY z%Rst4I3l%UxJrc&ai<@9Jzrq&w{$dc>K#}V#pixX9OjK93ZRS0z=K39S%D7u;QFy~ z>F|u_I|X6izPCiV{`up?^Xb=vL~7BbyhlH*{%dw}!7T5uIO;z$%mbF055rfa`P&bs z$*@h4m6mt#8hYFi{?-$}h+mP>0lV$gLY(s@;{9CL4Rgm6Z|<=8$q!E4&~%Ep<6wf+ zT(&hy3wDz-2`czawf=v+=RMwkEuYrRd}C2|{UYwK`TUxh@J9lE z{TMKdugj&_BJc8vH~W(JSG(ToIq&-`>#_3&6v6Y&EHd5^Uo&yN{Z3`(c>WctR3w16 z>ML1UUgmqoPu%$aokiO9D2voP67hS<%ihDzZ$-QlepFML^t(QF|Iy7NzEX?j{CMZr zD&n%dJ3Ronzhy)^GH&=4%+H`tNI9o`s6VTIz~}iHR;-g-wQJ>1*+{fQ-!bMl&`rBL zkC9aBG*^Bj7$`E$I7`3hT0HTw^FR80aSDzLlT}?}J-@_+Gi7G7>#Ooa7Y!)BiBA_7 z{5glIPd4bB+gXFWW!bsYsA7YzA5(nvIueR!z#5LOXsMT0;W>?vjUr z6goKH7@N69@Pm7?(J{%>f?cR+rA`E~R26^epvx(#A#v;(^oVdAe;|)@#ZUD8Ga98; zcG+`!ZDoWNzhRK z*f?NlWO1G7BG(d9CYo=OBTxuK!PQ(*qa!rUZF$D4$1VG{Voilb*ap&M=;kXBU_z9ZNNc5D(1zrH(jfl|(4m5{@p? zT_}wqrj;VDSgN?-LV$h4fF0cQ4ud?!)J;fvavjzO1(Ll@+_e5H(HEkbwwB z;M6?C-v0O>fA~NDlRcfwa2ydVY}}&PoExVby&6Y)lQ{J%>-*vbp_x{izIq}+5zFce zw0DLH$Ih-(3bL3@e@-*xjj5KTDJUJ^I+n$#vR3r4F2bX+%TyysgbX*LZ*iqeNa09} z?1RW@SC*M}aFg)?U;0TxQWD%h!3=cf4pK1E=5aWutV3>ah5^c zZNfKWFQB~$VCAYo%0Kr%S$;3rDDu+X4u(r>j6dG8-ZiwUyOZ_&5!v5YX^oE$QMG$Z zcj*`%oe7^a1K+RFrDe;0LK3pYVQKMCO@@~PkNg=o$v8sEyES^3c{|TStNvP~9SboP z%ohE?FvAKxzc#V~x0RBhwGM_b#vlW_^G}9@>!X=c*vKsFeDp@z*4G%qqq-`Llw^yo zgT$48kd3*(w4y_9bm5SpB9z~Vgw2pnrI1(I%Is}-;9k~u`lctmMK$JA>~VR z9z95pd^bz8$7teaq~cc-y-p0nr4b|*Sd;P1&I9*T9`rD0e9LFr)!;E8R^R_Cdy$@f zVYE}M!?)7PM82bd=hs6}`cWMww%RX)A)fro>K=aHIGH~JA;(QyB>$zEXMP1-e|u47 z%nad|_hEu;SsOFM`O%QD_ei5c7VgBsxo+_LO=R52^ZlK;;mAib=%riLQ^_O$L0X(< z8<<6ADKnG%&mpy*4NJ~Tj@ihX#*EBi|Ed{$}>Y?7GPxfTw3X8v1do_=vHuD7(SNAlr`_g__ODkEWUVQK!BfZ2Cx5$s|=3=fJ; z6z00XcbY~7!A`oMnN`c^uJe@S=RO+iRUGDrQa)n;rP>4XnW9qCiA-mG!a`47`0awMC z_R(%8bcGk-rL~FMpO4jGX^^Ym1@sL_-ElJ6>Ll!4+f=7{$EnmacwLEc%g?7+evmMu zaCU)kfrc45*Zsv>>7PoR5-O2-m{e2Bz%O9#ePp8*lc7Vk2~s)dEu8_HlTRjneP8V{ zR~$6P&tYP|#BWh9a=&1ZHWt|rE55DxKHfo!ulheN*xL7TP4@0pTFbN z(AZjGjg*M|H2w^}7LK2$oiU0rlxl{otr?$g&g9rmd@Pl(c1F_egy&aDSIR?B9%cmd zqum&#&t~ItHp+Ayf5l7b$!>np_I%y%h+}QKqe<~b3{$75hDrOg4J%Za6ymduOp4g% z(;pzqr=`8fi~$-<&(X%j$MSBjBa40LD`LvW}lkyXj9BvRQAt-WJyj zw04zXKq(GCyK2(8%we>Ob*1>SG2uv{XZ3-wx?gG9+;=}7m!Td%Hq%p@Z*=akp{Gbm z9k$+YYp1Zg@ag`cB4epJP=GZW@FUOcEsc;CCjG@6-K=J_m$*l1!}IFkdcShiM(K{r zJE7-bvcCvRFSTreVAlQtaJK-LKxn^Np2P)K7$+l;X*@%F=Y;AH@9352U5OvfLT5>Dwl1=@Ltzmn>< zP6Mf>e&+icf`6zLkcpe$SMPtiESJbeb$95Q>N9tlhUkqr%zzoh6u}L5$Yo>uLITh7 z|KoH!NX_THI>YKetL=%R6`mGvk+))xGo@X^TOg%%B%l{90n?ACZnQ^)ye<`bbS53d@Ld)=8+Z_p1y3R$E41X(n?@_@AFalryV8H?y5_O zr8-cjb;r7`H#UdCDh+gpD9^-JX>+Lz7(q8x87q)FT-D2+vdY%^C~vhw3ItKLIH~+e zpI?kWBSv*GG~2dw@$S@W6m;P+;Yp)45$3ExqxnGZ5x+DVNlWnk+Qq6R$0x*UC|b-f z`Yd?FyZfVk;`nw71y-xZ5f*pUZE<8ETIw~Sd2qo#5KpO}f^&@XP-8IMu6EwmGu^xb z+C^;FnBG6DTsn@hH#cP5>CWE29Z!EIV9#n7TZG^Cj*!??Q!!PKRDGh$PR`A(hvs*} z5_ad8`~I4N_dD;zjVq9Myr(_x%)ool_27bA>5Um4L3b64z&TeW?0yfJ%+%7{&AyX! z5*&8NJ8;3$>`|Nm){~K5tiW{6d+ztnZ#WMP;DPhKn?=0W^wqCF8uw3y4D$0^k%{~F zaej1v2+1Oi=bA|8O~l0c!Ts&dpWGd_f1bb3v(OCoaDTv0RR}mDOdz5$Pd?C)XDk3!M zxec4AzV&4LNCMVlk5T92-Qo*6U#b!gRlG|c-8(=3D-j>B@uB$YjO_TzOH;q8{=c#G zVbQeC41?YGS2yJSG}zz#*I&BRalQNeEtC~9Il?8U{g#9+j{BpqS61hvMX61v)<64v z7m5RRm$Fi1j}e1fBFzFWL~-*TS_pt@Hz6NC&sLrm{9--7n@$Vq;*%PgR)K}8b#g?=ILP1N;UXdV;UYGQ^2wR%|}*8PI{$4t*3x_^CfG;EFtY)bky!Gt5A}A&J@kW~CEDtYOVBTM>zfH7BYC zW_F6H)X1N$Y1K+bByrZP$2&yK&00UguQu|6Vt$ftZ2K5c#d-&$dVdofgPR$1}I!P^EBbU>k3ATpgs z_9ng5jt{3x3#Fpk0%iX8Zrqa`t+@^3r&at|;wi{F6BXZ1af2IwY-X>@GTZ!5N`~^< zkSCEC(V9U?=?ul8b+^HJ8Z~!JgMl@|*Z%3zK)6VHOYO3LI*LOa3h?ko$Z*;}dd-6Q znHxOa+{vi%V=%XK@DKdQ|4a+dp!^MCG_7y(dvE||xbJ!~^oxN3`49X(f3!NV z=J0O(WbZDDE2Nn_jA&{-f;XdOtq+k_a6$_#Z(n+I0>7gP_r3p15o|dmxKxT-fIxGS zngIq`n@;y?M_rsE5x|{Dvq*&5T|T}5GM^+6P@Ou{Zc!FeESnoZN^v&4mHMLY*6did zs+|YSYJGaX)k-0~{^K9o`HlP?d1px8GE({HXn$JiWckP&gu7v@_z}mHg~MX?1EZL@ z_9r1LijM@C-v+hV@t5DnYkW-nxGO(n)J#L(k%jT}6Oseu_^JS_l@_5nj=Psj zms!Q9|7&4=+{|+FPq~|g@rEm1Dy(hr9NpuoxjTe~M8OdA%WGNjk z4`g+(97YNh(Xu>Yyw$I$+F*XY@#c{)^@-84*MI}R%&uY?)7qLu>MQMmbA?@*CnDjG zz?J@%c%+>t55OyN-Fe-(o_TIu{;)m~7CuE?zjb~oC1)$l)N6j;xb4e(TxnO}B(A`f z=ZC<7KhnOpLRwJ_`3?4fKhiF@N8*@W8AZ8$;9PcnkMo_K@7;fZU4Gt-`4fH~etqZX zdz|mgA5_@~uD3s8*DuW9QqvRnH=OUz`N7V2c7A~WK&+bAX#cYFou41mj$L1B|J(_8 z_}d>jk6#9V^Re>>Hmn>9Znw=l=`q8dO`UB_tv@7#2{;B!* zsQiC`zQK-rlzBU02?tmE-?jfMe@YONBN64dW2pRTwY%zkq)(O`q_R3iA?NngI_Ufs zJ>cm2w&LsjHJ!iNACouay-j(DMGD2|RYLc_72i5O^RrX9b*g364=JK=<8ae{CO%et zOnlb2V}aNjtP|hzXX2;hXX0bUw^ct^eky)>^-;KP^}M{5HO=`Ay1!h=3Tz*Btxv_{3nZ z=dbnQI@r$JWp&Z_%A}Y<DRFHGEMAGgvxM*)g&gDutF*!3y&^Sfz% zSU-Oo-`4XhzJC)Ryqws8+)90{*I(andBbyv@>zUoYD@4un>4xN^Zoj)e^+<%$f@|& zs)eo6T;EPJx{_KIdcRx0%=i7vTD0C;P1UBpwHDK3e`CilSi0rX^3(dm{H*w^x4`>X zyPRX(Z&%&8)~C_>s_|6c2H;wsXw6N{%LRETC`aM5(D5U@>*rW*5>fZx>Cf1-Nj`ag zU<})h=NC2LIi|mwr;VCNwG&GO)Je|KW0eDlG>LV1_9Q%Hzqe#k1ZfBgI23Ix8qG#; z3!aF)qw;2@HhajMh7`tu&I!mwl$zSn1sj)8 zGA;AYWiBDeoYdd>r6l)uw4jc@BWchMJ&2O>l$gpgplIijx(e5q*knY;FYIW|!|yiv z5m%r0+mL4^>NjPMF>V!5nfE|SV(q%jLd=ytHNj>O&b^-D&M^zDHdsB#^8Vn5g#E5( ze_BM~jG`g>sMr2!+`UC8Gwl7}sSS48pE0E~buwpK$mEXiG)c$OBh~}G6+e`jU z)q(mnemiFokxu$ui=tKj$Bc)VMjOBeIa@Iw-t4=zqasF^l(<)F&80-kAb4NAM3K!d&Vi@-IUVdP-oho@dUc#_u!&ci)}lTSRsRZBZsacuUBZ z4?~VdvPR5>?V?(WdrvF`($||J_@{465@$ zu$Ll@Pds-m{m0?yG-kf?=gayejk(*Erq(AB@Kcg2WDvJlj<#{^%>wNJ?CSXVIW1^Z{+W9{7=J#t?+ATObaQ$G3^EhP!5t(+r zL$_M}WE$|E3G)kiv1aAozT^2$Eu;kO2bbt>#^u-dsS(F8=*NYSf_kC)LH5tMGYV0T zqY1w>UU)wGOa32Pznr@}KWF=^=6GV<84l*>h(>B_e|G(2tNv-M!$2G`^@Ci@r3nzW z;@jB%F+aCo@I1e!J`3VN1G8vg%y~IqSiiA;&UV56G(Kj$`!Rk_eQszSE<1I;G)8+& zVk@7k;WzIW@((nLhnL&({l30mAqOk{slI*D)h@rzNON57ejJgliPf{%riQjBZ=d6l zRVO%X4ht4o_73}DiRAxSef>+>ru$M_tMqcT9&q9^hBM^`h0l)nv*uN zX~)kMU*6U{8j*>)--RGnb6Z(#DIu?O!A1Je&E>D%^@+xMdPQOTex$x-jZ3Cx7BK{`kx5 zsm9<_@i$>X+B7(ar;@h)OulSx|aq}BZF_4%9SF#Q=U%?(-E zuY59>`&rdS4kEo(GHZQaU!BAWX_*SqWn@Wgi(D>t2B8myRqoB!eoq8c(CV{Az$ga7-a*I5q6G*`sQ6|h zsYm3Vd@6?MF8@BwzD7n2@Y+3H(-fIRpf0(hVJynXHt%v{Y*&QACR4GUCS$TTTceAG zF|&3^tGIAmrDj8u19T6f?mxquEoW$ULUqX|jM!!znacu!Mmy_4S%2Ix2W<%gT7Q>j zeERf0ta*sqho!~VeAOM_d)Q|Ni!gUjT%Kk)?v^-Ctk&`TZ~ndis`JY~p(NQkQF?f~ zpR>P;mZQ-jpP1K@aQD&#DWxsDS+BU;Gtw+gHnmhNo60_;r;Q&CqrE9%ZYLsKCvJui zVa3?u>($K^yqyYAq*g=_PMu=rE;zBR!(C>)+Q=>(Zc4BbB#e5=CHe>{F6jUo;Zz;r z+df(vx@M={$D#z?FgM&O zGd-vGUuy%KcWYmBj~)MFptT40C7r{*+NqyatQPwSJU<&|a2xQ>9y_4dzt*Da-w(*zi-<;p9fOt(q%FTlV8?mwtqEiVMpBmAqsJ^VkI;xEtWeyq8!+kw!*0C0 z#ZU46%_k?gDhfaj0gw-H_Nf0rATIFYVkCorbJhnY^ zr~Vn8K@%pr>)VBmq7IaSrU_O5jB*wNFZGXGpYLcm_-FCkaT|AyEE(Vb6^gI=*{VL* z`N;Zwl)tDvOPq+}=b#U|eohI^m}R`fkn#T4{h42^vP;Z7<{w(P=ZH|GJC6I|gr+c_ z^#c3+)nv$(Ib|W~UU#a}sjL}`H8+>})0Y5|@FME0u|l(+_%!ofqnUnwYa2YF(}W)! z{#V}zdd`iJJ<6HfQ*%h-`y!<3lF_MBLJsrS@~e+je9xhrs0q`z9-8a$#3_BH(nXRN z8x<;SK!avvBr!f-Mo+3VA4;2+Uv#G!;x?2JDb15k=icR0I*ZO~m zUldIr7&I$5#qY~r3#ys*O_s-P?)9gkV^AhvjBM)+ob&vNS)W|2h#`Ft9{|O7mX+8N z6^)7zIlKx`vfd!lvP*brd=H)TfWlEWy2WiMnltZ555#8uF{I+Y)V}y0YT{b)>F+^c zXRkj7<IFP^xbpZY3wx}87%H~t;tCfB-Nwk)Qw$*gTGpBR_WF(Mux z#~cJKv)*c{A6+x0^|-`xhPWeZtQ<$z9N3_XztfsHt@ULtpu>mwHsoDV!^=s{KgKsW zRm3OTcKjthmS{_<7zsa2as7V967{~htm|`%A8NBAR&?|(A(E6IgImE}VZbRn z_r&qzj?3H+et7eL=fD0J{`NyBHxMD;;w5#E`QqAAMkRsF+r-7A^FW<{e*XMlfB&QZ z=iiUCJ0sHWM26ks$^mo?jF71?IqitC20^#Wx_>_%ner8F^MRbbNCVt#=)dAOqHyJL zzDy+#5suM|+HNqG7N)tJ7cZlQwa{KA?dhxTRmL(nEX{;S@~E1Bnw$Xj$Z+xxT;pqC zk6iiuhn;)Kr9t+hzj=SIN@Yx6dK>9{&FuL0`TnQYYU>w}VZHEE+F0$+lJ*)uS#1A? z?@PR}*Z8e9J9NHWrh~HETJ&R#pl9W00Bir~6CDE0GDVquH|T&CCbsS0^s+vnhU4TP z3e(MMOD?@T4&LMA)|;H(aqEj!jVWNH+$EgGdnsjBb2-A-$(UKVZa0{TEt5$8#h;A!Fne3s?*?s!%Uk1&|7r^2Km{zs`HRj1sIh8D{^BaU4q} zZ%&Ooql_X2LuL43$Y$o2Egg@k7Q*)4|Fiz+Q%v{DQh!NrQUzKiO4NbY=%^KCt@0@V z0RR9=L_t(eiRRBYxZiIvrmeqAq-%Xz0igYBOru07|5z6)Hsne5m8}0lw{gwxvUXg< z;eqjc*M*LkExM8dci_&5z@2#B@l1Q(ao_&D^G-Y??!bHGecLngnRtKV2Hx*{roG=8 znGqIYcOnuI<=5tESLMDKS=|9g7Gl3o)YexsJHkz1EM_YaP?9sn5k4h3u+R6%td^GH zN^|o=NU8F(nZv*M^(g1h5kso@c3=)afYtwU|M)Q&s9JDE-l~5bmVUk%uC|qubnnz} zePn$+zrgwdZ4KW#29NQZO=1w-Oue=WS}Vjwk4N}Fpkb&>Fi_=!8QXu z=Otl&k9{~_eCclrI$nMKIloGu<@57p6o@82OK2goMLK$}%=*Ene$KHyWBi%?w*rsN zQS%yq>VGxE;5r`_KR=02@gWgNGVbz=v`irWJ^zNcU;Nm0&Mw@^Gq$BrZwWtsU1TOE zKE7YMEzJ8ZMst2sY>mN;No8I2Roz72zvS=!@|`4(*5Odofvhc)8oK^XgLUuwg*QIf z;8}_XWGZFf%Xm6s#fReCiceHkc|@{X)BLd=pS?Z7K{|e8W7<*97Cls4T#n&-SlL8Y_rJw$E>bm|L` zQk&0s&|ErwO*)e{gSJw~mu9Q_12aqR%-iN)(|@pjel{LixiP+Hp4KKal}<@=6hI&Z}j`PqL$@S874g^X^ts?F;;hH6Z|oimx)3 zvk|9-5FJoj^OpyQ;=9(TeOSyf>Qt)6m}S&uocR1^A98(DC*;lSH0+0g?C z3?+PwF+;lXTqtG9RH@=}Z4o(;po)}5GJI6nWW}M%rw(>E!(H(mIG*R1e{#HY+O?(D zKQ%yRzpyyXMT%*+cc+lUafA6eW>VOphNA1>$x)_5n7ak(%*jL)@K*m}D}nG4p-gNH z3qmcm!&A*`HM;NM5}C0cES36C%>MV<|0zLt>rSusXJ(1GsLv{pX;S>3@AvNGvK-A- z|3y+oEHO2u3pbuL;AF#kw@HlqQ&RcH+|lfMEtPowPI`byy~T_`dE>eB4yE%)Ua=c1|P@ zH(_B?Re*W=QY|f_u=%ust+OE8Nn)$dHj3R~Q znwpb;guWr>NtrQ|`QM~+#r>};*A}bw-`GNV!-92%d#1HCa9H$DQ3ST?g zY7QKCw>)NUdAYfzgWtTnUFN6cl#s{$q?MDYJIrwzoGMz?F(-D_>jZX}K3Cnn!fX4~ z=bfYEVU2oVE74Ng)s=%nZL@yAI)9_}K47=n%3VL?Oty5-6mzz4Qrth|`(XuOZG+gA zP-=goy>*#?O5Dq`av&8C!BaKOarAzvu2~&JiWjDvTOsKbpSNtqcS3}_ez2P(5!sSB9dG7z}#z$tPMmBb;{Pd3Z z8((jHz46m~e7*7Y{r>vIPxt-xnLoYXKRxZIXMTO=*E`>zcz?z-^8K05^ay0)4&2C_ zderRNmjifY5z**)lsP_AnL!p1MONsJh3sLY4=@|b?aunLA#jl{OnujHVOpQeOdR+F z_JG6fQSOAP{8<)dl|P~LJ=Yyi`E#~)X1YJ%ZgwS#7fFW|Z(wfK4{gsNsVL0()%z0? zs;OioV?R`V5}Ai18W*Fv5t>WOk15Spe#Xkr)}FA8Kj&{AKB8khS6Ycu^>aUe>9n>9 zvGV`Q&#WawPVk3t8XFh6s;_8)kkpZEy`e#Qlop9k*NOR=9#9qEJN{PWQ2lI@yjyv8 z>NjNe^UpQ7G851D^!6O;-V5VvK$%2;08W+O8ms*2Kk>*h4;8%))yD|kSXn=x%dgYs zqrHFj`%Uouj$qi${lif}Ras6i0&38Y9?d{l)=5_h!=x|E{s=EHQb|__&Rq3#{f;0L zT{k>c(rInfUG?E7@kKPpzL~r37h;Y#s<-QBaW+j&$h6GD#+%eLdnnR|d*c$Gj<%R& z-3`JFe8u+_A3qbn4VL!$Ongjyo&ctwO!51E%0IPG-ffl%v_g|A^V*-p@1lj+U9+p; z_ocm^t#{UBNWqr8`-v!htNLm9B#b|ZE8FJiUH^}s*L)@vzUGL!UH=qc`%~>Z#UElP zi`l+^rSr?>^5z7x-`Db5?2=CV#eX;ZkOGetsgL)iEKC@BFd9;qO?kbD@F7;M~|GcT9tF5D=6yGmOkQS%Ku@<$@4y%bely zu`qTFj>2c&?$cJ9u0$$6P1POa!Y~|c65HK7ehU%2#9ye$o++mj|2$IWZQ42Uo4iSq zgC)l1HCZLJob_Dm#a{!jx10%3RXkhu7Hgd2Jp(7N8y$a<9(t~;8K6R_>!e8r8dS5i zGZo(zKVwwq8(UZL8>xs?8$b#&WOV27fI@9+#cz2bHIwZHVWIcS^zO^W!>FFf_E-Oc zuL0zOGTVN?!v!P$kjy4;x&GQHA=ITdBd>=WAxK>1F*C!dE0Wo9T<&IgT;_)V#((sW zUBC6q!N+P0O8fWY)}(&NLe~SC838!}N??Hs_wWDWKl2a%!++`PGb8PexPdz|(gG3X zYy*o(8H)`}KK0Kj`Ky_)4q~X;WId9e?@Ors!H|#8XHLe|+YDdRm(lhjXN}EN=?IE^ z*s}@&1ak(AmP<5(9%mo04`MY+pr$RnW^vhi@|W+owNz=!u=4|k^IHfkck$z}P_^=g zwgx=a84z@mN?UK=nroJ?*pL$l_x7q&e9;=E)1>dqEhGI*ZY-zB_B~vv^?KQdUQUrS zN#;i~qHzsttLGQTj1sDQqy?9WA^kdg`u<6}9Njj(xvi2dCVl#4B{k*VjLrgoxe1{)tAF1;sjU$UM$6 z&@3HZXb!U`Tc`0E%$I43^ZWA5n_gi1r=CvkU=RkFkKy3MUp?%y2aw2@_iHbpVn~O$ z->J2hWArnG09*F@{Z6^SOxyD7qqERj?}jC(&)H;K4vXBQH`oW^+(_6(rBJkCIEEiE z8hWx6m$U(w;>mxO-%6?)Yl2(9^$yl~+Mi--mv9>7%(!rfMl*IoZ6Y$$?~Lj|Mc^5E zhdmSbjb~&Ox+?Iz<4$|O^A0@kc!uARcjhzl&b;pug(?RO+=)Ap$Oy#v4H4~C^*(m~ zY@HzSvZEERm@birx*s7-y2C9`uqj=OKcj=esT2N-oHmF&zv zt>cZs!Y&0v%>AqSP492{yT5q14{o_5UcyQ^Gq$@(3SSl3UY5houBjL}8xvVd@m-X= z6<_rmTC34?OB}Dv&*JAIFkzWhweYF1itKfE@+NpE1_#|Omet>z_{z_cYyYc_ZxesP z{9fX>j*m6Dv+W+VKHXKIVoEelymcNpL7lHYms}F2s_y!B;^#|!CGSG%$ztmTTXdGl zxu^6%R(1g@5xPF#^_y^KgYKU@+QJZQGH9crzKNe5-&XwmX8q;j@N&N>aC!e0(5ZOh z>!-6GI1ouO@i$Uc^FHXd&GTZe14m1YU0O=-(MzL9p#(&_A~KcDg^es zBzi3)d93yMHh!%5jq~{sKSvMY)A+X5r!i~VpDLVp{plZ8Mg;vvH2wkc%dc>qFPkg9 zSl(F;yR@Fqma|#8$f-9^1?$vZUSD0!C%Hdc$4A{Ck0^+V6BA^fMo^cTi%4N)u2Lev zdF$Y$LS7`^NMjc7ZWcEhLSabI4A# zPB+TQ?QfgCD@0t zIZ`9sJZ||!Kso|ehRH<;rU3}`;8`?^Ypwmm%BJ71JO|7?ZobVDek3l&b@sGxGF-H1 zt-sg%^+H>Si6-|0OMYD?LroM0RYYDUkl;X2M+q|y99C^-GnZux(ZWdy5I29`$oi&Q zL_ydzNCHP~`EEzpdj5)UMYfAhw9dI-#(ZB8E?q-+hwL(7n;yjPSG&cI(+Rs(X?p$d zfh?*T0MeY2S@5K5!A!DBS|~Sjt5Nd_WG#%`K^WZB~T~)(TZPp zDoQmV%slQA;-)aEL;`w$N+f{_L9KsGee1&6d9(MWzFA@&Zb$Kb;seqzEcaN(FN{e@ zh^U+job&&p(OPhjQQApSOmcW*3Dp307oi`W@gne?F#qG5{R0#9 zN7zQ9%U3hC_rK;7xt>2=>(9nT?&s(EC6DOx?h*ZOy47x0>mv!cwxqRE;)A;v2f%ys zIZ#*l;Y5pAJ^{Y{7-5sZb>L2?m}7Q4&%v?s8N@m6W|#TH^YU{}{R%GkV|ICIc)J-c zM`_-<;T$s}xrqO=Gah_={?aMuY1@-?EETrKw%BlrmF;=H2+f*pGQxpC7$l*`y~cu%JFFN;>*DQVtGRF}ydlI!1u$>Misp2D)y&uDms zg?2g818R~4cbW}rT2K|?YO)PnmH)|9BK}(NLZ~~R<>Mxtzen}lBavlrVs}~q&&*8R zk#Lrmq zW5v&jZ!3Pj#@A{%bN<9{wY$>MI>a5{U;nPZ^>*HRtRZ3J_$+gE2i_ijT+hGqXXPa! zzSWWUuFv5n6X?p%*7q0XA3?UuLk$fFSmebRtdKZRQtfH4v23$Rv{J1kG0`l0>4&EG zb&3ig!cwIOjqS(X?5>_XQ3*(X)6~gw@A&el<@=4LtgKN;YCW5ut> zRDR~!hnMq(@$J=+tDj%z)W_%d_57YTF1nD&D3NsfMy9{4`kZ=s;zPI($=2HGyRMEu z8((N0?-}2>@x`&6_;umr`%U+euMyIx$z#NqEtBjjlf7%prla*5=EAjQ!EyxVYLM*2 zN5>z-2xVjXgfhO*sI69Brj_F~U4V9<=jZL;C26b6m3)wWSaR--_7ih(t-sCsS5si_ zkIPJ_8C0Vuh{*#&x$5~lze)e`+xWycc`gJxcl-8pm+Hk3kY4V;OrljUm50oS_}B>d z6Cr9+YVQL`q>qNh8@5_?_xM|h17i8d*Ilb>KOnjjqTP(SQNt82yYu2W)6 zF520>{BZMCD!oa_kgBCR^cp()ix-s`F;M2S#d{yFsrB7rW{DVfRliMv$L3dhTyFXK z`&Inx_`WgEoS<^bl+xXAA2E%7melR&+N4;DK4Rn6(`*X6hmD$5&u?2rSnJ=wfN$P! z@1KD`sd74A^CgqCS-XbC&};ctF_YFk!`WImsS1VpL?3rjAh4~Dqv}`Z&laOm|q%p9IovAQ2RgTa_r+E z@q+jOmd>11WAkOK`?`PFyshsmdwJb2xo8bWdZ7&Wb{f21kFpa7EmBX2DXiQHBl zjFTyT0U;vl{s>y8{l6wl{$b*zj?zo~^E2`J{QU09D@yh82B&c%3RP@LqsJEM727w3 z@Zaoy0bJWfNSwl4gnpekR6uLhJZs$jluzY^I#^y)}I_F*~LEV@t;n?M2*gGUX4@v&7Dy zYyZfDX`Lg&;D~?J7p#SNCDD`m6D2!HgkZU|lN)FxAFG1!!gEFXXYp-OS**R))>K_u zB+~Kef*msr76!ySr!_e_WgKDJX)$<@!B(UpqkMLdun0ujof$zi)-y8F?}!Y$BO)#C zcn0pwJMDRABnof!9+9v+GLd&6VRu@>?g*safiwgx5`m>lSQD?gq|E*qOS6>f?Wl#; zRH+dYv|4EA;$^bKEZV1b_sd@M59GR~T=?)?pa`Iqm*u?;Dk*8NuwIm-zZq zXoZTF35weDGB@b!EA*zK}1%rXv0zYxh2V+9|!OoF zr-t&Ut@yG@nEd$~-@l2E3k#u{evYRS{sn$*uijT6tHoEfUloJE5DQ^AtXb?cQVA9lK5UN?7HN9V- znvaYX&9V+%720$G*+Tv-?JP$)8wO8#lv24ca@?jo&Zinqd5ahCd8*TOaHZ71R z#Nwm@M_6k=W}fQClC7CzI&vB!Q-v5Qtm6x&bHuYTK_Zh^&g5-*t&gYVmoSXW+W2~Y z8#6J!zXPh1wtYqYy~Xc#^BZ|+u4TC&l$aRhnQZ`+%AYbX zY!syY#o^KVbQmopb$MRn$4h*-iH{v$2Ylp*_?Y+wQ)c{J@sn_(f>=%bO~pxh3cpM~ zKE}tyr!`z;*AE%*$lL6)Lx~W#DfF`yJdzsQZ`a}7(mW{=J?HPL|C>cy=+~vwpY7Oy z$S~*mayJN}QVexGTc!AZbyLOG18JXPm!<^civ{YX@-ePT@a4bZ@DNJZ+PJ9i#uJ}T zCf(_Yt>VG>-C-Rss`A$ot$!}Cqh~^Ive6rAcZ2co99J<28$46eZ{oi^|BCNe?pHY~ zGv)0#pq;fo;jO0m(LF$2mbc{nZR(w^|6TkeXvEq|khQ=xL3X2ure=dm6$1YZy3MyRr#V)DpGyEtmn7={9Qj( z&42#b-|!c#$GX`7*_jza2#$9BhPFw3v})x*4qeqQTaW3y#N3vvtmtme#E;zmUrXGh zlckXiG~~G)Ag6D-124I@vIXlx#IuMw#d&+}A9irk z7PPqmd5xd2o=xRY-y{3XqfdvhRtWuBStl)YxX=DKserC;M0P4_r}I8yaNeGE)2c`3 zpC#ydyo=h)!1T>5S-dSI(waNoYCbRd2dCRF3(zxlMjsbTIbQqIefB?``(G)KM~a^h z$f?es^L_3319_nVr&6cW%dBrx^=j>O&7?*Iy#s3Y!u{VlL?dCh=Ap2;SV&2@lzA~& z1}x{*893S6KZ9xQS}0oA5@NY(rXFnNRZbB6^rzpE$1u2U;el8+e9{6PFv!L(hDQiTpXPL_nB7;SzmkJ)iMmdB5Kx|>@b zw_~NzPR8PK7@`=0EmQ7T8(PXr30qF^bLf}S3VWs!%Kt{iOfQ7Q8Dxy;UDsIpm4+KG zFGBfL(gbpvM19lmXvMANZY8tUj zj!YdWYj-QfH%reJCvDM9jbN8D30^Qe*mHobHiFJSvG9i0h(eQoM4ey!8zPaBfnq#C zXspa2;4U&FEenl>yp3qAJ2C@zg8wxvGP0tX-I;_mrbT90DJ0?!B6G*=W$+P`KlS{3 z|JcZ3DL=H9P{K3PjwwIC)+gSIFt6H7I$tn-AEF|5exQz<>Z=#=ke7M=%KvK$&$DUN zVd{L*pN}P`9sfE>)@VyzBT>4@`2_2J>!XGhAzJvh@2r{#o%~&zFBW@t4XW_0G?7!nMgiyxU;K zmm=z}55?AB@#B58-0%C}`nULB^QSIg=v6juxB&cm6lC^gf3AjW@Mo&{Gp%*ZBD}@n>!ti*?wBayIds0?_ea3gU0$XZn7> zKNH_pd>cRI{uU)mp}U?pl1=fc9vI)CxBB^BpTEzRg7?QVgiiGH1G-a zW35lCH+~a;t;_5Ct;1FK?4Vqva>a_zQLy^abLn`|@j^4TF!+Gp|GB6uFY6;+#F+^0 z__pGk@bB$TnZDxN&&F?Y!I%8jE>~;)X%5|Igijvr{P5D>fbPH9!-rPvTKQpPYK#u8 zgQk<-J1_d>?oC2`uJXTBQG7TWG)d_ChHp3ezZM>coQr5mrZ|=xPKd12>qWn?#7sbE zN0pX$W8`=swq`uVc71vig6zSNIEdKYt138M4mjcm+}-3=d}~>KZ~k+8g;=l8*?Fx? zD96%!t4oG3zy)-pY%ithwf-B;N|WsZ0HiG}a2EcctWRK*6-NnLC}9kz24+X&C$7=`Fa70Te~o8k6msZBBgO9IEmDbGz97PW&w<=> zA~r?x{=P*$Z^Ew`WxvHDHQ`Tx-AzoB1M-i z>$6y=q4gz8h*3I5M0=D-cFZJ>%}cV61@C~ESv^bNv4JMFK2jQn31lVJymo$V+XeSx zgiSiAal^5Qk9v)~Ek}>BD!=LXX#ZpOKT`XD>_h7No&DeSy^5>|>+kp5=dW70=D;<5LLM9rv-mfA3P!LH$TfWH(_{Z}vh(2u`-&GQJJY6C#{|d7J~Pilo}jyf!gR-vn;$deMM*8VLmenL*g+{_*>N0? z%gx=6=XLCOz8!xvyj}h{cpSTqKQ4Q_?CrAa;O(5pvB%|)gSTT3$2qth$Nd=2DPAFY zh$4bWXhjvAg^sBlwUH6Q(Dz$T@*&4Rn?2mFI@#xYh(RNhR~t8RoAoc1szMuk+UU~q ze6$?7$9aDC7E``PL9bkjjJcUrhcVsU-Tb&CF0+HI-Rfos^}h1J#MZ=!UIiMLV<+# zW+%<0vbCjFCffF;YW>|1H8X@C=98Tt5}sxeYw8xe=9H_R%kkPKKaU51O?@@z`_!L< zRcTT0oqy2v6-}**d-YP^R(^{~nSFahl@KI^(0!>!VrG^F^!k3oR+b^QClIQ}ut>SE~cl^_6N@&2-@_ ze*V?P&lNvc{9N&K;&WHr7@hK%`$fv7apK22{4&1^3up&__D@|^9?3KpRM(cT~5ZriXk}e|G%zkIm~O%pk?ip1vu4D3Tuo*TmYcMQjWb>AmBd%EU%hbo||y zwfq=gr1KRYU4hm|khbD;3ZBeXAL4uV9SOdkAG~vYj9>ZRbzy61G~T~#;xoAJ!Ef3a|AQFV!KN__3eAPhn&Iwx1v0#7~>}mRf&oG04~T7Y(uE zbNy|tziGP0g3i|+n<{L%;-mLZ4xR+9S=<|q9S-zGlu^U&}Z zKYYb+CqAuztD#Gz%CZ&1iR!Q^R!RUJLE^rXy5qACDqk`iVLi>g1~Fi!`h@DddKW77 zwEpY)>rx>B64@`e(E{TOp%WdyS`ovRdwqTzBP44kHJxK!2qDE6T3noZs#lY7}mL=vx+l_B$%8VL(A^8`Ha-3gZ~4Qq^2foUcA_D!IO zd1T|6k$H=mU=1TRGsYk2qO~Xrv9;xNJB$R(?oXE_hs{a zm70~L8gbKTq2dP}=wX@O5WbSL&zU+{P#4GmyRqV9;MrKhpm3**~U6IP+W0+>Cd?+W)rWFGOy2#|YJxFX!6-(hw{l>#Z;JllZ;y zOJPAD1E0TUxjBwGe17rO`TR-#m`l`y2&t`u1{xxA?*-lEnBRK;h`oho+1C5bo&PHz zMlJ&)c7!#yWPUI`zt{SkTjd{4=c~_k+g1`x6-|*&nQ>utH9&E9SRVRYl@N3xKFpD_ zog70QmtKGaZs~{IH@B>ImDN8O&aunv&Fwn*<+982arx!;<@n>++hvb~FOKWfui$cf zcwTPj;BvoO2HxE=uY!HGkE)GIQ6jv0#dR@CRS$iBHL<48_mNx91owYy+W{U84@J9Y za{ZB9|57~&>Fi`y{;vdjqgLCL>++5| zeKP`g#2xaG4MddMI-Yk%X8ou4j2n1HJ_DtA_#SZwo)J&r8F}A$f0hzrX5=#viD$%} z78%_d3m!QLl#Y8_+8}}G{M@?i8HL_8slU(p3yVJA8yNph{%pHfo$r}_z8Eqil>Y}h zxbt(LFP#0lgh}}PZNHJAr|qZb%RE069}|DnigdPoo8Q_{SzUzL|1tGXK_aVuX8lw7 zlN7nGe^_7Ht2byY4dc$wEB|lCH&i{&`Q`omSsoYI`F}h1TGv-8$KmyU#W!I-=lNv= z8cLY0__^ZeiqHCL;`_vx(j_u)V8yQ+s9D@OjU&OnWd;0F|A&5M)&F%qR=&D5`hDa1 z=tS`SnLl|omSVhd4eox9AE((9t69}ot|DWtzvfj-Qn$6Pd~gaW)jgfyj1SB4EAVLR z?0|SPVU7EcmJ0>n$J?EXxR!2Y2o}jaxc|rKmjthgj}<>xd~u@cO<5ATZKV%`67Q%! zROr%Gc0?}yKi-de0?+ugbLj6;#XBiL#SpBIUh%Qw!zO;_#J3&aK98R(emy@{{CJIT ztUski#Y`{`%Th|URy#GYXGkAM37 zQ(sxDtF2mz6atJ|@24hb%@rzG31^(#nEexr(0-?*C!XlY`0gH%PD-U)Ejm zc^`ox&^MZLp(Wcmf}V0V3RWH8=6or&le)~k#Fz3wW9Q6*UEOG%eW^BP@fFoSE|#yn zJFT(h2$J`CBUqB@Y$l#^17xRWnF7+>j1wdQOUO5*d}=FXarB-dr;W|`Y47c3JC45t z+i~K;vM2wrb{lr5mHie;7P)ONh6!{sS z?EQYfQSVn;Q5`>rPb9bX)&oij*fuMq&<`ndAZnuszn{x#Na}YZ6)ty=lqvJ{ECfQ^ z?{{1m-Dx~2sq|85jW$kG&VsHfm2xepKPDNytRdcsu&DckMEX?kI@h?4}8G*R{c~{jQDXvtsuC35q(w>5D=Q(R7z|Q`@5Ph3JWIo#{ zFQ~;OeMWj>`cipMh;~cBV)gX{s~27I$p^~v8(a*6Y$@SkPxLK%8o~SdOC(FSCDrse z*?2%|ruO=`@JJQB-YPyhK&5E9FQJ*_>&^a@XkWDN81gnyeXqsgZ4i{XVBIyL3e)+G zrhqet9_~+dLd_UoW>&NRHQE~a%}?TQ%Ac+H<7jS)n>R7SQ6^;Zi9+2aY^N$%MP(csE;BC|$iiU}m_e+7 zd)8Za>NS)xP8l4w*>Icjq4QfdFC_RaFn^{Y%YTp;@~CWx(&q z0Nw*pxUCy^;7;6;cT`u)o;NZJpOtrJSl*ct_8u0AxHH2eBf{>4^xE0$sr*^eUU&8W zt($OFt)DChUU}2gN0puWQc7Ze$YzwZX-dxf4GA+ZY-52iJ3r6>P?Coj*WE>EFAMdY z{6IjGE~+^U2Qx7L*v>!Ic@O0u|9JkkafmUitM**^Erj(ta;2m;qMHFXE4rzF0y53Y zT8Wkoa6~B|slKxIn10RAXz@tgg}*?@FISMBU-t87>Wu6B|4n{o{v4-sP9_>UJn>%+MpRX2Zd&^&-ogxRR~CQUwxyGznFkYuh;e@e=; zyfKylk&P06N`on1D|zEBPk{3LCce;s?U|!ql1bluVWW0@83^}%!5UpcP=E6{R(#ga zOJl`G=uEf$6+c(}T=8wiuaDk;bNn%n=fHjxfLWi*cgPUy`L(y(nSInnW>#x0cSn3B z>yw%%>3nIQ_tJLMPeKHZ_fx@I%Xp*#xL1jwel=$%`yK zdJm|Zav7d${Wa&FKW)0We~jO&(@=sGAN@o8wf?0Qg;59u}N)RDAW+7(Z70@jA*J*BTBHitlPD%MY-BCqGb{#!=ea^znpzY57^A@e!tk>3gb!FgoNlR zmSmH{>`37+3`ZscHVVrm3#JgFpkN?rIxK3;%q=KqV{q92>HqQd-~Mm>lhR@+Sf&Ho z|Jd_Cp%G^y*37NEfBMz``XB%2zuvg>ekQ*14ub@Ww|Hx&Dy>_LTSm6^0ucryI?=*ua*1= zNZWFryyExo8GY83A8a&MOv&0w>W-n?c2Aiq9Cc4SoP82?o$cd|q3U|$7PNI+F-+g@ zxUG#EOIOcp|4UqM@+UnU7Wlb~I5R6hcntTX-M-v*N855U|KV-iVs6XWFxm{*{>~ZpPyAB@5&asEtaeG{T z86KCr=Q*W{cHaP|-P~}o;Xd=a|8hLN_~y*{KBiH9 z2~mC!`!(B1hxNnZkX-=(eg4ipKpjCzY$LgQWU9FU(*F}Z>2uz&Oe0)S+ah@T*>?KatDpeX<0`H@pbn(XExv zt;655AWE8*c`QlrA{$oIi8@*EJ0pQxRZytq8%0YD&SMkairv}T#%VE0HCl>u8~~{m zV)9XmcEuijj$NPFtH~dbD?j9NuY6Gw+4YslD0tOZ%}U=FF2k5sQ&p^=f4Ow!IKg{< zIDeZ$+4Tc#E${6FU134*xypmjkY7!L-W@#xutom@`8;#O-q@yFsz zEyrJS%HTR9(^AhrO$0thNR{W`y zt+hy&+lIai4F6#&`s^XX@Bp)U*IkQ!h_43ZG8CAS?$h{Kwd=%3OES;;*w3$z)BG#d zM;_)7CT}D00!#T2A8UO&zOVSrboq4s@mc&oaQ%Hd5auWAlS@!5=jsJDsS{HlV)p-9 z|5YEp#xMTd^jTNlYk4r@rEGHocYGhgd>fwoLUwD8xN5F(D{Utd-3l)CJj(gP%O9!A z#vC)uR$fdI*ji@`#K=q{eo-kiLqus^-SoC-rJkJ2NtySM4-`a^c?)97$xe15mN{}v z5k@6^HPR$vW-<07++y2$-MDyS>2l zdqyNQvoH0iSKDHjg`xA+rzr`I@d6LnIdUBePoD0brZ{mw!r+0Zau&X-2E^7?tl4z^iO_$+8uGndEU{wDtFlp zmexsIut;b!!c$^-qHhOn2n&5>OB2gi}jKVqyquh<;r=ZF0h)MAGe zb6pbFGa%OfO!E_Yo4UiTR4T)CsnW9izCkBG%Q19O4Jv2MHp;8`ig9VigTnQ>sw9Fz`y}_Ps9=4 zp$8)^(O?NX1XOqyA`Iu4!7hgxj=|lI+duT5{j=wne`;K`2^6!88()7KtC2P#OiM%j z{_nx_Kl2a$zXDk-fg8Bo-y@fco|>sRbz;`MG>C?{8^z70hF%d5W4Qn$B5VJTU~BFF zu7HT)Z?{%=&$xv*B=&^g*N{>~GOm{&=<|K(YiJYGVyT!PAmOK6U&S0WNg*A6aF)PX zf(32}9Qh{@@dR4V&`Cu#^(ACAicV$t1SlTx_HC@URw%EN4kVH6n_&r@2>JnKXpPq9XrLo`vkHdo02u zTU~_Ro<`cT8|NNzvq!nkcbX_vDxvP)xAl)Uu{Vj&7rk80+!>=^Cr;eX4>(Cj+Eo2E zgNCsesA_KKpG?*dx@+hhQVm%SHT z#mX?bJ%UsLL|*ZA=>@n7r16@R(v-ygTi7!)9Vg9oXBvyRnXjZTISkCFRe#qALtx4lceBPGP z5=p8JmTgM0$bC2pRIRS~KCC9npjUjKHp0YjEKJ+7gj7m?Q+)MVlGbU_Yd_C#^ZeA< zQC|XjeqL$hR7EGD_U4hXxvlsDRSSgu3J*zZQcdHtx?jGWsoN?_Ur^X)UAUb4f@0wF zab&YB7aDE|gj8W4Fj>*Qbib3S9H);db3Q`SEB~+fX5u&xLD5GNyTokj{Z9N^ZKaK6 zJ7f}hDGgR>LibmbeN}6aU(j`z{*`OBJO0!sukoe1h?KP+`tHzedG=@Gc9!6?Th{Yu zyPEX<>ovZu_@_U6nxcJ-pRe)Tb)?ZGJH9D?m-T17gR&jO$+hz39Rpclhv-U34YKKZ z3K`rWYYte#JE#5W$+?T}KBnm4Re7i)>G*~-4ndH4QlgyI1f1OdujEA7GQ+X1PF8%Z z_(F(i%vSN+4qo<6Y8^`HS;ATbX=zDv)~A(;MQW-mY}Uan5V^Mq53zq?P4Y#Cda{AF z{;BDX6g_wiX(6p9kLEOJtZ$b3ebQe^^BT;hz<&x+FM&5W-25qiHyl#14;JC!B4kfKnm z{!-FM7JVYOg9s%`Z1$(w?9ZWnr;Md!zcYAc`2aP^9Xb2I2;>w&>hDW1k(87eq=2&|6Jf3BBWCwi{@zYN2uL7Y^6JuvBa zR@Lf7-gTVBk;Jo=tPSW`4{K=a?gk-FCV8vCDB8?CpE& zS*{P3+05N)^73%%{1)7w;?BfIH+L=D>%Yp;kQ26-yp1+mWq?SXFC-Z+IW8tyFYmpA zwUp{_31+1m9C6Id!ix!LIKbsF*TJ#8%r3`u{Ib%{kITy8(cEz?PntVCi&seI zg&T~jkzAlw3Y2|zntmz?0ig3+9nwLQJkxzJmue4dm;hr0n4j;&%^V*0jfmm{TIKDC zh{PR;uxDll?idG21r+#S0J?9xKKYtS+Sp1-G@_(mAQ~mZ${>NxA#H;6Z_Kg#*lapi9 zL()INZJDz_KdXO?YgCKkVZPFknj=(u(He>uM zm&GlZ{G3pJw%7c;^1qxe_1b*Luk#CYzUY$Jhc3?-%a8ei>4foP;*TeHbp0Rl|H{6E z_Vat>YyBn`{Cqo%ZxdhVW9oclYeaG>Qy;=3?{~i6gTMJ-eewMgL+R%_lwt4|pG@2P zKZSr${ZO6p)H7try`lYEd5cTw{8BKe6aeM9rF%tpoU;8mH-?T~U@A)xc^yKlMF}o#FHNoKK|$_7p!?{MI$@=T~cgJ%9TN+o*AL{anJ8vNX{8 z)Obm{%-agF^Dnr?C%&!tw&Ig0YyEy{sTj(}D&1n=kaFUy9I>i<`}wis=Za6v4Rz~e ze9mgWFM--W7zTzEihm}4uK4wSzlonZA2SiD<6~OC$&62(+ze2dj`{-S#SZlSCHs?Z zylx6e-SJmd83N~5;OzLsH(6VnRs2vfN`jo?_xYI1d;RnHHGxokNRKkSS};93SA3F% z=KT63KF^nz_&nKNTv>7)%fSRjj%6A1Xm)SxIj5=(f`Vid)qZ;gUa?YK@|M*b2bt2~ z5QfOb8DuT44=|fbj@W50^++Y0-52_GuKB-1Iry z#4ja(z29<>frUhDUFL}Ovt-4$_DdFLWWC>%{e@-%;xraJiSd_4mEP|MtR|b@sn;Vf z=J8oenM(HWu#7xL4Hx#vd;dr=x+|fE{L3JMBqegkoLJ^wL8AjsO_+&`=36Zk1`yt& zAL*Xd^CccWJnPnTrv?0(xWnF`syxoo~^J{4N(j2k%r>;K3<^-Mf7pLafi zJFBT#o?T^_S$E6eOLL)@MWQnIQIBj3<-SbUk4YMtFDH1Hw}b{{E|)j$&(>}u0*~)Y z`?KAbin7kjjXXDH9T@VqFS&4Up8qEDR+S`=F2SKcUm$I<>&L;ZM``VVI;>DG9J)c)lD7c)27M^02vqyM@cYB4q| z=alx`#P0y=V{fi*y_gsuBuso1a7aZ2u>i;Q{RTBqnn%q26D$8y?ve##0kFIk1+@N@ zhvwySBW0`GHz{E`1H6w~A%f`56Y-@yd0jT|$73S(w?e z@Jhc2v=1~p=3f5`J8pKYn0}k;kMg?x+sS`bZ(hc!1KU|K>+<{YFVm-gP6f~5|(~R@I`i{D?kKT0keXZ}eRK8L^ zu&JSQ14aua8Z9yCt$o8ma&5}fbn3~UP}k1}TUx&axFnHGc8O)`sP|QEK{GpU7m8(x z$2kwf+wsfs<=AC-D{iLQ@q8S+443FHsTJYP=nQd~EX$+-8v}g)9r?ZHUATD|qi6 zi1bKA;t2~w+|_JTinnI1l<-$EPx-&$g-S!0FVP5hnlv_Wg00bVP)#e)2yF60f4`$< zr6!iG`f5qItonyJojU(S$u!$;kRDhf6+(w1vt?>-uW)^YWQD8T&2_! z+54FInD|RuORMO4H@3wbH3KBo>mB)ftzm=Bky z@IEU(R{UJ?<(V#rXue-FWZepihzW?pvJV@IE~=*_%XFQiShPr{Hha+e$!bz?|0URtk+t9UK%Do zekOjxC-ckoPf`xn8q*)%FNw|WouBLZxlFd%ExoZ`;$y|f@CoX|8q5#w28rCLnw6h& zs6om}#>zic0xwqM%Y zccYKEfx0cng}EzQJ5t~A{Fi?BfAxR(7rs98{fWqY-ncXG$OyX;ciJ8F+G`1w8vWm? zIfj$p!95n%47I#L^fCklocV9_<8|NK8jnlegsEs5ZBKs4=|ZAS>RXP)TGla_U2x8) zF9HW*+2g+CpP@&ZfSv=tQ+vj?+{$bIX$y_fsR?tZnnsdgQH@+{CK^5aMxSeIz42QJ zX#bnNh~U+EAjDsB7WC`m{??G(+&W_~`umKZwj9~3iLv6_ir<@k=N~lmVqkNbt)ZAN z+<1Op(mGl2{c5Ky`qAtAwf@F(eZRuB_4z}8T={-v<)0R>8~g7bSz!Ku74Wo$>>t;K zo3Nt9SXD6YZW-?GNH1|Khl@Y#F!L~XGo1Rg-3&N|400137Iw_560-E_iO_k_Pv$v( z4432K=il{j`4jMC?Pwvt7>JkixkmfW=9uAz&aY~xWm`6TRW;_d zwr+OZ-11P#zLeVGKy|f_>wnX(Qt&F?~=6@6_h3O(o5>K!`mN2G2C>_> zu3$;~P5gbnjENOqe@z;Ma=XJO|`<$XTp&zz6HF}|zKh0J{ZoBt;Jvw!+aMqJl*%sak>7+0w?c@ZyO zRT7ZLXk~7+)UJQ#P~ZE%uLB{HbuKx#fBJWR8l8~bT5y_4k1U(CK$hmO*0DOt?P4 z>QF<9M``9~;&c7&CBCirHtUn?n3MtrY<(ZPUoW;G*QXny5HwKtuX#a=k7WejJXhVh z$Bf{U_#+y(?{^DV{qyl{#di!F@#FgU6{6~gocP%BvxTX)e%ZRee-pn^E+cD1&B|53 z>3|u9-|YIU&*rs0wmE|nzxyY)&PnXe*e!ujN8*&6r8l zV~4n%z1S4CUjoD}3>{cJSpL`6!1Vd6_$3l`-2YV{mgiVw*(gz#?BZy`^+>T@tOTW! zlUlPfOKC;30c*QrBa&mQjg)==pSfaBU4;1K<+?AAvZt#rDuA$zuz+QGNFknK z&pY1VBfkE={q(E+`uq9(RsQs=eEvFr`c=OFI^TcarZL<-6lC62g1Xy(@xTB7{^^N;IOzR1;jRkF>Ox14S6dBjyg7Xa60$XbF<)X#J>M8_mtv?#?YD=~joj+Ih z?~bQ(t^tSMf_wG&{IM#edj4UD63R7oWR11>5fdXQvG9;OxLj_`jNL2p&W`7SAH!vE z$7MK%%k8+Ggja@-=^HIagF&8PPNn~9vx zY2JL6KL<@(4|t|{^I_bR>|&Z2yC+>8tr&tRh5PFmaaC@Q%f3A9as2JFH@7dBJ&wJ- z;qrL9{Nes~*`qL7#~xR`Ry*ZO=~Sm8Id+i@4$pa|<2BkmMFS3OG0^;MQZ zP(%-a_F<3Y> zTpzj5_g7p%?La|%=6;mz{xSKDfIJMUj_zAAj?OR456po&Z>YSTqkY)N|3+=F=`L}~ zCWU#+1!!!CrS3-6zekCOF}^SL2=7LR-tVYW{#^X-M%kdy8+y*`h=z=!a%7)hjGsHc zeT?6dvLE7O#m^OAKBq70zI96?_PGzaZYpv%B~BNP(QqNUh?LeRbeNPhGWIILB2LCUGF!2w|lMEf5k76#X^n; zY%JEhRipUn>+~>p!n&|4XYC(WL@)V)RuTkt`J{i{@7r~fg)HV_SA4TB;=E0LRyp}i ze0|5+u9;x{)A9AC_xG#kkB|Ay(BId@kA8mk2Wo^kI#jm0hBl6?b-t9u=ETQ0@$)K7 z?c5bpJ?|!@7CV}c5Y^vG>H` zv)9_PtTf=dB4K}*7>CYwVp6ZPRMVt$NywG8euVPRdVYStZAyvpElq@@r&|lZ0PgDX z+7Kp|@#SPxI@Te%do+u%_rKgWnY{94|Fq{#+%Ml({i*$ZSR)TN0f{$XD zXHfa_epi>Z_h+%R!a!Zs7_BAoA32Djs;*JtwB1dl2AUF-Ag!>}7tBF+Wb6JGI@?8Q zatedTk@18I18>DCI$wK1O!Hzb7G(Wpx;qf36x`j9;W~Ex_=Z;JsPhH!(Segq@usk% z(a?YKKm14kOaJq~e8MB+*m)w{a1YKgMKuT|D=LCfXq6359;Hdma71dt$KisPV(8ub z|JBE{*Pr`Gt1ZWmKs!jH=gj<(i|+^_e~Z6L6q~hgMR{V7hhA$~rml72JSB>6<_W?paVwg|w&6tbAFUPc zDAu9;xnv0%-=wUj!GP+Ya?QyYmtoc?o^(=X{GnU|gG)ntdMZD-a(8B_6)Qh*I5|b7 z$=gfVq)^>QXl=Vjdg-ZC6k>oEPOTf|A2h<0R7>Tzy8R2@nyLcyZY`|bT6=xIt*@;0 zk_HLpsr=l^P^1hEN;h|Y==^N6{)rG=&LULwmE zZ-{t}&-1IX_Me_FMg^|??EF%EN=2pWLt}jW;o{@9KIG5d=dU{=o&ww1pZVjDbB+~H zzJ>1^R?4GV^`Vnk8>_y3Ip4c}&T9VizeeV9#M06oF@4tO-gZ;HAJY~Oqa7}VSWJFj z>G&4kHzXJ9_`C~bdleREFgBiI{PmLYlbTtMxgaf%vv1?$H^wKL1%Ej;Gr8wCw92~O#H$9pj7WGen`8lzh5~LZsY7O zpsJct{jRAKc<=-Mz~yrssJo;VS+A#-|?tSvGi#l9B=tw&>w{8trefv(G+J44HrfE zl@@|{G_E;+hPjUd=^@-CCnZ@Xg^u|;C*_j`NSijgaR&>;vO2&w?Uqilf)Qo8kiWwyk< zjus57_e)f0?VmOkm%dL=fxJZEoggqoz>gM`{ys7?GCD=4TpNryChfrH64KYH@QUh0 z6pQ#Sj(S_&aIju{^eAFw9+T%#`7|Z#%t*T<4gOw#`yGb!>#y-1b|H@2vG7waHoPx! z{fk#}XPCtunfCYp!++@x+>v+K`%Tf`XX*KcJyCk?QFpemMgMR~>EL-^A4g3l zP&XWE`odzQ1SN)VQfsbB7Xf^x8(r)SY+8TzF9PPhe^ik}1C$6?{x3H!H7*9B%pJw+ z9Ee7Qq>zxO)D;gLrBs#2?JA@j@|@!q#Tl&l$7M_v2|pR}hI1oMP?zm_N9F3LncZpT zPd!^`1QkRejBh_!trXura_EcQ)DRQ` zvODKvOEOe_sQEwZH{dtKk9ztb@!K7eM&LCkn_h#D)2Pfi<+l^m*AI7ZJ{tEYId@+D z;jp@S20O)d#w9Yt{ix;eZG5cwSn(UlQvD1s`#8v*punc|8gN%bWSbs0<4?ENhB3Iw zI9G}sb<%tuA1gloaPgfU6-H=K^~pIqOe%Pq=D_YFWg;0Ej9P_z*8PPlD?wjTvK1Vf z&RYBT%@zSNR(zRZb4oh#?eqAQDeCwZ_%=RqF48I?F^10%x)$=4JC!kOls_{?ueGhI ztKw@c;)X?GEY_4YJHc(mx7YY+W=JV-TJ$=vQL{Jec5Fp!{@4>`cin24e{z(yKE~%5 zbNG^<#JAV@wg30`yW`j6%=&kIZk^wnP(~y>g#hKMnf!it{%M)H)cv7UR>kusS}CbN8qVqILf9(Eju`pPcwzj+E9x&ht-vkLvGtelz?`e5YFo|E}ox z^|N#jtGfj3|1dM8bN~1ATg5-W^XL9TDT6l@$KQaD2)9>ap_f)OotBs8As@gc4 zc&dA=`-u;E&J}+c`@jqDH2a9KgyBqld>)^fcg2^zX?H@)nfT?O#ZRnUIe@)y-><9) zU-oCdAX*8#nNLW+dJt@4!2eR&m4L?|6pYcfN-`BM7{@{f@YcITE-__qQF2&7yU$TG6)N zueG>Tz0#h_qMd6?3bH$~J(XqS80-5gX_xvFH#+k!uz$1L#XRTlb}9C1VDfE#nEYce z?{~&w@V+d{(!7r>Y#$uupYf{K&;BvLNr^(tgsKk3y6W%C4k4*CR641t4q0LD?ZwmI zSBo<-Ke+cibH@kc@1d1xp!;d!+AH{_#fEri8nUs^N9j5N@fSzvm$>#MKw= zkln?^AO0U+_}L&jz?Mvd_D^eINfAtcCfX@s6rdbUhZeG`>PzbJ&H2LVW3|S-=?XFl zFv8xA=xcHY-HY2|UTig_yB~w&RDZcxX6`t4%tQK0Fr$XmIq9%-%p%?0y!z33+zvp6 zCiCN{?$wt!|MP#_pY-!q?6}5#QoDY9{nqzl+S_qw+|R%Ad;i=2>#rg*Kyou)^c*HGg(aV}8bqgjnVOIUmtKL+j&EVVC(~=g(2lE202K?VNaf zxU{a1mli%D^X~aLcFZ1b$8jBdcwP@Yj$c=47FeV zw9pcu=L)Y5^tTpNn#2G{-U;&>Z$uAeG|x;8!gXA=2cLRv3k@OGs}I&mDLb3dyuf}R z>MMUae_NHssf$e4F@gd4Cqa!Sm~UBtuZBPk%ihZO+H3Gb=vYW+86?|B=7_d-Fz#Vw!W( z9&QbTN|96F*7?Z$j^3}7%h$QqbAqlv`%Yo6_Y2jx`f6&X{!jhRPiU@fuCq`hW*4cX zzpsV~(@fVwc|rAU%kb)aS@CVfN5}Vt3LI$_s;dX57UlhTFY)n7d>cBMjSByY zpDTW@__pHPifF@+B8($zQC(!a!?=B3s@ zwFYbc5}(E&+jdpJE!5iXZQsPVKSX?d7T;cO!L8AfZ6=4;_!v~ImZ87UocxAiC9n9d zfzoe`FK(7C$7vp# z8;I{sFBs-=AMO40#`~2BRj7d1BG|~x4!CNVL6YN1?^>XZbcjYDih6BvpUal3O*)yT zT*=ZxKlTekP=mCWy52mU+C7J z^nK^k+28d+-S@~k=k2uKZ#0FO)({z071xEYnwqoUWWFy*^w_oSn-o_b?2p}8J zb-5DA2fF?vd3I}zLUEP4&-l{C_i;J3l0-7!e0A&hyDS>3=Ibv+8>NiDh{fm6;?__E zGZ2+CqK0zp4Ee)Xv#4~h(mjxw9)a8JnTWJ!Ms=p1fv^we1K%1Kw#)}GD*Sxx`-U@Ef`)8ClosMuOLt#Qjn;R7c69p4l?H_NH5aNtBA@@Ie z%Vv7rOuJ73-E8HwQ!}x)WwqX)n%>XzbJT7nc(}jcrps!zW8$lORc?~={CockOy{ka zITeZ9XhjOaie@iPev8J^=o}`qXPf|9il#*eVd?T51OfkISD>j@I^x)L^>%}ToKwbn z)YII{VGo8Z!CsRxOLd*x%5^S|+wnZ-Tn4}BUU#m1bH5yK$1cw=#~#PtF1ySh$1bzS zWp5`R4s}v+j=SYCxZ!fRqdH>#<%f1k%670)_qH;RQ^KXsmzIaY+YeXu)RwkLDSQdb z(5gQ2VtW;Z05Xr?vZ|Ci@$MJ{5FLSr6I$~ZZJO|>^x!~OY z6zOP!ky&mt20{+dx_G1tP#B}`*R3}kvZIhLA=8OESIu?{NbQDp+>{l`f+{6APR+$% zAe(IBv%a#?L9-Vd9d=?Ci^_5pQmvgMRyc80MAFvB+nN;o-9BBWzW0yY$1ro^<$US! zTIp5)_}m|ad+qa! zHjq4X9V5}8@ z;!;$R_^?yQ*H(Oto0}SA)&^y{T@dnT;@gUk6(6lx#d#Qdu|9u(>AebuME^%`X_;s{bp&)(!t*6L-z`LUj#g@`XF zaO--q1)_PsmW~zH@0BY@bJF>jaPLt(&*_Z_3r4s*w1gv;hp<);^Uk6 z_8LD|d|UC+wr=bB>%B~TVnWsbQU8Sb-^M`LZE0St_`aM3zq!9R2fRB|tWVlMk-ln` z|HtiwdSMa?QhbSX;u?yo6%fiI^O)2?HMym(2Cri?d?YbTor}n%d6!WOL|p|pUtGToB9RP5i`z>8=1u&LJc6+ z1a}tUCOST1wn8+{kcz<;l*!fp$=RRu-B}D_j@641fa*S=-)}JmT4OYXyK!iJ$csU? z{Ff1B>Z+V0?dWv8BaY#&@A+9@T^#vs-M|Grjod@M!L&un=qx1oI{)%Z4X~Vir(whWkHqSqW6u6h?;3b32*0O{c8BpxYE*xTh#l_x2bH zaPLWmr4$fe1vU^+d@9^!X=&Y5r4iL9BTzhiQjavPZS4ejNDGMhhj-G0yv25|?~WO7 ze%iL5h3l5m(_~+$}=Z z*rk32b9*@4^1u7{{W<&Y7u=P#SAX?;!^+cXBDQSAU->J-x`&zl=l{pQ3s~L(x41)E z0L6{>XGGB^h-(vWP@0JBBH4O=o=~ch#@kf4v~=E*4Y(zxprQGQeH1>wHGt|MzopPo z=L_8fbqY_PT}oTG#B&qOpH*g!l!x+jjX&@8e(%OLgADYpPh?T(t=$r(Opq^ltIGdU z&Vs4_5$|};mln_Kdi%CP4UL$(AhC69L=8+b+C3+DALuDm$B*tqN$E_ODM3NAwZeoQ zOAFv6Zq0n86#}Wo2(-q^5mP@@ebw4CfmYCx5*BCal|NkygJJv|TUB=D=M|sppIsj^ z{&K&iG8fO6Qsm0gD%$a}^MBD~^_ePuc6=;xp33{_1Z|vd0Kea= zPbd-BUHq5$(jZp#zv7RsASi9a3|@fzQxSAZzx&1Ti1#7#50n-AsBXrOm0{>%Nb@=vnG711gp=p5x<`yW*i zy~fwvxbD{zpVj_reB<{OVb1&(iVs+=UiV&@|M@p~a{o;I!1}WlvZzE{j9OECbo{Hd zU{V%;r7#zkobQJtiHyG9NSns zxRZ6*sAEluEF7c8T)oQE%nO(DVRSz`24Lv}9V<=_Cw0USRs5Wcqk8T%?}3_IJ@W@F zmY0NZQk=vTw2AvPbo=xAqL%vF=~kjsM%ekXOD{#XMcO}>t&nGR(;HTT!6DH$?t!{Z zSx;Amy|yB<#*~ zk&kN+gk(QYVY1Dhg$F6-Xvvae1PyD(PTjtUfJH{6M`ncG$V`jOpstxCBlrzx(A`{S zO%$byrp`2QezaXB7RPSYl2!y@b_`Ju)BCf9?LpD#6wOE)d)9^v;V#8?DORKynF+X; zj%>HKQZ78%O)~BOl^^8ukQKlFxwvGp(EhbQBXU7{`Eyiin1x^aGpdHJ{drT3g3}La zr6nEWS9~$@7C(adgl;U1k4)gmqkvjkj+&QxBaHZUzwS?b;9sO>Darh-oO9}#)iR{s zs^{zT)na9oU0xeXJ>~{Aj z_iCy~@6pPAeljEaROXZdEda4g);ixaq(WN46_w0H2+!PtMw6dehy3pV#=Ss zyuID3h7(2`dCL96y*OOvd5&LzJC5T~|9c!?ez5Pp$24QZ-hLRa83s_w3j4Q*AC{T_ zo&T8q7ygmoP4~!rT#jdE5E%H(R<<(B^dx|XNY#Mg4$A*jc$qU-rHK)SMb8_J2D%eb ze@S=v(nXD`mOIrkFeL@rUN;4*zB7+|c@#>fjGYwm`i7P_`7^RQ?}YgSTc(uewIib={&U;z0=FSkKfQUAEH z6k+Lh(3hlWHDYqgs=j3}ol2Lb&2^-ad}HM|3-J?^%+TW}gRFh#PjMV9$*gJt3H5Cw z^GNIWZndBCy%@Aw(*JY8m$=fS^U{i!fD6c5%ysrAq0AHXBl z8Uiy=Nm$ENP(^OVPhs@>-a7BMU5qOJdcPm`k1U#azm`hRDCog^Hl}geiZAtW69$Ys z%C@YC+lY(o``tc0OLa@}3k{P$A0IEWIxXtuqc(S7*Pl(PtlBc9sNk?y&8V0D zK&udP?klav&DQb^VXWHYq8yAusCHKgee=&m&3VeeAXT*RpXEr^G{hOvsr!)Zi?cC& zIzE@JO1f1IfYDaPE7(e&>EnA(|i&5uJ>!ZIIYkjJ7WI|?f$Jc!OPAVlw#*GL>B;KF- zPSZdN@czS^5g6D0Kh7te^_Z`vo%U-G`e_^}Ek?VlHRr0go0AIKC?e)Ays zr1z&VnDx)2>(BjPE|A$gj?eZ#*MH(0>~`({;1zkkuUy|R5q6?Y;}8CR3;VG1L+bl& zed6}kkv|i%(`Xpawnbs7Vv)(CDU;X^jryxWw1>stqcf91cR-IV+)AsY{$`5+K zT`e(#P*haF=-Q3RQi|P(LR?UzDZH#4(j9QXJ>BehT#n0dUHNc(d)VdqaC>w6a{1fA zm&bV=`*Qr@c)R@V;O*hpg~!2hzb-$H<9<9ZH@92fd7QmP>#$f_ra2-q6C`0nOctQ? zPZE8ecbI?oTZ2Y5t-s!Hd8htAh521`F*7pm!=?-zMj>Lp&HomC*39?Y`TsYZFZp`D{Cs@;A>;4!#ZpRb6W>;Ro$rbd#>a}U_xoBO z7Rrg|*WuS$^;_46fNK8KPfBOXiC>EBgrL#kz{2aCy=fQ~lq*#pN5S_}TSI z)!Y_nHi+^MGjiwq7?<>Bo_`c%XijQyNb5^(F+c3o#pY;QC;)-wUeEH`2 z3)dsVkk8|r;-l9m!*~KJKIVK(toUfze4G2be00g|&J{mbd_DjD&GY9lPJZV3TSsZd z?|J{r`h0diCQpZ#_dE5E;(M<6x#GX>pHJgk%=5?IKbpRG{05E8`ovlvyu4rb{F3^% z^%mmwe%(yxu0L&*l=b{r&u{DbZ9V_b$2Y}i{<+X!jnERp~Xd0g@4rnhZKl)Zi1d~%pz@gEoHCcSHRMe1`??a;&)2M|h6^7!- zP}R??KTSi0+t75EY_D|T^XXzxZ)$PJ&|1{$G;_r$gy1BpI>Mz2Pp!sS`@ss2M%uLA zk%!sF{!lL1MWmK&OTXV%7H)+#bvA1<1G#Aw)6#inPlHY1KL~Ng&JHYY&+tgP1m2m? zz%%nb^O<TYtTqqufL7LCcm$O zkKmi{*K7_f;teGhoP@wP=)z}^^O}F4{DUEjVifus-OKzyjIpkkg=)d;{uJYk%B)8AmPI7w(X+ghPcUGQ+%>f*vXX_^~8s{L-tN)WARv>Utr}2Ss$SW#MU@^sp<+B z-|?-Ujgqx7nmEW&VFOy>RC>Be2vd>?|*VSXI@^fU*{8Os!FfU|Qnwhb01-Qcu$IU&D zn_u5?eW)u}HZ+1QrH_%jH2>~(n56@q@Rx$9@Ke7vx-+*~r?Zgyc(q&Nn z(>pXr0dwMy^7QIp1%Fy;VnU1$_1vjNgYg$3mujz%;>W0sbpC8>Nuon;XVgGMi4vgkzdCRLV>okE71j?VK(Z1w=TlpW1FGPuVeOvK! z#mAgqMuZ6azjMB{vUbBYZrv~X`K85P@$DskuJ~B-bH#_OIXiwKTlp1)>K{)($NdfV zrutC&GICX``-{2H!Ao;WGJtDxk4!(N^O1^;@HDU7RmSA1KmUL1{lBla+p;AFRn_x; z`=_>8)qfPQD!}0`a+srk?ujH)A}ZM47F`*kAB#;VVM29@VzC+;mCv@3TvR-HX#&=f*0QeHtHJgdw*hkPWg zLh&E(w?5!=|A){1ftHpjKX<>x3gGQP_{|&)pXaxa&mS@Bd&KYaSNli!lp8T=g?Fgt z^v}efDdmRp=Y>Df%&%I{zwq7G^V@#@C0q^W9q<#gIemU{eWa@hPW0e63kvZq#_-Da zxU1#KYo__A$!1cjQdP@2ooMEMx_qYLWpLr=W)HSB<3KMq)r^`oXXqdD7wfr4IW79~Sn8%Crk%%Ec34#FKi!SG=Ee%B zRdVbUvsx~#ci#LfPmE3$8{Uk_?7VlbGuoU-^b!#;d9Y)?m#!aqj;~25arQMUPNlTQ z2WRp~HUK2tFj&GnoH!Xx2LH~*9ek%=K>H>tBeG$kn{X6yR9#q~MokgbYoJLuHFqsl1n0hO7PR^`vKG|RM%dRCpXbL?0& zBkP%Up8m{wuX=lCroCmoAAdXTKWOi1Z_hgA&}%<_%+IN`b|H-S%+tHt>s0m6;uN=c zjlbCunH`tAF21Vulozw*KPAD$WQ!1uB8jz<41qegTc)OA=_F8N`#4?i@#yUDi{G!~ z`vt$NLu<|;4&E<|Tce7#^!RrC0dEI`|7F3A&-e!CuZeH-e(m;thdo|v|M%=V6onA+ z3O`0ZGBug^`o6AI&S#?phi@(T{SLkvej^&>QV+7Ay0V_Xw3XGULVTd|S8yV%%I5jW zb&V0zhE5tzzRW|MSQ8dhl$o#y^)b%qRPmvpQd%p&(3a!=H0;k>m&_OZkUCy>ZwfPR zr73ccu^_S{(Y|ngIVP(ieA&f~o;3)wh={7k74CI~yVrI3H@E-IKleBPm;b%L@h|;% z|5kIXe)YJ1b;Wm={pxyr_btBr&L2^~e)zY`zq{hwW#3)#?eg!w#l!vEBd)M-mtSVr zBg`@$ZdUQDKgndt>gpDA7$ipV{2+uZ*0fQ1#LU&dN>1kX{_oqMxu29t=a3yO`ulBG zX^To(!n9oK$a6CCy^ArOs%m9X_fS*VIAQ(Puyf0Ey3?XLwqdq&ycyT7HN_7HYpXzk zCEBm>GH+F~E8-DWVVP&#QKoL@_lkCnCu}QL@?T#MbV0hF-npfT3g=dp7ZjwhmBEV9 z^V3zjs%HPQTG;;CDq!6EF_Ih_+x%G zy3Bs#szkE9%P5A1Yl$G0QCU51= zA6oCV&zIJV8Hr64$a}8S+`AUP1W@Uj1~eUO8xOwmu6Fq+HC7!OeS z*gMzK-G38U?6OTvhta^QZ>YY&# zm-xA!A5=e?^78qM`@gESM?q%R{tTVJEsnjlGOSuNgcV`kZGrC#-#+5c{ch%G%|=Bh z4fDTJW?Vr$5%#n`m9swM#@LElQdvn~-pcq=C@qNfhuCSi?SStK-xj|AV*Gi2%*p{D zogwq8Om9XroKkgPmX_b zsesC6PwpVvw^nTHc_ozxb$%f}m-)j(*7K^{S!H@e;+yDhRfbs|<3x9hcXQ|@3w|SJ zcwoeFx-~RPdF-k_EH+sxs26T+W`^*w>*$l7D3YK~kAGl&lak70BdC))1W#T-; z^~{@iscw8#5K#buegOgz@M}3fnnN$mCeE@DG~UFuHRxcr;FOD16QzBxd$X|zWs?i8 zdJt}FJ*7Twl%vq$IEJ8W>xga?%E^SYM$m z@RZJS=t6Z3C_$@pnxZyBw-6HJ#*Jgpy^c}p!7TcGQHwu9Nc4DC&QS_(B#^jt{F# z062=;dLq>o2;lrNO!nwgy`?Kf6 zb957e@eTSK$ftu8)1uX@(QO3ZS2>@*pcQIR9_{rh)Bbl`Pql4$A=?A@+IFwW%JHt9 z+6K*(X8)M0hivow+kBjTt#E4zAG%eBRe!{2kF~xPF52N&c}BT=UATgnWwo-^)tfOa z%;IvldPF>~dR+bp|JCKc`p^EkM^xGW;$Qf47Vflv3e~i?->Zut9hN=|-u}bC^S}PL z|6W}!nQdMcL5ajJn4orvC|%q?bPr??s=C;9RUFRSQcE={Egqf+Zc!0&vZ7lUP0R{A zV&XXUM4H3sVEo62{qGXc=cKRVzxIDb)G$4P?zSfiy7fMe<^Cf{plD*3%AF^&b#Mj∨{%ByfxJgXw2 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%0DHUTK?4nnlW|%kzN=)Dwc;5`k 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 Z0=ulGZv0wOc=ZhUqpNACQLSzt_J7}(J#+v7 literal 0 HcmV?d00001 diff --git a/site/assets/articles/medium-flame/article.md b/site/assets/articles/medium-flame/article.md new file mode 100644 index 000000000..d2c947441 --- /dev/null +++ b/site/assets/articles/medium-flame/article.md @@ -0,0 +1,361 @@ +# Adding 3D to a Flame game without rewriting it + +*How flame_flutter3d puts a real 3D renderer under a Flame game, what crosses between the two engines, and a step-by-step tutorial for your own project.* + +![River Sortie: a jet over a river, "+30" and "+60" over targets it hit, a helicopter going down in smoke. Jet model: Poly by Google, CC BY 3.0](https://flutter3d.pleion.dev/assets/articles/medium-flame/01_river_sortie.png) + +The screenshot above is River Sortie, a remake of River Raid that you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. + +I built it to find out whether that works in a real game. Flame is the 2D game engine most Flutter developers reach for, and a lot of people know how to write games with it. flutter3d is a 3D engine for Flutter that I've been working on. For a while the two lived side by side and never talked, and if you had a Flame game and wanted light, shadows and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. This article explains how it works and then walks through a small game built on it, from an empty project to a boat collecting buoys on a lake. + +## The idea: each engine keeps its job + +The bridge does not reimplement either renderer, and it does not ask you to move your game into a new framework. Flame keeps running the game: components, collisions, input, effects, the clock. flutter3d draws a 3D world under it. The bridge's job is to keep the two in agreement about where things are, when a frame happens, and what the player pressed. + +Four decisions hold the whole thing together. + +### 1. Two layers in one Stack + +![Two engines in one Stack: Flame moves the orange square, flutter3d turns the cube, and nothing connects them yet](https://flutter3d.pleion.dev/assets/articles/medium-flame/02_two_layers.png) + +`Flutter3dFlameWidget` is a Flutter `Stack`. flutter3d's `SceneSurface` is at the bottom and Flame's own `GameWidget` is on top. Flame goes on top because it needs raw input: on the web the 3D surface is a platform view that swallows pointer events, so anything that wants touches and clicks has to sit above it. + +One side effect catches everyone once. `GameWidget` paints the game's background colour as an opaque rectangle, black by default, and in this Stack that rectangle covers the 3D layer completely. A game with the `HasFlutter3d` mixin returns a transparent background for you. If you use the widget without the mixin, extend `TransparentFlameGame`. + +### 2. One clock + +This was the decision I cared about most. Flame already has a ticker synced to vsync. A second ticker for the 3D side would drift from it sooner or later, and I don't know a reliable way to stop two timers drifting except not having two. So a bridged game runs on Flame's clock and nothing else. + +![One frame of a hybrid game: Flame's clock, and everything else stepped from it](https://flutter3d.pleion.dev/assets/articles/medium-flame/03_frame_loop.png) + +The widget adds a `BridgeClock` component to the game with a very high priority (`1 << 20`), so it updates after everything the game adds. Every frame goes like this: + +1. Flame updates its components. The ones that step physics and actors run here, and collision callbacks fire in Flame during this pass. +2. Bridge components copy positions between the layers. +3. `BridgeClock` calls `onTick`, the cameras are synced, and a redraw of the 3D layer is requested. +4. `SceneSurface` draws the 3D frame. + +The redraw in step 3 is deferred to the end of the frame, because asking Flutter to rebuild in the middle of a build throws. For a long time I assumed that cost a frame of lag and even wrote that in the docs. It doesn't. The deferred call only marks the 3D layer dirty, and the next frame starts with the tickers, so Flame's ticker updates the game before Flutter gets to the scene. Both layers always draw the same state. + +A second rule follows from the first: exactly one component steps any shared simulation. If a hundred physics-backed boxes each stepped the world, the world would step a hundred times a frame. + +### 3. One plane + +Flame thinks in `Vector2`, flutter3d in `Vector3`, and somewhere you have to decide which axis becomes which. I put that decision in one class, `BridgePlane`, and every bridged component takes one in its constructor. When each component invents its own convention, they drift apart. + +There are two planes. `BridgePlane.ground()` is a floor for top-down games: Flame's `y` becomes the scene's `z`, and height is constant. `BridgePlane.backdrop()` is a wall for side-scrollers: Flame's `y` stays `y` and depth is constant. Flame's `y` grows down the screen and the scene's grows up, so the backdrop flips it by default. + +![Two cubes: the blue one is moved by the scene and read by Flame, the orange one is moved by Flame and read by the scene. The map in the corner is Flame's view of both](https://flutter3d.pleion.dev/assets/articles/medium-flame/04_transform_bridge.png) + +Who writes and who reads is chosen once, when the component is created, with `SyncDirection.flameToScene` or `SyncDirection.sceneToFlame`. I didn't try to guess the direction from whatever changed last. Two systems can easily both decide the other one is reading, and that kind of guess fails silently. A physics body is written from the scene side by default, because its position belongs to the solver and Flame only reads it. + +### 4. One input + +`FlameInputBridge` takes the keys, drags, joystick and buttons Flame receives and writes them into `Bindings` and `InputState` from `flutter3d_game`, the same objects a native flutter3d game reads. The bridge has no key table of its own. If a player remaps jump in your settings menu, the remap has to work in the Flame build too, and two tables that disagree look exactly like a remap that silently did nothing. + +![One key, read by both engines: Flame's key handler takes the press, and the flutter3d side sees the same action held](https://flutter3d.pleion.dev/assets/articles/medium-flame/06_input_bridge.png) + +On a phone this pays off. The on-screen stick is a plain Flame `JoystickComponent`, `followJoystick` feeds its deflection into the same move axis a gamepad stick would, and `bindButton` turns a Flame `HudButtonComponent` into an action. The code that moves the player never learns which it was. + +## What crosses the bridge + +With those four pieces in place, most of the package is components that carry one kind of thing across. + +`Object3dComponent` is the workhorse: a Flame `PositionComponent` that owns a scene node. Position, angle, scale, visibility, opacity and tint cross. Flame's effects reach the scene in the frame they happen, a nested component lands where Flame draws it, and `elevation` lifts a component off its plane. It also exposes `visual`, a node under it that the game can turn freely and the bridge leaves alone, which is how the jet in River Sortie banks without Flame knowing. + +For physics there are two directions, and River Sortie and the arcade demo use one each. In River Sortie everything is decided by Flame: hitboxes are Flame hitboxes and `onCollisionStart` says who hit whom, while the 3D layer only draws. In the arcade demo collisions are computed by flutter3d's 3D physics, and `CollisionBridge` relays them to Flame's `CollisionCallbacks`, so a ram computed in 3D arrives in Flame as a normal `onCollisionStart`. + +![A crate falls in the 3D scene; when it lands on the pad, an ordinary Flame component hears it and the pad turns green on both layers](https://flutter3d.pleion.dev/assets/articles/medium-flame/05_physics_bridge.png) + +Two things don't line up there, and I chose not to paper over them. flutter3d reports a pair of colliders, while Flame wants a `PositionComponent`, so you say which component a collider belongs to with `resolveOther`. When it returns null (a level wall, say), the bridge calls nothing, because inventing a component would tell Flame it collided with something that doesn't exist for it. And Flame's callback has room for intersection points, not a normal or a depth, so the bridge passes one approximate point. The real normal and depth stay available on the flutter3d side. + +For cameras, `ChaseCamera` follows a bridged component in perspective and can shake, and `CameraSyncController` keeps an orthographic camera and Flame's `Viewfinder` framed the same, or lets Flame's own camera drive a perspective one when you give it an eye offset. Beyond those there are instanced components (a hundred shots in one draw call), particles on Flame's clock, Flame sprites and text standing in the 3D scene as billboards, taps on 3D objects under a perspective camera, a Tiled level stood up in 3D, and a separate package, `flame_flutter3d_audio`, for sound that comes from where things happen. + +## How River Sortie uses it + +River Sortie is the biggest user of the bridge, and it shaped a lot of it. The game mixes in `HasFlutter3d`, so it owns its 3D world: scene, device, camera and renderer are fields of the game, and it builds the river once in `onOpen3d`. It also mixes in `HasFixedStep`, so its logic runs in fixed steps and a second of play comes out the same at any frame rate. Together with a seeded river generator, the river is the same on every run, the way it was on the cartridge. + +Every tanker, helicopter and fuel depot is an `Object3dComponent` on one `BridgePlane.ground()` laid over the water. Its Flame position is `x` across the river and `y` along it, and helicopters get an elevation that lifts them to flight height. The banks aren't hitboxes at all: the river generator answers whether any point is water or land, and the jet asks it every step. Stretches of river are built ahead of the jet and released behind it by `ChunkStreamer`. Shots are drawn as instances of one mesh, fire and smoke are 3D particles, reeds on the banks and the flash of an explosion are Flame sprites standing in the scene, and explosions are heard from where they happen. + +![A bridge hit in the middle breaks in two, each half going down on its own pier. The "+500" is Flame's text, drawn over a point in the scene](https://flutter3d.pleion.dev/assets/articles/medium-flame/08_river_bridge_down.png) + +The game also showed me what the bridge was missing, and most of what's listed above arrived because River Sortie needed it: the fixed step, particles, Flame sprites in the scene and positional sound. + +## Tutorial: a boat, a lake and some buoys + +Now the practical part. We'll build a small game where you steer a boat across a lake and collect buoys. It's about 170 lines, and it touches every piece described above: a game that owns a 3D world, components with bodies in the scene, Flame collisions, the input bridge, a chase camera and a fixed step. + +![The finished tutorial: a boat about to collect a buoy, with Flame's score text over the 3D scene](https://flutter3d.pleion.dev/assets/articles/medium-flame/07_buoy_run.png) + +### Step 1. Dependencies + +Start from any Flutter project and add: + +```yaml +dependencies: + flame: ^1.38.2 + flame_flutter3d: ^0.8.4 + flutter3d: ^0.8.3 + flutter3d_game: ^0.8.0 # Bindings, the key table + flutter3d_sim: ^0.8.1 # InputState and GameAction +``` + +### Step 2. Switch on Flutter GPU + +flutter3d draws through Flutter GPU, and Flutter GPU is switched on per app, in the platform settings. Skip this and the app starts, opens a window and draws no 3D at all, because it can't load its shader library. + +On macOS and iOS, add to `macos/Runner/Info.plist` and `ios/Runner/Info.plist`: + +```xml +FLTEnableFlutterGPU + +FLTEnableImpeller + +``` + +On Android, inside `` in `AndroidManifest.xml`: + +```xml + +``` + +### Step 3. A game that owns a 3D world + +The game is a normal `FlameGame` with four mixins. `HasFlutter3d` gives it a scene, a device and a camera, `HasFixedStep` gives it fixed steps, and the other two are plain Flame. + +```dart +class BuoyRun extends FlameGame + with HasFlutter3d, HasFixedStep, HasCollisionDetection, KeyboardEvents { + /// Flame's 2D world, laid flat on the water: Flame's y runs along the + /// scene's z. + static final BridgePlane water = BridgePlane.ground(); + + late final Boat boat; + final TextComponent score = TextComponent( + text: 'Buoys: 0', + position: Vector2(16, 16), + ); + int collected = 0; + + MeshNode _mesh(Shape shape, Vector4 colour) => MeshNode( + DeviceMesh.upload(device, shape.build()), + engine.Material(name: 'paint', baseColor: colour, roughness: 0.5), + ); +``` + +The world is built in `onOpen3d`, which runs once, after both the game has loaded and the 3D device is open. Before that there is no device to upload meshes to. + +```dart + @override + void onOpen3d() { + clearColor.setValues(0.55, 0.75, 0.95, 1); // the sky + scene + ..add( + _mesh(PlaneShape(width: 400, depth: 400), Vector4(0.15, 0.35, 0.55, 1)), + ) + ..add( + LightNode(name: 'sun', intensity: 3) + ..setLocalForward(Vector3(-0.4, -1, -0.3)), + ); + + boat = Boat( + node: _mesh( + CuboidShape(size: Vector3(0.8, 0.4, 1.6)), + Vector4(0.9, 0.9, 0.85, 1), + ), + scene: scene, + ); + add(boat); + for (final (x, y) in <(double, double)>[ + (3, -6), (-4, -12), (2, -18), (-2, -24), (5, -30), + ]) { + add( + Buoy( + node: _mesh(SphereShape(radius: 0.4), Vector4(0.95, 0.35, 0.1, 1)), + scene: scene, + position: Vector2(x, y), + ), + ); + } + add(score); + } +``` + +The meshes come from code: `PlaneShape`, `CuboidShape` and `SphereShape` are flutter3d's built-in shapes. A glTF model works the same way, as the node a component owns. The score is a regular Flame `TextComponent`, drawn on Flame's layer over the scene. + +### Step 4. The widget + +The game is created once and kept in the state. A game created in `build` starts over on every rebuild and never draws a frame. + +```dart +void main() => runApp(const MaterialApp(home: BuoyRunScreen())); + +class BuoyRunScreen extends StatefulWidget { + const BuoyRunScreen({super.key}); + + @override + State createState() => _BuoyRunScreenState(); +} + +class _BuoyRunScreenState extends State { + final BuoyRun _game = BuoyRun(); + + @override + Widget build(BuildContext context) => + Scaffold(body: Flutter3dFlameWidget(game: _game)); +} +``` + +Because the game has `HasFlutter3d`, the widget needs nothing but the game. + +### Step 5. Components with a body in the scene + +A buoy is an `Object3dComponent`: a Flame component with a size, an anchor and a hitbox, plus the scene node it owns. + +```dart +class Buoy extends Object3dComponent { + Buoy({required super.node, required super.scene, super.position}) + : super( + plane: BuoyRun.water, + direction: SyncDirection.flameToScene, + elevation: 0.2, + size: Vector2.all(0.8), + anchor: Anchor.center, + ); + + @override + Future onLoad() async { + await super.onLoad(); + add(CircleHitbox()); + } +} +``` + +Set `direction` explicitly. The default is `SyncDirection.sceneToFlame`, which suits a physics body whose position the solver decides, and with that default your Flame movement would be overwritten from the scene every frame. Size and hitbox are in the same units as the scene, metres here, because the plane maps Flame's units one to one. + +The boat is the same kind of component, with two Flame mixins added: + +```dart +class Boat extends Object3dComponent + with CollisionCallbacks, FixedStepUpdate, HasGameReference { + Boat({required super.node, required super.scene}) + : super( + plane: BuoyRun.water, + direction: SyncDirection.flameToScene, + elevation: 0.2, + size: Vector2(0.8, 1.6), + anchor: Anchor.center, + ); + + @override + Future onLoad() async { + await super.onLoad(); + add(RectangleHitbox()); + } +``` + +### Step 6. Collisions are just Flame collisions + +```dart + @override + void onCollisionStart(Set points, PositionComponent other) { + super.onCollisionStart(points, other); + if (other is Buoy) game.collect(other); + } +} +``` + +And in the game: + +```dart + void collect(Buoy buoy) { + buoy.removeFromParent(); + score.text = 'Buoys: ${++collected}'; + } +``` + +Removing the component removes its node from the scene too. Nothing in this step is 3D-specific. The collision is Flame's own, between two hitboxes on a 2D plane, exactly as it would be in a flat game. + +### Step 7. Input and a fixed step + +The input bridge writes Flame's key events into an `InputState`, through a `Bindings` table that maps keys to actions: + +```dart + final InputState input = InputState(); + late final FlameInputBridge inputBridge = FlameInputBridge( + bindings: Bindings({ + InputSource.key(LogicalKeyboardKey.arrowUp.keyId): GameAction.moveForward, + InputSource.key(LogicalKeyboardKey.arrowDown.keyId): GameAction.moveBack, + InputSource.key(LogicalKeyboardKey.arrowLeft.keyId): GameAction.moveLeft, + InputSource.key(LogicalKeyboardKey.arrowRight.keyId): + GameAction.moveRight, + }), + inputState: input, + ); + + @override + KeyEventResult onKeyEvent( + KeyEvent event, + Set keysPressed, + ) => inputBridge.onGameKeyEvent(event, keysPressed); +``` + +The boat reads the move axis in `fixedUpdate`, which `FixedStepUpdate` calls at a fixed rate instead of once per frame, so the boat covers the same distance per second whether the screen runs at 60 Hz or 120: + +```dart + @override + void fixedUpdate(double dt) { + final axis = game.input.moveAxis; + // Forward is up the screen in Flame, which is -y. + position.add(Vector2(axis.x, -axis.y) * (6 * dt)); + } +``` + +To add touch controls, put a Flame `JoystickComponent` in `camera.viewport` and add `inputBridge.followJoystick(stick)` to the game. The boat code stays the same. + +### Step 8. A camera that follows the boat + +The last piece goes at the end of `onOpen3d`: + +```dart + add( + ChaseCameraComponent( + ChaseCamera( + camera: camera3d, + target: boat, + offset: Vector3(0, 6, 8), + lookOffset: Vector3(0, 0, -6), + ), + ), + ); +``` + +`camera3d` is the 3D camera `HasFlutter3d` created and added to the scene. The chase camera sits six metres up and eight behind the boat and looks six metres ahead of it. + +Run it with `flutter run -d macos` (or your platform), and the arrow keys sail the boat. Each buoy it touches disappears, and the score goes up. + +## When you see black instead of 3D + +These are the checks I'd go through first: + +- Flutter GPU isn't switched on for the platform you're running (step 2). +- The game uses the widget without `HasFlutter3d` and extends plain `FlameGame`, so Flame's opaque background covers the scene. Use `HasFlutter3d` or `TransparentFlameGame`. +- The game is created in `build` instead of once in `State`. +- The camera is inside an object. A bare `CameraNode` sits at the origin. +- An orthographic camera driven by Flame's `Viewfinder` has no zoom set. The bridge maps zoom to the camera's height as `1 / zoom`, and Flame's default zoom of 1 gives a frame one metre tall. + +## What it doesn't do yet + +Flame is always on top. The layers don't interleave by depth, so HUDs, maps and sprites over the scene work, but a 3D pillar hiding a 2D sprite behind it does not. And Flame's collision callback carries no contact normal or depth, so when you need them you read them on the flutter3d side. + +## Try it + +- Play River Sortie in the browser: https://flutter3d.pleion.dev/river/demo/ +- The showcase has one page per mechanism, each with a live scene and a step-by-step guide: https://flutter3d.pleion.dev/showcase/ +- The package on pub.dev: https://pub.dev/packages/flame_flutter3d +- The source, including River Sortie and the package's minimal example: https://github.com/pleiondev/flutter3d +- A longer write-up with the arcade demo and more pitfalls, in Russian: https://habr.com/ru/articles/1087246/ + +If you try it with your own Flame game and something doesn't work, I'd rather hear about it than guess. Issues are open on GitHub. diff --git a/site/assets/articles/medium-flame/buoy_run/lib/main.dart b/site/assets/articles/medium-flame/buoy_run/lib/main.dart new file mode 100644 index 000000000..17dd3257e --- /dev/null +++ b/site/assets/articles/medium-flame/buoy_run/lib/main.dart @@ -0,0 +1,172 @@ +import 'package:flame/collisions.dart'; +import 'package:flame/components.dart'; +import 'package:flame/events.dart'; +import 'package:flame/game.dart'; +import 'package:flame_flutter3d/flame_flutter3d.dart'; +import 'package:flutter/material.dart' hide Material; +import 'package:flutter/services.dart'; +import 'package:flutter3d/flutter3d.dart' hide Material; +import 'package:flutter3d/flutter3d.dart' as engine show Material; +import 'package:flutter3d_game/flutter3d_game.dart' show Bindings, InputSource; +import 'package:flutter3d_sim/flutter3d_sim.dart'; + +// Step 4: the widget. The game is made once, not in build(). +void main() => runApp(const MaterialApp(home: BuoyRunScreen())); + +class BuoyRunScreen extends StatefulWidget { + const BuoyRunScreen({super.key}); + + @override + State createState() => _BuoyRunScreenState(); +} + +class _BuoyRunScreenState extends State { + final BuoyRun _game = BuoyRun(); + + @override + Widget build(BuildContext context) => + Scaffold(body: Flutter3dFlameWidget(game: _game)); +} + +// Step 3: the game owns its 3D world. +class BuoyRun extends FlameGame + with HasFlutter3d, HasFixedStep, HasCollisionDetection, KeyboardEvents { + /// Where Flame's 2D world lies in the scene: flat on the water, Flame's y + /// running along the scene's z. + static final BridgePlane water = BridgePlane.ground(); + + final InputState input = InputState(); + late final FlameInputBridge inputBridge = FlameInputBridge( + bindings: Bindings({ + InputSource.key(LogicalKeyboardKey.arrowUp.keyId): GameAction.moveForward, + InputSource.key(LogicalKeyboardKey.arrowDown.keyId): GameAction.moveBack, + InputSource.key(LogicalKeyboardKey.arrowLeft.keyId): GameAction.moveLeft, + InputSource.key(LogicalKeyboardKey.arrowRight.keyId): + GameAction.moveRight, + }), + inputState: input, + ); + + late final Boat boat; + final TextComponent score = TextComponent( + text: 'Buoys: 0', + position: Vector2(16, 16), + ); + int collected = 0; + + MeshNode _mesh(Shape shape, Vector4 colour) => MeshNode( + DeviceMesh.upload(device, shape.build()), + engine.Material(name: 'paint', baseColor: colour, roughness: 0.5), + ); + + @override + void onOpen3d() { + clearColor.setValues(0.55, 0.75, 0.95, 1); // the sky + scene + ..add( + _mesh(PlaneShape(width: 400, depth: 400), Vector4(0.15, 0.35, 0.55, 1)), + ) + ..add( + LightNode(name: 'sun', intensity: 3) + ..setLocalForward(Vector3(-0.4, -1, -0.3)), + ); + + boat = Boat( + node: _mesh( + CuboidShape(size: Vector3(0.8, 0.4, 1.6)), + Vector4(0.9, 0.9, 0.85, 1), + ), + scene: scene, + ); + add(boat); + for (final (x, y) in <(double, double)>[ + (3, -6), + (-4, -12), + (2, -18), + (-2, -24), + (5, -30), + ]) { + add( + Buoy( + node: _mesh(SphereShape(radius: 0.4), Vector4(0.95, 0.35, 0.1, 1)), + scene: scene, + position: Vector2(x, y), + ), + ); + } + + add( + ChaseCameraComponent( + ChaseCamera( + camera: camera3d, + target: boat, + offset: Vector3(0, 6, 8), + lookOffset: Vector3(0, 0, -6), + ), + ), + ); + add(score); + } + + void collect(Buoy buoy) { + buoy.removeFromParent(); + score.text = 'Buoys: ${++collected}'; + } + + @override + KeyEventResult onKeyEvent( + KeyEvent event, + Set keysPressed, + ) => inputBridge.onGameKeyEvent(event, keysPressed); +} + +// Step 5: a Flame component with a body in the scene. +class Boat extends Object3dComponent + with CollisionCallbacks, FixedStepUpdate, HasGameReference { + Boat({required super.node, required super.scene}) + : super( + plane: BuoyRun.water, + direction: SyncDirection.flameToScene, + elevation: 0.2, + size: Vector2(0.8, 1.6), + anchor: Anchor.center, + ); + + @override + Future onLoad() async { + await super.onLoad(); + add(RectangleHitbox()); + } + + // Step 7: the same distance per second at any frame rate. + @override + void fixedUpdate(double dt) { + final axis = game.input.moveAxis; + // Forward is up the screen in Flame, which is -y. + position.add(Vector2(axis.x, -axis.y) * (6 * dt)); + } + + // Step 6: an ordinary Flame collision. + @override + void onCollisionStart(Set points, PositionComponent other) { + super.onCollisionStart(points, other); + if (other is Buoy) game.collect(other); + } +} + +class Buoy extends Object3dComponent { + Buoy({required super.node, required super.scene, super.position}) + : super( + plane: BuoyRun.water, + direction: SyncDirection.flameToScene, + elevation: 0.2, + size: Vector2.all(0.8), + anchor: Anchor.center, + ); + + @override + Future onLoad() async { + await super.onLoad(); + add(CircleHitbox()); + } +} diff --git a/site/assets/articles/medium-flame/buoy_run/pubspec.yaml b/site/assets/articles/medium-flame/buoy_run/pubspec.yaml new file mode 100644 index 000000000..8bab597a9 --- /dev/null +++ b/site/assets/articles/medium-flame/buoy_run/pubspec.yaml @@ -0,0 +1,14 @@ +name: buoy_run +publish_to: 'none' +environment: + sdk: ^3.12.2 +dependencies: + flutter: + sdk: flutter + flame: ^1.38.2 + flame_flutter3d: ^0.8.4 + flutter3d: ^0.8.3 + flutter3d_game: ^0.8.0 + flutter3d_sim: ^0.8.1 +flutter: + uses-material-design: true From bf23cec06fd8245a2b755d5bceea1b28ac368bb1 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:15:02 +0300 Subject: [PATCH 016/141] The Medium article calls River Sortie a game along the lines of River Raid --- site/assets/articles/medium-flame/article.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/assets/articles/medium-flame/article.md b/site/assets/articles/medium-flame/article.md index d2c947441..39f4dd5d6 100644 --- a/site/assets/articles/medium-flame/article.md +++ b/site/assets/articles/medium-flame/article.md @@ -4,7 +4,7 @@ ![River Sortie: a jet over a river, "+30" and "+60" over targets it hit, a helicopter going down in smoke. Jet model: Poly by Google, CC BY 3.0](https://flutter3d.pleion.dev/assets/articles/medium-flame/01_river_sortie.png) -The screenshot above is River Sortie, a remake of River Raid that you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. +The screenshot above is River Sortie, a river shooter along the lines of River Raid, which you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. I built it to find out whether that works in a real game. Flame is the 2D game engine most Flutter developers reach for, and a lot of people know how to write games with it. flutter3d is a 3D engine for Flutter that I've been working on. For a while the two lived side by side and never talked, and if you had a Flame game and wanted light, shadows and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. This article explains how it works and then walks through a small game built on it, from an empty project to a boat collecting buoys on a lake. @@ -73,7 +73,7 @@ For cameras, `ChaseCamera` follows a bridged component in perspective and can sh ## How River Sortie uses it -River Sortie is the biggest user of the bridge, and it shaped a lot of it. The game mixes in `HasFlutter3d`, so it owns its 3D world: scene, device, camera and renderer are fields of the game, and it builds the river once in `onOpen3d`. It also mixes in `HasFixedStep`, so its logic runs in fixed steps and a second of play comes out the same at any frame rate. Together with a seeded river generator, the river is the same on every run, the way it was on the cartridge. +River Sortie is the biggest user of the bridge, and it shaped a lot of it. The game mixes in `HasFlutter3d`, so it owns its 3D world: scene, device, camera and renderer are fields of the game, and it builds the river once in `onOpen3d`. It also mixes in `HasFixedStep`, so its logic runs in fixed steps and a second of play comes out the same at any frame rate. Together with a seeded river generator, the river is the same on every run. Every tanker, helicopter and fuel depot is an `Object3dComponent` on one `BridgePlane.ground()` laid over the water. Its Flame position is `x` across the river and `y` along it, and helicopters get an elevation that lifts them to flight height. The banks aren't hitboxes at all: the river generator answers whether any point is water or land, and the jet asks it every step. Stretches of river are built ahead of the jet and released behind it by `ChunkStreamer`. Shots are drawn as instances of one mesh, fire and smoke are 3D particles, reeds on the banks and the flash of an explosion are Flame sprites standing in the scene, and explosions are heard from where they happen. From a08b56d87478a0fcb804fae781952284aa73c1cb Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:15:28 +0300 Subject: [PATCH 017/141] The Medium article calls River Sortie a River Raid-style shooter --- site/assets/articles/medium-flame/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/assets/articles/medium-flame/article.md b/site/assets/articles/medium-flame/article.md index 39f4dd5d6..bb0f135ed 100644 --- a/site/assets/articles/medium-flame/article.md +++ b/site/assets/articles/medium-flame/article.md @@ -4,7 +4,7 @@ ![River Sortie: a jet over a river, "+30" and "+60" over targets it hit, a helicopter going down in smoke. Jet model: Poly by Google, CC BY 3.0](https://flutter3d.pleion.dev/assets/articles/medium-flame/01_river_sortie.png) -The screenshot above is River Sortie, a river shooter along the lines of River Raid, which you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. +The screenshot above is River Sortie, a River Raid-style shooter you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. I built it to find out whether that works in a real game. Flame is the 2D game engine most Flutter developers reach for, and a lot of people know how to write games with it. flutter3d is a 3D engine for Flutter that I've been working on. For a while the two lived side by side and never talked, and if you had a Flame game and wanted light, shadows and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. This article explains how it works and then walks through a small game built on it, from an empty project to a boat collecting buoys on a lake. From 6375bd6899aa92e88cfb984ad7cc2c5ab9db77ee Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:17:35 +0300 Subject: [PATCH 018/141] The Medium article takes its title and points back to the first one --- site/assets/articles/medium-flame/article.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/site/assets/articles/medium-flame/article.md b/site/assets/articles/medium-flame/article.md index bb0f135ed..b3984ef0d 100644 --- a/site/assets/articles/medium-flame/article.md +++ b/site/assets/articles/medium-flame/article.md @@ -1,9 +1,11 @@ -# Adding 3D to a Flame game without rewriting it +# Keep your Flame game, get a 3D world: how I bridged Flame and flutter3d *How flame_flutter3d puts a real 3D renderer under a Flame game, what crosses between the two engines, and a step-by-step tutorial for your own project.* ![River Sortie: a jet over a river, "+30" and "+60" over targets it hit, a helicopter going down in smoke. Jet model: Poly by Google, CC BY 3.0](https://flutter3d.pleion.dev/assets/articles/medium-flame/01_river_sortie.png) +In my [previous article](https://medium.com/@dzolotov/i-wrote-a-3d-game-engine-for-flutter-from-scratch-and-you-can-ship-a-game-with-it-today-e635f1c1b8ed) I introduced flutter3d, a 3D engine for Flutter. This one is about making it work together with Flame. + The screenshot above is River Sortie, a River Raid-style shooter you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. I built it to find out whether that works in a real game. Flame is the 2D game engine most Flutter developers reach for, and a lot of people know how to write games with it. flutter3d is a 3D engine for Flutter that I've been working on. For a while the two lived side by side and never talked, and if you had a Flame game and wanted light, shadows and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. This article explains how it works and then walks through a small game built on it, from an empty project to a boat collecting buoys on a lake. From 74f0b9a54994ba0d03c32eda2d3713f3bc37858a Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:18:35 +0300 Subject: [PATCH 019/141] The Medium article reads less like a template --- site/assets/articles/medium-flame/article.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/site/assets/articles/medium-flame/article.md b/site/assets/articles/medium-flame/article.md index b3984ef0d..4909e6042 100644 --- a/site/assets/articles/medium-flame/article.md +++ b/site/assets/articles/medium-flame/article.md @@ -8,13 +8,11 @@ In my [previous article](https://medium.com/@dzolotov/i-wrote-a-3d-game-engine-f The screenshot above is River Sortie, a River Raid-style shooter you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. -I built it to find out whether that works in a real game. Flame is the 2D game engine most Flutter developers reach for, and a lot of people know how to write games with it. flutter3d is a 3D engine for Flutter that I've been working on. For a while the two lived side by side and never talked, and if you had a Flame game and wanted light, shadows and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. This article explains how it works and then walks through a small game built on it, from an empty project to a boat collecting buoys on a lake. +I built it to see whether that holds up in a real game. Flame is the 2D engine a lot of Flutter developers already write games with, and for a while it and flutter3d lived side by side without talking to each other. If you had a Flame game and wanted light, shadows and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. Below is how it works, and then a small game built on it, from an empty project to a boat collecting buoys on a lake. ## The idea: each engine keeps its job -The bridge does not reimplement either renderer, and it does not ask you to move your game into a new framework. Flame keeps running the game: components, collisions, input, effects, the clock. flutter3d draws a 3D world under it. The bridge's job is to keep the two in agreement about where things are, when a frame happens, and what the player pressed. - -Four decisions hold the whole thing together. +Both renderers stay as they are, and your game stays in Flame. Flame keeps running it: components, collisions, input, effects, the clock. flutter3d draws a 3D world under it. The bridge keeps the two in agreement about where things are, when a frame happens and what the player pressed, and it rests on four decisions. ### 1. Two layers in one Stack @@ -63,7 +61,7 @@ On a phone this pays off. The on-screen stick is a plain Flame `JoystickComponen With those four pieces in place, most of the package is components that carry one kind of thing across. -`Object3dComponent` is the workhorse: a Flame `PositionComponent` that owns a scene node. Position, angle, scale, visibility, opacity and tint cross. Flame's effects reach the scene in the frame they happen, a nested component lands where Flame draws it, and `elevation` lifts a component off its plane. It also exposes `visual`, a node under it that the game can turn freely and the bridge leaves alone, which is how the jet in River Sortie banks without Flame knowing. +Most of the work is done by `Object3dComponent`, a Flame `PositionComponent` that owns a scene node. Position, angle, scale, visibility, opacity and tint cross. Flame's effects reach the scene in the frame they happen, a nested component lands where Flame draws it, and `elevation` lifts a component off its plane. It also exposes `visual`, a node under it that the game can turn freely and the bridge leaves alone, which is how the jet in River Sortie banks without Flame knowing. For physics there are two directions, and River Sortie and the arcade demo use one each. In River Sortie everything is decided by Flame: hitboxes are Flame hitboxes and `onCollisionStart` says who hit whom, while the 3D layer only draws. In the arcade demo collisions are computed by flutter3d's 3D physics, and `CollisionBridge` relays them to Flame's `CollisionCallbacks`, so a ram computed in 3D arrives in Flame as a normal `onCollisionStart`. @@ -85,7 +83,7 @@ The game also showed me what the bridge was missing, and most of what's listed a ## Tutorial: a boat, a lake and some buoys -Now the practical part. We'll build a small game where you steer a boat across a lake and collect buoys. It's about 170 lines, and it touches every piece described above: a game that owns a 3D world, components with bodies in the scene, Flame collisions, the input bridge, a chase camera and a fixed step. +We'll build a small game where you steer a boat across a lake and collect buoys. It's about 170 lines and uses every piece described above: a game that owns a 3D world, components with bodies in the scene, Flame collisions, the input bridge, a chase camera and a fixed step. ![The finished tutorial: a boat about to collect a buoy, with Flame's score text over the 3D scene](https://flutter3d.pleion.dev/assets/articles/medium-flame/07_buoy_run.png) From 93ba6149ed6d21c8bd6f4b37cb2d850b7e08d35f Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:19:49 +0300 Subject: [PATCH 020/141] The Medium article takes the serial comma --- site/assets/articles/medium-flame/article.md | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/site/assets/articles/medium-flame/article.md b/site/assets/articles/medium-flame/article.md index 4909e6042..d5375ad31 100644 --- a/site/assets/articles/medium-flame/article.md +++ b/site/assets/articles/medium-flame/article.md @@ -8,11 +8,11 @@ In my [previous article](https://medium.com/@dzolotov/i-wrote-a-3d-game-engine-f The screenshot above is River Sortie, a River Raid-style shooter you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. -I built it to see whether that holds up in a real game. Flame is the 2D engine a lot of Flutter developers already write games with, and for a while it and flutter3d lived side by side without talking to each other. If you had a Flame game and wanted light, shadows and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. Below is how it works, and then a small game built on it, from an empty project to a boat collecting buoys on a lake. +I built it to see whether that holds up in a real game. Flame is the 2D engine a lot of Flutter developers already write games with, and for a while it and flutter3d lived side by side without talking to each other. If you had a Flame game and wanted light, shadows, and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. Below is how it works, and then a small game built on it, from an empty project to a boat collecting buoys on a lake. ## The idea: each engine keeps its job -Both renderers stay as they are, and your game stays in Flame. Flame keeps running it: components, collisions, input, effects, the clock. flutter3d draws a 3D world under it. The bridge keeps the two in agreement about where things are, when a frame happens and what the player pressed, and it rests on four decisions. +Both renderers stay as they are, and your game stays in Flame. Flame keeps running it: components, collisions, input, effects, the clock. flutter3d draws a 3D world under it. The bridge keeps the two in agreement about where things are, when a frame happens, and what the player pressed, and it rests on four decisions. ### 1. Two layers in one Stack @@ -51,7 +51,7 @@ Who writes and who reads is chosen once, when the component is created, with `Sy ### 4. One input -`FlameInputBridge` takes the keys, drags, joystick and buttons Flame receives and writes them into `Bindings` and `InputState` from `flutter3d_game`, the same objects a native flutter3d game reads. The bridge has no key table of its own. If a player remaps jump in your settings menu, the remap has to work in the Flame build too, and two tables that disagree look exactly like a remap that silently did nothing. +`FlameInputBridge` takes the keys, drags, joystick, and buttons Flame receives and writes them into `Bindings` and `InputState` from `flutter3d_game`, the same objects a native flutter3d game reads. The bridge has no key table of its own. If a player remaps jump in your settings menu, the remap has to work in the Flame build too, and two tables that disagree look exactly like a remap that silently did nothing. ![One key, read by both engines: Flame's key handler takes the press, and the flutter3d side sees the same action held](https://flutter3d.pleion.dev/assets/articles/medium-flame/06_input_bridge.png) @@ -61,7 +61,7 @@ On a phone this pays off. The on-screen stick is a plain Flame `JoystickComponen With those four pieces in place, most of the package is components that carry one kind of thing across. -Most of the work is done by `Object3dComponent`, a Flame `PositionComponent` that owns a scene node. Position, angle, scale, visibility, opacity and tint cross. Flame's effects reach the scene in the frame they happen, a nested component lands where Flame draws it, and `elevation` lifts a component off its plane. It also exposes `visual`, a node under it that the game can turn freely and the bridge leaves alone, which is how the jet in River Sortie banks without Flame knowing. +Most of the work is done by `Object3dComponent`, a Flame `PositionComponent` that owns a scene node. Position, angle, scale, visibility, opacity, and tint cross. Flame's effects reach the scene in the frame they happen, a nested component lands where Flame draws it, and `elevation` lifts a component off its plane. It also exposes `visual`, a node under it that the game can turn freely and the bridge leaves alone, which is how the jet in River Sortie banks without Flame knowing. For physics there are two directions, and River Sortie and the arcade demo use one each. In River Sortie everything is decided by Flame: hitboxes are Flame hitboxes and `onCollisionStart` says who hit whom, while the 3D layer only draws. In the arcade demo collisions are computed by flutter3d's 3D physics, and `CollisionBridge` relays them to Flame's `CollisionCallbacks`, so a ram computed in 3D arrives in Flame as a normal `onCollisionStart`. @@ -73,17 +73,17 @@ For cameras, `ChaseCamera` follows a bridged component in perspective and can sh ## How River Sortie uses it -River Sortie is the biggest user of the bridge, and it shaped a lot of it. The game mixes in `HasFlutter3d`, so it owns its 3D world: scene, device, camera and renderer are fields of the game, and it builds the river once in `onOpen3d`. It also mixes in `HasFixedStep`, so its logic runs in fixed steps and a second of play comes out the same at any frame rate. Together with a seeded river generator, the river is the same on every run. +River Sortie is the biggest user of the bridge, and it shaped a lot of it. The game mixes in `HasFlutter3d`, so it owns its 3D world: scene, device, camera, and renderer are fields of the game, and it builds the river once in `onOpen3d`. It also mixes in `HasFixedStep`, so its logic runs in fixed steps and a second of play comes out the same at any frame rate. Together with a seeded river generator, the river is the same on every run. -Every tanker, helicopter and fuel depot is an `Object3dComponent` on one `BridgePlane.ground()` laid over the water. Its Flame position is `x` across the river and `y` along it, and helicopters get an elevation that lifts them to flight height. The banks aren't hitboxes at all: the river generator answers whether any point is water or land, and the jet asks it every step. Stretches of river are built ahead of the jet and released behind it by `ChunkStreamer`. Shots are drawn as instances of one mesh, fire and smoke are 3D particles, reeds on the banks and the flash of an explosion are Flame sprites standing in the scene, and explosions are heard from where they happen. +Every tanker, helicopter, and fuel depot is an `Object3dComponent` on one `BridgePlane.ground()` laid over the water. Its Flame position is `x` across the river and `y` along it, and helicopters get an elevation that lifts them to flight height. The banks aren't hitboxes at all: the river generator answers whether any point is water or land, and the jet asks it every step. Stretches of river are built ahead of the jet and released behind it by `ChunkStreamer`. Shots are drawn as instances of one mesh, fire and smoke are 3D particles, reeds on the banks and the flash of an explosion are Flame sprites standing in the scene, and explosions are heard from where they happen. ![A bridge hit in the middle breaks in two, each half going down on its own pier. The "+500" is Flame's text, drawn over a point in the scene](https://flutter3d.pleion.dev/assets/articles/medium-flame/08_river_bridge_down.png) -The game also showed me what the bridge was missing, and most of what's listed above arrived because River Sortie needed it: the fixed step, particles, Flame sprites in the scene and positional sound. +The game also showed me what the bridge was missing, and most of what's listed above arrived because River Sortie needed it: the fixed step, particles, Flame sprites in the scene, and positional sound. -## Tutorial: a boat, a lake and some buoys +## Tutorial: a boat, a lake, and some buoys -We'll build a small game where you steer a boat across a lake and collect buoys. It's about 170 lines and uses every piece described above: a game that owns a 3D world, components with bodies in the scene, Flame collisions, the input bridge, a chase camera and a fixed step. +We'll build a small game where you steer a boat across a lake and collect buoys. It's about 170 lines and uses every piece described above: a game that owns a 3D world, components with bodies in the scene, Flame collisions, the input bridge, a chase camera, and a fixed step. ![The finished tutorial: a boat about to collect a buoy, with Flame's score text over the 3D scene](https://flutter3d.pleion.dev/assets/articles/medium-flame/07_buoy_run.png) @@ -102,7 +102,7 @@ dependencies: ### Step 2. Switch on Flutter GPU -flutter3d draws through Flutter GPU, and Flutter GPU is switched on per app, in the platform settings. Skip this and the app starts, opens a window and draws no 3D at all, because it can't load its shader library. +flutter3d draws through Flutter GPU, and Flutter GPU is switched on per app, in the platform settings. Skip this and the app starts, opens a window, and draws no 3D at all, because it can't load its shader library. On macOS and iOS, add to `macos/Runner/Info.plist` and `ios/Runner/Info.plist`: @@ -123,7 +123,7 @@ On Android, inside `` in `AndroidManifest.xml`: ### Step 3. A game that owns a 3D world -The game is a normal `FlameGame` with four mixins. `HasFlutter3d` gives it a scene, a device and a camera, `HasFixedStep` gives it fixed steps, and the other two are plain Flame. +The game is a normal `FlameGame` with four mixins. `HasFlutter3d` gives it a scene, a device, and a camera, `HasFixedStep` gives it fixed steps, and the other two are plain Flame. ```dart class BuoyRun extends FlameGame @@ -183,7 +183,7 @@ The world is built in `onOpen3d`, which runs once, after both the game has loade } ``` -The meshes come from code: `PlaneShape`, `CuboidShape` and `SphereShape` are flutter3d's built-in shapes. A glTF model works the same way, as the node a component owns. The score is a regular Flame `TextComponent`, drawn on Flame's layer over the scene. +The meshes come from code: `PlaneShape`, `CuboidShape`, and `SphereShape` are flutter3d's built-in shapes. A glTF model works the same way, as the node a component owns. The score is a regular Flame `TextComponent`, drawn on Flame's layer over the scene. ### Step 4. The widget @@ -212,7 +212,7 @@ Because the game has `HasFlutter3d`, the widget needs nothing but the game. ### Step 5. Components with a body in the scene -A buoy is an `Object3dComponent`: a Flame component with a size, an anchor and a hitbox, plus the scene node it owns. +A buoy is an `Object3dComponent`: a Flame component with a size, an anchor, and a hitbox, plus the scene node it owns. ```dart class Buoy extends Object3dComponent { @@ -348,7 +348,7 @@ These are the checks I'd go through first: ## What it doesn't do yet -Flame is always on top. The layers don't interleave by depth, so HUDs, maps and sprites over the scene work, but a 3D pillar hiding a 2D sprite behind it does not. And Flame's collision callback carries no contact normal or depth, so when you need them you read them on the flutter3d side. +Flame is always on top. The layers don't interleave by depth, so HUDs, maps, and sprites over the scene work, but a 3D pillar hiding a 2D sprite behind it does not. And Flame's collision callback carries no contact normal or depth, so when you need them you read them on the flutter3d side. ## Try it From c2cd17821ddf974a7e4ddd8c1cfc022eeab8b6ec Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:20:32 +0300 Subject: [PATCH 021/141] An agent can play the level it is editing Play leaves the editor application for a package of its own, flutter3d_editor_play: plain Dart with dart:io, since it starts a process and flutter3d_editor_core is written never to. FlutterRun, projectRootFor and pushLevel move there with Watched in place of ValueNotifier, beside flutterDevices, which reads flutter devices --machine, and a fake flutter run for tests. The editor's MCP server runs the same Play: play waits until the game is up or has failed, and play_status, play_swap, play_stop and play_devices follow it; a save while the game runs sends it the level. The editor's Play panel picks the device between runs. --- ARCHITECTURE.md | 6 +- README.md | 2 +- apps/flutter3d_editor/lib/main.dart | 9 +- .../lib/src/play/play_screen.dart | 195 ++++++++---- apps/flutter3d_editor/pubspec.yaml | 6 + .../test/play_screen_test.dart | 96 ++++++ packages/flutter3d_editor_mcp/CHANGELOG.md | 10 + packages/flutter3d_editor_mcp/README.md | 1 + .../lib/flutter3d_editor_mcp.dart | 1 + .../lib/src/editor_server.dart | 4 + .../lib/src/editor_session.dart | 24 +- .../lib/src/editor_tools.dart | 97 ++++++ .../lib/src/play_session.dart | 172 ++++++++++ packages/flutter3d_editor_mcp/pubspec.yaml | 4 + .../flutter3d_editor_mcp/test/play_test.dart | 221 +++++++++++++ .../flutter3d_editor_mcp/test/tools_test.dart | 5 + packages/flutter3d_editor_play/CHANGELOG.md | 12 + packages/flutter3d_editor_play/LICENSE | 21 ++ packages/flutter3d_editor_play/README.md | 48 +++ .../analysis_options.yaml | 11 + .../lib/flutter3d_editor_play.dart | 12 + .../lib/src/devices.dart | 86 +++++ .../lib/src/flutter_run.dart | 293 ++++++++++++++++++ .../lib/src/watched.dart | 23 ++ .../flutter3d_editor_play/lib/testing.dart | 105 +++++++ packages/flutter3d_editor_play/pubspec.yaml | 36 +++ .../test/devices_test.dart | 88 ++++++ .../test/flutter_run_test.dart | 140 +-------- .../test/level_push_test.dart | 6 +- pubspec.yaml | 1 + site/content/quickstart.md | 4 +- site/content/reference/testing.md | 12 +- tasks/0.9-engine-roadmap.md | 9 +- tool/structure/repository.dart | 4 + 34 files changed, 1537 insertions(+), 227 deletions(-) create mode 100644 apps/flutter3d_editor/test/play_screen_test.dart create mode 100644 packages/flutter3d_editor_mcp/lib/src/play_session.dart create mode 100644 packages/flutter3d_editor_mcp/test/play_test.dart create mode 100644 packages/flutter3d_editor_play/CHANGELOG.md create mode 100644 packages/flutter3d_editor_play/LICENSE create mode 100644 packages/flutter3d_editor_play/README.md create mode 100644 packages/flutter3d_editor_play/analysis_options.yaml create mode 100644 packages/flutter3d_editor_play/lib/flutter3d_editor_play.dart create mode 100644 packages/flutter3d_editor_play/lib/src/devices.dart create mode 100644 packages/flutter3d_editor_play/lib/src/flutter_run.dart create mode 100644 packages/flutter3d_editor_play/lib/src/watched.dart create mode 100644 packages/flutter3d_editor_play/lib/testing.dart create mode 100644 packages/flutter3d_editor_play/pubspec.yaml create mode 100644 packages/flutter3d_editor_play/test/devices_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 51ccc1555..cbcdbdf8b 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -238,6 +238,7 @@ point of §3.3. | `flutter3d_editor_core` | The headless half of a level editor: the document being changed and undone, the handles a pointer hits, the palette a level builds out of itself, the project a template becomes; `LevelScene`, which turns a level into brush meshes, materials, lights and probes with no Flutter; the seeded level generators; and `LightOptimizer`, which finds fewer lights that light a level the way it was lit. Plain Dart | | `flutter3d_editor_widgets` | Editor controls the modeller and the level editor share instead of each keeping its own copy — `ui-27`'s own package: `SectionLabel`, `NumberField`, `ColorField`, `RangeSliderField`, `EnumField`, `TextureSlotRow`, `TexturePathField`, `ColorSwatchField`, `HintTextBox`/`NumbersRow`, `FieldRow` and `EditorWidgetsTheme` so far | | `flutter3d_editor_mcp` | The same editor offered to an agent: `EditorCommand` as a table of MCP tools over stdio, one document per process, plus the two verbs a caller with no screen needs — a flat listing, and the validator. Plain Dart | +| `flutter3d_editor_play` | Play from a level editor: the game a level belongs to run with `flutter run --machine`, swapped, restarted and stopped, its devices listed, and a saved level sent to it. What the editor's Play button and `flutter3d_editor_mcp`'s `play` tools both run. Plain Dart with `dart:io`, which is why it is not in `flutter3d_editor_core` | | `flutter3d_mcp_kit` | What every MCP server here shares: a tool paired with its handler, a server that is a list of them over one session, the two shapes of answer, and a loopback HTTP transport an open application offers its session over. Plain Dart | | `flutter3d_sim_mcp` | A level an agent plays blind, of whatever `HeadlessGame` a host hands it — step, read back, digest, hand over the run — and many seeded playtests in isolates; and a rendered frame of a level offered to an agent: drawn with no GPU in one of the renderer's debug views, one pixel read back unclamped, the passes the frame graph ran. Names no genre | | `flutter3d_build` | The converter behind `dart run flutter3d_build:convert` and the build hook that runs it on every build, and the bakes that need a renderer or a mesh editor ahead of time: levels of detail, impostors, cluster tables, a file per device class, paged splat captures, six-way smoke sheets, and the light optimizer's command. That is why it depends on `flutter3d_cpu`, `flutter3d_mesh`, `flutter3d_model_core`, `flutter3d_editor_core` and `flutter3d_sim`. Not a dependency of the engine: no game that draws a frame runs it. Plain Dart | @@ -2688,7 +2689,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10860 tests** across 41 packages and 10 applications | +| Unit tests | **10875 tests** across 42 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | @@ -3711,7 +3712,8 @@ what went out at 0.4.2. 4. `flutter3d`, `flutter3d_model_core` 5. `flutter3d_impeller`, `flutter3d_webgl`, `flutter3d_webgpu`, `flutter3d_cpu`, `flutter3d_sim` -6. `flutter3d_app`, `flutter3d_editor_core`, `flutter3d_net`, `flutter3d_lab` +6. `flutter3d_app`, `flutter3d_editor_core`, `flutter3d_editor_play`, + `flutter3d_net`, `flutter3d_lab` 7. `flutter3d_game`, `flutter3d_stereo`, `flutter3d_testing`, `flutter3d_editor_mcp`, `flutter3d_model_mcp`, `flutter3d_net_webrtc` 8. `flutter3d_game_shooter`, `flutter3d_game_platformer`, `flutter3d_game_racing`, diff --git a/README.md b/README.md index 7b9e5dd49..fe80485c2 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10860 tests across forty-one packages and nine applications. The +There are 10875 tests across forty-two packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/apps/flutter3d_editor/lib/main.dart b/apps/flutter3d_editor/lib/main.dart index dc2b81154..779a6a081 100644 --- a/apps/flutter3d_editor/lib/main.dart +++ b/apps/flutter3d_editor/lib/main.dart @@ -32,6 +32,7 @@ import 'package:flutter3d/flutter3d.dart' hide Material; import 'package:flutter3d_app/flutter3d_app.dart'; import 'package:flutter3d_app/native.dart'; import 'package:flutter3d_editor_core/flutter3d_editor_core.dart'; +import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; import 'package:flutter3d_game/flutter3d_game.dart'; import 'package:flutter3d_sim/flutter3d_sim.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -50,8 +51,6 @@ import 'src/fly_camera.dart'; import 'src/light_plan_dialog.dart'; import 'src/material_panel.dart'; import 'src/open_run_channel.dart'; -import 'src/play/flutter_run.dart'; -import 'src/play/level_push.dart'; import 'src/play/live_material.dart'; import 'src/play/play_screen.dart'; import 'src/playtest_report_screen.dart'; @@ -1643,11 +1642,7 @@ class _EditorScreenState extends State /// Opens the play panel on the project [state]'s level belongs to, /// starting a run of it when none is going. Future _play(EditorReady state) async { - final root = projectRootFor( - state.editing.path, - hasPubspec: (String directory) => - File('$directory${Platform.pathSeparator}pubspec.yaml').existsSync(), - ); + final root = projectRootOnDisk(state.editing.path); if (root == null) { _changed( 'this level is not inside a Flutter project, so there is ' diff --git a/apps/flutter3d_editor/lib/src/play/play_screen.dart b/apps/flutter3d_editor/lib/src/play/play_screen.dart index 005e74301..d9e42624c 100644 --- a/apps/flutter3d_editor/lib/src/play/play_screen.dart +++ b/apps/flutter3d_editor/lib/src/play/play_screen.dart @@ -1,11 +1,10 @@ /// `HR5`'s panel: the project running, its console, and the four things /// done to it — start, hot reload, hot restart, stop — plus the timeline of -/// the game it started. +/// the game it started and the device it runs on. library; import 'package:flutter/material.dart'; - -import 'flutter_run.dart'; +import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; /// Shows [run] and drives it. /// @@ -13,80 +12,106 @@ import 'flutter_run.dart'; /// as the project is open, so closing this screen leaves the game running and /// opening it again finds the same console — the way a terminal tab with /// `flutter run` in it would behave. -final class PlayScreen extends StatelessWidget { - const PlayScreen({super.key, required this.run, required this.onTimeline}); +final class PlayScreen extends StatefulWidget { + const PlayScreen({ + super.key, + required this.run, + required this.onTimeline, + this.devices = flutterDevices, + }); final FlutterRun run; /// Opens the timeline panel on the running game's VM service. final void Function(String vmService) onTimeline; + /// What the device picker offers; `flutter devices --machine` by default. + final Future> Function() devices; + + @override + State createState() => _PlayScreenState(); +} + +class _PlayScreenState extends State { static const Color _background = Color(0xFF14161A); static const Color _text = Color(0xFFE6EAF0); + /// Asked once per opening of the panel: the tool takes a second or two to + /// answer, and a phone plugged in afterwards is found by opening it again. + late final Future> _devices = widget.devices(); + @override - Widget build(BuildContext context) => ValueListenableBuilder( - valueListenable: run.state, - builder: (BuildContext context, PlayState state, _) { - final running = state is PlayRunning; - final going = state is PlayStarting || running; - return Scaffold( - backgroundColor: _background, - appBar: AppBar( - title: Text(run.projectRoot, overflow: TextOverflow.ellipsis), - actions: [ - IconButton( - key: const ValueKey('play.start'), - icon: Icon(going ? Icons.stop : Icons.play_arrow), - tooltip: going ? 'Stop' : 'Run the project', - onPressed: going ? run.stop : run.start, - ), - IconButton( - key: const ValueKey('play.reload'), - icon: const Icon(Icons.bolt), - tooltip: 'Hot reload', - onPressed: running ? run.hotReload : null, - ), - IconButton( - key: const ValueKey('play.restart'), - icon: const Icon(Icons.restart_alt), - tooltip: 'Hot restart', - onPressed: running ? run.hotRestart : null, - ), - IconButton( - key: const ValueKey('play.timeline'), - icon: const Icon(Icons.podcasts), - tooltip: 'Timeline', - onPressed: switch (state) { - PlayRunning(:final vmService) => () => onTimeline(vmService), - _ => null, - }, - ), - ], - ), - body: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(12, 8, 12, 4), - child: Text( - switch (state) { - PlayIdle() => 'Not running', - PlayStarting(:final message) => message, - PlayRunning(:final vmService) => 'Running, $vmService', - PlayStopped(:final exitCode) => 'Stopped ($exitCode)', + Widget build(BuildContext context) { + final run = widget.run; + return StreamBuilder( + stream: run.state.changes, + initialData: run.state.value, + builder: (BuildContext context, _) { + final state = run.state.value; + final running = state is PlayRunning; + final going = state is PlayStarting || running; + return Scaffold( + backgroundColor: _background, + appBar: AppBar( + title: Text(run.projectRoot, overflow: TextOverflow.ellipsis), + actions: [ + _picker(enabled: !going), + IconButton( + key: const ValueKey('play.start'), + icon: Icon(going ? Icons.stop : Icons.play_arrow), + tooltip: going ? 'Stop' : 'Run the project', + onPressed: going ? run.stop : run.start, + ), + IconButton( + key: const ValueKey('play.reload'), + icon: const Icon(Icons.bolt), + tooltip: 'Hot reload', + onPressed: running ? run.hotSwap : null, + ), + IconButton( + key: const ValueKey('play.restart'), + icon: const Icon(Icons.restart_alt), + tooltip: 'Hot restart', + onPressed: running ? run.hotRestart : null, + ), + IconButton( + key: const ValueKey('play.timeline'), + icon: const Icon(Icons.podcasts), + tooltip: 'Timeline', + onPressed: switch (state) { + PlayRunning(:final vmService) => () => widget.onTimeline( + vmService, + ), + _ => null, }, - key: const ValueKey('play.status'), - style: const TextStyle(color: _text), ), - ), - Expanded( - child: ValueListenableBuilder>( - valueListenable: run.console, - // Reversed, so the newest line sits at the bottom and the list - // stays there as lines arrive, as a terminal does. - builder: (BuildContext context, List lines, _) => - ListView.builder( + ], + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(12, 8, 12, 4), + child: Text( + switch (state) { + PlayIdle() => 'Not running', + PlayStarting(:final message) => message, + PlayRunning(:final vmService) => 'Running, $vmService', + PlayStopped(:final exitCode) => 'Stopped ($exitCode)', + }, + key: const ValueKey('play.status'), + style: const TextStyle(color: _text), + ), + ), + Expanded( + child: StreamBuilder>( + stream: run.console.changes, + initialData: run.console.value, + // Reversed, so the newest line sits at the bottom and the + // list stays there as lines arrive, as a terminal does. + builder: (BuildContext context, _) { + final lines = run.console.value; + return ListView.builder( reverse: true, padding: const EdgeInsets.symmetric(horizontal: 12), itemCount: lines.length, @@ -99,11 +124,45 @@ final class PlayScreen extends StatelessWidget { fontSize: 12, ), ), - ), + ); + }, + ), ), - ), - ], + ], + ), + ); + }, + ); + } + + /// Which device the next run starts on. Held still while a run is going: + /// the device is `flutter run`'s argument, and changing it means a new run. + Widget _picker({required bool enabled}) => FutureBuilder>( + future: _devices, + builder: (BuildContext context, AsyncSnapshot> it) { + final devices = it.data ?? const []; + final chosen = widget.run.device; + return DropdownButton( + key: const ValueKey('play.device'), + value: devices.any((d) => d.id == chosen) ? chosen : null, + dropdownColor: _background, + style: const TextStyle(color: _text, fontSize: 13), + underline: const SizedBox.shrink(), + hint: Text( + it.hasError ? 'devices unavailable' : 'default device', + style: const TextStyle(color: _text, fontSize: 13), ), + items: >[ + const DropdownMenuItem(child: Text('default device')), + for (final device in devices) + DropdownMenuItem( + value: device.id, + child: Text(device.name), + ), + ], + onChanged: enabled + ? (String? id) => setState(() => widget.run.device = id) + : null, ); }, ); diff --git a/apps/flutter3d_editor/pubspec.yaml b/apps/flutter3d_editor/pubspec.yaml index 3fc5c6d14..a0bbf00ec 100644 --- a/apps/flutter3d_editor/pubspec.yaml +++ b/apps/flutter3d_editor/pubspec.yaml @@ -46,6 +46,12 @@ dependencies: flutter3d_editor_widgets: path: ../../packages/flutter3d_editor_widgets + # Play: the project run with `flutter run --machine`, its devices, and a + # saved level sent to it. Shared with `flutter3d_editor_mcp`, so an agent's + # Play is this toolbar's. + flutter3d_editor_play: + path: ../../packages/flutter3d_editor_play + # Level geometry to mesh nodes. `LevelLoader.build` is what an editor needs # and what a game never did: a level drawn from a document in memory rather # than from an asset. And `writeFileAtomically` through `native.dart`: this diff --git a/apps/flutter3d_editor/test/play_screen_test.dart b/apps/flutter3d_editor/test/play_screen_test.dart new file mode 100644 index 000000000..20e7ff893 --- /dev/null +++ b/apps/flutter3d_editor/test/play_screen_test.dart @@ -0,0 +1,96 @@ +/// `HR5`'s panel over a run of the test's own making. +/// +/// flutter test test/play_screen_test.dart +library; + +import 'package:flutter/material.dart'; +import 'package:flutter3d_editor/src/play/play_screen.dart'; +import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; +import 'package:flutter3d_editor_play/testing.dart'; +import 'package:flutter_test/flutter_test.dart'; + +const List _devices = [ + FlutterDevice(id: 'macos', name: 'macOS', platform: 'darwin'), + FlutterDevice(id: 'R5CX', name: 'Galaxy A55', platform: 'android-arm64'), +]; + +Future _pump( + WidgetTester tester, + FlutterRun run, { + List? timelines, +}) => tester.pumpWidget( + MaterialApp( + home: PlayScreen( + run: run, + onTimeline: timelines?.add ?? (String _) {}, + devices: () async => _devices, + ), + ), +); + +IconButton _button(WidgetTester tester, String key) => + tester.widget(find.byKey(ValueKey(key))); + +void main() { + testWidgets('the panel offers a reload only once the game is running', ( + WidgetTester tester, + ) async { + final it = fakeFlutterRun(); + final timelines = []; + await it.run.start(); + await _pump(tester, it.run, timelines: timelines); + + expect(_button(tester, 'play.reload').onPressed, isNull); + expect(_button(tester, 'play.timeline').onPressed, isNull); + + it.tool.running(); + await tester.pump(); + await tester.pump(); + + expect(find.text('Running, ws://game'), findsOneWidget); + await tester.tap(find.byKey(const ValueKey('play.reload'))); + await tester.pump(); + expect(it.tool.sent.single['method'], 'app.restart'); + + await tester.tap(find.byKey(const ValueKey('play.timeline'))); + expect(timelines, ['ws://game']); + }); + + testWidgets('a device picked between runs is the next run\'s', ( + WidgetTester tester, + ) async { + final it = fakeFlutterRun(device: null); + await _pump(tester, it.run); + await tester.pump(); + + await tester.tap(find.byKey(const ValueKey('play.device'))); + await tester.pumpAndSettle(); + await tester.tap(find.text('Galaxy A55').last); + await tester.pumpAndSettle(); + expect(it.run.device, 'R5CX'); + + await tester.tap(find.byKey(const ValueKey('play.start'))); + await tester.pump(); + expect(it.started.single, [ + '/game', + 'run', + '--machine', + '-d', + 'R5CX', + ]); + }); + + testWidgets('and is held still while one is going', ( + WidgetTester tester, + ) async { + final it = fakeFlutterRun(); + await it.run.start(); + await _pump(tester, it.run); + await tester.pump(); + + final picker = tester.widget>( + find.byKey(const ValueKey('play.device')), + ); + expect(picker.onChanged, isNull); + }); +} diff --git a/packages/flutter3d_editor_mcp/CHANGELOG.md b/packages/flutter3d_editor_mcp/CHANGELOG.md index 12f094002..524e7a099 100644 --- a/packages/flutter3d_editor_mcp/CHANGELOG.md +++ b/packages/flutter3d_editor_mcp/CHANGELOG.md @@ -1,3 +1,13 @@ +## Unreleased + +- **An agent can play the level it is editing.** `play` runs the game the + level belongs to through `flutter3d_editor_play`, the editor's own Play, + and waits until it is up or has failed; `play_status`, `play_swap`, + `play_stop` and `play_devices` follow it. While it runs, `save` sends the + level to the game, which takes it without starting over. +- **`EditorSession.save` returns a `Future`**, for that send. + `EditorSession(play:)` takes a `PlaySession` of the caller's making. + ## 0.8.0 Twenty-one tools, where 0.7 had seventeen, and the server can see the level. diff --git a/packages/flutter3d_editor_mcp/README.md b/packages/flutter3d_editor_mcp/README.md index dc3b4131e..5dd4ef9f9 100644 --- a/packages/flutter3d_editor_mcp/README.md +++ b/packages/flutter3d_editor_mcp/README.md @@ -47,6 +47,7 @@ one name in the undo stack, and come back out under the same key. What an edit | `save` | Write it out, or say why it will not | | `screenshot` | A flat picture of the level from a camera you may name | | `report` | What that camera sees of every brush, light and entity, and what is in the way | +| `play`, `play_status`, `play_swap`, `play_stop`, `play_devices` | Run the game the level belongs to, see its console, swap its code, stop it, and choose the device — the editor's Play button, from [`flutter3d_editor_play`](../flutter3d_editor_play). While it runs, `save` sends the level to it | Ten of those are the document commands. The two that are not, `list` and `validate`, were missing from every sketch of this, and missing in the same diff --git a/packages/flutter3d_editor_mcp/lib/flutter3d_editor_mcp.dart b/packages/flutter3d_editor_mcp/lib/flutter3d_editor_mcp.dart index 020584292..e6c55b820 100644 --- a/packages/flutter3d_editor_mcp/lib/flutter3d_editor_mcp.dart +++ b/packages/flutter3d_editor_mcp/lib/flutter3d_editor_mcp.dart @@ -38,3 +38,4 @@ export 'src/editor_server.dart'; export 'src/editor_session.dart'; export 'src/editor_tools.dart'; export 'src/level_view.dart'; +export 'src/play_session.dart'; diff --git a/packages/flutter3d_editor_mcp/lib/src/editor_server.dart b/packages/flutter3d_editor_mcp/lib/src/editor_server.dart index dc31ff8a0..3b6e4d8b1 100644 --- a/packages/flutter3d_editor_mcp/lib/src/editor_server.dart +++ b/packages/flutter3d_editor_mcp/lib/src/editor_server.dart @@ -61,4 +61,8 @@ worth calling before `save`. `undo` goes back sixty-four steps. `screenshot` draws the level in software, untextured, from a camera you may name; `report` says for every brush, light and entity how much of it that camera sees and what is in the way. Look before and after you change things. + +`play` runs the game this level belongs to. While it runs, every `save` goes +to it and the game takes the level without starting over; `play_status` +shows its console, `play_swap` puts changed code into it. '''; diff --git a/packages/flutter3d_editor_mcp/lib/src/editor_session.dart b/packages/flutter3d_editor_mcp/lib/src/editor_session.dart index 1a768c1d5..df40197d7 100644 --- a/packages/flutter3d_editor_mcp/lib/src/editor_session.dart +++ b/packages/flutter3d_editor_mcp/lib/src/editor_session.dart @@ -7,6 +7,7 @@ import 'package:flutter3d_sim/flutter3d_sim.dart'; import 'package:vector_math/vector_math.dart'; import 'level_view.dart'; +import 'play_session.dart'; // What a tool call did, and the sentence to say about it — the one `Answer` // every server here shares, and the `PictureAnswer` that carries a PNG @@ -30,7 +31,8 @@ export 'package:flutter3d_mcp_kit/flutter3d_mcp_kit.dart' /// sixty-four snapshots behind it would quietly become sixty-four snapshots of /// somebody else's work. final class EditorSession { - EditorSession(this.editing); + EditorSession(this.editing, {PlaySession? play}) + : play = play ?? PlaySession(levelPath: editing.path); /// Reads the document at [path]. /// @@ -44,6 +46,9 @@ final class EditorSession { final Editing editing; + /// The game this level belongs to, run from here. + final PlaySession play; + /// What this editor writes into `generatedBy` when it takes ownership of a /// copy. The application writes `apps/flutter3d_editor` for the same reason: /// a document should name what last wrote it. @@ -275,7 +280,10 @@ final class EditorSession { /// opened, changed and saved *somewhere else*, and the copy claims itself — /// a file saved beside the original still naming the generator is a file that /// invites somebody to regenerate it. - Answer save(String? path) { + /// + /// Written over the level the game is playing, it goes to the game too + /// when one is running, as the editor application's save does. + Future save(String? path) async { final elsewhere = path != null && path != editing.path; if (!elsewhere && !editing.mayOverwrite) { return ( @@ -287,10 +295,14 @@ final class EditorSession { ); } final to = path ?? editing.path; - File( - to, - ).writeAsStringSync(editing.write(claiming: elsewhere ? author : null)); + final document = editing.write(claiming: elsewhere ? author : null); + File(to).writeAsStringSync(document); editing.saved(); - return (did: true, says: 'written to $to'); + // Not a copy: the running game plays the original, not the file beside it. + final game = elsewhere ? null : await play.sendSaved(document); + return ( + did: true, + says: game == null ? 'written to $to' : 'written to $to; $game', + ); } } diff --git a/packages/flutter3d_editor_mcp/lib/src/editor_tools.dart b/packages/flutter3d_editor_mcp/lib/src/editor_tools.dart index f9d5481cb..78827cb01 100644 --- a/packages/flutter3d_editor_mcp/lib/src/editor_tools.dart +++ b/packages/flutter3d_editor_mcp/lib/src/editor_tools.dart @@ -111,6 +111,102 @@ Answer Function(EditorSession, Map) _command(String name) => return session.run(command); }; +/// `HR5`: the game the level belongs to, run and driven — the editor +/// application's Play toolbar, as tools. +List get _playTools => [ + _told( + Tool( + name: 'play', + description: + 'Run the game this level belongs to (the Flutter project above the ' + 'level file) with flutter run, and wait until it is up or has ' + 'failed; answers with where it runs and the last lines it printed. ' + 'Once it runs, every save of this level is sent to it and the game ' + 'takes it without starting over. Does nothing if it is already ' + 'running on that device.', + inputSchema: ObjectSchema( + properties: { + 'device': StringSchema( + description: + 'an id play_devices lists; leave out for the tool\'s own choice', + ), + }, + ), + ), + (EditorSession session, Map arguments) { + final device = arguments['device']; + return session.play.start(device: device is String ? device : null); + }, + ), + _told( + Tool( + name: 'play_status', + description: + 'Where the game started by play is — starting, running, stopped — ' + 'and the last lines of its console: what the game printed, build ' + 'errors, what the last play_swap said.', + inputSchema: ObjectSchema( + properties: { + 'lines': IntegerSchema( + description: 'how many console lines; default 40', + minimum: 0, + ), + }, + ), + ), + (EditorSession session, Map arguments) { + final lines = arguments['lines']; + return ( + did: true, + says: session.play.status(lines: lines is int ? lines : 40), + ); + }, + ), + _told( + Tool( + // `play_swap` and not the word flutter itself uses for it: CONTRIBUTING + // keeps that word out of the packages for a weapon's, and `HotSwap` is + // what the engine calls the same thing on the game's side. + name: 'play_swap', + description: + 'Swap the running game\'s code after it changed, the way flutter ' + 'run does on r: the game keeps its state and picks up the new code, ' + 'shaders and models. With restart, a hot restart from main instead. ' + 'Answers with what the tool said, including why it refused.', + inputSchema: ObjectSchema( + properties: { + 'restart': BooleanSchema( + description: 'true for a hot restart; default false', + ), + }, + ), + ), + (EditorSession session, Map arguments) => + session.play.swap(restart: arguments['restart'] == true), + ), + _told( + Tool( + name: 'play_stop', + description: + 'Stop the game started by play, and the flutter run with it.', + inputSchema: ObjectSchema(), + ), + (EditorSession session, Map arguments) => + session.play.stop(), + ), + _told( + Tool( + name: 'play_devices', + description: + 'The devices play can run the game on — this computer, a browser, ' + 'a phone on the cable — one per line with the id play takes.', + inputSchema: ObjectSchema(), + ), + (EditorSession session, Map arguments) => + session.play.listDevices(), + ), +]; + /// The ten document commands, one tool each, under the names they already have. /// /// **Named from [editorCommandNames] and nowhere else.** That list lives beside @@ -493,6 +589,7 @@ List get editorTools => [ return session.save(path is String ? path : null); }, ), + ..._playTools, EditorTool( Tool( name: 'screenshot', diff --git a/packages/flutter3d_editor_mcp/lib/src/play_session.dart b/packages/flutter3d_editor_mcp/lib/src/play_session.dart new file mode 100644 index 000000000..0412a01fb --- /dev/null +++ b/packages/flutter3d_editor_mcp/lib/src/play_session.dart @@ -0,0 +1,172 @@ +import 'dart:async'; + +import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; +import 'package:flutter3d_mcp_kit/flutter3d_mcp_kit.dart' show Answer; + +/// Makes a run of [projectRoot] on [device]. A parameter of [PlaySession] so +/// a test hands it a run over a tool of its own making. +typedef NewRun = FlutterRun Function(String projectRoot, String? device); + +FlutterRun _newRun(String projectRoot, String? device) => + FlutterRun(projectRoot: projectRoot, device: device); + +/// `HR5` for an agent: the game the open level belongs to, run, reloaded, +/// stopped and sent the level when it is saved — the editor application's +/// Play, through the same [FlutterRun]. +/// +/// **One run per process**, as there is one document: the level belongs to +/// one project, and a second run of it would be a second window of the same +/// game that nothing here could tell apart from the first. +final class PlaySession { + PlaySession({ + required this.levelPath, + this.projectRootOf = projectRootOnDisk, + this.newRun = _newRun, + this.devices = flutterDevices, + this.push = pushLevel, + this.waitFor = const Duration(minutes: 3), + }); + + /// The level being edited; its project is what [start] runs. + final String levelPath; + + final String? Function(String levelPath) projectRootOf; + final NewRun newRun; + final Future> Function() devices; + final Future> Function(String vmService, String level) + push; + + /// How long [start] waits for the game to be up before answering that it + /// is still starting. A first build of a desktop game takes a minute. + final Duration waitFor; + + FlutterRun? _run; + + /// Runs the project, on [device] or the tool's own choice, and waits until + /// it is up, has failed, or [waitFor] has passed. + Future start({String? device}) async { + final root = projectRootOf(levelPath); + if (root == null) { + return ( + did: false, + says: + '$levelPath is not inside a Flutter project, so there is nothing ' + 'to run', + ); + } + final running = _run; + if (running != null && _going(running.state.value)) { + if (device == null || device == running.device) { + return (did: true, says: 'already ${_describe(running)}'); + } + return ( + did: false, + says: + 'the game is running on ${running.device ?? 'the default device'}; ' + 'call play_stop first to run it on $device', + ); + } + unawaited(running?.dispose()); + final run = _run = newRun(root, device); + await run.start(); + await _settled(run); + return ( + did: run.state.value is! PlayStopped, + says: '${_describe(run)}\n${_tail(run, 12)}'.trim(), + ); + } + + /// Where the run is and the last [lines] of its console. + String status({int lines = 40}) { + final run = _run; + if (run == null) return 'not running; call play to start the game'; + return '${_describe(run)}\n${_tail(run, lines)}'.trim(); + } + + /// A hot reload, or a hot restart when [restart], and what the tool said. + Future swap({bool restart = false}) async { + final run = _run; + if (run == null || run.state.value is! PlayRunning) { + return (did: false, says: 'nothing is running; call play first'); + } + try { + final said = restart ? await run.hotRestart() : await run.hotSwap(); + final what = restart ? 'hot restart' : 'hot swap'; + return ( + did: true, + says: said == null || said.isEmpty ? '$what done' : '$what: $said', + ); + } on StateError catch (error) { + return (did: false, says: error.message); + } + } + + /// Stops the game. + Future stop() async { + final run = _run; + if (run == null || !_going(run.state.value)) { + return (did: false, says: 'nothing is running'); + } + await run.stop(); + return (did: true, says: 'asked the game to stop'); + } + + /// What `play` can be pointed at, one line each, with the id it takes. + Future listDevices() async { + try { + final found = await devices(); + if (found.isEmpty) return (did: true, says: 'no devices'); + return (did: true, says: found.map((it) => '$it').join('\n')); + } on Object catch (error) { + return (did: false, says: '$error'); + } + } + + /// Sends [document], just saved, to the game when it is running, and says + /// what the game did with it; null when nothing is running to send it to. + Future sendSaved(String document) async { + if (_run?.state.value case PlayRunning(:final vmService)) { + try { + return describeLevelApplied(await push(vmService, document)); + } on Object catch (error) { + return 'the running game did not take it: $error'; + } + } + return null; + } + + Future dispose() async => _run?.dispose(); + + Future _settled(FlutterRun run) async { + if (_settledState(run.state.value)) return; + try { + await run.state.changes.firstWhere(_settledState).timeout(waitFor); + } on TimeoutException { + // Still starting; the answer says so and `play_status` follows it. + } on StateError { + // The run closed its state before it settled. + } + } + + static bool _settledState(PlayState state) => + state is PlayRunning || state is PlayStopped; + + static bool _going(PlayState state) => + state is PlayStarting || state is PlayRunning; + + static String _describe(FlutterRun run) => switch (run.state.value) { + PlayIdle() => 'not started', + PlayStarting(:final message) => 'still starting: $message', + PlayRunning(:final vmService) => + 'running ${run.projectRoot} on ${run.device ?? 'the default device'}, ' + 'VM service $vmService', + PlayStopped(:final exitCode) => 'stopped (exit code $exitCode)', + }; + + static String _tail(FlutterRun run, int lines) { + final console = run.console.value; + return console + .skip(console.length > lines ? console.length - lines : 0) + .join('\n'); + } +} diff --git a/packages/flutter3d_editor_mcp/pubspec.yaml b/packages/flutter3d_editor_mcp/pubspec.yaml index 42e7335df..0b3817a57 100644 --- a/packages/flutter3d_editor_mcp/pubspec.yaml +++ b/packages/flutter3d_editor_mcp/pubspec.yaml @@ -21,6 +21,10 @@ dependencies: # `EditorCommand.fromJson` rather than a switch written a second time here. flutter3d_editor_core: ^0.8.0 + # Play: the game the level belongs to, run with `flutter run --machine` + # and sent the level when it is saved — the editor application's own. + flutter3d_editor_play: ^0.8.0 + # `Level`, `LevelIssue` and `LevelValidator`, which `validate` reports. flutter3d_sim: ^0.8.0 diff --git a/packages/flutter3d_editor_mcp/test/play_test.dart b/packages/flutter3d_editor_mcp/test/play_test.dart new file mode 100644 index 000000000..71216a8ce --- /dev/null +++ b/packages/flutter3d_editor_mcp/test/play_test.dart @@ -0,0 +1,221 @@ +/// `HR5` for an agent: Play through the server's tools, over a +/// `flutter run --machine` of the test's own making. +/// +/// dart test test/play_test.dart +library; + +import 'dart:io'; + +import 'package:flutter3d_editor_core/flutter3d_editor_core.dart'; +import 'package:flutter3d_editor_mcp/flutter3d_editor_mcp.dart'; +import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; +import 'package:flutter3d_editor_play/testing.dart'; +import 'package:test/test.dart'; + +const String _level = ''' +{ + "version": 1, + "materials": {"stone": {"baseColor": [0.5, 0.5, 0.5, 1]}}, + "brushes": [{"at": [0, 0, 0], "size": [4, 1, 4], "material": "stone"}] +} +'''; + +/// A session on a level in a project at `/game`, whose runs are fake, and +/// what the game was sent. +({ + EditorSession session, + List tools, + List pushed, + List devices, +}) +_session({String levelPath = '/game/assets/levels/one.json'}) { + final tools = []; + final pushed = []; + final devices = []; + final play = PlaySession( + levelPath: levelPath, + projectRootOf: (String path) => path.startsWith('/game/') ? '/game' : null, + newRun: (String root, String? device) { + devices.add(device); + final fake = fakeFlutterRun(projectRoot: root, device: device); + tools.add(fake.tool); + return fake.run; + }, + devices: () async => const [ + FlutterDevice(id: 'macos', name: 'macOS', platform: 'darwin'), + ], + push: (String vmService, String level) async { + pushed.add(level); + return { + 'diff': {'fog': true}, + }; + }, + waitFor: const Duration(seconds: 5), + ); + return ( + session: EditorSession(Editing.parse(_level, path: levelPath), play: play), + tools: tools, + pushed: pushed, + devices: devices, + ); +} + +Future _call( + EditorSession session, + String name, [ + Map arguments = const {}, +]) async { + final answer = await editorTools + .firstWhere((EditorTool it) => it.name == name) + .run(session, arguments); + return (did: answer.did, says: answer.says); +} + +/// `play`, answered by the tool as a game coming up. +Future _played( + ({ + EditorSession session, + List tools, + List pushed, + List devices, + }) + it, { + String? device, +}) async { + final answer = _call(it.session, 'play', { + 'device': ?device, + }); + await pumpEventQueue(); + it.tools.last + ..say('Launching lib/main.dart') + ..running(); + return answer; +} + +void main() { + test('a level outside any project has nothing to play', () async { + final it = _session(levelPath: '/elsewhere/one.json'); + final answer = await _call(it.session, 'play'); + expect(answer.did, isFalse); + expect(answer.says, contains('not inside a Flutter project')); + expect(it.tools, isEmpty); + }); + + test('play waits for the game and says where it runs', () async { + final it = _session(); + final answer = await _played(it, device: 'macos'); + + expect(answer.did, isTrue); + expect(answer.says, contains('running /game on macos')); + expect(answer.says, contains('ws://game')); + expect(answer.says, contains('Launching lib/main.dart')); + expect(it.devices, ['macos']); + + final status = await _call(it.session, 'play_status'); + expect(status.says, contains('running /game')); + }); + + test('a game that fails to build is a refusal with its console', () async { + final it = _session(); + final answer = _call(it.session, 'play'); + await pumpEventQueue(); + it.tools.last + ..say('lib/main.dart:3: Error: expected a semicolon') + ..exit(1); + + final said = await answer; + expect(said.did, isFalse); + expect(said.says, contains('stopped (exit code 1)')); + expect(said.says, contains('expected a semicolon')); + }); + + test('a reload is the tool\'s, and so is its answer', () async { + final it = _session(); + await _played(it); + + final reload = _call(it.session, 'play_swap'); + await pumpEventQueue(); + final sent = it.tools.last.sent.single; + expect(sent['method'], 'app.restart'); + expect((sent['params']! as Map)['fullRestart'], isFalse); + it.tools.last.answer(0, { + 'code': 0, + 'message': 'Reloaded 3 of 812 libraries', + }); + + final said = await reload; + expect(said.did, isTrue); + expect(said.says, 'hot swap: Reloaded 3 of 812 libraries'); + }); + + test('nothing running is nothing to reload or stop', () async { + final it = _session(); + expect((await _call(it.session, 'play_swap')).did, isFalse); + expect((await _call(it.session, 'play_stop')).did, isFalse); + expect( + (await _call(it.session, 'play_status')).says, + contains('not running'), + ); + }); + + test('another device while one runs is refused, not a second game', () async { + final it = _session(); + await _played(it, device: 'macos'); + + final again = await _call(it.session, 'play', { + 'device': 'chrome', + }); + expect(again.did, isFalse); + expect(again.says, contains('play_stop')); + expect(it.tools, hasLength(1)); + }); + + test('a save reaches the running game; a copy does not', () async { + final directory = await Directory.systemTemp.createTemp('play_test'); + addTearDown(() => directory.delete(recursive: true)); + final path = '${directory.path}/one.json'; + File(path).writeAsStringSync(_level); + final it = _session(levelPath: path); + // The temporary directory is not under `/game`: the session's project + // is found by path, so a level there is played by pointing at it. + final play = PlaySession( + levelPath: path, + projectRootOf: (String _) => directory.path, + newRun: (String root, String? device) { + final fake = fakeFlutterRun(projectRoot: root, device: device); + it.tools.add(fake.tool); + return fake.run; + }, + push: (String vmService, String level) async { + it.pushed.add(level); + return { + 'diff': {'fog': true}, + }; + }, + waitFor: const Duration(seconds: 5), + ); + final session = EditorSession( + Editing.parse(_level, path: path), + play: play, + ); + final started = _call(session, 'play'); + await pumpEventQueue(); + it.tools.last.running(); + await started; + + final saved = await _call(session, 'save'); + expect(saved.says, contains('the game took the new look')); + expect(it.pushed, hasLength(1)); + + await _call(session, 'save', { + 'path': '${directory.path}/copy.json', + }); + expect(it.pushed, hasLength(1), reason: 'the game plays the original'); + }); + + test('the devices are listed with the id play takes', () async { + final it = _session(); + final answer = await _call(it.session, 'play_devices'); + expect(answer.says, 'macOS (macos, darwin)'); + }); +} diff --git a/packages/flutter3d_editor_mcp/test/tools_test.dart b/packages/flutter3d_editor_mcp/test/tools_test.dart index d4382935e..3bead7a35 100644 --- a/packages/flutter3d_editor_mcp/test/tools_test.dart +++ b/packages/flutter3d_editor_mcp/test/tools_test.dart @@ -41,6 +41,11 @@ void main() { 'screenshot', 'report', 'optimizeLights', + 'play', + 'play_status', + 'play_swap', + 'play_stop', + 'play_devices', }; expect( namesOf(editorTools).difference(editorCommandNames.toSet()), diff --git a/packages/flutter3d_editor_play/CHANGELOG.md b/packages/flutter3d_editor_play/CHANGELOG.md new file mode 100644 index 000000000..42c4155e4 --- /dev/null +++ b/packages/flutter3d_editor_play/CHANGELOG.md @@ -0,0 +1,12 @@ +## Unreleased + +- **Play leaves the editor application.** `FlutterRun`, `projectRootFor` + and `pushLevel` move here from `apps/flutter3d_editor/lib/src/play`, with + `Watched` in place of Flutter's `ValueNotifier`, so the editor's MCP server + runs the same Play. `FlutterRun.device` is settable between runs, and + `hotSwap`/`hotRestart` return what the tool said. +- **`flutterDevices` lists what `flutter run -d` can be given**, from + `flutter devices --machine`, reading past whatever the tool prints first + and leaving out devices it marks unsupported. +- **`package:flutter3d_editor_play/testing.dart`**: `FakeFlutterTool` and + `fakeFlutterRun`, a `flutter run --machine` a test controls. diff --git a/packages/flutter3d_editor_play/LICENSE b/packages/flutter3d_editor_play/LICENSE new file mode 100644 index 000000000..c602d8d40 --- /dev/null +++ b/packages/flutter3d_editor_play/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Dmitrii Zolotov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/flutter3d_editor_play/README.md b/packages/flutter3d_editor_play/README.md new file mode 100644 index 000000000..97881f656 --- /dev/null +++ b/packages/flutter3d_editor_play/README.md @@ -0,0 +1,48 @@ +# flutter3d_editor_play + +Play from a level editor. The game a level belongs to is run with +`flutter run --machine`, reloaded, restarted and stopped from the editor, and +a level saved while it runs is sent to it and taken without the game starting +over. + +It is what the Play button of `apps/flutter3d_editor` runs, and what the +`play` tools of [`flutter3d_editor_mcp`](../flutter3d_editor_mcp) run, so an +agent's Play and a person's are the same code. + +```dart +final root = projectRootOnDisk('game/assets/levels/first.json'); +final run = FlutterRun(projectRoot: root!, device: 'macos'); +await run.start(); +run.state.changes.listen((PlayState state) => print(state)); + +// Later, after the code changed: +print(await run.hotSwap()); // "Reloaded 3 of 812 libraries" + +// After a save: +if (run.state.value case PlayRunning(:final vmService)) { + print(describeLevelApplied(await pushLevel(vmService, savedDocument))); +} +``` + +| | | +|---|---| +| `FlutterRun` | One `flutter run --machine`: its state, its console, reload, restart, stop | +| `projectRootFor`, `projectRootOnDisk` | The project a level file belongs to: the nearest `pubspec.yaml` above it | +| `flutterDevices`, `parseFlutterDevices` | What `-d` can be given, from `flutter devices --machine` | +| `pushLevel` | A saved level, sent to the running game's `ext.flutter3d.level.apply` | +| `Watched` | A value and a stream of its changes, where Flutter's `ValueNotifier` cannot go | + +`package:flutter3d_editor_play/testing.dart` has `FakeFlutterTool`, a +`flutter run --machine` a test controls, and `fakeFlutterRun` over it. + +## Why a package of its own + +Play starts a process and talks to it. `flutter3d_editor_core` is written +never to do either, so a validator or a web build can open a level with it, +and the editor's MCP server could not reach code that lived inside the editor +application. Here both depend on it, and neither on the other. It is plain +Dart with `dart:io`: it runs under `dart run` and in a desktop app, and not in +a browser, where there is no process to start. + +The game side of it, the extension a level is sent to, is `LiveLevel` in +`flutter3d_game`. diff --git a/packages/flutter3d_editor_play/analysis_options.yaml b/packages/flutter3d_editor_play/analysis_options.yaml new file mode 100644 index 000000000..0fdfb4706 --- /dev/null +++ b/packages/flutter3d_editor_play/analysis_options.yaml @@ -0,0 +1,11 @@ +include: ../../analysis_options.yaml + +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** diff --git a/packages/flutter3d_editor_play/lib/flutter3d_editor_play.dart b/packages/flutter3d_editor_play/lib/flutter3d_editor_play.dart new file mode 100644 index 000000000..c1f3bd17b --- /dev/null +++ b/packages/flutter3d_editor_play/lib/flutter3d_editor_play.dart @@ -0,0 +1,12 @@ +/// Play from a level editor: the game a level belongs to, run with +/// `flutter run --machine`, reloaded, restarted and stopped, and handed a +/// level as soon as it is saved. +/// +/// Used by the editor application's toolbar and by `flutter3d_editor_mcp`, +/// so an agent's Play is a person's Play. +library; + +export 'src/devices.dart'; +export 'src/flutter_run.dart'; +export 'src/level_push.dart'; +export 'src/watched.dart'; diff --git a/packages/flutter3d_editor_play/lib/src/devices.dart b/packages/flutter3d_editor_play/lib/src/devices.dart new file mode 100644 index 000000000..8b04b067d --- /dev/null +++ b/packages/flutter3d_editor_play/lib/src/devices.dart @@ -0,0 +1,86 @@ +import 'dart:convert'; +import 'dart:io'; + +/// A device `flutter run -d` can be given, as `flutter devices --machine` +/// lists it. +final class FlutterDevice { + const FlutterDevice({ + required this.id, + required this.name, + required this.platform, + this.emulator = false, + }); + + /// What `-d` takes. + final String id; + + /// What a person calls it: "Galaxy A55", "macOS", "Chrome". + final String name; + + /// The tool's `targetPlatform`: `darwin`, `android-arm64`, `web-javascript`. + final String platform; + + final bool emulator; + + Map toJson() => { + 'id': id, + 'name': name, + 'platform': platform, + 'emulator': emulator, + }; + + @override + String toString() => '$name ($id, $platform${emulator ? ', emulator' : ''})'; +} + +/// The devices in what `flutter devices --machine` printed. +/// +/// **From the first `[` on**, because the tool may say something before the +/// list — that it is downloading an artifact, that a newer version exists — +/// and that is not a reason to offer no devices. Entries the tool marks +/// unsupported, or that lack an id, are left out: `-d` would refuse them. +List parseFlutterDevices(String printed) { + final start = printed.indexOf('['); + if (start < 0) return const []; + final Object? decoded; + try { + decoded = jsonDecode(printed.substring(start)); + } on FormatException { + return const []; + } + if (decoded is! List) return const []; + return [ + for (final entry in decoded) + if (entry case { + 'id': final String id, + 'name': final String name, + } when entry['isSupported'] != false) + FlutterDevice( + id: id, + name: name, + platform: '${entry['targetPlatform'] ?? 'unknown'}', + emulator: entry['emulator'] == true, + ), + ]; +} + +/// Runs `flutter` with [arguments] to the end. A parameter of +/// [flutterDevices] so a test answers for the tool. +typedef RunFlutter = Future Function(List arguments); + +Future _runFlutter(List arguments) => + Process.run('flutter', arguments, runInShell: Platform.isWindows); + +/// What `flutter run -d` could be pointed at on this machine now. +/// +/// Throws a [StateError] with what the tool said when it fails, since "no +/// devices" and "flutter is not on the path" are different news. +Future> flutterDevices({ + RunFlutter run = _runFlutter, +}) async { + final result = await run(const ['devices', '--machine']); + if (result.exitCode != 0) { + throw StateError('flutter devices failed: ${result.stderr}'.trim()); + } + return parseFlutterDevices('${result.stdout}'); +} diff --git a/packages/flutter3d_editor_play/lib/src/flutter_run.dart b/packages/flutter3d_editor_play/lib/src/flutter_run.dart new file mode 100644 index 000000000..53f84deab --- /dev/null +++ b/packages/flutter3d_editor_play/lib/src/flutter_run.dart @@ -0,0 +1,293 @@ +/// `HR5`: the project a level belongs to, run from the editor. +/// +/// `flutter run --machine` and not a game built into the editor: the level +/// belongs to another application, with its own code, packages and entry +/// point, and the only thing that runs it the way its author does is the +/// author's own `flutter run`. `--machine` makes it a daemon the editor talks +/// to in JSON lines — the same protocol an IDE uses — so a hot reload from the +/// toolbar is the same hot reload the terminal would have done, and the game +/// answers it through `HotSwap` the same way. +library; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'watched.dart'; + +/// Where a run is: not yet started, starting, running with a VM service to +/// attach to, or over. +sealed class PlayState { + const PlayState(); +} + +final class PlayIdle extends PlayState { + const PlayIdle(); +} + +final class PlayStarting extends PlayState { + const PlayStarting(this.message); + + /// What the tool says it is doing: "Launching lib/main.dart…". + final String message; +} + +final class PlayRunning extends PlayState { + const PlayRunning({required this.appId, required this.vmService}); + + final String appId; + + /// The game's VM service, for the timeline panel and `ext.flutter3d.*`. + final String vmService; +} + +final class PlayStopped extends PlayState { + const PlayStopped(this.exitCode); + + final int exitCode; +} + +/// Starts `flutter` with [arguments] in [workingDirectory]. A parameter of +/// [FlutterRun] so a test hands it a process of its own making. +typedef StartFlutter = + Future Function(List arguments, String workingDirectory); + +Future _startFlutter( + List arguments, + String workingDirectory, +) => Process.start( + 'flutter', + arguments, + workingDirectory: workingDirectory, + runInShell: Platform.isWindows, +); + +/// One `flutter run --machine`, from start to exit. +/// +/// **Its console is the tool's, line for line.** What the game prints, what +/// the tool reports and any line that is not the protocol at all — a build +/// error comes out as plain text before the daemon has said anything — all +/// go to [console] in the order they arrived, because the moment somebody +/// needs this panel is the moment something went wrong, and a filtered +/// console hides exactly that. +final class FlutterRun { + FlutterRun({ + required this.projectRoot, + this.device, + this._start = _startFlutter, + }); + + /// The directory with the project's `pubspec.yaml`. + final String projectRoot; + + /// `-d` for `flutter run`; the tool's own choice when null. Read by the + /// next [start], so a picker sets it between runs. + String? device; + + final StartFlutter _start; + + /// Where the run is; a panel rebuilds from it. + final Watched state = Watched(const PlayIdle()); + + /// Every line the run has printed, oldest first. Capped at [consoleLimit], + /// dropping the oldest, so a game that logs every frame costs a bounded + /// list and not the editor's memory. + final Watched> console = Watched>(const []); + + static const int consoleLimit = 2000; + + Process? _process; + String? _appId; + var _nextId = 0; + final Map> _pending = >{}; + + /// Starts the run. Does nothing while one is already going. + Future start() async { + if (_process != null) return; + state.value = const PlayStarting('Starting flutter run…'); + final Process process; + try { + process = await _start([ + 'run', + '--machine', + if (device case final String id) ...['-d', id], + ], projectRoot); + } on ProcessException catch (error) { + _print('could not start flutter: ${error.message}'); + state.value = const PlayStopped(-1); + return; + } + _process = process; + process.stdout + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen(_line); + process.stderr + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen(_print); + unawaited( + process.exitCode.then((int code) { + _process = null; + _appId = null; + for (final pending in _pending.values) { + pending.completeError(StateError('flutter run exited ($code)')); + } + _pending.clear(); + state.value = PlayStopped(code); + }), + ); + } + + /// A hot reload: the game keeps its state, and `HotSwap` relinks shaders + /// and swaps changed models from `reassemble`. What the tool said about it, + /// "Reloaded 3 of 812 libraries" or why it refused, or null when nothing is + /// running. + Future hotSwap() => _restart(fullRestart: false); + + /// A hot restart: the game starts over from `main`, with the new code. + Future hotRestart() => _restart(fullRestart: true); + + /// Asks the game to stop, and the tool with it. + Future stop() async { + final appId = _appId; + if (appId == null) { + _process?.kill(); + return; + } + try { + await _send('app.stop', {'appId': appId}); + } on StateError { + // Already gone. + } + } + + /// Stops the run if there is one and lets go of the process. + Future dispose() async { + await stop(); + await state.close(); + await console.close(); + } + + Future _restart({required bool fullRestart}) async { + final appId = _appId; + if (appId == null) return null; + final result = await _send('app.restart', { + 'appId': appId, + 'fullRestart': fullRestart, + 'pause': false, + 'reason': 'manual', + }); + // "Reloaded 3 of 812 libraries" as well as a refusal: after a press of + // the button, the console is where somebody looks to see it happened. + if (result case {'message': final String message} when message.isNotEmpty) { + _print(message); + return message; + } + return ''; + } + + Future _send(String method, Map params) { + final process = _process; + if (process == null) { + return Future.error(StateError('nothing is running')); + } + final id = _nextId++; + final reply = Completer(); + _pending[id] = reply; + process.stdin.writeln( + jsonEncode([ + {'id': id, 'method': method, 'params': params}, + ]), + ); + return reply.future; + } + + void _line(String line) { + // The protocol wraps each message in a one-element list; anything else + // on stdout is the tool talking before or around the daemon. + final Object? decoded; + try { + decoded = line.startsWith('[{') ? jsonDecode(line) : null; + } on FormatException { + _print(line); + return; + } + switch (decoded) { + case [final Map message]: + _message(message); + default: + _print(line); + } + } + + void _message(Map message) { + switch (message) { + case {'id': final int id, 'error': final Object? error}: + _pending.remove(id)?.completeError(StateError('$error')); + case {'id': final int id}: + _pending.remove(id)?.complete(message['result']); + case {'event': 'app.start', 'params': {'appId': final String appId}}: + _appId = appId; + case { + 'event': 'app.debugPort', + 'params': {'appId': final String appId, 'wsUri': final String uri}, + }: + _appId = appId; + state.value = PlayRunning(appId: appId, vmService: uri); + case {'event': 'app.log', 'params': {'log': final String log}}: + _print(log); + case { + 'event': 'daemon.logMessage', + 'params': {'message': final String m}, + }: + _print(m); + case { + 'event': 'app.progress', + 'params': {'message': final String progress}, + }: + _print(progress); + if (state.value is PlayStarting) { + state.value = PlayStarting(progress); + } + default: + // `app.started`, `app.stop`, `daemon.connected`, `device.*` and + // whatever a later tool adds: nothing the panel shows. + break; + } + } + + void _print(String line) { + final lines = console.value; + console.value = [ + ...lines.length >= consoleLimit + ? lines.skip(lines.length - consoleLimit + 1) + : lines, + line, + ]; + } +} + +/// The directory of the nearest `pubspec.yaml` above [levelPath], or null +/// when the level is not inside a project at all. +/// +/// [hasPubspec] answers for a directory; injected, like +/// `Documents.assetRootFor`'s own check, so this is tested with no disk. +String? projectRootFor( + String levelPath, { + required bool Function(String directory) hasPubspec, +}) => _nearest(File(levelPath).parent, hasPubspec); + +String? _nearest(Directory directory, bool Function(String) hasPubspec) => + switch (directory.parent) { + _ when hasPubspec(directory.path) => directory.path, + final parent when parent.path == directory.path => null, + final parent => _nearest(parent, hasPubspec), + }; + +/// [projectRootFor], asking the disk. +String? projectRootOnDisk(String levelPath) => projectRootFor( + levelPath, + hasPubspec: (String directory) => + File('$directory${Platform.pathSeparator}pubspec.yaml').existsSync(), +); diff --git a/packages/flutter3d_editor_play/lib/src/watched.dart b/packages/flutter3d_editor_play/lib/src/watched.dart new file mode 100644 index 000000000..3b391c622 --- /dev/null +++ b/packages/flutter3d_editor_play/lib/src/watched.dart @@ -0,0 +1,23 @@ +import 'dart:async'; + +/// A value and the news of it changing, with no Flutter in it. +/// +/// What `ValueNotifier` was to Play inside the editor application: a panel +/// rebuilds from [changes], a server reads [value] when it is asked. +final class Watched { + Watched(this._value); + + T _value; + final StreamController _changes = StreamController.broadcast(); + + T get value => _value; + set value(T next) { + _value = next; + if (!_changes.isClosed) _changes.add(next); + } + + /// Every value set from now on. + Stream get changes => _changes.stream; + + Future close() => _changes.close(); +} diff --git a/packages/flutter3d_editor_play/lib/testing.dart b/packages/flutter3d_editor_play/lib/testing.dart new file mode 100644 index 000000000..d2d24f86d --- /dev/null +++ b/packages/flutter3d_editor_play/lib/testing.dart @@ -0,0 +1,105 @@ +/// A `flutter run --machine` a test controls, for anything that drives a +/// [FlutterRun] and wants to say what the tool answers. +library; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'flutter3d_editor_play.dart'; + +/// A `flutter run --machine` that says what [say] is given and records what +/// it is sent. +final class FakeFlutterTool implements Process { + final StreamController> _out = StreamController>(); + final StreamController> _err = StreamController>(); + final Completer _exit = Completer(); + + /// Every message sent to the tool, in order. + final List> sent = >[]; + late final IOSink _in = IOSink(_Sent(this)); + + /// A line on the tool's stdout. + void say(String line) => _out.add(utf8.encode('$line\n')); + + /// A daemon event. + void event(String event, Map params) => say( + jsonEncode([ + {'event': event, 'params': params}, + ]), + ); + + /// The answer to request [id]. + void answer(int id, Object? result) => say( + jsonEncode([ + {'id': id, 'result': result}, + ]), + ); + + /// The tool announcing a game running with its VM service at [wsUri]. + void running({String appId = 'a1', String wsUri = 'ws://game'}) => + event('app.debugPort', {'appId': appId, 'wsUri': wsUri}); + + void exit(int code) { + if (!_exit.isCompleted) _exit.complete(code); + } + + @override + Stream> get stdout => _out.stream; + + @override + Stream> get stderr => _err.stream; + + @override + IOSink get stdin => _in; + + @override + Future get exitCode => _exit.future; + + @override + int get pid => 1; + + @override + bool kill([ProcessSignal signal = ProcessSignal.sigterm]) { + exit(-15); + return true; + } +} + +final class _Sent implements StreamConsumer> { + _Sent(this.tool); + + final FakeFlutterTool tool; + + @override + Future addStream(Stream> stream) => stream + .transform(utf8.decoder) + .transform(const LineSplitter()) + .forEach((String line) { + final [Object? message] = jsonDecode(line) as List; + tool.sent.add(message! as Map); + }); + + @override + Future close() async {} +} + +/// A [FlutterRun] over a [FakeFlutterTool], and the arguments each start +/// was given, the working directory first. +({FlutterRun run, FakeFlutterTool tool, List> started}) +fakeFlutterRun({String projectRoot = '/game', String? device = 'macos'}) { + final tool = FakeFlutterTool(); + final started = >[]; + return ( + run: FlutterRun( + projectRoot: projectRoot, + device: device, + start: (List arguments, String directory) async { + started.add([directory, ...arguments]); + return tool; + }, + ), + tool: tool, + started: started, + ); +} diff --git a/packages/flutter3d_editor_play/pubspec.yaml b/packages/flutter3d_editor_play/pubspec.yaml new file mode 100644 index 000000000..8c9186d95 --- /dev/null +++ b/packages/flutter3d_editor_play/pubspec.yaml @@ -0,0 +1,36 @@ +name: flutter3d_editor_play +description: "Play from a level editor: the game a level belongs to run with `flutter run --machine`, reloaded, stopped, and handed a saved level while it runs." +version: 0.8.0 +repository: https://github.com/pleiondev/flutter3d +homepage: https://flutter3d.pleion.dev +issue_tracker: https://github.com/pleiondev/flutter3d/issues +topics: + - game-development + - level-editor + - hot-reload + - tooling +resolution: workspace + +environment: + sdk: ^3.12.2 + +dependencies: + # `Level.digestHex`, which the game checks a level it is sent against. + flutter3d_sim: ^0.8.0 + + # The running game's VM service, where `ext.flutter3d.level.apply` is. + vm_service: ^15.3.0 + +dev_dependencies: + test: ^1.25.0 + flutter_lints: ^6.0.0 + +# **Plain Dart with `dart:io` in it, and a package of its own for that.** +# +# Play starts a process and talks to it, which is exactly what +# `flutter3d_editor_core` is written never to do: that package opens a level +# with no disk and no process, so a validator or a web build can use it. The +# editor application had this code under `lib/src/play`, where the editor's +# MCP server, which wants the same Play for an agent, could not reach it +# without depending on an application. Here both depend on it, and neither +# depends on the other. diff --git a/packages/flutter3d_editor_play/test/devices_test.dart b/packages/flutter3d_editor_play/test/devices_test.dart new file mode 100644 index 000000000..8be971238 --- /dev/null +++ b/packages/flutter3d_editor_play/test/devices_test.dart @@ -0,0 +1,88 @@ +/// `HR5`: the devices Play can be pointed at, as the tool lists them. +/// +/// dart test test/devices_test.dart +library; + +import 'dart:io'; + +import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; +import 'package:test/test.dart'; + +/// Trimmed from a real `flutter devices --machine` on a Mac with a phone on +/// the cable, with the line the tool prints first when it has news. +const String _printed = ''' +A new version of Flutter is available! +[ + { + "name": "Galaxy A55", + "id": "R5CX10ABCDE", + "isSupported": true, + "targetPlatform": "android-arm64", + "emulator": false + }, + { + "name": "macOS", + "id": "macos", + "isSupported": true, + "targetPlatform": "darwin", + "emulator": false + }, + { + "name": "Old iPad", + "id": "00008", + "isSupported": false, + "targetPlatform": "ios" + } +] +'''; + +void main() { + test('reads the list past whatever the tool said first', () { + final devices = parseFlutterDevices(_printed); + + expect(devices.map((it) => it.id), ['R5CX10ABCDE', 'macos']); + expect(devices.first.name, 'Galaxy A55'); + expect(devices.first.platform, 'android-arm64'); + }); + + test('an unsupported device is not offered', () { + expect( + parseFlutterDevices(_printed).where((it) => it.id == '00008'), + isEmpty, + ); + }); + + test('something that is not a list is no devices, not a crash', () { + expect(parseFlutterDevices('No devices found'), isEmpty); + expect(parseFlutterDevices('[{"id": '), isEmpty); + }); + + test('the tool failing is news of its own', () async { + await expectLater( + flutterDevices( + run: (List arguments) async => + ProcessResult(1, 1, '', 'flutter: command not found'), + ), + throwsA( + isA().having( + (it) => it.message, + 'message', + contains('command not found'), + ), + ), + ); + }); + + test('asks the tool the way an IDE does', () async { + final asked = >[]; + final devices = await flutterDevices( + run: (List arguments) async { + asked.add(arguments); + return ProcessResult(1, 0, _printed, ''); + }, + ); + + expect(asked.single, ['devices', '--machine']); + expect(devices, hasLength(2)); + }); +} diff --git a/packages/flutter3d_editor_play/test/flutter_run_test.dart b/packages/flutter3d_editor_play/test/flutter_run_test.dart index 6a784d601..b6087200e 100644 --- a/packages/flutter3d_editor_play/test/flutter_run_test.dart +++ b/packages/flutter3d_editor_play/test/flutter_run_test.dart @@ -2,103 +2,16 @@ /// drives it — against a process of the test's own making, which speaks the /// daemon protocol the way the tool does. /// -/// flutter test test/flutter_run_test.dart +/// dart test test/flutter_run_test.dart library; -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter3d_editor/src/play/flutter_run.dart'; -import 'package:flutter3d_editor/src/play/play_screen.dart'; -import 'package:flutter_test/flutter_test.dart'; - -/// A `flutter run --machine` that says what [say] is given and records what -/// it is sent. -final class _Tool implements Process { - final StreamController> _out = StreamController>(); - final StreamController> _err = StreamController>(); - final Completer _exit = Completer(); - final List> sent = >[]; - late final IOSink _in = IOSink(_Sent(this)); - - void say(String line) => _out.add(utf8.encode('$line\n')); - - void event(String event, Map params) => say( - jsonEncode([ - {'event': event, 'params': params}, - ]), - ); - - void answer(int id, Object? result) => say( - jsonEncode([ - {'id': id, 'result': result}, - ]), - ); - - void exit(int code) => _exit.complete(code); - - @override - Stream> get stdout => _out.stream; - - @override - Stream> get stderr => _err.stream; - - @override - IOSink get stdin => _in; - - @override - Future get exitCode => _exit.future; - - @override - int get pid => 1; - - @override - bool kill([ProcessSignal signal = ProcessSignal.sigterm]) { - if (!_exit.isCompleted) _exit.complete(-15); - return true; - } -} - -final class _Sent implements StreamConsumer> { - _Sent(this.tool); - - final _Tool tool; - - @override - Future addStream(Stream> stream) => stream - .transform(utf8.decoder) - .transform(const LineSplitter()) - .forEach((String line) { - final [Object? message] = jsonDecode(line) as List; - tool.sent.add(message! as Map); - }); - - @override - Future close() async {} -} - -({FlutterRun run, _Tool tool, List> started}) _run() { - final tool = _Tool(); - final started = >[]; - return ( - run: FlutterRun( - projectRoot: '/game', - device: 'macos', - start: (List arguments, String directory) async { - started.add([directory, ...arguments]); - return tool; - }, - ), - tool: tool, - started: started, - ); -} +import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; +import 'package:flutter3d_editor_play/testing.dart'; +import 'package:test/test.dart'; void main() { test('starts the tool in the project and follows it to running', () async { - final it = _run(); + final it = fakeFlutterRun(); await it.run.start(); expect(it.started.single, [ '/game', @@ -132,7 +45,7 @@ void main() { }); test('a hot reload and a hot restart are the tool\'s own commands', () async { - final it = _run(); + final it = fakeFlutterRun(); await it.run.start(); it.tool.event('app.debugPort', { 'appId': 'a1', @@ -140,7 +53,7 @@ void main() { }); await pumpEventQueue(); - final reload = it.run.hotReload(); + final reload = it.run.hotSwap(); await pumpEventQueue(); expect(it.tool.sent.single, { 'id': 0, @@ -167,7 +80,7 @@ void main() { }); test('the tool exiting stops the run and fails what was waiting', () async { - final it = _run(); + final it = fakeFlutterRun(); await it.run.start(); it.tool.event('app.debugPort', { 'appId': 'a1', @@ -175,7 +88,7 @@ void main() { }); await pumpEventQueue(); - final reload = it.run.hotReload(); + final reload = it.run.hotSwap(); await pumpEventQueue(); it.tool.exit(1); @@ -186,7 +99,7 @@ void main() { test( 'the console keeps the newest lines when a game logs every frame', () async { - final it = _run(); + final it = fakeFlutterRun(); await it.run.start(); for (var i = 0; i < FlutterRun.consoleLimit + 5; i++) { it.tool.say('line $i'); @@ -213,37 +126,4 @@ void main() { isNull, ); }); - - testWidgets('the panel offers a reload only once the game is running', ( - WidgetTester tester, - ) async { - final it = _run(); - final timelines = []; - await it.run.start(); - await tester.pumpWidget( - MaterialApp( - home: PlayScreen(run: it.run, onTimeline: timelines.add), - ), - ); - - IconButton button(String key) => - tester.widget(find.byKey(ValueKey(key))); - expect(button('play.reload').onPressed, isNull); - expect(button('play.timeline').onPressed, isNull); - - it.tool.event('app.debugPort', { - 'appId': 'a1', - 'wsUri': 'ws://game', - }); - await tester.pump(); - await tester.pump(); - - expect(find.text('Running, ws://game'), findsOneWidget); - await tester.tap(find.byKey(const ValueKey('play.reload'))); - await tester.pump(); - expect(it.tool.sent.single['method'], 'app.restart'); - - await tester.tap(find.byKey(const ValueKey('play.timeline'))); - expect(timelines, ['ws://game']); - }); } diff --git a/packages/flutter3d_editor_play/test/level_push_test.dart b/packages/flutter3d_editor_play/test/level_push_test.dart index 01b2d48d0..2121ade2a 100644 --- a/packages/flutter3d_editor_play/test/level_push_test.dart +++ b/packages/flutter3d_editor_play/test/level_push_test.dart @@ -1,14 +1,14 @@ /// `HR3`'s editor half: what a saved level is sent as, and how the game's /// answer reads in the status strip. /// -/// flutter test test/level_push_test.dart +/// dart test test/level_push_test.dart library; import 'dart:convert'; -import 'package:flutter3d_editor/src/play/level_push.dart'; +import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; import 'package:flutter3d_sim/flutter3d_sim.dart'; -import 'package:flutter_test/flutter_test.dart'; +import 'package:test/test.dart'; const String _document = '{"version": 1, "name": "yard", "fogDensity": 0.02}'; diff --git a/pubspec.yaml b/pubspec.yaml index a4de2ba6f..11caf481e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,6 +48,7 @@ workspace: - packages/flutter3d_editor_core - packages/flutter3d_editor_widgets - packages/flutter3d_editor_mcp + - packages/flutter3d_editor_play - packages/flutter3d_game_shooter - packages/flutter3d_game_strategy - packages/flutter3d_game_platformer diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 09ca4cdd8..dbfe800d1 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -25,7 +25,7 @@ This takes about fifteen minutes, from a fresh checkout to a lit mesh turning on ## Resolve the workspace -The repository is a [pub workspace](https://dart.dev/tools/pub/workspaces): one resolve covers all forty-one packages and twelve applications against a single lock file. Without it, packages that depend on each other by path drift apart at the first version bump, and the drift only shows up as an unbuildable checkout on somebody else's machine. +The repository is a [pub workspace](https://dart.dev/tools/pub/workspaces): one resolve covers all forty-two packages and twelve applications against a single lock file. Without it, packages that depend on each other by path drift apart at the first version bump, and the drift only shows up as an unbuildable checkout on somebody else's machine. ```bash git clone https://github.com/pleiondev/flutter3d.git @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10860 tests across 41 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10875 tests across 42 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 7f6ca4249..2bb6426a1 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10860 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10875 tests need a GPU. --- # Testing -10860 tests across 41 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10875 tests across 42 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -20,17 +20,17 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 63 | | `apps/flutter3d_demo_platformer` | 202 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 328 | | `apps/flutter3d_demo_strategy` | 50 | -| `apps/flutter3d_editor` | 229 | | `apps/flutter3d_demo_arcade` | 20 | +| `apps/flutter3d_editor` | 224 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | -| `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 28 | +| `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 130 | | `flutter3d_game` | 279 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | -| | | | `flutter3d_net` | 15 | +| `flutter3d_editor_play` | 12 | | `flutter3d_net` | 15 | | | | | `flutter3d_net_webrtc` | 2 | | | | | `flutter3d_sim_mcp` | 25 | | `flutter3d_mcp_kit` | 2 | | `flutter3d_build` | 127 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10834 rather than 10860: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10849 rather than 10875: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 1f6350507..e70bba0d8 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -201,8 +201,13 @@ belongs to the editor, not the panel, so closing the panel leaves the game running. The protocol was checked against a real run of `packages/flutter3d/example` on macOS (`app.start`, `app.debugPort` with `wsUri`, `app.restart` answering `{code, message}`, `app.stop`). -*Left:* the same from MCP, a device picker (the tool's own default today), and -the web editor, which has no process to start and will attach instead. +Play moved into a package of its own, `flutter3d_editor_play` (plain Dart +with `dart:io`, `Watched` in place of `ValueNotifier`), so the editor's MCP +server runs the same code: `play`, `play_status`, `play_swap`, `play_stop` +and `play_devices`, and a `save` while the game runs sends it the level. +The panel picks the device between runs from `flutter devices --machine`. +*Left:* the web editor, which has no process to start and will attach +instead. ## 4. Parity with flutter_scene 0.24 diff --git a/tool/structure/repository.dart b/tool/structure/repository.dart index cab2ffa51..6d12cd57b 100644 --- a/tool/structure/repository.dart +++ b/tool/structure/repository.dart @@ -117,6 +117,10 @@ const Map flatDartPackages = { 'which cannot resolve a package that depends on the Flutter SDK — so a ' 'single Flutter import here is not a heavier process, it is a server ' 'that will not start on any machine that has not got the Flutter tool', + 'flutter3d_editor_play': + 'the editor\'s MCP server runs Play through it, and that server is ' + 'started with `dart run`: a Flutter import here is `play` taking the ' + 'whole server down with it', 'flutter3d_hardware': 'the vocabulary a backend implements named no graphics API already; ' '`GraphicsDevice.present` was its one Flutter import, returning the ' From 45eac9a4e5a5fd6c218aa20e9344f93c70c43fed Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:28:12 +0300 Subject: [PATCH 022/141] The Medium article credits Carol Shaw for River Raid --- site/assets/articles/medium-flame/article.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/assets/articles/medium-flame/article.md b/site/assets/articles/medium-flame/article.md index d5375ad31..f752f1071 100644 --- a/site/assets/articles/medium-flame/article.md +++ b/site/assets/articles/medium-flame/article.md @@ -8,6 +8,8 @@ In my [previous article](https://medium.com/@dzolotov/i-wrote-a-3d-game-engine-f The screenshot above is River Sortie, a River Raid-style shooter you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. +The original River Raid was designed and programmed by Carol Shaw and published by Activision for the Atari 2600 in 1982. Shaw was one of the first women to design video games professionally, and her river was generated from a pseudo-random sequence rather than stored, so the whole course came out the same on every play from a few bytes of code. + I built it to see whether that holds up in a real game. Flame is the 2D engine a lot of Flutter developers already write games with, and for a while it and flutter3d lived side by side without talking to each other. If you had a Flame game and wanted light, shadows, and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. Below is how it works, and then a small game built on it, from an empty project to a boat collecting buoys on a lake. ## The idea: each engine keeps its job From 33aeea8ff8ed8e713545d954648fb46c5267d784 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:28:17 +0300 Subject: [PATCH 023/141] The Carol Shaw paragraph claims only what it can back --- site/assets/articles/medium-flame/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/assets/articles/medium-flame/article.md b/site/assets/articles/medium-flame/article.md index f752f1071..78b316123 100644 --- a/site/assets/articles/medium-flame/article.md +++ b/site/assets/articles/medium-flame/article.md @@ -8,7 +8,7 @@ In my [previous article](https://medium.com/@dzolotov/i-wrote-a-3d-game-engine-f The screenshot above is River Sortie, a River Raid-style shooter you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. -The original River Raid was designed and programmed by Carol Shaw and published by Activision for the Atari 2600 in 1982. Shaw was one of the first women to design video games professionally, and her river was generated from a pseudo-random sequence rather than stored, so the whole course came out the same on every play from a few bytes of code. +The original River Raid was designed and programmed by Carol Shaw and published by Activision for the Atari 2600 in 1982. Shaw was one of the first women to design video games professionally, and her river was generated from a pseudo-random sequence rather than stored, so the whole course came out the same on every play without the cartridge having to store it. I built it to see whether that holds up in a real game. Flame is the 2D engine a lot of Flutter developers already write games with, and for a while it and flutter3d lived side by side without talking to each other. If you had a Flame game and wanted light, shadows, and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. Below is how it works, and then a small game built on it, from an empty project to a boat collecting buoys on a lake. From d6d5fd00a36d68d31c667f51a73edb662c81c432 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:28:23 +0300 Subject: [PATCH 024/141] The Carol Shaw paragraph says it once --- site/assets/articles/medium-flame/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/assets/articles/medium-flame/article.md b/site/assets/articles/medium-flame/article.md index 78b316123..c3db02753 100644 --- a/site/assets/articles/medium-flame/article.md +++ b/site/assets/articles/medium-flame/article.md @@ -8,7 +8,7 @@ In my [previous article](https://medium.com/@dzolotov/i-wrote-a-3d-game-engine-f The screenshot above is River Sortie, a River Raid-style shooter you can [play in the browser](https://flutter3d.pleion.dev/river/demo/). Everything that moves in it is an ordinary Flame component with a Flame hitbox. Nothing in the game logic knows it is being drawn in 3D. -The original River Raid was designed and programmed by Carol Shaw and published by Activision for the Atari 2600 in 1982. Shaw was one of the first women to design video games professionally, and her river was generated from a pseudo-random sequence rather than stored, so the whole course came out the same on every play without the cartridge having to store it. +The original River Raid was designed and programmed by Carol Shaw and published by Activision for the Atari 2600 in 1982. Shaw was one of the first women to design video games professionally, and her river was generated from a pseudo-random sequence, so the whole course came out the same on every play without the cartridge storing it. I built it to see whether that holds up in a real game. Flame is the 2D engine a lot of Flutter developers already write games with, and for a while it and flutter3d lived side by side without talking to each other. If you had a Flame game and wanted light, shadows, and depth, you were looking at a rewrite. flame_flutter3d is the package that joins them. Below is how it works, and then a small game built on it, from an empty project to a boat collecting buoys on a lake. From 9860d08237177c49dde27ab86ac441d206fb8c2a Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:29:44 +0300 Subject: [PATCH 025/141] The Medium article ends with where to write --- site/assets/articles/medium-flame/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/assets/articles/medium-flame/article.md b/site/assets/articles/medium-flame/article.md index c3db02753..75a60f897 100644 --- a/site/assets/articles/medium-flame/article.md +++ b/site/assets/articles/medium-flame/article.md @@ -360,4 +360,4 @@ Flame is always on top. The layers don't interleave by depth, so HUDs, maps, and - The source, including River Sortie and the package's minimal example: https://github.com/pleiondev/flutter3d - A longer write-up with the arcade demo and more pitfalls, in Russian: https://habr.com/ru/articles/1087246/ -If you try it with your own Flame game and something doesn't work, I'd rather hear about it than guess. Issues are open on GitHub. +If you put it under your own Flame game, tell me how it went, and especially where it broke. Bug reports and questions are welcome on [GitHub](https://github.com/pleiondev/flutter3d/issues), and there's also [r/flutter3d](https://www.reddit.com/r/flutter3d/) for sharing what you built. From f95f7920bdbeb1c4241df3b66ca0519c28a2a722 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 02:44:21 +0300 Subject: [PATCH 026/141] A texture repainted under a running game is drawn at its new size HotSwap keeps a texture with the image file it came from. A swap after the file changed uploads the new picture as a texture of its own, puts it in every slot of every material in the registered scenes and in their environment, and releases the old one through the renderer once no frame in flight samples it. Size, format and mip count may all change; writing into the old handle would have left its smaller mips showing the old picture. LevelLoader watches every map a level names and keeps materialTextures in step with the swap, and LoadedLevel.dispose stops watching them, so nothing is released twice. ext.flutter3d.assets.put takes an image too. --- ARCHITECTURE.md | 2 +- README.md | 2 +- packages/flutter3d_app/CHANGELOG.md | 9 + .../lib/src/hot_swap/hot_swap.dart | 246 +++++++++++++++++- .../lib/src/level/level_loader.dart | 44 +++- .../lib/src/level/loaded_level.dart | 7 + .../flutter3d_app/test/hot_swap_test.dart | 161 ++++++++++++ .../flutter3d_app/test/level_loader_test.dart | 32 +++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- tasks/0.9-engine-roadmap.md | 20 +- 11 files changed, 508 insertions(+), 25 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index cbcdbdf8b..75aeed65c 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2689,7 +2689,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10875 tests** across 42 packages and 10 applications | +| Unit tests | **10880 tests** across 42 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index fe80485c2..0bfc1cdd2 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10875 tests across forty-two packages and nine applications. The +There are 10880 tests across forty-two packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/packages/flutter3d_app/CHANGELOG.md b/packages/flutter3d_app/CHANGELOG.md index 3d8d9f215..f84a6f82f 100644 --- a/packages/flutter3d_app/CHANGELOG.md +++ b/packages/flutter3d_app/CHANGELOG.md @@ -1,5 +1,14 @@ ## Unreleased +- **A texture repainted under a running game is drawn at whatever size it + now is.** `HotSwap.loadTexture` and `registerTexture` watch an image file; + a swap after it changed uploads the new picture as a texture of its own, + puts it where the old one was in every material of the registered scenes + and in their environment, and releases the old one once no frame in flight + samples it. `SwappableTexture.changes` tells anything else holding it. + `LevelLoader` watches every map a level names, and `LoadedLevel.dispose` + stops. `ext.flutter3d.assets.put` accepts an image registered this way. + **A hot reload shows the shaders it reloaded.** Flutter 3.47 reinitializes an asset-loaded shader bundle on hot reload, which the engine's own bundle is, but a pipeline linked from the old code went on drawing it: the library diff --git a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart index fe2f62dd4..ae43d2d21 100644 --- a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart +++ b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart @@ -20,6 +20,14 @@ typedef ModelBytesSource = Future Function(); /// Builds a model from a file's bytes, on the device the game draws with. typedef ModelBuild = Future Function(Uint8List bytes); +/// Reads the current bytes of an image file, or null when they cannot be +/// read now. +typedef TextureBytesSource = Future Function(); + +/// Uploads an image file's bytes as a texture, or null when they do not +/// decode into one. +typedef TextureBuild = Future Function(Uint8List bytes); + /// The renderers and shader libraries a running application has, and what to /// do with them when its code or its shaders change under it. /// @@ -67,6 +75,7 @@ final class HotSwap { final List> _renderers = >[]; final List<_Library> _libraries = <_Library>[]; final List _models = []; + final List _textures = []; final List> _scenes = >[]; final Map> _overrides = >{}; @@ -227,6 +236,165 @@ final class HotSwap { return null; } + /// Uploads the image asset at [path], as a level or a material file does, + /// and hands it back ready to be swapped when the file changes. Null when + /// the file is not there or does not decode. + /// + /// **A texture of any new size.** A swap uploads the new file as a texture + /// of its own and puts it in every slot of every material in the + /// registered scenes that held the old one, and in a scene's environment; + /// then the old one goes back to the device once no frame in flight can + /// still sample it. So a texture may change size, format or mip count + /// under a running game. What holds the texture anywhere else — a + /// post-process look-up table, a billboard atlas — reads + /// [SwappableTexture.texture] or listens to it. + /// + /// **For a game's own images**, the ones no loader reads for it: a decal, + /// a sign, a picture on a wall set up in code. A level's maps are + /// `LevelLoader`'s, which registers them itself through [registerTexture]. + Future loadTexture( + String path, { + required GraphicsDevice device, + TextureSampling sampling = const TextureSampling(), + }) async { + Future read() async { + try { + final data = await rootBundle.load(path); + return data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + } on FlutterError { + return null; + } + } + + Future build(Uint8List bytes) => uploadEncodedImage( + device, + bytes, + decodeImage: defaultImageDecoder, + sampling: sampling, + ); + + final bytes = await read(); + if (bytes == null) return null; + final texture = await build(bytes); + if (texture == null) return null; + return registerTexture( + path, + texture, + read: read, + build: build, + loadedFrom: bytes, + ); + } + + /// Wraps [texture], uploaded from the file [read] reads, so that a [swap] + /// after the file changed uploads it again with [build] and puts the new + /// texture where the old one was — see [loadTexture]. + /// + /// [loadedFrom] are the bytes [texture] was made from, when the caller has + /// them; without them the first swap takes the file as it finds it as the + /// starting point rather than as a change. + SwappableTexture registerTexture( + String path, + TextureHandle texture, { + required TextureBytesSource read, + required TextureBuild build, + Uint8List? loadedFrom, + }) { + final swappable = SwappableTexture._( + path, + texture, + read, + build, + loadedFrom == null + ? null + : _fingerprint(ByteData.sublistView(loadedFrom)), + ); + if (!enabled) return swappable; + _textures + ..removeWhere((SwappableTexture t) => t.path == path) + ..add(swappable); + _registerExtension(); + return swappable; + } + + /// Puts [bytes] in place of the texture registered at [path], as a changed + /// file would: what `ext.flutter3d.assets.put` does for an image. Why it + /// did not take when it did not, or null when it did or nothing is + /// registered there — [hasTexture] tells those two apart. + Future putTexture(String path, Uint8List bytes) async { + if (!enabled) return null; + for (final texture in _textures) { + if (texture.path == path) return _adoptTexture(texture, bytes); + } + return null; + } + + /// Stops watching [texture], for its owner letting it go: a swap after + /// that would upload a texture for nothing and release one already + /// released. + void forgetTexture(SwappableTexture texture) => + _textures.removeWhere((SwappableTexture t) => identical(t, texture)); + + /// Whether a texture is registered at [path]. + bool hasTexture(String path) => + _textures.any((SwappableTexture t) => t.path == path); + + /// Uploads [bytes] for [texture] and puts the result everywhere the old + /// one was. Why not, when it did not. + Future _adoptTexture( + SwappableTexture texture, + Uint8List bytes, + ) async { + final TextureHandle? next; + try { + next = await texture._build(bytes); + } on Object catch (error) { + return '$error'; + } + if (next == null) return 'the new bytes do not decode into an image'; + final previous = texture.texture; + texture._handle.value = next; + _replaceTexture(previous, next); + _renderers.removeWhere((WeakReference r) => r.target == null); + // One renderer releases it: a handle released twice is a mistake a + // backend may refuse. With none alive nothing is drawing it either, and + // it goes with the device. + for (final renderer in _renderers) { + if (renderer.target case final Renderer live) { + live.releaseTextureAfterFrame(previous); + break; + } + } + return null; + } + + /// Every slot of every material in the registered scenes, and every + /// scene's environment, that held [previous] now holds [next]. + int _replaceTexture(TextureHandle previous, TextureHandle next) { + _scenes.removeWhere((WeakReference s) => s.target == null); + final seen = Set.identity(); + TextureHandle? swapped(TextureHandle? slot) => + identical(slot, previous) ? next : slot; + for (final reference in _scenes) { + final scene = reference.target; + if (scene == null) continue; + scene.environment = swapped(scene.environment); + scene.root.traverse((SceneNode node) { + if (node is! MeshNode || !seen.add(node.material)) return; + node.material + ..albedo = swapped(node.material.albedo) + ..normal = swapped(node.material.normal) + ..metallicRoughness = swapped(node.material.metallicRoughness) + ..occlusion = swapped(node.material.occlusion) + ..emissiveTexture = swapped(node.material.emissiveTexture) + ..lightmap = swapped(node.material.lightmap) + ..coatMap = swapped(node.material.coatMap) + ..sheenMap = swapped(node.material.sheenMap); + }); + } + return seen.length; + } + /// Remembers [scene] until it is garbage, for [setMaterial] to find /// materials in. `SceneSurface` registers the scene it draws. void registerScene(Scene scene) { @@ -413,6 +581,28 @@ final class HotSwap { } } if (models.isNotEmpty) _applyOverrides(); + final textures = []; + for (final texture in List.of(_textures)) { + final Uint8List? bytes; + try { + bytes = await texture._read(); + } on Object catch (error) { + refused.add('${texture.path}: could not be read ($error)'); + continue; + } + if (bytes == null) continue; + final fingerprint = _fingerprint(ByteData.sublistView(bytes)); + if (texture._fingerprint == null || fingerprint == texture._fingerprint) { + texture._fingerprint = fingerprint; + continue; + } + texture._fingerprint = fingerprint; + if (await _adoptTexture(texture, bytes) case final String reason) { + refused.add('${texture.path}: $reason'); + } else { + textures.add(texture.path); + } + } _renderers.removeWhere((WeakReference r) => r.target == null); for (final renderer in _renderers) { renderer.target?.relinkShaders(); @@ -422,6 +612,7 @@ final class HotSwap { refreshed: List.unmodifiable(refreshed), refused: List.unmodifiable(refused), models: List.unmodifiable(models), + textures: List.unmodifiable(textures), ); for (final line in refused) { debugPrint('flutter3d: kept the last version that loaded, $line'); @@ -481,11 +672,24 @@ final class HotSwap { 'assets.put takes a path and its bytes in base64', ); } - final report = await instance.put(path, base64Decode(bytes)); + final decoded = base64Decode(bytes); + final report = await instance.put(path, decoded); + if (report == null && instance.hasTexture(path)) { + final refused = await instance.putTexture(path, decoded); + if (refused != null) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + refused, + ); + } + return developer.ServiceExtensionResponse.result( + jsonEncode({'path': path, 'texture': true}), + ); + } if (report == null) { return developer.ServiceExtensionResponse.error( developer.ServiceExtensionResponse.invalidParams, - 'no model is registered at $path', + 'no model or texture is registered at $path', ); } return developer.ServiceExtensionResponse.result( @@ -517,6 +721,7 @@ final class HotSwapReport { required this.refreshed, required this.refused, this.models = const [], + this.textures = const [], }); /// What a profile or release build reports: nothing was touched. @@ -539,11 +744,16 @@ final class HotSwapReport { /// The models that changed and were adopted. final List models; + /// The textures whose file changed and whose new texture took the old + /// one's place. + final List textures; + Map toJson() => { 'renderers': renderers, 'refreshed': refreshed, 'refused': refused, 'models': [for (final model in models) model.toJson()], + 'textures': textures, }; } @@ -639,6 +849,38 @@ final class SwappableModel { } } +/// A texture a game draws, kept so a changed image file reaches every +/// material that samples it — see [HotSwap.loadTexture]. +/// +/// **The handle changes; [texture] follows it.** A swap puts a new texture +/// in place of the old one rather than writing into it, because the new one +/// may be another size and because writing a level over an existing one +/// would leave its smaller mips showing the old picture. +final class SwappableTexture { + SwappableTexture._( + this.path, + TextureHandle texture, + this._read, + this._build, + this._fingerprint, + ) : _handle = ValueNotifier(texture); + + /// The file it was loaded from, as the game named it. + final String path; + + /// The texture drawn now; a new one after each swap. + TextureHandle get texture => _handle.value; + + /// [texture], for something that holds it outside a material and has to + /// hear when it changes. + ValueListenable get changes => _handle; + + final ValueNotifier _handle; + final TextureBytesSource _read; + final TextureBuild _build; + int? _fingerprint; +} + /// What a changed file did to one [SwappableModel]. @immutable final class ModelSwapReport { diff --git a/packages/flutter3d_app/lib/src/level/level_loader.dart b/packages/flutter3d_app/lib/src/level/level_loader.dart index 76f1ef26a..4deb69136 100644 --- a/packages/flutter3d_app/lib/src/level/level_loader.dart +++ b/packages/flutter3d_app/lib/src/level/level_loader.dart @@ -7,6 +7,7 @@ import 'package:flutter3d_editor_core/flutter3d_editor_core.dart' show LevelBatching, LevelScene; import 'package:flutter3d_sim/flutter3d_sim.dart'; +import '../hot_swap/hot_swap.dart'; import 'loaded_level.dart'; import 'visibility_culler.dart'; @@ -395,6 +396,12 @@ final class LevelLoader { // and this is the same kind of fact: the level plays, a wall is flat, and // the person who renamed the file is the one who wants to hear about it. final loadIssues = [...issues]; + // `HR2`: each map watched, so a picture repainted under a running game is + // drawn at whatever size it now is. The level's own map follows the swap, + // so what `rebuildBrushes` binds and what `dispose` releases is the + // texture on screen and never the one already given back. + final watched = []; + final read = readAsset ?? _bundleAsset; for (final source in level.materials.values) { // A material that defers to a `.fmat` names its maps in that file, and // `bindMaterial` uploads them below. Uploading these three as well would @@ -402,12 +409,23 @@ final class LevelLoader { if (source.fmat != null) continue; for (final path in [source.albedo, source.normal, source.orm]) { if (path == null || textures.containsKey(path)) continue; - textures[path] = await _upload( - device, + final (texture, bytes) = await _upload(device, path, read, loadIssues); + textures[path] = texture; + if (texture == null || bytes == null) continue; + final swappable = HotSwap.instance.registerTexture( path, - readAsset ?? _bundleAsset, - loadIssues, + texture, + read: () async => + _LevelMaterialSource._bytes(await read(AssetRequest(path))), + build: (Uint8List next) => uploadEncodedImage( + device, + next, + decodeImage: defaultImageDecoder, + ), + loadedFrom: bytes, ); + swappable.changes.addListener(() => textures[path] = swappable.texture); + watched.add(swappable); } } @@ -557,6 +575,7 @@ final class LevelLoader { ..brushNodes.addAll([ for (final batch in parts.batches) batch.node, ]) + ..watchedTextures.addAll(watched) // A `.fmat`'s maps are uploaded by `bindMaterial` and named nowhere but // on the material, so they are collected here or never released. ..boundTextures.addAll({ @@ -646,22 +665,24 @@ final class LevelLoader { return material; } - static Future _upload( + /// The texture [path] names, and the bytes it was made from for a swap to + /// compare against; both null when it could not be made. + static Future<(TextureHandle?, Uint8List?)> _upload( GraphicsDevice device, String path, AssetBytes read, List issues, ) async { try { - final bytes = await read(AssetRequest(path)); + // The view, not the whole buffer: a reader may hand back a window + // onto a larger one, and the bytes outside it are not this file. + final bytes = _LevelMaterialSource._bytes(await read(AssetRequest(path))); // Mipmapped: a level's walls and floors are the surfaces most often seen // small and at a glancing angle, which is exactly where a single level // crawls as the camera moves. - return await uploadEncodedImage( + final texture = await uploadEncodedImage( device, - // The view, not the whole buffer: a reader may hand back a window - // onto a larger one, and the bytes outside it are not this file. - _LevelMaterialSource._bytes(bytes), + bytes, decodeImage: defaultImageDecoder, sampling: const TextureSampling(), // A KTX2 the device does not sample, or a feature of one the reader @@ -671,6 +692,7 @@ final class LevelLoader { LevelIssue(LevelIssueSeverity.warning, message, where: path), ), ); + return (texture, bytes); } catch (error) { // A missing texture leaves the material flat rather than stopping the // level. Losing a wall texture should not cost the play-test — but it is @@ -683,7 +705,7 @@ final class LevelLoader { where: 'texture "$path"', ), ); - return null; + return (null, null); } } } diff --git a/packages/flutter3d_app/lib/src/level/loaded_level.dart b/packages/flutter3d_app/lib/src/level/loaded_level.dart index 36724a9d5..45b9b3cab 100644 --- a/packages/flutter3d_app/lib/src/level/loaded_level.dart +++ b/packages/flutter3d_app/lib/src/level/loaded_level.dart @@ -4,6 +4,7 @@ import 'package:flutter3d_editor_core/flutter3d_editor_core.dart' import 'package:flutter3d_sim/flutter3d_sim.dart'; import 'package:vector_math/vector_math.dart'; +import '../hot_swap/hot_swap.dart'; import 'visibility_culler.dart'; /// A loaded level, in the two forms the game needs it. @@ -111,6 +112,11 @@ final class LoadedLevel { /// all because [dispose] is the only place they can be given back. final List boundTextures = []; + /// [materialTextures] as `HotSwap` watches them, so an image repainted + /// under a running game is swapped in. Forgotten by [dispose]: a swap after + /// the level is gone would release what [dispose] already released. + final List watchedTextures = []; + /// The baked lightmap the brush batches sample, when the level came with /// one that matched it. Uploaded by the loader, released by [dispose]. TextureHandle? lightmap; @@ -133,6 +139,7 @@ final class LoadedLevel { /// `SharedMeshes.dispose`: a no-op release on flutter_gpu, the one real /// `gl.delete*` per resource on WebGL2. void dispose(GraphicsDevice device) { + watchedTextures.forEach(HotSwap.instance.forgetTexture); for (final mesh in brushMeshes) { device.releaseGeometry(mesh.vertices); device.releaseGeometry(mesh.indices); diff --git a/packages/flutter3d_app/test/hot_swap_test.dart b/packages/flutter3d_app/test/hot_swap_test.dart index 0c3a83f91..b500d2bb5 100644 --- a/packages/flutter3d_app/test/hot_swap_test.dart +++ b/packages/flutter3d_app/test/hot_swap_test.dart @@ -13,6 +13,8 @@ import 'package:flutter3d/flutter3d.dart' hide Material; import 'package:flutter3d/flutter3d.dart' as engine show Material; import 'package:flutter3d_app/flutter3d_app.dart'; import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:flutter3d_hardware/trace.dart' + show RecordingDevice, TraceReleaseTexture; import 'package:flutter_test/flutter_test.dart'; import 'package:vector_math/vector_math.dart' show Vector3, Vector4; @@ -379,4 +381,163 @@ void main() { expect(ship.meshes.single.material.roughness, isNot(0.1)); }); }); + + group('a texture', () { + late CpuDevice device; + late HotSwap swap; + + /// A "file" whose first byte is the side of the square it decodes to, + /// and zero a file that does not decode. + TextureHandle? decode(Uint8List bytes) => bytes.first == 0 + ? null + : device.createTextureFromPixels( + width: bytes.first, + height: bytes.first, + format: TextureFormat.r8g8b8a8UNormInt, + pixels: ByteData(bytes.first * bytes.first * 4), + ); + + setUp(() { + device = CpuDevice( + width: 16, + height: 9, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + swap = HotSwap(enabled: true); + }); + + ({ + SwappableTexture texture, + engine.Material wall, + Scene scene, + void Function(int) write, + }) + stage() { + var file = Uint8List.fromList([4]); + final first = decode(file)!; + final texture = swap.registerTexture( + 'assets/wall.png', + first, + read: () async => file, + build: (Uint8List bytes) async => decode(bytes), + loadedFrom: file, + ); + final wall = engine.Material(name: 'wall') + ..albedo = first + ..normal = first; + final scene = Scene() + ..environment = first + ..add( + MeshNode( + DeviceMesh.upload( + device, + CuboidShape(size: Vector3.all(1.0)).build(), + ), + wall, + ), + ); + swap.registerScene(scene); + return ( + texture: texture, + wall: wall, + scene: scene, + write: (int side) => file = Uint8List.fromList([side]), + ); + } + + test('of a new size takes the old one\'s place in every slot', () async { + // Mutation: skip `_replaceTexture` and the material keeps sampling the + // four-texel texture the file no longer is. + final it = stage(); + final heard = []; + it.texture.changes.addListener(() => heard.add(it.texture.texture)); + + it.write(8); + final report = await swap.swap(); + + expect(report.textures, ['assets/wall.png']); + final now = it.texture.texture; + expect(now.width, 8); + expect(it.wall.albedo, same(now)); + expect(it.wall.normal, same(now), reason: 'every slot that held it'); + expect(it.scene.environment, same(now)); + expect(heard, [now]); + }); + + test('an unchanged file is not uploaded again', () async { + final it = stage(); + final before = it.texture.texture; + + final report = await swap.swap(); + + expect(report.textures, isEmpty); + expect(it.wall.albedo, same(before)); + }); + + test('a file that does not decode keeps the texture that did', () async { + final it = stage(); + final before = it.texture.texture; + + it.write(0); + final report = await swap.swap(); + + expect(report.refused.single, contains('assets/wall.png')); + expect(it.texture.texture, same(before)); + expect(it.wall.albedo, same(before)); + }); + + test( + 'the old texture goes back through a renderer, after its frames', + () async { + // Mutation: release through no renderer and nothing is ever given + // back; release at once and it is gone before the frames that still + // sample it are. + Future> releasesPerFrame({required bool change}) async { + final it = stage(); + final recording = RecordingDevice(device); + final renderer = Renderer.create(device: recording); + final camera = CameraNode()..setPosition(0.0, 0.0, 4.0); + it.scene.add(camera); + final view = RenderView(camera: camera); + swap.registerRenderer(renderer); + void frame() => renderer.render( + width: 16, + height: 9, + scene: it.scene, + views: [view], + ); + for (var i = 0; i < 4; i++) { + frame(); + } + if (change) it.write(8); + await swap.swap(); + final counts = []; + for (var i = 0; i < 4; i++) { + final before = recording.events.length; + frame(); + counts.add( + recording.events + .skip(before) + .whereType() + .length, + ); + } + renderer.dispose(); + return counts; + } + + final unchanged = await releasesPerFrame(change: false); + swap = HotSwap(enabled: true); + final swapped = await releasesPerFrame(change: true); + + expect(unchanged.every((int n) => n == 0), isTrue); + expect(swapped.fold(0, (int a, int b) => a + b), 1); + expect( + swapped.first, + 0, + reason: 'not while a frame may still sample it', + ); + }, + ); + }); } diff --git a/packages/flutter3d_app/test/level_loader_test.dart b/packages/flutter3d_app/test/level_loader_test.dart index af445592a..c86eb8c72 100644 --- a/packages/flutter3d_app/test/level_loader_test.dart +++ b/packages/flutter3d_app/test/level_loader_test.dart @@ -114,6 +114,38 @@ void main() { expect(loaded.materialTextures['assets/textures/wall.png'], isNotNull); }); + test('a wall repainted at another size is drawn at it after a hot swap, ' + 'and is not watched once the level is gone', () async { + // `HR2`. Mutation: leave out the listener that keeps `materialTextures` + // in step, and the level's map names a texture already given back — the + // one `rebuildBrushes` would bind and `dispose` would release again. + var file = encodePng(Uint8List(4), 1, 1); + final loaded = await const LevelLoader().build( + _level(albedo: 'assets/textures/wall.png'), + device: device, + registry: registry, + readAsset: (AssetRequest request) async => ByteData.sublistView(file), + ); + HotSwap.instance.registerScene(loaded.scene); + final before = loaded.materialTextures['assets/textures/wall.png']!; + expect(before.width, 1); + + file = encodePng(Uint8List(4 * 4 * 4), 4, 4); + final report = await HotSwap.instance.swap(); + + expect(report.textures, contains('assets/textures/wall.png')); + final now = loaded.materialTextures['assets/textures/wall.png']!; + expect(now.width, 4); + final walls = []; + loaded.scene.root.traverse((SceneNode node) { + if (node is MeshNode) walls.add(node.material.albedo); + }); + expect(walls, everyElement(same(now))); + + loaded.dispose(device); + expect(HotSwap.instance.hasTexture('assets/textures/wall.png'), isFalse); + }); + test( 'load reads its document through readDocument, not just its textures', () async { diff --git a/site/content/quickstart.md b/site/content/quickstart.md index dbfe800d1..287ed3453 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10875 tests across 42 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10880 tests across 42 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 2bb6426a1..1acfdda61 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10875 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10880 tests need a GPU. --- # Testing -10875 tests across 42 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10880 tests across 42 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -26,7 +26,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | -| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 130 | +| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 135 | | `flutter3d_game` | 279 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10849 rather than 10875: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10854 rather than 10880: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index e70bba0d8..875c1cea2 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -134,11 +134,21 @@ guessed at. `ext.flutter3d.assets.put {path, bytes}` does the same from bytes sent over the VM service, so a phone gets the new model with no restart; the file on disk wins again once it changes. A bundle or model that does not build keeps the last version that did. -*Left:* a texture of a new size in place. `overwriteTexture` already keeps a -handle across a same-size write; a new size needs `TextureHandle`'s fields -mutable across four backends and the conformance suite, and waits until a -texture registry gives it a caller. Environments follow textures. The editor's -save hook arrives with HR5. +*Textures done, without touching the backends:* `HotSwap.loadTexture` and +`registerTexture` keep a texture with the file it came from, and a swap +after the file changed uploads it again as a texture of its own, puts it in +every slot of every material in the registered scenes (and a scene's +environment) that held the old one, and hands the old one to +`Renderer.releaseTextureAfterFrame`. So size, format and mips may all change; +writing into the old handle would have left its smaller mips showing the old +picture, and mutable handles across four backends were not needed after all. +`SwappableTexture.changes` is for holders outside a material. +`LevelLoader` registers every map a level names and keeps +`materialTextures` in step; `LoadedLevel.dispose` stops watching them. +`ext.flutter3d.assets.put` takes an image too. +*Left:* environments built from a file by prefiltering (a cube map and its +irradiance), which need their own loader to register; a model's embedded +images, which swap with the model. **HR3. A level from the editor into a running game (L).** *Done:* saving in the editor while `PlayScreen` has the project running sends the document and From 03c77810d31a97a43795490ee330dd27fdb209fd Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 05:00:14 +0300 Subject: [PATCH 027/141] A decal box paints its picture onto the geometry inside it P3. A DecalNode is its node's unit cube, stamped down its y axis: a picture or none, an sRGB tint with an opacity, an emission, an atlas region, an order between overlapping decals, and an angle limit past which a surface turned away from the box is left alone. RenderSettings.decals switches them on and is off by default. A depth attachment cannot be sampled, so the new Decal stage finds the point under each pixel in the surface buffer, as reflections and occlusion do. The light the point was lit by is read back as the lit colour over the albedo buffer, and the decal's colour is laid under that light, so a decal sits in shadows and under coloured lamps as the surface did; recomputing the light would have meant a second lighting model with every map bound. A pixel needs a factor and a term, and one blend cannot do both, so each batch of sixteen decals and four pictures is two fullscreen draws, scissored to the boxes on screen, per view. A surface that wrote no albedo gets the decal at its own colour. The pass runs between the opaque and the transparent halves of the scene and splits the frame as glass does, so glass in front of a decal is drawn over it; it needs three colour attachments and turns MSAA off. The CPU mirror chooses each mip level from neighbouring texels of the surface buffer, as the GLSL does, rather than from derivatives it does not have. The decal-floor golden is recorded in all four sets: WebGL and WebGPU draw it 0 of 172800 pixels from Impeller, the software set 0.067%. Every other Impeller golden is unchanged. What is left (normals and roughness, decals on glass, exact order across batches, the level format and the editor) is in the 0.9 plan. --- ARCHITECTURE.md | 10 +- README.md | 2 +- packages/flutter3d/README.md | 4 +- .../example/lib/src/spike/backend_web.dart | 2 +- .../example/lib/src/spike/golden_runner.dart | 6 +- .../example/lib/src/spike/golden_scenes.dart | 12 + .../example/lib/src/spike/golden_stages.dart | 85 +++ .../lib/src/spike/golden_store_web.dart | 8 +- packages/flutter3d/test/ambient_test.dart | 2 +- packages/flutter3d/test/anisotropy_test.dart | 4 +- packages/flutter3d/test/auto_batch_test.dart | 4 +- .../flutter3d/test/binding_contract_test.dart | 55 +- packages/flutter3d/test/fog_test.dart | 2 +- .../flutter3d/test/goldens/decal-floor.png | Bin 0 -> 16503 bytes packages/flutter3d/test/grade_test.dart | 2 +- packages/flutter3d/test/halation_test.dart | 2 +- .../test/irradiance_bounce_test.dart | 2 +- .../flutter3d/test/masked_shadow_test.dart | 2 +- .../test/material_vertex_stage_test.dart | 2 +- .../test/per_view_exposure_test.dart | 2 +- .../flutter3d/test/render_scale_test.dart | 2 +- packages/flutter3d/test/sharpen_test.dart | 2 +- .../flutter3d/test/sky_procedural_test.dart | 2 +- packages/flutter3d/test/ssao_blur_test.dart | 2 +- packages/flutter3d_conformance/CHANGELOG.md | 5 + .../lib/src/shader_link_checks.dart | 2 + packages/flutter3d_core/CHANGELOG.md | 14 + .../flutter3d_core/lib/flutter3d_core.dart | 1 + .../src/engine/render/render_settings.dart | 31 +- .../lib/src/engine/render/renderer.dart | 15 +- .../engine/render/renderer_decal_pass.dart | 347 ++++++++++ .../engine/render/renderer_frame_nodes.dart | 78 ++- .../src/engine/render/renderer_post_pass.dart | 4 +- .../engine/render/renderer_shadow_pass.dart | 2 +- .../lib/src/engine/scene/decal_node.dart | 87 +++ .../lib/src/engine/scene/scene.dart | 18 + .../formats/ktx2_supercompression_test.dart | 2 +- packages/flutter3d_cpu/CHANGELOG.md | 7 + .../lib/src/cpu_shaders_builtin.dart | 3 + .../lib/src/cpu_shaders_decal.dart | 208 ++++++ .../lib/src/cpu_shaders_post.dart | 2 +- .../flutter3d_cpu/lib/src/cpu_texture.dart | 2 +- .../flutter3d_cpu/lib/src/portable_log2.dart | 2 +- .../test/cross_backend_test.dart | 5 + packages/flutter3d_cpu/test/decal_test.dart | 429 ++++++++++++ .../test/goldens/decal-floor.png | Bin 0 -> 16263 bytes .../test/gpu_formats_test.dart | 2 +- packages/flutter3d_shaders/CHANGELOG.md | 8 + .../lib/flutter3d_shaders.dart | 1 + .../flutter3d_shaders/lib/stage_bindings.dart | 11 + .../flutter3d_shaders/lib/typed_blocks.dart | 61 ++ .../flutter3d_shaders/lib/uniform_blocks.dart | 27 + .../shaders/flutter3d.shaderbundle.json | 4 + .../flutter3d_shaders/shaders/post/decal.frag | 251 +++++++ packages/flutter3d_webgl/CHANGELOG.md | 4 + .../flutter3d_webgl/lib/engine_shaders.dart | 257 +++++++ .../test/cross_backend_test.dart | 2 + .../test/goldens/decal-floor.png | Bin 0 -> 16525 bytes .../test/lighting_models_test.dart | 2 +- packages/flutter3d_webgl/tool/golden_web.sh | 2 +- packages/flutter3d_webgpu/CHANGELOG.md | 5 + .../flutter3d_webgpu/lib/engine_shaders.dart | 652 ++++++++++++++++++ .../test/cross_backend_test.dart | 2 + .../test/goldens/decal-floor.png | Bin 0 -> 16526 bytes site/content/core/architecture.md | 2 +- site/content/core/backends.md | 2 +- site/content/core/index.md | 2 +- site/content/core/rendering.md | 27 +- site/content/quickstart.md | 2 +- site/content/reference/packages.md | 2 +- site/content/reference/testing.md | 14 +- tasks/0.9-engine-roadmap.md | 36 +- tool/ci.sh | 2 +- 73 files changed, 2796 insertions(+), 64 deletions(-) create mode 100644 packages/flutter3d/test/goldens/decal-floor.png create mode 100644 packages/flutter3d_core/lib/src/engine/render/renderer_decal_pass.dart create mode 100644 packages/flutter3d_core/lib/src/engine/scene/decal_node.dart create mode 100644 packages/flutter3d_cpu/lib/src/cpu_shaders_decal.dart create mode 100644 packages/flutter3d_cpu/test/decal_test.dart create mode 100644 packages/flutter3d_cpu/test/goldens/decal-floor.png create mode 100644 packages/flutter3d_shaders/shaders/post/decal.frag create mode 100644 packages/flutter3d_webgl/test/goldens/decal-floor.png create mode 100644 packages/flutter3d_webgpu/test/goldens/decal-floor.png diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 75aeed65c..fbe72a58b 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -405,7 +405,7 @@ written and read in: ``` point shadows (static) → point shadows → directional shadows → shadow moments reflection probe 0 … n → irradiance update -scene → scene colour copy → transparent → object ids +scene → decals → scene colour copy → transparent → object ids (overlay nodes) → reflections → luminance → depth pyramid ssao → ssao blur → contact shadows → contact shadow resolve camera velocity → object velocity → reactive mask @@ -2689,7 +2689,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10880 tests** across 42 packages and 10 applications | +| Unit tests | **10892 tests** across 42 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | @@ -2729,7 +2729,7 @@ material parameter — is the case `doc/boundary-0.5.0.md` worked through for set belongs to whoever builds on the package rather than to the package itself. -**Golden render tests.** 78 scenes against **four complete independent +**Golden render tests.** 79 scenes against **four complete independent reference sets** — Impeller, the software rasteriser, WebGL2 and WebGPU — each held to zero differing pixels against its own set, with a per-channel tolerance of 8. @@ -3141,7 +3141,7 @@ metres. The directional light's cascades fit the view up to that distance and nothing beyond it casts — a level whose far end matters visually wants the number raised, and pays for it in texels. -**The web backend draws all seventy-eight golden scenes the way Impeller does**, +**The web backend draws all seventy-nine golden scenes the way Impeller does**, between 0.01% and 0.42% of pixels differing by more than 8 per channel — the silhouette's worth of disagreement two rasterisers always have. Two of those numbers fell when the minification filter learned to read a sampler's @@ -3257,7 +3257,7 @@ and charge each of them the bytes, and neither is a change to make on somebody else's behalf. A build that wants it says so in one flag, and the engine's own example takes the same answer from `?backend=webgpu` in the URL — a query parameter rather than a define, because the browser golden stand's whole saving -is one dart2js run serving seventy-eight scenes and both browser backends. +is one dart2js run serving seventy-nine scenes and both browser backends. **`flutter3d_shaders` is one text and no two backends take it the same way.** Impeller compiles the GLSL with `impellerc`; the WebGL2 generator translates it diff --git a/README.md b/README.md index 0bfc1cdd2..03bd39ce0 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10880 tests across forty-two packages and nine applications. The +There are 10892 tests across forty-two packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/packages/flutter3d/README.md b/packages/flutter3d/README.md index fe9ee3f4e..c0bd401a8 100644 --- a/packages/flutter3d/README.md +++ b/packages/flutter3d/README.md @@ -104,7 +104,7 @@ What works today: `CUBICSPLINE` with authored tangents), slerped rotations, an `AnimationPlayer` with play/pause/seek/speed and once/loop/ping-pong, and the decoded node hierarchy rebuilt on instantiation so an animated parent carries its subtree; -- 1620 tests covering projection, scene, sorting, debug draw, raycasting, +- 1621 tests covering projection, scene, sorting, debug draw, raycasting, animation, skinning, lighting, render targets, BVH, LOD, glTF, OBJ and `.f3d`, plus a real frame drawn through `flutter3d_cpu`'s software rasteriser for the ones that need one, all without a GPU. The geometry the engine is written in @@ -322,7 +322,7 @@ lib/src/engine/assets/ glTF, OBJ and .f3d decoders, isolate loading, ca example/lib/ the demo, and the frame capture hook skills/ the conventions, as agent skills — see below bin/skills.dart what copies them into a project that uses this -test/ 1620 tests, all runnable without a GPU +test/ 1621 tests, all runnable without a GPU ``` The GLSL is not here. Every shader this package draws with lives in diff --git a/packages/flutter3d/example/lib/src/spike/backend_web.dart b/packages/flutter3d/example/lib/src/spike/backend_web.dart index 1594c7699..6f7f66298 100644 --- a/packages/flutter3d/example/lib/src/spike/backend_web.dart +++ b/packages/flutter3d/example/lib/src/spike/backend_web.dart @@ -13,7 +13,7 @@ import 'golden_store.dart'; /// /// Read from the URL, exactly the way the golden scene and the record/compare /// direction are read, and for exactly the same arithmetic: one dart2js run -/// serves seventy-eight scenes because the scene is a query parameter, and one +/// serves seventy-nine scenes because the scene is a query parameter, and one /// dart2js run serves the browser backends because this is one too. A /// `--dart-define` here would be a build per backend, which is the whole saving /// of the browser golden stand spent on a single word. diff --git a/packages/flutter3d/example/lib/src/spike/golden_runner.dart b/packages/flutter3d/example/lib/src/spike/golden_runner.dart index a2849360a..946523582 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_runner.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_runner.dart @@ -25,8 +25,8 @@ import 'png.dart'; /// a match and 1 on a mismatch, so `tool/golden.sh` can launch the built /// application once per scene and read the code. The same three names still work /// as `--dart-define`s for a run driven by hand, but the environment is what the -/// harness uses, because a define is a compile-time input and seventy-eight -/// of them are seventy-eight builds. +/// harness uses, because a define is a compile-time input and seventy-nine +/// of them are seventy-nine builds. final class GoldenRunner { GoldenRunner._(this.scene, {required this.update, required this.directory}); @@ -122,7 +122,7 @@ final class GoldenRunner { scene, // The store's answer wins where it has one. A desktop run takes the // direction from the environment and a browser run from the URL, for the - // same reason on both: one build has to serve seventy-eight scenes in both + // same reason on both: one build has to serve seventy-nine scenes in both // directions, and anything the compiler sees is another build. update: updateOverride ?? diff --git a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart index 8a364e6dd..75f0bc71f 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart @@ -1140,6 +1140,18 @@ final List kGoldenScenes = [ ground: false, stage: GoldenStages.smokeSixWay, ), + + // P3. Decals on a floor: one in a shadow and against a box, one over it, + // one that glows. + const GoldenScene( + name: 'decal-floor', + source: 'Cube', + lights: {'none'}, + bloom: false, + ground: false, + stage: GoldenStages.decalFloor, + configure: GoldenStages.decalFloorSettings, + ), ]; /// Looks up a scene by name. diff --git a/packages/flutter3d/example/lib/src/spike/golden_stages.dart b/packages/flutter3d/example/lib/src/spike/golden_stages.dart index f5f5c8aaf..eb65b5c6f 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_stages.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_stages.dart @@ -1578,4 +1578,89 @@ abstract final class GoldenStages { _look(stage.camera, Vector3(0.0, 0.0, 4.0), Vector3.zero()), ); } + + // ------------------------------------------------------------------ P3 + + /// `decal-floor`: three decals on a floor beside a box under the sun — + /// `decal_test.dart`'s floor, with each of its claims in one frame. + /// + /// A ring with a hole in it, half in the box's shadow and reaching up the + /// box's side, which its angle limit keeps it off; an orange square of a + /// higher order over the ring's corner; and a cyan stripe that glows. The + /// ring's picture is drawn here, in Dart, so every backend reads the same + /// texels, and its four quarters are four colours so a mirrored picture + /// shows. + static Future decalFloor(GoldenStage stage) async { + final device = stage.device; + stage.sun.setLocalForward(Vector3(-0.9, -1.0, -0.3).normalized()); + // Enough sky in the shadow for the ring's colours to show there too. + stage.scene.ambientIntensity = 0.25; + const size = 64; + final pixels = Uint8List(size * size * 4); + for (var y = 0; y < size; y++) { + for (var x = 0; x < size; x++) { + final dx = (x + 0.5) / size - 0.5; + final dy = (y + 0.5) / size - 0.5; + final r = math.sqrt(dx * dx + dy * dy); + final quarter = (dx < 0.0 ? 0 : 1) + (dy < 0.0 ? 0 : 2); + final colour = const >[ + [220, 40, 40], + [40, 180, 60], + [50, 80, 220], + [240, 230, 210], + ][quarter]; + final at = (y * size + x) * 4; + pixels + ..[at] = colour[0] + ..[at + 1] = colour[1] + ..[at + 2] = colour[2] + ..[at + 3] = r > 0.18 && r < 0.48 ? 255 : 0; + } + } + final ring = device.createTextureFromPixels( + width: size, + height: size, + format: TextureFormat.r8g8b8a8UNormInt, + pixels: ByteData.sublistView(pixels), + )!; + return GoldenStaged( + nodes: [ + _slab( + device, + Vector3(14.0, 0.2, 14.0), + Vector3(0.0, -0.1, 0.0), + Material( + name: 'floor', + lighting: LightingModel.lambert, + baseColor: Vector4(0.45, 0.45, 0.45, 1.0), + ), + ), + _slab( + device, + Vector3(1.0, 1.0, 1.0), + Vector3(-0.2, 0.5, -0.6), + Material(name: 'box', baseColor: Vector4(0.6, 0.6, 0.65, 1.0)), + ), + DecalNode(texture: ring, name: 'ring') + ..setScale(3.0, 1.2, 3.0) + ..setRotationYawPitchRoll(0.4, 0.0, 0.0) + ..setPosition(0.3, 0.0, 0.2), + DecalNode(color: Vector4(1.0, 0.55, 0.1, 0.85), order: 1, name: 'tag') + ..setScale(0.9, 0.6, 0.9) + ..setPosition(1.4, 0.0, 1.0), + DecalNode( + color: Vector4(0.2, 0.9, 1.0, 1.0), + emissive: Vector3.all(1.5), + name: 'glow', + ) + ..setScale(2.4, 0.6, 0.25) + ..setPosition(-0.6, 0.0, 1.9), + ], + everyFrame: (_, _) => + _look(stage.camera, Vector3(1.0, 4.0, 5.0), Vector3(0.0, 0.0, 0.2)), + ); + } + + static RenderSettings decalFloorSettings(RenderSettings settings) => + settings.copyWith(decals: const DecalSettings(enabled: true)); } diff --git a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart index aeacfce16..96c831f40 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart @@ -40,9 +40,9 @@ void reportLine(String message) { /// The scene named in the page's URL, if any. /// /// A run-time choice here, where the desktop path takes a compile-time define. -/// The reason is arithmetic: the suite is seventy-eight scenes, and rebuilding -/// the bundle for each is seventy-eight dart2js runs to compare seventy-eight -/// pictures. One build and seventy-eight navigations is the same information +/// The reason is arithmetic: the suite is seventy-nine scenes, and rebuilding +/// the bundle for each is seventy-nine dart2js runs to compare seventy-nine +/// pictures. One build and seventy-nine navigations is the same information /// in a fraction of the time. /// /// These three said thirty-nine while the suite had been forty-three for some @@ -58,7 +58,7 @@ String? get sceneOverride { /// Whether this run records rather than compares, from the page's URL. /// /// A run-time choice for the same reason [sceneOverride] is one: the suite is -/// seventy-eight scenes and rebuilding for each would be seventy-eight dart2js +/// seventy-nine scenes and rebuilding for each would be seventy-nine dart2js /// runs. One build serves both directions, and the URL says which. bool get updateOverride => Uri.base.queryParameters['update'] == '1'; diff --git a/packages/flutter3d/test/ambient_test.dart b/packages/flutter3d/test/ambient_test.dart index bb140c44b..7474816e7 100644 --- a/packages/flutter3d/test/ambient_test.dart +++ b/packages/flutter3d/test/ambient_test.dart @@ -141,7 +141,7 @@ void main() { // approximately, bit for bit. // // Mutation: default either end of the hemisphere to anything but white. - // Seventy-eight goldens move at once. + // Seventy-nine goldens move at once. final lit = await _pixels(_ball(), _noSky); final top = _band(lit, 0.15, 0.35); final bottom = _band(lit, 0.65, 0.85); diff --git a/packages/flutter3d/test/anisotropy_test.dart b/packages/flutter3d/test/anisotropy_test.dart index 14a8cdc09..6c2b0a7e0 100644 --- a/packages/flutter3d/test/anisotropy_test.dart +++ b/packages/flutter3d/test/anisotropy_test.dart @@ -24,7 +24,7 @@ /// which spends the taps and throws away what they bought — contrast drops /// *below* the trilinear baseline and the first assertion fails; and letting /// a sampler that asked for one reach the new path at all, which would move a -/// backend seventy-eight golden scenes are recorded on. +/// backend seventy-nine golden scenes are recorded on. /// /// **One they do not catch, said rather than hidden:** taking the taps along /// the *short* axis instead of the long one passes every line here. The @@ -213,7 +213,7 @@ void main() { }); test('asking for one tap is the old sampler exactly', () async { - // What makes this safe to add to the backend seventy-eight golden scenes are + // What makes this safe to add to the backend seventy-nine golden scenes are // recorded on. Not "close": the same bytes, because with `anisotropy` at // one the new path is not reached at all. expect(await _floorAt(1), await _floorAt(1)); diff --git a/packages/flutter3d/test/auto_batch_test.dart b/packages/flutter3d/test/auto_batch_test.dart index 16b78e235..1b3ee79fc 100644 --- a/packages/flutter3d/test/auto_batch_test.dart +++ b/packages/flutter3d/test/auto_batch_test.dart @@ -22,7 +22,7 @@ /// three backends.** It computes in Dart doubles; Impeller, WebGL and WebGPU /// compute in 32-bit floats, where those two expressions have far less room /// before they part. Nothing here can run them, so the setting stays off by -/// default — which is also what keeps the seventy-eight goldens where they are. +/// default — which is also what keeps the seventy-nine goldens where they are. library; import 'dart:math' as math; @@ -191,7 +191,7 @@ void main() { }); test('nothing is batched unless it is asked for', () async { - // Which is what keeps the seventy-eight goldens where they are: the default + // Which is what keeps the seventy-nine goldens where they are: the default // frame is the frame it always was, to the byte. final device = _device(); final scene = _field(device, count: 100); diff --git a/packages/flutter3d/test/binding_contract_test.dart b/packages/flutter3d/test/binding_contract_test.dart index 0eec9ef69..727332379 100644 --- a/packages/flutter3d/test/binding_contract_test.dart +++ b/packages/flutter3d/test/binding_contract_test.dart @@ -27,9 +27,13 @@ import 'package:vector_math/vector_math.dart'; /// The declared slots [draw] left unbound, one line each. List _unbound( - void Function(FakeBackend device, Renderer renderer) draw, -) { - final device = FakeBackend(stageBindings: stageBindings); + void Function(FakeBackend device, Renderer renderer) draw, { + int attachments = 2, +}) { + final device = FakeBackend( + stageBindings: stageBindings, + maxColorAttachments: attachments, + ); final renderer = Renderer.create(device: device); draw(device, renderer); return device.bindingViolations.toSet().toList()..sort(); @@ -143,4 +147,49 @@ void main() { }); expect(unbound, isEmpty, reason: unbound.join('\n')); }); + + test('nor does the decal pass, with pictures and without', () { + // `P3`: four picture slots a draw binds whether or not a decal names + // them, and two buffers out of the scene pass. Three attachments, or the + // pass is refused before it binds anything. + // + // Mutation: binding only the slots a batch fills leaves three of the four + // pictures unbound, which is a crash on Metal. + final ran = []; + final unbound = _unbound((FakeBackend device, Renderer renderer) { + final picture = device.createTextureFromPixels( + width: 4, + height: 4, + format: TextureFormat.r8g8b8a8UNormInt, + pixels: ByteData(4 * 4 * 4), + )!; + final scene = Scene() + ..add( + MeshNode( + DeviceMesh.upload( + device, + CuboidShape(size: Vector3(4.0, 0.2, 4.0)).build(), + ), + Material(name: 'floor'), + )..setPosition(0.0, -0.1, 0.0), + ) + ..add(DecalNode(texture: picture)..setScale(2.0, 1.0, 2.0)) + ..add(DecalNode()..setScale(1.0, 1.0, 1.0)) + ..add( + CameraNode() + ..setPosition(0.0, 4.0, 2.0) + ..lookAt(Vector3.zero()), + ); + final result = renderer.render( + width: 64, + height: 64, + scene: scene, + views: [RenderView(camera: scene.cameras.single)], + settings: const RenderSettings(decals: DecalSettings(enabled: true)), + ); + ran.addAll(result.passes.map((p) => p.name)); + }, attachments: 3); + expect(ran, contains('decals')); + expect(unbound, isEmpty, reason: unbound.join('\n')); + }); } diff --git a/packages/flutter3d/test/fog_test.dart b/packages/flutter3d/test/fog_test.dart index b3063caf6..b6761f91e 100644 --- a/packages/flutter3d/test/fog_test.dart +++ b/packages/flutter3d/test/fog_test.dart @@ -151,7 +151,7 @@ void main() { test('no fog is byte-identical to the fog nobody asked for', () async { // The early return at zero density is not an optimisation. The golden sets - // — seventy-eight scenes, zero-pixel threshold, three backends — are all + // — seventy-nine scenes, zero-pixel threshold, three backends — are all // recorded with the default `FogSettings()`, and this is the property that // lets them stay recorded. final none = FogSettings(); diff --git a/packages/flutter3d/test/goldens/decal-floor.png b/packages/flutter3d/test/goldens/decal-floor.png new file mode 100644 index 0000000000000000000000000000000000000000..a22e15067c96a87afca826b812b148077592be9b GIT binary patch literal 16503 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 literal 0 HcmV?d00001 diff --git a/packages/flutter3d/test/grade_test.dart b/packages/flutter3d/test/grade_test.dart index 247cd834b..7c0bd87e6 100644 --- a/packages/flutter3d/test/grade_test.dart +++ b/packages/flutter3d/test/grade_test.dart @@ -63,7 +63,7 @@ Future> _ramp(LookSettings look, {int width = 256}) async { void main() { test('every default is an exact identity', () async { - // The promise seventy-eight goldens rest on. Neutral for these three is + // The promise seventy-nine goldens rest on. Neutral for these three is // (0,0,0) added, (1,1,1) as an exponent and (1,1,1) multiplied, and the // composite has to take all three without moving a byte. final plain = await _ramp(const LookSettings()); diff --git a/packages/flutter3d/test/halation_test.dart b/packages/flutter3d/test/halation_test.dart index 910292c87..b26b235ff 100644 --- a/packages/flutter3d/test/halation_test.dart +++ b/packages/flutter3d/test/halation_test.dart @@ -87,7 +87,7 @@ void main() { test('zero is an exact identity, and is the default', () async { // The multiplier is one on every channel at zero, so the chain comes out - // byte for byte as it did — which is what seventy-eight goldens need. + // byte for byte as it did — which is what seventy-nine goldens need. expect(const BloomSettings().halation, 0.0); final a = await _glow(halation: 0.0); final b = await _glow(halation: 0.0); diff --git a/packages/flutter3d/test/irradiance_bounce_test.dart b/packages/flutter3d/test/irradiance_bounce_test.dart index cb3325c58..a697f4efc 100644 --- a/packages/flutter3d/test/irradiance_bounce_test.dart +++ b/packages/flutter3d/test/irradiance_bounce_test.dart @@ -136,7 +136,7 @@ double _tint(Uint8List rgba) { void main() { test('off, the picture is the bytes it was', () async { // The clause every recorded frame depends on. A field is null by default, - // so this is the path seventy-eight goldens take. + // so this is the path seventy-nine goldens take. final a = await _draw(field: false, redWall: true); final b = await _draw(field: false, redWall: true); expect(a, orderedEquals(b)); diff --git a/packages/flutter3d/test/masked_shadow_test.dart b/packages/flutter3d/test/masked_shadow_test.dart index 9ea6e71e2..11e0532bd 100644 --- a/packages/flutter3d/test/masked_shadow_test.dart +++ b/packages/flutter3d/test/masked_shadow_test.dart @@ -223,7 +223,7 @@ void main() { test('a caster with no map keeps the shadow it always cast', () async { // **The half that lets this land.** A material that is not cut out goes // through the stage it has always gone through, with no sampler in the - // pipeline and no texture bound per draw, so the seventy-eight goldens + // pipeline and no texture bound per draw, so the seventy-nine goldens // recorded against that stage cannot move. Asking for MASK with no map to // read is the same case: there is nothing to cut out. final plain = await _frame(masked: false, withTexture: false); diff --git a/packages/flutter3d/test/material_vertex_stage_test.dart b/packages/flutter3d/test/material_vertex_stage_test.dart index 0eda03c58..715be1530 100644 --- a/packages/flutter3d/test/material_vertex_stage_test.dart +++ b/packages/flutter3d/test/material_vertex_stage_test.dart @@ -172,7 +172,7 @@ void main() { // **The half that lets this land**, and the reason the seam is a null field // rather than a new required one: a material saying nothing about its vertex // stage goes through the pipeline it has always gone through, which is also - // why the seventy-eight goldens cannot move. + // why the seventy-nine goldens cannot move. final withSeam = await _draw(lighting: _plain); final device = CpuDevice( diff --git a/packages/flutter3d/test/per_view_exposure_test.dart b/packages/flutter3d/test/per_view_exposure_test.dart index 928ab0c2c..f0acbc6d8 100644 --- a/packages/flutter3d/test/per_view_exposure_test.dart +++ b/packages/flutter3d/test/per_view_exposure_test.dart @@ -157,7 +157,7 @@ void main() { // **The strongest form of "this costs nothing where it is not wanted".** // One view is the whole frame, so its rectangle is the whole histogram // and its exposure is the frame's. The composite takes its single-draw - // path, and seventy-eight goldens stay where they are. + // path, and seventy-nine goldens stay where they are. final off = await frame( const RenderSettings(autoExposure: AutoExposureSettings(enabled: true)), ); diff --git a/packages/flutter3d/test/render_scale_test.dart b/packages/flutter3d/test/render_scale_test.dart index 61a8419b2..4dd0bd26b 100644 --- a/packages/flutter3d/test/render_scale_test.dart +++ b/packages/flutter3d/test/render_scale_test.dart @@ -36,7 +36,7 @@ import 'package:vector_math/vector_math.dart'; void main() { test('one is the whole resolution, and is the default', () { - // Seventy-eight goldens are recorded at the size they asked for. A default + // Seventy-nine goldens are recorded at the size they asked for. A default // that shrank anything would move every one of them. expect(const RenderSettings().renderScale, 1.0); expect(_drawn(1.0), (width: 64, height: 64)); diff --git a/packages/flutter3d/test/sharpen_test.dart b/packages/flutter3d/test/sharpen_test.dart index 512e848dd..4d2a2bb21 100644 --- a/packages/flutter3d/test/sharpen_test.dart +++ b/packages/flutter3d/test/sharpen_test.dart @@ -95,7 +95,7 @@ int _steepest(List row) { void main() { test('zero is an exact identity, and is the default', () async { - // Seventy-eight goldens go through this pass. The shader returns the centre + // Seventy-nine goldens go through this pass. The shader returns the centre // untouched at zero rather than running a kernel that rounds to nothing, // because "rounds to nothing" is a claim about the target's bit depth. expect(const AntiAliasSettings().sharpen, 0.0); diff --git a/packages/flutter3d/test/sky_procedural_test.dart b/packages/flutter3d/test/sky_procedural_test.dart index 864609085..3f22d5119 100644 --- a/packages/flutter3d/test/sky_procedural_test.dart +++ b/packages/flutter3d/test/sky_procedural_test.dart @@ -167,7 +167,7 @@ double _mean(Uint8List rgba, {required int channel}) { void main() { _hardEdgedSun(); test('no sky is byte-identical to the sky nobody asked for', () async { - // The property seventy-eight goldens rest on: a frame with no sky in it is + // The property seventy-nine goldens rest on: a frame with no sky in it is // the frame this renderer has always drawn. // // Mutation: drop the early return. The sky is drawn into every scene that diff --git a/packages/flutter3d/test/ssao_blur_test.dart b/packages/flutter3d/test/ssao_blur_test.dart index ec770e01d..b1c103f5a 100644 --- a/packages/flutter3d/test/ssao_blur_test.dart +++ b/packages/flutter3d/test/ssao_blur_test.dart @@ -75,7 +75,7 @@ void main() { test('no taps is no pass, and is the default', () async { // The occlusion is already off by default, and this is a second // full-screen pass over it — so nothing pays for either until two things - // are switched on. Seventy-eight goldens depend on the frame being what it + // are switched on. Seventy-nine goldens depend on the frame being what it // was. expect(const AmbientOcclusionSettings().blurTaps, 0); expect(await _frame(blurTaps: 0), await _frame(blurTaps: 0)); diff --git a/packages/flutter3d_conformance/CHANGELOG.md b/packages/flutter3d_conformance/CHANGELOG.md index 7357f9088..a054a117b 100644 --- a/packages/flutter3d_conformance/CHANGELOG.md +++ b/packages/flutter3d_conformance/CHANGELOG.md @@ -1,3 +1,8 @@ +## Unreleased + +**The linking check names `Decal`**, so a backend whose bundle lacks it +fails the check rather than the first frame with a decal in it. + ## 0.8.1 **The linking check names `ContactShadowResolve`**, the stage diff --git a/packages/flutter3d_conformance/lib/src/shader_link_checks.dart b/packages/flutter3d_conformance/lib/src/shader_link_checks.dart index f9ffd75b2..1e6038f6e 100644 --- a/packages/flutter3d_conformance/lib/src/shader_link_checks.dart +++ b/packages/flutter3d_conformance/lib/src/shader_link_checks.dart @@ -145,6 +145,8 @@ Future checkLinking(GraphicsDevice device) async { 'VelocityNeighborMax', 'MotionBlur', 'ViewportShade', + // `P3`: the decals, over the surface and albedo buffers. + 'Decal', 'MrtProbe', // `H5`: the field kernel the conformance suite steps `FieldPass` with. 'FieldDecay', diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index 25bfb626c..525b7ed07 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -1,5 +1,19 @@ ## Unreleased +- **Projected box decals.** A `DecalNode` is a box, its node's unit cube, + that paints a picture onto whatever geometry stands inside it: a texture + (or none) times an sRGB tint with an opacity, an optional emission, a + region of an atlas, an `order` between overlapping decals, and an angle + limit past which a surface turned away from the box's up is left alone. + `RenderSettings.decals` switches them on and is off by default. The pass + reads the point under each pixel back out of the surface buffer, since a + depth attachment cannot be sampled, and lays the decal's colour under the + light the surface was lit by, read back through the albedo buffer, so a + decal in a shadow is in the shadow. It runs between the opaque half of the + scene and the transparent one, which it splits the frame for, so glass in + front of a decal is drawn over it. It needs three colour attachments and + turns multisampling off, as every reader of the surface buffer does. + - **A reload reaches the contributors too.** `Renderer.relinkShaders` asks every `PassContributor` to drop what it linked, through the new `PassContributor.relinkShaders`, which does nothing by default. diff --git a/packages/flutter3d_core/lib/flutter3d_core.dart b/packages/flutter3d_core/lib/flutter3d_core.dart index e9e29a026..a7a02cbc4 100644 --- a/packages/flutter3d_core/lib/flutter3d_core.dart +++ b/packages/flutter3d_core/lib/flutter3d_core.dart @@ -111,6 +111,7 @@ export 'src/engine/render/view_model_node.dart'; export 'src/engine/scene/atmosphere.dart'; export 'src/engine/scene/bvh.dart'; export 'src/engine/scene/camera_node.dart'; +export 'src/engine/scene/decal_node.dart'; export 'src/engine/scene/free_look.dart'; export 'src/engine/scene/impostor_node.dart'; export 'src/engine/scene/instanced_mesh_node.dart'; diff --git a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart index 9369099e3..e6818d86d 100644 --- a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart +++ b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart @@ -828,6 +828,25 @@ final class MotionBlurSettings { ); } +/// `P3`'s projected decals: every visible `DecalNode` in the scene painted +/// onto the geometry inside its box. +/// +/// **Off by default, and not because a scene without decals would pay.** A +/// frame with no visible decal registers the pass, finds it inactive and culls +/// it, whatever this says. It is off because a frame with one reads the +/// scene's surface and albedo buffers, and that turns multisampling off and +/// splits the scene pass around the decals so glass in front of one is drawn +/// over it rather than painted. Those are costs a caller turns on, the way +/// they turn on every other effect that reads the buffers. +final class DecalSettings { + const DecalSettings({this.enabled = false}); + + final bool enabled; + + DecalSettings copyWith({bool? enabled}) => + DecalSettings(enabled: enabled ?? this.enabled); +} + /// Which viewport shading a frame is drawn with — `gfx-43n`, `44n`, `45n`. /// /// **A final class with const instances rather than an enum**, the shape @@ -1150,6 +1169,7 @@ final class RenderSettings { this.volumetricFog = const VolumetricFogSettings(), this.depthOfField = const DepthOfFieldSettings(), this.motionBlur = const MotionBlurSettings(), + this.decals = const DecalSettings(), this.viewportShading = const ViewportShadingSettings(), this.transparency = TransparencyMode.sorted, }) : assert(anisotropy >= 1, 'anisotropy is a count of taps, one or more'), @@ -1268,7 +1288,7 @@ final class RenderSettings { /// eight-bit answer — `auto_batch_test.dart` holds a hundred cubes, turned and /// scaled, to byte equality. Impeller, WebGL and WebGPU compute in 32-bit /// floats, where those expressions have far less room before they part, and - /// nothing headless can run them. So the seventy-eight goldens keep the frame + /// nothing headless can run them. So the seventy-nine goldens keep the frame /// they have, and an application that wants the draw calls back asks. /// /// Shadows and picking are unaffected: both walk the scene themselves and @@ -1357,6 +1377,9 @@ final class RenderSettings { /// `R6`'s motion blur along the velocity buffer. final MotionBlurSettings motionBlur; + /// `P3`'s projected decals, painted onto the scene before its glass. + final DecalSettings decals; + /// `gfx-43n`/`44n`/`45n`'s shading read out of the surface buffer rather /// than out of the materials. final ViewportShadingSettings viewportShading; @@ -1658,6 +1681,7 @@ final class RenderSettings { VolumetricFogSettings? volumetricFog, DepthOfFieldSettings? depthOfField, MotionBlurSettings? motionBlur, + DecalSettings? decals, ViewportShadingSettings? viewportShading, TransparencyMode? transparency, }) => RenderSettings( @@ -1704,6 +1728,7 @@ final class RenderSettings { volumetricFog: volumetricFog ?? this.volumetricFog, depthOfField: depthOfField ?? this.depthOfField, motionBlur: motionBlur ?? this.motionBlur, + decals: decals ?? this.decals, viewportShading: viewportShading ?? this.viewportShading, transparency: transparency ?? this.transparency, ); @@ -1783,6 +1808,8 @@ final class RenderSettings { // Reflection probes are registered here, one per probe in the scene, and // are named by index rather than by a constant — see [probePassName]. 'scene', + // `P3`: painted onto the opaque half, before the glass is drawn over it. + 'decals', // `M3`: on a frame with glass, the scene as the opaque half left it, and // the glass and the transparent half drawn over it. Culled on any other. 'scene colour copy', @@ -2025,7 +2052,7 @@ final class DisplayTransform { /// **Everything here defaults to doing nothing, exactly.** Not nearly nothing: /// a vignette of zero multiplies by one and grain of zero adds zero, so a scene /// that asks for none of it composites to the same bytes it did before this -/// existed. Seventy-eight goldens depend on that being exact, and the composite +/// existed. Seventy-nine goldens depend on that being exact, and the composite /// pass already keeps the same promise for ambient occlusion. /// /// Applied in the composite rather than as passes of their own, which is the diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer.dart b/packages/flutter3d_core/lib/src/engine/render/renderer.dart index 6b51491a8..6fde6281a 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer.dart @@ -11,6 +11,7 @@ import 'package:vector_math/vector_math.dart' as vm; import '../geometry/device_mesh.dart'; import '../scene/camera_node.dart'; +import '../scene/decal_node.dart'; import '../scene/instanced_mesh_node.dart'; import '../scene/irradiance_field.dart'; import '../scene/irradiance_gather.dart' show kIrradianceReach; @@ -65,6 +66,7 @@ export 'render_settings.dart'; part 'renderer_batch.dart'; part 'renderer_contributor_lights.dart'; +part 'renderer_decal_pass.dart'; part 'renderer_fog_pass.dart'; part 'renderer_frame_nodes.dart'; part 'renderer_irradiance_pass.dart'; @@ -176,6 +178,7 @@ final class Renderer implements RenderServices { required this.velocityNeighborMaxShader, required this.motionBlurShader, required this.viewportShadeShader, + required this.decalShader, required this.wboitResolveShader, required this.sceneColourCopyShader, required TextureHandle fallbackAlbedo, @@ -365,6 +368,9 @@ final class Renderer implements RenderServices { /// `gfx-43n`/`44n`/`45n`'s three branches over the surface buffer. final ShaderHandle viewportShadeShader; + /// `P3`'s projected decals, read out of the surface and albedo buffers. + final ShaderHandle decalShader; + /// `R8`'s resolve: the transparent layers' weighted average, laid over the /// scene. final ShaderHandle wboitResolveShader; @@ -756,6 +762,7 @@ final class Renderer implements RenderServices { final ProbeInfoBlock _probeInfo = ProbeInfoBlock(); final ReflectionInfoBlock _reflectionInfo = ReflectionInfoBlock(); final ShadeInfoBlock _shadeInfo = ShadeInfoBlock(); + final DecalInfoBlock _decalInfo = DecalInfoBlock(); final ShadowLightBlock _shadowLight = ShadowLightBlock(); final ShaftInfoBlock _shaftInfo = ShaftInfoBlock(); final VolumeFogInfoBlock _volumeFogInfo = VolumeFogInfoBlock(); @@ -1521,7 +1528,7 @@ final class Renderer implements RenderServices { /// `gfx-76n`'s strength, in x. Neutral is zero, which the composite reads as /// a multiplier of exactly one — the same arrangement the occlusion's - /// strength has, and for the same reason: seventy-eight goldens go through this + /// strength has, and for the same reason: seventy-nine goldens go through this /// block and "off" has to be a number the shader cancels, not one it nearly /// cancels. Float32List get _compositeContact => _compositeInfo.contact; @@ -1606,6 +1613,7 @@ final class Renderer implements RenderServices { velocityNeighborMaxShader: require('VelocityNeighborMax'), motionBlurShader: require('MotionBlur'), viewportShadeShader: require('ViewportShade'), + decalShader: require('Decal'), wboitResolveShader: require('WboitResolve'), sceneColourCopyShader: require('SceneColourCopy'), fallbackAlbedo: @@ -2530,6 +2538,9 @@ final class Renderer implements RenderServices { graph.addNode(irradiance); graph ..addNode(scene) + // `P3`: onto the opaque half, so the copy the glass reads holds the + // decals and the glass is drawn over them rather than painted. + ..addNode(scene.decals) // `M3`: the copy of the scene and the transparent half drawn over it, // straight after the scene they split, and culled on a frame without // glass. @@ -3626,6 +3637,7 @@ final class Renderer implements RenderServices { contributors: contributors.active.toList(growable: false), shadowCaster: shadowCaster, lightOverflow: planLights.overflow, + settings: settings, ), bloom: _BloomNode(this, settings.bloom), composite: _CompositeNode(this, scene, ordered, settings), @@ -4081,6 +4093,7 @@ final class Renderer implements RenderServices { contributors: contributors.active.toList(growable: false), shadowCaster: shadowCaster, lightOverflow: lightOverflowCount, + settings: settings, ); final bloomNode = _BloomNode(this, settings.bloom); compositeNode = _CompositeNode(this, scene, ordered, settings); diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_decal_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_decal_pass.dart new file mode 100644 index 000000000..c37b13c5b --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_decal_pass.dart @@ -0,0 +1,347 @@ +/// Projected box decals — `P3`. +/// +/// A `part` of `renderer.dart` — see `renderer_shadow_pass.dart` for why. +/// +/// **One pass over the scene target, after the opaque half and before the +/// glass.** Each view's decals are cut into batches the stage can hold — +/// sixteen decals and four pictures — and each batch is two fullscreen draws, +/// scissored to the screen rectangle its boxes cover: the factor that swaps +/// the albedo under the light the surface was lit by, multiplied in, and the +/// term an unlit surface and an emissive decal add. `decal.frag` says why it +/// takes two. +/// +/// **Order is kept within a batch and approximated across two.** Inside one +/// draw the stage stacks its decals in order and swaps the albedo once, which +/// is exact. A second batch swaps from the surface's albedo again rather than +/// from the one the first left, so where a decal of the second lies over one +/// of the first, the light under the first is read through the surface's +/// colour rather than the first decal's. Seventeen overlapping decals, or a +/// fifth picture, are where that starts. +part of 'renderer.dart'; + +/// One draw's decals and the pictures they read. +final class _DecalBatch { + /// Decals per draw, as `kMaxDecals` in `decal.frag` holds them. + static const int maxDecals = 16; + + /// Pictures per draw, the stage's four `decal_texture_` slots. + static const int maxSlots = 4; + + final List decals = []; + final List slots = []; + + /// The screen rectangle the boxes cover, in pixels from the top left. + int left = 1 << 30; + int top = 1 << 30; + int right = -1; + int bottom = -1; + + /// Whether [decal] fits: a free place, and its picture already bound or a + /// free slot for it. + bool admits(DecalNode decal) { + if (decals.length >= maxDecals) return false; + final texture = decal.texture; + return texture == null || + slots.contains(texture) || + slots.length < maxSlots; + } + + /// The slot [texture] is read through, binding it if it is new; minus one + /// for none. + int slotOf(TextureHandle? texture) { + if (texture == null) return -1; + final at = slots.indexOf(texture); + if (at >= 0) return at; + slots.add(texture); + return slots.length - 1; + } + + void cover(ScreenRect rect) { + left = math.min(left, rect.x); + top = math.min(top, rect.y); + right = math.max(right, rect.x + rect.width); + bottom = math.max(bottom, rect.y + rect.height); + } + + ScreenRect get covered => + ScreenRect(x: left, y: top, width: right - left, height: bottom - top); +} + +extension _DecalPass on Renderer { + /// Multiplies the target by what the stage writes, leaving alpha alone. + static const BlendState _multiply = BlendState( + sourceColorFactor: BlendFactor.zero, + destinationColorFactor: BlendFactor.sourceColor, + sourceAlphaFactor: BlendFactor.zero, + destinationAlphaFactor: BlendFactor.one, + ); + + /// Adds what the stage writes, leaving alpha alone. + static const BlendState _add = BlendState( + sourceColorFactor: BlendFactor.one, + destinationColorFactor: BlendFactor.one, + sourceAlphaFactor: BlendFactor.zero, + destinationAlphaFactor: BlendFactor.one, + ); + + /// Trilinear and clamped: a decal's picture is minified as a material's + /// is, and its edge must not wrap the opposite edge in. + static const SamplerOptions _pictureSampler = SamplerOptions( + minFilter: MinMagFilter.linear, + magFilter: MinMagFilter.linear, + mipFilter: MipFilter.linear, + ); + + /// The decals [views] can see, painted into [target] in order. + void _encodeDecals({ + required TextureHandle target, + required TextureHandle surface, + required TextureHandle albedo, + required List decals, + required List views, + required FramePassState passState, + }) { + // Higher order over lower, attachment order between equals: a stable + // sort, which `List.sort` does not promise, so the index breaks the tie. + final visible = + <(int, DecalNode)>[ + for (var i = 0; i < decals.length; i++) + if (decals[i].visibleInHierarchy) (i, decals[i]), + ]..sort((a, b) { + final byOrder = a.$2.order.compareTo(b.$2.order); + return byOrder != 0 ? byOrder : a.$1.compareTo(b.$1); + }); + if (visible.isEmpty) return; + + final width = target.width; + final height = target.height; + final pass = device.beginRenderPass( + RenderPassDescriptor( + label: _passLabel, + colors: [ + ColorTarget(texture: target, loadAction: LoadAction.load), + ], + ), + ); + pass + ..bindPipeline( + _fullscreenPipelines[decalShader] ??= device.createPipeline( + fullscreenVertexShader, + decalShader, + ), + ) + ..bindVertexBuffer(_fullscreenTriangle, 3) + ..bindIndexBuffer(_identityIndices(3), IndexType.int32, 3) + ..bindTexture( + decalShader, + 'surface_texture', + surface, + sampler: SamplerOptions.nearestClamp, + ) + ..bindTexture( + decalShader, + 'albedo_texture', + albedo, + sampler: SamplerOptions.nearestClamp, + ); + passState.invalidatePipeline(); + + final info = _decalInfo; + info.params + ..[1] = 1.0 / width + ..[2] = 1.0 / height; + final full = ScreenRect(width: width, height: height); + for (final view in views) { + final rect = Renderer._viewportPixels( + view.viewportFraction, + width, + height, + ); + final aspect = rect.width / rect.height; + // Unadjusted for the depth range, for the reason `_encodeReflections` + // gives: the stage unprojects both ends of a pixel's ray at clip depths + // nought and one. Adjusted for the framebuffer origin, so the texture + // coordinates it derives land on the rows the buffer was written in. + final viewProjection = view.camera.viewProjection(aspect); + final inverse = vm.Matrix4.copy( + toFramebufferOrigin(viewProjection, device.framebufferOrigin), + )..invert(); + info.inverseViewProjection.setAll(0, inverse.storage); + final eye = view.camera.readWorldPosition(); + final forward = vm.Vector3.zero(); + view.camera.readForward(forward); + info.camera + ..[0] = eye.x + ..[1] = eye.y + ..[2] = eye.z; + info.forward + ..[0] = forward.x + ..[1] = forward.y + ..[2] = forward.z; + info.view + ..[0] = rect.x / width + ..[1] = rect.y / height + ..[2] = rect.width / width + ..[3] = rect.height / height; + + for (final batch in _decalBatches(visible, viewProjection, rect)) { + _fillDecalBatch(batch); + for (var i = 0; i < _DecalBatch.maxSlots; i++) { + pass.bindTexture( + decalShader, + 'decal_texture_$i', + i < batch.slots.length ? batch.slots[i] : fallbackAlbedo, + sampler: _pictureSampler, + ); + } + final scissor = batch.covered; + for (final term in const [0.0, 1.0]) { + info.params[3] = term; + pass + ..setState( + Renderer._kFullscreenState.copyWith( + viewport: full, + scissor: scissor, + blend: term == 0.0 ? _multiply : _add, + ), + ) + ..bindBlock(decalShader, info) + ..draw(); + _frameCounters?.drawCalls++; + } + } + } + pass.submit(); + } + + /// [visible]'s decals cut into draws, leaving out every one whose box the + /// view does not see. + List<_DecalBatch> _decalBatches( + List<(int, DecalNode)> visible, + vm.Matrix4 viewProjection, + ScreenRect view, + ) { + final batches = <_DecalBatch>[]; + for (final (_, decal) in visible) { + final covers = _decalCover(decal, viewProjection, view); + if (covers == null) continue; + if (batches.isEmpty || !batches.last.admits(decal)) { + batches.add(_DecalBatch()); + } + batches.last + ..decals.add(decal) + ..slotOf(decal.texture) + ..cover(covers); + } + return batches; + } + + /// The pixels of [view] that [decal]'s box can reach, or null for none. + /// + /// The eight corners projected and their bounds taken, a pixel wider on + /// each side for the rounding. A corner behind the eye has no place on the + /// screen, and then the box may reach anywhere in the view. + ScreenRect? _decalCover( + DecalNode decal, + vm.Matrix4 viewProjection, + ScreenRect view, + ) { + final toClip = vm.Matrix4.copy(viewProjection)..multiply(decal.worldMatrix); + final corners = [ + for (var corner = 0; corner < 8; corner++) + toClip.transform( + vm.Vector4( + (corner & 1) == 0 ? -0.5 : 0.5, + (corner & 2) == 0 ? -0.5 : 0.5, + (corner & 4) == 0 ? -0.5 : 0.5, + 1.0, + ), + ), + ]; + if (corners.any((clip) => clip.w <= 1e-6)) return view; + final xs = corners.map((clip) => clip.x / clip.w); + final ys = corners.map((clip) => clip.y / clip.w); + final minX = xs.reduce(math.min); + final maxX = xs.reduce(math.max); + final minY = ys.reduce(math.min); + final maxY = ys.reduce(math.max); + final left = math.max( + view.x, + (view.x + (minX * 0.5 + 0.5) * view.width).floor() - 1, + ); + final right = math.min( + view.x + view.width, + (view.x + (maxX * 0.5 + 0.5) * view.width).ceil() + 1, + ); + final top = math.max( + view.y, + (view.y + (0.5 - maxY * 0.5) * view.height).floor() - 1, + ); + final bottom = math.min( + view.y + view.height, + (view.y + (0.5 - minY * 0.5) * view.height).ceil() + 1, + ); + if (right <= left || bottom <= top) return null; + return ScreenRect( + x: left, + y: top, + width: right - left, + height: bottom - top, + ); + } + + /// [batch] written into the stage's block. + void _fillDecalBatch(_DecalBatch batch) { + final info = _decalInfo; + info.params[0] = batch.decals.length.toDouble(); + for (var i = 0; i < _DecalBatch.maxSlots; i++) { + final texture = i < batch.slots.length ? batch.slots[i] : null; + info.slots + ..[i * 4] = (texture?.width ?? 1).toDouble() + ..[i * 4 + 1] = (texture?.height ?? 1).toDouble(); + } + for (var i = 0; i < batch.decals.length; i++) { + final decal = batch.decals[i]; + // The rows of the world-to-box matrix, out of column-major storage. + final m = decal.inverseWorldMatrix.storage; + for (final (row, into) in <(int, Float32List)>[ + (0, info.axisX), + (1, info.axisY), + (2, info.axisZ), + ]) { + into + ..[i * 4] = m[row] + ..[i * 4 + 1] = m[4 + row] + ..[i * 4 + 2] = m[8 + row] + ..[i * 4 + 3] = m[12 + row]; + } + final region = decal.region; + info.region + ..[i * 4] = region.x + ..[i * 4 + 1] = region.y + ..[i * 4 + 2] = region.z + ..[i * 4 + 3] = region.w; + // Linear here rather than in the stage: one conversion, in doubles, + // that every backend then reads as the same float. + final tint = Renderer._srgbToLinear(decal.color); + info.color + ..[i * 4] = tint.x + ..[i * 4 + 1] = tint.y + ..[i * 4 + 2] = tint.z + ..[i * 4 + 3] = tint.w.clamp(0.0, 1.0); + final limit = decal.angleLimit.clamp(0.0, math.pi); + final gone = math.cos(limit); + final whole = math.cos(math.max(limit - decal.angleFade, 0.0)); + info.fade + ..[i * 4] = batch.slotOf(decal.texture).toDouble() + ..[i * 4 + 1] = gone + ..[i * 4 + 2] = math.max(whole - gone, 1e-4) + ..[i * 4 + 3] = decal.depthFade.clamp(0.0, 1.0); + final emissive = decal.emissive; + info.emissive + ..[i * 4] = emissive.x + ..[i * 4 + 1] = emissive.y + ..[i * 4 + 2] = emissive.z; + } + } +} diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart index cd905fe41..8dcd99e5c 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart @@ -582,18 +582,24 @@ final class _SceneNode extends RenderNode { required this.contributors, required this.shadowCaster, required this.lightOverflow, + required RenderSettings settings, }) { final transmits = _TransmissionPasses._holdsTransmission(scene, ordered); // A contributor that reads the scene's depth splits the frame too, and // for the same reason: what it reads is an attachment of this pass. It // needs no copy of the colour, so that node stays as the glass has it. final readsDepth = contributors.any((c) => c.readsSceneDepth); + // `P3`: decals split it for the glass's sake rather than their own. They + // read the buffers after the pass, which an unsplit frame would allow + // too, but glass drawn in the same pass would already be in the picture + // and the decal behind it would be painted over the glass. + decals = _DecalNode(_renderer, scene, ordered, settings); copy = _SceneColourCopyNode(_renderer, active: transmits); transparent = _TransparentNode( _renderer, scene: scene, contributors: contributors, - active: transmits || readsDepth, + active: transmits || readsDepth || decals.isActive, readsDepth: readsDepth, samples: optionalReads, ); @@ -615,6 +621,9 @@ final class _SceneNode extends RenderNode { /// What the pass counted, for the frame's own report. _ScenePass? result; + /// `P3`'s decals, painted between the two halves. + late final _DecalNode decals; + /// The copy of the scene the transmissive draws read, and the pass that /// draws them over it — `M3`. late final _SceneColourCopyNode copy; @@ -782,6 +791,73 @@ final class _SceneNode extends RenderNode { } } +/// `P3`'s projected decals, painted into the scene target between the +/// opaque half and the transparent one. +/// +/// **In place, which no other link in the colour chain is.** The transparent +/// half draws into the renderer's own scene target and loads it, so a decal +/// pass that wrote a texture of its own would be drawn over by glass that +/// never saw it. It can write in place because it never reads the colour: +/// the two blends that make a decal are a factor times what is there and a +/// term added to it, and both are blend state rather than texture reads. +/// +/// Needs three attachments rather than the two [_NeedsSurfaceBuffer] asks +/// for: the light a decal is laid under is read back through the albedo. +final class _DecalNode extends RenderNode { + _DecalNode(this._renderer, this._scene, this._views, this._settings); + + final Renderer _renderer; + final Scene _scene; + + /// Every view, each painted with its own camera inside its own rectangle. + final List _views; + final RenderSettings _settings; + + @override + String get name => 'decals'; + + @override + bool get supported => _renderer.device.maxColorAttachments > 2; + + /// Asked of the scene when the graph is built, so a frame whose decals are + /// all hidden is a frame that neither splits nor gives up multisampling. + @override + bool get isActive => + _settings.decals.enabled && + _scene.decals.any((decal) => decal.visibleInHierarchy); + + @override + List get reads => const [ + FrameResourceIds.hdrColour, + FrameResourceIds.surfaceBuffer, + FrameResourceIds.albedoBuffer, + ]; + + @override + List get writes => const [FrameResourceIds.hdrColour]; + + @override + void execute(NodeFrame frame) { + final resources = frame.resources; + final scene = resources.texture(FrameResourceIds.hdrColour); + final surface = resources.tryTexture(FrameResourceIds.surfaceBuffer); + final albedo = resources.tryTexture(FrameResourceIds.albedoBuffer); + // Hard reads, culled on a device that cannot attach them; a frame that + // got neither has no surface to paint, and the picture stands. + if (surface != null && albedo != null) { + _renderer._encodeDecals( + target: scene, + surface: surface, + albedo: albedo, + decals: _scene.decals, + views: _views, + passState: frame.state, + ); + } + resources.provide(FrameResourceIds.hdrColour, scene); + } +} + /// The scene as the opaque half left it, in levels for the transmissive /// draws to read — `M3`. Active only on a frame that holds one; see /// `renderer_transmission_pass.dart`. diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart index 57a6d5cf0..375cd3208 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart @@ -205,7 +205,7 @@ extension _PostPasses on Renderer { _fxaaParams[3] = settings.blend.clamp(0.0, 1.0); // `gfx-29n`. Zero exactly when nobody asked: the shader returns the // centre untouched at zero rather than running a kernel that rounds to - // nothing, and seventy-eight goldens depend on that being the same bytes. + // nothing, and seventy-nine goldens depend on that being the same bytes. // // After a temporal resolve the robust kernel, at the resolve's own // strength — `R2`: what softens a resolved picture is the history, and @@ -1568,7 +1568,7 @@ extension _PostPasses on Renderer { // itself — then one draw per view, scissored to its own rectangle, so the // exposure in the uniform is the one that view metered. With per-view // metering off, or with a single view, this is the one full-frame draw it - // has always been and the bytes are the bytes seventy-eight goldens hold. + // has always been and the bytes are the bytes seventy-nine goldens hold. final perView = settings.autoExposure.enabled && settings.autoExposure.perView && diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart index 95fb4cd80..1261e256c 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart @@ -1063,7 +1063,7 @@ extension _ShadowPasses on Renderer { final skinned = skeleton != null; // `gfx-60n`. A cut-out caster goes through a stage with a sampler in // it; everything else keeps the stage it has always had, which is why - // the masked half costs the common path nothing and why seventy-eight + // the masked half costs the common path nothing and why seventy-nine // goldens recorded against the plain stage cannot move. final masked = maskedShadowShader != shadowShader && _castsMasked(node); final kind = diff --git a/packages/flutter3d_core/lib/src/engine/scene/decal_node.dart b/packages/flutter3d_core/lib/src/engine/scene/decal_node.dart new file mode 100644 index 000000000..1e1936884 --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/scene/decal_node.dart @@ -0,0 +1,87 @@ +import 'package:flutter3d_hardware/flutter3d_hardware.dart' show TextureHandle; +import 'package:vector_math/vector_math.dart'; + +import 'scene.dart'; +import 'scene_node.dart'; + +/// A picture projected onto whatever geometry stands inside a box — `P3`. +/// +/// **The box is this node's unit cube**, from minus a half to a half on every +/// axis, so its position, rotation and scale are the decal's: scale x and z +/// for the size of the picture and y for how deep it reaches. The picture is +/// stamped down the box's y axis and faces up it, so a decal with no rotation +/// lies on a floor; to put one on a wall, rotate the box until its up points +/// out of the wall. +/// +/// **It paints the surface's colour, not the light on it.** The renderer +/// reads back the light each pixel was lit by and lays [color] (times +/// [texture]) under that same light, so a decal in a shadow is in the shadow. +/// It does not change a normal or a roughness; see `decal.frag` for what the +/// light that is read back cannot say. +/// +/// Drawn only while `RenderSettings.decals` is on, which it is not by +/// default, and only on a device that opens three colour attachments: the +/// renderer needs the scene's surface and albedo buffers. Reading those turns +/// multisampling off for the frame. +final class DecalNode extends SceneNode { + DecalNode({ + this.texture, + Vector4? color, + Vector3? emissive, + Vector4? region, + this.order = 0, + this.angleLimit = 1.3, + this.angleFade = 0.2, + this.depthFade = 0.1, + super.name, + }) : color = color ?? Vector4(1.0, 1.0, 1.0, 1.0), + emissive = emissive ?? Vector3.zero(), + region = region ?? Vector4(0.0, 0.0, 1.0, 1.0), + assert(angleLimit >= 0.0, 'an angle limit is an angle'), + assert(angleFade >= 0.0, 'a fade is a width'), + assert(depthFade >= 0.0 && depthFade <= 1.0, 'a share of the box'); + + /// The picture, sRGB-encoded as a base colour map is. Null paints [color] + /// alone, which is what a stain or a scorch with no detail needs. + /// + /// Mipmapped if it was uploaded with a chain: the level is chosen from how + /// much of the picture one pixel covers, as a material's would be. + TextureHandle? texture; + + /// The tint, sRGB like `Material.baseColor`, and in [Vector4.w] the opacity + /// the picture's own alpha is multiplied by. + Vector4 color; + + /// Light the painted colour gives off, as a multiple of it. Zero, the + /// default, emits nothing; a glowing sign wants a few. + Vector3 emissive; + + /// The part of [texture] this decal shows: x and y where it starts in + /// texture coordinates, z and w how far it reaches. The whole picture by + /// default; a sheet of decals in one atlas names its cell. + Vector4 region; + + /// Which decal is painted over which where two overlap: higher over lower, + /// and attachment order between equals. + int order; + + /// The angle, in radians from the box's up, past which a surface takes + /// none of the decal. Seventy-five degrees by default, so a decal on a + /// floor stays off the walls around it rather than smearing down them. + double angleLimit; + + /// How many radians inside [angleLimit] the decal fades in over, so the + /// edge where a surface turns away is soft rather than cut. + double angleFade; + + /// The share of the box's half height, from its top and bottom faces, the + /// decal fades out over, so geometry that only just enters the box is not + /// painted with a hard edge. + double depthFade; + + @override + void onAttachedToScene(Scene scene) => scene.registerDecal(this); + + @override + void onDetachedFromScene(Scene scene) => scene.unregisterDecal(this); +} diff --git a/packages/flutter3d_core/lib/src/engine/scene/scene.dart b/packages/flutter3d_core/lib/src/engine/scene/scene.dart index 8df97bf5e..f2cceb619 100644 --- a/packages/flutter3d_core/lib/src/engine/scene/scene.dart +++ b/packages/flutter3d_core/lib/src/engine/scene/scene.dart @@ -4,6 +4,7 @@ import 'package:flutter3d_hardware/flutter3d_hardware.dart'; import 'package:vector_math/vector_math.dart'; import 'camera_node.dart'; +import 'decal_node.dart'; import 'irradiance_field.dart'; import 'light_node.dart'; import 'lod_group.dart'; @@ -68,6 +69,13 @@ final class Scene { /// per draw, so both want a list rather than a tree walk. final List _probes = []; + /// Projected decals, in attachment order — `P3`. + /// + /// A registry for the probes' reason: the decal pass reads every one each + /// frame, and the order they were attached in breaks ties between equal + /// [DecalNode.order]s. + final List _decals = []; + /// Everything drawable currently in the scene, in attachment order. /// /// **A view, where these four used to be the lists themselves.** Handing out @@ -103,6 +111,11 @@ final class Scene { late final List _probesView = UnmodifiableListView(_probes); + List get decals => _decalsView; + late final List _decalsView = UnmodifiableListView( + _decals, + ); + /// Ambient light applied where no direct light reaches. /// /// The flat stand-in for [environment], used when a scene has none. It also @@ -198,6 +211,7 @@ final class Scene { _cameras.clear(); _lodGroups.clear(); _probes.clear(); + _decals.clear(); // Back to front, because `SceneNode.remove` takes the last child without // searching for it or shifting the rest; front to back shifted the whole @@ -228,6 +242,10 @@ final class Scene { void unregisterProbe(ReflectionProbeNode node) => _probes.remove(node); + void registerDecal(DecalNode node) => _decals.add(node); + + void unregisterDecal(DecalNode node) => _decals.remove(node); + /// First light of the given type, or null. /// /// A convenience for scene code — "where is the key light" — not something the diff --git a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart index b63f5dd0a..e47ee9648 100644 --- a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart +++ b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart @@ -157,7 +157,7 @@ void main() { }); test('an uncompressed file is untouched by any of this', () { - // The path seventy-eight goldens and every existing asset take. A level with + // The path seventy-nine goldens and every existing asset take. A level with // no supercompression is handed back as a view on the file's own bytes, // with nothing copied and nothing decoded. final file = buildKtx2( diff --git a/packages/flutter3d_cpu/CHANGELOG.md b/packages/flutter3d_cpu/CHANGELOG.md index dd68ed307..9d7da4119 100644 --- a/packages/flutter3d_cpu/CHANGELOG.md +++ b/packages/flutter3d_cpu/CHANGELOG.md @@ -1,3 +1,10 @@ +## Unreleased + +**The decal stage, as the GLSL has it.** `DecalShader` mirrors +`post/decal.frag` line for line, the mip level of each picture chosen from +the same footprint the GLSL computes by hand from neighbouring texels of the +surface buffer. + ## 0.8.2 **The PCSS receiver-plane bias, as the GLSL has it.** The directional diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart index df140fd70..817a7605f 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart @@ -51,6 +51,7 @@ import 'cpu_shaders_bloom.dart'; import 'cpu_shaders_compute.dart'; import 'cpu_shaders_contact_shadow.dart'; import 'cpu_shaders_debug.dart'; +import 'cpu_shaders_decal.dart'; import 'cpu_shaders_evsm.dart'; import 'cpu_shaders_impostor.dart'; import 'cpu_shaders_irradiance.dart'; @@ -75,6 +76,7 @@ export 'cpu_shaders_bloom.dart'; export 'cpu_shaders_color.dart'; export 'cpu_shaders_contact_shadow.dart'; export 'cpu_shaders_debug.dart'; +export 'cpu_shaders_decal.dart'; export 'cpu_shaders_irradiance.dart'; export 'cpu_shaders_layout.dart'; export 'cpu_shaders_lighting.dart'; @@ -222,6 +224,7 @@ Map builtinCpuShaders() { 'VelocityNeighborMax': const CpuStage.fragment(VelocityNeighborMaxShader()), 'MotionBlur': const CpuStage.fragment(MotionBlurShader()), 'ViewportShade': const CpuStage.fragment(ViewportShadeShader()), + 'Decal': const CpuStage.fragment(DecalShader()), 'ShadowDepthMasked': const CpuStage.fragment(ShadowDepthMaskedShader()), 'ShadowDistanceMasked': const CpuStage.fragment( ShadowDistanceMaskedShader(), diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_decal.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_decal.dart new file mode 100644 index 000000000..b34912ad6 --- /dev/null +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_decal.dart @@ -0,0 +1,208 @@ +/// `decal.frag` on the software rasteriser — `P3`. +/// +/// Line for line, the order of the stack included: the GLSL paints each decal +/// over the ones before it, and so does this. See the GLSL for why the light +/// is read back through the albedo and why one stage writes two terms. +library; + +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:vector_math/vector_math.dart'; + +import 'cpu_shader.dart'; +import 'cpu_shaders_color.dart' show decodeOctahedral, pixelRay, toLinear; + +/// `kMaxDecals` in `decal.frag`. +const int _maxDecals = 16; + +/// `kAlbedoFloor` in `decal.frag`. +const double _albedoFloor = 0.08; + +/// `decal.frag`: the factor a decal multiplies the scene by, or the term it +/// adds, by `params.w`. +final class DecalShader implements CpuFragmentShader { + const DecalShader(); + + @override + Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) { + final params = b.vec4('DecalInfo', 'params', Vector4.zero()); + final factor = params.w < 0.5; + final keep = factor + ? Vector4(1.0, 1.0, 1.0, 1.0) + : Vector4(0.0, 0.0, 0.0, 1.0); + final surfaceMap = b.textures['surface_texture']; + final albedoMap = b.textures['albedo_texture']; + if (surfaceMap == null || albedoMap == null) return keep; + + final u = v[0]; + final w = v[1]; + final surface = surfaceMap.sample(u, w); + if (surface.w <= 0.0) return keep; + + final inverse = b.mat4('DecalInfo', 'inverse_view_projection'); + final camera = b.vec4('DecalInfo', 'camera', Vector4.zero()); + final forward = b.vec4('DecalInfo', 'forward', Vector4.zero()); + final view = b.vec4('DecalInfo', 'view', Vector4(0.0, 0.0, 1.0, 1.0)); + final eye = Vector3(camera.x, camera.y, camera.z); + final axis = Vector3(forward.x, forward.y, forward.z); + + // `WorldAt`, with the target's coordinates taken into the view's. + Vector3 worldAt(double atU, double atV, double depth) { + final (:origin, :along) = pixelRay( + inverse, + (atU - view.x) / view.z, + (atV - view.y) / view.w, + ); + return origin + + along * ((depth - (origin - eye).dot(axis)) / along.dot(axis)); + } + + final depth = surface.w; + final at = worldAt(u, w, depth); + final normal = decodeOctahedral(surface.x, surface.y); + + // `WorldStep`: toward the neighbour nearer in depth. + Vector3 worldStep(double du, double dv) { + final ahead = surfaceMap.sample(u + du, w + dv); + final behind = surfaceMap.sample(u - du, w - dv); + final aheadGap = ahead.w > 0.0 ? (ahead.w - depth).abs() : 1e30; + final behindGap = behind.w > 0.0 ? (behind.w - depth).abs() : 1e30; + if (math.min(aheadGap, behindGap) >= 1e29) return Vector3.zero(); + return aheadGap <= behindGap + ? worldAt(u + du, w + dv, ahead.w) - at + : at - worldAt(u - du, w - dv, behind.w); + } + + final dx = worldStep(params.y, 0.0); + final dy = worldStep(0.0, params.z); + final albedoTexel = albedoMap.sample(u, w); + final albedo = Vector3( + toLinear(albedoTexel.x), + toLinear(albedoTexel.y), + toLinear(albedoTexel.z), + ); + + final painted = albedo.clone(); + var kept = 1.0; + final laid = Vector3.zero(); + final emitted = Vector3.zero(); + final count = (params.x + 0.5).floor(); + for (var i = 0; i < _maxDecals && i < count; i++) { + final rowX = b.vec4('DecalInfo', 'axis_x', Vector4.zero(), at: i); + final rowY = b.vec4('DecalInfo', 'axis_y', Vector4.zero(), at: i); + final rowZ = b.vec4('DecalInfo', 'axis_z', Vector4.zero(), at: i); + double row(Vector4 r, Vector3 p) => r.x * p.x + r.y * p.y + r.z * p.z; + final lx = row(rowX, at) + rowX.w; + final ly = row(rowY, at) + rowY.w; + final lz = row(rowZ, at) + rowZ.w; + if (lx.abs() > 0.5 || ly.abs() > 0.5 || lz.abs() > 0.5) continue; + + final fade = b.vec4('DecalInfo', 'fade', Vector4.zero(), at: i); + final up = Vector3(rowY.x, rowY.y, rowY.z)..normalize(); + final facing = normal.dot(up); + final byAngle = ((facing - fade.y) / math.max(fade.z, 1e-4)).clamp( + 0.0, + 1.0, + ); + final byDepth = fade.w > 0.0 + ? ((0.5 - ly.abs()) / (fade.w * 0.5)).clamp(0.0, 1.0) + : 1.0; + + final region = b.vec4('DecalInfo', 'region', Vector4.zero(), at: i); + final pu = region.x + (lx + 0.5) * region.z; + final pv = region.y + (lz + 0.5) * region.w; + + final slot = fade.x; + final alongXu = row(rowX, dx) * region.z; + final alongXv = row(rowZ, dx) * region.w; + final alongYu = row(rowX, dy) * region.z; + final alongYv = row(rowZ, dy) * region.w; + + final picture = _sampleSlot( + b, + slot, + pu, + pv, + // The sampler picks the level from the larger axis in texels, which + // is the footprint the GLSL computes by hand. + du: math.max(alongXu.abs(), alongYu.abs()), + dv: math.max(alongXv.abs(), alongYv.abs()), + ); + final tint = b.vec4('DecalInfo', 'color', Vector4.zero(), at: i); + final colour = Vector3( + toLinear(picture.x) * tint.x, + toLinear(picture.y) * tint.y, + toLinear(picture.z) * tint.z, + ); + final alpha = (picture.w * tint.w * byAngle * byDepth).clamp(0.0, 1.0); + final glow = b.vec4('DecalInfo', 'emissive', Vector4.zero(), at: i); + + _mixInto(painted, colour, alpha); + kept *= 1.0 - alpha; + _mixInto(laid, colour, alpha); + _mixInto( + emitted, + Vector3(colour.x * glow.x, colour.y * glow.y, colour.z * glow.z), + alpha, + ); + } + + final named = + (math.max(albedo.x, math.max(albedo.y, albedo.z)) / _albedoFloor).clamp( + 0.0, + 1.0, + ); + double swap(double paint, double was) => + 1.0 + (paint - was) / math.max(was, _albedoFloor); + if (factor) { + final unlit = kept * (1.0 - named); + return Vector4( + swap(painted.x, albedo.x) * named + unlit, + swap(painted.y, albedo.y) * named + unlit, + swap(painted.z, albedo.z) * named + unlit, + 1.0, + ); + } + return Vector4( + laid.x * (1.0 - named) + emitted.x, + laid.y * (1.0 - named) + emitted.y, + laid.z * (1.0 - named) + emitted.z, + 1.0, + ); + } + + /// `mix(into, colour, alpha)`, in place. + static void _mixInto(Vector3 into, Vector3 colour, double alpha) { + into + ..x += (colour.x - into.x) * alpha + ..y += (colour.y - into.y) * alpha + ..z += (colour.z - into.z) * alpha; + } + + /// `SampleSlot`: the picture in [slot], or white for none. + static Vector4 _sampleSlot( + ShaderBindings b, + double slot, + double u, + double v, { + required double du, + required double dv, + }) { + final index = slot > 2.5 + ? 3 + : slot > 1.5 + ? 2 + : slot > 0.5 + ? 1 + : slot > -0.5 + ? 0 + : -1; + final BoundTexture? picture = index < 0 + ? null + : b.textures['decal_texture_$index']; + if (picture == null) return Vector4(1.0, 1.0, 1.0, 1.0); + // The texture's own size is the one the GLSL reads out of `slots`. + return picture.sample(u, v, du: du, dv: dv); + } +} diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart index 5b2aeda28..977de6f7a 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart @@ -142,7 +142,7 @@ final class CompositeShader implements CpuFragmentShader { // Skipped at exactly one, which is what both settings off comes to: a // multiply by one is exact, so this is a shortcut rather than a difference, - // and it keeps the frames seventy-eight goldens hold untouched by arithmetic + // and it keeps the frames seventy-nine goldens hold untouched by arithmetic // they never used to go through. if (shade != 1.0) colour.scale(shade); diff --git a/packages/flutter3d_cpu/lib/src/cpu_texture.dart b/packages/flutter3d_cpu/lib/src/cpu_texture.dart index 2251fa559..d56d0c7bd 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_texture.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_texture.dart @@ -300,7 +300,7 @@ final class BoundTexture { // from the short one. // // **A sampler that did not ask is untouched**, which is what makes this - // safe to add to a backend seventy-eight golden scenes are recorded on: with + // safe to add to a backend seventy-nine golden scenes are recorded on: with // `anisotropy` at one — the default everywhere in this engine — the // arithmetic below is not reached and the bytes are the ones that were // recorded. `anisotropic-floor` is the one scene that asks. diff --git a/packages/flutter3d_cpu/lib/src/portable_log2.dart b/packages/flutter3d_cpu/lib/src/portable_log2.dart index 932f7365b..712fada34 100644 --- a/packages/flutter3d_cpu/lib/src/portable_log2.dart +++ b/packages/flutter3d_cpu/lib/src/portable_log2.dart @@ -10,7 +10,7 @@ /// twenty-five five. `anisotropic-floor` is the scene built to sit exactly /// there: 2059 of its 172800 pixels differed between a reference recorded on /// macOS and the same code run on a Linux runner, and nothing else in -/// seventy-eight scenes differed at all. +/// seventy-nine scenes differed at all. /// /// **The same rule the simulation already keeps.** `flutter3d_sim`'s /// `Portable` exists for this, and a step in that package is forbidden from diff --git a/packages/flutter3d_cpu/test/cross_backend_test.dart b/packages/flutter3d_cpu/test/cross_backend_test.dart index f59819d81..abd28c7ce 100644 --- a/packages/flutter3d_cpu/test/cross_backend_test.dart +++ b/packages/flutter3d_cpu/test/cross_backend_test.dart @@ -297,6 +297,11 @@ const Map _budgets = { 'transmission-glass': 0.01, 'velocity-shapes': 0.01, 'window-interior': 0.39, + // `P3`. 0.067% measured, single pixels along the ring's alpha edge, the + // boxes' edges and the box's shadow: where a sample lands on either side + // of a picture's texel or a box's face is decided in float32 here and in + // half floats and the GPU's own arithmetic there. + 'decal-floor': 0.08, }; /// How far apart two channels may be before the pixel counts as differing. diff --git a/packages/flutter3d_cpu/test/decal_test.dart b/packages/flutter3d_cpu/test/decal_test.dart new file mode 100644 index 000000000..d3f9abbf3 --- /dev/null +++ b/packages/flutter3d_cpu/test/decal_test.dart @@ -0,0 +1,429 @@ +/// A box projects a picture onto the geometry inside it, and the picture is +/// lit as the surface was — `P3`. +/// +/// dart test test/decal_test.dart +/// +/// Most of these read the lit scene before the post chain — the target the +/// decals are painted into — through a node registered behind them, and set +/// it against the same frame with the decal hidden. Where a decal is opaque +/// the two differ by the change of albedo alone, so the ratio of the two is +/// a number the test can state rather than a picture it has to look at. +library; + +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:flutter3d_core/flutter3d_core.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +const int _size = 32; + +/// Reads the scene target as the decals and the glass left it. +final class _SceneProbe extends RenderNode { + _SceneProbe(this._device); + + final CpuDevice _device; + Float32List? last; + + @override + String get name => 'scene probe'; + + @override + List get reads => const [FrameResourceIds.hdrColour]; + + @override + List get writes => const [FrameResourceIds.hdrColour]; + + @override + void execute(NodeFrame frame) { + final scene = frame.resources.texture(FrameResourceIds.hdrColour); + last = _device.readHdrPixels(scene); + frame.resources.provide(FrameResourceIds.hdrColour, scene); + } +} + +typedef _Stage = ({ + CpuDevice device, + Renderer renderer, + Scene scene, + CameraNode camera, + LightNode sun, + _SceneProbe probe, +}); + +/// sRGB 0.5, which the lit models take as a linear albedo of 0.214. +final Vector4 _grey = Vector4(0.5, 0.5, 0.5, 1.0); + +double _linear(double c) => + c <= 0.04045 ? c / 12.92 : math.pow((c + 0.055) / 1.055, 2.4).toDouble(); + +/// A floor eight metres square with its top at y = 0, a sun, and a camera +/// [from] somewhere above it looking at the middle. +_Stage _stage({Material? floor, Vector3? from, Vector3? up}) { + final device = CpuDevice( + width: _size, + height: _size, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final camera = CameraNode() + ..setPositionFrom(from ?? Vector3(0.0, 6.0, 0.0)) + ..lookAt(Vector3.zero(), up: up ?? Vector3(0.0, 0.0, -1.0)); + final sun = LightNode(intensity: 2.0) + ..setPosition(0.4, 5.0, 0.3) + ..lookAt(Vector3.zero()); + final scene = Scene() + ..add( + MeshNode( + DeviceMesh.upload( + device, + CuboidShape(size: Vector3(8.0, 0.2, 8.0)).build(), + ), + floor ?? + Material( + name: 'floor', + lighting: LightingModel.lambert, + baseColor: _grey, + ), + )..setPosition(0.0, -0.1, 0.0), + ) + ..add(sun) + ..add(camera); + scene.ambientIntensity = 0.3; + final probe = _SceneProbe(device); + final renderer = Renderer.create(device: device)..addNode(probe); + return ( + device: device, + renderer: renderer, + scene: scene, + camera: camera, + sun: sun, + probe: probe, + ); +} + +const RenderSettings _on = RenderSettings( + bloom: BloomSettings(enabled: false), + decals: DecalSettings(enabled: true), +); + +/// A decal three metres square on the floor, painted [colour]. +DecalNode _decal(Vector4 colour, {int order = 0}) => + DecalNode(color: colour, order: order)..setScale(3.0, 1.0, 3.0); + +FrameResult _render(_Stage it, [RenderSettings settings = _on]) => + it.renderer.render( + width: _size, + height: _size, + scene: it.scene, + views: [RenderView(camera: it.camera)], + settings: settings, + ); + +Float32List _scene(_Stage it, [RenderSettings settings = _on]) { + _render(it, settings); + return Float32List.fromList(it.probe.last!); +} + +/// The texel at [x], [y] of a frame read back as floats. +Vector4 _at(Float32List frame, int x, int y) { + final i = (y * _size + x) * 4; + return Vector4(frame[i], frame[i + 1], frame[i + 2], frame[i + 3]); +} + +/// Every pixel whose colour [a] and [b] disagree on. +List _changed(Float32List a, Float32List b) => [ + for (var p = 0; p < _size * _size; p++) + if ([0, 1, 2].any((c) => (a[p * 4 + c] - b[p * 4 + c]).abs() > 1e-4)) + p, +]; + +void main() { + test('off by default: the pass does not run and the frame is the same', () { + final it = _stage(); + final without = _scene(it, const RenderSettings()); + it.scene.add(_decal(Vector4(1.0, 0.0, 0.0, 1.0))); + final result = _render(it, const RenderSettings()); + // Mutation: an `isActive` that forgets the setting runs the pass on every + // frame with a decal in it. + expect(result.passes.map((p) => p.name), isNot(contains('decals'))); + expect(_changed(it.probe.last!, without), isEmpty); + }); + + test('on, with every decal hidden, nothing splits and nothing moves', () { + final it = _stage(); + final without = _scene(it); + it.scene.add(_decal(Vector4(1.0, 0.0, 0.0, 1.0))..visible = false); + final result = _render(it); + final ran = result.passes.map((p) => p.name); + // Mutation: asking `decals.isNotEmpty` instead of whether any is visible + // splits the scene and gives up multisampling for a decal nobody sees. + expect(ran, isNot(contains('decals'))); + expect(ran, isNot(contains('transparent'))); + expect(_changed(it.probe.last!, without), isEmpty); + }); + + test('the colour is swapped under the light the surface was lit by', () { + final it = _stage(); + // A blocker over the middle of the decal, high enough to stay out of the + // box, under a sun low enough that its shadow falls clear of it: part of + // the painted floor the camera sees is in that shadow. + it.sun.setPosition(3.0, 4.0, 0.0); + it.sun.lookAt(Vector3.zero()); + it.scene.add( + MeshNode( + DeviceMesh.upload( + it.device, + CuboidShape(size: Vector3(1.5, 0.2, 4.0)).build(), + ), + Material(lighting: LightingModel.lambert, baseColor: _grey), + )..setPosition(0.0, 1.2, 0.0), + ); + final decal = it.scene.add(_decal(Vector4(1.0, 0.0, 0.0, 1.0))); + decal.visible = false; + final reference = _scene(it); + decal.visible = true; + final painted = _scene(it); + + final inside = _changed(painted, reference); + expect(inside.length, greaterThan(40)); + final albedo = _linear(0.5); + final lights = []; + for (final p in inside) { + final was = reference[p * 4]; + lights.add(was); + // Red is a whole albedo over the floor's 0.214: the light that reached + // the point, times the new colour. Green and blue are nought. + // + // Mutation: laying the decal's colour over the lit picture rather than + // swapping it under the light makes the ratio a function of how lit the + // point is, and the shadowed half fails. + expect(painted[p * 4] / was, closeTo(1.0 / albedo, 0.02 / albedo)); + expect(painted[p * 4 + 1], closeTo(0.0, 1e-3)); + expect(painted[p * 4 + 2], closeTo(0.0, 1e-3)); + } + // And the region really did hold two lights, so the line above was asked + // of both of them. + expect(lights.reduce(math.max) / lights.reduce(math.min), greaterThan(2.0)); + }); + + test('a surface turned away from the box is left alone', () { + // A blue wall standing inside the box; the camera sees its side. + final it = _stage(from: Vector3(5.0, 4.0, 3.0), up: Vector3(0.0, 1.0, 0.0)); + it.scene.add( + MeshNode( + DeviceMesh.upload( + it.device, + CuboidShape(size: Vector3(0.2, 2.0, 2.0)).build(), + ), + Material( + lighting: LightingModel.lambert, + baseColor: Vector4(0.1, 0.2, 0.9, 1.0), + ), + )..setPosition(0.5, 1.0, 0.0), + ); + final decal = it.scene.add(_decal(Vector4(1.0, 0.0, 0.0, 1.0))); + decal.visible = false; + final reference = _scene(it); + decal.visible = true; + + bool onWall(int p) => reference[p * 4 + 2] > reference[p * 4] * 2.0; + final defaultLimit = _changed(_scene(it), reference); + expect(defaultLimit, isNotEmpty); + // Mutation: dropping the angle test paints the wall's side, which faces + // across the box rather than up it. + expect(defaultLimit.where(onWall), isEmpty); + + decal.angleLimit = math.pi; + final anyAngle = _changed(_scene(it), reference); + expect(anyAngle.where(onWall), isNotEmpty); + }); + + test('higher order is painted over lower, then attachment order', () { + final it = _stage(); + final red = it.scene.add(_decal(Vector4(1.0, 0.0, 0.0, 1.0), order: 1)); + final green = it.scene.add(_decal(Vector4(0.0, 1.0, 0.0, 1.0))); + Vector4 centre() => _at(_scene(it), _size ~/ 2, _size ~/ 2); + + // Mutation: drawing in attachment order ignores `order`, and green, the + // later, wins. + expect(centre().x, greaterThan(0.1)); + expect(centre().y, closeTo(0.0, 1e-3)); + + red.order = 0; + // Equal now, so the later attached is on top. + expect(centre().y, greaterThan(0.1)); + expect(centre().x, closeTo(0.0, 1e-3)); + + green.order = -1; + expect(centre().x, greaterThan(0.1)); + }); + + test('more decals and pictures than one draw holds are all painted', () { + final it = _stage(); + final colours = [ + Vector4(1.0, 0.0, 0.0, 1.0), + Vector4(0.0, 1.0, 0.0, 1.0), + Vector4(0.0, 0.0, 1.0, 1.0), + Vector4(1.0, 1.0, 0.0, 1.0), + Vector4(0.0, 1.0, 1.0, 1.0), + ]; + final pictures = [ + for (final colour in colours) SolidColorTexture(colour).upload(it.device), + ]; + // Twenty-five, a metre apart: two draws' worth of decals, and five + // pictures where a draw binds four. + final decals = [ + for (var row = 0; row < 5; row++) + for (var column = 0; column < 5; column++) + it.scene.add( + DecalNode(texture: pictures[(row + column) % 5]) + ..setScale(0.6, 1.0, 0.6) + ..setPosition(column - 2.0, 0.0, row - 2.0), + ), + ]; + final painted = _scene(it); + for (final decal in decals) { + decal.visible = false; + } + final reference = _scene(it); + + final project = it.camera.viewProjection(1.0); + for (var i = 0; i < decals.length; i++) { + final at = decals[i].readWorldPosition(); + final clip = project.transform(Vector4(at.x, at.y, at.z, 1.0)); + final x = ((clip.x / clip.w * 0.5 + 0.5) * _size).floor(); + final y = ((0.5 - clip.y / clip.w * 0.5) * _size).floor(); + final colour = colours[(i ~/ 5 + i % 5) % 5]; + final got = _at(painted, x, y); + final was = _at(reference, x, y); + // The picture's channels that are off are off; the ones that are on + // are the floor's light over its albedo. + // + // Mutation: a batch that stops at sixteen without starting another, or + // a fifth picture read through a slot holding the fourth, leaves a + // decal here the floor's colour or another's. + for (final (channel, on) in <(double, double)>[ + (got.x, colour.x), + (got.y, colour.y), + (got.z, colour.z), + ]) { + expect( + channel, + on > 0.0 ? greaterThan(was.x * 2.0) : closeTo(0.0, 1e-3), + reason: 'decal $i at ($x, $y)', + ); + } + } + }); + + test('the picture faces up its box, unmirrored', () { + final it = _stage(); + // Red, green / blue, white, from the top left of the picture. + final picture = it.device.createTextureFromPixels( + width: 2, + height: 2, + format: TextureFormat.r8g8b8a8UNormInt, + pixels: Uint8List.fromList([ + 255, 0, 0, 255, 0, 255, 0, 255, // + 0, 0, 255, 255, 255, 255, 255, 255, + ]).buffer.asByteData(), + )!; + it.scene.add(_decal(Vector4(1.0, 1.0, 1.0, 1.0))..texture = picture); + final painted = _scene(it); + // The camera looks straight down with the box's -z at the top of the + // screen, so the picture reads as it was drawn. A quarter in from each + // edge of the box is a texel centre, where bilinear is the texel. + final project = it.camera.viewProjection(1.0); + Vector4 quarter(double x, double z) { + final clip = project.transform(Vector4(x, 0.0, z, 1.0)); + return _at( + painted, + ((clip.x / clip.w * 0.5 + 0.5) * _size).floor(), + ((0.5 - clip.y / clip.w * 0.5) * _size).floor(), + ); + } + + // Mutation: taking v from -z, or u from -x, mirrors the picture, and the + // red corner moves. + final topLeft = quarter(-0.75, -0.75); + final topRight = quarter(0.75, -0.75); + final bottomLeft = quarter(-0.75, 0.75); + expect(topLeft.x, greaterThan(topLeft.y * 4.0)); + expect(topRight.y, greaterThan(topRight.x * 4.0)); + expect(bottomLeft.z, greaterThan(bottomLeft.x * 4.0)); + }); + + test( + 'an unlit surface has no light to lend, so the decal is its own colour', + () { + final it = _stage( + floor: Material(lighting: LightingModel.unlit, baseColor: _grey), + ); + it.scene.add(_decal(Vector4(0.2, 0.6, 1.0, 1.0))); + final centre = _at(_scene(it), _size ~/ 2, _size ~/ 2); + // Mutation: dividing by the albedo buffer's nought with no floor under + // it paints the unlit floor white, or infinite. + expect(centre.x, closeTo(_linear(0.2), 2e-3)); + expect(centre.y, closeTo(_linear(0.6), 2e-3)); + expect(centre.z, closeTo(1.0, 2e-3)); + }, + ); + + test('an emissive decal adds its glow over the light it borrowed', () { + final it = _stage(); + final decal = it.scene.add(_decal(Vector4(1.0, 0.5, 0.0, 1.0))); + final lit = _at(_scene(it), _size ~/ 2, _size ~/ 2); + decal.emissive = Vector3(3.0, 3.0, 3.0); + final glowing = _at(_scene(it), _size ~/ 2, _size ~/ 2); + // Mutation: an emission multiplied in with the factor rather than added + // with the term scales with the light and is nought in the dark. + expect(glowing.x - lit.x, closeTo(3.0, 0.01)); + expect(glowing.y - lit.y, closeTo(3.0 * _linear(0.5), 0.01)); + expect(glowing.z - lit.z, closeTo(0.0, 1e-3)); + }); + + test('glass above a decal is drawn over it, not painted', () { + final it = _stage(); + // A pane a metre over the box's top, so its own points are outside it. + it.scene.add( + MeshNode( + DeviceMesh.upload( + it.device, + CuboidShape(size: Vector3(5.0, 0.05, 5.0)).build(), + ), + Material( + lighting: LightingModel.unlit, + baseColor: Vector4(1.0, 1.0, 1.0, 0.3), + alphaMode: MaterialAlphaMode.blend, + ), + )..setPosition(0.0, 1.5, 0.0), + ); + final decal = it.scene.add(_decal(Vector4(1.0, 0.0, 0.0, 1.0))); + decal.visible = false; + final reference = _scene(it); + decal.visible = true; + final result = _render(it); + final painted = it.probe.last!; + expect(result.passes.map((p) => p.name), contains('transparent')); + final centre = _at(painted, _size ~/ 2, _size ~/ 2); + final was = _at(reference, _size ~/ 2, _size ~/ 2); + // Mutation: painting after the glass reads the pane's depth out of the + // surface buffer, finds it outside the box, and the decal is gone. + expect(centre.x, greaterThan(was.x + 0.1)); + expect(centre.y, lessThan(was.y)); + }); + + test('switched off by name like any other pass', () { + final it = _stage(); + final without = _scene(it); + it.scene.add(_decal(Vector4(1.0, 0.0, 0.0, 1.0))); + final result = _render( + it, + _on.copyWith(disabledPasses: const {'decals'}), + ); + expect(result.passes.map((p) => p.name), isNot(contains('decals'))); + expect(_changed(it.probe.last!, without), isEmpty); + }); +} diff --git a/packages/flutter3d_cpu/test/goldens/decal-floor.png b/packages/flutter3d_cpu/test/goldens/decal-floor.png new file mode 100644 index 0000000000000000000000000000000000000000..edede9747fa67eb3b338105aa62f02ba53e4c08c GIT binary patch literal 16263 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 kRequiredShaders = [ (name: 'VelocityNeighborMax', fragment: true), (name: 'MotionBlur', fragment: true), (name: 'ViewportShade', fragment: true), + (name: 'Decal', fragment: true), (name: 'ShadowDepth', fragment: true), (name: 'ShadowDistance', fragment: true), (name: 'ShadowDepthMasked', fragment: true), diff --git a/packages/flutter3d_shaders/lib/stage_bindings.dart b/packages/flutter3d_shaders/lib/stage_bindings.dart index acb4ee93d..c29e08cb8 100644 --- a/packages/flutter3d_shaders/lib/stage_bindings.dart +++ b/packages/flutter3d_shaders/lib/stage_bindings.dart @@ -71,6 +71,17 @@ stageBindings = blocks, Set samplers})>{ ), 'DebugLine': (blocks: {}, samplers: {}), 'DebugLineVertex': (blocks: {'LineInfo'}, samplers: {}), + 'Decal': ( + blocks: {'DecalInfo'}, + samplers: { + 'albedo_texture', + 'decal_texture_0', + 'decal_texture_1', + 'decal_texture_2', + 'decal_texture_3', + 'surface_texture', + }, + ), 'DepthOfField': ( blocks: {'DofInfo', 'FragCoordInfo'}, samplers: {'coc_tile_texture', 'scene_texture', 'surface_texture'}, diff --git a/packages/flutter3d_shaders/lib/typed_blocks.dart b/packages/flutter3d_shaders/lib/typed_blocks.dart index 87b4624dc..f2fbb1aff 100644 --- a/packages/flutter3d_shaders/lib/typed_blocks.dart +++ b/packages/flutter3d_shaders/lib/typed_blocks.dart @@ -207,6 +207,67 @@ final class ConvolveInfoBlock extends UniformBlock { }; } +/// `DecalInfo`. +final class DecalInfoBlock extends UniformBlock { + DecalInfoBlock() : super('DecalInfo'); + + /// `inverse_view_projection`: Matrix, at byte 0. + final Float32List inverseViewProjection = Float32List(16); + + /// `camera`: Vector4, at byte 64. + final Float32List camera = Float32List(4); + + /// `forward`: Vector4, at byte 80. + final Float32List forward = Float32List(4); + + /// `params`: Vector4, at byte 96. + final Float32List params = Float32List(4); + + /// `view`: Vector4, at byte 112. + final Float32List view = Float32List(4); + + /// `slots`: 4 × Vector4, at byte 128. + final Float32List slots = Float32List(16); + + /// `axis_x`: 16 × Vector4, at byte 192. + final Float32List axisX = Float32List(64); + + /// `axis_y`: 16 × Vector4, at byte 448. + final Float32List axisY = Float32List(64); + + /// `axis_z`: 16 × Vector4, at byte 704. + final Float32List axisZ = Float32List(64); + + /// `region`: 16 × Vector4, at byte 960. + final Float32List region = Float32List(64); + + /// `color`: 16 × Vector4, at byte 1216. + final Float32List color = Float32List(64); + + /// `fade`: 16 × Vector4, at byte 1472. + final Float32List fade = Float32List(64); + + /// `emissive`: 16 × Vector4, at byte 1728. + final Float32List emissive = Float32List(64); + + @override + late final Map members = { + 'inverse_view_projection': inverseViewProjection, + 'camera': camera, + 'forward': forward, + 'params': params, + 'view': view, + 'slots': slots, + 'axis_x': axisX, + 'axis_y': axisY, + 'axis_z': axisZ, + 'region': region, + 'color': color, + 'fade': fade, + 'emissive': emissive, + }; +} + /// `DepthPyramidInfo`. final class DepthPyramidInfoBlock extends UniformBlock { DepthPyramidInfoBlock() : super('DepthPyramidInfo'); diff --git a/packages/flutter3d_shaders/lib/uniform_blocks.dart b/packages/flutter3d_shaders/lib/uniform_blocks.dart index bc5e0f742..32ecffe61 100644 --- a/packages/flutter3d_shaders/lib/uniform_blocks.dart +++ b/packages/flutter3d_shaders/lib/uniform_blocks.dart @@ -256,6 +256,33 @@ uniformBlocks = >>{ ), }, }, + 'Decal': >{ + 'DecalInfo': { + 'inverse_view_projection': ( + offset: 0, + byteLength: 64, + elements: 1, + type: 'Matrix', + ), + 'camera': (offset: 64, byteLength: 16, elements: 1, type: 'Vector4'), + 'forward': (offset: 80, byteLength: 16, elements: 1, type: 'Vector4'), + 'params': (offset: 96, byteLength: 16, elements: 1, type: 'Vector4'), + 'view': (offset: 112, byteLength: 16, elements: 1, type: 'Vector4'), + 'slots': (offset: 128, byteLength: 64, elements: 4, type: 'Vector4'), + 'axis_x': (offset: 192, byteLength: 256, elements: 16, type: 'Vector4'), + 'axis_y': (offset: 448, byteLength: 256, elements: 16, type: 'Vector4'), + 'axis_z': (offset: 704, byteLength: 256, elements: 16, type: 'Vector4'), + 'region': (offset: 960, byteLength: 256, elements: 16, type: 'Vector4'), + 'color': (offset: 1216, byteLength: 256, elements: 16, type: 'Vector4'), + 'fade': (offset: 1472, byteLength: 256, elements: 16, type: 'Vector4'), + 'emissive': ( + offset: 1728, + byteLength: 256, + elements: 16, + type: 'Vector4', + ), + }, + }, 'DepthOfField': >{ 'DofInfo': { 'lens': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), diff --git a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json index e0181a153..f9f60e936 100644 --- a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json +++ b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json @@ -239,6 +239,10 @@ "type": "fragment", "file": "shaders/post/viewport_shade.frag" }, + "Decal": { + "type": "fragment", + "file": "shaders/post/decal.frag" + }, "ProbePrefilter": { "type": "fragment", "file": "shaders/post/probe_prefilter.frag" diff --git a/packages/flutter3d_shaders/shaders/post/decal.frag b/packages/flutter3d_shaders/shaders/post/decal.frag new file mode 100644 index 000000000..b0783a574 --- /dev/null +++ b/packages/flutter3d_shaders/shaders/post/decal.frag @@ -0,0 +1,251 @@ +#version 460 core + +// Projected box decals — `P3`: a picture laid onto whatever geometry stands +// inside a box, as if it were part of that geometry's material. +// +// **The point under a pixel comes from the surface buffer, because it can +// come from nowhere else.** flutter_gpu cannot sample a depth attachment, so a +// decal cannot be the usual box drawn against the depth it would read. The +// scene pass already wrote a world normal and a depth along the view axis into +// its second attachment, and `WorldAt` turns the two back into a point, as +// the reflections and the occlusion do. +// +// **A material, not a sticker over the lit picture.** The light a surface was +// lit by is recovered from what the scene pass left: the lit colour over the +// albedo buffer's colour is the light that reached the point, shadows, +// probes and the sky's share included. The decal's colour replaces the +// albedo under that same light, so a decal in shadow is in shadow and a decal +// under a red lamp is red. It is what keeps this a fullscreen pass of two +// draws rather than a second lighting model with every light and map bound. +// +// **Two draws with one stage, because a pixel needs two terms.** The new +// colour is the old one times a factor (the albedo swap) plus a term (an +// unlit stack and an emitted light), and one blend cannot multiply and add. +// The first draw writes the factor under a multiplying blend, the second the +// term under an adding one; `params.w` says which. +// +// What the albedo cannot say is stated rather than guessed: a surface that +// wrote no albedo (unlit, or black) has no light to borrow, and there the +// decal is laid over the picture at its own colour. The surface's emission, +// its fog and its specular highlight are scaled with the light, which is +// close for a matte wall and wrong for a mirror. +precision highp float; + +in vec2 v_uv; + +out vec4 frag_color; + +uniform sampler2D surface_texture; +uniform sampler2D albedo_texture; + +/// Four pictures a draw may read, each a slot a decal names. A slot nobody +/// names is bound to a one-texel white, for the reason `lib/pbr.glsl` gives: +/// a declared sampler nobody binds is a crash on Metal. +uniform sampler2D decal_texture_0; +uniform sampler2D decal_texture_1; +uniform sampler2D decal_texture_2; +uniform sampler2D decal_texture_3; + +/// Decals per draw. Must match `_DecalBatch.maxDecals` in +/// `renderer_decal_pass.dart`; a frame with more draws again over what the +/// first draw left. +#define kMaxDecals 16 + +/// How little albedo still names a light. Below it the decal stops borrowing +/// the surface's light and is laid on at its own colour, through a ramp +/// rather than a step so a dark texture does not show a seam where it +/// crosses. +#define kAlbedoFloor 0.08 + +uniform DecalInfo { + /// Screen to world, carrying the framebuffer origin — see `UvFromNdc` in + /// `post/reflections.frag`. + mat4 inverse_view_projection; + /// xyz: the camera's position. w: unused. + vec4 camera; + /// xyz: the direction the camera looks; with [camera] it names the planes + /// the surface buffer's depths are measured against. w: unused. + vec4 forward; + /// x: how many decals. y, z: one texel of the target. w: which term this + /// draw writes, nought the factor and one the term. + vec4 params; + /// The view this draw paints, in the target's texture coordinates: xy + /// where it starts, zw how much of the target it spans. The surface buffer + /// holds every view side by side and the matrix above is this one's. + vec4 view; + /// Per slot: xy its size in texels, zw unused. + vec4 slots[4]; + /// The rows of each decal's world-to-box matrix: the box is the unit cube + /// about its origin, and its picture faces up its y axis. + vec4 axis_x[kMaxDecals]; + vec4 axis_y[kMaxDecals]; + vec4 axis_z[kMaxDecals]; + /// The part of its slot's picture a decal shows: xy where it starts in + /// texture coordinates, zw how far it reaches. + vec4 region[kMaxDecals]; + /// rgb: the tint, linear. a: the opacity. + vec4 color[kMaxDecals]; + /// x: the slot, negative for none. y: the cosine of the angle from the + /// box's up at which the decal is gone, z: the cosine width over which it + /// fades in from there. w: the share of the box's half height, from its + /// top and bottom faces, over which it fades out. + vec4 fade[kMaxDecals]; + /// rgb: light the painted colour emits, as a multiple of it. w: unused. + vec4 emissive[kMaxDecals]; +} +decal_info; + +vec3 DecodeOctahedral(vec2 e) { + e = e * 2.0 - 1.0; + vec3 n = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y)); + float t = max(-n.z, 0.0); + n.x += n.x >= 0.0 ? -t : t; + n.y += n.y >= 0.0 ? -t : t; + return normalize(n); +} + +vec3 SrgbToLinear(vec3 srgb) { + return mix(srgb / 12.92, pow((srgb + vec3(0.055)) / 1.055, vec3(2.4)), + step(vec3(0.04045), srgb)); +} + +/// The world point at [uv], [depth] metres along the view axis: the ray +/// through the pixel, crossing the plane the depth names. See `WorldAt` in +/// `post/reflections.frag`, which this is, but for [uv] being the target's +/// rather than the view's. +vec3 WorldAt(vec2 uv, float depth) { + vec2 inView = (uv - decal_info.view.xy) / decal_info.view.zw; + vec2 xy = vec2(inView.x * 2.0 - 1.0, 1.0 - inView.y * 2.0); + vec4 nearH = decal_info.inverse_view_projection * vec4(xy, 0.0, 1.0); + vec4 farH = decal_info.inverse_view_projection * vec4(xy, 1.0, 1.0); + vec3 origin = nearH.xyz / nearH.w; + vec3 along = normalize(farH.xyz / farH.w - origin); + vec3 axis = decal_info.forward.xyz; + return origin + + along * ((depth - dot(origin - decal_info.camera.xyz, axis)) / + dot(along, axis)); +} + +/// How far the world moves across one texel along [step], taken toward the +/// neighbour nearer in depth. +/// +/// **Read from the buffer rather than from `dFdx`**, so the software backend, +/// which has no quads to difference, picks the same level. The nearer +/// neighbour, because the far one across a silhouette belongs to another +/// surface, and its distance would choose the blurriest level for a pixel +/// that is in the middle of a decal. +vec3 WorldStep(vec2 step, float depth, vec3 at) { + vec4 ahead = textureLod(surface_texture, v_uv + step, 0.0); + vec4 behind = textureLod(surface_texture, v_uv - step, 0.0); + float aheadGap = ahead.a > 0.0 ? abs(ahead.a - depth) : 1e30; + float behindGap = behind.a > 0.0 ? abs(behind.a - depth) : 1e30; + vec3 forward = WorldAt(v_uv + step, ahead.a) - at; + vec3 backward = at - WorldAt(v_uv - step, behind.a); + vec3 chosen = aheadGap <= behindGap ? forward : backward; + // Nothing drawn on either side: no footprint, which is the base level. + return min(aheadGap, behindGap) < 1e29 ? chosen : vec3(0.0); +} + +vec4 SampleSlot(float slot, vec2 uv, float lod) { + // A chain of selects rather than an index: GLSL ES 3.00 cannot index an + // array of samplers with anything but a constant. + vec4 picture = vec4(1.0); + if (slot > 2.5) { + picture = textureLod(decal_texture_3, uv, lod); + } else if (slot > 1.5) { + picture = textureLod(decal_texture_2, uv, lod); + } else if (slot > 0.5) { + picture = textureLod(decal_texture_1, uv, lod); + } else if (slot > -0.5) { + picture = textureLod(decal_texture_0, uv, lod); + } + return picture; +} + +void main() { + bool factor = decal_info.params.w < 0.5; + vec4 surface = textureLod(surface_texture, v_uv, 0.0); + + // What leaves the pixel as it was, for each draw: a factor of one, a term of + // nought. Alpha is not touched by either blend. + vec3 keepFactor = vec3(1.0); + vec3 keepTerm = vec3(0.0); + if (surface.a <= 0.0) { + frag_color = vec4(factor ? keepFactor : keepTerm, 1.0); + return; + } + + float depth = surface.a; + vec3 at = WorldAt(v_uv, depth); + vec3 normal = DecodeOctahedral(surface.rg); + vec3 dx = WorldStep(vec2(decal_info.params.y, 0.0), depth, at); + vec3 dy = WorldStep(vec2(0.0, decal_info.params.z), depth, at); + vec3 albedo = + SrgbToLinear(textureLod(albedo_texture, v_uv, 0.0).rgb); + + // Three stacks, decal over decal in order: the albedo the decals leave, the + // share of the picture an unlit stack leaves, and that stack's colour — + // premultiplied, as an over composite keeps it — and the emitted light. + vec3 painted = albedo; + float kept = 1.0; + vec3 laid = vec3(0.0); + vec3 emitted = vec3(0.0); + int count = int(decal_info.params.x + 0.5); + for (int i = 0; i < kMaxDecals; i++) { + if (i >= count) break; + vec4 rowX = decal_info.axis_x[i]; + vec4 rowY = decal_info.axis_y[i]; + vec4 rowZ = decal_info.axis_z[i]; + vec3 local = vec3(dot(rowX.xyz, at) + rowX.w, dot(rowY.xyz, at) + rowY.w, + dot(rowZ.xyz, at) + rowZ.w); + if (any(greaterThan(abs(local), vec3(0.5)))) continue; + + vec4 fade = decal_info.fade[i]; + // The box's up in the world is the gradient of its y, which is the row + // itself; normalised, it stays the up under a box scaled unevenly. + float facing = dot(normal, normalize(rowY.xyz)); + float byAngle = clamp((facing - fade.y) / max(fade.z, 1e-4), 0.0, 1.0); + float byDepth = fade.w > 0.0 + ? clamp((0.5 - abs(local.y)) / (fade.w * 0.5), 0.0, 1.0) + : 1.0; + + // Seen from above, along the box's down, x runs right and z runs down + // the picture: the picture is not mirrored on the face it is stamped on. + vec4 region = decal_info.region[i]; + vec2 uv = region.xy + (local.xz + 0.5) * region.zw; + + // The level from the footprint, as the software backend's sampler + // chooses one: the larger axis, in texels, over one pixel. + float slot = fade.x; + vec2 size = decal_info.slots[int(clamp(slot, 0.0, 3.0))].xy; + vec2 alongX = vec2(dot(rowX.xyz, dx), dot(rowZ.xyz, dx)) * region.zw; + vec2 alongY = vec2(dot(rowX.xyz, dy), dot(rowZ.xyz, dy)) * region.zw; + float footprint = max(max(abs(alongX.x), abs(alongY.x)) * size.x, + max(abs(alongX.y), abs(alongY.y)) * size.y); + float lod = footprint > 1.0 ? log2(footprint) : 0.0; + + vec4 picture = SampleSlot(slot, uv, lod); + vec4 tint = decal_info.color[i]; + vec3 colour = SrgbToLinear(picture.rgb) * tint.rgb; + float alpha = clamp(picture.a * tint.a * byAngle * byDepth, 0.0, 1.0); + + painted = mix(painted, colour, alpha); + kept *= 1.0 - alpha; + laid = mix(laid, colour, alpha); + emitted = mix(emitted, colour * decal_info.emissive[i].rgb, alpha); + } + + // How far the albedo names the light: one at the floor and over it. + float named = clamp(max(albedo.r, max(albedo.g, albedo.b)) / kAlbedoFloor, + 0.0, 1.0); + // The change of albedo under the light it was lit by, as a factor: the new + // albedo over the old where the old is over the floor, and exactly one + // where no decal reached, whatever the albedo. Written as one plus the + // change rather than as the ratio, because under the floor the ratio is not + // one for an untouched pixel and the ramp below would darken every dark + // surface in the frame. + vec3 swap = vec3(1.0) + (painted - albedo) / max(albedo, vec3(kAlbedoFloor)); + vec3 multiply = swap * named + vec3(kept * (1.0 - named)); + vec3 add = laid * (1.0 - named) + emitted; + frag_color = vec4(factor ? multiply : add, 1.0); +} diff --git a/packages/flutter3d_webgl/CHANGELOG.md b/packages/flutter3d_webgl/CHANGELOG.md index 87a6bb5a3..77fa143c1 100644 --- a/packages/flutter3d_webgl/CHANGELOG.md +++ b/packages/flutter3d_webgl/CHANGELOG.md @@ -1,3 +1,7 @@ +## Unreleased + +**The generated tables carry the decal stage** of `flutter3d_shaders`. + ## 0.8.2 **The generated tables carry the PCSS receiver-plane bias** of diff --git a/packages/flutter3d_webgl/lib/engine_shaders.dart b/packages/flutter3d_webgl/lib/engine_shaders.dart index 7c1464f7e..8ba944e0d 100644 --- a/packages/flutter3d_webgl/lib/engine_shaders.dart +++ b/packages/flutter3d_webgl/lib/engine_shaders.dart @@ -28830,6 +28830,263 @@ void main() { frag_color = vec4(mix(scene.rgb, shaded, mix_amount), scene.a); } +''', + 'Decal': r'''#version 300 es +precision highp float; +precision highp int; +precision highp sampler2D; +precision highp samplerCube; + +// Projected box decals — `P3`: a picture laid onto whatever geometry stands +// inside a box, as if it were part of that geometry's material. +// +// **The point under a pixel comes from the surface buffer, because it can +// come from nowhere else.** flutter_gpu cannot sample a depth attachment, so a +// decal cannot be the usual box drawn against the depth it would read. The +// scene pass already wrote a world normal and a depth along the view axis into +// its second attachment, and `WorldAt` turns the two back into a point, as +// the reflections and the occlusion do. +// +// **A material, not a sticker over the lit picture.** The light a surface was +// lit by is recovered from what the scene pass left: the lit colour over the +// albedo buffer's colour is the light that reached the point, shadows, +// probes and the sky's share included. The decal's colour replaces the +// albedo under that same light, so a decal in shadow is in shadow and a decal +// under a red lamp is red. It is what keeps this a fullscreen pass of two +// draws rather than a second lighting model with every light and map bound. +// +// **Two draws with one stage, because a pixel needs two terms.** The new +// colour is the old one times a factor (the albedo swap) plus a term (an +// unlit stack and an emitted light), and one blend cannot multiply and add. +// The first draw writes the factor under a multiplying blend, the second the +// term under an adding one; `params.w` says which. +// +// What the albedo cannot say is stated rather than guessed: a surface that +// wrote no albedo (unlit, or black) has no light to borrow, and there the +// decal is laid over the picture at its own colour. The surface's emission, +// its fog and its specular highlight are scaled with the light, which is +// close for a matte wall and wrong for a mirror. +precision highp float; + +in vec2 v_uv; + +layout(location = 0) out vec4 frag_color; + +uniform sampler2D surface_texture; +uniform sampler2D albedo_texture; + +/// Four pictures a draw may read, each a slot a decal names. A slot nobody +/// names is bound to a one-texel white, for the reason `lib/pbr.glsl` gives: +/// a declared sampler nobody binds is a crash on Metal. +uniform sampler2D decal_texture_0; +uniform sampler2D decal_texture_1; +uniform sampler2D decal_texture_2; +uniform sampler2D decal_texture_3; + +/// Decals per draw. Must match `_DecalBatch.maxDecals` in +/// `renderer_decal_pass.dart`; a frame with more draws again over what the +/// first draw left. +#define kMaxDecals 16 + +/// How little albedo still names a light. Below it the decal stops borrowing +/// the surface's light and is laid on at its own colour, through a ramp +/// rather than a step so a dark texture does not show a seam where it +/// crosses. +#define kAlbedoFloor 0.08 + +layout(std140) uniform DecalInfo { + /// Screen to world, carrying the framebuffer origin — see `UvFromNdc` in + /// `post/reflections.frag`. + mat4 inverse_view_projection; + /// xyz: the camera's position. w: unused. + vec4 camera; + /// xyz: the direction the camera looks; with [camera] it names the planes + /// the surface buffer's depths are measured against. w: unused. + vec4 forward; + /// x: how many decals. y, z: one texel of the target. w: which term this + /// draw writes, nought the factor and one the term. + vec4 params; + /// The view this draw paints, in the target's texture coordinates: xy + /// where it starts, zw how much of the target it spans. The surface buffer + /// holds every view side by side and the matrix above is this one's. + vec4 view; + /// Per slot: xy its size in texels, zw unused. + vec4 slots[4]; + /// The rows of each decal's world-to-box matrix: the box is the unit cube + /// about its origin, and its picture faces up its y axis. + vec4 axis_x[kMaxDecals]; + vec4 axis_y[kMaxDecals]; + vec4 axis_z[kMaxDecals]; + /// The part of its slot's picture a decal shows: xy where it starts in + /// texture coordinates, zw how far it reaches. + vec4 region[kMaxDecals]; + /// rgb: the tint, linear. a: the opacity. + vec4 color[kMaxDecals]; + /// x: the slot, negative for none. y: the cosine of the angle from the + /// box's up at which the decal is gone, z: the cosine width over which it + /// fades in from there. w: the share of the box's half height, from its + /// top and bottom faces, over which it fades out. + vec4 fade[kMaxDecals]; + /// rgb: light the painted colour emits, as a multiple of it. w: unused. + vec4 emissive[kMaxDecals]; +} +decal_info; + +vec3 DecodeOctahedral(vec2 e) { + e = e * 2.0 - 1.0; + vec3 n = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y)); + float t = max(-n.z, 0.0); + n.x += n.x >= 0.0 ? -t : t; + n.y += n.y >= 0.0 ? -t : t; + return normalize(n); +} + +vec3 SrgbToLinear(vec3 srgb) { + return mix(srgb / 12.92, pow((srgb + vec3(0.055)) / 1.055, vec3(2.4)), + step(vec3(0.04045), srgb)); +} + +/// The world point at [uv], [depth] metres along the view axis: the ray +/// through the pixel, crossing the plane the depth names. See `WorldAt` in +/// `post/reflections.frag`, which this is, but for [uv] being the target's +/// rather than the view's. +vec3 WorldAt(vec2 uv, float depth) { + vec2 inView = (uv - decal_info.view.xy) / decal_info.view.zw; + vec2 xy = vec2(inView.x * 2.0 - 1.0, 1.0 - inView.y * 2.0); + vec4 nearH = decal_info.inverse_view_projection * vec4(xy, 0.0, 1.0); + vec4 farH = decal_info.inverse_view_projection * vec4(xy, 1.0, 1.0); + vec3 origin = nearH.xyz / nearH.w; + vec3 along = normalize(farH.xyz / farH.w - origin); + vec3 axis = decal_info.forward.xyz; + return origin + + along * ((depth - dot(origin - decal_info.camera.xyz, axis)) / + dot(along, axis)); +} + +/// How far the world moves across one texel along [step], taken toward the +/// neighbour nearer in depth. +/// +/// **Read from the buffer rather than from `dFdx`**, so the software backend, +/// which has no quads to difference, picks the same level. The nearer +/// neighbour, because the far one across a silhouette belongs to another +/// surface, and its distance would choose the blurriest level for a pixel +/// that is in the middle of a decal. +vec3 WorldStep(vec2 step, float depth, vec3 at) { + vec4 ahead = textureLod(surface_texture, v_uv + step, 0.0); + vec4 behind = textureLod(surface_texture, v_uv - step, 0.0); + float aheadGap = ahead.a > 0.0 ? abs(ahead.a - depth) : 1e30; + float behindGap = behind.a > 0.0 ? abs(behind.a - depth) : 1e30; + vec3 forward = WorldAt(v_uv + step, ahead.a) - at; + vec3 backward = at - WorldAt(v_uv - step, behind.a); + vec3 chosen = aheadGap <= behindGap ? forward : backward; + // Nothing drawn on either side: no footprint, which is the base level. + return min(aheadGap, behindGap) < 1e29 ? chosen : vec3(0.0); +} + +vec4 SampleSlot(float slot, vec2 uv, float lod) { + // A chain of selects rather than an index: GLSL ES 3.00 cannot index an + // array of samplers with anything but a constant. + vec4 picture = vec4(1.0); + if (slot > 2.5) { + picture = textureLod(decal_texture_3, uv, lod); + } else if (slot > 1.5) { + picture = textureLod(decal_texture_2, uv, lod); + } else if (slot > 0.5) { + picture = textureLod(decal_texture_1, uv, lod); + } else if (slot > -0.5) { + picture = textureLod(decal_texture_0, uv, lod); + } + return picture; +} + +void main() { + bool factor = decal_info.params.w < 0.5; + vec4 surface = textureLod(surface_texture, v_uv, 0.0); + + // What leaves the pixel as it was, for each draw: a factor of one, a term of + // nought. Alpha is not touched by either blend. + vec3 keepFactor = vec3(1.0); + vec3 keepTerm = vec3(0.0); + if (surface.a <= 0.0) { + frag_color = vec4(factor ? keepFactor : keepTerm, 1.0); + return; + } + + float depth = surface.a; + vec3 at = WorldAt(v_uv, depth); + vec3 normal = DecodeOctahedral(surface.rg); + vec3 dx = WorldStep(vec2(decal_info.params.y, 0.0), depth, at); + vec3 dy = WorldStep(vec2(0.0, decal_info.params.z), depth, at); + vec3 albedo = + SrgbToLinear(textureLod(albedo_texture, v_uv, 0.0).rgb); + + // Three stacks, decal over decal in order: the albedo the decals leave, the + // share of the picture an unlit stack leaves, and that stack's colour — + // premultiplied, as an over composite keeps it — and the emitted light. + vec3 painted = albedo; + float kept = 1.0; + vec3 laid = vec3(0.0); + vec3 emitted = vec3(0.0); + int count = int(decal_info.params.x + 0.5); + for (int i = 0; i < kMaxDecals; i++) { + if (i >= count) break; + vec4 rowX = decal_info.axis_x[i]; + vec4 rowY = decal_info.axis_y[i]; + vec4 rowZ = decal_info.axis_z[i]; + vec3 local = vec3(dot(rowX.xyz, at) + rowX.w, dot(rowY.xyz, at) + rowY.w, + dot(rowZ.xyz, at) + rowZ.w); + if (any(greaterThan(abs(local), vec3(0.5)))) continue; + + vec4 fade = decal_info.fade[i]; + // The box's up in the world is the gradient of its y, which is the row + // itself; normalised, it stays the up under a box scaled unevenly. + float facing = dot(normal, normalize(rowY.xyz)); + float byAngle = clamp((facing - fade.y) / max(fade.z, 1e-4), 0.0, 1.0); + float byDepth = fade.w > 0.0 + ? clamp((0.5 - abs(local.y)) / (fade.w * 0.5), 0.0, 1.0) + : 1.0; + + // Seen from above, along the box's down, x runs right and z runs down + // the picture: the picture is not mirrored on the face it is stamped on. + vec4 region = decal_info.region[i]; + vec2 uv = region.xy + (local.xz + 0.5) * region.zw; + + // The level from the footprint, as the software backend's sampler + // chooses one: the larger axis, in texels, over one pixel. + float slot = fade.x; + vec2 size = decal_info.slots[int(clamp(slot, 0.0, 3.0))].xy; + vec2 alongX = vec2(dot(rowX.xyz, dx), dot(rowZ.xyz, dx)) * region.zw; + vec2 alongY = vec2(dot(rowX.xyz, dy), dot(rowZ.xyz, dy)) * region.zw; + float footprint = max(max(abs(alongX.x), abs(alongY.x)) * size.x, + max(abs(alongX.y), abs(alongY.y)) * size.y); + float lod = footprint > 1.0 ? log2(footprint) : 0.0; + + vec4 picture = SampleSlot(slot, uv, lod); + vec4 tint = decal_info.color[i]; + vec3 colour = SrgbToLinear(picture.rgb) * tint.rgb; + float alpha = clamp(picture.a * tint.a * byAngle * byDepth, 0.0, 1.0); + + painted = mix(painted, colour, alpha); + kept *= 1.0 - alpha; + laid = mix(laid, colour, alpha); + emitted = mix(emitted, colour * decal_info.emissive[i].rgb, alpha); + } + + // How far the albedo names the light: one at the floor and over it. + float named = clamp(max(albedo.r, max(albedo.g, albedo.b)) / kAlbedoFloor, + 0.0, 1.0); + // The change of albedo under the light it was lit by, as a factor: the new + // albedo over the old where the old is over the floor, and exactly one + // where no decal reached, whatever the albedo. Written as one plus the + // change rather than as the ratio, because under the floor the ratio is not + // one for an untouched pixel and the ramp below would darken every dark + // surface in the frame. + vec3 swap = vec3(1.0) + (painted - albedo) / max(albedo, vec3(kAlbedoFloor)); + vec3 multiply = swap * named + vec3(kept * (1.0 - named)); + vec3 add = laid * (1.0 - named) + emitted; + frag_color = vec4(factor ? multiply : add, 1.0); +} + ''', 'ProbePrefilter': r'''#version 300 es precision highp float; diff --git a/packages/flutter3d_webgl/test/cross_backend_test.dart b/packages/flutter3d_webgl/test/cross_backend_test.dart index e94ad0c5d..0b8903c1c 100644 --- a/packages/flutter3d_webgl/test/cross_backend_test.dart +++ b/packages/flutter3d_webgl/test/cross_backend_test.dart @@ -292,6 +292,8 @@ const Map _budgets = { // pixels here and on Impeller. Two-sided recording took the dots away, and // the difference went with them. 'window-interior': 0.01, + // `P3`. 0 of 172800 measured against Impeller on 2026-10-01. + 'decal-floor': 0.01, }; /// Scenes budgeted before this set had a picture of them. diff --git a/packages/flutter3d_webgl/test/goldens/decal-floor.png b/packages/flutter3d_webgl/test/goldens/decal-floor.png new file mode 100644 index 0000000000000000000000000000000000000000..2eef1e462a0538a0985a5c0157acf96b48aab9bd GIT binary patch literal 16525 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 literal 0 HcmV?d00001 diff --git a/packages/flutter3d_webgl/test/lighting_models_test.dart b/packages/flutter3d_webgl/test/lighting_models_test.dart index e4985b65f..3b305660b 100644 --- a/packages/flutter3d_webgl/test/lighting_models_test.dart +++ b/packages/flutter3d_webgl/test/lighting_models_test.dart @@ -6,7 +6,7 @@ /// `lighting-unlit` came back as an empty frame — the background colour in every /// pixel — while the other five drew a sphere. The golden set found it, once /// there was a golden set; this says the same thing in one second instead of a -/// browser run over seventy-eight scenes, and says it per model rather than per +/// browser run over seventy-nine scenes, and says it per model rather than per /// picture. /// /// It deliberately does not compare against a reference. What a shader *should* diff --git a/packages/flutter3d_webgl/tool/golden_web.sh b/packages/flutter3d_webgl/tool/golden_web.sh index 2dffea504..8d75a5552 100755 --- a/packages/flutter3d_webgl/tool/golden_web.sh +++ b/packages/flutter3d_webgl/tool/golden_web.sh @@ -16,7 +16,7 @@ # the same one either way, so this script has no reason to read it. # # **One build for the whole suite, and for both browser backends.** The scene is -# a query parameter rather than a compile-time define, so the seventy-eight scenes +# a query parameter rather than a compile-time define, so the seventy-nine scenes # are one dart2js run and a navigation each, rather than a dart2js run each. # That is the only reason this is minutes rather than an hour, and it is why the # backend arrives the same way: a define per backend would have spent the saving diff --git a/packages/flutter3d_webgpu/CHANGELOG.md b/packages/flutter3d_webgpu/CHANGELOG.md index 186d20f4d..dc89efb59 100644 --- a/packages/flutter3d_webgpu/CHANGELOG.md +++ b/packages/flutter3d_webgpu/CHANGELOG.md @@ -1,3 +1,8 @@ +## Unreleased + +**The generated tables carry the decal stage** of `flutter3d_shaders`, +through glslang and naga like every other stage. + ## 0.8.2 **The generated tables carry the PCSS receiver-plane bias** of diff --git a/packages/flutter3d_webgpu/lib/engine_shaders.dart b/packages/flutter3d_webgpu/lib/engine_shaders.dart index dd3f1f32f..58a959700 100644 --- a/packages/flutter3d_webgpu/lib/engine_shaders.dart +++ b/packages/flutter3d_webgpu/lib/engine_shaders.dart @@ -38902,6 +38902,658 @@ fn main(@location(5) v_uv: vec2) -> @location(0) vec4 { ), ], ), + 'Decal': WebGpuStage( + wgsl: r''' +struct DecalInfo { + inverse_view_projection: mat4x4, + camera: vec4, + forward: vec4, + params: vec4, + view: vec4, + slots: array, 4>, + axis_x: array, 16>, + axis_y: array, 16>, + axis_z: array, 16>, + region: array, 16>, + color: array, 16>, + fade: array, 16>, + emissive: array, 16>, +} + +@group(1) @binding(0) +var decal_info: DecalInfo; +@group(1) @binding(11) +var surface_texture_tex: texture_2d; +@group(1) @binding(12) +var surface_texture_smp: sampler; +var v_uv_1: vec2; +@group(1) @binding(9) +var decal_texture_3_tex: texture_2d; +@group(1) @binding(10) +var decal_texture_3_smp: sampler; +@group(1) @binding(7) +var decal_texture_2_tex: texture_2d; +@group(1) @binding(8) +var decal_texture_2_smp: sampler; +@group(1) @binding(5) +var decal_texture_1_tex: texture_2d; +@group(1) @binding(6) +var decal_texture_1_smp: sampler; +@group(1) @binding(3) +var decal_texture_0_tex: texture_2d; +@group(1) @binding(4) +var decal_texture_0_smp: sampler; +var frag_color: vec4; +@group(1) @binding(1) +var albedo_texture_tex: texture_2d; +@group(1) @binding(2) +var albedo_texture_smp: sampler; + +fn SampleSlot_u0028_f1_u003b_vf2_u003b_f1_u003b(slot: ptr, uv: ptr>, lod: ptr) -> vec4 { + var picture: vec4; + + picture = vec4(1f, 1f, 1f, 1f); + let _e64 = (*slot); + if (_e64 > 2.5f) { + let _e66 = (*uv); + let _e67 = (*lod); + let _e68 = textureSampleLevel(decal_texture_3_tex, decal_texture_3_smp, _e66, _e67); + picture = _e68; + } else { + let _e69 = (*slot); + if (_e69 > 1.5f) { + let _e71 = (*uv); + let _e72 = (*lod); + let _e73 = textureSampleLevel(decal_texture_2_tex, decal_texture_2_smp, _e71, _e72); + picture = _e73; + } else { + let _e74 = (*slot); + if (_e74 > 0.5f) { + let _e76 = (*uv); + let _e77 = (*lod); + let _e78 = textureSampleLevel(decal_texture_1_tex, decal_texture_1_smp, _e76, _e77); + picture = _e78; + } else { + let _e79 = (*slot); + if (_e79 > -0.5f) { + let _e81 = (*uv); + let _e82 = (*lod); + let _e83 = textureSampleLevel(decal_texture_0_tex, decal_texture_0_smp, _e81, _e82); + picture = _e83; + } + } + } + } + let _e84 = picture; + return _e84; +} + +fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { + let _e61 = (*srgb); + let _e64 = (*srgb); + let _e69 = (*srgb); + return mix((_e61 / vec3(12.92f)), pow(((_e64 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e69)); +} + +fn WorldAt_u0028_vf2_u003b_f1_u003b(uv_1: ptr>, depth: ptr) -> vec3 { + var inView: vec2; + var xy: vec2; + var nearH: vec4; + var farH: vec4; + var origin: vec3; + var along: vec3; + var axis: vec3; + + let _e69 = (*uv_1); + let _e71 = decal_info.view; + let _e75 = decal_info.view; + inView = ((_e69 - _e71.xy) / _e75.zw); + let _e79 = inView[0u]; + let _e83 = inView[1u]; + xy = vec2(((_e79 * 2f) - 1f), (1f - (_e83 * 2f))); + let _e88 = decal_info.inverse_view_projection; + let _e89 = xy; + nearH = (_e88 * vec4(_e89.x, _e89.y, 0f, 1f)); + let _e95 = decal_info.inverse_view_projection; + let _e96 = xy; + farH = (_e95 * vec4(_e96.x, _e96.y, 1f, 1f)); + let _e101 = nearH; + let _e104 = nearH[3u]; + origin = (_e101.xyz / vec3(_e104)); + let _e107 = farH; + let _e110 = farH[3u]; + let _e113 = origin; + along = normalize(((_e107.xyz / vec3(_e110)) - _e113)); + let _e117 = decal_info.forward; + axis = _e117.xyz; + let _e119 = origin; + let _e120 = along; + let _e121 = (*depth); + let _e122 = origin; + let _e124 = decal_info.camera; + let _e127 = axis; + let _e130 = along; + let _e131 = axis; + return (_e119 + (_e120 * ((_e121 - dot((_e122 - _e124.xyz), _e127)) / dot(_e130, _e131)))); +} + +fn WorldStep_u0028_vf2_u003b_f1_u003b_vf3_u003b(step_: ptr>, depth_1: ptr, at: ptr>) -> vec3 { + var ahead: vec4; + var behind: vec4; + var aheadGap: f32; + var local: f32; + var behindGap: f32; + var local_1: f32; + var forward: vec3; + var param: vec2; + var param_1: f32; + var backward: vec3; + var param_2: vec2; + var param_3: f32; + var chosen: vec3; + + let _e76 = v_uv_1; + let _e77 = (*step_); + let _e79 = textureSampleLevel(surface_texture_tex, surface_texture_smp, (_e76 + _e77), 0f); + ahead = _e79; + let _e80 = v_uv_1; + let _e81 = (*step_); + let _e83 = textureSampleLevel(surface_texture_tex, surface_texture_smp, (_e80 - _e81), 0f); + behind = _e83; + let _e85 = ahead[3u]; + if (_e85 > 0f) { + let _e88 = ahead[3u]; + let _e89 = (*depth_1); + local = abs((_e88 - _e89)); + } else { + local = 1000000000000000000000000000000f; + } + let _e92 = local; + aheadGap = _e92; + let _e94 = behind[3u]; + if (_e94 > 0f) { + let _e97 = behind[3u]; + let _e98 = (*depth_1); + local_1 = abs((_e97 - _e98)); + } else { + local_1 = 1000000000000000000000000000000f; + } + let _e101 = local_1; + behindGap = _e101; + let _e102 = v_uv_1; + let _e103 = (*step_); + param = (_e102 + _e103); + let _e106 = ahead[3u]; + param_1 = _e106; + let _e107 = WorldAt_u0028_vf2_u003b_f1_u003b((¶m), (¶m_1)); + let _e108 = (*at); + forward = (_e107 - _e108); + let _e110 = (*at); + let _e111 = v_uv_1; + let _e112 = (*step_); + param_2 = (_e111 - _e112); + let _e115 = behind[3u]; + param_3 = _e115; + let _e116 = WorldAt_u0028_vf2_u003b_f1_u003b((¶m_2), (¶m_3)); + backward = (_e110 - _e116); + let _e118 = aheadGap; + let _e119 = behindGap; + let _e121 = forward; + let _e122 = backward; + chosen = select(_e122, _e121, vec3((_e118 <= _e119))); + let _e125 = aheadGap; + let _e126 = behindGap; + let _e129 = chosen; + return select(vec3(0f, 0f, 0f), _e129, vec3((min(_e125, _e126) < 100000000000000000000000000000f))); +} + +fn DecodeOctahedral_u0028_vf2_u003b(e: ptr>) -> vec3 { + var n: vec3; + var t: f32; + var local_2: f32; + var local_3: f32; + + let _e65 = (*e); + (*e) = ((_e65 * 2f) - vec2(1f)); + let _e69 = (*e); + let _e71 = (*e)[0u]; + let _e75 = (*e)[1u]; + n = vec3(_e69.x, _e69.y, ((1f - abs(_e71)) - abs(_e75))); + let _e82 = n[2u]; + t = max(-(_e82), 0f); + let _e86 = n[0u]; + if (_e86 >= 0f) { + let _e88 = t; + local_2 = -(_e88); + } else { + let _e90 = t; + local_2 = _e90; + } + let _e91 = local_2; + let _e93 = n[0u]; + n[0u] = (_e93 + _e91); + let _e97 = n[1u]; + if (_e97 >= 0f) { + let _e99 = t; + local_3 = -(_e99); + } else { + let _e101 = t; + local_3 = _e101; + } + let _e102 = local_3; + let _e104 = n[1u]; + n[1u] = (_e104 + _e102); + let _e107 = n; + return normalize(_e107); +} + +fn main_1() { + var factor: bool; + var surface: vec4; + var keepFactor: vec3; + var keepTerm: vec3; + var depth_2: f32; + var at_1: vec3; + var param_4: vec2; + var param_5: f32; + var normal: vec3; + var param_6: vec2; + var dx: vec3; + var param_7: vec2; + var param_8: f32; + var param_9: vec3; + var dy: vec3; + var param_10: vec2; + var param_11: f32; + var param_12: vec3; + var albedo: vec3; + var param_13: vec3; + var painted: vec3; + var kept: f32; + var laid: vec3; + var emitted: vec3; + var count: i32; + var i: i32; + var rowX: vec4; + var rowY: vec4; + var rowZ: vec4; + var local_4: vec3; + var fade: vec4; + var facing: f32; + var byAngle: f32; + var byDepth: f32; + var local_5: f32; + var region: vec4; + var uv_2: vec2; + var slot_1: f32; + var size: vec2; + var alongX: vec2; + var alongY: vec2; + var footprint: f32; + var lod_1: f32; + var local_6: f32; + var picture_1: vec4; + var param_14: f32; + var param_15: vec2; + var param_16: f32; + var tint: vec4; + var colour: vec3; + var param_17: vec3; + var alpha: f32; + var named: f32; + var swap: vec3; + var multiply: vec3; + var add: vec3; + + let _e118 = decal_info.params[3u]; + factor = (_e118 < 0.5f); + let _e120 = v_uv_1; + let _e121 = textureSampleLevel(surface_texture_tex, surface_texture_smp, _e120, 0f); + surface = _e121; + keepFactor = vec3(1f, 1f, 1f); + keepTerm = vec3(0f, 0f, 0f); + let _e123 = surface[3u]; + if (_e123 <= 0f) { + let _e125 = factor; + let _e126 = keepFactor; + let _e127 = keepTerm; + let _e129 = select(_e127, _e126, vec3(_e125)); + frag_color = vec4(_e129.x, _e129.y, _e129.z, 1f); + return; + } + let _e135 = surface[3u]; + depth_2 = _e135; + let _e136 = v_uv_1; + param_4 = _e136; + let _e137 = depth_2; + param_5 = _e137; + let _e138 = WorldAt_u0028_vf2_u003b_f1_u003b((¶m_4), (¶m_5)); + at_1 = _e138; + let _e139 = surface; + param_6 = _e139.xy; + let _e141 = DecodeOctahedral_u0028_vf2_u003b((¶m_6)); + normal = _e141; + let _e144 = decal_info.params[1u]; + param_7 = vec2(_e144, 0f); + let _e146 = depth_2; + param_8 = _e146; + let _e147 = at_1; + param_9 = _e147; + let _e148 = WorldStep_u0028_vf2_u003b_f1_u003b_vf3_u003b((¶m_7), (¶m_8), (¶m_9)); + dx = _e148; + let _e151 = decal_info.params[2u]; + param_10 = vec2(0f, _e151); + let _e153 = depth_2; + param_11 = _e153; + let _e154 = at_1; + param_12 = _e154; + let _e155 = WorldStep_u0028_vf2_u003b_f1_u003b_vf3_u003b((¶m_10), (¶m_11), (¶m_12)); + dy = _e155; + let _e156 = v_uv_1; + let _e157 = textureSampleLevel(albedo_texture_tex, albedo_texture_smp, _e156, 0f); + param_13 = _e157.xyz; + let _e159 = SrgbToLinear_u0028_vf3_u003b((¶m_13)); + albedo = _e159; + let _e160 = albedo; + painted = _e160; + kept = 1f; + laid = vec3(0f, 0f, 0f); + emitted = vec3(0f, 0f, 0f); + let _e163 = decal_info.params[0u]; + count = i32((_e163 + 0.5f)); + i = 0i; + loop { + let _e166 = i; + if (_e166 < 16i) { + let _e168 = i; + let _e169 = count; + if (_e168 >= _e169) { + break; + } + let _e171 = i; + let _e174 = decal_info.axis_x[_e171]; + rowX = _e174; + let _e175 = i; + let _e178 = decal_info.axis_y[_e175]; + rowY = _e178; + let _e179 = i; + let _e182 = decal_info.axis_z[_e179]; + rowZ = _e182; + let _e183 = rowX; + let _e185 = at_1; + let _e188 = rowX[3u]; + let _e190 = rowY; + let _e192 = at_1; + let _e195 = rowY[3u]; + let _e197 = rowZ; + let _e199 = at_1; + let _e202 = rowZ[3u]; + local_4 = vec3((dot(_e183.xyz, _e185) + _e188), (dot(_e190.xyz, _e192) + _e195), (dot(_e197.xyz, _e199) + _e202)); + let _e205 = local_4; + if any((abs(_e205) > vec3(0.5f, 0.5f, 0.5f))) { + continue; + } + let _e209 = i; + let _e212 = decal_info.fade[_e209]; + fade = _e212; + let _e213 = normal; + let _e214 = rowY; + facing = dot(_e213, normalize(_e214.xyz)); + let _e218 = facing; + let _e220 = fade[1u]; + let _e223 = fade[2u]; + byAngle = clamp(((_e218 - _e220) / max(_e223, 0.0001f)), 0f, 1f); + let _e228 = fade[3u]; + if (_e228 > 0f) { + let _e231 = local_4[1u]; + let _e235 = fade[3u]; + local_5 = clamp(((0.5f - abs(_e231)) / (_e235 * 0.5f)), 0f, 1f); + } else { + local_5 = 1f; + } + let _e239 = local_5; + byDepth = _e239; + let _e240 = i; + let _e243 = decal_info.region[_e240]; + region = _e243; + let _e244 = region; + let _e246 = local_4; + let _e250 = region; + uv_2 = (_e244.xy + ((_e246.xz + vec2(0.5f)) * _e250.zw)); + let _e255 = fade[0u]; + slot_1 = _e255; + let _e256 = slot_1; + let _e261 = decal_info.slots[i32(clamp(_e256, 0f, 3f))]; + size = _e261.xy; + let _e263 = rowX; + let _e265 = dx; + let _e267 = rowZ; + let _e269 = dx; + let _e272 = region; + alongX = (vec2(dot(_e263.xyz, _e265), dot(_e267.xyz, _e269)) * _e272.zw); + let _e275 = rowX; + let _e277 = dy; + let _e279 = rowZ; + let _e281 = dy; + let _e284 = region; + alongY = (vec2(dot(_e275.xyz, _e277), dot(_e279.xyz, _e281)) * _e284.zw); + let _e288 = alongX[0u]; + let _e291 = alongY[0u]; + let _e295 = size[0u]; + let _e298 = alongX[1u]; + let _e301 = alongY[1u]; + let _e305 = size[1u]; + footprint = max((max(abs(_e288), abs(_e291)) * _e295), (max(abs(_e298), abs(_e301)) * _e305)); + let _e308 = footprint; + if (_e308 > 1f) { + let _e310 = footprint; + local_6 = log2(_e310); + } else { + local_6 = 0f; + } + let _e312 = local_6; + lod_1 = _e312; + let _e313 = slot_1; + param_14 = _e313; + let _e314 = uv_2; + param_15 = _e314; + let _e315 = lod_1; + param_16 = _e315; + let _e316 = SampleSlot_u0028_f1_u003b_vf2_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16)); + picture_1 = _e316; + let _e317 = i; + let _e320 = decal_info.color[_e317]; + tint = _e320; + let _e321 = picture_1; + param_17 = _e321.xyz; + let _e323 = SrgbToLinear_u0028_vf3_u003b((¶m_17)); + let _e324 = tint; + colour = (_e323 * _e324.xyz); + let _e328 = picture_1[3u]; + let _e330 = tint[3u]; + let _e332 = byAngle; + let _e334 = byDepth; + alpha = clamp((((_e328 * _e330) * _e332) * _e334), 0f, 1f); + let _e337 = painted; + let _e338 = colour; + let _e339 = alpha; + painted = mix(_e337, _e338, vec3(_e339)); + let _e342 = alpha; + let _e344 = kept; + kept = (_e344 * (1f - _e342)); + let _e346 = laid; + let _e347 = colour; + let _e348 = alpha; + laid = mix(_e346, _e347, vec3(_e348)); + let _e351 = emitted; + let _e352 = colour; + let _e353 = i; + let _e356 = decal_info.emissive[_e353]; + let _e359 = alpha; + emitted = mix(_e351, (_e352 * _e356.xyz), vec3(_e359)); + continue; + } else { + break; + } + continuing { + let _e362 = i; + i = (_e362 + 1i); + } + } + let _e365 = albedo[0u]; + let _e367 = albedo[1u]; + let _e369 = albedo[2u]; + named = clamp((max(_e365, max(_e367, _e369)) / 0.08f), 0f, 1f); + let _e374 = painted; + let _e375 = albedo; + let _e377 = albedo; + swap = (vec3(1f, 1f, 1f) + ((_e374 - _e375) / max(_e377, vec3(0.08f, 0.08f, 0.08f)))); + let _e381 = swap; + let _e382 = named; + let _e384 = kept; + let _e385 = named; + multiply = ((_e381 * _e382) + vec3((_e384 * (1f - _e385)))); + let _e390 = laid; + let _e391 = named; + let _e394 = emitted; + add = ((_e390 * (1f - _e391)) + _e394); + let _e396 = factor; + let _e397 = multiply; + let _e398 = add; + let _e400 = select(_e398, _e397, vec3(_e396)); + frag_color = vec4(_e400.x, _e400.y, _e400.z, 1f); + return; +} + +@fragment +fn main(@location(5) v_uv: vec2) -> @location(0) vec4 { + v_uv_1 = v_uv; + main_1(); + let _e3 = frag_color; + return _e3; +} +''', + attributes: [], + blocks: [ + WebGpuBlock( + name: 'DecalInfo', + group: 1, + binding: 0, + sizeInBytes: 1984, + members: [ + WebGpuBlockMember( + name: 'inverse_view_projection', + offsetInBytes: 0, + sizeInBytes: 64, + ), + WebGpuBlockMember( + name: 'camera', + offsetInBytes: 64, + sizeInBytes: 16, + ), + WebGpuBlockMember( + name: 'forward', + offsetInBytes: 80, + sizeInBytes: 16, + ), + WebGpuBlockMember( + name: 'params', + offsetInBytes: 96, + sizeInBytes: 16, + ), + WebGpuBlockMember( + name: 'view', + offsetInBytes: 112, + sizeInBytes: 16, + ), + WebGpuBlockMember( + name: 'slots', + offsetInBytes: 128, + sizeInBytes: 64, + ), + WebGpuBlockMember( + name: 'axis_x', + offsetInBytes: 192, + sizeInBytes: 256, + ), + WebGpuBlockMember( + name: 'axis_y', + offsetInBytes: 448, + sizeInBytes: 256, + ), + WebGpuBlockMember( + name: 'axis_z', + offsetInBytes: 704, + sizeInBytes: 256, + ), + WebGpuBlockMember( + name: 'region', + offsetInBytes: 960, + sizeInBytes: 256, + ), + WebGpuBlockMember( + name: 'color', + offsetInBytes: 1216, + sizeInBytes: 256, + ), + WebGpuBlockMember( + name: 'fade', + offsetInBytes: 1472, + sizeInBytes: 256, + ), + WebGpuBlockMember( + name: 'emissive', + offsetInBytes: 1728, + sizeInBytes: 256, + ), + ], + ), + ], + samplers: [ + WebGpuSampler( + name: 'albedo_texture', + group: 1, + textureBinding: 1, + samplerBinding: 2, + dimension: WebGpuTextureDimension.twoDimensional, + ), + WebGpuSampler( + name: 'decal_texture_0', + group: 1, + textureBinding: 3, + samplerBinding: 4, + dimension: WebGpuTextureDimension.twoDimensional, + ), + WebGpuSampler( + name: 'decal_texture_1', + group: 1, + textureBinding: 5, + samplerBinding: 6, + dimension: WebGpuTextureDimension.twoDimensional, + ), + WebGpuSampler( + name: 'decal_texture_2', + group: 1, + textureBinding: 7, + samplerBinding: 8, + dimension: WebGpuTextureDimension.twoDimensional, + ), + WebGpuSampler( + name: 'decal_texture_3', + group: 1, + textureBinding: 9, + samplerBinding: 10, + dimension: WebGpuTextureDimension.twoDimensional, + ), + WebGpuSampler( + name: 'surface_texture', + group: 1, + textureBinding: 11, + samplerBinding: 12, + dimension: WebGpuTextureDimension.twoDimensional, + ), + ], + ), 'ProbePrefilter': WebGpuStage( wgsl: r''' struct ProbeInfo { diff --git a/packages/flutter3d_webgpu/test/cross_backend_test.dart b/packages/flutter3d_webgpu/test/cross_backend_test.dart index c119c5b22..3d8223ef2 100644 --- a/packages/flutter3d_webgpu/test/cross_backend_test.dart +++ b/packages/flutter3d_webgpu/test/cross_backend_test.dart @@ -222,6 +222,8 @@ const Map _budgets = { 'transmission-glass': 0.01, 'velocity-shapes': 0.01, 'window-interior': 0.01, + // `P3`, 0 of 172800 measured on 2026-10-01. + 'decal-floor': 0.01, }; /// Scenes this set holds no picture of, and why the picture was refused. diff --git a/packages/flutter3d_webgpu/test/goldens/decal-floor.png b/packages/flutter3d_webgpu/test/goldens/decal-floor.png new file mode 100644 index 0000000000000000000000000000000000000000..77cadccd161e713a35457ea13bd4478ece52ca5c GIT binary patch literal 16526 zcmeIaXH=72^Diu-A|Rk5f>gbwC>@bb5(N<{f+Ety&_M<1HH4xlNR5IJ=_*a7_uixl z5kiO1LXSWogpvRuojfCL}*jPZYaqaI_$F?zw)=s_ZF#>ti@&=O)^c3{UOcod9Zw zP{20%SD7R#+Rcu|IZnQAiZ+kK&{SgNYFe#SAyy$P+U19wmCpvgYN1|BcfN7nxbXH1 zGJuB4q!8~IB#&9aCL{8`7>g(NTv|-d4^?VD43JJNG7MhUdOU4W@oZq8V%w+7v3JA` z*{)joq?UcQ8*v*Ks!;H=r~G8)x7!02w|hdUMYm2b;i_AO7gO>~lDz z*yy|Q*X_EclcXsLYk_G=R2j$Uz>>ulba|930p@W+Pm9vD404W-lDB&RQw8O( zf+svz1xsL!x;aMQ)9yk;h*HI{igHrqzbqsv^IGb~lyQ*t;|6*Yb%|uBV#+VBk|Jvn z6hJ{Ulhm+rbvM*!-4S?e3}}EV@f}oo9FP}yTc*w@G|o7>|F^4gpQ4m#>c{e_2I}?) zzQJURB*1t}&(+o#*8cpbGY;vvor(|;T<1j6Uu|2L@Aqo=MHn8Zb2iS%tJo(n{(a;1 z>~YKAXMK>DLbW01-_x6W+5h`+`TZ&bT{cfalamF2L+LIZJ_1>r1MK=*e}pE|JN~{h z!uXfHdI5_WAa-KUejcoaE^%7eV+IL&n`F%&Ou%l9ztXk+D_vU1 zzpPdM%UUJ-U+E(Ll}?5BFKe&=vi7?7uXG&$O6Pg>U)I+D&x*+?8yj^kZ94kL*}2;6 zNr)U7cNJBZNgsfL+}J4em|aV4S2?Xltj2;WPR11*i{p^Xt?7t#FU(?E@9#3n%-?Hk zGJ?uuf?m?#;gQvsGDlGJBt!amm%Ke+QacuUa}!hwF#$h+6eQ|GhFpTTOWOdWmNm>m zpEfG2)p{?RI7p$k%K;sFvg{Af5DG||=n2E_)gvUdVMBN6AG&Gaxs<PK^OiUCK zb^AT+$sq`t#I;x8PHdQ5O?xr%g9Ek@uxaLS-nX06x5){Qa@1HUvfwg&EnwIsPX8dP zRxUK)#u6480lFk)4n2_%_)*u+#6ffz3uzcM)F3gEx6gxy=$v;0M#0?yc+8f^y7EJW z_i#dT;1cXTfS-k@LD=af37}PS@}f1m~I$4yO_*9IF$!t4!uBUhrlFctY+h= zaDy#)BmLCwhU+RF=n}*{*f>s|cq<9Ei`*oP?npCR<>YL1>Av62W(b;9d|z zvDyL(P}z0=lHvTE*ploV8rIb60I%S>h>cW~!Hk z^#%u_-re`ycoJL?M+lY%}hCnTdK~6By)g=@Hu#FSTV+B}LWgR!i+&qZ3xvaU+BF)+bq1^7R-u$UE&U+8eFS{b*8qm5;5%`i2CZ8`SRNN)l>4>p58ENPWDahQ=t`S?t0y>Noo zd;9eS!=NqDwJveUr*9`R7ro-!<^q0$8?5XD1_}rO>7BO1mte{vYt!DAV#{Zvbu2{j zxxs+E3T*GfbGRpjz03(v5%k0wCDeg1<)UbA4qaX=Cb$#lPtF98sinGHpOU*ItURkx zZOhD#^TaIDAL><9G=0_(;DjSlvXao|GbbbheDIMB-s?5%P(`Vm0|M{R1>QLV1g3{P zQ&+{*g2F;iO1|4MN6*=jp{?(r*)|vDNfmx+fKmVp5Q|7X04;Z19|(M~$2rr@-tZc; z3l{?+vmy;Yh#Ha?E(dPW$&~@*l#ULgCzX-Qog)$rnF9VN%h&b|4up=JLLh;v!8@Ui ztIXRUy8<`q&C7x2+`%=Ky{kPeghrY~FmZzthPt1kvJvbX7PVIanw6j6T<%7oDfD4I z1B8*WEF7)zA`5Z|y4rlAWviu#Z7>*4xL0{9Thgb&g#y8nw0a2b5OuS8FW#x>=@Qi{ zPH6Yc_8LQ~xpADAzPuh?)}7$^B`uFneeGeDEan+`e6gaG5%Ky!O2hVsOaLLw3f(xL zu5bOdEL~l7!m8dpC2xw*P0>l>?zRzH>2!kUCM}s$j+PlmM@{zUqbH}}8>a}gEu~t6 zrd+GR*vGGjNVZFpZjB0My~=wYBTJ_$Ki>vD8I&<{_tmD!o6ilV^g zSSCr_a>SEKWt=~OYDH{5912MaTvGcM>h$=#^a~RCbbqF|5Sdnb=(MAM2l44wd%Lii zBws79-%lpXd!x@%6z>m-wsg||(qy5Ck|=EJ4Pey3J*1(_Y)Fa4`L({8#iAP)B^)8;Bw_-15 zo+R)6be)gM($pN5l7r>4qQbfy!Rt~(gq>_a2$%xor()06Z%G0Dw$KBAS+FHyDFJN|& zNTmIadICbge@T<>1Fflo8x`vY_UtPR2)`5j&0x-%Wvj%QB`Zs+*U{mY8r+j8@zr*5gkqH>1{hnN8{%Km5nS1Wvw|nD zaZB+lczrhxy6n>KP3yrO=1bpSFt_s+W9qSTP^W4}ss7VvvH%?K>R*AX zWO3U$okSD1vSS51FiZUI__r}J@s5B@ZagNSjUP^NCvUq? z1y!^SD4u%>v;MqZcj6=}C(%cGM8?0e=@$-l0JaXuZ3-lSk2O;_IbFfz0`7$M+vYKL z8O37(FTD~>m6XK9wGt4$3FI4IDxiJ&p<> zTbQaoV3f;8OQI0kyyBQ*5qpz6!`3=6yswnMtH7uJW5OGw3lF30iiTel_^O&Ay*iTZ z*gM_YF|41G=cs48f@&ng{anI+>_>65xm~LZDtJFa3*L? z-8w)tc+Wk$xVfnq{_+kKJfnWuS0=k&s-(1I?Ns!1gZr7v>&mE&ej zDP>5F$LeAmGxO7gWBrQ#m8t5I>qg=iYZgsvFM-$m_bn2@>ma(3##|E&; z9!7+av8bRywip|gxY~gg_pE_gi^5Ke@^0N??dwse?}=UVdVuD__QKWUX&qZ3-Fvnd zCth-v+O1Ai*TZcITOM-~;HQo29RTg8e`yyPiyMpUh0amjf;sHYU_-Xq{+27KoPUb$ zoRb+{n;OUBJPYrB5;o(>-{};?;t9m=T9K=0vDh0To!owoQ}S|?`0ao>_SUzdyZ(u? z%x3FSoZc@t){t;tAf>233ZXoaVI!Li60VaVY`F;@G*YXuvd99zUSH|Nnn&%>+>6FO zi+nA)IH6Q~!Bn`mM2lD2vYeq{$O!gZn$+0+2=sc;MpD)Fvz{X6xQ;*OO+$g0{9Qe$ z3sC%_d$F*6krv{Xo(wyC|Ek*#Jq^NT!ybKfy~(R?nos=Q$DSkH>d1{z#^XB0?bP$% z;GT_TzMNk4CvkV}c`D-FEA){UpC0>BrE{UvcvN?4BBU>RR|g6QbfdzJFzM`53EreC z`I8&1j-QE30$;Y1JUiIm@nm~BgLCNMXKDG@eDB-r>*eC~!9Uft| z{3h?XOa?VcyLdKM+^&7H^CBpo8z`($wFgT`*Vvc%G_OMhjT+KcPY62piSvG^DGcOl zl0P**X;(Qd>Mczri%Lx9DHD3O?`1r>!4DbBj6X&5a&2%+nW}sOZ#Wcj^M(>_sUNr} z0h%*=behW3e`t-gSvR)BitTrr*4LxV5dRb@@LE2<`ORDPO`pIxkimowCqojk)P<15 zK#tLBes8u@sLi$^b3gr`afRC+dt4(DY@-WYqf<9b)sWvSdTNUyTgPF*X2(H<%2(rq zPnjXXd=Y&7TD=U3MBNKScvdUmIeX?aGc-ZPGH8KJPlpb@^rCK|o;$e6zF%jj z2GWkO$N6da%tN<=yXQ<*1&U1bEq{!C-+>|4?<$D(bOp8~kg{-UlnnO}^>8ltkgJ|Y zQ&&$V9E*#n(tY)QcjPg2GN?OxR}0Dyc#2GnRq%etD+b*@Vl{0bL>Fg8d()FnIW!D8 zbUMMQtwMH+P2kzg#|2s%icp#rshM;8&2hvfc%63p)kjNNn|i#+)PfF_WIYQ~jr&@) z9<&fr76MdvQ02*M%dsfrX(5+~&wRgG&a_xg%?hd(;zwnBOrq7z=RpZj!; z8p3D8FIkN2^3|i19sN22R>=W#E*4TQGP+`5wcy$4Mxx~GHIpQd-c#0lnuCIlI1=)3 z$Pk?Rg2R6>cn9t@%qjTffbU$uAm|@omvpz+_Z3ZKylSg9Mh>o0?0~G>;r0EEymi{u z)RW~si8f1BO>wm)?CdOu$0F?ZdzJRv$TN&~dzrL7!%k5{zl(sSz*-!ij@IPN7V~Ny zQ`AcW;n2Ml)G}N1s1SyaZk{)7*S=Pi>&?w6l8zYlKhDaoWG91)AeRg$rfR^-KHuIi zg={U8R~l*3;ay^O!;Ih(CQHwg|8R09#ONzfW24kxFyWI`$v_4$qlF_$uCPjQcQAd} zS1c!BcUH2{vVo6Xj`#7zC~zjqM5L^ZQM`6O-#B7&YtoU)kGZsRi6b~8Qg@$+^mPmL zmQqY>K0{MM*XO&G>Gf6|JI#DLjlu5SQw{t1$nJMu>s2eI>!f}026SSN)@X{vOo3sU z5WUhLkF%}Uv~fbtBdzR6&Sp$zYTuq^n(PZMhl$(C9(I`)&sSCNuQ0>@I&R=W#wNnA zxeMv+S8MA3D^3R;Bvtzlm#HIm*q;YqdOuNm<<&iE)B-13pU@Y|h<#oHJBOEa!->ut~Wb%ib5=SltUpkiezxL&feAtP5Q+UZ|ay6Wv- zXmSAOTNxM)IT|~=|+9e!~ycz^{^+&OU z(N%UoRt`)lW!NPlXw~?o^QHrNVF3oV%x#HXyn$VB{GDH4-C#~z+_&H=8O*QH`MK%_ z7Op9eeCgBjN=~uSZ&OyDBIuqWXxtw+-r;TKAE%9W*>rGE#Gg!lVz{?nJ;RF+2CvlA z9s7FuA9sdGoxT0l;{BU@;Sp7;9-l||Eu?F?pfSbUZKG=iN+u0vw2xjhGneejsx@I* zlDOyGX82&P$rW2!9==eUJ!N^Km(xJUKaN2HqhqCLYCbuzV=``c8k1v2eDhH9|JAXp0G()F={gWY_X4q$)F*vrDB7i6XOsfM1T?Wa;bd=} zH;YGcKZoP|{v_&l1yjWnMUs#b%3ap+8(8d%O835pZZ}(Q7tXY(XvXjzUq zVfW1*kvifpQ>iQY@D%?Ti|MnXs_)@!DY=@jQ$H0b%d<0o`y?V>)LwSI{7xj-Bgb@> zUR}F>R?>^0k52R@4C$xMg-279StYxt>-W!ccA4K+g!4BU|B#n!*Aw|_3e7zjENs0S zII}QNoQB&cOlHi%;BO19k-So?JNdJ73l}@I9$9xM4s|P}+Dlw8&;5N~OFXwdUZTuC z`s33NB9F55U*&ubT@!7Wh?|DRQx2}7yMX!wS_3BwcA%SeWt0M{)tE(homtB(vmv(5Y%aFCzXiJVBDG zQ*{e-;d=eqx{J0MH9SnC?FEmI|LFadPyFBnWF7|@{)&lDj?O>EV5FJ8SC#F^F$^WY z8A9$E65|fMVmi;Ne%g23@4iK@lH7iBZLNOfl8cf@M>gl0{mCbCITf!f%}?rmusyGV zzNKCl3A)Q0R+-c-JOJB1D`c^ei)^HVY8kPh?-RMHc9dl zLZ~+mi+g0j6Jdkx=GxKHksjVFR@w(8E9@Ga?6BfCLHy!k3JFSS95*s)J@uhuXAmbp zZcKtm#`2PPD28@?x8_e5+WB-FxrxohAk-Pxbj^AiJfHg^=DDrTxvt+TkG(Gw6;;}X z`loI#&Bs^Yx2c$c+ag|%`_D8i%f zRPpuTJ?30Y>$ogA(FVE_&r@*4{7EilqIw0|aI3iNBgKF34-=A|Di@bQ*p%x4-cog6bmh5!-|g>b z`&T-kF%jxd*DU%yT@9kN^v`CmVqHZA{Vu*7WDL-+ZyW3@Qd*cxXuE7(>tZftk@V!G zZsA!A>yj{?d|H!=o@hP)^ZC!C;V&e<4CxtAVLibUb+@0YtIDIc@asJy@pv{&bC5et zrDTP4x>_heUJzVs9BOp>m5&);HlL(?L&)YMr{C{4&fPer`vtOa1>HG{G(5hw?`14$ z`_tN8Lf_=4(^#k3J6o5+jb8t~MH8{<2jX7DE^+=+r5C1J=u-UU7T@}#m0;dB3-Maz0h*&r>t9le)T>*bQN3%qi#W^I+~|Qd8`!-KEWz zF>R;|-KF<)y?`_7ukHkf)+_=2%2$z`?pdZSx{&KBt%!7idg0c$3NeXkF@^CGqJva|6R*N0)cG1QsKge>-m{l#PoL?yNFiX zf?h#IqF3}Yq;=i7qS20Z(mh4asdSOAGD`0Rn{Ed#6%RFRDGH0yh-Sz97IqYsV7-e+ zS?Vj8zCJ~e_ylj%7omkKh6BKP>2_aq$UeKT7i+3Azi+k?zol}Io3>mLpC*T;Sm?Jq zgHK*9o-due%$#YbW`=X+*_6GB=aSq#?#-H1GkZ;{_3g+Tq}$zsqB|Efx^#0i<)*f6 z;qZHQ);auVCr&t1aGkkKqHKt9HKu5<_mxs8)vSy$l{Lx+ zergG{>hrNGabJ+Lb<8`GI!bT8X`C-ynBubaN_U-8X{usKJ&E$|cpzbBaq=DODbr(o zDfq=p@->I{2Oe#Mdi?d3p=5B`1y(H+31&1@Vr#V!B-7c&6xPa%yx(-unbA-&d0QWO zq?8I@IFTlwozC0bsRvH>k$M(z#I9)xJAd$@C|#Y?FR)+lGvkg%Je2wC2joY~n?F z-fHh75Mmo0;r*&H4x0zD;HXHQH9Thao*35kL}i|F6F3G5hXZE-OZ#f42czBkA-evj z5?$i6>m%#g>n@H-#P5jLEdCQ&A5}i~ooTG*(P_+?Gink^FHWa_6%jvnGK`r&{DW@N za28*4u!Guxdfb|FxWQOix#>KHjia-RQ%UVyjz*}*im*N@_|n?ROphmaIx$&i>!!Li zGnzE%2e-JcqO@T0qh_nf)`M+Pje`6~_=DJ#R2s^ z7UCkB)=zx}3toM2zFqH?fYEwV#^Ed+mt%A)5_(iTY_q!N;GWB^9h^x!9!jIlNa*2iyj;IID zQ2WZVr7{0r%=EP{M!I4%-p8x1a?Ne#^)(2DF_wJg`PzQALuX^*sv(*_Oh3vX+t*a| ztJ-$zG}i_%%2#E*wJIvy)mONbnS#?|$2N$r^`s17(JQbLu(C(E?^wwbsWJhN6<0O$QcapLtO*qV?nfnkj3#|*Pu3+4Q>}_N zt(87=Cl3^^y#isjtr;be0@bcN{?sQ5u!pzKDo5D}IM>z2jWu|FzhK19`MO1e)_^@) z^M-OGp-gH9=Y7}uqCJd&+b_U$*r>Ywnr$#zDm$V7j?5lE@0s4XxpFs_Lr93nhs>tx zPB*Z*O>?junV5p)L}rj#&q&t|giQU(LO)K~X}f1F<;=A<;@_QDP2c-}div_|wR^>_ zbJ`rEOlqH}6)FP5EruSty}iH^7o8s*i>+uZyI@qpJen({AD&3KJcz!Q_J}XqOYX~C zjc0$f{1t)$;{lo4t6J{m&&%~b73cZYj~Y@wjG@kVJa9WD?25@JrXEpVeUN~@UE!9F zX%M8|9}7^RKKgK`;N!NCOaN^3Z;<&CQMvL;F^n?ZzJRVx_iB&)GVpqOmE~c>-8{cY zXhRBR?|$1W1xci)2~zTvj+?yWB7r~~4)BXVbI>AP-)vb?i^xn@!ePcmHyxPUCnN@E-%?AGCf1u0+O4g{jT z25BrPyhZ;*Tb=We&PU)zdV(Wx z6W_hY?dK_k&bm~fQ!*Mya1@Xg3hmKuHw=(_qQU=BOZb4zF6C z^QWw1b@HZePqZ4}I3V{V4_zH987;H?nM(YaXP?eg@cSxxm2WD<%(mI_nUCbi-IRgx z99OD};g~iPLxH4-W%GHj08s7&?*b{Z*^`-5@wIVzawWzrr(>@EGH}(eR%naulSR*w z4>CMGbYi@i*9C7*9DrxbO9S=pP4e#J|I`_B-b~1ZirmDf z{*b^B*ihIm)?@x%dJeaWDoC@QHFi{7LJw6@55(C1*fSlnbMq++%eyyBrPclC_jbOr z4yTIz*bTiaacsd%T21=tpArjYP91C zRQDgWBTM{w_c&#pd*?M}d{4X*iA#7?7*ZxQTKO4RPB=DN%d28`2-@H%PTVseDRNcI zM5wS%GwphiiWFt{jX|+~t@m44`K?Bq=T(d;>)u~CyuWbseLX!tepci;y2{9)CRk)P z*3^_a%t8^W6%$#$?RSafSx)40dEvP70VLAaO}c(GqNZth1aJ1tz->c@9o>&5c_mSE zn@+C$HV0YfaO~$Bx@~o&AKx{po9nK+3*EKko*vb(9ty+$@^$KDR(9$XUF*)QzSz_8 zhE*bNy)h0fj8{cB)1}kvtO$ZpyG>n%FjI)?7X(ZJ61PN%qgb#j7 zyW_Z|V%((Nm-l%7YQpvat?7utZ$&D$D$ph6FTKC3KFe#__4i(yeE*=y@fe)KcfTtb zAcCt2{_@UidM;NawZUm4YX=^*S&RkM;HJoYZlF1Wo{Z8ZGt@zuUGoRvc&TT|;}1%V zVIN+9R`i5#wy1knB2SsNYqd@Ds=x7DXKqtjS0}ULoF_<3;DhIJ2EnX z_IbKi9*(Apv`=u;2&r5#HK6V~P8}7Xx6T!AhbDrvy%-*r>BjT?yu5rgdZT&2;J0h< zS&;ODD#ISvsy&L>u<>u7vfH zZT9+AGO(tpt*VrJYmG3nLAGo>5+?=zTv=2ziR^~fV7!f;!wa9b6tUm+8eIuDw1c02 z^33TaH^1;X(a2NvnXg(8Jk1Wuz$oqV-kDPv@bmW$LY9*4Cm2>vUCTc^SU*%a_Brn# z+_JLba7x?@#x!$*OCTPh-s^!CXZ@Uhh3WX4F(U+686qZ-yQbh2AR9F97^%$vwD@`O zBP%}0&)xcf?DRxxv0>VU*Z0Tfw->Ku)9;iGoN&Yq=s15#zyDQQZMH|m96iQPn`sq% z*M>(QNC@@PmuY3{s~Qh*9ZqTuB-;pVP!38@305GbR?vH3<2sL9vYc{JB{>6b3XDvm z{ODU>OE@DekJb4US21A9;V|J&5#k;;Bzwn+nuq|-vKiq3rm*M&s0f#58Sw!vXJ24_ou@bcFd?s zREeh!350QMP{Gds?qvPs&6l=Zthq8YvbPj>T?p@pDlvVlO*!@ZOkgW`$Hj1+n3d68 zL{x`cWvU06TPyK?n|Q zfA+Nh8gF@79yYwaIh=Jwse$O^g%F7Np#yOtS0I(YCGO_GY@)&urfxp_=}*of$82@q z;(tAqKkygB&B7Q-FT&F;Lgj9&zEI`ZyDkXzw%farVz&eD3ff)Bq(_&CC%|^u@C1mpp@E#djqphO!OU^58Xqe{+v^^q)Hw*Uv6-#tQZoH?kXUSN&pq!pB-S zCU{0DZl?Qiy!vvhZGioGUme(HqM8HRZxCFVxiv>W&@!`X>R(7kF(XIqqzi;UM#pK~ zvv~YzpbNF_nP>M+Nr2)LCNHs)oe(SNF3uq;Cn~C7l`b4seBq`JtTc8g9T#|$QWgxT zgKnB2o5I_rXT2oYOGM|^0v(!W&U{mldN_r>7Ki+2%SAvt&H}hZt;Rc+WX3#Xco2rB zZ7gmeaPDQ>!{%?D)7nG^W(l(PSkIf~Gj0&78Dy!A?xQ7DxqZ40snPkVmS)rQ2X~Rq zNCD*Nc@v2^qgDrQ!XML9)WU3aJ*CM9&b*`-p@nDeu2-m}y;eU}&BObeM>8A6u{-dDisKt@p5^54$+rQM zG!-DW=77MyB){f){Ypvqy|=QS&FYVop2S&j`u<>daWETjBxkmV>A1>X3UnNt?z2ZG z3j==i$ZBDKZ>8-(>_2hLPz=!=VFpPZ@riKTbWNKWaZ&H`zX3!2)pM&~H~@}PXz zDx2mHyRWau#2BP>Z4JSSYW)`}@C4AT4sGi354Jj%63BL@*Caf(Yn_wv+nR)iJaDW z!KRt#w6e?ZVf)TUYUO+Vm^|K80706ojdkep^)sv89m~xEZdTt6n2fQszq+<-048*0 z(A0;JyN2N3HbPWGqpf}dbWaf>)7HJ$7|k@zfyGkWqj53_zOki)5%$EKHwI&a>z6jb zXE22pKoNUms>d-A0k^1D4X4)GRXYL|N{q*PtM<5EjtppKKz3+!T> zz`>SIi(#fq0AXIj@Y5&AE}ozk0)e*`*O!@Y0Y_G6ok$FrDTtxni^Ihd76L1fwiP83jVwMFEo z^HJgu9T+HVy-uC!aDxz!ju!DKYzP#-V3F$i{azMV|9UIQ^%qoNV!GlV!F>FC#G0^y zOi$?(1>;naQ4~X8N89~}&9Bvd+kr5bVE7yH-M@s@!TY%gzS^L>T_ z7CSEd>=6c5qZa7j4EtT~ms5e-l4pK@EPRse>P~U}FrD&cvRqi_wVm0yyhH)c#Eiq6 z)f3tEU)D-2pg$i7nbfR|{&7c`uGIpuNjAmH{&N$&cXmJA z??1O?N|0?9EN&ICPkecUw5}EXKA_lei&!AI`6lz`%6`SSE_F4T#QcW6DWf{i6LREg zlf6aX$n+phv9d50!fcNWEh8V{iy@ydT`MuIwlrh$kftPk{_xQMD{f#-V0s!K-%4q% z9gz4QYw|l-m9VP~&KO}7Bo4tUqOEs784_B2OL7w${p0eHEH@_^^p-;Jq6Rj2C(?I9 z3zaXC&Jwe6tNE#gJGTnfL2Yk)SUT6)B>LK5Ep`c;A7M?1U6?kkfl;j1v_ecTWK?W_ z#e~EEcee9&N99-P=Y)psL?_eN(QAZE*4weE=$6i$V|Hg^la_{xdt3;-6Cl0#7~{)f zJ%~u%2S9p^b*$s7!XP@p1BM$36L=h?kq55IL#ba{#{kQ@tJGAo^z_i+Q z)Ap%P6{_6`NN0pR_akwTz`HNtTV-;fo(^rvM5AJ9nTKv~cr<>*zHvqOM_;P{#FVjX z9psU<0#p$7z>39!+0x1W(auLajW>5-VSO^rw2pOjEy!rOvFTSAaGkYA%s>MdptARm za9b26kC>XrcE8B#sm*u(p^X!Fs}EgUwPL9IbGYw91^G(eeK5iL++fJUpt6JJ(jP88~qY!O8+^#`FSP0~NHzpt&x- z;apSJWgxmWhF}d@+`T+HN-(e+q&$ioW8Ac`GIsC}q$3pzAEw3p3iwTy2Jc&-x4aLj z(`Zj2oVjDZ*V_m+k=58x->wJOl#_IsQHgJsa^BT*^uU84pC>ASdg1 zk;fE_9U6Epqq&YrCp}M^`sn@Lz#6-R=kblb8MDIF&+Wg^yFnL`g{Cy!jkU0?KLJI7 zEfG)G%roLL;o$8&Xrqw9s;5ULu9!PiF#Oqka41_{C`f(vL^;;LL#&Pk_l5)Pan8XyLI8;V_2$RRL76$juTw^h7w1OZ9 zwv*8sqONbJsa!95-y0}_;t%Go>Ph9l6KK=**jk7Vbh&78*x-%X9K7Gfhkd;}7<5L? zb$4VQNB%20t-KZ6*Zr zz3Vz|tt;@iHsHZxuU~ZRNpt@+}GDZkY3)UhFCz#aqiF_g--Jl#zYk{o%6!$k1o9A08 zPr>nMuaodJ_+UVVXH$kcBH*ECAc6OZn$`~>fjD2|;$J%dQIA<7@lEjt*Z2h6vx+Mo zD@S8H{`Av>F<}G|vb9H07mF63?a%?>V_ob*U}nj0+;fA8I3JyS#|BvQ&brx^DAr>q zs_t7)+L5u?;3OHsh4FlI3xNbs-?{L!7TTr9-oDPV)E8T)TD0^veZiF8A^sX@rCMRE za1^i9OW*ai1FItZce46tF&VSurw#W0M3pW-t8i2z(=9(`j6sIQ!d-)xJ%2*4g+pEb z?98{WM>HPM7nfuf9}?FlVd>~73E}v-MPB#*?<48xe!Gpk-2Qvteg;c@p1vwT=v)hEo*Ri?Dk`MD39&KbqYbb(VVoweLK+-x4wlRW%``3mNS6K1l? z1t*;^pBd~f<<8495x|jeAE*sZ_iy&F#yuJ1{q!u*HvYa<#F0Ip*h)sIfy|Yt>0v9v zK$HqWun5+I6B}X7C?BySjV)krX!Q}0q3v99oi@*e4*a#>)^kXglnWWT@qYdFKvK0Q zXXoQJ=ea(|U#`7_8$1WdoL>e}KY3e}kEa@tUI~~?y={4T`ipW|YJ;)+RKWYACX+7v z`tBiI0=W1n`(=q}dIPY19PujafxG3TyLyN~#^j&#!W{R`-;=#lnl+-+U}hC7XSx4J z3~F4p%8+?G|9Bsy3(>iOl?H%_DhL&W(G@L*&RaaB)A~X9BN+m?YmBV*ijNpCBN~+Df*o{sHCn<-&N@D;Ivg zF=Eiw>N3KV8Uqb=&5CxueSE^hTifg^)|)RxyCB5_LSWyS|}O~S`M3f zX63l{Fu5@$XQ zWXhCG5avnTzkx=1IzgFRw*C)`lM9VAmH@O+Mr0*lpj=W1E$O;B)Di$0_9^(p;ycWY zCi}-0>SbS6GQVPCl>(Zllcg=d{~fGnAn5K1?pW53(zt~8Zu59&L<`-3I z9{I#oIYXz19A@9sf&jo{W&i*;%cspCGhYY@NTKM!#-)H>ZzW%BTcXBjK%4H8IvpsP zQQ~U{oRFgaVlKDd1wh#k2M5r)OK|#2a1Po6u}@JuxF~9#5lzQRmUnBd%dJr&0fgsY zz>*VX#%`3W#6AE_2ROpw!8@OPcFK)Zn(25b5LrF&x9Wj;5{?cqU7BBVSMsI#=GxM~ z9R8)R34R%3ZrT#Rv~NyFQL%4*r2srv5@aYiACZ^kw zjvNJ4^Aey%hl(!0|GU!xKtth0{{@2ne;9-M|ATY?U!s

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps seventy-eight scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    +

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps seventy-nine scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    `cross_backend_test.dart` compares them with per-scene budgets, and any new backend has to pass `flutter3d_conformance` before it counts as one. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 875c1cea2..f2759ca6e 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -227,12 +227,46 @@ instead. |---|---|---|---|---| | P1 | SMAA 1x | `render/smaa_pass.dart` | FXAA, TAA/TAAU | M | | P2 | Lens flare, lens distortion, `.cube` LUT import | `bloom_pass.dart` flares, `screen_distortion.dart`, `post_process/color_lut.dart` | halation, strip LUT grading (`doc/postprocessing-parity.md`, `gfx-30n` partial) | S–M | -| P3 | Projected box decals painting a material | `decal.dart` (0.24) | none | M | +| P3 | Projected box decals painting a material | `decal.dart` (0.24) | done, off by default; see below | M | | P4 | Planar reflections; public render-to-texture camera | `components/planar_reflector_component.dart`, `render_texture.dart` | SSR, probes; no public RTT | M | | P5 | Physical sky (atmospheric scattering), stars, height fog | `sky_sources.dart` `PhysicalSkySource` | gradient sky, cubemap, `Atmosphere` presets | M | | P6 | Debug views (material channels, UV, NaN, split wipe) and render stats | `render/debug_view.dart`, `render_stats.dart` (0.24) | viewport shading, NaN scan in `sim_mcp` | S | | P7 | Orthographic everywhere, per-draw ranges, alpha to coverage, render order | 0.24 commits | orthographic exists; check every effect | S | +**P3. Projected decals.** *Done:* a `DecalNode` is its node's unit cube and +paints a picture onto whatever geometry stands inside it, stamped down the +box's y axis: a texture or none, an sRGB tint with an opacity, an emission, a +region of an atlas, an `order`, and an angle limit with a fade past which a +surface turned away from the box's up is left alone, plus a fade at the top +and bottom faces. `RenderSettings.decals` is off by default. Depth cannot be +sampled, so `post/decal.frag` takes the point under each pixel from the +surface buffer, as reflections and occlusion do, and the light that point was +lit by from the lit colour over the albedo buffer: the decal's colour is laid +under that light, so it sits in shadows, under coloured lamps and in the +irradiance as the surface did. A fullscreen pass per batch of sixteen decals +and four pictures, two draws each (a multiplied factor, an added term for +unlit surfaces and emission), scissored to the boxes' screen rectangle, +drawn per view with each view's camera. Order is exact inside a batch. The +pass runs between the opaque half of the scene and the transparent one, +splitting the frame as glass does, so glass in front of a decal is drawn +over it. The mip level is chosen from neighbouring texels of the surface +buffer rather than `dFdx`, so the software backend picks the same one. CPU +mirror `DecalShader`; `decal_test.dart` (eleven claims, among them the ratio +to the light in and out of a shadow, the angle limit, order, batches past +sixteen and four, an unlit floor, emission, glass); the binding contract on +`FakeBackend`; the `decal-floor` golden in all four sets, WebGL and WebGPU at +0 of 172800 pixels from Impeller and the software set at 0.067%. +*Left:* decals that change the normal or the roughness (the light is read +back, not recomputed, so a normal map would need the surface buffer written +again and the surface lit again); decals on glass and other transparent +draws, which are drawn after the pass; exact order across batches (a second +batch swaps from the surface's albedo, not the first batch's); a surface that +wrote no albedo gets the decal at its own colour, and the surface's highlight, +emission and fog are scaled with the light it borrowed, both stated in +`decal.frag`; a device with two colour attachments draws none; a layer mask +per decal; decals in the level format, the editor and the MCP tools; the +comparison page row, with the 0.9.0 revision (decision 19). + Already ours and not theirs, to keep: volumetric fog, weighted blended OIT, motion blur, local exposure and HDR out, EVSM, impostors, Hi-Z and software occlusion, generated LOD, WebGPU, the CPU backend, retargeting. diff --git a/tool/ci.sh b/tool/ci.sh index de3f886d2..654ea9c5f 100755 --- a/tool/ci.sh +++ b/tool/ci.sh @@ -9,7 +9,7 @@ # # What it does NOT do, stated so the gap is not mistaken for coverage: # -# * The Impeller half of the golden set. Those seventy-eight scenes render through +# * The Impeller half of the golden set. Those seventy-nine scenes render through # flutter_gpu and need a real device, so they run from # packages/flutter3d/tool/golden.sh on a machine with a GPU. The software # half runs here, and cross_backend_test.dart compares the two committed From da2a61ad814dc158427cb791b40d17af2a0ed20c Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 05:02:16 +0300 Subject: [PATCH 028/141] The root's own test run checks the structure rules The root's test directory held only reference pictures, so running the test runner there found no test and exited 79, and a check that runs it at the root read that as a failure. test/structure_test.dart runs the detectors' self-proof and every rule in allRules as a test of its own: the same list tool/structure.dart runs, not a second copy of it. The root takes package:test as a dev dependency for it; the lock file only moves test from transitive to direct. The root is not one of the directories the test count scans, so the counts in the documents do not move. --- pubspec.lock | 2 +- pubspec.yaml | 6 ++++++ test/structure_test.dart | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 test/structure_test.dart diff --git a/pubspec.lock b/pubspec.lock index ea22f2b93..353f69f61 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -871,7 +871,7 @@ packages: source: hosted version: "1.2.2" test: - dependency: transitive + dependency: "direct dev" description: name: test sha256: ca578dc12bb8b2f40b67b7d3bd2fac4f31c01a6ff7130a14e2597b919934507f diff --git a/pubspec.yaml b/pubspec.yaml index 11caf481e..56ca1a601 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,12 @@ publish_to: 'none' environment: sdk: ^3.12.2 +# For `test/structure_test.dart`, the one suite the root has: `dart test` run +# here used to find no test at all and exit 79, which a check that runs it +# reads as a failure rather than as an empty directory. +dev_dependencies: + test: ^1.25.0 + workspace: - packages/flutter3d - packages/flutter3d_core diff --git a/test/structure_test.dart b/test/structure_test.dart new file mode 100644 index 000000000..310034eb4 --- /dev/null +++ b/test/structure_test.dart @@ -0,0 +1,34 @@ +/// The repository's own arrangement, held to the rules `tool/structure.dart` +/// checks, under `dart test` from the root. +/// +/// dart test +/// +/// **The same rules, not a second copy of them.** The script is the first step +/// of `tool/ci.sh` and prints a report a person reads; this runs the list it +/// runs, `allRules`, so a check that only knows to call `dart test` at the root +/// asks the same questions. Before this file the root held only reference +/// pictures, and `dart test` there found nothing and exited 79. +library; + +import 'package:test/test.dart'; + +import '../tool/structure/detectors.dart'; +import '../tool/structure/rules.dart'; + +void main() { + // Mutation: a detector that can no longer fire passes every rule built on + // it; this is the test that says so before the rules are believed. + test('every detector fires on what it is meant to find', () { + final broken = proveDetectorsWork(); + expect(broken, isEmpty, reason: broken.join('\n')); + }); + + for (final rule in allRules) { + // Mutation: change a test count in README.md, or drop a golden from one + // set, and the rule that holds it fails here as it does in the script. + test(rule.name, () { + final found = rule.run(); + expect(found, isEmpty, reason: found.join('\n')); + }); + } +} From 0ec4912a2692adddb104d08d5ae2e6acce8dcad0 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Thu, 1 Oct 2026 11:00:58 +0300 Subject: [PATCH 029/141] The README counts the golden scenes again, and a decal's scissor is pinned The README still said 78 scenes after decal-floor made it 79. The rule that holds the documents to the count never read the README, so it is in the list now, and the old sentence fails it. The decal pass hands its scissor to the backend from the top left, with no adjustment for the framebuffer origin, while it adjusts the matrix. That is the engine's contract: WebGL2's encoder turns every rectangle upside down itself, and the scene pass hands over its view rectangles the same way; the matrix is adjusted because the stage reads texture rows, which no encoder translates. Flipping the scissor for a bottom-left origin as well was run: the WebGL2 decal-floor golden then differs by 602 pixels. The code says so beside the scissor, and a new software test keeps a decal in the top rows of the frame, which a scissor measured from the wrong edge cuts away entirely. --- ARCHITECTURE.md | 2 +- README.md | 4 +- .../engine/render/renderer_decal_pass.dart | 9 +++++ packages/flutter3d_cpu/test/decal_test.dart | 37 +++++++++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 ++-- tasks/0.9-engine-roadmap.md | 5 ++- tool/structure/rules.dart | 4 ++ 8 files changed, 61 insertions(+), 10 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index fbe72a58b..498fd2a63 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2689,7 +2689,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10892 tests** across 42 packages and 10 applications | +| Unit tests | **10893 tests** across 42 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 03bd39ce0..0fd39b5ad 100644 --- a/README.md +++ b/README.md @@ -158,9 +158,9 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10892 tests across forty-two packages and nine applications. The +There are 10893 tests across forty-two packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other -half is rendered by the software backend, which is what makes 78 scenes +half is rendered by the software backend, which is what makes 79 scenes checkable in a headless run. Several steps run in a browser. `flutter test --platform chrome` covers the two diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_decal_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_decal_pass.dart index c37b13c5b..cbd87eaed 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_decal_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_decal_pass.dart @@ -194,6 +194,15 @@ extension _DecalPass on Renderer { sampler: _pictureSampler, ); } + // **From the top left, and not adjusted for the framebuffer origin, + // unlike the matrix above.** A rectangle is the engine's own + // vocabulary and every backend takes it from the top: WebGL2's + // encoder turns it upside down itself in `setScissor`, as it does for + // the scene pass's view rectangles, which are handed over the same + // way. The matrix is different because the stage reads texture rows, + // which no encoder translates. Flipping here as well was tried: the + // WebGL2 `decal-floor` golden differs by 602 pixels, and + // `decal_test.dart` keeps a decal in the top rows for the same reason. final scissor = batch.covered; for (final term in const [0.0, 1.0]) { info.params[3] = term; diff --git a/packages/flutter3d_cpu/test/decal_test.dart b/packages/flutter3d_cpu/test/decal_test.dart index d3f9abbf3..216a060b9 100644 --- a/packages/flutter3d_cpu/test/decal_test.dart +++ b/packages/flutter3d_cpu/test/decal_test.dart @@ -415,6 +415,43 @@ void main() { expect(centre.y, lessThan(was.y)); }); + test('a decal near the top of the frame is painted to its edges', () { + // The pass cuts each draw to the screen rectangle its boxes cover, counted + // from the top left as every rectangle the engine hands a backend is. A + // decal in the top rows of the picture, with nothing in the bottom ones, + // is the arrangement where a rectangle counted from the wrong edge cuts + // away all of it. + final it = _stage(); + final decal = it.scene.add( + DecalNode(color: Vector4(1.0, 0.0, 0.0, 1.0)) + ..setScale(2.0, 1.0, 1.0) + ..setPosition(0.0, 0.0, -1.5), + ); + decal.visible = false; + final reference = _scene(it); + decal.visible = true; + final painted = _scene(it); + + // Where the box's footprint on the floor lands, row by row. + final project = it.camera.viewProjection(1.0); + int row(double z) { + final clip = project.transform(Vector4(0.0, 0.0, z, 1.0)); + return ((0.5 - clip.y / clip.w * 0.5) * _size).floor(); + } + + final top = row(-2.0); + final bottom = row(-1.0); + expect(bottom, lessThan(_size ~/ 2), reason: 'the box is in the top half'); + final rows = _changed(painted, reference).map((p) => p ~/ _size).toSet(); + // Mutation: a scissor whose y is measured from the bottom, or flipped + // again for a backend whose rows run upward, leaves none of these rows + // painted. + for (var y = top + 1; y < bottom; y++) { + expect(rows, contains(y), reason: 'row $y of $top..$bottom'); + } + expect(rows.every((y) => y >= top && y <= bottom), isTrue); + }); + test('switched off by name like any other pass', () { final it = _stage(); final without = _scene(it); diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 5db90b11d..b25a27f53 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10892 tests across 42 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-nine scenes stay checkable in a headless run. +There are 10893 tests across 42 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-nine scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 49483fb3e..a2e030523 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10892 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10893 tests need a GPU. --- # Testing -10892 tests across 42 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10893 tests across 42 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -19,7 +19,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_racing` | 223 | | `flutter3d_webgl` | 62 | | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 63 | | `apps/flutter3d_demo_platformer` | 202 | | `flutter3d_impeller` | 64 | -| `flutter3d_cpu` | 339 | | `apps/flutter3d_demo_strategy` | 50 | +| `flutter3d_cpu` | 340 | | `apps/flutter3d_demo_strategy` | 50 | | `apps/flutter3d_editor` | 224 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | @@ -39,7 +39,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 137 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10866 rather than 10892: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10867 rather than 10893: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index f2759ca6e..c4da9de8e 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -251,9 +251,10 @@ pass runs between the opaque half of the scene and the transparent one, splitting the frame as glass does, so glass in front of a decal is drawn over it. The mip level is chosen from neighbouring texels of the surface buffer rather than `dFdx`, so the software backend picks the same one. CPU -mirror `DecalShader`; `decal_test.dart` (eleven claims, among them the ratio +mirror `DecalShader`; `decal_test.dart` (twelve claims, among them the ratio to the light in and out of a shadow, the angle limit, order, batches past -sixteen and four, an unlit floor, emission, glass); the binding contract on +sixteen and four, an unlit floor, emission, glass, and a decal in the top +rows the scissor must not cut); the binding contract on `FakeBackend`; the `decal-floor` golden in all four sets, WebGL and WebGPU at 0 of 172800 pixels from Impeller and the software set at 0.067%. *Left:* decals that change the normal or the roughness (the light is read diff --git a/tool/structure/rules.dart b/tool/structure/rules.dart index 2b5964f07..81241fbea 100644 --- a/tool/structure/rules.dart +++ b/tool/structure/rules.dart @@ -2265,6 +2265,10 @@ List _goldenSceneCount() { 'tool/ci.sh', 'packages/flutter3d_webgl/tool/golden_web.sh', 'ARCHITECTURE.md', + // The README's own count of the scenes was left at seventy-eight when + // the seventy-ninth landed: it was the one page that says it and that + // nothing here read. + 'README.md', ]) File('${repositoryRoot.path}/$where'), ..._prosePages(), From 9f8e399549abc4ba3b573e6dc7e24a9edf89bff3 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 03:46:06 +0300 Subject: [PATCH 030/141] Edges can be smoothed by SMAA as well as FXAA AntiAliasSettings takes a method now, and EdgeSmoothing.smaa runs SMAA 1x on the finished picture in three passes: the luma steps marked, the line behind each staircase rebuilt from where its run ends and which side of each end a crossing edge stands on, and each pixel blended by the area that line covers of it. Against an 8x8 supersample a tilted edge comes out at about a quarter of its hard error, at both slopes and both orientations. FXAA stays the default, and a bundle without the stages falls back to it. The orthogonal area table is generated by tool/make_tables.dart and read by the GPUs and the software backend alike. smaa-teapot is recorded in all four sets; the software and WebGPU pictures match Impeller's to the pixel. Diagonal search and corner rounding are left out. --- ARCHITECTURE.md | 8 +- README.md | 2 +- .../example/lib/src/spike/backend_web.dart | 2 +- .../example/lib/src/spike/golden_runner.dart | 2 +- .../example/lib/src/spike/golden_scenes.dart | 8 + .../example/lib/src/spike/golden_stages.dart | 12 + .../lib/src/spike/golden_store_web.dart | 4 +- packages/flutter3d/test/ambient_test.dart | 2 +- packages/flutter3d/test/anisotropy_test.dart | 4 +- packages/flutter3d/test/auto_batch_test.dart | 4 +- packages/flutter3d/test/fog_test.dart | 2 +- .../flutter3d/test/goldens/smaa-teapot.png | Bin 0 -> 28664 bytes packages/flutter3d/test/grade_test.dart | 2 +- packages/flutter3d/test/halation_test.dart | 2 +- .../test/irradiance_bounce_test.dart | 2 +- .../flutter3d/test/masked_shadow_test.dart | 2 +- .../test/material_vertex_stage_test.dart | 2 +- .../test/per_view_exposure_test.dart | 2 +- .../flutter3d/test/render_scale_test.dart | 2 +- packages/flutter3d/test/sharpen_test.dart | 2 +- .../flutter3d/test/sky_procedural_test.dart | 2 +- packages/flutter3d/test/ssao_blur_test.dart | 2 +- packages/flutter3d_core/CHANGELOG.md | 11 + .../lib/src/engine/render/engine_tables.dart | 5 + .../src/engine/render/render_settings.dart | 41 +- .../lib/src/engine/render/renderer.dart | 3 +- .../engine/render/renderer_frame_nodes.dart | 46 +- .../src/engine/render/renderer_post_pass.dart | 84 ++- .../engine/render/renderer_shadow_pass.dart | 2 +- .../src/engine/render/tables/smaa_area.dart | 483 +++++++++++++ .../test/engine/engine_tables_test.dart | 32 +- .../formats/ktx2_supercompression_test.dart | 2 +- packages/flutter3d_core/tool/make_tables.dart | 130 ++++ packages/flutter3d_cpu/CHANGELOG.md | 8 + .../lib/src/cpu_shaders_builtin.dart | 5 + .../lib/src/cpu_shaders_post.dart | 2 +- .../lib/src/cpu_shaders_smaa.dart | 210 ++++++ .../flutter3d_cpu/lib/src/cpu_texture.dart | 2 +- .../flutter3d_cpu/lib/src/portable_log2.dart | 2 +- .../test/cross_backend_test.dart | 4 + .../test/goldens/smaa-teapot.png | Bin 0 -> 28664 bytes packages/flutter3d_cpu/test/smaa_test.dart | 150 ++++ .../test/gpu_formats_test.dart | 2 +- packages/flutter3d_shaders/CHANGELOG.md | 6 + .../lib/flutter3d_shaders.dart | 3 + .../flutter3d_shaders/lib/stage_bindings.dart | 12 + .../flutter3d_shaders/lib/typed_blocks.dart | 13 + .../flutter3d_shaders/lib/uniform_blocks.dart | 15 + .../shaders/flutter3d.shaderbundle.json | 12 + .../shaders/post/smaa_blend.frag | 50 ++ .../shaders/post/smaa_edges.frag | 56 ++ .../shaders/post/smaa_weights.frag | 122 ++++ packages/flutter3d_webgl/CHANGELOG.md | 5 + .../flutter3d_webgl/lib/engine_shaders.dart | 246 +++++++ .../test/cross_backend_test.dart | 4 + .../test/goldens/smaa-teapot.png | Bin 0 -> 28757 bytes .../test/lighting_models_test.dart | 2 +- packages/flutter3d_webgl/tool/golden_web.sh | 2 +- packages/flutter3d_webgpu/CHANGELOG.md | 6 + .../flutter3d_webgpu/lib/engine_shaders.dart | 658 ++++++++++++++++++ .../test/cross_backend_test.dart | 1 + .../test/goldens/smaa-teapot.png | Bin 0 -> 28677 bytes site/content/core/architecture.md | 2 +- site/content/core/backends.md | 2 +- site/content/core/index.md | 2 +- site/content/core/rendering.md | 2 +- site/content/quickstart.md | 2 +- site/content/reference/packages.md | 2 +- site/content/reference/testing.md | 14 +- tasks/0.9-engine-roadmap.md | 8 + tool/ci.sh | 2 +- 71 files changed, 2484 insertions(+), 59 deletions(-) create mode 100644 packages/flutter3d/test/goldens/smaa-teapot.png create mode 100644 packages/flutter3d_core/lib/src/engine/render/tables/smaa_area.dart create mode 100644 packages/flutter3d_cpu/lib/src/cpu_shaders_smaa.dart create mode 100644 packages/flutter3d_cpu/test/goldens/smaa-teapot.png create mode 100644 packages/flutter3d_cpu/test/smaa_test.dart create mode 100644 packages/flutter3d_shaders/shaders/post/smaa_blend.frag create mode 100644 packages/flutter3d_shaders/shaders/post/smaa_edges.frag create mode 100644 packages/flutter3d_shaders/shaders/post/smaa_weights.frag create mode 100644 packages/flutter3d_webgl/test/goldens/smaa-teapot.png create mode 100644 packages/flutter3d_webgpu/test/goldens/smaa-teapot.png diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0c26831d1..37e52ff17 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2690,7 +2690,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10883 tests** across 43 packages and 10 applications | +| Unit tests | **10886 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | @@ -2730,7 +2730,7 @@ material parameter — is the case `doc/boundary-0.5.0.md` worked through for set belongs to whoever builds on the package rather than to the package itself. -**Golden render tests.** 78 scenes against **four complete independent +**Golden render tests.** 79 scenes against **four complete independent reference sets** — Impeller, the software rasteriser, WebGL2 and WebGPU — each held to zero differing pixels against its own set, with a per-channel tolerance of 8. @@ -3142,7 +3142,7 @@ metres. The directional light's cascades fit the view up to that distance and nothing beyond it casts — a level whose far end matters visually wants the number raised, and pays for it in texels. -**The web backend draws all seventy-eight golden scenes the way Impeller does**, +**The web backend draws all seventy-nine golden scenes the way Impeller does**, between 0.01% and 0.42% of pixels differing by more than 8 per channel — the silhouette's worth of disagreement two rasterisers always have. Two of those numbers fell when the minification filter learned to read a sampler's @@ -3258,7 +3258,7 @@ and charge each of them the bytes, and neither is a change to make on somebody else's behalf. A build that wants it says so in one flag, and the engine's own example takes the same answer from `?backend=webgpu` in the URL — a query parameter rather than a define, because the browser golden stand's whole saving -is one dart2js run serving seventy-eight scenes and both browser backends. +is one dart2js run serving seventy-nine scenes and both browser backends. **`flutter3d_shaders` is one text and no two backends take it the same way.** Impeller compiles the GLSL with `impellerc`; the WebGL2 generator translates it diff --git a/README.md b/README.md index 00e114aa8..652849853 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10883 tests across forty-three packages and nine applications. The +There are 10886 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/packages/flutter3d/example/lib/src/spike/backend_web.dart b/packages/flutter3d/example/lib/src/spike/backend_web.dart index 1594c7699..6f7f66298 100644 --- a/packages/flutter3d/example/lib/src/spike/backend_web.dart +++ b/packages/flutter3d/example/lib/src/spike/backend_web.dart @@ -13,7 +13,7 @@ import 'golden_store.dart'; /// /// Read from the URL, exactly the way the golden scene and the record/compare /// direction are read, and for exactly the same arithmetic: one dart2js run -/// serves seventy-eight scenes because the scene is a query parameter, and one +/// serves seventy-nine scenes because the scene is a query parameter, and one /// dart2js run serves the browser backends because this is one too. A /// `--dart-define` here would be a build per backend, which is the whole saving /// of the browser golden stand spent on a single word. diff --git a/packages/flutter3d/example/lib/src/spike/golden_runner.dart b/packages/flutter3d/example/lib/src/spike/golden_runner.dart index a2849360a..265d7a878 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_runner.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_runner.dart @@ -122,7 +122,7 @@ final class GoldenRunner { scene, // The store's answer wins where it has one. A desktop run takes the // direction from the environment and a browser run from the URL, for the - // same reason on both: one build has to serve seventy-eight scenes in both + // same reason on both: one build has to serve seventy-nine scenes in both // directions, and anything the compiler sees is another build. update: updateOverride ?? diff --git a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart index 8a364e6dd..e6681930c 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart @@ -1072,6 +1072,14 @@ final List kGoldenScenes = [ configure: GoldenStages.easuHalf, ), + // P1. `shadow-teapot` with its edges smoothed by SMAA 1x. + const GoldenScene( + name: 'smaa-teapot', + source: 'obj: Teapot', + bloom: false, + configure: GoldenStages.smaaTeapot, + ), + // R6. A turning wheel blurred along its own motion. const GoldenScene( name: 'motion-blur-spin', diff --git a/packages/flutter3d/example/lib/src/spike/golden_stages.dart b/packages/flutter3d/example/lib/src/spike/golden_stages.dart index f5f5c8aaf..75ba59058 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_stages.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_stages.dart @@ -1118,6 +1118,18 @@ abstract final class GoldenStages { spatialUpscale: const SpatialUpscaleSettings(enabled: true), ); + // ------------------------------------------------------------------ P1 + + /// `smaa-teapot`: `shadow-teapot`'s silhouette and shadow edge smoothed by + /// SMAA 1x on the finished picture. + static RenderSettings smaaTeapot(RenderSettings settings) => + settings.copyWith( + antiAlias: settings.antiAlias.copyWith( + enabled: true, + method: EdgeSmoothing.smaa, + ), + ); + // ------------------------------------------------------------------ R6 /// `motion-blur-spin`: a wheel of three spokes round a hub, turning at a diff --git a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart index aeacfce16..ac73959df 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart @@ -40,7 +40,7 @@ void reportLine(String message) { /// The scene named in the page's URL, if any. /// /// A run-time choice here, where the desktop path takes a compile-time define. -/// The reason is arithmetic: the suite is seventy-eight scenes, and rebuilding +/// The reason is arithmetic: the suite is seventy-nine scenes, and rebuilding /// the bundle for each is seventy-eight dart2js runs to compare seventy-eight /// pictures. One build and seventy-eight navigations is the same information /// in a fraction of the time. @@ -58,7 +58,7 @@ String? get sceneOverride { /// Whether this run records rather than compares, from the page's URL. /// /// A run-time choice for the same reason [sceneOverride] is one: the suite is -/// seventy-eight scenes and rebuilding for each would be seventy-eight dart2js +/// seventy-nine scenes and rebuilding for each would be seventy-eight dart2js /// runs. One build serves both directions, and the URL says which. bool get updateOverride => Uri.base.queryParameters['update'] == '1'; diff --git a/packages/flutter3d/test/ambient_test.dart b/packages/flutter3d/test/ambient_test.dart index bb140c44b..7474816e7 100644 --- a/packages/flutter3d/test/ambient_test.dart +++ b/packages/flutter3d/test/ambient_test.dart @@ -141,7 +141,7 @@ void main() { // approximately, bit for bit. // // Mutation: default either end of the hemisphere to anything but white. - // Seventy-eight goldens move at once. + // Seventy-nine goldens move at once. final lit = await _pixels(_ball(), _noSky); final top = _band(lit, 0.15, 0.35); final bottom = _band(lit, 0.65, 0.85); diff --git a/packages/flutter3d/test/anisotropy_test.dart b/packages/flutter3d/test/anisotropy_test.dart index 14a8cdc09..6c2b0a7e0 100644 --- a/packages/flutter3d/test/anisotropy_test.dart +++ b/packages/flutter3d/test/anisotropy_test.dart @@ -24,7 +24,7 @@ /// which spends the taps and throws away what they bought — contrast drops /// *below* the trilinear baseline and the first assertion fails; and letting /// a sampler that asked for one reach the new path at all, which would move a -/// backend seventy-eight golden scenes are recorded on. +/// backend seventy-nine golden scenes are recorded on. /// /// **One they do not catch, said rather than hidden:** taking the taps along /// the *short* axis instead of the long one passes every line here. The @@ -213,7 +213,7 @@ void main() { }); test('asking for one tap is the old sampler exactly', () async { - // What makes this safe to add to the backend seventy-eight golden scenes are + // What makes this safe to add to the backend seventy-nine golden scenes are // recorded on. Not "close": the same bytes, because with `anisotropy` at // one the new path is not reached at all. expect(await _floorAt(1), await _floorAt(1)); diff --git a/packages/flutter3d/test/auto_batch_test.dart b/packages/flutter3d/test/auto_batch_test.dart index 16b78e235..1b3ee79fc 100644 --- a/packages/flutter3d/test/auto_batch_test.dart +++ b/packages/flutter3d/test/auto_batch_test.dart @@ -22,7 +22,7 @@ /// three backends.** It computes in Dart doubles; Impeller, WebGL and WebGPU /// compute in 32-bit floats, where those two expressions have far less room /// before they part. Nothing here can run them, so the setting stays off by -/// default — which is also what keeps the seventy-eight goldens where they are. +/// default — which is also what keeps the seventy-nine goldens where they are. library; import 'dart:math' as math; @@ -191,7 +191,7 @@ void main() { }); test('nothing is batched unless it is asked for', () async { - // Which is what keeps the seventy-eight goldens where they are: the default + // Which is what keeps the seventy-nine goldens where they are: the default // frame is the frame it always was, to the byte. final device = _device(); final scene = _field(device, count: 100); diff --git a/packages/flutter3d/test/fog_test.dart b/packages/flutter3d/test/fog_test.dart index b3063caf6..b6761f91e 100644 --- a/packages/flutter3d/test/fog_test.dart +++ b/packages/flutter3d/test/fog_test.dart @@ -151,7 +151,7 @@ void main() { test('no fog is byte-identical to the fog nobody asked for', () async { // The early return at zero density is not an optimisation. The golden sets - // — seventy-eight scenes, zero-pixel threshold, three backends — are all + // — seventy-nine scenes, zero-pixel threshold, three backends — are all // recorded with the default `FogSettings()`, and this is the property that // lets them stay recorded. final none = FogSettings(); diff --git a/packages/flutter3d/test/goldens/smaa-teapot.png b/packages/flutter3d/test/goldens/smaa-teapot.png new file mode 100644 index 0000000000000000000000000000000000000000..dbf463c5791987941a9ca654eeff19c805925108 GIT binary patch literal 28664 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 Ny}aswh_@zz{|8CNy{iBK literal 0 HcmV?d00001 diff --git a/packages/flutter3d/test/grade_test.dart b/packages/flutter3d/test/grade_test.dart index 247cd834b..7c0bd87e6 100644 --- a/packages/flutter3d/test/grade_test.dart +++ b/packages/flutter3d/test/grade_test.dart @@ -63,7 +63,7 @@ Future> _ramp(LookSettings look, {int width = 256}) async { void main() { test('every default is an exact identity', () async { - // The promise seventy-eight goldens rest on. Neutral for these three is + // The promise seventy-nine goldens rest on. Neutral for these three is // (0,0,0) added, (1,1,1) as an exponent and (1,1,1) multiplied, and the // composite has to take all three without moving a byte. final plain = await _ramp(const LookSettings()); diff --git a/packages/flutter3d/test/halation_test.dart b/packages/flutter3d/test/halation_test.dart index 910292c87..b26b235ff 100644 --- a/packages/flutter3d/test/halation_test.dart +++ b/packages/flutter3d/test/halation_test.dart @@ -87,7 +87,7 @@ void main() { test('zero is an exact identity, and is the default', () async { // The multiplier is one on every channel at zero, so the chain comes out - // byte for byte as it did — which is what seventy-eight goldens need. + // byte for byte as it did — which is what seventy-nine goldens need. expect(const BloomSettings().halation, 0.0); final a = await _glow(halation: 0.0); final b = await _glow(halation: 0.0); diff --git a/packages/flutter3d/test/irradiance_bounce_test.dart b/packages/flutter3d/test/irradiance_bounce_test.dart index cb3325c58..a697f4efc 100644 --- a/packages/flutter3d/test/irradiance_bounce_test.dart +++ b/packages/flutter3d/test/irradiance_bounce_test.dart @@ -136,7 +136,7 @@ double _tint(Uint8List rgba) { void main() { test('off, the picture is the bytes it was', () async { // The clause every recorded frame depends on. A field is null by default, - // so this is the path seventy-eight goldens take. + // so this is the path seventy-nine goldens take. final a = await _draw(field: false, redWall: true); final b = await _draw(field: false, redWall: true); expect(a, orderedEquals(b)); diff --git a/packages/flutter3d/test/masked_shadow_test.dart b/packages/flutter3d/test/masked_shadow_test.dart index 9ea6e71e2..11e0532bd 100644 --- a/packages/flutter3d/test/masked_shadow_test.dart +++ b/packages/flutter3d/test/masked_shadow_test.dart @@ -223,7 +223,7 @@ void main() { test('a caster with no map keeps the shadow it always cast', () async { // **The half that lets this land.** A material that is not cut out goes // through the stage it has always gone through, with no sampler in the - // pipeline and no texture bound per draw, so the seventy-eight goldens + // pipeline and no texture bound per draw, so the seventy-nine goldens // recorded against that stage cannot move. Asking for MASK with no map to // read is the same case: there is nothing to cut out. final plain = await _frame(masked: false, withTexture: false); diff --git a/packages/flutter3d/test/material_vertex_stage_test.dart b/packages/flutter3d/test/material_vertex_stage_test.dart index 0eda03c58..715be1530 100644 --- a/packages/flutter3d/test/material_vertex_stage_test.dart +++ b/packages/flutter3d/test/material_vertex_stage_test.dart @@ -172,7 +172,7 @@ void main() { // **The half that lets this land**, and the reason the seam is a null field // rather than a new required one: a material saying nothing about its vertex // stage goes through the pipeline it has always gone through, which is also - // why the seventy-eight goldens cannot move. + // why the seventy-nine goldens cannot move. final withSeam = await _draw(lighting: _plain); final device = CpuDevice( diff --git a/packages/flutter3d/test/per_view_exposure_test.dart b/packages/flutter3d/test/per_view_exposure_test.dart index 928ab0c2c..f0acbc6d8 100644 --- a/packages/flutter3d/test/per_view_exposure_test.dart +++ b/packages/flutter3d/test/per_view_exposure_test.dart @@ -157,7 +157,7 @@ void main() { // **The strongest form of "this costs nothing where it is not wanted".** // One view is the whole frame, so its rectangle is the whole histogram // and its exposure is the frame's. The composite takes its single-draw - // path, and seventy-eight goldens stay where they are. + // path, and seventy-nine goldens stay where they are. final off = await frame( const RenderSettings(autoExposure: AutoExposureSettings(enabled: true)), ); diff --git a/packages/flutter3d/test/render_scale_test.dart b/packages/flutter3d/test/render_scale_test.dart index 61a8419b2..4dd0bd26b 100644 --- a/packages/flutter3d/test/render_scale_test.dart +++ b/packages/flutter3d/test/render_scale_test.dart @@ -36,7 +36,7 @@ import 'package:vector_math/vector_math.dart'; void main() { test('one is the whole resolution, and is the default', () { - // Seventy-eight goldens are recorded at the size they asked for. A default + // Seventy-nine goldens are recorded at the size they asked for. A default // that shrank anything would move every one of them. expect(const RenderSettings().renderScale, 1.0); expect(_drawn(1.0), (width: 64, height: 64)); diff --git a/packages/flutter3d/test/sharpen_test.dart b/packages/flutter3d/test/sharpen_test.dart index 512e848dd..4d2a2bb21 100644 --- a/packages/flutter3d/test/sharpen_test.dart +++ b/packages/flutter3d/test/sharpen_test.dart @@ -95,7 +95,7 @@ int _steepest(List row) { void main() { test('zero is an exact identity, and is the default', () async { - // Seventy-eight goldens go through this pass. The shader returns the centre + // Seventy-nine goldens go through this pass. The shader returns the centre // untouched at zero rather than running a kernel that rounds to nothing, // because "rounds to nothing" is a claim about the target's bit depth. expect(const AntiAliasSettings().sharpen, 0.0); diff --git a/packages/flutter3d/test/sky_procedural_test.dart b/packages/flutter3d/test/sky_procedural_test.dart index 864609085..3f22d5119 100644 --- a/packages/flutter3d/test/sky_procedural_test.dart +++ b/packages/flutter3d/test/sky_procedural_test.dart @@ -167,7 +167,7 @@ double _mean(Uint8List rgba, {required int channel}) { void main() { _hardEdgedSun(); test('no sky is byte-identical to the sky nobody asked for', () async { - // The property seventy-eight goldens rest on: a frame with no sky in it is + // The property seventy-nine goldens rest on: a frame with no sky in it is // the frame this renderer has always drawn. // // Mutation: drop the early return. The sky is drawn into every scene that diff --git a/packages/flutter3d/test/ssao_blur_test.dart b/packages/flutter3d/test/ssao_blur_test.dart index ec770e01d..b1c103f5a 100644 --- a/packages/flutter3d/test/ssao_blur_test.dart +++ b/packages/flutter3d/test/ssao_blur_test.dart @@ -75,7 +75,7 @@ void main() { test('no taps is no pass, and is the default', () async { // The occlusion is already off by default, and this is a second // full-screen pass over it — so nothing pays for either until two things - // are switched on. Seventy-eight goldens depend on the frame being what it + // are switched on. Seventy-nine goldens depend on the frame being what it // was. expect(const AmbientOcclusionSettings().blurTaps, 0); expect(await _frame(blurTaps: 0), await _frame(blurTaps: 0)); diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index cce175b2b..ea18d2e96 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -1,5 +1,16 @@ ## Unreleased +- **SMAA 1x beside FXAA.** `AntiAliasSettings(method: EdgeSmoothing.smaa)` + smooths the finished picture in three passes: the luma steps marked, the + line behind each staircase rebuilt from where its run ends and which side + of each end a crossing edge stands on, and each pixel blended by the area + that line covers of it. Against an 8×8 supersample a tilted edge comes out + at about a quarter of its hard error. FXAA stays the default; a bundle + without the three stages falls back to it. Orthogonal edges only: no + diagonal search and no corner rounding. The area table is the engine's + `smaaArea`, written by `tool/make_tables.dart` and shared with the + software backend byte for byte. + - **A reload reaches the contributors too.** `Renderer.relinkShaders` asks every `PassContributor` to drop what it linked, through the new `PassContributor.relinkShaders`, which does nothing by default. diff --git a/packages/flutter3d_core/lib/src/engine/render/engine_tables.dart b/packages/flutter3d_core/lib/src/engine/render/engine_tables.dart index f3d0cbd8c..f5aba0883 100644 --- a/packages/flutter3d_core/lib/src/engine/render/engine_tables.dart +++ b/packages/flutter3d_core/lib/src/engine/render/engine_tables.dart @@ -13,6 +13,7 @@ import 'engine_table.dart'; import 'tables/aces2_display.dart' as tables; import 'tables/blue_noise.dart' as tables; import 'tables/ltc.dart' as tables; +import 'tables/smaa_area.dart' as tables; export 'engine_table.dart'; @@ -32,6 +33,7 @@ final class EngineTables { tables.blueNoise, tables.aces2Display, tables.ltc, + tables.smaaArea, ]; /// Entries per axis of [aces2Display] — `L2`. @@ -53,6 +55,9 @@ final class EngineTables { /// See `tables/ltc.dart`. TextureHandle get ltc => this[tables.ltc]; + /// See `tables/smaa_area.dart`. + TextureHandle get smaaArea => this[tables.smaaArea]; + /// [table] on this device, uploaded now if it was not already. TextureHandle operator [](EngineTable table) => _uploaded[table] ??= _device.createTextureFromPixels( diff --git a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart index 9369099e3..5cebd0a5a 100644 --- a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart +++ b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart @@ -1268,7 +1268,7 @@ final class RenderSettings { /// eight-bit answer — `auto_batch_test.dart` holds a hundred cubes, turned and /// scaled, to byte equality. Impeller, WebGL and WebGPU compute in 32-bit /// floats, where those expressions have far less room before they part, and - /// nothing headless can run them. So the seventy-eight goldens keep the frame + /// nothing headless can run them. So the seventy-nine goldens keep the frame /// they have, and an application that wants the draw calls back asks. /// /// Shadows and picking are unaffected: both walk the scene themselves and @@ -2025,7 +2025,7 @@ final class DisplayTransform { /// **Everything here defaults to doing nothing, exactly.** Not nearly nothing: /// a vignette of zero multiplies by one and grain of zero adds zero, so a scene /// that asks for none of it composites to the same bytes it did before this -/// existed. Seventy-eight goldens depend on that being exact, and the composite +/// existed. Seventy-nine goldens depend on that being exact, and the composite /// pass already keeps the same promise for ambient occlusion. /// /// Applied in the composite rather than as passes of their own, which is the @@ -2293,6 +2293,7 @@ int bloomLevelsFor(BloomSettings settings, {required int frameHeight}) { final class AntiAliasSettings { const AntiAliasSettings({ this.enabled = false, + this.method = EdgeSmoothing.fxaa, this.contrastThreshold = 0.125, this.blend = 0.75, this.sharpen = 0.0, @@ -2301,6 +2302,11 @@ final class AntiAliasSettings { final bool enabled; + /// How the edges are found and smoothed when [enabled] — `P1`. + /// [EdgeSmoothing.fxaa], the one pass this setting has always run, is the + /// default. + final EdgeSmoothing method; + /// Anti-aliasing across frames — `R1`, `R2`. Independent of [enabled]: the /// temporal resolve and the edge pass can run together, and the resolve is /// the one that replaces multisampling. @@ -2346,12 +2352,14 @@ final class AntiAliasSettings { AntiAliasSettings copyWith({ bool? enabled, + EdgeSmoothing? method, double? contrastThreshold, double? blend, double? sharpen, TemporalSettings? temporal, }) => AntiAliasSettings( enabled: enabled ?? this.enabled, + method: method ?? this.method, contrastThreshold: contrastThreshold ?? this.contrastThreshold, blend: blend ?? this.blend, sharpen: sharpen ?? this.sharpen, @@ -2359,6 +2367,35 @@ final class AntiAliasSettings { ); } +/// How [AntiAliasSettings] finds and smooths an edge — `P1`. +/// +/// **A class with constants rather than an enum**, because the set grows: +/// SMAA's 2x and 4x modes, or its diagonal search, would each be one more +/// value, and an enum would break every `switch` written against it the day +/// one arrived. +final class EdgeSmoothing { + const EdgeSmoothing._(this.name); + + /// FXAA 3.11 Quality: one pass, which walks along an edge from every pixel + /// with enough local contrast and moves it towards the side the edge leans + /// to. Uses [AntiAliasSettings.contrastThreshold] and + /// [AntiAliasSettings.blend]. + static const EdgeSmoothing fxaa = EdgeSmoothing._('fxaa'); + + /// SMAA 1x: three passes — the edges marked, the line behind each + /// staircase reconstructed from where its run ends and which sides those + /// ends are crossed on, and each pixel blended by the area that line + /// covers of it. Sharper than FXAA on text and fine texture, which it + /// leaves alone, and truer on long shallow edges; a little more work. + /// Orthogonal edges only: no diagonal search and no corner rounding. + static const EdgeSmoothing smaa = EdgeSmoothing._('smaa'); + + final String name; + + @override + String toString() => 'EdgeSmoothing.$name'; +} + /// Edges smoothed across frames — `R1`, `R2`. /// /// **The scene is drawn a fraction of a pixel off each frame**, along a diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer.dart b/packages/flutter3d_core/lib/src/engine/render/renderer.dart index 6b51491a8..f4dc4159e 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer.dart @@ -742,6 +742,7 @@ final class Renderer implements RenderServices { final FragCoordInfoBlock _fragCoordInfo = FragCoordInfoBlock(); final FragInfoBlock _fragInfo = FragInfoBlock(); final FxaaInfoBlock _fxaaInfo = FxaaInfoBlock(); + final SmaaInfoBlock _smaaInfo = SmaaInfoBlock(); final EasuInfoBlock _easuInfo = EasuInfoBlock(); final LocalExposureInfoBlock _localExposureInfo = LocalExposureInfoBlock(); final LocalExposureBlurInfoBlock _localExposureBlurInfo = @@ -1521,7 +1522,7 @@ final class Renderer implements RenderServices { /// `gfx-76n`'s strength, in x. Neutral is zero, which the composite reads as /// a multiplier of exactly one — the same arrangement the occlusion's - /// strength has, and for the same reason: seventy-eight goldens go through this + /// strength has, and for the same reason: seventy-nine goldens go through this /// block and "off" has to be a number the shader cancels, not one it nearly /// cancels. Float32List get _compositeContact => _compositeInfo.contact; diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart index cd905fe41..b4af82f96 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart @@ -2182,12 +2182,46 @@ final class _FxaaNode extends RenderNode { final source = frame.resources.texture(FrameResourceIds.frame); final target = _renderer._ldrColor!; frame.resources.provide(FrameResourceIds.frame, target); - _renderer._encodeFxaa( - target: target, - source: source, - settings: _settings, - upscaleSharpen: upscaleSharpen, - ); + // `P1`. SMAA smooths and nothing else; a sharpening asked for alongside + // it still rides in the FXAA pass, with the smoothing there switched off, + // over SMAA's output. + if (_settings.enabled && + _settings.method == EdgeSmoothing.smaa && + _renderer._hasSmaa) { + final sharpens = + _settings.sharpen > 0.0 || + (_settings.temporal.enabled && _settings.temporal.sharpen > 0.0) || + upscaleSharpen > 0.0; + final smoothed = sharpens + ? frame.resources.transient( + RenderTargetSpec( + width: source.width, + height: source.height, + format: _renderer._frameFormat, + ), + ) + : target; + _renderer._encodeSmaa( + target: smoothed, + source: source, + resources: frame.resources, + ); + if (sharpens) { + _renderer._encodeFxaa( + target: target, + source: smoothed, + settings: _settings.copyWith(enabled: false), + upscaleSharpen: upscaleSharpen, + ); + } + } else { + _renderer._encodeFxaa( + target: target, + source: source, + settings: _settings, + upscaleSharpen: upscaleSharpen, + ); + } developer.Timeline.finishSync(); } } diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart index 57a6d5cf0..bdb575b02 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart @@ -205,7 +205,7 @@ extension _PostPasses on Renderer { _fxaaParams[3] = settings.blend.clamp(0.0, 1.0); // `gfx-29n`. Zero exactly when nobody asked: the shader returns the // centre untouched at zero rather than running a kernel that rounds to - // nothing, and seventy-eight goldens depend on that being the same bytes. + // nothing, and seventy-nine goldens depend on that being the same bytes. // // After a temporal resolve the robust kernel, at the resolve's own // strength — `R2`: what softens a resolved picture is the history, and @@ -234,6 +234,86 @@ extension _PostPasses on Renderer { ); } + /// Whether this device's bundle has SMAA's three stages — `P1`. A bundle + /// built before them falls back to FXAA rather than drawing nothing. + bool get _hasSmaa => + shaders['SmaaEdges'] != null && + shaders['SmaaWeights'] != null && + shaders['SmaaBlend'] != null; + + /// Smooths the edges of [source] into [target] with SMAA 1x — `P1`. + /// + /// Three full-screen draws: the edges into a scratch target, the shares + /// along them into another, and the blend into [target]. The two + /// intermediates are read texel for texel and bound unfiltered; the + /// picture and the area table are filtered, which is how the blend moves + /// a pixel a fraction of a texel and how the table interpolates between + /// the square roots of two distances. + /// + /// The thresholds are SMAA's own presets, not settings: a luma step of a + /// tenth, and a step half the largest beside it. + void _encodeSmaa({ + required TextureHandle target, + required TextureHandle source, + required FrameResources resources, + }) { + TextureHandle scratch() => resources.transient( + RenderTargetSpec( + width: source.width, + height: source.height, + format: TextureFormat.r8g8b8a8UNormInt, + ), + ); + _smaaInfo.params + ..[0] = 1.0 / math.max(source.width, 1) + ..[1] = 1.0 / math.max(source.height, 1) + ..[2] = 0.1 + ..[3] = 2.0; + final uniforms = >{ + _smaaInfo.name: _smaaInfo.members, + }; + final edges = scratch(); + final weights = scratch(); + drawFullscreen( + FullscreenDraw( + target: edges, + fragment: shaders['SmaaEdges']!, + textures: {_kPostSourceSlot: source}, + uniforms: uniforms, + sampler: SamplerOptions.nearestClamp, + ), + ); + drawFullscreen( + FullscreenDraw( + target: weights, + fragment: shaders['SmaaWeights']!, + textures: { + 'edges_texture': edges, + 'area_texture': EngineTables.of(device).smaaArea, + }, + uniforms: uniforms, + sampler: SamplerOptions.nearestClamp, + samplers: const { + 'area_texture': SamplerOptions.linearClamp, + }, + ), + ); + drawFullscreen( + FullscreenDraw( + target: target, + fragment: shaders['SmaaBlend']!, + textures: { + _kPostSourceSlot: source, + 'blend_texture': weights, + }, + uniforms: uniforms, + samplers: const { + 'blend_texture': SamplerOptions.nearestClamp, + }, + ), + ); + } + /// `R7`: the three exposures' weights from [scene] at [target]'s size, /// blurred across and down, the second blur writing the exposure in stops /// into [target]. @@ -1568,7 +1648,7 @@ extension _PostPasses on Renderer { // itself — then one draw per view, scissored to its own rectangle, so the // exposure in the uniform is the one that view metered. With per-view // metering off, or with a single view, this is the one full-frame draw it - // has always been and the bytes are the bytes seventy-eight goldens hold. + // has always been and the bytes are the bytes seventy-nine goldens hold. final perView = settings.autoExposure.enabled && settings.autoExposure.perView && diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart index 95fb4cd80..1261e256c 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart @@ -1063,7 +1063,7 @@ extension _ShadowPasses on Renderer { final skinned = skeleton != null; // `gfx-60n`. A cut-out caster goes through a stage with a sampler in // it; everything else keeps the stage it has always had, which is why - // the masked half costs the common path nothing and why seventy-eight + // the masked half costs the common path nothing and why seventy-nine // goldens recorded against the plain stage cannot move. final masked = maskedShadowShader != shadowShader && _castsMasked(node); final kind = diff --git a/packages/flutter3d_core/lib/src/engine/render/tables/smaa_area.dart b/packages/flutter3d_core/lib/src/engine/render/tables/smaa_area.dart new file mode 100644 index 000000000..33f949dfb --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/render/tables/smaa_area.dart @@ -0,0 +1,483 @@ +// GENERATED by tool/make_tables.dart. Do not edit by hand. +// +// ignore_for_file: lines_longer_than_80_chars + +import 'package:flutter3d_hardware/flutter3d_hardware.dart'; + +import '../engine_table.dart'; + +/// How much of each pixel along an edge the line behind it covers — `P1`, +/// SMAA 1x's orthogonal area table: 80×80 rgba8, red and green used. +/// +/// An edge is a run of pixels between two ends, and at each end a crossing +/// edge may stand on either side of it or on both. Those two ends pick one +/// of 5×5 cells 16 texels square (only the cells at 0, 1, 3 and 4 are ever +/// read, the codes `round(4·e)` a crossing's bilinear fetch gives), and +/// inside a cell the texel at (i, j) is the pixel `i²` from the run's first +/// end and `j²` from its last: the square root, so a short run gets a texel +/// per pixel and a long one shares them. Red is how much of the pixel on the +/// near side of the edge takes the far side's colour; green, the far +/// pixel's share of the near one. +/// +/// The lines are the ones Jimenez, Echevarria, Sousa and Gutierrez +/// reconstruct in "SMAA: Enhanced Subpixel Morphological Antialiasing", +/// Eurographics 2012, for the sixteen patterns of crossing edges, without +/// the subsample offsets of the 2x and 4x modes and without the diagonal +/// half of their table, which this engine does not search. +const EngineTable smaaArea = EngineTable( + name: 'smaaArea', + width: 80, + height: 80, + format: TextureFormat.r8g8b8a8UNormInt, + base64: + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAALAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA' + 'AAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAZgAAAEAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGYAAABAAAAABAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABz' + 'AAAAXQAAAC4AAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAcwAAAF0AAAAuAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAABqAAAASQAAACIAAAABAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAagAAAEkA' + 'AAAiAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAewAAAHEAAABZAAAAOgAAABsAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHsAAABxAAAAWQAAADoAAAAbAAAAAQAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8AAAAdQAAAGQA' + 'AABLAAAAMAAAABcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAfAAAAHUAAABkAAAASwAAADAAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0AAAB4AAAAagAAAFYAAABAAAAAKQAAABMAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9AAAAeAAAAGoAAABWAAAAQAAA' + 'ACkAAAATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAfgAAAHoAAABvAAAAXwAAAEwAAAA3AAAAIwAAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAH4AAAB6AAAAbwAAAF8AAABMAAAANwAAACMAAAARAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAewAAAHIAAABlAAAAVQAA' + 'AEMAAAAxAAAAHwAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAHsA' + 'AAByAAAAZQAAAFUAAABDAAAAMQAAAB8AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAH4AAAB8AAAAdQAAAGkAAABcAAAATAAAADwAAAArAAAAHAAAAA0AAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAfAAAAHUAAABpAAAAXAAAAEwAAAA8AAAA' + 'KwAAABwAAAANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAHwA' + 'AAB2AAAAbQAAAGEAAABTAAAARQAAADYAAAAnAAAAGQAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAH4AAAB8AAAAdgAAAG0AAABhAAAAUwAAAEUAAAA2AAAAJwAAABkAAAAMAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAfQAAAHgAAABwAAAAZQAAAFkAAABMAAAA' + 'PgAAADEAAAAkAAAAFwAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAH0AAAB4AAAAcAAA' + 'AGUAAABZAAAATAAAAD4AAAAxAAAAJAAAABcAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAH8AAAB9AAAAeQAAAHIAAABpAAAAXgAAAFIAAABGAAAAOQAAAC0AAAAhAAAAFQAAAAoA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAfQAAAHkAAAByAAAAaQAAAF4AAABSAAAARgAAADkAAAAt' + 'AAAAIQAAABUAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAH4AAAB6AAAAdAAA' + 'AGwAAABiAAAAWAAAAEwAAABAAAAANQAAACkAAAAeAAAAEwAAAAkAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8A' + 'AAB+AAAAegAAAHQAAABsAAAAYgAAAFgAAABMAAAAQAAAADUAAAApAAAAHgAAABMAAAAJAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAfgAAAHsAAAB1AAAAbgAAAGYAAABcAAAAUgAAAEcAAAA8' + 'AAAAMQAAACYAAAAcAAAAEgAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAH4AAAB7AAAAdQAAAG4AAABmAAAA' + 'XAAAAFIAAABHAAAAPAAAADEAAAAmAAAAHAAAABIAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAEAAAABm' + 'AAAAcwAAAHgAAAB7AAAAfAAAAH0AAAB+AAAAfgAAAH4AAAB+AAAAfwAAAH8AAAB/AAAAfwAAAH4A' + 'AABZAAAAcAAAAHcAAAB6AAAAewAAAHwAAAB9AAAAfgAAAH4AAAB+AAAAfgAAAH8AAAB/AAAAfwAA' + 'AH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAgIAAAAEAAAABmAAAAcwAAAHgAAAB7AAAAfAAAAH0AAAB+AAAAfgAAAH4AAAB+' + 'AAAAfwAAAH8AAAB/AAAAfwAAICAAAABAAAAAZgAAAHMAAAB4AAAAewAAAHwAAAB9AAAAfgAAAH4A' + 'AAB+AAAAfgAAAH8AAAB/AAAAfwAAAH8AAAAAAAAACwAAAEAAAABdAAAAagAAAHEAAAB1AAAAeAAA' + 'AHoAAAB7AAAAfAAAAHwAAAB9AAAAfQAAAH4AAAB+AAAAWQAAAEUAAABVAAAAZwAAAG8AAAByAAAA' + 'dQAAAHgAAAB6AAAAewAAAHwAAAB8AAAAfQAAAH0AAAB+AAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAALCwAAAEAA' + 'AABdAAAAagAAAHEAAAB1AAAAeAAAAHoAAAB7AAAAfAAAAHwAAAB9AAAAfQAAAH4AAAB+AABAAAAA' + 'CwsAAABAAAAAXQAAAGoAAABxAAAAdQAAAHgAAAB6AAAAewAAAHwAAAB8AAAAfQAAAH0AAAB+AAAA' + 'fgAAAAAAAAAAAAAABAAAAC4AAABJAAAAWQAAAGQAAABqAAAAbwAAAHIAAAB1AAAAdgAAAHgAAAB5' + 'AAAAegAAAHsAAABwAAAAVQAAACEAAAA/AAAAUQAAAFoAAABkAAAAagAAAG8AAAByAAAAdQAAAHYA' + 'AAB4AAAAeQAAAHoAAAB7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZgAAAEAAAAAEBAAAAC4AAABJAAAAWQAAAGQAAABqAAAA' + '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' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAEAAAABmAAAAcwAAAHgAAAB7AAAAfAAAAH0A' + 'AAB+AAAAfgAAAH4AAAB+AAAAfwAAAH8AAAB/AAAAfwAAACAgAABAAAAAZgAAAHMAAAB4AAAAewAA' + 'AHwAAAB9AAAAfgAAAH4AAAB+AAAAfgAAAH8AAAB/AAAAfwAAAH8AAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAFkAAABw' + 'AAAAdwAAAHoAAAB7AAAAfAAAAH0AAAB+AAAAfgAAAH4AAAB+AAAAfwAAAH8AAAB/AAAAfwAAACAg' + 'AABAAAAAZgAAAHMAAAB4AAAAewAAAHwAAAB9AAAAfgAAAH4AAAB+AAAAfgAAAH8AAAB/AAAAfwAA' + 'AH8AAAAAAAAACwAAAEAAAABdAAAAagAAAHEAAAB1AAAAeAAAAHoAAAB7AAAAfAAAAHwAAAB9AAAA' + 'fQAAAH4AAAB+AAAAAEAAAAsLAABAAAAAXQAAAGoAAABxAAAAdQAAAHgAAAB6AAAAewAAAHwAAAB8' + 'AAAAfQAAAH0AAAB+AAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZAAAARQAAAFUAAABnAAAAbwAAAHIAAAB1AAAAeAAA' + 'AHoAAAB7AAAAfAAAAHwAAAB9AAAAfQAAAH4AAAB+AAAAAEAAAAsLAABAAAAAXQAAAGoAAABxAAAA' + 'dQAAAHgAAAB6AAAAewAAAHwAAAB8AAAAfQAAAH0AAAB+AAAAfgAAAAAAAAAAAAAABAAAAC4AAABJ' + 'AAAAWQAAAGQAAABqAAAAbwAAAHIAAAB1AAAAdgAAAHgAAAB5AAAAegAAAHsAAAAAZgAAAEAAAAQE' + 'AAAuAAAASQAAAFkAAABkAAAAagAAAG8AAAByAAAAdQAAAHYAAAB4AAAAeQAAAHoAAAB7AAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAHAAAABVAAAAIQAAAD8AAABRAAAAWgAAAGQAAABqAAAAbwAAAHIAAAB1AAAAdgAAAHgAAAB5' + 'AAAAegAAAHsAAAAAZgAAAEAAAAQEAAAuAAAASQAAAFkAAABkAAAAagAAAG8AAAByAAAAdQAAAHYA' + 'AAB4AAAAeQAAAHoAAAB7AAAAAAAAAAAAAAAAAAAAAgAAACIAAAA6AAAASwAAAFYAAABfAAAAZQAA' + 'AGkAAABtAAAAcAAAAHIAAAB0AAAAdQAAAABzAAAAXQAAAC4AAAICAAAiAAAAOgAAAEsAAABWAAAA' + 'XwAAAGUAAABpAAAAbQAAAHAAAAByAAAAdAAAAHUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAGcAAAA/AAAADgAAACgA' + 'AAA6AAAASwAAAFYAAABfAAAAZQAAAGkAAABtAAAAcAAAAHIAAAB0AAAAdQAAAABzAAAAXQAAAC4A' + 'AAICAAAiAAAAOgAAAEsAAABWAAAAXwAAAGUAAABpAAAAbQAAAHAAAAByAAAAdAAAAHUAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAQAAABsAAAAwAAAAQAAAAEwAAABVAAAAXAAAAGEAAABlAAAAaQAAAGwAAABu' + 'AAAAAHgAAABqAAAASQAAACIAAAEBAAAbAAAAMAAAAEAAAABMAAAAVQAAAFwAAABhAAAAZQAAAGkA' + 'AABsAAAAbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAB6AAAAbwAAAFEAAAAoAAAAAgAAABsAAAAwAAAAQAAAAEwAAABVAAAA' + 'XAAAAGEAAABlAAAAaQAAAGwAAABuAAAAAHgAAABqAAAASQAAACIAAAEBAAAbAAAAMAAAAEAAAABM' + 'AAAAVQAAAFwAAABhAAAAZQAAAGkAAABsAAAAbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABcA' + 'AAApAAAANwAAAEMAAABMAAAAUwAAAFkAAABeAAAAYgAAAGYAAAAAewAAAHEAAABZAAAAOgAAABsA' + 'AAEBAAAXAAAAKQAAADcAAABDAAAATAAAAFMAAABZAAAAXgAAAGIAAABmAAAAAAAAAAAAAAAAAAAA' + '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' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICAAAEAAAABmAAAAcwAAAHgAAAB7AAAAfAAAAH0AAAB+' + 'AAAAfgAAAH4AAAB+AAAAfwAAAH8AAAB/AAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIAAAAEAAAABmAAAAcwAAAHgA' + 'AAB7AAAAfAAAAH0AAAB+AAAAfgAAAH4AAAB+AAAAfwAAAH8AAAB/AAAAfwAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAQAAACwsAAEAAAABdAAAAagAAAHEAAAB1AAAAeAAAAHoAAAB7AAAAfAAAAHwAAAB9AAAAfQAA' + 'AH4AAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAEAAAAALCwAAAEAAAABdAAAAagAAAHEAAAB1AAAAeAAAAHoAAAB7AAAA' + 'fAAAAHwAAAB9AAAAfQAAAH4AAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAAAQAAABAQAAC4AAABJAAAA' + 'WQAAAGQAAABqAAAAbwAAAHIAAAB1AAAAdgAAAHgAAAB5AAAAegAAAHsAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZgAAAEAA' + 'AAAEBAAAAC4AAABJAAAAWQAAAGQAAABqAAAAbwAAAHIAAAB1AAAAdgAAAHgAAAB5AAAAegAAAHsA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAHMAAABdAAAALgAAAgIAACIAAAA6AAAASwAAAFYAAABfAAAAZQAAAGkA' + 'AABtAAAAcAAAAHIAAAB0AAAAdQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzAAAAXQAAAC4AAAACAgAAACIAAAA6AAAASwAA' + 'AFYAAABfAAAAZQAAAGkAAABtAAAAcAAAAHIAAAB0AAAAdQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAGoA' + 'AABJAAAAIgAAAQEAABsAAAAwAAAAQAAAAEwAAABVAAAAXAAAAGEAAABlAAAAaQAAAGwAAABuAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAHgAAABqAAAASQAAACIAAAABAQAAABsAAAAwAAAAQAAAAEwAAABVAAAAXAAAAGEAAABl' + 'AAAAaQAAAGwAAABuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB7AAAAcQAAAFkAAAA6AAAAGwAAAQEAABcAAAAp' + 'AAAANwAAAEMAAABMAAAAUwAAAFkAAABeAAAAYgAAAGYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAewAAAHEAAABZAAAAOgAA' + 'ABsAAAABAQAAABcAAAApAAAANwAAAEMAAABMAAAAUwAAAFkAAABeAAAAYgAAAGYAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAHwAAAB1AAAAZAAAAEsAAAAwAAAAFwAAAAAAABMAAAAjAAAAMQAAADwAAABFAAAATAAA' + 'AFIAAABYAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAB8AAAAdQAAAGQAAABLAAAAMAAAABcAAAAAAAAAABMAAAAjAAAA' + 'MQAAADwAAABFAAAATAAAAFIAAABYAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQAAAHgAAABqAAAAVgAA' + 'AEAAAAApAAAAEwAAAAAAABEAAAAfAAAAKwAAADYAAAA+AAAARgAAAEwAAABSAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0A' + 'AAB4AAAAagAAAFYAAABAAAAAKQAAABMAAAAAAAAAABEAAAAfAAAAKwAAADYAAAA+AAAARgAAAEwA' + 'AABSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAegAAAG8AAABfAAAATAAAADcAAAAjAAAAEQAAAAAAAA8A' + 'AAAcAAAAJwAAADEAAAA5AAAAQAAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAHoAAABvAAAAXwAAAEwAAAA3AAAA' + 'IwAAABEAAAAAAAAAAA8AAAAcAAAAJwAAADEAAAA5AAAAQAAAAEcAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4A' + 'AAB7AAAAcgAAAGUAAABVAAAAQwAAADEAAAAfAAAADwAAAAAAAA0AAAAZAAAAJAAAAC0AAAA1AAAA' + 'PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAB+AAAAewAAAHIAAABlAAAAVQAAAEMAAAAxAAAAHwAAAA8AAAAAAAAAAA0AAAAZ' + 'AAAAJAAAAC0AAAA1AAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAHwAAAB1AAAAaQAAAFwAAABMAAAA' + 'PAAAACsAAAAcAAAADQAAAAAAAAwAAAAXAAAAIQAAACkAAAAxAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAB8AAAAdQAA' + 'AGkAAABcAAAATAAAADwAAAArAAAAHAAAAA0AAAAAAAAAAAwAAAAXAAAAIQAAACkAAAAxAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAB+AAAAfAAAAHYAAABtAAAAYQAAAFMAAABFAAAANgAAACcAAAAZAAAADAAAAAAA' + 'AAsAAAAVAAAAHgAAACYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAHwAAAB2AAAAbQAAAGEAAABTAAAARQAAADYAAAAn' + 'AAAAGQAAAAwAAAAAAAAAAAsAAAAVAAAAHgAAACYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAB9AAAAeAAA' + 'AHAAAABlAAAAWQAAAEwAAAA+AAAAMQAAACQAAAAXAAAACwAAAAAAAAoAAAATAAAAHAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAB/AAAAfQAAAHgAAABwAAAAZQAAAFkAAABMAAAAPgAAADEAAAAkAAAAFwAAAAsAAAAAAAAAAAoA' + 'AAATAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAH0AAAB5AAAAcgAAAGkAAABeAAAAUgAAAEYAAAA5' + 'AAAALQAAACEAAAAVAAAACgAAAAAAAAkAAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAB9AAAAeQAAAHIAAABpAAAA' + 'XgAAAFIAAABGAAAAOQAAAC0AAAAhAAAAFQAAAAoAAAAAAAAAAAkAAAASAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAB/AAAAfgAAAHoAAAB0AAAAbAAAAGIAAABYAAAATAAAAEAAAAA1AAAAKQAAAB4AAAATAAAACQAA' + 'AAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAfwAAAH4AAAB6AAAAdAAAAGwAAABiAAAAWAAAAEwAAABAAAAANQAAACkA' + 'AAAeAAAAEwAAAAkAAAAAAAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAB+AAAAewAAAHUAAABuAAAA' + 'ZgAAAFwAAABSAAAARwAAADwAAAAxAAAAJgAAABwAAAASAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAfgAA' + 'AHsAAAB1AAAAbgAAAGYAAABcAAAAUgAAAEcAAAA8AAAAMQAAACYAAAAcAAAAEgAAAAkAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAA==', +); diff --git a/packages/flutter3d_core/test/engine/engine_tables_test.dart b/packages/flutter3d_core/test/engine/engine_tables_test.dart index 3a2fa30f6..8235fbfb8 100644 --- a/packages/flutter3d_core/test/engine/engine_tables_test.dart +++ b/packages/flutter3d_core/test/engine/engine_tables_test.dart @@ -15,7 +15,8 @@ import 'package:flutter3d_hardware/flutter3d_hardware.dart' show TextureFormat; import 'package:flutter3d_hardware/testing.dart'; import 'package:test/test.dart'; -import '../../tool/make_tables.dart' show aces2DisplayTable, ltcTable, toHalf; +import '../../tool/make_tables.dart' + show aces2DisplayTable, ltcTable, smaaAreaTable, toHalf; int _fnv1a(Uint8List bytes) => bytes.fold( 0x811c9dc5, @@ -35,6 +36,7 @@ void main() { for (final table in EngineTables.all) { final texel = switch (table.format) { TextureFormat.r8UNormInt => 1, + TextureFormat.r8g8b8a8UNormInt => 4, TextureFormat.r16g16b16a16Float => 8, _ => throw StateError('no size known for ${table.format}'), }; @@ -59,9 +61,36 @@ void main() { // `M2` wrote the sheen's albedo into the second table's z lane, // which the published fit leaves empty and nothing read before. 'ltc': 1806585161, + // `P1`: SMAA 1x's orthogonal areas, without the diagonal half. + 'smaaArea': 2858171383, }); }); + test('the SMAA area table moves nothing on a run crossed nowhere, and ' + 'one side at a time elsewhere — P1', () { + // Mutation: swap red and green in `smaaAreaTable`, and the step below + // gives its share to the far pixel: the second expectation fails. + final bytes = EngineTables.all + .firstWhere((table) => table.name == 'smaaArea') + .bytes; + int red(int x, int y) => bytes[(y * 80 + x) * 4]; + int green(int x, int y) => bytes[(y * 80 + x) * 4 + 1]; + // Codes (0, 0): a straight edge, whatever its length. + for (var j = 0; j < 16; j++) { + for (var i = 0; i < 16; i++) { + expect(red(i, j) + green(i, j), 0); + } + } + // Codes (3, 0): a step on the near side at the first end. One pixel in + // from it, on a run of ten, the near pixel takes the far colour and the + // far one keeps its own. + expect(red(3 * 16 + 1, 3), greaterThan(0)); + expect(green(3 * 16 + 1, 3), 0); + // Codes (1, 0): the same step on the far side moves the far pixel. + expect(red(16 + 1, 3), 0); + expect(green(16 + 1, 3), greaterThan(0)); + }); + test('the LTC table carries the sheen albedo in its spare lane', () { // Row `64 + j` of the second table is sqrt(1 − n·v) = j / 63 and column // `i` is roughness i / 63; z is the half at byte 4 of each texel. @@ -149,6 +178,7 @@ void main() { EngineTables.all.firstWhere((table) => table.name == name).bytes; expect(aces2DisplayTable(), shipped('aces2Display')); expect(ltcTable(), shipped('ltc')); + expect(smaaAreaTable(), shipped('smaaArea')); }); }); diff --git a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart index b63f5dd0a..e47ee9648 100644 --- a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart +++ b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart @@ -157,7 +157,7 @@ void main() { }); test('an uncompressed file is untouched by any of this', () { - // The path seventy-eight goldens and every existing asset take. A level with + // The path seventy-nine goldens and every existing asset take. A level with // no supercompression is handed back as a view on the file's own bytes, // with nothing copied and nothing decoded. final file = buildKtx2( diff --git a/packages/flutter3d_core/tool/make_tables.dart b/packages/flutter3d_core/tool/make_tables.dart index fde8e8c26..261de511d 100644 --- a/packages/flutter3d_core/tool/make_tables.dart +++ b/packages/flutter3d_core/tool/make_tables.dart @@ -86,6 +86,136 @@ void main() { format: 'r16g16b16a16Float', bytes: ltcTable(), ); + + _write( + 'smaa_area.dart', + name: 'smaaArea', + doc: ''' +/// How much of each pixel along an edge the line behind it covers — `P1`, +/// SMAA 1x's orthogonal area table: 80×80 rgba8, red and green used. +/// +/// An edge is a run of pixels between two ends, and at each end a crossing +/// edge may stand on either side of it or on both. Those two ends pick one +/// of 5×5 cells 16 texels square (only the cells at 0, 1, 3 and 4 are ever +/// read, the codes `round(4·e)` a crossing's bilinear fetch gives), and +/// inside a cell the texel at (i, j) is the pixel `i²` from the run's first +/// end and `j²` from its last: the square root, so a short run gets a texel +/// per pixel and a long one shares them. Red is how much of the pixel on the +/// near side of the edge takes the far side's colour; green, the far +/// pixel's share of the near one. +/// +/// The lines are the ones Jimenez, Echevarria, Sousa and Gutierrez +/// reconstruct in "SMAA: Enhanced Subpixel Morphological Antialiasing", +/// Eurographics 2012, for the sixteen patterns of crossing edges, without +/// the subsample offsets of the 2x and 4x modes and without the diagonal +/// half of their table, which this engine does not search.''', + width: 80, + height: 80, + format: 'r8g8b8a8UNormInt', + bytes: smaaAreaTable(), + ); +} + +/// SMAA's orthogonal area table — `P1`. See the doc written above it. +Uint8List smaaAreaTable() { + const cell = 16; + const size = cell * 5; + // The crossing codes of the two ends, per pattern. A pattern's bits: + // 1 a crossing below the first end, 2 below the last, 4 above the first, + // 8 above the last; below is 3 and above is 1 in a code, both 4. + const codes = <(int, int)>[ + (0, 0), (3, 0), (0, 3), (3, 3), (1, 0), (4, 0), (1, 3), (4, 3), // + (0, 1), (3, 1), (0, 4), (3, 4), (1, 1), (4, 1), (1, 4), (4, 4), + ]; + final table = Uint8List(size * size * 4); + for (var pattern = 0; pattern < 16; pattern++) { + final (first, last) = codes[pattern]; + for (var j = 0; j < cell; j++) { + for (var i = 0; i < cell; i++) { + final (near, far) = smaaOrthogonalArea( + pattern, + (i * i).toDouble(), + (j * j).toDouble(), + ); + final at = ((last * cell + j) * size + first * cell + i) * 4; + table[at] = (near.clamp(0.0, 1.0) * 255.0).round(); + table[at + 1] = (far.clamp(0.0, 1.0) * 255.0).round(); + } + } + } + return table; +} + +/// The area a pixel [left] pixels from a run's first end and [right] from +/// its last keeps of the line [pattern] reconstructs, as (near, far): the +/// share of the pixel on the near side of the edge that the far colour +/// takes, and the far pixel's share of the near colour. One of the two is +/// always nought except where the line crosses the edge inside the pixel. +(double, double) smaaOrthogonalArea(int pattern, double left, double right) { + final d = left + right + 1.0; + // Half a pixel above the edge and half a pixel below it: where a run's + // line starts at an end with a crossing on that side. + const above = 0.5; + const below = -0.5; + (double, double) at((double, double) a, (double, double) b) => + _smaaArea(a, b, left); + (double, double) smooth((double, double) a, (double, double) b) { + // A U turns its corners less sharply the longer it is: the square root + // of the area for a short run, the area itself from 32 pixels on. + final p = (d / 32.0).clamp(0.0, 1.0); + double soften(double v) { + final rounded = math.sqrt(v * 2.0) * 0.5; + return rounded + (v - rounded) * p; + } + + return (soften(a.$1) + soften(b.$1), soften(a.$2) + soften(b.$2)); + } + + final middle = (d / 2.0, 0.0); + return switch (pattern) { + 1 => left <= right ? at((0.0, below), middle) : (0.0, 0.0), + 2 => left >= right ? at(middle, (d, below)) : (0.0, 0.0), + 3 => smooth(at((0.0, below), middle), at(middle, (d, below))), + 4 => left <= right ? at((0.0, above), middle) : (0.0, 0.0), + 6 => at((0.0, above), (d, below)), + 7 => at((0.0, above), (d, below)), + 8 => left >= right ? at(middle, (d, above)) : (0.0, 0.0), + 9 => at((0.0, below), (d, above)), + 11 => at((0.0, below), (d, above)), + 12 => smooth(at((0.0, above), middle), at(middle, (d, above))), + 13 => at((0.0, below), (d, above)), + 14 => at((0.0, above), (d, below)), + // Nothing to smooth: no crossing at all (0), a crossing on both sides of + // one end (5, 10), or of both (15). + _ => (0.0, 0.0), + }; +} + +/// The area the line from [p1] to [p2] keeps of the pixel from [x] to +/// `x + 1` along the edge, signed by side, as SMAA's reference generator +/// computes it. +(double, double) _smaaArea((double, double) p1, (double, double) p2, double x) { + final dx = p2.$1 - p1.$1; + final dy = p2.$2 - p1.$2; + final x1 = x; + final x2 = x + 1.0; + final y1 = p1.$2 + dy * (x1 - p1.$1) / dx; + final y2 = p1.$2 + dy * (x2 - p1.$1) / dx; + final inside = (x1 >= p1.$1 && x1 < p2.$1) || (x2 > p1.$1 && x2 <= p2.$1); + if (!inside) return (0.0, 0.0); + final trapezoid = y1.sign == y2.sign || y1.abs() < 1e-4 || y2.abs() < 1e-4; + if (trapezoid) { + final a = (y1 + y2) / 2.0; + return a < 0.0 ? (a.abs(), 0.0) : (0.0, a.abs()); + } + // The line crosses the edge inside the pixel: two triangles, one on each + // side. + final crossing = -p1.$2 * dx / dy + p1.$1; + final fraction = crossing - crossing.truncateToDouble(); + final a1 = crossing > p1.$1 ? y1 * fraction / 2.0 : 0.0; + final a2 = crossing < p2.$1 ? y2 * (1.0 - fraction) / 2.0 : 0.0; + final a = a1.abs() > a2.abs() ? a1 : -a2; + return a < 0.0 ? (a1.abs(), a2.abs()) : (a2.abs(), a1.abs()); } /// The two published LTC tables, headers dropped, one under the other. diff --git a/packages/flutter3d_cpu/CHANGELOG.md b/packages/flutter3d_cpu/CHANGELOG.md index 6715f67a3..d77fb127b 100644 --- a/packages/flutter3d_cpu/CHANGELOG.md +++ b/packages/flutter3d_cpu/CHANGELOG.md @@ -1,3 +1,11 @@ +## Unreleased + +- **SMAA 1x in software.** `SmaaEdgesShader`, `SmaaWeightsShader` and + `SmaaBlendShader` mirror `smaa_edges.frag`, `smaa_weights.frag` and + `smaa_blend.frag` line for line, so the software backend smooths the same + edges by the same amounts; its `smaa-teapot` reference matches Impeller's + to the pixel. + ## 0.8.2+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart index df140fd70..02ca95c5d 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart @@ -65,6 +65,7 @@ import 'cpu_shaders_reactive.dart'; import 'cpu_shaders_reflections.dart'; import 'cpu_shaders_shadow_passes.dart'; import 'cpu_shaders_sky.dart'; +import 'cpu_shaders_smaa.dart'; import 'cpu_shaders_ssao.dart'; import 'cpu_shaders_temporal.dart'; import 'cpu_shaders_velocity.dart'; @@ -90,6 +91,7 @@ export 'cpu_shaders_shadow_directional.dart'; export 'cpu_shaders_shadow_passes.dart'; export 'cpu_shaders_shadow_point.dart'; export 'cpu_shaders_sky.dart'; +export 'cpu_shaders_smaa.dart'; export 'cpu_shaders_ssao.dart'; export 'cpu_shaders_surface.dart'; export 'cpu_shaders_temporal.dart'; @@ -233,6 +235,9 @@ Map builtinCpuShaders() { 'Composite': const CpuStage.fragment(CompositeShader()), 'Easu': const CpuStage.fragment(EasuShader()), 'Fxaa': const CpuStage.fragment(FxaaShader()), + 'SmaaEdges': const CpuStage.fragment(SmaaEdgesShader()), + 'SmaaWeights': const CpuStage.fragment(SmaaWeightsShader()), + 'SmaaBlend': const CpuStage.fragment(SmaaBlendShader()), 'LocalExposure': const CpuStage.fragment(LocalExposureShader()), 'LocalExposureBlur': const CpuStage.fragment(LocalExposureBlurShader()), 'ShadowDepth': const CpuStage.fragment(ShadowDepthShader()), diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart index 5b2aeda28..977de6f7a 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart @@ -142,7 +142,7 @@ final class CompositeShader implements CpuFragmentShader { // Skipped at exactly one, which is what both settings off comes to: a // multiply by one is exact, so this is a shortcut rather than a difference, - // and it keeps the frames seventy-eight goldens hold untouched by arithmetic + // and it keeps the frames seventy-nine goldens hold untouched by arithmetic // they never used to go through. if (shade != 1.0) colour.scale(shade); diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_smaa.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_smaa.dart new file mode 100644 index 000000000..33197a8eb --- /dev/null +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_smaa.dart @@ -0,0 +1,210 @@ +/// `smaa_edges.frag`, `smaa_weights.frag` and `smaa_blend.frag`: SMAA 1x — +/// `P1` — line for line, so the software backend smooths the same edges by +/// the same amounts as the GPUs. +/// +/// Three passes over the finished picture. The first marks where the luma +/// steps: red for a step across a pixel's left side, green across its top. +/// The second walks along each marked edge to both of its ends, reads which +/// sides of each end a crossing edge stands on, and looks up in the engine's +/// area table how much of this pixel the line behind the staircase covers. +/// The third moves each pixel that far towards its neighbour across the +/// edge. +/// +/// Orthogonal edges only. The diagonal search and the corner rounding of +/// the paper are not here, on either side of the backend boundary. +library; + +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:vector_math/vector_math.dart'; + +import 'cpu_shader.dart'; + +/// `kSmaaMaxSearch` in the GLSL: the most pixels a search walks from the one +/// it starts at, either way. +const int kSmaaMaxSearch = 32; + +/// `Luma` in `smaa_edges.frag`: Rec. 709 weights on the encoded picture. +double _luma(Vector4 c) => 0.2126 * c.x + 0.7152 * c.y + 0.0722 * c.z; + +/// GLSL's `step(edge, x)`. +double _step(double edge, double x) => x < edge ? 0.0 : 1.0; + +/// `smaa_edges.frag`. +final class SmaaEdgesShader implements CpuFragmentShader { + const SmaaEdgesShader(); + + @override + Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) { + final source = b.textures['source_texture']; + if (source == null) return Vector4.zero(); + final params = b.vec4('SmaaInfo', 'params', Vector4(0.0, 0.0, 0.1, 2.0)); + double at(double dx, double dy) => + _luma(source.sample(v[0] + dx * params.x, v[1] + dy * params.y)); + + final middle = at(0.0, 0.0); + final left = at(-1.0, 0.0); + final top = at(0.0, -1.0); + final deltaLeft = (middle - left).abs(); + final deltaTop = (middle - top).abs(); + var edgeLeft = _step(params.z, deltaLeft); + var edgeTop = _step(params.z, deltaTop); + if (edgeLeft + edgeTop == 0.0) return Vector4.zero(); + + // Local contrast adaptation: a step is not an edge when a much larger + // one sits right beside it, which is what keeps the inside of a + // high-contrast texture from being smoothed as a staircase. + final right = (middle - at(1.0, 0.0)).abs(); + final bottom = (middle - at(0.0, 1.0)).abs(); + final leftLeft = (left - at(-2.0, 0.0)).abs(); + final topTop = (top - at(0.0, -2.0)).abs(); + final largest = math.max( + math.max(math.max(deltaLeft, deltaTop), math.max(right, bottom)), + math.max(leftLeft, topTop), + ); + edgeLeft *= _step(largest, params.w * deltaLeft); + edgeTop *= _step(largest, params.w * deltaTop); + return Vector4(edgeLeft, edgeTop, 0.0, 0.0); + } +} + +/// `smaa_weights.frag`. +final class SmaaWeightsShader implements CpuFragmentShader { + const SmaaWeightsShader(); + + @override + Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) { + final edges = b.textures['edges_texture']; + final area = b.textures['area_texture']; + if (edges == null || area == null) return Vector4.zero(); + final params = b.vec4('SmaaInfo', 'params', Vector4.zero()); + // Red: an edge across the left side. Green: across the top. + Vector4 e(int dx, int dy) => + edges.sample(v[0] + dx * params.x, v[1] + dy * params.y); + + final here = e(0, 0); + final weights = Vector4.zero(); + + if (here.y > 0.5) { + // Along a top edge, to the left: an end where this pixel's own left + // side is crossed, or where the next one has no top edge. + var first = kSmaaMaxSearch; + for (var i = 0; i < kSmaaMaxSearch; i++) { + if (e(-i, 0).x > 0.5 || e(-i - 1, 0).y < 0.5) { + first = i; + break; + } + } + var last = kSmaaMaxSearch; + for (var i = 0; i < kSmaaMaxSearch; i++) { + final next = e(i + 1, 0); + if (next.x > 0.5 || next.y < 0.5) { + last = i; + break; + } + } + // A crossing on the near side of the edge counts three, on the far + // side one: the codes the bilinear fetch of the reference gives. + final codeFirst = first == kSmaaMaxSearch + ? 0.0 + : 3.0 * e(-first, 0).x + e(-first, -1).x; + final codeLast = last == kSmaaMaxSearch + ? 0.0 + : 3.0 * e(last + 1, 0).x + e(last + 1, -1).x; + final share = _area(area, first, last, codeFirst, codeLast); + weights + ..x = share.x + ..y = share.y; + } + + if (here.x > 0.5) { + // Along a left edge, upwards and then down. + var first = kSmaaMaxSearch; + for (var i = 0; i < kSmaaMaxSearch; i++) { + if (e(0, -i).y > 0.5 || e(0, -i - 1).x < 0.5) { + first = i; + break; + } + } + var last = kSmaaMaxSearch; + for (var i = 0; i < kSmaaMaxSearch; i++) { + final next = e(0, i + 1); + if (next.y > 0.5 || next.x < 0.5) { + last = i; + break; + } + } + final codeFirst = first == kSmaaMaxSearch + ? 0.0 + : 3.0 * e(0, -first).y + e(-1, -first).y; + final codeLast = last == kSmaaMaxSearch + ? 0.0 + : 3.0 * e(0, last + 1).y + e(-1, last + 1).y; + final share = _area(area, first, last, codeFirst, codeLast); + weights + ..z = share.x + ..w = share.y; + } + return weights; + } + + /// `Area` in the GLSL: the table's texel for the two ends' codes and the + /// square roots of the two distances, filtered between square roots. + static Vector2 _area( + BoundTexture area, + int first, + int last, + double codeFirst, + double codeLast, + ) { + const cell = 16.0; + const size = 80.0; + final u = (cell * codeFirst + math.sqrt(first) + 0.5) / size; + final w = (cell * codeLast + math.sqrt(last) + 0.5) / size; + final texel = area.sample(u, w); + return Vector2(texel.x, texel.y); + } +} + +/// `smaa_blend.frag`. +final class SmaaBlendShader implements CpuFragmentShader { + const SmaaBlendShader(); + + @override + Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) { + final source = b.textures['source_texture']; + final blend = b.textures['blend_texture']; + if (source == null) return Vector4(0.0, 0.0, 0.0, 1.0); + if (blend == null) return source.sample(v[0], v[1]); + final params = b.vec4('SmaaInfo', 'params', Vector4.zero()); + + // What the four sides of this pixel ask of it: right and bottom are the + // neighbours' far shares, top and left this pixel's own near ones. + final right = blend.sample(v[0] + params.x, v[1]).w; + final bottom = blend.sample(v[0], v[1] + params.y).y; + final mine = blend.sample(v[0], v[1]); + final top = mine.x; + final left = mine.z; + if (right + bottom + top + left < 1e-5) return source.sample(v[0], v[1]); + + final across = math.max(right, left) > math.max(bottom, top); + final double toward; + final double away; + final Vector4 first; + final Vector4 second; + if (across) { + toward = right; + away = left; + first = source.sample(v[0] + right * params.x, v[1]); + second = source.sample(v[0] - left * params.x, v[1]); + } else { + toward = bottom; + away = top; + first = source.sample(v[0], v[1] + bottom * params.y); + second = source.sample(v[0], v[1] - top * params.y); + } + final total = toward + away; + return first * (toward / total) + second * (away / total); + } +} diff --git a/packages/flutter3d_cpu/lib/src/cpu_texture.dart b/packages/flutter3d_cpu/lib/src/cpu_texture.dart index 2251fa559..d56d0c7bd 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_texture.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_texture.dart @@ -300,7 +300,7 @@ final class BoundTexture { // from the short one. // // **A sampler that did not ask is untouched**, which is what makes this - // safe to add to a backend seventy-eight golden scenes are recorded on: with + // safe to add to a backend seventy-nine golden scenes are recorded on: with // `anisotropy` at one — the default everywhere in this engine — the // arithmetic below is not reached and the bytes are the ones that were // recorded. `anisotropic-floor` is the one scene that asks. diff --git a/packages/flutter3d_cpu/lib/src/portable_log2.dart b/packages/flutter3d_cpu/lib/src/portable_log2.dart index 932f7365b..712fada34 100644 --- a/packages/flutter3d_cpu/lib/src/portable_log2.dart +++ b/packages/flutter3d_cpu/lib/src/portable_log2.dart @@ -10,7 +10,7 @@ /// twenty-five five. `anisotropic-floor` is the scene built to sit exactly /// there: 2059 of its 172800 pixels differed between a reference recorded on /// macOS and the same code run on a Linux runner, and nothing else in -/// seventy-eight scenes differed at all. +/// seventy-nine scenes differed at all. /// /// **The same rule the simulation already keeps.** `flutter3d_sim`'s /// `Portable` exists for this, and a step in that package is forbidden from diff --git a/packages/flutter3d_cpu/test/cross_backend_test.dart b/packages/flutter3d_cpu/test/cross_backend_test.dart index f59819d81..5dc6351e5 100644 --- a/packages/flutter3d_cpu/test/cross_backend_test.dart +++ b/packages/flutter3d_cpu/test/cross_backend_test.dart @@ -250,6 +250,10 @@ const Map _budgets = { // 1.501% measured: the upscale sharpens the rasteriser's edges and // Impeller's multisampled ones differently. 'easu-half': 1.66, + // 0.000% measured, where `shadow-teapot` without it sits at half a + // percent: the two rasterisers' staircases differ, and SMAA rebuilds the + // same line behind both. + 'smaa-teapot': 0.05, 'evsm-soft': 0.5, // 0.069% measured: the air right round a torch, where the light falls off // as one over the distance squared, is shadowed by one unfiltered tap of diff --git a/packages/flutter3d_cpu/test/goldens/smaa-teapot.png b/packages/flutter3d_cpu/test/goldens/smaa-teapot.png new file mode 100644 index 0000000000000000000000000000000000000000..dbf463c5791987941a9ca654eeff19c805925108 GIT binary patch literal 28664 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 Ny}aswh_@zz{|8CNy{iBK literal 0 HcmV?d00001 diff --git a/packages/flutter3d_cpu/test/smaa_test.dart b/packages/flutter3d_cpu/test/smaa_test.dart new file mode 100644 index 000000000..c6a03c3a0 --- /dev/null +++ b/packages/flutter3d_cpu/test/smaa_test.dart @@ -0,0 +1,150 @@ +/// SMAA 1x smooths a staircase towards the edge it stands for — `P1`. +/// +/// dart test test/smaa_test.dart +/// +/// Against the coverage an 8×8 supersample gives, at both slopes and both +/// orientations: a share applied to the wrong side of an edge, or a pattern +/// read the wrong way up, makes the staircase worse rather than better, and +/// one tilt alone would not see it. +library; + +import 'dart:typed_data'; + +import 'package:flutter3d_core/flutter3d_core.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +const int _width = 96; +const int _height = 64; + +/// A white card tilted by [roll] on black, [factor] times the size, through +/// [antiAlias]. Unlit, so the only thing to smooth is the silhouette. +Float32List _render({ + required double roll, + AntiAliasSettings antiAlias = const AntiAliasSettings(), + int factor = 1, + bool tall = false, +}) { + final width = _width * factor; + final height = _height * factor; + final device = CpuDevice( + width: width, + height: height, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final card = + MeshNode( + DeviceMesh.upload(device, CuboidShape().build()), + Material( + lighting: LightingModel.unlit, + baseColor: Vector4(1.0, 1.0, 1.0, 1.0), + ), + ) + ..setPosition(0.0, -0.3, -3.0) + ..setRotationYawPitchRoll(0.0, 0.0, roll) + ..setScale(tall ? 1.4 : 6.0, tall ? 6.0 : 1.4, 0.05); + final camera = CameraNode(); + final result = Renderer.create(device: device).render( + width: width, + height: height, + scene: Scene() + ..add(card) + ..add(camera), + views: [ + RenderView(camera: camera, clearColor: Vector4(0.0, 0.0, 0.0, 1.0)), + ], + settings: RenderSettings( + bloom: const BloomSettings(enabled: false), + look: const LookSettings(dither: 0.0), + antiAlias: antiAlias, + ), + ); + return device.readHdrPixels(result.frame); +} + +/// [big] box-filtered down by [factor]: the coverage each pixel should have. +Float32List _downsample(Float32List big, int factor) { + final out = Float32List(_width * _height); + for (var y = 0; y < _height; y++) { + for (var x = 0; x < _width; x++) { + var sum = 0.0; + for (var dy = 0; dy < factor; dy++) { + for (var dx = 0; dx < factor; dx++) { + sum += + big[((y * factor + dy) * _width * factor + x * factor + dx) * 4]; + } + } + out[y * _width + x] = sum / (factor * factor); + } + } + return out; +} + +/// The summed distance of [frame]'s red from [reference]. +double _error(Float32List frame, Float32List reference) { + var sum = 0.0; + for (var i = 0; i < reference.length; i++) { + sum += (frame[i * 4] - reference[i]).abs(); + } + return sum; +} + +const AntiAliasSettings _smaa = AntiAliasSettings( + enabled: true, + method: EdgeSmoothing.smaa, +); + +void main() { + for (final (name, roll, tall) in <(String, double, bool)>[ + ('a shallow edge rising', 0.07, false), + ('a shallow edge falling', -0.07, false), + ('a steep edge leaning right', 0.07, true), + ('a steep edge leaning left', -0.07, true), + ]) { + test('$name comes out nearer its supersampled coverage', () { + // Mutation: swap red and green in the table, or the near and far + // codes, and the share goes to the wrong side of the edge — the error + // grows past the hard edge's. + final reference = _downsample( + _render(roll: roll, factor: 8, tall: tall), + 8, + ); + final hard = _error(_render(roll: roll, tall: tall), reference); + final smoothed = _error( + _render(roll: roll, antiAlias: _smaa, tall: tall), + reference, + ); + // ignore: avoid_print + print('$name: hard $hard, SMAA $smoothed'); + expect(smoothed, lessThan(hard * 0.6)); + }); + } + + test('a picture with no edge comes back as it went in', () { + final device = CpuDevice( + width: 16, + height: 16, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final camera = CameraNode(); + Float32List draw(AntiAliasSettings antiAlias) { + final result = Renderer.create(device: device).render( + width: 16, + height: 16, + scene: Scene()..add(camera), + views: [ + RenderView(camera: camera, clearColor: Vector4(0.3, 0.5, 0.7, 1.0)), + ], + settings: RenderSettings( + bloom: const BloomSettings(enabled: false), + look: const LookSettings(dither: 0.0), + antiAlias: antiAlias, + ), + ); + return Float32List.fromList(device.readHdrPixels(result.frame)); + } + + expect(draw(_smaa), draw(const AntiAliasSettings())); + }); +} diff --git a/packages/flutter3d_impeller/test/gpu_formats_test.dart b/packages/flutter3d_impeller/test/gpu_formats_test.dart index 8de20b89a..a767ef7ba 100644 --- a/packages/flutter3d_impeller/test/gpu_formats_test.dart +++ b/packages/flutter3d_impeller/test/gpu_formats_test.dart @@ -2,7 +2,7 @@ /// /// The hazard this exists for: a wrong mapping compiles, runs, and renders /// wrong only for the values a scene happens to use. The goldens cover the -/// values the seventy-eight scenes exercise and nothing else — `BlendFactor` has +/// values the seventy-nine scenes exercise and nothing else — `BlendFactor` has /// fifteen members and the engine draws with two of them — so a mistake in the /// other thirteen would be found by a user rather than by us. /// diff --git a/packages/flutter3d_shaders/CHANGELOG.md b/packages/flutter3d_shaders/CHANGELOG.md index db4f338c5..126284fc1 100644 --- a/packages/flutter3d_shaders/CHANGELOG.md +++ b/packages/flutter3d_shaders/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +- **Three stages for SMAA 1x**: `SmaaEdges`, `SmaaWeights` and `SmaaBlend`, + with the `SmaaInfo` block. A bundle must answer to them; the renderer falls + back to FXAA when one does not. + ## 0.8.2+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_shaders/lib/flutter3d_shaders.dart b/packages/flutter3d_shaders/lib/flutter3d_shaders.dart index 17ed19cf1..6220611fc 100644 --- a/packages/flutter3d_shaders/lib/flutter3d_shaders.dart +++ b/packages/flutter3d_shaders/lib/flutter3d_shaders.dart @@ -41,6 +41,9 @@ const List kRequiredShaders = [ (name: 'FullscreenVertex', fragment: false), (name: 'Easu', fragment: true), (name: 'Fxaa', fragment: true), + (name: 'SmaaEdges', fragment: true), + (name: 'SmaaWeights', fragment: true), + (name: 'SmaaBlend', fragment: true), (name: 'LocalExposure', fragment: true), (name: 'LocalExposureBlur', fragment: true), (name: 'Lambert', fragment: true), diff --git a/packages/flutter3d_shaders/lib/stage_bindings.dart b/packages/flutter3d_shaders/lib/stage_bindings.dart index acb4ee93d..a13335b87 100644 --- a/packages/flutter3d_shaders/lib/stage_bindings.dart +++ b/packages/flutter3d_shaders/lib/stage_bindings.dart @@ -340,6 +340,18 @@ stageBindings = blocks, Set samplers})>{ 'SkyCube': (blocks: {}, samplers: {'sky_texture'}), 'SkyCubeVertex': (blocks: {}, samplers: {}), 'SkyVertex': (blocks: {}, samplers: {}), + 'SmaaBlend': ( + blocks: {'SmaaInfo'}, + samplers: {'blend_texture', 'source_texture'}, + ), + 'SmaaEdges': ( + blocks: {'SmaaInfo'}, + samplers: {'source_texture'}, + ), + 'SmaaWeights': ( + blocks: {'SmaaInfo'}, + samplers: {'area_texture', 'edges_texture'}, + ), 'Splat': (blocks: {'FogInfo'}, samplers: {}), 'SplatHashed': ( blocks: {'FogInfo', 'SplatHashInfo'}, diff --git a/packages/flutter3d_shaders/lib/typed_blocks.dart b/packages/flutter3d_shaders/lib/typed_blocks.dart index 87b4624dc..dff3f1830 100644 --- a/packages/flutter3d_shaders/lib/typed_blocks.dart +++ b/packages/flutter3d_shaders/lib/typed_blocks.dart @@ -1112,6 +1112,19 @@ final class SkinInfoBlock extends UniformBlock { }; } +/// `SmaaInfo`. +final class SmaaInfoBlock extends UniformBlock { + SmaaInfoBlock() : super('SmaaInfo'); + + /// `params`: Vector4, at byte 0. + final Float32List params = Float32List(4); + + @override + late final Map members = { + 'params': params, + }; +} + /// `SoftParticleInfo`. final class SoftParticleInfoBlock extends UniformBlock { SoftParticleInfoBlock() : super('SoftParticleInfo'); diff --git a/packages/flutter3d_shaders/lib/uniform_blocks.dart b/packages/flutter3d_shaders/lib/uniform_blocks.dart index bc5e0f742..e85ce1198 100644 --- a/packages/flutter3d_shaders/lib/uniform_blocks.dart +++ b/packages/flutter3d_shaders/lib/uniform_blocks.dart @@ -1422,6 +1422,21 @@ uniformBlocks = >>{ 'light': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), }, }, + 'SmaaBlend': >{ + 'SmaaInfo': { + 'params': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), + }, + }, + 'SmaaEdges': >{ + 'SmaaInfo': { + 'params': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), + }, + }, + 'SmaaWeights': >{ + 'SmaaInfo': { + 'params': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), + }, + }, 'Splat': >{ 'FogInfo': { 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), diff --git a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json index e0181a153..e2ae611db 100644 --- a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json +++ b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json @@ -79,6 +79,18 @@ "type": "fragment", "file": "shaders/post/fxaa.frag" }, + "SmaaEdges": { + "type": "fragment", + "file": "shaders/post/smaa_edges.frag" + }, + "SmaaWeights": { + "type": "fragment", + "file": "shaders/post/smaa_weights.frag" + }, + "SmaaBlend": { + "type": "fragment", + "file": "shaders/post/smaa_blend.frag" + }, "MrtProbe": { "type": "fragment", "file": "shaders/post/mrt_probe.frag" diff --git a/packages/flutter3d_shaders/shaders/post/smaa_blend.frag b/packages/flutter3d_shaders/shaders/post/smaa_blend.frag new file mode 100644 index 000000000..264130fa8 --- /dev/null +++ b/packages/flutter3d_shaders/shaders/post/smaa_blend.frag @@ -0,0 +1,50 @@ +#version 460 core + +// SMAA 1x, last of three passes — `P1`: each pixel moved towards its +// neighbour across the edge by the share the second pass found. +// +// Four shares reach a pixel: its own for its top and left sides, and its +// right and bottom neighbours' far shares for the sides it shares with them. +// The larger pair wins — across or along — and the pixel is two filtered +// taps, each pulled that fraction of a texel towards its side. +// +// `flutter3d_cpu`'s `SmaaBlendShader` is this, line for line. +precision highp float; + +in vec2 v_uv; + +out vec4 frag_color; + +/// The finished picture, filtered. +uniform sampler2D source_texture; + +/// The second pass's shares. +uniform sampler2D blend_texture; + +uniform SmaaInfo { + /// x, y: one texel. z, w: the first pass's, unread here. + vec4 params; +} +smaa_info; + +void main() { + vec2 texel = smaa_info.params.xy; + float right = textureLod(blend_texture, v_uv + vec2(texel.x, 0.0), 0.0).a; + float bottom = textureLod(blend_texture, v_uv + vec2(0.0, texel.y), 0.0).g; + vec4 mine = textureLod(blend_texture, v_uv, 0.0); + float top = mine.r; + float left = mine.b; + if (right + bottom + top + left < 1e-5) { + frag_color = textureLod(source_texture, v_uv, 0.0); + return; + } + + bool across = max(right, left) > max(bottom, top); + float toward = across ? right : bottom; + float away = across ? left : top; + vec2 axis = across ? vec2(texel.x, 0.0) : vec2(0.0, texel.y); + vec4 first = textureLod(source_texture, v_uv + toward * axis, 0.0); + vec4 second = textureLod(source_texture, v_uv - away * axis, 0.0); + float total = toward + away; + frag_color = first * (toward / total) + second * (away / total); +} diff --git a/packages/flutter3d_shaders/shaders/post/smaa_edges.frag b/packages/flutter3d_shaders/shaders/post/smaa_edges.frag new file mode 100644 index 000000000..4ada3437f --- /dev/null +++ b/packages/flutter3d_shaders/shaders/post/smaa_edges.frag @@ -0,0 +1,56 @@ +#version 460 core + +// SMAA 1x, first of three passes — `P1`: where the luma steps. +// +// Red marks a step across a pixel's left side, green across its top. The +// other two passes read nothing else, so this is where the method decides +// what an edge is: a step of at least `params.z` in luma, and not much +// smaller than the largest step beside it. That second test is what keeps +// SMAA off the inside of a high-contrast texture, which FXAA would smooth as +// though it were a staircase. +// +// `flutter3d_cpu`'s `SmaaEdgesShader` is this, line for line. +precision highp float; + +in vec2 v_uv; + +out vec4 frag_color; + +/// The finished picture, tone mapped and encoded. +uniform sampler2D source_texture; + +uniform SmaaInfo { + /// x, y: one texel. z: the smallest luma step that is an edge. w: how + /// many times smaller than the largest step beside it a step may be and + /// still count. + vec4 params; +} +smaa_info; + +/// Rec. 709 weights on the encoded picture. +float Luma(vec2 at) { + vec3 c = textureLod(source_texture, at, 0.0).rgb; + return dot(c, vec3(0.2126, 0.7152, 0.0722)); +} + +void main() { + vec2 texel = smaa_info.params.xy; + float middle = Luma(v_uv); + float left = Luma(v_uv + vec2(-texel.x, 0.0)); + float top = Luma(v_uv + vec2(0.0, -texel.y)); + vec2 delta = abs(vec2(middle - left, middle - top)); + vec2 edges = step(vec2(smaa_info.params.z), delta); + if (edges.x + edges.y == 0.0) { + frag_color = vec4(0.0); + return; + } + + float right = abs(middle - Luma(v_uv + vec2(texel.x, 0.0))); + float bottom = abs(middle - Luma(v_uv + vec2(0.0, texel.y))); + float leftLeft = abs(left - Luma(v_uv + vec2(-2.0 * texel.x, 0.0))); + float topTop = abs(top - Luma(v_uv + vec2(0.0, -2.0 * texel.y))); + float largest = max(max(max(delta.x, delta.y), max(right, bottom)), + max(leftLeft, topTop)); + edges *= step(vec2(largest), smaa_info.params.w * delta); + frag_color = vec4(edges, 0.0, 0.0); +} diff --git a/packages/flutter3d_shaders/shaders/post/smaa_weights.frag b/packages/flutter3d_shaders/shaders/post/smaa_weights.frag new file mode 100644 index 000000000..76e1ea127 --- /dev/null +++ b/packages/flutter3d_shaders/shaders/post/smaa_weights.frag @@ -0,0 +1,122 @@ +#version 460 core + +// SMAA 1x, second of three passes — `P1`: how far each pixel along an edge +// moves. +// +// From every pixel on an edge, a walk to both ends of the run it belongs +// to: the run ends where the edge stops or where an edge crosses it. Which +// side of each end a crossing stands on says what shape the staircase is — +// a step down, a step up, a U — and the line behind that shape, and this +// pixel's place along it, say how much of the pixel the line covers. That +// area is precomputed: the engine's `smaaArea` table, 16×16 texels for each +// pair of end codes, indexed by the square roots of the two distances. +// +// Red and green: the top edge's two shares, this pixel's and the one +// above's. Blue and alpha: the left edge's, this pixel's and the one to the +// left's. Orthogonal edges only; no diagonal search, no corner rounding. +// +// `flutter3d_cpu`'s `SmaaWeightsShader` is this, line for line. +precision highp float; + +in vec2 v_uv; + +out vec4 frag_color; + +/// The first pass's edges: red across the left side, green across the top. +uniform sampler2D edges_texture; + +/// The engine's `smaaArea` table, 80×80, filtered. +uniform sampler2D area_texture; + +uniform SmaaInfo { + /// x, y: one texel. z, w: the first pass's, unread here. + vec4 params; +} +smaa_info; + +/// The most pixels a search walks from the one it starts at, either way. +const int kSmaaMaxSearch = 32; + +vec4 Edges(vec2 offset) { + return textureLod(edges_texture, v_uv + offset * smaa_info.params.xy, 0.0); +} + +/// The table's texel for the two ends' codes and the square roots of the +/// two distances, filtered between square roots. +vec2 Area(float first, float last, float codeFirst, float codeLast) { + vec2 at = (16.0 * vec2(codeFirst, codeLast) + sqrt(vec2(first, last)) + 0.5) / + 80.0; + return textureLod(area_texture, at, 0.0).rg; +} + +void main() { + vec4 here = Edges(vec2(0.0)); + vec4 weights = vec4(0.0); + + if (here.g > 0.5) { + // Along a top edge, to the left: an end where this pixel's own left + // side is crossed, or where the next one has no top edge. + int first = kSmaaMaxSearch; + for (int i = 0; i < kSmaaMaxSearch; i++) { + if (Edges(vec2(-float(i), 0.0)).r > 0.5 || + Edges(vec2(-float(i) - 1.0, 0.0)).g < 0.5) { + first = i; + break; + } + } + int last = kSmaaMaxSearch; + for (int i = 0; i < kSmaaMaxSearch; i++) { + vec4 next = Edges(vec2(float(i) + 1.0, 0.0)); + if (next.r > 0.5 || next.g < 0.5) { + last = i; + break; + } + } + // A crossing on the near side of the edge counts three, on the far + // side one: the codes the bilinear fetch of the reference gives. + float f = float(first); + float l = float(last); + float codeFirst = first == kSmaaMaxSearch + ? 0.0 + : 3.0 * Edges(vec2(-f, 0.0)).r + + Edges(vec2(-f, -1.0)).r; + float codeLast = last == kSmaaMaxSearch + ? 0.0 + : 3.0 * Edges(vec2(l + 1.0, 0.0)).r + + Edges(vec2(l + 1.0, -1.0)).r; + weights.rg = Area(f, l, codeFirst, codeLast); + } + + if (here.r > 0.5) { + // Along a left edge, upwards and then down. + int first = kSmaaMaxSearch; + for (int i = 0; i < kSmaaMaxSearch; i++) { + if (Edges(vec2(0.0, -float(i))).g > 0.5 || + Edges(vec2(0.0, -float(i) - 1.0)).r < 0.5) { + first = i; + break; + } + } + int last = kSmaaMaxSearch; + for (int i = 0; i < kSmaaMaxSearch; i++) { + vec4 next = Edges(vec2(0.0, float(i) + 1.0)); + if (next.g > 0.5 || next.r < 0.5) { + last = i; + break; + } + } + float f = float(first); + float l = float(last); + float codeFirst = first == kSmaaMaxSearch + ? 0.0 + : 3.0 * Edges(vec2(0.0, -f)).g + + Edges(vec2(-1.0, -f)).g; + float codeLast = last == kSmaaMaxSearch + ? 0.0 + : 3.0 * Edges(vec2(0.0, l + 1.0)).g + + Edges(vec2(-1.0, l + 1.0)).g; + weights.ba = Area(f, l, codeFirst, codeLast); + } + + frag_color = weights; +} diff --git a/packages/flutter3d_webgl/CHANGELOG.md b/packages/flutter3d_webgl/CHANGELOG.md index 3072daaa4..ad81600b2 100644 --- a/packages/flutter3d_webgl/CHANGELOG.md +++ b/packages/flutter3d_webgl/CHANGELOG.md @@ -1,3 +1,8 @@ +## Unreleased + +- **SMAA 1x's three stages translated**, from the same sources as every + other stage; `smaa-teapot` is in the browser reference set. + ## 0.8.2+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_webgl/lib/engine_shaders.dart b/packages/flutter3d_webgl/lib/engine_shaders.dart index 7c1464f7e..7b674a99f 100644 --- a/packages/flutter3d_webgl/lib/engine_shaders.dart +++ b/packages/flutter3d_webgl/lib/engine_shaders.dart @@ -22084,6 +22084,252 @@ void main() { vec4(Sharpen(smoothed, northRgb, southRgb, westRgb, eastRgb), 1.0); } +''', + 'SmaaEdges': r'''#version 300 es +precision highp float; +precision highp int; +precision highp sampler2D; +precision highp samplerCube; + +// SMAA 1x, first of three passes — `P1`: where the luma steps. +// +// Red marks a step across a pixel's left side, green across its top. The +// other two passes read nothing else, so this is where the method decides +// what an edge is: a step of at least `params.z` in luma, and not much +// smaller than the largest step beside it. That second test is what keeps +// SMAA off the inside of a high-contrast texture, which FXAA would smooth as +// though it were a staircase. +// +// `flutter3d_cpu`'s `SmaaEdgesShader` is this, line for line. +precision highp float; + +in vec2 v_uv; + +layout(location = 0) out vec4 frag_color; + +/// The finished picture, tone mapped and encoded. +uniform sampler2D source_texture; + +layout(std140) uniform SmaaInfo { + /// x, y: one texel. z: the smallest luma step that is an edge. w: how + /// many times smaller than the largest step beside it a step may be and + /// still count. + vec4 params; +} +smaa_info; + +/// Rec. 709 weights on the encoded picture. +float Luma(vec2 at) { + vec3 c = textureLod(source_texture, at, 0.0).rgb; + return dot(c, vec3(0.2126, 0.7152, 0.0722)); +} + +void main() { + vec2 texel = smaa_info.params.xy; + float middle = Luma(v_uv); + float left = Luma(v_uv + vec2(-texel.x, 0.0)); + float top = Luma(v_uv + vec2(0.0, -texel.y)); + vec2 delta = abs(vec2(middle - left, middle - top)); + vec2 edges = step(vec2(smaa_info.params.z), delta); + if (edges.x + edges.y == 0.0) { + frag_color = vec4(0.0); + return; + } + + float right = abs(middle - Luma(v_uv + vec2(texel.x, 0.0))); + float bottom = abs(middle - Luma(v_uv + vec2(0.0, texel.y))); + float leftLeft = abs(left - Luma(v_uv + vec2(-2.0 * texel.x, 0.0))); + float topTop = abs(top - Luma(v_uv + vec2(0.0, -2.0 * texel.y))); + float largest = max(max(max(delta.x, delta.y), max(right, bottom)), + max(leftLeft, topTop)); + edges *= step(vec2(largest), smaa_info.params.w * delta); + frag_color = vec4(edges, 0.0, 0.0); +} + +''', + 'SmaaWeights': r'''#version 300 es +precision highp float; +precision highp int; +precision highp sampler2D; +precision highp samplerCube; + +// SMAA 1x, second of three passes — `P1`: how far each pixel along an edge +// moves. +// +// From every pixel on an edge, a walk to both ends of the run it belongs +// to: the run ends where the edge stops or where an edge crosses it. Which +// side of each end a crossing stands on says what shape the staircase is — +// a step down, a step up, a U — and the line behind that shape, and this +// pixel's place along it, say how much of the pixel the line covers. That +// area is precomputed: the engine's `smaaArea` table, 16×16 texels for each +// pair of end codes, indexed by the square roots of the two distances. +// +// Red and green: the top edge's two shares, this pixel's and the one +// above's. Blue and alpha: the left edge's, this pixel's and the one to the +// left's. Orthogonal edges only; no diagonal search, no corner rounding. +// +// `flutter3d_cpu`'s `SmaaWeightsShader` is this, line for line. +precision highp float; + +in vec2 v_uv; + +layout(location = 0) out vec4 frag_color; + +/// The first pass's edges: red across the left side, green across the top. +uniform sampler2D edges_texture; + +/// The engine's `smaaArea` table, 80×80, filtered. +uniform sampler2D area_texture; + +layout(std140) uniform SmaaInfo { + /// x, y: one texel. z, w: the first pass's, unread here. + vec4 params; +} +smaa_info; + +/// The most pixels a search walks from the one it starts at, either way. +const int kSmaaMaxSearch = 32; + +vec4 Edges(vec2 offset) { + return textureLod(edges_texture, v_uv + offset * smaa_info.params.xy, 0.0); +} + +/// The table's texel for the two ends' codes and the square roots of the +/// two distances, filtered between square roots. +vec2 Area(float first, float last, float codeFirst, float codeLast) { + vec2 at = (16.0 * vec2(codeFirst, codeLast) + sqrt(vec2(first, last)) + 0.5) / + 80.0; + return textureLod(area_texture, at, 0.0).rg; +} + +void main() { + vec4 here = Edges(vec2(0.0)); + vec4 weights = vec4(0.0); + + if (here.g > 0.5) { + // Along a top edge, to the left: an end where this pixel's own left + // side is crossed, or where the next one has no top edge. + int first = kSmaaMaxSearch; + for (int i = 0; i < kSmaaMaxSearch; i++) { + if (Edges(vec2(-float(i), 0.0)).r > 0.5 || + Edges(vec2(-float(i) - 1.0, 0.0)).g < 0.5) { + first = i; + break; + } + } + int last = kSmaaMaxSearch; + for (int i = 0; i < kSmaaMaxSearch; i++) { + vec4 next = Edges(vec2(float(i) + 1.0, 0.0)); + if (next.r > 0.5 || next.g < 0.5) { + last = i; + break; + } + } + // A crossing on the near side of the edge counts three, on the far + // side one: the codes the bilinear fetch of the reference gives. + float f = float(first); + float l = float(last); + float codeFirst = first == kSmaaMaxSearch + ? 0.0 + : 3.0 * Edges(vec2(-f, 0.0)).r + + Edges(vec2(-f, -1.0)).r; + float codeLast = last == kSmaaMaxSearch + ? 0.0 + : 3.0 * Edges(vec2(l + 1.0, 0.0)).r + + Edges(vec2(l + 1.0, -1.0)).r; + weights.rg = Area(f, l, codeFirst, codeLast); + } + + if (here.r > 0.5) { + // Along a left edge, upwards and then down. + int first = kSmaaMaxSearch; + for (int i = 0; i < kSmaaMaxSearch; i++) { + if (Edges(vec2(0.0, -float(i))).g > 0.5 || + Edges(vec2(0.0, -float(i) - 1.0)).r < 0.5) { + first = i; + break; + } + } + int last = kSmaaMaxSearch; + for (int i = 0; i < kSmaaMaxSearch; i++) { + vec4 next = Edges(vec2(0.0, float(i) + 1.0)); + if (next.g > 0.5 || next.r < 0.5) { + last = i; + break; + } + } + float f = float(first); + float l = float(last); + float codeFirst = first == kSmaaMaxSearch + ? 0.0 + : 3.0 * Edges(vec2(0.0, -f)).g + + Edges(vec2(-1.0, -f)).g; + float codeLast = last == kSmaaMaxSearch + ? 0.0 + : 3.0 * Edges(vec2(0.0, l + 1.0)).g + + Edges(vec2(-1.0, l + 1.0)).g; + weights.ba = Area(f, l, codeFirst, codeLast); + } + + frag_color = weights; +} + +''', + 'SmaaBlend': r'''#version 300 es +precision highp float; +precision highp int; +precision highp sampler2D; +precision highp samplerCube; + +// SMAA 1x, last of three passes — `P1`: each pixel moved towards its +// neighbour across the edge by the share the second pass found. +// +// Four shares reach a pixel: its own for its top and left sides, and its +// right and bottom neighbours' far shares for the sides it shares with them. +// The larger pair wins — across or along — and the pixel is two filtered +// taps, each pulled that fraction of a texel towards its side. +// +// `flutter3d_cpu`'s `SmaaBlendShader` is this, line for line. +precision highp float; + +in vec2 v_uv; + +layout(location = 0) out vec4 frag_color; + +/// The finished picture, filtered. +uniform sampler2D source_texture; + +/// The second pass's shares. +uniform sampler2D blend_texture; + +layout(std140) uniform SmaaInfo { + /// x, y: one texel. z, w: the first pass's, unread here. + vec4 params; +} +smaa_info; + +void main() { + vec2 texel = smaa_info.params.xy; + float right = textureLod(blend_texture, v_uv + vec2(texel.x, 0.0), 0.0).a; + float bottom = textureLod(blend_texture, v_uv + vec2(0.0, texel.y), 0.0).g; + vec4 mine = textureLod(blend_texture, v_uv, 0.0); + float top = mine.r; + float left = mine.b; + if (right + bottom + top + left < 1e-5) { + frag_color = textureLod(source_texture, v_uv, 0.0); + return; + } + + bool across = max(right, left) > max(bottom, top); + float toward = across ? right : bottom; + float away = across ? left : top; + vec2 axis = across ? vec2(texel.x, 0.0) : vec2(0.0, texel.y); + vec4 first = textureLod(source_texture, v_uv + toward * axis, 0.0); + vec4 second = textureLod(source_texture, v_uv - away * axis, 0.0); + float total = toward + away; + frag_color = first * (toward / total) + second * (away / total); +} + ''', 'MrtProbe': r'''#version 300 es precision highp float; diff --git a/packages/flutter3d_webgl/test/cross_backend_test.dart b/packages/flutter3d_webgl/test/cross_backend_test.dart index e94ad0c5d..c75562cd7 100644 --- a/packages/flutter3d_webgl/test/cross_backend_test.dart +++ b/packages/flutter3d_webgl/test/cross_backend_test.dart @@ -229,6 +229,10 @@ const Map _budgets = { 'cascade-walk': 0.04, 'clearcoat-car-paint': 0.2, 'easu-half': 0.9, + // 0.591% measured, every pixel of it on an edge: WebGL2 already draws + // `shadow-teapot`'s silhouette 0.28% apart from Impeller's, and SMAA reads + // a slightly different staircase there and moves its neighbours with it. + 'smaa-teapot': 0.65, 'evsm-soft': 0.4, // 0.047% measured: the air right round a torch, where the light falls off // as one over the distance squared, is shadowed by one unfiltered tap of diff --git a/packages/flutter3d_webgl/test/goldens/smaa-teapot.png b/packages/flutter3d_webgl/test/goldens/smaa-teapot.png new file mode 100644 index 0000000000000000000000000000000000000000..7798a4d36bf971ee2a86f1cd60232fc1f4179c7b GIT binary patch literal 28757 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 literal 0 HcmV?d00001 diff --git a/packages/flutter3d_webgl/test/lighting_models_test.dart b/packages/flutter3d_webgl/test/lighting_models_test.dart index e4985b65f..3b305660b 100644 --- a/packages/flutter3d_webgl/test/lighting_models_test.dart +++ b/packages/flutter3d_webgl/test/lighting_models_test.dart @@ -6,7 +6,7 @@ /// `lighting-unlit` came back as an empty frame — the background colour in every /// pixel — while the other five drew a sphere. The golden set found it, once /// there was a golden set; this says the same thing in one second instead of a -/// browser run over seventy-eight scenes, and says it per model rather than per +/// browser run over seventy-nine scenes, and says it per model rather than per /// picture. /// /// It deliberately does not compare against a reference. What a shader *should* diff --git a/packages/flutter3d_webgl/tool/golden_web.sh b/packages/flutter3d_webgl/tool/golden_web.sh index 2dffea504..8d75a5552 100755 --- a/packages/flutter3d_webgl/tool/golden_web.sh +++ b/packages/flutter3d_webgl/tool/golden_web.sh @@ -16,7 +16,7 @@ # the same one either way, so this script has no reason to read it. # # **One build for the whole suite, and for both browser backends.** The scene is -# a query parameter rather than a compile-time define, so the seventy-eight scenes +# a query parameter rather than a compile-time define, so the seventy-nine scenes # are one dart2js run and a navigation each, rather than a dart2js run each. # That is the only reason this is minutes rather than an hour, and it is why the # backend arrives the same way: a define per backend would have spent the saving diff --git a/packages/flutter3d_webgpu/CHANGELOG.md b/packages/flutter3d_webgpu/CHANGELOG.md index ad3ae5e31..077465505 100644 --- a/packages/flutter3d_webgpu/CHANGELOG.md +++ b/packages/flutter3d_webgpu/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +- **SMAA 1x's three stages translated**, through glslang and naga like every + other stage; `smaa-teapot` is in the WebGPU reference set and matches + Impeller's to the pixel. + ## 0.8.2+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_webgpu/lib/engine_shaders.dart b/packages/flutter3d_webgpu/lib/engine_shaders.dart index dd3f1f32f..50f52b92f 100644 --- a/packages/flutter3d_webgpu/lib/engine_shaders.dart +++ b/packages/flutter3d_webgpu/lib/engine_shaders.dart @@ -27615,6 +27615,664 @@ fn main(@location(5) v_uv: vec2) -> @location(0) vec4 { ), ], ), + 'SmaaEdges': WebGpuStage( + wgsl: r''' +struct SmaaInfo { + params: vec4, +} + +@group(1) @binding(1) +var source_texture_tex: texture_2d; +@group(1) @binding(2) +var source_texture_smp: sampler; +@group(1) @binding(0) +var smaa_info: SmaaInfo; +var v_uv_1: vec2; +var frag_color: vec4; + +fn Luma_u0028_vf2_u003b(at: ptr>) -> f32 { + var c: vec3; + + let _e19 = (*at); + let _e20 = textureSampleLevel(source_texture_tex, source_texture_smp, _e19, 0f); + c = _e20.xyz; + let _e22 = c; + return dot(_e22, vec3(0.2126f, 0.7152f, 0.0722f)); +} + +fn main_1() { + var texel: vec2; + var middle: f32; + var param: vec2; + var left: f32; + var param_1: vec2; + var top: f32; + var param_2: vec2; + var delta: vec2; + var edges: vec2; + var right: f32; + var param_3: vec2; + var bottom: f32; + var param_4: vec2; + var leftLeft: f32; + var param_5: vec2; + var topTop: f32; + var param_6: vec2; + var largest: f32; + + let _e36 = smaa_info.params; + texel = _e36.xy; + let _e38 = v_uv_1; + param = _e38; + let _e39 = Luma_u0028_vf2_u003b((¶m)); + middle = _e39; + let _e40 = v_uv_1; + let _e42 = texel[0u]; + param_1 = (_e40 + vec2(-(_e42), 0f)); + let _e46 = Luma_u0028_vf2_u003b((¶m_1)); + left = _e46; + let _e47 = v_uv_1; + let _e49 = texel[1u]; + param_2 = (_e47 + vec2(0f, -(_e49))); + let _e53 = Luma_u0028_vf2_u003b((¶m_2)); + top = _e53; + let _e54 = middle; + let _e55 = left; + let _e57 = middle; + let _e58 = top; + delta = abs(vec2((_e54 - _e55), (_e57 - _e58))); + let _e64 = smaa_info.params[2u]; + let _e66 = delta; + edges = step(vec2(_e64), _e66); + let _e69 = edges[0u]; + let _e71 = edges[1u]; + if ((_e69 + _e71) == 0f) { + frag_color = vec4(0f, 0f, 0f, 0f); + return; + } + let _e74 = middle; + let _e75 = v_uv_1; + let _e77 = texel[0u]; + param_3 = (_e75 + vec2(_e77, 0f)); + let _e80 = Luma_u0028_vf2_u003b((¶m_3)); + right = abs((_e74 - _e80)); + let _e83 = middle; + let _e84 = v_uv_1; + let _e86 = texel[1u]; + param_4 = (_e84 + vec2(0f, _e86)); + let _e89 = Luma_u0028_vf2_u003b((¶m_4)); + bottom = abs((_e83 - _e89)); + let _e92 = left; + let _e93 = v_uv_1; + let _e95 = texel[0u]; + param_5 = (_e93 + vec2((-2f * _e95), 0f)); + let _e99 = Luma_u0028_vf2_u003b((¶m_5)); + leftLeft = abs((_e92 - _e99)); + let _e102 = top; + let _e103 = v_uv_1; + let _e105 = texel[1u]; + param_6 = (_e103 + vec2(0f, (-2f * _e105))); + let _e109 = Luma_u0028_vf2_u003b((¶m_6)); + topTop = abs((_e102 - _e109)); + let _e113 = delta[0u]; + let _e115 = delta[1u]; + let _e117 = right; + let _e118 = bottom; + let _e121 = leftLeft; + let _e122 = topTop; + largest = max(max(max(_e113, _e115), max(_e117, _e118)), max(_e121, _e122)); + let _e125 = largest; + let _e129 = smaa_info.params[3u]; + let _e130 = delta; + let _e133 = edges; + edges = (_e133 * step(vec2(_e125), (_e130 * _e129))); + let _e135 = edges; + frag_color = vec4(_e135.x, _e135.y, 0f, 0f); + return; +} + +@fragment +fn main(@location(5) v_uv: vec2) -> @location(0) vec4 { + v_uv_1 = v_uv; + main_1(); + let _e3 = frag_color; + return _e3; +} +''', + attributes: [], + blocks: [ + WebGpuBlock( + name: 'SmaaInfo', + group: 1, + binding: 0, + sizeInBytes: 16, + members: [ + WebGpuBlockMember( + name: 'params', + offsetInBytes: 0, + sizeInBytes: 16, + ), + ], + ), + ], + samplers: [ + WebGpuSampler( + name: 'source_texture', + group: 1, + textureBinding: 1, + samplerBinding: 2, + dimension: WebGpuTextureDimension.twoDimensional, + ), + ], + ), + 'SmaaWeights': WebGpuStage( + wgsl: r''' +struct SmaaInfo { + params: vec4, +} + +@group(1) @binding(3) +var edges_texture_tex: texture_2d; +@group(1) @binding(4) +var edges_texture_smp: sampler; +var v_uv_1: vec2; +@group(1) @binding(0) +var smaa_info: SmaaInfo; +@group(1) @binding(1) +var area_texture_tex: texture_2d; +@group(1) @binding(2) +var area_texture_smp: sampler; +var frag_color: vec4; + +fn Area_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b(first: ptr, last: ptr, codeFirst: ptr, codeLast: ptr) -> vec2 { + var at: vec2; + + let _e28 = (*codeFirst); + let _e29 = (*codeLast); + let _e32 = (*first); + let _e33 = (*last); + at = ((((vec2(_e28, _e29) * 16f) + sqrt(vec2(_e32, _e33))) + vec2(0.5f)) / vec2(80f)); + let _e41 = at; + let _e42 = textureSampleLevel(area_texture_tex, area_texture_smp, _e41, 0f); + return _e42.xy; +} + +fn Edges_u0028_vf2_u003b(offset: ptr>) -> vec4 { + let _e24 = v_uv_1; + let _e25 = (*offset); + let _e27 = smaa_info.params; + let _e31 = textureSampleLevel(edges_texture_tex, edges_texture_smp, (_e24 + (_e25 * _e27.xy)), 0f); + return _e31; +} + +fn main_1() { + var here: vec4; + var param: vec2; + var weights: vec4; + var first_1: i32; + var i: i32; + var param_1: vec2; + var param_2: vec2; + var last_1: i32; + var i_1: i32; + var next: vec4; + var param_3: vec2; + var f: f32; + var l: f32; + var codeFirst_1: f32; + var local: f32; + var param_4: vec2; + var param_5: vec2; + var codeLast_1: f32; + var local_1: f32; + var param_6: vec2; + var param_7: vec2; + var param_8: f32; + var param_9: f32; + var param_10: f32; + var param_11: f32; + var first_2: i32; + var i_2: i32; + var param_12: vec2; + var param_13: vec2; + var last_2: i32; + var i_3: i32; + var next_1: vec4; + var param_14: vec2; + var f_1: f32; + var l_1: f32; + var codeFirst_2: f32; + var local_2: f32; + var param_15: vec2; + var param_16: vec2; + var codeLast_2: f32; + var local_3: f32; + var param_17: vec2; + var param_18: vec2; + var param_19: f32; + var param_20: f32; + var param_21: f32; + var param_22: f32; + var phi_126_: bool; + var phi_159_: bool; + var phi_265_: bool; + var phi_297_: bool; + + param = vec2(0f, 0f); + let _e70 = Edges_u0028_vf2_u003b((¶m)); + here = _e70; + weights = vec4(0f, 0f, 0f, 0f); + let _e72 = here[1u]; + if (_e72 > 0.5f) { + first_1 = 32i; + i = 0i; + loop { + let _e74 = i; + if (_e74 < 32i) { + let _e76 = i; + param_1 = vec2(-(f32(_e76)), 0f); + let _e80 = Edges_u0028_vf2_u003b((¶m_1)); + let _e82 = (_e80.x > 0.5f); + phi_126_ = _e82; + if !(_e82) { + let _e84 = i; + param_2 = vec2((-(f32(_e84)) - 1f), 0f); + let _e89 = Edges_u0028_vf2_u003b((¶m_2)); + phi_126_ = (_e89.y < 0.5f); + } + let _e93 = phi_126_; + if _e93 { + let _e94 = i; + first_1 = _e94; + break; + } + continue; + } else { + break; + } + continuing { + let _e95 = i; + i = (_e95 + 1i); + } + } + last_1 = 32i; + i_1 = 0i; + loop { + let _e97 = i_1; + if (_e97 < 32i) { + let _e99 = i_1; + param_3 = vec2((f32(_e99) + 1f), 0f); + let _e103 = Edges_u0028_vf2_u003b((¶m_3)); + next = _e103; + let _e105 = next[0u]; + let _e106 = (_e105 > 0.5f); + phi_159_ = _e106; + if !(_e106) { + let _e109 = next[1u]; + phi_159_ = (_e109 < 0.5f); + } + let _e112 = phi_159_; + if _e112 { + let _e113 = i_1; + last_1 = _e113; + break; + } + continue; + } else { + break; + } + continuing { + let _e114 = i_1; + i_1 = (_e114 + 1i); + } + } + let _e116 = first_1; + f = f32(_e116); + let _e118 = last_1; + l = f32(_e118); + let _e120 = first_1; + if (_e120 == 32i) { + local = 0f; + } else { + let _e122 = f; + param_4 = vec2(-(_e122), 0f); + let _e125 = Edges_u0028_vf2_u003b((¶m_4)); + let _e128 = f; + param_5 = vec2(-(_e128), -1f); + let _e131 = Edges_u0028_vf2_u003b((¶m_5)); + local = ((3f * _e125.x) + _e131.x); + } + let _e134 = local; + codeFirst_1 = _e134; + let _e135 = last_1; + if (_e135 == 32i) { + local_1 = 0f; + } else { + let _e137 = l; + param_6 = vec2((_e137 + 1f), 0f); + let _e140 = Edges_u0028_vf2_u003b((¶m_6)); + let _e143 = l; + param_7 = vec2((_e143 + 1f), -1f); + let _e146 = Edges_u0028_vf2_u003b((¶m_7)); + local_1 = ((3f * _e140.x) + _e146.x); + } + let _e149 = local_1; + codeLast_1 = _e149; + let _e150 = f; + param_8 = _e150; + let _e151 = l; + param_9 = _e151; + let _e152 = codeFirst_1; + param_10 = _e152; + let _e153 = codeLast_1; + param_11 = _e153; + let _e154 = Area_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_8), (¶m_9), (¶m_10), (¶m_11)); + weights[0u] = _e154.x; + weights[1u] = _e154.y; + } + let _e160 = here[0u]; + if (_e160 > 0.5f) { + first_2 = 32i; + i_2 = 0i; + loop { + let _e162 = i_2; + if (_e162 < 32i) { + let _e164 = i_2; + param_12 = vec2(0f, -(f32(_e164))); + let _e168 = Edges_u0028_vf2_u003b((¶m_12)); + let _e170 = (_e168.y > 0.5f); + phi_265_ = _e170; + if !(_e170) { + let _e172 = i_2; + param_13 = vec2(0f, (-(f32(_e172)) - 1f)); + let _e177 = Edges_u0028_vf2_u003b((¶m_13)); + phi_265_ = (_e177.x < 0.5f); + } + let _e181 = phi_265_; + if _e181 { + let _e182 = i_2; + first_2 = _e182; + break; + } + continue; + } else { + break; + } + continuing { + let _e183 = i_2; + i_2 = (_e183 + 1i); + } + } + last_2 = 32i; + i_3 = 0i; + loop { + let _e185 = i_3; + if (_e185 < 32i) { + let _e187 = i_3; + param_14 = vec2(0f, (f32(_e187) + 1f)); + let _e191 = Edges_u0028_vf2_u003b((¶m_14)); + next_1 = _e191; + let _e193 = next_1[1u]; + let _e194 = (_e193 > 0.5f); + phi_297_ = _e194; + if !(_e194) { + let _e197 = next_1[0u]; + phi_297_ = (_e197 < 0.5f); + } + let _e200 = phi_297_; + if _e200 { + let _e201 = i_3; + last_2 = _e201; + break; + } + continue; + } else { + break; + } + continuing { + let _e202 = i_3; + i_3 = (_e202 + 1i); + } + } + let _e204 = first_2; + f_1 = f32(_e204); + let _e206 = last_2; + l_1 = f32(_e206); + let _e208 = first_2; + if (_e208 == 32i) { + local_2 = 0f; + } else { + let _e210 = f_1; + param_15 = vec2(0f, -(_e210)); + let _e213 = Edges_u0028_vf2_u003b((¶m_15)); + let _e216 = f_1; + param_16 = vec2(-1f, -(_e216)); + let _e219 = Edges_u0028_vf2_u003b((¶m_16)); + local_2 = ((3f * _e213.y) + _e219.y); + } + let _e222 = local_2; + codeFirst_2 = _e222; + let _e223 = last_2; + if (_e223 == 32i) { + local_3 = 0f; + } else { + let _e225 = l_1; + param_17 = vec2(0f, (_e225 + 1f)); + let _e228 = Edges_u0028_vf2_u003b((¶m_17)); + let _e231 = l_1; + param_18 = vec2(-1f, (_e231 + 1f)); + let _e234 = Edges_u0028_vf2_u003b((¶m_18)); + local_3 = ((3f * _e228.y) + _e234.y); + } + let _e237 = local_3; + codeLast_2 = _e237; + let _e238 = f_1; + param_19 = _e238; + let _e239 = l_1; + param_20 = _e239; + let _e240 = codeFirst_2; + param_21 = _e240; + let _e241 = codeLast_2; + param_22 = _e241; + let _e242 = Area_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_19), (¶m_20), (¶m_21), (¶m_22)); + weights[2u] = _e242.x; + weights[3u] = _e242.y; + } + let _e247 = weights; + frag_color = _e247; + return; +} + +@fragment +fn main(@location(5) v_uv: vec2) -> @location(0) vec4 { + v_uv_1 = v_uv; + main_1(); + let _e3 = frag_color; + return _e3; +} +''', + attributes: [], + blocks: [ + WebGpuBlock( + name: 'SmaaInfo', + group: 1, + binding: 0, + sizeInBytes: 16, + members: [ + WebGpuBlockMember( + name: 'params', + offsetInBytes: 0, + sizeInBytes: 16, + ), + ], + ), + ], + samplers: [ + WebGpuSampler( + name: 'area_texture', + group: 1, + textureBinding: 1, + samplerBinding: 2, + dimension: WebGpuTextureDimension.twoDimensional, + ), + WebGpuSampler( + name: 'edges_texture', + group: 1, + textureBinding: 3, + samplerBinding: 4, + dimension: WebGpuTextureDimension.twoDimensional, + ), + ], + ), + 'SmaaBlend': WebGpuStage( + wgsl: r''' +struct SmaaInfo { + params: vec4, +} + +@group(1) @binding(0) +var smaa_info: SmaaInfo; +@group(1) @binding(1) +var blend_texture_tex: texture_2d; +@group(1) @binding(2) +var blend_texture_smp: sampler; +var v_uv_1: vec2; +var frag_color: vec4; +@group(1) @binding(3) +var source_texture_tex: texture_2d; +@group(1) @binding(4) +var source_texture_smp: sampler; + +fn main_1() { + var texel: vec2; + var right: f32; + var bottom: f32; + var mine: vec4; + var top: f32; + var left: f32; + var across: bool; + var toward: f32; + var away: f32; + var axis: vec2; + var local: vec2; + var first: vec4; + var second: vec4; + var total: f32; + + let _e29 = smaa_info.params; + texel = _e29.xy; + let _e31 = v_uv_1; + let _e33 = texel[0u]; + let _e36 = textureSampleLevel(blend_texture_tex, blend_texture_smp, (_e31 + vec2(_e33, 0f)), 0f); + right = _e36.w; + let _e38 = v_uv_1; + let _e40 = texel[1u]; + let _e43 = textureSampleLevel(blend_texture_tex, blend_texture_smp, (_e38 + vec2(0f, _e40)), 0f); + bottom = _e43.y; + let _e45 = v_uv_1; + let _e46 = textureSampleLevel(blend_texture_tex, blend_texture_smp, _e45, 0f); + mine = _e46; + let _e48 = mine[0u]; + top = _e48; + let _e50 = mine[2u]; + left = _e50; + let _e51 = right; + let _e52 = bottom; + let _e54 = top; + let _e56 = left; + if ((((_e51 + _e52) + _e54) + _e56) < 0.00001f) { + let _e59 = v_uv_1; + let _e60 = textureSampleLevel(source_texture_tex, source_texture_smp, _e59, 0f); + frag_color = _e60; + return; + } + let _e61 = right; + let _e62 = left; + let _e64 = bottom; + let _e65 = top; + across = (max(_e61, _e62) > max(_e64, _e65)); + let _e68 = across; + let _e69 = right; + let _e70 = bottom; + toward = select(_e70, _e69, _e68); + let _e72 = across; + let _e73 = left; + let _e74 = top; + away = select(_e74, _e73, _e72); + let _e76 = across; + if _e76 { + let _e78 = texel[0u]; + local = vec2(_e78, 0f); + } else { + let _e81 = texel[1u]; + local = vec2(0f, _e81); + } + let _e83 = local; + axis = _e83; + let _e84 = v_uv_1; + let _e85 = toward; + let _e86 = axis; + let _e89 = textureSampleLevel(source_texture_tex, source_texture_smp, (_e84 + (_e86 * _e85)), 0f); + first = _e89; + let _e90 = v_uv_1; + let _e91 = away; + let _e92 = axis; + let _e95 = textureSampleLevel(source_texture_tex, source_texture_smp, (_e90 - (_e92 * _e91)), 0f); + second = _e95; + let _e96 = toward; + let _e97 = away; + total = (_e96 + _e97); + let _e99 = first; + let _e100 = toward; + let _e101 = total; + let _e104 = second; + let _e105 = away; + let _e106 = total; + frag_color = ((_e99 * (_e100 / _e101)) + (_e104 * (_e105 / _e106))); + return; +} + +@fragment +fn main(@location(5) v_uv: vec2) -> @location(0) vec4 { + v_uv_1 = v_uv; + main_1(); + let _e3 = frag_color; + return _e3; +} +''', + attributes: [], + blocks: [ + WebGpuBlock( + name: 'SmaaInfo', + group: 1, + binding: 0, + sizeInBytes: 16, + members: [ + WebGpuBlockMember( + name: 'params', + offsetInBytes: 0, + sizeInBytes: 16, + ), + ], + ), + ], + samplers: [ + WebGpuSampler( + name: 'blend_texture', + group: 1, + textureBinding: 1, + samplerBinding: 2, + dimension: WebGpuTextureDimension.twoDimensional, + ), + WebGpuSampler( + name: 'source_texture', + group: 1, + textureBinding: 3, + samplerBinding: 4, + dimension: WebGpuTextureDimension.twoDimensional, + ), + ], + ), 'MrtProbe': WebGpuStage( wgsl: r''' struct FragmentOutput { diff --git a/packages/flutter3d_webgpu/test/cross_backend_test.dart b/packages/flutter3d_webgpu/test/cross_backend_test.dart index c119c5b22..fcb2e9204 100644 --- a/packages/flutter3d_webgpu/test/cross_backend_test.dart +++ b/packages/flutter3d_webgpu/test/cross_backend_test.dart @@ -182,6 +182,7 @@ const Map _budgets = { 'cascade-walk': 0.01, 'clearcoat-car-paint': 0.01, 'easu-half': 0.01, + 'smaa-teapot': 0.01, 'evsm-soft': 0.33, 'fog-torches': 0.01, 'glass-stack-oit': 0.01, diff --git a/packages/flutter3d_webgpu/test/goldens/smaa-teapot.png b/packages/flutter3d_webgpu/test/goldens/smaa-teapot.png new file mode 100644 index 0000000000000000000000000000000000000000..53cf1437d1f2232ead9a730217254b8e2f4461dd GIT binary patch literal 28677 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 bi#w}C+FKZ@KfrhVuDnrH{}1`f^z;7#1OmZ( literal 0 HcmV?d00001 diff --git a/site/content/core/architecture.md b/site/content/core/architecture.md index 050941ae6..e6b76e172 100644 --- a/site/content/core/architecture.md +++ b/site/content/core/architecture.md @@ -53,7 +53,7 @@ Each exists for a different reason, and none of them is a fallback for another. Its shaders are GLSL ES 3.00 generated from `flutter3d_shaders`. Nothing checked that the generated file was current for a long time, and it cost two failures that reported nothing: a uniform member the browser's copy had never heard of, and a sky shader from before the sky was rewritten. `tool/ci.sh` regenerates it and fails on the diff now, and the compiled Impeller bundle, which cannot be diffed, is held by a freshness rule instead. -**`flutter3d_cpu`** is the one that makes the agreement mean something. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. It is also what makes 78 golden scenes checkable in a headless run, and what caught three bugs that every simulation test passed. +**`flutter3d_cpu`** is the one that makes the agreement mean something. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. It is also what makes 79 golden scenes checkable in a headless run, and what caught three bugs that every simulation test passed. **`flutter3d_webgpu`** asks a question none of the other three could: whether the *shader* half of the contract is a seam. The first three all read one text (`impellerc` compiles the GLSL, the WebGL generator translates it, the software rasteriser transcribes it into Dart by hand), and WebGPU cannot, because WGSL is a different language and a browser will not take SPIR-V. So its table is generated down a second toolchain, the same manifest through `glslangValidator` and then `naga`, and all thirty-nine stages come out. What it declines it declines by name: no blend constant (WebGPU has no colour/alpha pair for one) and no wireframe (no polygon fill mode in the API at all). Two more used to be on that list. Rendering into a mip is on now and cost no code: a reflection probe convolves its own chain, and a face and a level are `baseArrayLayer` and `baseMipLevel` on an ordinary view here. Block compression is on too, and what it cost was the asking: a WebGPU device gets exactly the features it requested, and requesting one the adapter does not carry rejects the device instead of handing back a lesser one, so the adapter is asked, the intersection is requested, and the capability answers from what was granted. diff --git a/site/content/core/backends.md b/site/content/core/backends.md index 6ca5d4729..cc457c2d8 100644 --- a/site/content/core/backends.md +++ b/site/content/core/backends.md @@ -405,7 +405,7 @@ Eighty-three entry points. `kRequiredShaders` and the bundle manifest are kept i | Vertex | `MeshVertex`, `MeshSkinnedVertex`, `MeshInstancedVertex`, `MeshLightmappedVertex`, `FullscreenVertex`, `DebugLineVertex`, `ParticleVertex`, `ParticleMeshVertex`, `PolylineVertex`, `ShadowTileResetVertex`, `SkyVertex`, `SkyCubeVertex`, `ImpostorVertex`, `VelocityVertex`, `VelocitySkinnedVertex`, `VelocityInstancedVertex` | | Lighting | `Unlit`, `Lambert`, `BlinnPhong`, `Pbr`, `Toon`, `Normals`, `PbrLayered`, `Impostor` | | Shadows | `ShadowDepth`, `ShadowDistance`, `ShadowDepthMasked`, `ShadowDistanceMasked`, `ShadowTileReset`, `EvsmFilter`, `ShadowCopy` | -| Post | `BloomThreshold`, `BloomDownsample`, `BloomUpsample`, `Composite`, `Fxaa`, `Reflections`, `Ssao`, `SsaoBlur`, `ContactShadow`, `ContactShadowResolve`, `Splat`, `LightShafts`, `DepthOfField`, `ViewportShade`, `Luminance`, `ProbePrefilter`, `CameraVelocity`, `Velocity`, `VelocityTileMax`, `VelocityNeighborMax`, `MotionBlur`, `TemporalResolve`, `TemporalAccumulate`, `Reactive`, `Easu`, `LocalExposure`, `LocalExposureBlur`, `VolumetricFog`, `VolumetricFogUpsample`, `DepthPyramid`, `SceneColourCopy`, `WboitResolve`, `DofTileMax` | +| Post | `BloomThreshold`, `BloomDownsample`, `BloomUpsample`, `Composite`, `Fxaa`, `SmaaEdges`, `SmaaWeights`, `SmaaBlend`, `Reflections`, `Ssao`, `SsaoBlur`, `ContactShadow`, `ContactShadowResolve`, `Splat`, `LightShafts`, `DepthOfField`, `ViewportShade`, `Luminance`, `ProbePrefilter`, `CameraVelocity`, `Velocity`, `VelocityTileMax`, `VelocityNeighborMax`, `MotionBlur`, `TemporalResolve`, `TemporalAccumulate`, `Reactive`, `Easu`, `LocalExposure`, `LocalExposureBlur`, `VolumetricFog`, `VolumetricFogUpsample`, `DepthPyramid`, `SceneColourCopy`, `WboitResolve`, `DofTileMax` | | Particles | `Particle`, `ParticleTextured`, `ParticleMesh`, `ParticleSixWay`, `ReactiveSprite`, `SplatHashed`, `ParticleSoft`, `ParticleTexturedSoft`, `ParticleSixWaySoft` | | Sky | `Sky`, `SkyCube` | | Debug | `DebugLine`, `MrtProbe`, `ObjectId`, `Xray` | diff --git a/site/content/core/index.md b/site/content/core/index.md index 4298a3978..45819ac13 100644 --- a/site/content/core/index.md +++ b/site/content/core/index.md @@ -25,7 +25,7 @@ Nothing in this section is genre knowledge. That property is what made the secon |---|---| | `flutter3d_impeller` | `flutter_gpu`, over Metal and Vulkan. The production one | | `flutter3d_webgl` | WebGL2 in the browser. Runs all three games at a fixed resolution and a lower frame rate, and is what an ordinary web build opens | -| `flutter3d_cpu` | Nothing: it rasterises in Dart, so 78 golden scenes stay checkable with no GPU in the room | +| `flutter3d_cpu` | Nothing: it rasterises in Dart, so 79 golden scenes stay checkable with no GPU in the room | | `flutter3d_webgpu` | WebGPU in a browser that has an adapter. Reached by `--dart-define=FLUTTER3D_WEBGPU=true`, because a build that can try it ships it | `flutter3d_conformance` is the suite each of them passed before it belonged in that table, and [Writing a HAL backend](/core/backends/) is the guide for writing the fifth. diff --git a/site/content/core/rendering.md b/site/content/core/rendering.md index cc2b879df..b64a8b45a 100644 --- a/site/content/core/rendering.md +++ b/site/content/core/rendering.md @@ -487,7 +487,7 @@ It is marched at half resolution and brought up by depth, so a halo behind a pil ```dart SkySettings( - enabled: true, // off by default: seventy-eight goldens are recorded against none + enabled: true, // off by default: seventy-nine goldens are recorded against none zenith: Vector3(0.10, 0.22, 0.52), horizon: Vector3(0.42, 0.50, 0.62), nadir: Vector3(0.06, 0.06, 0.07), // what fills the frame looking down at nothing diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 95bc4714b..b72a59795 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10883 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10886 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-nine scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/packages.md b/site/content/reference/packages.md index f7a0fc8f8..d87038740 100644 --- a/site/content/reference/packages.md +++ b/site/content/reference/packages.md @@ -60,7 +60,7 @@ A rasteriser written in Dart. `CpuDevice` implements the same HAL, plus PNG outp Not a fallback. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. -It is how seventy-eight golden scenes are checkable with no GPU in the room, and it is a dev dependency of every game because three shipped bugs would have been caught by rendering a single frame in a test. It stopped being *only* a dev dependency once `flutter3d_app` started reaching for it as the runtime fallback when Impeller will not start, which makes it a real production path now, if a last-resort one. +It is how seventy-nine golden scenes are checkable with no GPU in the room, and it is a dev dependency of every game because three shipped bugs would have been caught by rendering a single frame in a test. It stopped being *only* a dev dependency once `flutter3d_app` started reaching for it as the runtime fallback when Impeller will not start, which makes it a real production path now, if a last-resort one. ### `flutter3d_conformance` The suite any fourth backend would have to pass before it counted as one, plus the cross-backend comparison with per-scene budgets. diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index a336f562f..30d550ef6 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10883 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10886 tests need a GPU. --- # Testing -10883 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10886 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -12,7 +12,7 @@ description: Four independent golden sets, mutation-checking every new test, det | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | | `flutter3d_sim` | 591 | | `pad_input` | 67 | -| `flutter3d_lab` | 15 | | `flutter3d_core` | 735 | +| `flutter3d_lab` | 15 | | `flutter3d_core` | 736 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | | `flutter3d_game_shooter` | 340 | | `flutter3d_audio_core` | 55 | @@ -20,7 +20,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_racing` | 223 | | `flutter3d_webgl` | 62 | | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 63 | | `apps/flutter3d_demo_platformer` | 202 | | `flutter3d_impeller` | 64 | -| `flutter3d_cpu` | 328 | | `apps/flutter3d_demo_strategy` | 50 | +| `flutter3d_cpu` | 330 | | `apps/flutter3d_demo_strategy` | 50 | | `apps/flutter3d_editor` | 224 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10857 rather than 10883: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10860 rather than 10886: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. @@ -52,7 +52,7 @@ tool/ci.sh # shaders, analyze, every test ## Four independent golden sets, not one -Seventy-eight scenes are rendered four times: through Impeller, through the software rasteriser in `flutter3d_cpu`, through WebGL2, and through WebGPU, the last two in a driven browser. Each backend is held to zero differing pixels against its own set, with a per-channel tolerance of 8. +Seventy-nine scenes are rendered four times: through Impeller, through the software rasteriser in `flutter3d_cpu`, through WebGL2, and through WebGPU, the last two in a driven browser. Each backend is held to zero differing pixels against its own set, with a per-channel tolerance of 8. The browser sets are recorded when a branch lands, not beside it (`golden_web.sh` holds one fixed port for the whole of its run), so a new scene is in the other sets for as long as that takes. Which scenes, and what they are waiting for, is `_provisional` in `flutter3d_webgl/test/cross_backend_test.dart`: the comparison is skipped with the reason printed instead of quietly missing, and the check beside it fails the moment a reference lands and the name is still there. @@ -65,7 +65,7 @@ The browser sets are recorded when a branch lands, not beside it (`golden_web.sh {{golden3 shadow-teapot | One scene, three sets: a GPU through Metal, a rasteriser written in Dart, and a browser. The pictures on this site are the Impeller set.}}
    -

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps seventy-eight scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    +

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps seventy-nine scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    `cross_backend_test.dart` compares them with per-scene budgets, and any new backend has to pass `flutter3d_conformance` before it counts as one. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 875c1cea2..e35fc6e2d 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -233,6 +233,14 @@ instead. | P6 | Debug views (material channels, UV, NaN, split wipe) and render stats | `render/debug_view.dart`, `render_stats.dart` (0.24) | viewport shading, NaN scan in `sim_mcp` | S | | P7 | Orthographic everywhere, per-draw ranges, alpha to coverage, render order | 0.24 commits | orthographic exists; check every effect | S | +**P1 done.** `AntiAliasSettings(method: EdgeSmoothing.smaa)`: edges, area +weights and neighbourhood blend as three stages on all four backends, the +software ones mirrored line for line, the orthogonal area table generated by +`tool/make_tables.dart` as the engine's `smaaArea`. `smaa-teapot` is recorded +in all four sets (CPU and WebGPU match Impeller to the pixel, WebGL2 0.59%, +all on edges). *Left:* the diagonal search and corner rounding of the paper, +and SMAA's 2x and 4x modes, which ride on the temporal jitter. + Already ours and not theirs, to keep: volumetric fog, weighted blended OIT, motion blur, local exposure and HDR out, EVSM, impostors, Hi-Z and software occlusion, generated LOD, WebGPU, the CPU backend, retargeting. diff --git a/tool/ci.sh b/tool/ci.sh index de3f886d2..654ea9c5f 100755 --- a/tool/ci.sh +++ b/tool/ci.sh @@ -9,7 +9,7 @@ # # What it does NOT do, stated so the gap is not mistaken for coverage: # -# * The Impeller half of the golden set. Those seventy-eight scenes render through +# * The Impeller half of the golden set. Those seventy-nine scenes render through # flutter_gpu and need a real device, so they run from # packages/flutter3d/tool/golden.sh on a machine with a GPU. The software # half runs here, and cross_backend_test.dart compares the two committed From 4154005041dd9645585fe9bb8e096e4d4af328c2 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 04:11:43 +0300 Subject: [PATCH 031/141] The lens bends the picture, flares, and takes a grading tool's table LookSettings.distortion bends the frame radially, barrel above nought and pincushion below, held on the frame's border. The composite bends its coordinate once before reading anything, so the glow, the occlusion and the local exposure bend with the scene while the vignette and the grain stay where they were. BloomSettings.lensFlare throws a bright light's ghosts and a halo across the middle of the frame. It is drawn from the glow and added to it before the composite, so only what blooms flares and the flare is tone mapped with the rest of the light. CubeLut reads a .cube file, 3D or 1D with its domain, into the strip the composite grades through, and uploads it. lens-flare and lens-distortion are recorded in all four reference sets. --- ARCHITECTURE.md | 8 +- README.md | 2 +- .../example/lib/src/spike/backend_web.dart | 2 +- .../example/lib/src/spike/golden_runner.dart | 2 +- .../example/lib/src/spike/golden_scenes.dart | 19 + .../example/lib/src/spike/golden_stages.dart | 40 + .../lib/src/spike/golden_store_web.dart | 4 +- packages/flutter3d/test/ambient_test.dart | 2 +- packages/flutter3d/test/anisotropy_test.dart | 4 +- packages/flutter3d/test/auto_batch_test.dart | 4 +- packages/flutter3d/test/fog_test.dart | 2 +- .../test/goldens/lens-distortion.png | Bin 0 -> 35296 bytes .../flutter3d/test/goldens/lens-flare.png | Bin 0 -> 21020 bytes packages/flutter3d/test/grade_test.dart | 2 +- packages/flutter3d/test/halation_test.dart | 2 +- .../test/irradiance_bounce_test.dart | 2 +- .../flutter3d/test/masked_shadow_test.dart | 2 +- .../test/material_vertex_stage_test.dart | 2 +- .../test/per_view_exposure_test.dart | 2 +- .../flutter3d/test/render_scale_test.dart | 2 +- packages/flutter3d/test/sharpen_test.dart | 2 +- .../flutter3d/test/sky_procedural_test.dart | 2 +- packages/flutter3d/test/ssao_blur_test.dart | 2 +- packages/flutter3d_core/CHANGELOG.md | 8 + packages/flutter3d_core/lib/formats.dart | 1 + .../src/engine/render/render_settings.dart | 110 ++- .../lib/src/engine/render/renderer.dart | 6 +- .../engine/render/renderer_frame_nodes.dart | 49 ++ .../src/engine/render/renderer_post_pass.dart | 36 +- .../engine/render/renderer_shadow_pass.dart | 2 +- .../lib/src/formats/cube_lut.dart | 236 ++++++ .../test/formats/cube_lut_test.dart | 139 ++++ .../formats/ktx2_supercompression_test.dart | 2 +- packages/flutter3d_cpu/CHANGELOG.md | 4 + .../lib/src/cpu_shaders_builtin.dart | 3 + .../lib/src/cpu_shaders_lens.dart | 74 ++ .../lib/src/cpu_shaders_post.dart | 47 +- .../flutter3d_cpu/lib/src/cpu_texture.dart | 2 +- .../flutter3d_cpu/lib/src/portable_log2.dart | 2 +- .../test/cross_backend_test.dart | 4 + .../test/goldens/lens-distortion.png | Bin 0 -> 35296 bytes .../flutter3d_cpu/test/goldens/lens-flare.png | Bin 0 -> 21020 bytes packages/flutter3d_cpu/test/lens_test.dart | 166 ++++ .../test/gpu_formats_test.dart | 2 +- packages/flutter3d_shaders/CHANGELOG.md | 2 + .../lib/flutter3d_shaders.dart | 1 + .../flutter3d_shaders/lib/stage_bindings.dart | 4 + .../flutter3d_shaders/lib/typed_blocks.dart | 21 + .../flutter3d_shaders/lib/uniform_blocks.dart | 7 + .../shaders/flutter3d.shaderbundle.json | 4 + .../shaders/post/composite.frag | 55 +- .../shaders/post/lens_flare.frag | 88 +++ packages/flutter3d_webgl/CHANGELOG.md | 2 + .../flutter3d_webgl/lib/engine_shaders.dart | 149 +++- .../test/cross_backend_test.dart | 4 + .../test/goldens/lens-distortion.png | Bin 0 -> 35284 bytes .../test/goldens/lens-flare.png | Bin 0 -> 21006 bytes .../test/lighting_models_test.dart | 2 +- packages/flutter3d_webgl/tool/golden_web.sh | 2 +- packages/flutter3d_webgpu/CHANGELOG.md | 2 + .../flutter3d_webgpu/lib/engine_shaders.dart | 712 ++++++++++++------ .../test/cross_backend_test.dart | 2 + .../test/goldens/lens-distortion.png | Bin 0 -> 35309 bytes .../test/goldens/lens-flare.png | Bin 0 -> 21033 bytes site/content/core/architecture.md | 4 +- site/content/core/backends.md | 4 +- site/content/core/index.md | 2 +- site/content/core/rendering.md | 2 +- site/content/quickstart.md | 2 +- site/content/reference/packages.md | 2 +- site/content/reference/testing.md | 14 +- tasks/0.9-engine-roadmap.md | 8 + tool/ci.sh | 2 +- tool/structure/rules.dart | 4 + 74 files changed, 1787 insertions(+), 316 deletions(-) create mode 100644 packages/flutter3d/test/goldens/lens-distortion.png create mode 100644 packages/flutter3d/test/goldens/lens-flare.png create mode 100644 packages/flutter3d_core/lib/src/formats/cube_lut.dart create mode 100644 packages/flutter3d_core/test/formats/cube_lut_test.dart create mode 100644 packages/flutter3d_cpu/lib/src/cpu_shaders_lens.dart create mode 100644 packages/flutter3d_cpu/test/goldens/lens-distortion.png create mode 100644 packages/flutter3d_cpu/test/goldens/lens-flare.png create mode 100644 packages/flutter3d_cpu/test/lens_test.dart create mode 100644 packages/flutter3d_shaders/shaders/post/lens_flare.frag create mode 100644 packages/flutter3d_webgl/test/goldens/lens-distortion.png create mode 100644 packages/flutter3d_webgl/test/goldens/lens-flare.png create mode 100644 packages/flutter3d_webgpu/test/goldens/lens-distortion.png create mode 100644 packages/flutter3d_webgpu/test/goldens/lens-flare.png diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 37e52ff17..49102d3ea 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2690,7 +2690,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10886 tests** across 43 packages and 10 applications | +| Unit tests | **10901 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | @@ -2730,7 +2730,7 @@ material parameter — is the case `doc/boundary-0.5.0.md` worked through for set belongs to whoever builds on the package rather than to the package itself. -**Golden render tests.** 79 scenes against **four complete independent +**Golden render tests.** 81 scenes against **four complete independent reference sets** — Impeller, the software rasteriser, WebGL2 and WebGPU — each held to zero differing pixels against its own set, with a per-channel tolerance of 8. @@ -3142,7 +3142,7 @@ metres. The directional light's cascades fit the view up to that distance and nothing beyond it casts — a level whose far end matters visually wants the number raised, and pays for it in texels. -**The web backend draws all seventy-nine golden scenes the way Impeller does**, +**The web backend draws all eighty-one golden scenes the way Impeller does**, between 0.01% and 0.42% of pixels differing by more than 8 per channel — the silhouette's worth of disagreement two rasterisers always have. Two of those numbers fell when the minification filter learned to read a sampler's @@ -3258,7 +3258,7 @@ and charge each of them the bytes, and neither is a change to make on somebody else's behalf. A build that wants it says so in one flag, and the engine's own example takes the same answer from `?backend=webgpu` in the URL — a query parameter rather than a define, because the browser golden stand's whole saving -is one dart2js run serving seventy-nine scenes and both browser backends. +is one dart2js run serving eighty-one scenes and both browser backends. **`flutter3d_shaders` is one text and no two backends take it the same way.** Impeller compiles the GLSL with `impellerc`; the WebGL2 generator translates it diff --git a/README.md b/README.md index 652849853..1e6dbceef 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10886 tests across forty-three packages and nine applications. The +There are 10901 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/packages/flutter3d/example/lib/src/spike/backend_web.dart b/packages/flutter3d/example/lib/src/spike/backend_web.dart index 6f7f66298..134f53299 100644 --- a/packages/flutter3d/example/lib/src/spike/backend_web.dart +++ b/packages/flutter3d/example/lib/src/spike/backend_web.dart @@ -13,7 +13,7 @@ import 'golden_store.dart'; /// /// Read from the URL, exactly the way the golden scene and the record/compare /// direction are read, and for exactly the same arithmetic: one dart2js run -/// serves seventy-nine scenes because the scene is a query parameter, and one +/// serves eighty-one scenes because the scene is a query parameter, and one /// dart2js run serves the browser backends because this is one too. A /// `--dart-define` here would be a build per backend, which is the whole saving /// of the browser golden stand spent on a single word. diff --git a/packages/flutter3d/example/lib/src/spike/golden_runner.dart b/packages/flutter3d/example/lib/src/spike/golden_runner.dart index 265d7a878..81e951f83 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_runner.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_runner.dart @@ -122,7 +122,7 @@ final class GoldenRunner { scene, // The store's answer wins where it has one. A desktop run takes the // direction from the environment and a browser run from the URL, for the - // same reason on both: one build has to serve seventy-nine scenes in both + // same reason on both: one build has to serve eighty-one scenes in both // directions, and anything the compiler sees is another build. update: updateOverride ?? diff --git a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart index e6681930c..9bb98dd60 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart @@ -1080,6 +1080,25 @@ final List kGoldenScenes = [ configure: GoldenStages.smaaTeapot, ), + // P2. A bright panel's reflections thrown across the middle of the frame. + const GoldenScene( + name: 'lens-flare', + source: 'Cube', + lights: {'none'}, + shadows: false, + ground: false, + stage: GoldenStages.lensFlare, + configure: GoldenStages.lensFlareSettings, + ), + + // P2. `shadow-teapot` through a barrel lens. + const GoldenScene( + name: 'lens-distortion', + source: 'obj: Teapot', + bloom: false, + configure: GoldenStages.lensDistortion, + ), + // R6. A turning wheel blurred along its own motion. const GoldenScene( name: 'motion-blur-spin', diff --git a/packages/flutter3d/example/lib/src/spike/golden_stages.dart b/packages/flutter3d/example/lib/src/spike/golden_stages.dart index 75ba59058..e7038015b 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_stages.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_stages.dart @@ -1130,6 +1130,46 @@ abstract final class GoldenStages { ), ); + // ------------------------------------------------------------------ P2 + + /// `lens-flare`: one small, very bright panel up and to the left on black, + /// and the ghosts and ring its reflections throw across the middle of the + /// frame. The glow is kept tight so the ghosts read as discs. + static Future lensFlare(GoldenStage stage) async { + final device = stage.device; + return GoldenStaged( + nodes: [ + _slab( + device, + Vector3(0.25, 0.25, 0.05), + Vector3(-1.1, 0.7, -3.0), + Material( + baseColor: Vector4(0.0, 0.0, 0.0, 1.0), + emissive: Vector3(30.0, 26.0, 20.0), + ), + )..shadowCasting = ShadowCastingMode.off, + ], + everyFrame: (_, _) => + _look(stage.camera, Vector3(0.0, 0.0, 1.0), Vector3(0.0, 0.0, -3.0)), + ); + } + + static RenderSettings lensFlareSettings(RenderSettings settings) => + settings.copyWith( + bloom: settings.bloom.copyWith( + enabled: true, + intensity: 0.05, + levels: 3, + scatter: 0.5, + lensFlare: const LensFlareSettings(enabled: true, intensity: 3.0), + ), + ); + + /// `lens-distortion`: `shadow-teapot` through a barrel lens, the floor's + /// straight edges bowed and the corners held where they were. + static RenderSettings lensDistortion(RenderSettings settings) => + settings.copyWith(look: settings.look.copyWith(distortion: 0.3)); + // ------------------------------------------------------------------ R6 /// `motion-blur-spin`: a wheel of three spokes round a hub, turning at a diff --git a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart index ac73959df..1cadeadf3 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart @@ -40,7 +40,7 @@ void reportLine(String message) { /// The scene named in the page's URL, if any. /// /// A run-time choice here, where the desktop path takes a compile-time define. -/// The reason is arithmetic: the suite is seventy-nine scenes, and rebuilding +/// The reason is arithmetic: the suite is eighty-one scenes, and rebuilding /// the bundle for each is seventy-eight dart2js runs to compare seventy-eight /// pictures. One build and seventy-eight navigations is the same information /// in a fraction of the time. @@ -58,7 +58,7 @@ String? get sceneOverride { /// Whether this run records rather than compares, from the page's URL. /// /// A run-time choice for the same reason [sceneOverride] is one: the suite is -/// seventy-nine scenes and rebuilding for each would be seventy-eight dart2js +/// eighty-one scenes and rebuilding for each would be seventy-eight dart2js /// runs. One build serves both directions, and the URL says which. bool get updateOverride => Uri.base.queryParameters['update'] == '1'; diff --git a/packages/flutter3d/test/ambient_test.dart b/packages/flutter3d/test/ambient_test.dart index 7474816e7..fcb7bdae4 100644 --- a/packages/flutter3d/test/ambient_test.dart +++ b/packages/flutter3d/test/ambient_test.dart @@ -141,7 +141,7 @@ void main() { // approximately, bit for bit. // // Mutation: default either end of the hemisphere to anything but white. - // Seventy-nine goldens move at once. + // Eighty-one goldens move at once. final lit = await _pixels(_ball(), _noSky); final top = _band(lit, 0.15, 0.35); final bottom = _band(lit, 0.65, 0.85); diff --git a/packages/flutter3d/test/anisotropy_test.dart b/packages/flutter3d/test/anisotropy_test.dart index 6c2b0a7e0..88b94a27f 100644 --- a/packages/flutter3d/test/anisotropy_test.dart +++ b/packages/flutter3d/test/anisotropy_test.dart @@ -24,7 +24,7 @@ /// which spends the taps and throws away what they bought — contrast drops /// *below* the trilinear baseline and the first assertion fails; and letting /// a sampler that asked for one reach the new path at all, which would move a -/// backend seventy-nine golden scenes are recorded on. +/// backend eighty-one golden scenes are recorded on. /// /// **One they do not catch, said rather than hidden:** taking the taps along /// the *short* axis instead of the long one passes every line here. The @@ -213,7 +213,7 @@ void main() { }); test('asking for one tap is the old sampler exactly', () async { - // What makes this safe to add to the backend seventy-nine golden scenes are + // What makes this safe to add to the backend eighty-one golden scenes are // recorded on. Not "close": the same bytes, because with `anisotropy` at // one the new path is not reached at all. expect(await _floorAt(1), await _floorAt(1)); diff --git a/packages/flutter3d/test/auto_batch_test.dart b/packages/flutter3d/test/auto_batch_test.dart index 1b3ee79fc..9f17f3441 100644 --- a/packages/flutter3d/test/auto_batch_test.dart +++ b/packages/flutter3d/test/auto_batch_test.dart @@ -22,7 +22,7 @@ /// three backends.** It computes in Dart doubles; Impeller, WebGL and WebGPU /// compute in 32-bit floats, where those two expressions have far less room /// before they part. Nothing here can run them, so the setting stays off by -/// default — which is also what keeps the seventy-nine goldens where they are. +/// default — which is also what keeps the eighty-one goldens where they are. library; import 'dart:math' as math; @@ -191,7 +191,7 @@ void main() { }); test('nothing is batched unless it is asked for', () async { - // Which is what keeps the seventy-nine goldens where they are: the default + // Which is what keeps the eighty-one goldens where they are: the default // frame is the frame it always was, to the byte. final device = _device(); final scene = _field(device, count: 100); diff --git a/packages/flutter3d/test/fog_test.dart b/packages/flutter3d/test/fog_test.dart index b6761f91e..1370893ea 100644 --- a/packages/flutter3d/test/fog_test.dart +++ b/packages/flutter3d/test/fog_test.dart @@ -151,7 +151,7 @@ void main() { test('no fog is byte-identical to the fog nobody asked for', () async { // The early return at zero density is not an optimisation. The golden sets - // — seventy-nine scenes, zero-pixel threshold, three backends — are all + // — eighty-one scenes, zero-pixel threshold, three backends — are all // recorded with the default `FogSettings()`, and this is the property that // lets them stay recorded. final none = FogSettings(); diff --git a/packages/flutter3d/test/goldens/lens-distortion.png b/packages/flutter3d/test/goldens/lens-distortion.png new file mode 100644 index 0000000000000000000000000000000000000000..c97f9bf46e81a092c649bce8b795623d3c649a1b GIT binary patch literal 35296 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
    KW_!lx6ySgV|izWx4+2!9PY^SmU4lXVy-Eh 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 bCqYK7Cn8$nLZCqIQRK3X> _ramp(LookSettings look, {int width = 256}) async { void main() { test('every default is an exact identity', () async { - // The promise seventy-nine goldens rest on. Neutral for these three is + // The promise eighty-one goldens rest on. Neutral for these three is // (0,0,0) added, (1,1,1) as an exponent and (1,1,1) multiplied, and the // composite has to take all three without moving a byte. final plain = await _ramp(const LookSettings()); diff --git a/packages/flutter3d/test/halation_test.dart b/packages/flutter3d/test/halation_test.dart index b26b235ff..623bb87af 100644 --- a/packages/flutter3d/test/halation_test.dart +++ b/packages/flutter3d/test/halation_test.dart @@ -87,7 +87,7 @@ void main() { test('zero is an exact identity, and is the default', () async { // The multiplier is one on every channel at zero, so the chain comes out - // byte for byte as it did — which is what seventy-nine goldens need. + // byte for byte as it did — which is what eighty-one goldens need. expect(const BloomSettings().halation, 0.0); final a = await _glow(halation: 0.0); final b = await _glow(halation: 0.0); diff --git a/packages/flutter3d/test/irradiance_bounce_test.dart b/packages/flutter3d/test/irradiance_bounce_test.dart index a697f4efc..6af1a908d 100644 --- a/packages/flutter3d/test/irradiance_bounce_test.dart +++ b/packages/flutter3d/test/irradiance_bounce_test.dart @@ -136,7 +136,7 @@ double _tint(Uint8List rgba) { void main() { test('off, the picture is the bytes it was', () async { // The clause every recorded frame depends on. A field is null by default, - // so this is the path seventy-nine goldens take. + // so this is the path eighty-one goldens take. final a = await _draw(field: false, redWall: true); final b = await _draw(field: false, redWall: true); expect(a, orderedEquals(b)); diff --git a/packages/flutter3d/test/masked_shadow_test.dart b/packages/flutter3d/test/masked_shadow_test.dart index 11e0532bd..74c3567f3 100644 --- a/packages/flutter3d/test/masked_shadow_test.dart +++ b/packages/flutter3d/test/masked_shadow_test.dart @@ -223,7 +223,7 @@ void main() { test('a caster with no map keeps the shadow it always cast', () async { // **The half that lets this land.** A material that is not cut out goes // through the stage it has always gone through, with no sampler in the - // pipeline and no texture bound per draw, so the seventy-nine goldens + // pipeline and no texture bound per draw, so the eighty-one goldens // recorded against that stage cannot move. Asking for MASK with no map to // read is the same case: there is nothing to cut out. final plain = await _frame(masked: false, withTexture: false); diff --git a/packages/flutter3d/test/material_vertex_stage_test.dart b/packages/flutter3d/test/material_vertex_stage_test.dart index 715be1530..406843ac4 100644 --- a/packages/flutter3d/test/material_vertex_stage_test.dart +++ b/packages/flutter3d/test/material_vertex_stage_test.dart @@ -172,7 +172,7 @@ void main() { // **The half that lets this land**, and the reason the seam is a null field // rather than a new required one: a material saying nothing about its vertex // stage goes through the pipeline it has always gone through, which is also - // why the seventy-nine goldens cannot move. + // why the eighty-one goldens cannot move. final withSeam = await _draw(lighting: _plain); final device = CpuDevice( diff --git a/packages/flutter3d/test/per_view_exposure_test.dart b/packages/flutter3d/test/per_view_exposure_test.dart index f0acbc6d8..f1f4ed8c6 100644 --- a/packages/flutter3d/test/per_view_exposure_test.dart +++ b/packages/flutter3d/test/per_view_exposure_test.dart @@ -157,7 +157,7 @@ void main() { // **The strongest form of "this costs nothing where it is not wanted".** // One view is the whole frame, so its rectangle is the whole histogram // and its exposure is the frame's. The composite takes its single-draw - // path, and seventy-nine goldens stay where they are. + // path, and eighty-one goldens stay where they are. final off = await frame( const RenderSettings(autoExposure: AutoExposureSettings(enabled: true)), ); diff --git a/packages/flutter3d/test/render_scale_test.dart b/packages/flutter3d/test/render_scale_test.dart index 4dd0bd26b..0e8d21b53 100644 --- a/packages/flutter3d/test/render_scale_test.dart +++ b/packages/flutter3d/test/render_scale_test.dart @@ -36,7 +36,7 @@ import 'package:vector_math/vector_math.dart'; void main() { test('one is the whole resolution, and is the default', () { - // Seventy-nine goldens are recorded at the size they asked for. A default + // Eighty-one goldens are recorded at the size they asked for. A default // that shrank anything would move every one of them. expect(const RenderSettings().renderScale, 1.0); expect(_drawn(1.0), (width: 64, height: 64)); diff --git a/packages/flutter3d/test/sharpen_test.dart b/packages/flutter3d/test/sharpen_test.dart index 4d2a2bb21..4ce12a063 100644 --- a/packages/flutter3d/test/sharpen_test.dart +++ b/packages/flutter3d/test/sharpen_test.dart @@ -95,7 +95,7 @@ int _steepest(List row) { void main() { test('zero is an exact identity, and is the default', () async { - // Seventy-nine goldens go through this pass. The shader returns the centre + // Eighty-one goldens go through this pass. The shader returns the centre // untouched at zero rather than running a kernel that rounds to nothing, // because "rounds to nothing" is a claim about the target's bit depth. expect(const AntiAliasSettings().sharpen, 0.0); diff --git a/packages/flutter3d/test/sky_procedural_test.dart b/packages/flutter3d/test/sky_procedural_test.dart index 3f22d5119..1d2357d77 100644 --- a/packages/flutter3d/test/sky_procedural_test.dart +++ b/packages/flutter3d/test/sky_procedural_test.dart @@ -167,7 +167,7 @@ double _mean(Uint8List rgba, {required int channel}) { void main() { _hardEdgedSun(); test('no sky is byte-identical to the sky nobody asked for', () async { - // The property seventy-nine goldens rest on: a frame with no sky in it is + // The property eighty-one goldens rest on: a frame with no sky in it is // the frame this renderer has always drawn. // // Mutation: drop the early return. The sky is drawn into every scene that diff --git a/packages/flutter3d/test/ssao_blur_test.dart b/packages/flutter3d/test/ssao_blur_test.dart index b1c103f5a..758573366 100644 --- a/packages/flutter3d/test/ssao_blur_test.dart +++ b/packages/flutter3d/test/ssao_blur_test.dart @@ -75,7 +75,7 @@ void main() { test('no taps is no pass, and is the default', () async { // The occlusion is already off by default, and this is a second // full-screen pass over it — so nothing pays for either until two things - // are switched on. Seventy-nine goldens depend on the frame being what it + // are switched on. Eighty-one goldens depend on the frame being what it // was. expect(const AmbientOcclusionSettings().blurTaps, 0); expect(await _frame(blurTaps: 0), await _frame(blurTaps: 0)); diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index ea18d2e96..c88679f44 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -1,5 +1,13 @@ ## Unreleased +- **The lens: distortion, flare, and tables from a grading tool.** + `LookSettings.distortion` bends the frame radially, barrel above nought + and pincushion below, held on its border, and everything laid over the + scene bends with it while the vignette and the grain stay put. + `BloomSettings.lensFlare` throws a bright light's ghosts and halo across + the middle of the frame, drawn from the glow so only what blooms flares. + `CubeLut` reads a `.cube` file, 3D or 1D with its domain, into the strip + `LookSettings.lut` grades through, and `upload` puts it on a device. - **SMAA 1x beside FXAA.** `AntiAliasSettings(method: EdgeSmoothing.smaa)` smooths the finished picture in three passes: the luma steps marked, the line behind each staircase rebuilt from where its run ends and which side diff --git a/packages/flutter3d_core/lib/formats.dart b/packages/flutter3d_core/lib/formats.dart index bf1da19fc..6d1ead382 100644 --- a/packages/flutter3d_core/lib/formats.dart +++ b/packages/flutter3d_core/lib/formats.dart @@ -28,6 +28,7 @@ export 'src/formats/animation/animation_mask.dart'; export 'src/formats/animation/animation_track.dart'; export 'src/formats/asset_resolver.dart'; export 'src/formats/asset_source.dart'; +export 'src/formats/cube_lut.dart'; export 'src/formats/document_compare.dart'; export 'src/formats/draco/draco.dart'; export 'src/formats/export_report.dart'; diff --git a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart index 5cebd0a5a..f3c7b5945 100644 --- a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart +++ b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart @@ -1268,7 +1268,7 @@ final class RenderSettings { /// eight-bit answer — `auto_batch_test.dart` holds a hundred cubes, turned and /// scaled, to byte equality. Impeller, WebGL and WebGPU compute in 32-bit /// floats, where those expressions have far less room before they part, and - /// nothing headless can run them. So the seventy-nine goldens keep the frame + /// nothing headless can run them. So the eighty-one goldens keep the frame /// they have, and an application that wants the draw calls back asks. /// /// Shadows and picking are unaffected: both walk the scene themselves and @@ -1821,6 +1821,9 @@ final class RenderSettings { // `R7`: measured on the resolved picture, applied in the composite. 'local exposure', 'bloom', + // `P2`: drawn from the glow and added to it, before the composite reads + // it. + 'lens flare', 'composite', // `R5`: the finished picture brought up to the asked-for size, before // the sharpening that follows it. @@ -1862,6 +1865,7 @@ final class RenderSettings { /// composite is where `tonemap: false` is honoured. static const Set pixelAlteringPasses = { 'bloom', + 'lens flare', 'ssao', // Multiplied into the ambient term in the composite beside the occlusion, // so it moves a measured pixel exactly as `ssao` does. @@ -2025,7 +2029,7 @@ final class DisplayTransform { /// **Everything here defaults to doing nothing, exactly.** Not nearly nothing: /// a vignette of zero multiplies by one and grain of zero adds zero, so a scene /// that asks for none of it composites to the same bytes it did before this -/// existed. Seventy-nine goldens depend on that being exact, and the composite +/// existed. Eighty-one goldens depend on that being exact, and the composite /// pass already keeps the same promise for ambient occlusion. /// /// Applied in the composite rather than as passes of their own, which is the @@ -2045,6 +2049,7 @@ final class LookSettings { this.vignetteRoundness = 1.0, this.grain = 0.0, this.chromaticAberration = 0.0, + this.distortion = 0.0, this.dither = 1.0 / 255.0, this.lift, this.gamma, @@ -2091,6 +2096,19 @@ final class LookSettings { /// visible without reading as a fault. final double chromaticAberration; + /// Radial lens distortion — `P2`: barrel above nought, pincushion below, + /// nought off exactly. The frame is bent by `1 + k·r²` about its middle on + /// its own aspect and scaled so its border stays on the frame: a barrel + /// holds the corners, a pincushion the edge nearest the middle. 0.1 is a + /// wide lens's gentle bow; past 0.5 it reads as a fisheye. + /// + /// **Everything laid over the scene bends with it** — the glow, the + /// occlusion, the contact shadow, the local exposure — because the + /// composite bends the coordinate once, before anything is read. The + /// vignette, the grain and the dither stay put: they are the film, and the + /// film does not move with the glass. + final double distortion; + /// Where black is **lifted to**, per channel, so the shadows move and white /// stays — `gfx-27n`. Null is neutral, the same as zero. Applied as /// `c * (1 - lift) + lift`; until 0.7.4 it was a plain add, which moved @@ -2167,6 +2185,8 @@ final class LookSettings { /// and N tall, blue selecting the slice, red running across it and green /// down. [buildIdentityLut] makes the one that changes nothing, which is /// what a test compares against and what somebody starts from. + /// `CubeLut.parse(...).upload(device)` makes one from a `.cube` file, the + /// format the grading tools write — `P2`. /// /// Null is not "a neutral table": nothing is sampled at all, and that is /// the difference [lutStrength] of zero also makes. @@ -2203,6 +2223,7 @@ final class LookSettings { vignette == 0.0 && grain == 0.0 && chromaticAberration == 0.0 && + distortion == 0.0 && whiteBalance == 0.0 && tint == 0.0 && _isNeutralTriple(lift, 0.0) && @@ -2223,6 +2244,7 @@ final class LookSettings { double? vignetteRoundness, double? grain, double? chromaticAberration, + double? distortion, double? dither, vm.Vector3? lift, vm.Vector3? gamma, @@ -2240,6 +2262,7 @@ final class LookSettings { vignetteRoundness: vignetteRoundness ?? this.vignetteRoundness, grain: grain ?? this.grain, chromaticAberration: chromaticAberration ?? this.chromaticAberration, + distortion: distortion ?? this.distortion, dither: dither ?? this.dither, lift: lift ?? this.lift, gamma: gamma ?? this.gamma, @@ -2577,10 +2600,15 @@ final class BloomSettings { this.referenceHeight = 0, this.halation = 0.0, this.scatter = 1.0, + this.lensFlare = const LensFlareSettings(), }); final bool enabled; + /// Ghosts and a halo thrown from what blooms — `P2`. Off by default, and + /// off whenever the bloom is: it is drawn from the glow. + final LensFlareSettings lensFlare; + /// Luminance above which a pixel starts to bloom. One is display white, which /// is the only value with a physical meaning: below it nothing is clipping, /// above it the display cannot show the difference and a lens would scatter. @@ -2671,6 +2699,7 @@ final class BloomSettings { int? referenceHeight, double? halation, double? scatter, + LensFlareSettings? lensFlare, }) => BloomSettings( enabled: enabled ?? this.enabled, threshold: threshold ?? this.threshold, @@ -2681,5 +2710,82 @@ final class BloomSettings { referenceHeight: referenceHeight ?? this.referenceHeight, halation: halation ?? this.halation, scatter: scatter ?? this.scatter, + lensFlare: lensFlare ?? this.lensFlare, + ); +} + +/// The reflections a lens throws from a bright light — `P2`. +/// +/// **Drawn from the glow, and so only where something blooms.** The bloom +/// chain has already found what is bright and spread it; reading it again at +/// positions mirrored through the middle of the frame is what puts each ghost +/// where the light's reflection between two lens elements would land. A +/// frame with nothing over the bloom threshold has no flare, which is the +/// right answer, and a frame with the bloom off has none at all. +/// +/// The ghosts thin out towards the edges and none is drawn past the frame, +/// and each one's three channels are taken a little apart along its line, +/// the fringe a coated lens leaves on its reflections. The ring is every +/// light at [haloRadius] from the middle landing on the far side of it. +/// +/// Added into the glow, so the composite scales it by the bloom's own +/// intensity and tone maps it with the rest of the light. +/// +/// **A ghost is as soft as the glow it is drawn from.** Five levels at full +/// weight — the bloom's defaults — throw a small lamp's skirt half the frame +/// wide, and its reflections are as wide as that: a wash rather than a row +/// of discs. Fewer [BloomSettings.levels] or a [BloomSettings.scatter] below +/// one tighten the glow, and the ghosts with it. +final class LensFlareSettings { + const LensFlareSettings({ + this.enabled = false, + this.intensity = 1.0, + this.ghosts = 4, + this.ghostSpacing = 0.35, + this.haloRadius = 0.45, + this.halo = 0.5, + this.fringe = 0.004, + }); + + final bool enabled; + + /// How much flare, against the glow it is drawn from. + final double intensity; + + /// How many ghosts, up to eight. + final int ghosts; + + /// How far apart the ghosts fall, as a fraction of the way from the light + /// to the middle of the frame. + final double ghostSpacing; + + /// The halo's radius, as a fraction of the frame's height. + final double haloRadius; + + /// The halo's strength against the ghosts'. Nought draws no ring. + final double halo; + + /// How far apart a ghost's channels are taken, in screen widths. + final double fringe; + + /// Whether a flare is drawn at all. + bool get isActive => enabled && intensity > 0.0 && ghosts > 0; + + LensFlareSettings copyWith({ + bool? enabled, + double? intensity, + int? ghosts, + double? ghostSpacing, + double? haloRadius, + double? halo, + double? fringe, + }) => LensFlareSettings( + enabled: enabled ?? this.enabled, + intensity: intensity ?? this.intensity, + ghosts: ghosts ?? this.ghosts, + ghostSpacing: ghostSpacing ?? this.ghostSpacing, + haloRadius: haloRadius ?? this.haloRadius, + halo: halo ?? this.halo, + fringe: fringe ?? this.fringe, ); } diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer.dart b/packages/flutter3d_core/lib/src/engine/render/renderer.dart index f4dc4159e..d15dedec5 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer.dart @@ -743,6 +743,7 @@ final class Renderer implements RenderServices { final FragInfoBlock _fragInfo = FragInfoBlock(); final FxaaInfoBlock _fxaaInfo = FxaaInfoBlock(); final SmaaInfoBlock _smaaInfo = SmaaInfoBlock(); + final LensFlareInfoBlock _lensFlareInfo = LensFlareInfoBlock(); final EasuInfoBlock _easuInfo = EasuInfoBlock(); final LocalExposureInfoBlock _localExposureInfo = LocalExposureInfoBlock(); final LocalExposureBlurInfoBlock _localExposureBlurInfo = @@ -1522,7 +1523,7 @@ final class Renderer implements RenderServices { /// `gfx-76n`'s strength, in x. Neutral is zero, which the composite reads as /// a multiplier of exactly one — the same arrangement the occlusion's - /// strength has, and for the same reason: seventy-nine goldens go through this + /// strength has, and for the same reason: eighty-one goldens go through this /// block and "off" has to be a number the shader cancels, not one it nearly /// cancels. Float32List get _compositeContact => _compositeInfo.contact; @@ -2656,6 +2657,9 @@ final class Renderer implements RenderServices { ..addNode(resolve) ..addNode(_LocalExposureNode(this, s)) ..addNode(bloom) + // `P2`: the flare, drawn from the glow and added to it, so the + // composite reads both as one. + ..addNode(_LensFlareNode(this, s.bloom)) ..addNode(composite) ..addNode(easu) // And the smoothing after the composite, which is what lets it read a diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart index b4af82f96..5888fcbdd 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart @@ -1978,6 +1978,55 @@ final class _BloomNode extends RenderNode { } } +/// The flare thrown from the glow, added to it — `P2`. +/// +/// Registered after the bloom and before the composite, so it reads the glow +/// the chain finished and hands the composite the next version of it; with +/// the bloom off nothing produces the glow and this is culled with it. +final class _LensFlareNode extends RenderNode { + _LensFlareNode(this._renderer, this._settings); + + final Renderer _renderer; + final BloomSettings _settings; + + @override + String get name => 'lens flare'; + + @override + bool get isActive => + _settings.enabled && + _settings.intensity > 0.0 && + _settings.lensFlare.isActive && + _renderer.shaders['LensFlare'] != null; + + @override + List get reads => const [FrameResourceIds.bloom]; + + @override + List get writes => const [FrameResourceIds.bloom]; + + @override + void execute(NodeFrame frame) { + developer.Timeline.startSync('Renderer.lensFlare'); + final glow = frame.resources.texture(FrameResourceIds.bloom); + final target = frame.resources.transient( + RenderTargetSpec( + width: glow.width, + height: glow.height, + format: glow.format, + ), + ); + frame.resources.provide(FrameResourceIds.bloom, target); + _renderer._encodeLensFlare( + target: target, + glow: glow, + settings: _settings.lensFlare, + aspect: frame.width / math.max(frame.height, 1), + ); + developer.Timeline.finishSync(); + } +} + /// Tone map, sRGB and the debug overlay, as a graph node — the end of the post /// chain and the only pass that writes what is shown. /// diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart index bdb575b02..83805235e 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart @@ -205,7 +205,7 @@ extension _PostPasses on Renderer { _fxaaParams[3] = settings.blend.clamp(0.0, 1.0); // `gfx-29n`. Zero exactly when nobody asked: the shader returns the // centre untouched at zero rather than running a kernel that rounds to - // nothing, and seventy-nine goldens depend on that being the same bytes. + // nothing, and eighty-one goldens depend on that being the same bytes. // // After a temporal resolve the robust kernel, at the resolve's own // strength — `R2`: what softens a resolved picture is the history, and @@ -314,6 +314,36 @@ extension _PostPasses on Renderer { ); } + /// The ghosts and the halo [settings] asks for, drawn from [glow] and + /// added to it in [target] — `P2`. One full-screen draw. + void _encodeLensFlare({ + required TextureHandle target, + required TextureHandle glow, + required LensFlareSettings settings, + required double aspect, + }) { + _lensFlareInfo.params + ..[0] = math.max(settings.intensity, 0.0) + ..[1] = settings.ghosts.clamp(0, 8).toDouble() + ..[2] = settings.ghostSpacing + ..[3] = settings.haloRadius; + _lensFlareInfo.more + ..[0] = settings.fringe + ..[1] = math.max(settings.halo, 0.0) + ..[2] = aspect + ..[3] = 0.0; + drawFullscreen( + FullscreenDraw( + target: target, + fragment: shaders['LensFlare']!, + textures: {'bloom_texture': glow}, + uniforms: >{ + _lensFlareInfo.name: _lensFlareInfo.members, + }, + ), + ); + } + /// `R7`: the three exposures' weights from [scene] at [target]'s size, /// blurred across and down, the second blur writing the exposure in stops /// into [target]. @@ -1648,7 +1678,7 @@ extension _PostPasses on Renderer { // itself — then one draw per view, scissored to its own rectangle, so the // exposure in the uniform is the one that view metered. With per-view // metering off, or with a single view, this is the one full-frame draw it - // has always been and the bytes are the bytes seventy-nine goldens hold. + // has always been and the bytes are the bytes eighty-one goldens hold. final perView = settings.autoExposure.enabled && settings.autoExposure.perView && @@ -1761,6 +1791,8 @@ extension _PostPasses on Renderer { _compositeContact[3] = localExposure == null ? 0.0 : settings.localExposure.strength.clamp(0.0, 1.0); + // `P2`: the lens's bend, nought off exactly. + _compositeInfo.lens[0] = settings.look.distortion; pass.bindTexture( compositeShader, 'local_exposure_texture', diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart index 1261e256c..6d76a23dc 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart @@ -1063,7 +1063,7 @@ extension _ShadowPasses on Renderer { final skinned = skeleton != null; // `gfx-60n`. A cut-out caster goes through a stage with a sampler in // it; everything else keeps the stage it has always had, which is why - // the masked half costs the common path nothing and why seventy-nine + // the masked half costs the common path nothing and why eighty-one // goldens recorded against the plain stage cannot move. final masked = maskedShadowShader != shadowShader && _castsMasked(node); final kind = diff --git a/packages/flutter3d_core/lib/src/formats/cube_lut.dart b/packages/flutter3d_core/lib/src/formats/cube_lut.dart new file mode 100644 index 000000000..f068721eb --- /dev/null +++ b/packages/flutter3d_core/lib/src/formats/cube_lut.dart @@ -0,0 +1,236 @@ +/// A colour table in the `.cube` format — `P2`. +/// +/// **The format every grading tool writes**: Resolve, Premiere, Photoshop and +/// OCIO all export it. Plain text: a few keywords, then one line of three +/// numbers per entry, red running fastest, then green, then blue. A table is +/// either 3D (`LUT_3D_SIZE N`, N³ lines) or 1D (`LUT_1D_SIZE N`, one curve +/// per channel), and `DOMAIN_MIN`/`DOMAIN_MAX` say what input the first and +/// last entries stand for. +/// +/// The engine grades through a strip — N slices of N×N, `LookSettings.lut` — +/// so [CubeLut.toStrip] resamples whatever the file holds into one, the same +/// layout `buildIdentityLut` writes. +library; + +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:flutter3d_hardware/flutter3d_hardware.dart'; + +/// A parsed `.cube` file. +final class CubeLut { + CubeLut._({ + required this.title, + required this.size, + required this.isOneD, + required this.domainMin, + required this.domainMax, + required this._values, + }); + + /// Reads [text], a `.cube` file's contents. Throws a [FormatException] + /// naming the line when it is not one: a missing size, a line that is not + /// three numbers, too few or too many entries, a domain that is empty. + factory CubeLut.parse(String text) { + String? title; + int? size3; + int? size1; + var domainMin = (0.0, 0.0, 0.0); + var domainMax = (1.0, 1.0, 1.0); + final values = []; + final lines = text.split(RegExp(r'\r\n|\r|\n')); + + (double, double, double) triple(List words, int line) { + if (words.length != 3) { + throw FormatException('line $line: expected three numbers'); + } + final parsed = words.map(double.tryParse).toList(); + if (parsed.contains(null)) { + throw FormatException( + 'line $line: "${words.join(' ')}" is not three ' + 'numbers', + ); + } + return (parsed[0]!, parsed[1]!, parsed[2]!); + } + + for (var i = 0; i < lines.length; i++) { + final line = i + 1; + final trimmed = lines[i].trim(); + if (trimmed.isEmpty || trimmed.startsWith('#')) continue; + final words = trimmed.split(RegExp(r'\s+')); + switch (words.first) { + case 'TITLE': + final quoted = RegExp(r'"(.*)"').firstMatch(trimmed); + title = quoted?.group(1) ?? words.skip(1).join(' '); + case 'LUT_3D_SIZE': + size3 = int.tryParse(words.length == 2 ? words[1] : ''); + if (size3 == null || size3 < 2 || size3 > 256) { + throw FormatException('line $line: LUT_3D_SIZE must be 2 to 256'); + } + case 'LUT_1D_SIZE': + size1 = int.tryParse(words.length == 2 ? words[1] : ''); + if (size1 == null || size1 < 2 || size1 > 65536) { + throw FormatException('line $line: LUT_1D_SIZE must be 2 to 65536'); + } + case 'DOMAIN_MIN': + domainMin = triple(words.sublist(1), line); + case 'DOMAIN_MAX': + domainMax = triple(words.sublist(1), line); + // Written by some tools and meaningless to a table read here. + case 'LUT_3D_INPUT_RANGE' || 'LUT_1D_INPUT_RANGE': + final range = words.skip(1).map(double.tryParse).toList(); + if (range.length != 2 || range.contains(null)) { + throw FormatException('line $line: an input range is two numbers'); + } + domainMin = (range[0]!, range[0]!, range[0]!); + domainMax = (range[1]!, range[1]!, range[1]!); + default: + final (r, g, b) = triple(words, line); + values + ..add(r) + ..add(g) + ..add(b); + } + } + + if (size3 != null && size1 != null) { + throw const FormatException('a table is 3D or 1D, not both'); + } + final size = size3 ?? size1; + if (size == null) { + throw const FormatException('no LUT_3D_SIZE or LUT_1D_SIZE'); + } + final expected = size3 != null ? size * size * size : size; + if (values.length != expected * 3) { + throw FormatException( + 'expected $expected entries for a ' + '${size3 != null ? '3D' : '1D'} table of $size, found ' + '${values.length ~/ 3}', + ); + } + if (domainMax.$1 <= domainMin.$1 || + domainMax.$2 <= domainMin.$2 || + domainMax.$3 <= domainMin.$3) { + throw const FormatException('DOMAIN_MAX must be above DOMAIN_MIN'); + } + return CubeLut._( + title: title, + size: size, + isOneD: size1 != null, + domainMin: domainMin, + domainMax: domainMax, + values: Float32List.fromList(values), + ); + } + + /// The file's `TITLE`, when it has one. + final String? title; + + /// Entries per axis: N of N³ for a 3D table, the curve's length for 1D. + final int size; + + /// Whether this is three curves rather than a cube. + final bool isOneD; + + /// The input the first and last entries stand for, per channel. + final (double, double, double) domainMin; + final (double, double, double) domainMax; + + final Float32List _values; + + /// What the table makes of ([r], [g], [b]): trilinear between the eight + /// nearest entries of a 3D table, linear along each curve of a 1D one. + /// Inputs outside the domain are held to its edge. + (double, double, double) lookup(double r, double g, double b) { + double at(double value, double min, double max) => + ((value - min) / (max - min)).clamp(0.0, 1.0) * (size - 1); + final x = at(r, domainMin.$1, domainMax.$1); + final y = at(g, domainMin.$2, domainMax.$2); + final z = at(b, domainMin.$3, domainMax.$3); + if (isOneD) { + double curve(double position, int channel) { + final lower = position.floor(); + final upper = math.min(lower + 1, size - 1); + final t = position - lower; + final a = _values[lower * 3 + channel]; + return a + (_values[upper * 3 + channel] - a) * t; + } + + return (curve(x, 0), curve(y, 1), curve(z, 2)); + } + final x0 = x.floor(); + final y0 = y.floor(); + final z0 = z.floor(); + final x1 = math.min(x0 + 1, size - 1); + final y1 = math.min(y0 + 1, size - 1); + final z1 = math.min(z0 + 1, size - 1); + final tx = x - x0; + final ty = y - y0; + final tz = z - z0; + double channel(int c) { + double entry(int i, int j, int k) => + _values[((k * size + j) * size + i) * 3 + c]; + double along(int j, int k) => + entry(x0, j, k) + (entry(x1, j, k) - entry(x0, j, k)) * tx; + double across(int k) => along(y0, k) + (along(y1, k) - along(y0, k)) * ty; + return across(z0) + (across(z1) - across(z0)) * tz; + } + + return (channel(0), channel(1), channel(2)); + } + + /// [toStrip] uploaded through [device], ready for `LookSettings.lut`; null + /// when the device refuses a texture that size. + /// + /// ```dart + /// final table = CubeLut.parse(await rootBundle.loadString('grade.cube')); + /// final look = LookSettings(lut: table.upload(device)); + /// ``` + TextureHandle? upload(GraphicsDevice device, {int? stripSize}) { + final n = stripSize ?? (isOneD ? 33 : math.min(size, 64)); + return device.createTextureFromPixels( + width: n * n, + height: n, + format: TextureFormat.r8g8b8a8UNormInt, + pixels: ByteData.sublistView(toStrip(stripSize: n)), + ); + } + + /// This table as the strip `LookSettings.lut` grades through: [stripSize] + /// slices of [stripSize]×[stripSize], blue picking the slice, red across + /// and green down, eight bits a channel — `buildIdentityLut`'s layout. + /// + /// [stripSize] defaults to the table's own size for a 3D table, which + /// copies its entries exactly when the domain is the unit cube, and to 33 + /// for a 1D one. Outputs are clamped to 0…1: the strip is read after the + /// picture is encoded for the display, where nothing lies outside them. + Uint8List toStrip({int? stripSize}) { + final n = stripSize ?? (isOneD ? 33 : math.min(size, 64)); + if (n < 2) { + throw ArgumentError.value(n, 'stripSize', 'a table needs two ends'); + } + final width = n * n; + final pixels = Uint8List(width * n * 4); + final last = n - 1; + int byte(double v) => (v.clamp(0.0, 1.0) * 255.0).round(); + double input(int i, double min, double max) => min + (max - min) * i / last; + for (var blue = 0; blue < n; blue++) { + for (var green = 0; green < n; green++) { + for (var red = 0; red < n; red++) { + final (r, g, b) = lookup( + input(red, domainMin.$1, domainMax.$1), + input(green, domainMin.$2, domainMax.$2), + input(blue, domainMin.$3, domainMax.$3), + ); + final at = ((green * width) + blue * n + red) * 4; + pixels[at] = byte(r); + pixels[at + 1] = byte(g); + pixels[at + 2] = byte(b); + pixels[at + 3] = 255; + } + } + } + return pixels; + } +} diff --git a/packages/flutter3d_core/test/formats/cube_lut_test.dart b/packages/flutter3d_core/test/formats/cube_lut_test.dart new file mode 100644 index 000000000..8314ec2ae --- /dev/null +++ b/packages/flutter3d_core/test/formats/cube_lut_test.dart @@ -0,0 +1,139 @@ +/// `.cube` colour tables read into the strip the composite grades through — +/// `P2`. +/// +/// dart test test/formats/cube_lut_test.dart +library; + +import 'package:flutter3d_core/flutter3d_core.dart'; +import 'package:flutter3d_hardware/testing.dart'; +import 'package:test/test.dart'; + +/// A 3D table of [size] whose entry is what [map] makes of its input. +String _cube( + int size, + (double, double, double) Function(double r, double g, double b) map, { + String header = '', +}) { + final out = StringBuffer() + ..writeln('# written by a test') + ..writeln('TITLE "test table"') + ..write(header) + ..writeln('LUT_3D_SIZE $size'); + final last = size - 1; + for (var b = 0; b < size; b++) { + for (var g = 0; g < size; g++) { + for (var r = 0; r < size; r++) { + final (x, y, z) = map(r / last, g / last, b / last); + out.writeln('$x $y $z'); + } + } + } + return out.toString(); +} + +void main() { + test('the identity table is the identity strip, byte for byte', () { + // Mutation: run green fastest instead of red in `lookup`, and the strip + // comes out transposed. + final lut = CubeLut.parse(_cube(17, (r, g, b) => (r, g, b))); + expect(lut.title, 'test table'); + expect(lut.size, 17); + expect(lut.toStrip(), buildIdentityLut(size: 17)); + }); + + test('uploads as the strip the composite grades through', () { + final lut = CubeLut.parse(_cube(17, (r, g, b) => (r, g, b))); + final texture = lut.upload(FakeBackend())!; + expect(texture.width, 17 * 17); + expect(texture.height, 17); + }); + + test('a table is resampled into a strip of another size', () { + final lut = CubeLut.parse(_cube(9, (r, g, b) => (r, g, b))); + expect(lut.toStrip(stripSize: 33), buildIdentityLut()); + }); + + test('entries are interpolated between, and red runs fastest', () { + // Swaps red and blue: a channel-order mistake reads as this table being + // the identity. + final lut = CubeLut.parse(_cube(2, (r, g, b) => (b, g, r))); + final (r, g, b) = lut.lookup(0.25, 0.5, 0.75); + expect(r, closeTo(0.75, 1e-6)); + expect(g, closeTo(0.5, 1e-6)); + expect(b, closeTo(0.25, 1e-6)); + }); + + test('a domain wider than the unit cube is mapped onto it', () { + final lut = CubeLut.parse( + _cube( + 2, + (r, g, b) => (r, g, b), + header: 'DOMAIN_MIN 0 0 0\nDOMAIN_MAX 2 2 2\n', + ), + ); + // Input 1 is halfway along a domain of 0…2. + expect(lut.lookup(1.0, 1.0, 1.0).$1, closeTo(0.5, 1e-6)); + // Past the edge is held to it. + expect(lut.lookup(3.0, 3.0, 3.0).$1, closeTo(1.0, 1e-6)); + }); + + test('a 1D table is three curves', () { + const text = ''' +LUT_1D_SIZE 3 +0 0 0 +0.25 0.5 0.75 +1 1 1 +'''; + final lut = CubeLut.parse(text); + expect(lut.isOneD, isTrue); + final (r, g, b) = lut.lookup(0.5, 0.5, 0.5); + expect(r, closeTo(0.25, 1e-6)); + expect(g, closeTo(0.5, 1e-6)); + expect(b, closeTo(0.75, 1e-6)); + expect(lut.toStrip().length, 33 * 33 * 33 * 4); + }); + + group('refuses', () { + test('a table with no size', () { + expect( + () => CubeLut.parse('0 0 0\n1 1 1\n'), + throwsA(isA()), + ); + }); + + test('a table with the wrong number of entries', () { + expect( + () => CubeLut.parse('LUT_3D_SIZE 2\n0 0 0\n1 1 1\n'), + throwsA( + isA().having( + (e) => e.message, + 'message', + contains('expected 8 entries'), + ), + ), + ); + }); + + test('a line that is not three numbers, naming it', () { + expect( + () => CubeLut.parse('LUT_1D_SIZE 2\n0 0 0\n1 one 1\n'), + throwsA( + isA().having( + (e) => e.message, + 'message', + contains('line 3'), + ), + ), + ); + }); + + test('an empty domain', () { + expect( + () => CubeLut.parse( + 'LUT_1D_SIZE 2\nDOMAIN_MIN 1 1 1\nDOMAIN_MAX 1 1 1\n0 0 0\n1 1 1\n', + ), + throwsA(isA()), + ); + }); + }); +} diff --git a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart index e47ee9648..b629af3e4 100644 --- a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart +++ b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart @@ -157,7 +157,7 @@ void main() { }); test('an uncompressed file is untouched by any of this', () { - // The path seventy-nine goldens and every existing asset take. A level with + // The path eighty-one goldens and every existing asset take. A level with // no supercompression is handed back as a view on the file's own bytes, // with nothing copied and nothing decoded. final file = buildKtx2( diff --git a/packages/flutter3d_cpu/CHANGELOG.md b/packages/flutter3d_cpu/CHANGELOG.md index d77fb127b..3fd24c29e 100644 --- a/packages/flutter3d_cpu/CHANGELOG.md +++ b/packages/flutter3d_cpu/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +- **The lens in software.** The composite bends its coordinate as + `composite.frag` does, and `LensFlareShader` mirrors `lens_flare.frag`; + `lens-flare` and `lens-distortion` match Impeller's references to the + pixel. - **SMAA 1x in software.** `SmaaEdgesShader`, `SmaaWeightsShader` and `SmaaBlendShader` mirror `smaa_edges.frag`, `smaa_weights.frag` and `smaa_blend.frag` line for line, so the software backend smooths the same diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart index 02ca95c5d..17d053b75 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart @@ -54,6 +54,7 @@ import 'cpu_shaders_debug.dart'; import 'cpu_shaders_evsm.dart'; import 'cpu_shaders_impostor.dart'; import 'cpu_shaders_irradiance.dart'; +import 'cpu_shaders_lens.dart'; import 'cpu_shaders_lit.dart'; import 'cpu_shaders_mesh_vertex.dart'; import 'cpu_shaders_motion_blur.dart'; @@ -78,6 +79,7 @@ export 'cpu_shaders_contact_shadow.dart'; export 'cpu_shaders_debug.dart'; export 'cpu_shaders_irradiance.dart'; export 'cpu_shaders_layout.dart'; +export 'cpu_shaders_lens.dart'; export 'cpu_shaders_lighting.dart'; export 'cpu_shaders_lit.dart'; export 'cpu_shaders_mesh_vertex.dart'; @@ -238,6 +240,7 @@ Map builtinCpuShaders() { 'SmaaEdges': const CpuStage.fragment(SmaaEdgesShader()), 'SmaaWeights': const CpuStage.fragment(SmaaWeightsShader()), 'SmaaBlend': const CpuStage.fragment(SmaaBlendShader()), + 'LensFlare': const CpuStage.fragment(LensFlareShader()), 'LocalExposure': const CpuStage.fragment(LocalExposureShader()), 'LocalExposureBlur': const CpuStage.fragment(LocalExposureBlurShader()), 'ShadowDepth': const CpuStage.fragment(ShadowDepthShader()), diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_lens.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_lens.dart new file mode 100644 index 000000000..f88e98218 --- /dev/null +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_lens.dart @@ -0,0 +1,74 @@ +/// `lens_flare.frag`: the ghosts and the halo a lens throws from what is +/// bright in the frame, added to the glow — `P2`, line for line. +library; + +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:vector_math/vector_math.dart'; + +import 'cpu_shader.dart'; + +/// `lens_flare.frag`. +final class LensFlareShader implements CpuFragmentShader { + const LensFlareShader(); + + /// `Falloff`: all of a reflection in the middle, nothing at the corners. + static double _falloff(double u, double w, double power) { + final dx = 0.5 - u; + final dy = 0.5 - w; + final d = math.sqrt(dx * dx + dy * dy) / 0.70710678; + return math.pow(math.max(1.0 - d, 0.0), power).toDouble(); + } + + @override + Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) { + final bloom = b.textures['bloom_texture']; + if (bloom == null) return Vector4(0.0, 0.0, 0.0, 1.0); + final params = b.vec4('LensFlareInfo', 'params', Vector4.zero()); + final more = b.vec4('LensFlareInfo', 'more', Vector4.zero()); + + // `Fringed`: the glow at (u, w), its channels taken [spread] apart. + Vector3 fringed(double u, double w, double ax, double ay, double spread) => + Vector3( + bloom.sample(u + ax * spread, w + ay * spread).x, + bloom.sample(u, w).y, + bloom.sample(u - ax * spread, w - ay * spread).z, + ); + + final glow = bloom.sample(v[0], v[1]); + final flippedU = 1.0 - v[0]; + final flippedW = 1.0 - v[1]; + final towardU = (0.5 - flippedU) * params.z; + final towardW = (0.5 - flippedW) * params.z; + final reach = math.sqrt(towardU * towardU + towardW * towardW); + final alongU = reach > 1e-6 ? towardU / reach : 0.0; + final alongW = reach > 1e-6 ? towardW / reach : 0.0; + final spread = more.x; + + final flare = Vector3.zero(); + final ghosts = (params.y + 0.5).floor(); + for (var i = 0; i < 8; i++) { + if (i >= ghosts) break; + final u = flippedU + towardU * i; + final w = flippedW + towardW * i; + flare.add(fringed(u, w, alongU, alongW, spread) * _falloff(u, w, 10.0)); + } + + final aspect = math.max(more.z, 1e-4); + final haloU = flippedU + alongU / aspect * params.w; + final haloW = flippedW + alongW * params.w; + flare.add( + fringed(haloU, haloW, alongU, alongW, spread) * + (_falloff(haloU, haloW, 5.0) * more.y), + ); + + final intensity = params.x; + return Vector4( + glow.x + flare.x * intensity, + glow.y + flare.y * intensity, + glow.z + flare.z * intensity, + 1.0, + ); + } +} diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart index 977de6f7a..758010d8c 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart @@ -41,6 +41,19 @@ final class FullscreenVertexShader implements CpuVertexShader { final class CompositeShader implements CpuFragmentShader { const CompositeShader(); + /// `Distort` in `composite.frag`: radially by `1 + k·r²` on the frame's + /// aspect, held on the border — at the corners for a barrel, at the + /// nearest edge for a pincushion. + static (double, double) _distort(double u, double w, double k, double aspect) { + final a = math.max(aspect, 1e-4); + final dx = u - 0.5; + final dy = w - 0.5; + final r2 = dx * a * dx * a + dy * dy; + final border = k > 0.0 ? 0.25 * (a * a + 1.0) : 0.25 * math.min(a * a, 1.0); + final scale = (1.0 + k * r2) / (1.0 + k * border); + return (0.5 + dx * scale, 0.5 + dy * scale); + } + @override Vector4? run(Float32List v, ShaderBindings bindings, FragmentContext c) { final scene = bindings.textures['scene_texture']; @@ -62,15 +75,23 @@ final class CompositeShader implements CpuFragmentShader { Vector4(0.0, 0.0, 0.0, 1.0), ); + // `P2`: the lens's own bend, before anything is read, so everything laid + // over the scene bends with it — `Distort` in `composite.frag`. Nought + // leaves the coordinate the very value it was. + final lens = bindings.vec4('CompositeInfo', 'lens', Vector4.zero()); + final (u, w) = lens.x != 0.0 + ? _distort(v[0], v[1], lens.x, lookMore.w) + : (v[0], v[1]); + // Dispersion at sampling, because that is where a lens does it — see the // note in `composite.frag`, which this mirrors operation for operation. - final sampled = scene.sample(v[0], v[1]); + final sampled = scene.sample(u, w); var colour = Vector3(sampled.x, sampled.y, sampled.z); if (look.w > 0.0) { - final ox = (v[0] - 0.5) * look.w; - final oy = (v[1] - 0.5) * look.w; - colour.x = scene.sample(v[0] + ox, v[1] + oy).x; - colour.z = scene.sample(v[0] - ox, v[1] - oy).z; + final ox = (u - 0.5) * look.w; + final oy = (w - 0.5) * look.w; + colour.x = scene.sample(u + ox, w + oy).x; + colour.z = scene.sample(u - ox, w - oy).z; } // Occlusion first, then the glow — the order matters and it is the order @@ -97,7 +118,7 @@ final class CompositeShader implements CpuFragmentShader { final stops = bindings.textures['local_exposure_texture']; if (stops != null) { colour.scale( - math.pow(2.0, stops.sample(v[0], v[1]).x * contactInfo.w).toDouble(), + math.pow(2.0, stops.sample(u, w).x * contactInfo.w).toDouble(), ); } } @@ -108,10 +129,10 @@ final class CompositeShader implements CpuFragmentShader { final texel = bindings.vec4('CompositeInfo', 'ao_texel', Vector4.zero()); final hx = texel.x * 0.5; final hy = texel.y * 0.5; - final t0 = ao.sample(v[0] + hx, v[1] + hy); - final t1 = ao.sample(v[0] - hx, v[1] + hy); - final t2 = ao.sample(v[0] + hx, v[1] - hy); - final t3 = ao.sample(v[0] - hx, v[1] - hy); + final t0 = ao.sample(u + hx, w + hy); + final t1 = ao.sample(u - hx, w + hy); + final t2 = ao.sample(u + hx, w - hy); + final t3 = ao.sample(u - hx, w - hy); // The share left open is in a; the occlusion methods write it into // every channel. final occlusion = 0.25 * (t0.w + t1.w + t2.w + t3.w); @@ -136,13 +157,13 @@ final class CompositeShader implements CpuFragmentShader { final contactMap = bindings.textures['contact_shadow_texture']; final contactStrength = contactInfo.x.clamp(0.0, 1.0); if (contactMap != null && contactStrength > 0.0) { - final contact = contactMap.sample(v[0], v[1]).x; + final contact = contactMap.sample(u, w).x; shade *= 1.0 + (contact - 1.0) * contactStrength; } // Skipped at exactly one, which is what both settings off comes to: a // multiply by one is exact, so this is a shortcut rather than a difference, - // and it keeps the frames seventy-nine goldens hold untouched by arithmetic + // and it keeps the frames eighty-one goldens hold untouched by arithmetic // they never used to go through. if (shade != 1.0) colour.scale(shade); @@ -151,7 +172,7 @@ final class CompositeShader implements CpuFragmentShader { // make here either. final bloom = bindings.textures['bloom_texture']; if (bloom != null) { - final b = bloom.sample(v[0], v[1]); + final b = bloom.sample(u, w); colour += Vector3(b.x, b.y, b.z) * params.y; } if (bounced != null) colour += bounced; diff --git a/packages/flutter3d_cpu/lib/src/cpu_texture.dart b/packages/flutter3d_cpu/lib/src/cpu_texture.dart index d56d0c7bd..8862ee197 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_texture.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_texture.dart @@ -300,7 +300,7 @@ final class BoundTexture { // from the short one. // // **A sampler that did not ask is untouched**, which is what makes this - // safe to add to a backend seventy-nine golden scenes are recorded on: with + // safe to add to a backend eighty-one golden scenes are recorded on: with // `anisotropy` at one — the default everywhere in this engine — the // arithmetic below is not reached and the bytes are the ones that were // recorded. `anisotropic-floor` is the one scene that asks. diff --git a/packages/flutter3d_cpu/lib/src/portable_log2.dart b/packages/flutter3d_cpu/lib/src/portable_log2.dart index 712fada34..77fb51371 100644 --- a/packages/flutter3d_cpu/lib/src/portable_log2.dart +++ b/packages/flutter3d_cpu/lib/src/portable_log2.dart @@ -10,7 +10,7 @@ /// twenty-five five. `anisotropic-floor` is the scene built to sit exactly /// there: 2059 of its 172800 pixels differed between a reference recorded on /// macOS and the same code run on a Linux runner, and nothing else in -/// seventy-nine scenes differed at all. +/// eighty-one scenes differed at all. /// /// **The same rule the simulation already keeps.** `flutter3d_sim`'s /// `Portable` exists for this, and a step in that package is forbidden from diff --git a/packages/flutter3d_cpu/test/cross_backend_test.dart b/packages/flutter3d_cpu/test/cross_backend_test.dart index 5dc6351e5..6f13a3dd3 100644 --- a/packages/flutter3d_cpu/test/cross_backend_test.dart +++ b/packages/flutter3d_cpu/test/cross_backend_test.dart @@ -253,6 +253,10 @@ const Map _budgets = { // 0.000% measured, where `shadow-teapot` without it sits at half a // percent: the two rasterisers' staircases differ, and SMAA rebuilds the // same line behind both. + 'lens-flare': 0.01, + // 0.000% measured: the bend resamples the picture through the filter, + // which smooths the two rasterisers' staircases into one. + 'lens-distortion': 0.05, 'smaa-teapot': 0.05, 'evsm-soft': 0.5, // 0.069% measured: the air right round a torch, where the light falls off diff --git a/packages/flutter3d_cpu/test/goldens/lens-distortion.png b/packages/flutter3d_cpu/test/goldens/lens-distortion.png new file mode 100644 index 0000000000000000000000000000000000000000..c97f9bf46e81a092c649bce8b795623d3c649a1b GIT binary patch literal 35296 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 bCqYK7Cn8$nLZCqIQRK3X[ + RenderView(camera: camera, clearColor: Vector4(0.0, 0.0, 0.0, 1.0)), + ], + settings: settings, + ); + return Float32List.fromList(device.readHdrPixels(result.frame)); +} + +/// How many pixels are mostly lit. +int _lit(Float32List frame) { + var count = 0; + for (var i = 0; i < frame.length; i += 4) { + if (frame[i] > 0.5) count++; + } + return count; +} + +/// The red at pixel ([x], [y]). +double _at(Float32List frame, int x, int y) => frame[(y * _width + x) * 4]; + +const RenderSettings _plain = RenderSettings( + bloom: BloomSettings(enabled: false), + look: LookSettings(dither: 0.0), +); + +void main() { + group('distortion', () { + test('nought is the picture as it was', () { + expect( + _render(_plain.copyWith(look: _plain.look.copyWith(distortion: 0.0))), + _render(_plain), + ); + }); + + test('a barrel magnifies the middle and a pincushion shrinks it', () { + // Mutation: turn the sign of `k` in `Distort`, and the two swap. + // A card big enough that a pixel's worth of quantisation at its edge + // does not hide the bend. + int lit(double k) => _lit( + _render( + _plain.copyWith(look: _plain.look.copyWith(distortion: k)), + size: 1.2, + ), + ); + final straight = lit(0.0); + final barrel = lit(0.5); + final pincushion = lit(-0.5); + // ignore: avoid_print + print('lit: pincushion $pincushion, straight $straight, barrel $barrel'); + expect(barrel, greaterThan(straight * 1.15)); + expect(pincushion, lessThan(straight * 0.9)); + }); + }); + + group('flare', () { + // A small bright card up and to the left, well over the bloom + // threshold: its reflections land down and to the right. + RenderSettings flaring({bool flare = true, bool bloom = true}) => + _plain.copyWith( + // A tight glow: the flare is as soft as the glow it is drawn + // from, and the default five full levels throw a skirt half the + // frame wide. + bloom: BloomSettings( + enabled: bloom, + intensity: 0.02, + levels: 3, + scatter: 0.4, + lensFlare: LensFlareSettings(enabled: flare, intensity: 6.0), + ), + ); + Float32List light(RenderSettings settings) => + _render(settings, x: -0.9, y: 0.45, size: 0.15, colour: 6.0); + + /// What [after] adds over [before] in the quarter of the frame from + /// column [x0] and row [y0]. + double added(Float32List before, Float32List after, int x0, int y0) { + var sum = 0.0; + for (var y = y0; y < y0 + _height ~/ 2; y++) { + for (var x = x0; x < x0 + _width ~/ 2; x++) { + sum += _at(after, x, y) - _at(before, x, y); + } + } + return sum; + } + + test('throws light across the middle from a bright one', () { + // The light is in the top left quarter; its reflections land in the + // bottom right one, across the middle, and not beside it in the top + // right. Mutation: drop the flip in `lens_flare.frag`, and the light + // lands on itself instead. + final without = light(flaring(flare: false)); + final withFlare = light(flaring()); + final across = added(without, withFlare, _width ~/ 2, _height ~/ 2); + final beside = added(without, withFlare, _width ~/ 2, 0); + // ignore: avoid_print + print('flare added: $across across the middle, $beside beside'); + expect(across, greaterThan(1.0)); + expect(across, greaterThan(beside * 4.0)); + }); + + test('is nothing with the bloom off', () { + expect(light(flaring(bloom: false)), light(_plain)); + }); + + test('is nothing when nothing blooms', () { + // A card well under the bloom's knee: no glow, so no reflection of + // one. + Float32List dim({required bool flare}) => _render( + _plain.copyWith( + bloom: BloomSettings( + lensFlare: LensFlareSettings(enabled: flare, intensity: 6.0), + ), + ), + size: 0.15, + colour: 0.2, + ); + expect(dim(flare: true), dim(flare: false)); + }); + }); +} diff --git a/packages/flutter3d_impeller/test/gpu_formats_test.dart b/packages/flutter3d_impeller/test/gpu_formats_test.dart index a767ef7ba..55c496eaf 100644 --- a/packages/flutter3d_impeller/test/gpu_formats_test.dart +++ b/packages/flutter3d_impeller/test/gpu_formats_test.dart @@ -2,7 +2,7 @@ /// /// The hazard this exists for: a wrong mapping compiles, runs, and renders /// wrong only for the values a scene happens to use. The goldens cover the -/// values the seventy-nine scenes exercise and nothing else — `BlendFactor` has +/// values the eighty-one scenes exercise and nothing else — `BlendFactor` has /// fifteen members and the engine draws with two of them — so a mistake in the /// other thirteen would be found by a user rather than by us. /// diff --git a/packages/flutter3d_shaders/CHANGELOG.md b/packages/flutter3d_shaders/CHANGELOG.md index 126284fc1..792af92a2 100644 --- a/packages/flutter3d_shaders/CHANGELOG.md +++ b/packages/flutter3d_shaders/CHANGELOG.md @@ -1,5 +1,7 @@ ## Unreleased +- **`LensFlare`**, with the `LensFlareInfo` block, and a `lens` member + appended to `CompositeInfo` for the distortion. - **Three stages for SMAA 1x**: `SmaaEdges`, `SmaaWeights` and `SmaaBlend`, with the `SmaaInfo` block. A bundle must answer to them; the renderer falls back to FXAA when one does not. diff --git a/packages/flutter3d_shaders/lib/flutter3d_shaders.dart b/packages/flutter3d_shaders/lib/flutter3d_shaders.dart index 6220611fc..88f532798 100644 --- a/packages/flutter3d_shaders/lib/flutter3d_shaders.dart +++ b/packages/flutter3d_shaders/lib/flutter3d_shaders.dart @@ -44,6 +44,7 @@ const List kRequiredShaders = [ (name: 'SmaaEdges', fragment: true), (name: 'SmaaWeights', fragment: true), (name: 'SmaaBlend', fragment: true), + (name: 'LensFlare', fragment: true), (name: 'LocalExposure', fragment: true), (name: 'LocalExposureBlur', fragment: true), (name: 'Lambert', fragment: true), diff --git a/packages/flutter3d_shaders/lib/stage_bindings.dart b/packages/flutter3d_shaders/lib/stage_bindings.dart index a13335b87..592a1bb5c 100644 --- a/packages/flutter3d_shaders/lib/stage_bindings.dart +++ b/packages/flutter3d_shaders/lib/stage_bindings.dart @@ -139,6 +139,10 @@ stageBindings = blocks, Set samplers})>{ 'shadow_texture', }, ), + 'LensFlare': ( + blocks: {'LensFlareInfo'}, + samplers: {'bloom_texture'}, + ), 'LightShafts': ( blocks: {'FragCoordInfo', 'NoiseInfo', 'ShaftInfo'}, samplers: { diff --git a/packages/flutter3d_shaders/lib/typed_blocks.dart b/packages/flutter3d_shaders/lib/typed_blocks.dart index dff3f1830..24cd856a1 100644 --- a/packages/flutter3d_shaders/lib/typed_blocks.dart +++ b/packages/flutter3d_shaders/lib/typed_blocks.dart @@ -97,6 +97,9 @@ final class CompositeInfoBlock extends UniformBlock { /// `contact`: Vector4, at byte 128. final Float32List contact = Float32List(4); + /// `lens`: Vector4, at byte 144. + final Float32List lens = Float32List(4); + @override late final Map members = { 'params': params, @@ -108,6 +111,7 @@ final class CompositeInfoBlock extends UniformBlock { 'gamma': gamma, 'gain': gain, 'contact': contact, + 'lens': lens, }; } @@ -590,6 +594,23 @@ final class LayerInfoBlock extends UniformBlock { }; } +/// `LensFlareInfo`. +final class LensFlareInfoBlock extends UniformBlock { + LensFlareInfoBlock() : super('LensFlareInfo'); + + /// `params`: Vector4, at byte 0. + final Float32List params = Float32List(4); + + /// `more`: Vector4, at byte 16. + final Float32List more = Float32List(4); + + @override + late final Map members = { + 'params': params, + 'more': more, + }; +} + /// `LightListInfo`. final class LightListInfoBlock extends UniformBlock { LightListInfoBlock() : super('LightListInfo'); diff --git a/packages/flutter3d_shaders/lib/uniform_blocks.dart b/packages/flutter3d_shaders/lib/uniform_blocks.dart index e85ce1198..63a6ef2ea 100644 --- a/packages/flutter3d_shaders/lib/uniform_blocks.dart +++ b/packages/flutter3d_shaders/lib/uniform_blocks.dart @@ -210,6 +210,7 @@ uniformBlocks = >>{ 'gamma': (offset: 96, byteLength: 16, elements: 1, type: 'Vector4'), 'gain': (offset: 112, byteLength: 16, elements: 1, type: 'Vector4'), 'contact': (offset: 128, byteLength: 16, elements: 1, type: 'Vector4'), + 'lens': (offset: 144, byteLength: 16, elements: 1, type: 'Vector4'), }, 'FragCoordInfo': { 'origin': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), @@ -601,6 +602,12 @@ uniformBlocks = >>{ 'params3': (offset: 2560, byteLength: 16, elements: 1, type: 'Vector4'), }, }, + 'LensFlare': >{ + 'LensFlareInfo': { + 'params': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), + 'more': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), + }, + }, 'LightShafts': >{ 'FragCoordInfo': { 'origin': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), diff --git a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json index e2ae611db..293d70d06 100644 --- a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json +++ b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json @@ -91,6 +91,10 @@ "type": "fragment", "file": "shaders/post/smaa_blend.frag" }, + "LensFlare": { + "type": "fragment", + "file": "shaders/post/lens_flare.frag" + }, "MrtProbe": { "type": "fragment", "file": "shaders/post/mrt_probe.frag" diff --git a/packages/flutter3d_shaders/shaders/post/composite.frag b/packages/flutter3d_shaders/shaders/post/composite.frag index ab44a4346..085a56af5 100644 --- a/packages/flutter3d_shaders/shaders/post/composite.frag +++ b/packages/flutter3d_shaders/shaders/post/composite.frag @@ -116,9 +116,33 @@ uniform CompositeInfo { /// offset above unchanged and the four backends do not have to agree about /// anything they had not already agreed on. vec4 contact; + + /// x: radial lens distortion — `P2`, barrel above nought, pincushion + /// below, nought off exactly. y, z, w: unclaimed. Appended for the reason + /// `contact` was. + vec4 lens; } composite_info; +/// Where the lens bends [at] to: radially, by `1 + k·r²` on the frame's own +/// aspect, scaled so the frame's border stays on the frame — `P2`. +/// +/// **Normalised on the border, not on the corner alone.** A barrel (k above +/// nought) magnifies the middle and pulls everything else inwards, so the +/// corners are what reach furthest and are held there. A pincushion does the +/// opposite, and normalising it on the corners would send the middles of the +/// edges past the frame and stretch the last row of texels across them; it is +/// held at the edge nearest the middle instead, and the corners come in. +vec2 Distort(vec2 at, float k) { + float aspect = max(composite_info.look_more.w, 1e-4); + vec2 fromCentre = at - vec2(0.5); + vec2 onFrame = fromCentre * vec2(aspect, 1.0); + float r2 = dot(onFrame, onFrame); + float border = k > 0.0 ? 0.25 * (aspect * aspect + 1.0) + : 0.25 * min(aspect * aspect, 1.0); + return vec2(0.5) + fromCentre * (1.0 + k * r2) / (1.0 + k * border); +} + /// Rec. 709 luma, which is what the sRGB primaries weight to. float Luma(vec3 color) { return dot(color, vec3(0.2126, 0.7152, 0.0722)); } @@ -418,18 +442,25 @@ void main() { // // The offset grows from the centre outwards, which is what a real lens does: // a ray through the middle of the glass is not dispersed at all. + // + // `P2`: the lens's own bend, before anything is read, so the scene and + // everything laid over it — the glow, the occlusion, the contact shadow, + // the local exposure — bend together. Nought leaves `uv` the very value + // `v_uv` is, which every golden in the repository depends on. + float distortion = composite_info.lens.x; + vec2 uv = distortion != 0.0 ? Distort(v_uv, distortion) : v_uv; float dispersion = composite_info.look.w; vec4 scene; if (dispersion > 0.0) { - vec2 fromCentre = v_uv - vec2(0.5); + vec2 fromCentre = uv - vec2(0.5); vec2 step_uv = fromCentre * dispersion; - scene = texture(scene_texture, v_uv); - scene.r = texture(scene_texture, v_uv + step_uv).r; - scene.b = texture(scene_texture, v_uv - step_uv).b; + scene = texture(scene_texture, uv); + scene.r = texture(scene_texture, uv + step_uv).r; + scene.b = texture(scene_texture, uv - step_uv).b; } else { - scene = texture(scene_texture, v_uv); + scene = texture(scene_texture, uv); } - vec3 bloom = texture(bloom_texture, v_uv).rgb; + vec3 bloom = texture(bloom_texture, uv).rgb; // Four taps in a 2×2, which is not a general-purpose blur: the occlusion pass // rotates its kernel by the parity of the pixel, leaving a 2×2 pattern, and @@ -438,10 +469,10 @@ void main() { // one without the other either leaves the pattern or smears the contact // shadows this whole pass exists to draw. vec2 half_texel = composite_info.ao_texel.xy * 0.5; - vec4 occlusion = 0.25 * (texture(ao_texture, v_uv + vec2(half_texel.x, half_texel.y)) + - texture(ao_texture, v_uv + vec2(-half_texel.x, half_texel.y)) + - texture(ao_texture, v_uv + vec2(half_texel.x, -half_texel.y)) + - texture(ao_texture, v_uv + vec2(-half_texel.x, -half_texel.y))); + vec4 occlusion = 0.25 * (texture(ao_texture, uv + vec2(half_texel.x, half_texel.y)) + + texture(ao_texture, uv + vec2(-half_texel.x, half_texel.y)) + + texture(ao_texture, uv + vec2(half_texel.x, -half_texel.y)) + + texture(ao_texture, uv + vec2(-half_texel.x, -half_texel.y))); // The share left open is in a; the occlusion methods write it into every // channel, and the indirect one keeps its light in rgb. float ao = occlusion.a; @@ -461,7 +492,7 @@ void main() { // the whole point of a contact shadow is the first few centimetres at the // join, and a half-resolution one is the seam it exists to draw, blurred // away. - float contact = texture(contact_shadow_texture, v_uv).r; + float contact = texture(contact_shadow_texture, uv).r; ao *= mix(1.0, contact, clamp(composite_info.contact.x, 0.0, 1.0)); // Applied to the scene and **not** to the bloom, which is the whole reason @@ -484,7 +515,7 @@ void main() { // `R7`: each place of the scene at the exposure that shows it best, before // the glow is added and the curve applied. Bilinear from an eighth of the // frame: the stops were blurred wide, so there is no edge in them to keep. - float localStops = texture(local_exposure_texture, v_uv).r; + float localStops = texture(local_exposure_texture, uv).r; vec3 exposed = scene.rgb * exp2(localStops * composite_info.contact.w); vec3 color = exposed * ao + bloom * composite_info.params.y; diff --git a/packages/flutter3d_shaders/shaders/post/lens_flare.frag b/packages/flutter3d_shaders/shaders/post/lens_flare.frag new file mode 100644 index 000000000..f87f9bca1 --- /dev/null +++ b/packages/flutter3d_shaders/shaders/post/lens_flare.frag @@ -0,0 +1,88 @@ +#version 460 core + +// Lens flare, added to the glow — `P2`. +// +// A bright light in the frame reflects between the elements of a lens and +// lands again as a row of ghosts on the line through the middle of the +// frame, on the far side, and as a ring round the middle. Both are drawn +// here from the glow itself: the bloom chain has already picked out what is +// bright and spread it, so reading it at the mirrored positions is what puts +// a ghost where the light's reflection would fall, and nothing that is not +// bright enough to bloom ever flares. +// +// Each ghost is weighted by how near the middle it lands, so ghosts thin out +// towards the edges and one that would land outside the frame is not drawn +// — no wrapping round, which reads as a light on the wrong side. The three +// channels are taken a little apart along the ghost's line, which is the +// colour fringe a coated lens leaves on its reflections. +// +// Drawn into the glow rather than into the picture so the composite adds it +// with the glow, at the glow's own intensity and before the tone map: a +// flare is light, and it saturates the way the rest of the light does. +// +// `flutter3d_cpu`'s `LensFlareShader` is this, line for line. +precision highp float; + +in vec2 v_uv; + +out vec4 frag_color; + +/// The glow, as the bloom chain finished it. +uniform sampler2D bloom_texture; + +uniform LensFlareInfo { + /// x: how much flare, against the glow it is drawn from. y: how many + /// ghosts, up to eight. z: how far apart they fall, as a fraction of the + /// way from the light to the middle. w: the halo's radius, as a fraction + /// of the frame's height. + vec4 params; + + /// x: how far apart the channels of a ghost are taken, in uv. y: the + /// halo's strength against the ghosts'. z: the frame's aspect, width over + /// height. w: unclaimed. + vec4 more; +} +lens_flare_info; + +/// The glow at [at], its channels taken [spread] apart along [along]. +vec3 Fringed(vec2 at, vec2 along, float spread) { + return vec3(textureLod(bloom_texture, at + along * spread, 0.0).r, + textureLod(bloom_texture, at, 0.0).g, + textureLod(bloom_texture, at - along * spread, 0.0).b); +} + +/// How much a reflection landing at [at] keeps: all of it in the middle, +/// falling off to nothing at the corners and beyond. +float Falloff(vec2 at, float power) { + float d = length(vec2(0.5) - at) / 0.70710678; + return pow(max(1.0 - d, 0.0), power); +} + +void main() { + vec3 glow = textureLod(bloom_texture, v_uv, 0.0).rgb; + float intensity = lens_flare_info.params.x; + // The reflection of the point at `v_uv` lands on the far side of the + // middle, so a ghost at `v_uv` comes from the light at `flipped`. + vec2 flipped = vec2(1.0) - v_uv; + vec2 towardMiddle = (vec2(0.5) - flipped) * lens_flare_info.params.z; + float reach = length(towardMiddle); + vec2 along = reach > 1e-6 ? towardMiddle / reach : vec2(0.0); + float spread = lens_flare_info.more.x; + + vec3 flare = vec3(0.0); + int ghosts = int(lens_flare_info.params.y + 0.5); + for (int i = 0; i < 8; i++) { + if (i >= ghosts) break; + vec2 at = flipped + towardMiddle * float(i); + flare += Fringed(at, along, spread) * Falloff(at, 10.0); + } + + // The ring: every light at one radius from the middle lands on it. + float aspect = max(lens_flare_info.more.z, 1e-4); + vec2 halo = along * vec2(1.0 / aspect, 1.0) * lens_flare_info.params.w; + vec2 haloAt = flipped + halo; + flare += Fringed(haloAt, along, spread) * Falloff(haloAt, 5.0) * + lens_flare_info.more.y; + + frag_color = vec4(glow + flare * intensity, 1.0); +} diff --git a/packages/flutter3d_webgl/CHANGELOG.md b/packages/flutter3d_webgl/CHANGELOG.md index ad81600b2..e3732b59c 100644 --- a/packages/flutter3d_webgl/CHANGELOG.md +++ b/packages/flutter3d_webgl/CHANGELOG.md @@ -1,5 +1,7 @@ ## Unreleased +- **`LensFlare` translated, and the composite's distortion**; + `lens-flare` and `lens-distortion` are in the browser reference set. - **SMAA 1x's three stages translated**, from the same sources as every other stage; `smaa-teapot` is in the browser reference set. diff --git a/packages/flutter3d_webgl/lib/engine_shaders.dart b/packages/flutter3d_webgl/lib/engine_shaders.dart index 7b674a99f..9e68fc919 100644 --- a/packages/flutter3d_webgl/lib/engine_shaders.dart +++ b/packages/flutter3d_webgl/lib/engine_shaders.dart @@ -20960,9 +20960,33 @@ layout(std140) uniform CompositeInfo { /// offset above unchanged and the four backends do not have to agree about /// anything they had not already agreed on. vec4 contact; + + /// x: radial lens distortion — `P2`, barrel above nought, pincushion + /// below, nought off exactly. y, z, w: unclaimed. Appended for the reason + /// `contact` was. + vec4 lens; } composite_info; +/// Where the lens bends [at] to: radially, by `1 + k·r²` on the frame's own +/// aspect, scaled so the frame's border stays on the frame — `P2`. +/// +/// **Normalised on the border, not on the corner alone.** A barrel (k above +/// nought) magnifies the middle and pulls everything else inwards, so the +/// corners are what reach furthest and are held there. A pincushion does the +/// opposite, and normalising it on the corners would send the middles of the +/// edges past the frame and stretch the last row of texels across them; it is +/// held at the edge nearest the middle instead, and the corners come in. +vec2 Distort(vec2 at, float k) { + float aspect = max(composite_info.look_more.w, 1e-4); + vec2 fromCentre = at - vec2(0.5); + vec2 onFrame = fromCentre * vec2(aspect, 1.0); + float r2 = dot(onFrame, onFrame); + float border = k > 0.0 ? 0.25 * (aspect * aspect + 1.0) + : 0.25 * min(aspect * aspect, 1.0); + return vec2(0.5) + fromCentre * (1.0 + k * r2) / (1.0 + k * border); +} + /// Rec. 709 luma, which is what the sRGB primaries weight to. float Luma(vec3 color) { return dot(color, vec3(0.2126, 0.7152, 0.0722)); } @@ -21262,18 +21286,25 @@ void main() { // // The offset grows from the centre outwards, which is what a real lens does: // a ray through the middle of the glass is not dispersed at all. + // + // `P2`: the lens's own bend, before anything is read, so the scene and + // everything laid over it — the glow, the occlusion, the contact shadow, + // the local exposure — bend together. Nought leaves `uv` the very value + // `v_uv` is, which every golden in the repository depends on. + float distortion = composite_info.lens.x; + vec2 uv = distortion != 0.0 ? Distort(v_uv, distortion) : v_uv; float dispersion = composite_info.look.w; vec4 scene; if (dispersion > 0.0) { - vec2 fromCentre = v_uv - vec2(0.5); + vec2 fromCentre = uv - vec2(0.5); vec2 step_uv = fromCentre * dispersion; - scene = texture(scene_texture, v_uv); - scene.r = texture(scene_texture, v_uv + step_uv).r; - scene.b = texture(scene_texture, v_uv - step_uv).b; + scene = texture(scene_texture, uv); + scene.r = texture(scene_texture, uv + step_uv).r; + scene.b = texture(scene_texture, uv - step_uv).b; } else { - scene = texture(scene_texture, v_uv); + scene = texture(scene_texture, uv); } - vec3 bloom = texture(bloom_texture, v_uv).rgb; + vec3 bloom = texture(bloom_texture, uv).rgb; // Four taps in a 2×2, which is not a general-purpose blur: the occlusion pass // rotates its kernel by the parity of the pixel, leaving a 2×2 pattern, and @@ -21282,10 +21313,10 @@ void main() { // one without the other either leaves the pattern or smears the contact // shadows this whole pass exists to draw. vec2 half_texel = composite_info.ao_texel.xy * 0.5; - vec4 occlusion = 0.25 * (texture(ao_texture, v_uv + vec2(half_texel.x, half_texel.y)) + - texture(ao_texture, v_uv + vec2(-half_texel.x, half_texel.y)) + - texture(ao_texture, v_uv + vec2(half_texel.x, -half_texel.y)) + - texture(ao_texture, v_uv + vec2(-half_texel.x, -half_texel.y))); + vec4 occlusion = 0.25 * (texture(ao_texture, uv + vec2(half_texel.x, half_texel.y)) + + texture(ao_texture, uv + vec2(-half_texel.x, half_texel.y)) + + texture(ao_texture, uv + vec2(half_texel.x, -half_texel.y)) + + texture(ao_texture, uv + vec2(-half_texel.x, -half_texel.y))); // The share left open is in a; the occlusion methods write it into every // channel, and the indirect one keeps its light in rgb. float ao = occlusion.a; @@ -21305,7 +21336,7 @@ void main() { // the whole point of a contact shadow is the first few centimetres at the // join, and a half-resolution one is the seam it exists to draw, blurred // away. - float contact = texture(contact_shadow_texture, v_uv).r; + float contact = texture(contact_shadow_texture, uv).r; ao *= mix(1.0, contact, clamp(composite_info.contact.x, 0.0, 1.0)); // Applied to the scene and **not** to the bloom, which is the whole reason @@ -21328,7 +21359,7 @@ void main() { // `R7`: each place of the scene at the exposure that shows it best, before // the glow is added and the curve applied. Bilinear from an eighth of the // frame: the stops were blurred wide, so there is no edge in them to keep. - float localStops = texture(local_exposure_texture, v_uv).r; + float localStops = texture(local_exposure_texture, uv).r; vec3 exposed = scene.rgb * exp2(localStops * composite_info.contact.w); vec3 color = exposed * ao + bloom * composite_info.params.y; @@ -22330,6 +22361,100 @@ void main() { frag_color = first * (toward / total) + second * (away / total); } +''', + 'LensFlare': r'''#version 300 es +precision highp float; +precision highp int; +precision highp sampler2D; +precision highp samplerCube; + +// Lens flare, added to the glow — `P2`. +// +// A bright light in the frame reflects between the elements of a lens and +// lands again as a row of ghosts on the line through the middle of the +// frame, on the far side, and as a ring round the middle. Both are drawn +// here from the glow itself: the bloom chain has already picked out what is +// bright and spread it, so reading it at the mirrored positions is what puts +// a ghost where the light's reflection would fall, and nothing that is not +// bright enough to bloom ever flares. +// +// Each ghost is weighted by how near the middle it lands, so ghosts thin out +// towards the edges and one that would land outside the frame is not drawn +// — no wrapping round, which reads as a light on the wrong side. The three +// channels are taken a little apart along the ghost's line, which is the +// colour fringe a coated lens leaves on its reflections. +// +// Drawn into the glow rather than into the picture so the composite adds it +// with the glow, at the glow's own intensity and before the tone map: a +// flare is light, and it saturates the way the rest of the light does. +// +// `flutter3d_cpu`'s `LensFlareShader` is this, line for line. +precision highp float; + +in vec2 v_uv; + +layout(location = 0) out vec4 frag_color; + +/// The glow, as the bloom chain finished it. +uniform sampler2D bloom_texture; + +layout(std140) uniform LensFlareInfo { + /// x: how much flare, against the glow it is drawn from. y: how many + /// ghosts, up to eight. z: how far apart they fall, as a fraction of the + /// way from the light to the middle. w: the halo's radius, as a fraction + /// of the frame's height. + vec4 params; + + /// x: how far apart the channels of a ghost are taken, in uv. y: the + /// halo's strength against the ghosts'. z: the frame's aspect, width over + /// height. w: unclaimed. + vec4 more; +} +lens_flare_info; + +/// The glow at [at], its channels taken [spread] apart along [along]. +vec3 Fringed(vec2 at, vec2 along, float spread) { + return vec3(textureLod(bloom_texture, at + along * spread, 0.0).r, + textureLod(bloom_texture, at, 0.0).g, + textureLod(bloom_texture, at - along * spread, 0.0).b); +} + +/// How much a reflection landing at [at] keeps: all of it in the middle, +/// falling off to nothing at the corners and beyond. +float Falloff(vec2 at, float power) { + float d = length(vec2(0.5) - at) / 0.70710678; + return pow(max(1.0 - d, 0.0), power); +} + +void main() { + vec3 glow = textureLod(bloom_texture, v_uv, 0.0).rgb; + float intensity = lens_flare_info.params.x; + // The reflection of the point at `v_uv` lands on the far side of the + // middle, so a ghost at `v_uv` comes from the light at `flipped`. + vec2 flipped = vec2(1.0) - v_uv; + vec2 towardMiddle = (vec2(0.5) - flipped) * lens_flare_info.params.z; + float reach = length(towardMiddle); + vec2 along = reach > 1e-6 ? towardMiddle / reach : vec2(0.0); + float spread = lens_flare_info.more.x; + + vec3 flare = vec3(0.0); + int ghosts = int(lens_flare_info.params.y + 0.5); + for (int i = 0; i < 8; i++) { + if (i >= ghosts) break; + vec2 at = flipped + towardMiddle * float(i); + flare += Fringed(at, along, spread) * Falloff(at, 10.0); + } + + // The ring: every light at one radius from the middle lands on it. + float aspect = max(lens_flare_info.more.z, 1e-4); + vec2 halo = along * vec2(1.0 / aspect, 1.0) * lens_flare_info.params.w; + vec2 haloAt = flipped + halo; + flare += Fringed(haloAt, along, spread) * Falloff(haloAt, 5.0) * + lens_flare_info.more.y; + + frag_color = vec4(glow + flare * intensity, 1.0); +} + ''', 'MrtProbe': r'''#version 300 es precision highp float; diff --git a/packages/flutter3d_webgl/test/cross_backend_test.dart b/packages/flutter3d_webgl/test/cross_backend_test.dart index c75562cd7..bf0a81c97 100644 --- a/packages/flutter3d_webgl/test/cross_backend_test.dart +++ b/packages/flutter3d_webgl/test/cross_backend_test.dart @@ -232,6 +232,10 @@ const Map _budgets = { // 0.591% measured, every pixel of it on an edge: WebGL2 already draws // `shadow-teapot`'s silhouette 0.28% apart from Impeller's, and SMAA reads // a slightly different staircase there and moves its neighbours with it. + 'lens-flare': 0.01, + // 0.388% measured, on the silhouette: `shadow-teapot`'s 0.28% between + // WebGL2 and Impeller, magnified by the barrel. + 'lens-distortion': 0.45, 'smaa-teapot': 0.65, 'evsm-soft': 0.4, // 0.047% measured: the air right round a torch, where the light falls off diff --git a/packages/flutter3d_webgl/test/goldens/lens-distortion.png b/packages/flutter3d_webgl/test/goldens/lens-distortion.png new file mode 100644 index 0000000000000000000000000000000000000000..2dba36025c5ae836f14779f06bf0b5852471632d GIT binary patch literal 35284 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, gain: vec4, contact: vec4, + lens: vec4, } var gl_FragCoord_1: vec4; @group(1) @binding(1) var frag_coord_info: FragCoordInfo; +@group(1) @binding(0) +var composite_info: CompositeInfo; @group(1) @binding(12) var lut_texture_tex: texture_2d; @group(1) @binding(13) var lut_texture_smp: sampler; -@group(1) @binding(0) -var composite_info: CompositeInfo; @group(1) @binding(8) var display_texture_tex: texture_2d; @group(1) @binding(9) @@ -25461,9 +25462,52 @@ fn TonemapBy_u0028_vf3_u003b_i1_u003b(color_9: ptr>, curve: return _e175; } +fn Distort_u0028_vf2_u003b_f1_u003b(at_2: ptr>, k: ptr) -> vec2 { + var aspect: f32; + var fromCentre: vec2; + var onFrame: vec2; + var r2_: f32; + var border: f32; + var local_2: f32; + + let _e153 = composite_info.look_more[3u]; + aspect = max(_e153, 0.0001f); + let _e155 = (*at_2); + fromCentre = (_e155 - vec2(0.5f, 0.5f)); + let _e157 = fromCentre; + let _e158 = aspect; + onFrame = (_e157 * vec2(_e158, 1f)); + let _e161 = onFrame; + let _e162 = onFrame; + r2_ = dot(_e161, _e162); + let _e164 = (*k); + if (_e164 > 0f) { + let _e166 = aspect; + let _e167 = aspect; + local_2 = (0.25f * ((_e166 * _e167) + 1f)); + } else { + let _e171 = aspect; + let _e172 = aspect; + local_2 = (0.25f * min((_e171 * _e172), 1f)); + } + let _e176 = local_2; + border = _e176; + let _e177 = fromCentre; + let _e178 = (*k); + let _e179 = r2_; + let _e183 = (*k); + let _e184 = border; + return (vec2(0.5f, 0.5f) + ((_e177 * (1f + (_e178 * _e179))) / vec2((1f + (_e183 * _e184))))); +} + fn main_1() { + var distortion: f32; + var uv: vec2; + var local_3: vec2; + var param_9: vec2; + var param_10: f32; var dispersion: f32; - var fromCentre: vec2; + var fromCentre_1: vec2; var step_uv: vec2; var scene: vec4; var bloom: vec3; @@ -25474,12 +25518,12 @@ fn main_1() { var localStops: f32; var exposed: vec3; var color_10: vec3; - var param_9: vec3; - var param_10: i32; + var param_11: vec3; + var param_12: i32; var contrast: f32; var saturation: f32; var temperature: f32; - var param_11: vec3; + var param_13: vec3; var lift: vec3; var gammaCurve: vec3; var gain: vec3; @@ -25487,238 +25531,254 @@ fn main_1() { var tint: f32; var lutStrength: f32; var encodedIn: vec3; - var param_12: vec3; + var param_14: vec3; var graded: vec3; - var param_13: vec3; - var param_14: f32; var param_15: vec3; + var param_16: f32; + var param_17: vec3; var vignette: f32; - var fromCentre_1: vec2; - var aspect: f32; + var fromCentre_2: vec2; + var aspect_1: f32; var radius: f32; var encoded_1: vec3; - var param_16: vec3; + var param_18: vec3; var grain: f32; - var param_17: vec2; + var param_19: vec2; var dither: f32; - var param_18: vec2; + var param_20: vec2; - let _e185 = composite_info.look[3u]; - dispersion = _e185; - let _e186 = dispersion; - if (_e186 > 0f) { - let _e188 = v_uv_1; - fromCentre = (_e188 - vec2(0.5f, 0.5f)); - let _e190 = fromCentre; - let _e191 = dispersion; - step_uv = (_e190 * _e191); + let _e190 = composite_info.lens[0u]; + distortion = _e190; + let _e191 = distortion; + if (_e191 != 0f) { let _e193 = v_uv_1; - let _e194 = textureSample(scene_texture_tex, scene_texture_smp, _e193); - scene = _e194; - let _e195 = v_uv_1; - let _e196 = step_uv; - let _e198 = textureSample(scene_texture_tex, scene_texture_smp, (_e195 + _e196)); - scene[0u] = _e198.x; - let _e201 = v_uv_1; - let _e202 = step_uv; - let _e204 = textureSample(scene_texture_tex, scene_texture_smp, (_e201 - _e202)); - scene[2u] = _e204.z; + param_9 = _e193; + let _e194 = distortion; + param_10 = _e194; + let _e195 = Distort_u0028_vf2_u003b_f1_u003b((¶m_9), (¶m_10)); + local_3 = _e195; + } else { + let _e196 = v_uv_1; + local_3 = _e196; + } + let _e197 = local_3; + uv = _e197; + let _e200 = composite_info.look[3u]; + dispersion = _e200; + let _e201 = dispersion; + if (_e201 > 0f) { + let _e203 = uv; + fromCentre_1 = (_e203 - vec2(0.5f, 0.5f)); + let _e205 = fromCentre_1; + let _e206 = dispersion; + step_uv = (_e205 * _e206); + let _e208 = uv; + let _e209 = textureSample(scene_texture_tex, scene_texture_smp, _e208); + scene = _e209; + let _e210 = uv; + let _e211 = step_uv; + let _e213 = textureSample(scene_texture_tex, scene_texture_smp, (_e210 + _e211)); + scene[0u] = _e213.x; + let _e216 = uv; + let _e217 = step_uv; + let _e219 = textureSample(scene_texture_tex, scene_texture_smp, (_e216 - _e217)); + scene[2u] = _e219.z; } else { - let _e207 = v_uv_1; - let _e208 = textureSample(scene_texture_tex, scene_texture_smp, _e207); - scene = _e208; - } - let _e209 = v_uv_1; - let _e210 = textureSample(bloom_texture_tex, bloom_texture_smp, _e209); - bloom = _e210.xyz; - let _e213 = composite_info.ao_texel; - half_texel = (_e213.xy * 0.5f); - let _e216 = v_uv_1; - let _e218 = half_texel[0u]; - let _e220 = half_texel[1u]; - let _e223 = textureSample(ao_texture_tex, ao_texture_smp, (_e216 + vec2(_e218, _e220))); - let _e224 = v_uv_1; - let _e226 = half_texel[0u]; - let _e229 = half_texel[1u]; - let _e232 = textureSample(ao_texture_tex, ao_texture_smp, (_e224 + vec2(-(_e226), _e229))); - let _e234 = v_uv_1; - let _e236 = half_texel[0u]; - let _e238 = half_texel[1u]; - let _e242 = textureSample(ao_texture_tex, ao_texture_smp, (_e234 + vec2(_e236, -(_e238)))); - let _e244 = v_uv_1; - let _e246 = half_texel[0u]; - let _e249 = half_texel[1u]; - let _e253 = textureSample(ao_texture_tex, ao_texture_smp, (_e244 + vec2(-(_e246), -(_e249)))); - occlusion = ((((_e223 + _e232) + _e242) + _e253) * 0.25f); - let _e257 = occlusion[3u]; - ao = _e257; - let _e258 = ao; - let _e261 = composite_info.params[3u]; - ao = mix(1f, _e258, clamp(_e261, 0f, 1f)); - let _e264 = v_uv_1; - let _e265 = textureSample(contact_shadow_texture_tex, contact_shadow_texture_smp, _e264); - contact = _e265.x; - let _e267 = contact; - let _e270 = composite_info.contact[0u]; + let _e222 = uv; + let _e223 = textureSample(scene_texture_tex, scene_texture_smp, _e222); + scene = _e223; + } + let _e224 = uv; + let _e225 = textureSample(bloom_texture_tex, bloom_texture_smp, _e224); + bloom = _e225.xyz; + let _e228 = composite_info.ao_texel; + half_texel = (_e228.xy * 0.5f); + let _e231 = uv; + let _e233 = half_texel[0u]; + let _e235 = half_texel[1u]; + let _e238 = textureSample(ao_texture_tex, ao_texture_smp, (_e231 + vec2(_e233, _e235))); + let _e239 = uv; + let _e241 = half_texel[0u]; + let _e244 = half_texel[1u]; + let _e247 = textureSample(ao_texture_tex, ao_texture_smp, (_e239 + vec2(-(_e241), _e244))); + let _e249 = uv; + let _e251 = half_texel[0u]; + let _e253 = half_texel[1u]; + let _e257 = textureSample(ao_texture_tex, ao_texture_smp, (_e249 + vec2(_e251, -(_e253)))); + let _e259 = uv; + let _e261 = half_texel[0u]; + let _e264 = half_texel[1u]; + let _e268 = textureSample(ao_texture_tex, ao_texture_smp, (_e259 + vec2(-(_e261), -(_e264)))); + occlusion = ((((_e238 + _e247) + _e257) + _e268) * 0.25f); + let _e272 = occlusion[3u]; + ao = _e272; let _e273 = ao; - ao = (_e273 * mix(1f, _e267, clamp(_e270, 0f, 1f))); - let _e275 = v_uv_1; - let _e276 = textureSample(local_exposure_texture_tex, local_exposure_texture_smp, _e275); - localStops = _e276.x; - let _e278 = scene; - let _e280 = localStops; - let _e283 = composite_info.contact[3u]; - exposed = (_e278.xyz * exp2((_e280 * _e283))); - let _e287 = exposed; + let _e276 = composite_info.params[3u]; + ao = mix(1f, _e273, clamp(_e276, 0f, 1f)); + let _e279 = uv; + let _e280 = textureSample(contact_shadow_texture_tex, contact_shadow_texture_smp, _e279); + contact = _e280.x; + let _e282 = contact; + let _e285 = composite_info.contact[0u]; let _e288 = ao; - let _e290 = bloom; - let _e293 = composite_info.params[1u]; - color_10 = ((_e287 * _e288) + (_e290 * _e293)); - let _e296 = occlusion; - let _e300 = composite_info.contact[2u]; - let _e304 = composite_info.params[3u]; - let _e307 = color_10; - color_10 = (_e307 + ((_e296.xyz * _e300) * clamp(_e304, 0f, 1f))); - let _e311 = composite_info.params[0u]; - let _e313 = color_10; - color_10 = (_e313 * max(_e311, 0f)); - let _e317 = composite_info.params[2u]; - let _e320 = color_10; - param_9 = _e320; - param_10 = i32((_e317 + 0.5f)); - let _e321 = TonemapBy_u0028_vf3_u003b_i1_u003b((¶m_9), (¶m_10)); - color_10 = _e321; - let _e324 = composite_info.look[0u]; - contrast = _e324; - let _e327 = composite_info.look[1u]; - saturation = _e327; - let _e330 = composite_info.look[2u]; - temperature = _e330; - let _e331 = contrast; - if (_e331 != 1f) { - let _e333 = color_10; - let _e337 = contrast; - color_10 = (vec3(0.18f, 0.18f, 0.18f) * pow((max(_e333, vec3(0f, 0f, 0f)) / vec3(0.18f)), vec3(_e337))); - } - let _e341 = color_10; - param_11 = _e341; - let _e342 = Luma_u0028_vf3_u003b((¶m_11)); - let _e344 = color_10; - let _e345 = saturation; - color_10 = mix(vec3(_e342), _e344, vec3(_e345)); - let _e348 = temperature; - let _e351 = temperature; - let _e355 = color_10; - color_10 = (_e355 * vec3((1f + (_e348 * 0.1f)), 1f, (1f - (_e351 * 0.1f)))); - let _e358 = composite_info.lift; - lift = _e358.xyz; - let _e361 = composite_info.gamma; - gammaCurve = _e361.xyz; - let _e364 = composite_info.gain; - gain = _e364.xyz; - let _e366 = color_10; - let _e367 = lift; - let _e370 = lift; - color_10 = ((_e366 * (vec3(1f, 1f, 1f) - _e367)) + _e370); - let _e372 = color_10; - color_10 = max(_e372, vec3(0f, 0f, 0f)); - let _e374 = gammaCurve; - if any((_e374 != vec3(1f, 1f, 1f))) { - let _e377 = color_10; - let _e378 = gammaCurve; - color_10 = pow(_e377, (vec3(1f, 1f, 1f) / _e378)); - } - let _e381 = gain; - let _e382 = color_10; - color_10 = (_e382 * _e381); - let _e386 = composite_info.output_encode[1u]; - balance = _e386; - let _e389 = composite_info.output_encode[2u]; - tint = _e389; - let _e390 = balance; - let _e392 = tint; - if ((_e390 != 0f) || (_e392 != 0f)) { - let _e395 = balance; - let _e398 = tint; - let _e401 = balance; - let _e405 = color_10; - color_10 = (_e405 * vec3((1f + (_e395 * 0.2f)), (1f + (_e398 * 0.15f)), (1f - (_e401 * 0.2f)))); - let _e407 = tint; - let _e410 = color_10[0u]; - color_10[0u] = (_e410 - (_e407 * 0.075f)); + ao = (_e288 * mix(1f, _e282, clamp(_e285, 0f, 1f))); + let _e290 = uv; + let _e291 = textureSample(local_exposure_texture_tex, local_exposure_texture_smp, _e290); + localStops = _e291.x; + let _e293 = scene; + let _e295 = localStops; + let _e298 = composite_info.contact[3u]; + exposed = (_e293.xyz * exp2((_e295 * _e298))); + let _e302 = exposed; + let _e303 = ao; + let _e305 = bloom; + let _e308 = composite_info.params[1u]; + color_10 = ((_e302 * _e303) + (_e305 * _e308)); + let _e311 = occlusion; + let _e315 = composite_info.contact[2u]; + let _e319 = composite_info.params[3u]; + let _e322 = color_10; + color_10 = (_e322 + ((_e311.xyz * _e315) * clamp(_e319, 0f, 1f))); + let _e326 = composite_info.params[0u]; + let _e328 = color_10; + color_10 = (_e328 * max(_e326, 0f)); + let _e332 = composite_info.params[2u]; + let _e335 = color_10; + param_11 = _e335; + param_12 = i32((_e332 + 0.5f)); + let _e336 = TonemapBy_u0028_vf3_u003b_i1_u003b((¶m_11), (¶m_12)); + color_10 = _e336; + let _e339 = composite_info.look[0u]; + contrast = _e339; + let _e342 = composite_info.look[1u]; + saturation = _e342; + let _e345 = composite_info.look[2u]; + temperature = _e345; + let _e346 = contrast; + if (_e346 != 1f) { + let _e348 = color_10; + let _e352 = contrast; + color_10 = (vec3(0.18f, 0.18f, 0.18f) * pow((max(_e348, vec3(0f, 0f, 0f)) / vec3(0.18f)), vec3(_e352))); + } + let _e356 = color_10; + param_13 = _e356; + let _e357 = Luma_u0028_vf3_u003b((¶m_13)); + let _e359 = color_10; + let _e360 = saturation; + color_10 = mix(vec3(_e357), _e359, vec3(_e360)); + let _e363 = temperature; + let _e366 = temperature; + let _e370 = color_10; + color_10 = (_e370 * vec3((1f + (_e363 * 0.1f)), 1f, (1f - (_e366 * 0.1f)))); + let _e373 = composite_info.lift; + lift = _e373.xyz; + let _e376 = composite_info.gamma; + gammaCurve = _e376.xyz; + let _e379 = composite_info.gain; + gain = _e379.xyz; + let _e381 = color_10; + let _e382 = lift; + let _e385 = lift; + color_10 = ((_e381 * (vec3(1f, 1f, 1f) - _e382)) + _e385); + let _e387 = color_10; + color_10 = max(_e387, vec3(0f, 0f, 0f)); + let _e389 = gammaCurve; + if any((_e389 != vec3(1f, 1f, 1f))) { + let _e392 = color_10; + let _e393 = gammaCurve; + color_10 = pow(_e392, (vec3(1f, 1f, 1f) / _e393)); + } + let _e396 = gain; + let _e397 = color_10; + color_10 = (_e397 * _e396); + let _e401 = composite_info.output_encode[1u]; + balance = _e401; + let _e404 = composite_info.output_encode[2u]; + tint = _e404; + let _e405 = balance; + let _e407 = tint; + if ((_e405 != 0f) || (_e407 != 0f)) { + let _e410 = balance; let _e413 = tint; - let _e416 = color_10[2u]; - color_10[2u] = (_e416 - (_e413 * 0.075f)); - } - let _e421 = composite_info.ao_texel[2u]; - lutStrength = _e421; - let _e422 = lutStrength; - if (_e422 > 0f) { - let _e424 = color_10; - param_12 = clamp(_e424, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e426 = LinearToSrgb_u0028_vf3_u003b((¶m_12)); - encodedIn = _e426; - let _e429 = composite_info.ao_texel[3u]; - let _e431 = encodedIn; - param_13 = _e431; - param_14 = max(_e429, 2f); - let _e432 = SampleLut_u0028_vf3_u003b_f1_u003b((¶m_13), (¶m_14)); - param_15 = _e432; - let _e433 = SrgbToLinear_u0028_vf3_u003b((¶m_15)); - graded = _e433; - let _e434 = color_10; - let _e435 = graded; - let _e436 = lutStrength; - color_10 = mix(_e434, _e435, vec3(clamp(_e436, 0f, 1f))); - } - let _e442 = composite_info.look_more[0u]; - vignette = _e442; - let _e443 = vignette; - if (_e443 > 0f) { - let _e445 = v_uv_1; - fromCentre_1 = (_e445 - vec2(0.5f, 0.5f)); - let _e449 = composite_info.look_more[3u]; - aspect = max(_e449, 0.0001f); - let _e451 = aspect; - let _e454 = composite_info.look_more[1u]; - let _e457 = fromCentre_1[0u]; - fromCentre_1[0u] = (_e457 * mix(1f, _e451, _e454)); - let _e460 = fromCentre_1; - radius = (length(_e460) * 1.4142135f); - let _e463 = vignette; - let _e465 = radius; - let _e468 = color_10; - color_10 = (_e468 * mix(1f, (1f - _e463), clamp(_e465, 0f, 1f))); - } - let _e470 = color_10; - param_16 = max(_e470, vec3(0f, 0f, 0f)); - let _e472 = LinearToSrgb_u0028_vf3_u003b((¶m_16)); - encoded_1 = _e472; - let _e475 = composite_info.look_more[2u]; - grain = _e475; - let _e476 = grain; - if (_e476 > 0f) { - let _e478 = TargetFragCoord_u0028_(); - param_17 = _e478; - let _e479 = Hash_u0028_vf2_u003b((¶m_17)); - let _e481 = grain; - let _e484 = encoded_1; - encoded_1 = (_e484 + vec3(((_e479 - 0.5f) * _e481))); - } - let _e488 = composite_info.output_encode[0u]; - dither = _e488; - let _e489 = dither; - if (_e489 > 0f) { - let _e491 = TargetFragCoord_u0028_(); - param_18 = _e491; - let _e492 = BayerCell_u0028_vf2_u003b((¶m_18)); - let _e494 = dither; - let _e497 = encoded_1; - encoded_1 = (_e497 + vec3(((_e492 + 0.03125f) * _e494))); - } - let _e499 = encoded_1; - let _e501 = scene[3u]; - frag_color = vec4(_e499.x, _e499.y, _e499.z, _e501); + let _e416 = balance; + let _e420 = color_10; + color_10 = (_e420 * vec3((1f + (_e410 * 0.2f)), (1f + (_e413 * 0.15f)), (1f - (_e416 * 0.2f)))); + let _e422 = tint; + let _e425 = color_10[0u]; + color_10[0u] = (_e425 - (_e422 * 0.075f)); + let _e428 = tint; + let _e431 = color_10[2u]; + color_10[2u] = (_e431 - (_e428 * 0.075f)); + } + let _e436 = composite_info.ao_texel[2u]; + lutStrength = _e436; + let _e437 = lutStrength; + if (_e437 > 0f) { + let _e439 = color_10; + param_14 = clamp(_e439, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e441 = LinearToSrgb_u0028_vf3_u003b((¶m_14)); + encodedIn = _e441; + let _e444 = composite_info.ao_texel[3u]; + let _e446 = encodedIn; + param_15 = _e446; + param_16 = max(_e444, 2f); + let _e447 = SampleLut_u0028_vf3_u003b_f1_u003b((¶m_15), (¶m_16)); + param_17 = _e447; + let _e448 = SrgbToLinear_u0028_vf3_u003b((¶m_17)); + graded = _e448; + let _e449 = color_10; + let _e450 = graded; + let _e451 = lutStrength; + color_10 = mix(_e449, _e450, vec3(clamp(_e451, 0f, 1f))); + } + let _e457 = composite_info.look_more[0u]; + vignette = _e457; + let _e458 = vignette; + if (_e458 > 0f) { + let _e460 = v_uv_1; + fromCentre_2 = (_e460 - vec2(0.5f, 0.5f)); + let _e464 = composite_info.look_more[3u]; + aspect_1 = max(_e464, 0.0001f); + let _e466 = aspect_1; + let _e469 = composite_info.look_more[1u]; + let _e472 = fromCentre_2[0u]; + fromCentre_2[0u] = (_e472 * mix(1f, _e466, _e469)); + let _e475 = fromCentre_2; + radius = (length(_e475) * 1.4142135f); + let _e478 = vignette; + let _e480 = radius; + let _e483 = color_10; + color_10 = (_e483 * mix(1f, (1f - _e478), clamp(_e480, 0f, 1f))); + } + let _e485 = color_10; + param_18 = max(_e485, vec3(0f, 0f, 0f)); + let _e487 = LinearToSrgb_u0028_vf3_u003b((¶m_18)); + encoded_1 = _e487; + let _e490 = composite_info.look_more[2u]; + grain = _e490; + let _e491 = grain; + if (_e491 > 0f) { + let _e493 = TargetFragCoord_u0028_(); + param_19 = _e493; + let _e494 = Hash_u0028_vf2_u003b((¶m_19)); + let _e496 = grain; + let _e499 = encoded_1; + encoded_1 = (_e499 + vec3(((_e494 - 0.5f) * _e496))); + } + let _e503 = composite_info.output_encode[0u]; + dither = _e503; + let _e504 = dither; + if (_e504 > 0f) { + let _e506 = TargetFragCoord_u0028_(); + param_20 = _e506; + let _e507 = BayerCell_u0028_vf2_u003b((¶m_20)); + let _e509 = dither; + let _e512 = encoded_1; + encoded_1 = (_e512 + vec3(((_e507 + 0.03125f) * _e509))); + } + let _e514 = encoded_1; + let _e516 = scene[3u]; + frag_color = vec4(_e514.x, _e514.y, _e514.z, _e516); return; } @@ -25737,7 +25797,7 @@ fn main(@builtin(position) gl_FragCoord: vec4, @location(5) v_uv: vec2 name: 'CompositeInfo', group: 1, binding: 0, - sizeInBytes: 144, + sizeInBytes: 160, members: [ WebGpuBlockMember( name: 'params', @@ -25776,6 +25836,11 @@ fn main(@builtin(position) gl_FragCoord: vec4, @location(5) v_uv: vec2 offsetInBytes: 128, sizeInBytes: 16, ), + WebGpuBlockMember( + name: 'lens', + offsetInBytes: 144, + sizeInBytes: 16, + ), ], ), WebGpuBlock( @@ -28273,6 +28338,201 @@ fn main(@location(5) v_uv: vec2) -> @location(0) vec4 { ), ], ), + 'LensFlare': WebGpuStage( + wgsl: r''' +struct LensFlareInfo { + params: vec4, + more: vec4, +} + +@group(1) @binding(1) +var bloom_texture_tex: texture_2d; +@group(1) @binding(2) +var bloom_texture_smp: sampler; +var v_uv_1: vec2; +@group(1) @binding(0) +var lens_flare_info: LensFlareInfo; +var frag_color: vec4; + +fn Falloff_u0028_vf2_u003b_f1_u003b(at: ptr>, power: ptr) -> f32 { + var d: f32; + + let _e27 = (*at); + d = (length((vec2(0.5f, 0.5f) - _e27)) / 0.70710677f); + let _e31 = d; + let _e34 = (*power); + return pow(max((1f - _e31), 0f), _e34); +} + +fn Fringed_u0028_vf2_u003b_vf2_u003b_f1_u003b(at_1: ptr>, along: ptr>, spread: ptr) -> vec3 { + let _e27 = (*at_1); + let _e28 = (*along); + let _e29 = (*spread); + let _e32 = textureSampleLevel(bloom_texture_tex, bloom_texture_smp, (_e27 + (_e28 * _e29)), 0f); + let _e34 = (*at_1); + let _e35 = textureSampleLevel(bloom_texture_tex, bloom_texture_smp, _e34, 0f); + let _e37 = (*at_1); + let _e38 = (*along); + let _e39 = (*spread); + let _e42 = textureSampleLevel(bloom_texture_tex, bloom_texture_smp, (_e37 - (_e38 * _e39)), 0f); + return vec3(_e32.x, _e35.y, _e42.z); +} + +fn main_1() { + var glow: vec3; + var intensity: f32; + var flipped: vec2; + var towardMiddle: vec2; + var reach: f32; + var along_1: vec2; + var local: vec2; + var spread_1: f32; + var flare: vec3; + var ghosts: i32; + var i: i32; + var at_2: vec2; + var param: vec2; + var param_1: vec2; + var param_2: f32; + var param_3: vec2; + var param_4: f32; + var aspect: f32; + var halo: vec2; + var haloAt: vec2; + var param_5: vec2; + var param_6: vec2; + var param_7: f32; + var param_8: vec2; + var param_9: f32; + + let _e49 = v_uv_1; + let _e50 = textureSampleLevel(bloom_texture_tex, bloom_texture_smp, _e49, 0f); + glow = _e50.xyz; + let _e54 = lens_flare_info.params[0u]; + intensity = _e54; + let _e55 = v_uv_1; + flipped = (vec2(1f, 1f) - _e55); + let _e57 = flipped; + let _e61 = lens_flare_info.params[2u]; + towardMiddle = ((vec2(0.5f, 0.5f) - _e57) * _e61); + let _e63 = towardMiddle; + reach = length(_e63); + let _e65 = reach; + if (_e65 > 0.000001f) { + let _e67 = towardMiddle; + let _e68 = reach; + local = (_e67 / vec2(_e68)); + } else { + local = vec2(0f, 0f); + } + let _e71 = local; + along_1 = _e71; + let _e74 = lens_flare_info.more[0u]; + spread_1 = _e74; + flare = vec3(0f, 0f, 0f); + let _e77 = lens_flare_info.params[1u]; + ghosts = i32((_e77 + 0.5f)); + i = 0i; + loop { + let _e80 = i; + if (_e80 < 8i) { + let _e82 = i; + let _e83 = ghosts; + if (_e82 >= _e83) { + break; + } + let _e85 = flipped; + let _e86 = towardMiddle; + let _e87 = i; + at_2 = (_e85 + (_e86 * f32(_e87))); + let _e91 = at_2; + param = _e91; + let _e92 = along_1; + param_1 = _e92; + let _e93 = spread_1; + param_2 = _e93; + let _e94 = Fringed_u0028_vf2_u003b_vf2_u003b_f1_u003b((¶m), (¶m_1), (¶m_2)); + let _e95 = at_2; + param_3 = _e95; + param_4 = 10f; + let _e96 = Falloff_u0028_vf2_u003b_f1_u003b((¶m_3), (¶m_4)); + let _e98 = flare; + flare = (_e98 + (_e94 * _e96)); + continue; + } else { + break; + } + continuing { + let _e100 = i; + i = (_e100 + 1i); + } + } + let _e104 = lens_flare_info.more[2u]; + aspect = max(_e104, 0.0001f); + let _e106 = along_1; + let _e107 = aspect; + let _e113 = lens_flare_info.params[3u]; + halo = ((_e106 * vec2((1f / _e107), 1f)) * _e113); + let _e115 = flipped; + let _e116 = halo; + haloAt = (_e115 + _e116); + let _e118 = haloAt; + param_5 = _e118; + let _e119 = along_1; + param_6 = _e119; + let _e120 = spread_1; + param_7 = _e120; + let _e121 = Fringed_u0028_vf2_u003b_vf2_u003b_f1_u003b((¶m_5), (¶m_6), (¶m_7)); + let _e122 = haloAt; + param_8 = _e122; + param_9 = 5f; + let _e123 = Falloff_u0028_vf2_u003b_f1_u003b((¶m_8), (¶m_9)); + let _e127 = lens_flare_info.more[1u]; + let _e129 = flare; + flare = (_e129 + ((_e121 * _e123) * _e127)); + let _e131 = glow; + let _e132 = flare; + let _e133 = intensity; + let _e135 = (_e131 + (_e132 * _e133)); + frag_color = vec4(_e135.x, _e135.y, _e135.z, 1f); + return; +} + +@fragment +fn main(@location(5) v_uv: vec2) -> @location(0) vec4 { + v_uv_1 = v_uv; + main_1(); + let _e3 = frag_color; + return _e3; +} +''', + attributes: [], + blocks: [ + WebGpuBlock( + name: 'LensFlareInfo', + group: 1, + binding: 0, + sizeInBytes: 32, + members: [ + WebGpuBlockMember( + name: 'params', + offsetInBytes: 0, + sizeInBytes: 16, + ), + WebGpuBlockMember(name: 'more', offsetInBytes: 16, sizeInBytes: 16), + ], + ), + ], + samplers: [ + WebGpuSampler( + name: 'bloom_texture', + group: 1, + textureBinding: 1, + samplerBinding: 2, + dimension: WebGpuTextureDimension.twoDimensional, + ), + ], + ), 'MrtProbe': WebGpuStage( wgsl: r''' struct FragmentOutput { diff --git a/packages/flutter3d_webgpu/test/cross_backend_test.dart b/packages/flutter3d_webgpu/test/cross_backend_test.dart index fcb2e9204..740f4f213 100644 --- a/packages/flutter3d_webgpu/test/cross_backend_test.dart +++ b/packages/flutter3d_webgpu/test/cross_backend_test.dart @@ -182,6 +182,8 @@ const Map _budgets = { 'cascade-walk': 0.01, 'clearcoat-car-paint': 0.01, 'easu-half': 0.01, + 'lens-flare': 0.01, + 'lens-distortion': 0.01, 'smaa-teapot': 0.01, 'evsm-soft': 0.33, 'fog-torches': 0.01, diff --git a/packages/flutter3d_webgpu/test/goldens/lens-distortion.png b/packages/flutter3d_webgpu/test/goldens/lens-distortion.png new file mode 100644 index 0000000000000000000000000000000000000000..71610b149c3ff3328b8898acd9fdd5f6a5a3ddfe GIT binary patch literal 35309 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 literal 0 HcmV?d00001 diff --git a/site/content/core/architecture.md b/site/content/core/architecture.md index e6b76e172..0194f7f57 100644 --- a/site/content/core/architecture.md +++ b/site/content/core/architecture.md @@ -53,7 +53,7 @@ Each exists for a different reason, and none of them is a fallback for another. Its shaders are GLSL ES 3.00 generated from `flutter3d_shaders`. Nothing checked that the generated file was current for a long time, and it cost two failures that reported nothing: a uniform member the browser's copy had never heard of, and a sky shader from before the sky was rewritten. `tool/ci.sh` regenerates it and fails on the diff now, and the compiled Impeller bundle, which cannot be diffed, is held by a freshness rule instead. -**`flutter3d_cpu`** is the one that makes the agreement mean something. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. It is also what makes 79 golden scenes checkable in a headless run, and what caught three bugs that every simulation test passed. +**`flutter3d_cpu`** is the one that makes the agreement mean something. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. It is also what makes 81 golden scenes checkable in a headless run, and what caught three bugs that every simulation test passed. **`flutter3d_webgpu`** asks a question none of the other three could: whether the *shader* half of the contract is a seam. The first three all read one text (`impellerc` compiles the GLSL, the WebGL generator translates it, the software rasteriser transcribes it into Dart by hand), and WebGPU cannot, because WGSL is a different language and a browser will not take SPIR-V. So its table is generated down a second toolchain, the same manifest through `glslangValidator` and then `naga`, and all thirty-nine stages come out. What it declines it declines by name: no blend constant (WebGPU has no colour/alpha pair for one) and no wireframe (no polygon fill mode in the API at all). Two more used to be on that list. Rendering into a mip is on now and cost no code: a reflection probe convolves its own chain, and a face and a level are `baseArrayLayer` and `baseMipLevel` on an ordinary view here. Block compression is on too, and what it cost was the asking: a WebGPU device gets exactly the features it requested, and requesting one the adapter does not carry rejects the device instead of handing back a lesser one, so the adapter is asked, the intersection is requested, and the capability answers from what was granted. @@ -62,7 +62,7 @@ A browser build opens WebGL2 unless it says otherwise. That is a decision about Any new backend has to pass `flutter3d_conformance` before it counts as one. ### What the HAL actually names diff --git a/site/content/core/backends.md b/site/content/core/backends.md index cc457c2d8..599b4832c 100644 --- a/site/content/core/backends.md +++ b/site/content/core/backends.md @@ -20,7 +20,7 @@ What could **not** be written from the contract is the shaders. That limit is re
  • The semantics that are part of the contract and appear in no signature
  • What the 0.8 members ask, compute among them, and which backend answers yes
  • The conformance suite, and how to run it before you have a single shader
  • -
  • The eighty-three shader entry points your bundle must answer to
  • +
  • The eighty-seven shader entry points your bundle must answer to
  • @@ -405,7 +405,7 @@ Eighty-three entry points. `kRequiredShaders` and the bundle manifest are kept i | Vertex | `MeshVertex`, `MeshSkinnedVertex`, `MeshInstancedVertex`, `MeshLightmappedVertex`, `FullscreenVertex`, `DebugLineVertex`, `ParticleVertex`, `ParticleMeshVertex`, `PolylineVertex`, `ShadowTileResetVertex`, `SkyVertex`, `SkyCubeVertex`, `ImpostorVertex`, `VelocityVertex`, `VelocitySkinnedVertex`, `VelocityInstancedVertex` | | Lighting | `Unlit`, `Lambert`, `BlinnPhong`, `Pbr`, `Toon`, `Normals`, `PbrLayered`, `Impostor` | | Shadows | `ShadowDepth`, `ShadowDistance`, `ShadowDepthMasked`, `ShadowDistanceMasked`, `ShadowTileReset`, `EvsmFilter`, `ShadowCopy` | -| Post | `BloomThreshold`, `BloomDownsample`, `BloomUpsample`, `Composite`, `Fxaa`, `SmaaEdges`, `SmaaWeights`, `SmaaBlend`, `Reflections`, `Ssao`, `SsaoBlur`, `ContactShadow`, `ContactShadowResolve`, `Splat`, `LightShafts`, `DepthOfField`, `ViewportShade`, `Luminance`, `ProbePrefilter`, `CameraVelocity`, `Velocity`, `VelocityTileMax`, `VelocityNeighborMax`, `MotionBlur`, `TemporalResolve`, `TemporalAccumulate`, `Reactive`, `Easu`, `LocalExposure`, `LocalExposureBlur`, `VolumetricFog`, `VolumetricFogUpsample`, `DepthPyramid`, `SceneColourCopy`, `WboitResolve`, `DofTileMax` | +| Post | `BloomThreshold`, `BloomDownsample`, `BloomUpsample`, `Composite`, `Fxaa`, `SmaaEdges`, `SmaaWeights`, `SmaaBlend`, `LensFlare`, `Reflections`, `Ssao`, `SsaoBlur`, `ContactShadow`, `ContactShadowResolve`, `Splat`, `LightShafts`, `DepthOfField`, `ViewportShade`, `Luminance`, `ProbePrefilter`, `CameraVelocity`, `Velocity`, `VelocityTileMax`, `VelocityNeighborMax`, `MotionBlur`, `TemporalResolve`, `TemporalAccumulate`, `Reactive`, `Easu`, `LocalExposure`, `LocalExposureBlur`, `VolumetricFog`, `VolumetricFogUpsample`, `DepthPyramid`, `SceneColourCopy`, `WboitResolve`, `DofTileMax` | | Particles | `Particle`, `ParticleTextured`, `ParticleMesh`, `ParticleSixWay`, `ReactiveSprite`, `SplatHashed`, `ParticleSoft`, `ParticleTexturedSoft`, `ParticleSixWaySoft` | | Sky | `Sky`, `SkyCube` | | Debug | `DebugLine`, `MrtProbe`, `ObjectId`, `Xray` | diff --git a/site/content/core/index.md b/site/content/core/index.md index 45819ac13..d73935b46 100644 --- a/site/content/core/index.md +++ b/site/content/core/index.md @@ -25,7 +25,7 @@ Nothing in this section is genre knowledge. That property is what made the secon |---|---| | `flutter3d_impeller` | `flutter_gpu`, over Metal and Vulkan. The production one | | `flutter3d_webgl` | WebGL2 in the browser. Runs all three games at a fixed resolution and a lower frame rate, and is what an ordinary web build opens | -| `flutter3d_cpu` | Nothing: it rasterises in Dart, so 79 golden scenes stay checkable with no GPU in the room | +| `flutter3d_cpu` | Nothing: it rasterises in Dart, so 81 golden scenes stay checkable with no GPU in the room | | `flutter3d_webgpu` | WebGPU in a browser that has an adapter. Reached by `--dart-define=FLUTTER3D_WEBGPU=true`, because a build that can try it ships it | `flutter3d_conformance` is the suite each of them passed before it belonged in that table, and [Writing a HAL backend](/core/backends/) is the guide for writing the fifth. diff --git a/site/content/core/rendering.md b/site/content/core/rendering.md index b64a8b45a..a6ebf8434 100644 --- a/site/content/core/rendering.md +++ b/site/content/core/rendering.md @@ -487,7 +487,7 @@ It is marched at half resolution and brought up by depth, so a halo behind a pil ```dart SkySettings( - enabled: true, // off by default: seventy-nine goldens are recorded against none + enabled: true, // off by default: eighty-one goldens are recorded against none zenith: Vector3(0.10, 0.22, 0.52), horizon: Vector3(0.42, 0.50, 0.62), nadir: Vector3(0.06, 0.06, 0.07), // what fills the frame looking down at nothing diff --git a/site/content/quickstart.md b/site/content/quickstart.md index b72a59795..dd8ab555b 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10886 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-nine scenes stay checkable in a headless run. +There are 10901 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-one scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/packages.md b/site/content/reference/packages.md index d87038740..a49c1683f 100644 --- a/site/content/reference/packages.md +++ b/site/content/reference/packages.md @@ -60,7 +60,7 @@ A rasteriser written in Dart. `CpuDevice` implements the same HAL, plus PNG outp Not a fallback. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. -It is how seventy-nine golden scenes are checkable with no GPU in the room, and it is a dev dependency of every game because three shipped bugs would have been caught by rendering a single frame in a test. It stopped being *only* a dev dependency once `flutter3d_app` started reaching for it as the runtime fallback when Impeller will not start, which makes it a real production path now, if a last-resort one. +It is how eighty-one golden scenes are checkable with no GPU in the room, and it is a dev dependency of every game because three shipped bugs would have been caught by rendering a single frame in a test. It stopped being *only* a dev dependency once `flutter3d_app` started reaching for it as the runtime fallback when Impeller will not start, which makes it a real production path now, if a last-resort one. ### `flutter3d_conformance` The suite any fourth backend would have to pass before it counted as one, plus the cross-backend comparison with per-scene budgets. diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 30d550ef6..8fc1e0370 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10886 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10901 tests need a GPU. --- # Testing -10886 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10901 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -12,7 +12,7 @@ description: Four independent golden sets, mutation-checking every new test, det | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | | `flutter3d_sim` | 591 | | `pad_input` | 67 | -| `flutter3d_lab` | 15 | | `flutter3d_core` | 736 | +| `flutter3d_lab` | 15 | | `flutter3d_core` | 746 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | | `flutter3d_game_shooter` | 340 | | `flutter3d_audio_core` | 55 | @@ -20,7 +20,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_racing` | 223 | | `flutter3d_webgl` | 62 | | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 63 | | `apps/flutter3d_demo_platformer` | 202 | | `flutter3d_impeller` | 64 | -| `flutter3d_cpu` | 330 | | `apps/flutter3d_demo_strategy` | 50 | +| `flutter3d_cpu` | 335 | | `apps/flutter3d_demo_strategy` | 50 | | `apps/flutter3d_editor` | 224 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10860 rather than 10886: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10875 rather than 10901: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. @@ -52,7 +52,7 @@ tool/ci.sh # shaders, analyze, every test ## Four independent golden sets, not one -Seventy-nine scenes are rendered four times: through Impeller, through the software rasteriser in `flutter3d_cpu`, through WebGL2, and through WebGPU, the last two in a driven browser. Each backend is held to zero differing pixels against its own set, with a per-channel tolerance of 8. +Eighty-one scenes are rendered four times: through Impeller, through the software rasteriser in `flutter3d_cpu`, through WebGL2, and through WebGPU, the last two in a driven browser. Each backend is held to zero differing pixels against its own set, with a per-channel tolerance of 8. The browser sets are recorded when a branch lands, not beside it (`golden_web.sh` holds one fixed port for the whole of its run), so a new scene is in the other sets for as long as that takes. Which scenes, and what they are waiting for, is `_provisional` in `flutter3d_webgl/test/cross_backend_test.dart`: the comparison is skipped with the reason printed instead of quietly missing, and the check beside it fails the moment a reference lands and the name is still there. @@ -65,7 +65,7 @@ The browser sets are recorded when a branch lands, not beside it (`golden_web.sh {{golden3 shadow-teapot | One scene, three sets: a GPU through Metal, a rasteriser written in Dart, and a browser. The pictures on this site are the Impeller set.}}
    -

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps seventy-nine scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    +

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps eighty-one scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    `cross_backend_test.dart` compares them with per-scene budgets, and any new backend has to pass `flutter3d_conformance` before it counts as one. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index e35fc6e2d..2bfd9968b 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -241,6 +241,14 @@ in all four sets (CPU and WebGPU match Impeller to the pixel, WebGL2 0.59%, all on edges). *Left:* the diagonal search and corner rounding of the paper, and SMAA's 2x and 4x modes, which ride on the temporal jitter. +**P2 done.** `LookSettings.distortion` (radial, barrel or pincushion, held +on the frame's border; everything over the scene bends with it, the film +does not), `BloomSettings.lensFlare` (ghosts and a halo drawn from the glow, +added to it before the composite) and `CubeLut` (`.cube`, 3D or 1D with a +domain, into the composite's strip, `upload` onto a device). `lens-flare` +and `lens-distortion` are recorded in all four sets. *Left:* the +anamorphic streak, and a starburst from an aperture texture. + Already ours and not theirs, to keep: volumetric fog, weighted blended OIT, motion blur, local exposure and HDR out, EVSM, impostors, Hi-Z and software occlusion, generated LOD, WebGPU, the CPU backend, retargeting. diff --git a/tool/ci.sh b/tool/ci.sh index 654ea9c5f..2d5146a50 100755 --- a/tool/ci.sh +++ b/tool/ci.sh @@ -9,7 +9,7 @@ # # What it does NOT do, stated so the gap is not mistaken for coverage: # -# * The Impeller half of the golden set. Those seventy-nine scenes render through +# * The Impeller half of the golden set. Those eighty-one scenes render through # flutter_gpu and need a real device, so they run from # packages/flutter3d/tool/golden.sh on a machine with a GPU. The software # half runs here, and cross_backend_test.dart compares the two committed diff --git a/tool/structure/rules.dart b/tool/structure/rules.dart index 2b5964f07..b42396c81 100644 --- a/tool/structure/rules.dart +++ b/tool/structure/rules.dart @@ -2623,6 +2623,10 @@ const List _countedInWords = [ 'sixty-seven', 'sixty-eight', 'sixty-nine', 'seventy', 'seventy-one', 'seventy-two', 'seventy-three', 'seventy-four', 'seventy-five', 'seventy-six', 'seventy-seven', 'seventy-eight', 'seventy-nine', 'eighty', + 'eighty-one', 'eighty-two', 'eighty-three', 'eighty-four', 'eighty-five', + 'eighty-six', 'eighty-seven', 'eighty-eight', 'eighty-nine', 'ninety', + 'ninety-one', 'ninety-two', 'ninety-three', 'ninety-four', 'ninety-five', + 'ninety-six', 'ninety-seven', 'ninety-eight', 'ninety-nine', 'one hundred', ]; /// A count said both ways, so a finding can be read and searched for. From c9e26f9cfed3d40d83ffa04c7e5c4ec044799d42 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 04:35:53 +0300 Subject: [PATCH 032/141] A mirror in the floor, and a camera drawing into a texture P4. A PlanarReflectorNode has the world drawn again through a mirrored camera before the scene, and its surfaces show that picture; RenderSettings.planarReflections switches it on. A RenderTexture is a camera that draws into a texture a material can sample, for a monitor, a portal or a map, added to the scene and drawn every frame or once. planar-mirror and render-texture are recorded in all four reference sets. Taken from the work an agent left on its own branch, whose last commit carried no message of its own. --- ARCHITECTURE.md | 9 +- README.md | 7 +- packages/flutter3d/README.md | 4 +- .../example/lib/src/spike/backend_web.dart | 2 +- .../example/lib/src/spike/golden_runner.dart | 2 +- .../example/lib/src/spike/golden_scenes.dart | 19 + .../example/lib/src/spike/golden_stages.dart | 153 ++++++ .../lib/src/spike/golden_store_web.dart | 4 +- packages/flutter3d/test/ambient_test.dart | 2 +- packages/flutter3d/test/anisotropy_test.dart | 4 +- packages/flutter3d/test/auto_batch_test.dart | 4 +- .../flutter3d/test/binding_contract_test.dart | 54 ++ packages/flutter3d/test/fog_test.dart | 2 +- .../flutter3d/test/goldens/planar-mirror.png | Bin 0 -> 25348 bytes .../flutter3d/test/goldens/render-texture.png | Bin 0 -> 25437 bytes packages/flutter3d/test/grade_test.dart | 2 +- packages/flutter3d/test/halation_test.dart | 2 +- .../test/irradiance_bounce_test.dart | 2 +- .../flutter3d/test/masked_shadow_test.dart | 2 +- .../test/material_vertex_stage_test.dart | 2 +- .../test/per_view_exposure_test.dart | 2 +- .../flutter3d/test/render_scale_test.dart | 2 +- packages/flutter3d/test/sharpen_test.dart | 2 +- .../flutter3d/test/sky_procedural_test.dart | 2 +- packages/flutter3d/test/ssao_blur_test.dart | 2 +- packages/flutter3d_conformance/CHANGELOG.md | 5 + .../lib/src/shader_link_checks.dart | 12 +- packages/flutter3d_core/CHANGELOG.md | 24 + .../flutter3d_core/lib/flutter3d_core.dart | 3 + .../lib/src/engine/render/frame_plan.dart | 12 + .../lib/src/engine/render/mirror_view.dart | 96 ++++ .../src/engine/render/render_settings.dart | 32 +- .../lib/src/engine/render/render_texture.dart | 120 +++++ .../lib/src/engine/render/renderer.dart | 64 ++- .../engine/render/renderer_frame_nodes.dart | 147 +++++ .../engine/render/renderer_mesh_encode.dart | 13 +- .../engine/render/renderer_planar_pass.dart | 406 ++++++++++++++ .../src/engine/render/renderer_post_pass.dart | 4 +- .../engine/render/renderer_scene_pass.dart | 15 + .../engine/render/renderer_shadow_pass.dart | 2 +- .../engine/scene/planar_reflector_node.dart | 95 ++++ .../lib/src/engine/scene/scene.dart | 35 ++ .../lib/src/formats/lighting_model.dart | 19 + .../test/engine/mirror_view_test.dart | 104 ++++ .../formats/ktx2_supercompression_test.dart | 2 +- packages/flutter3d_cpu/CHANGELOG.md | 15 + .../flutter3d_cpu/lib/src/cpu_encoder.dart | 7 + .../lib/src/cpu_shaders_builtin.dart | 4 + .../lib/src/cpu_shaders_planar.dart | 83 +++ .../lib/src/cpu_shaders_post.dart | 2 +- .../flutter3d_cpu/lib/src/cpu_texture.dart | 2 +- .../flutter3d_cpu/lib/src/portable_log2.dart | 2 +- .../test/cross_backend_test.dart | 5 + .../test/goldens/planar-mirror.png | Bin 0 -> 22543 bytes .../test/goldens/render-texture.png | Bin 0 -> 22278 bytes .../test/planar_reflection_test.dart | 384 +++++++++++++ .../test/gpu_formats_test.dart | 2 +- packages/flutter3d_shaders/CHANGELOG.md | 8 + .../lib/flutter3d_shaders.dart | 2 + .../flutter3d_shaders/lib/stage_bindings.dart | 8 + .../flutter3d_shaders/lib/typed_blocks.dart | 34 ++ .../flutter3d_shaders/lib/uniform_blocks.dart | 17 + .../shaders/flutter3d.shaderbundle.json | 8 + .../shaders/lighting/planar_reflection.frag | 84 +++ .../shaders/post/render_texture_encode.frag | 50 ++ packages/flutter3d_webgl/CHANGELOG.md | 3 + .../flutter3d_webgl/lib/engine_shaders.dart | 510 ++++++++++++++++++ .../test/cross_backend_test.dart | 6 + .../test/goldens/planar-mirror.png | Bin 0 -> 25340 bytes .../test/goldens/render-texture.png | Bin 0 -> 25430 bytes .../test/lighting_models_test.dart | 2 +- packages/flutter3d_webgl/tool/golden_web.sh | 2 +- packages/flutter3d_webgpu/CHANGELOG.md | 4 + .../flutter3d_webgpu/lib/engine_shaders.dart | 294 ++++++++++ .../test/cross_backend_test.dart | 3 + .../test/goldens/planar-mirror.png | Bin 0 -> 25361 bytes .../test/goldens/render-texture.png | Bin 0 -> 25450 bytes site/content/core/architecture.md | 4 +- site/content/core/backends.md | 7 +- site/content/core/index.md | 2 +- site/content/core/rendering.md | 41 +- site/content/quickstart.md | 2 +- site/content/reference/packages.md | 2 +- site/content/reference/testing.md | 16 +- tasks/0.9-engine-roadmap.md | 54 ++ tool/ci.sh | 2 +- 86 files changed, 3103 insertions(+), 64 deletions(-) create mode 100644 packages/flutter3d/test/goldens/planar-mirror.png create mode 100644 packages/flutter3d/test/goldens/render-texture.png create mode 100644 packages/flutter3d_core/lib/src/engine/render/mirror_view.dart create mode 100644 packages/flutter3d_core/lib/src/engine/render/render_texture.dart create mode 100644 packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart create mode 100644 packages/flutter3d_core/lib/src/engine/scene/planar_reflector_node.dart create mode 100644 packages/flutter3d_core/test/engine/mirror_view_test.dart create mode 100644 packages/flutter3d_cpu/lib/src/cpu_shaders_planar.dart create mode 100644 packages/flutter3d_cpu/test/goldens/planar-mirror.png create mode 100644 packages/flutter3d_cpu/test/goldens/render-texture.png create mode 100644 packages/flutter3d_cpu/test/planar_reflection_test.dart create mode 100644 packages/flutter3d_shaders/shaders/lighting/planar_reflection.frag create mode 100644 packages/flutter3d_shaders/shaders/post/render_texture_encode.frag create mode 100644 packages/flutter3d_webgl/test/goldens/planar-mirror.png create mode 100644 packages/flutter3d_webgl/test/goldens/render-texture.png create mode 100644 packages/flutter3d_webgpu/test/goldens/planar-mirror.png create mode 100644 packages/flutter3d_webgpu/test/goldens/render-texture.png diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index ceec68270..aaad1327e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -406,6 +406,7 @@ written and read in: ``` point shadows (static) → point shadows → directional shadows → shadow moments reflection probe 0 … n → irradiance update +render textures → planar reflections scene → decals → scene colour copy → transparent → object ids (overlay nodes) → reflections → luminance → depth pyramid ssao → ssao blur → contact shadows → contact shadow resolve @@ -2690,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10914 tests** across 43 packages and 10 applications | +| Unit tests | **10929 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | @@ -2730,7 +2731,7 @@ material parameter — is the case `doc/boundary-0.5.0.md` worked through for set belongs to whoever builds on the package rather than to the package itself. -**Golden render tests.** 82 scenes against **four complete independent +**Golden render tests.** 84 scenes against **four complete independent reference sets** — Impeller, the software rasteriser, WebGL2 and WebGPU — each held to zero differing pixels against its own set, with a per-channel tolerance of 8. @@ -3142,7 +3143,7 @@ metres. The directional light's cascades fit the view up to that distance and nothing beyond it casts — a level whose far end matters visually wants the number raised, and pays for it in texels. -**The web backend draws all eighty-two golden scenes the way Impeller does**, +**The web backend draws all eighty-four golden scenes the way Impeller does**, between 0.01% and 0.42% of pixels differing by more than 8 per channel — the silhouette's worth of disagreement two rasterisers always have. Two of those numbers fell when the minification filter learned to read a sampler's @@ -3258,7 +3259,7 @@ and charge each of them the bytes, and neither is a change to make on somebody else's behalf. A build that wants it says so in one flag, and the engine's own example takes the same answer from `?backend=webgpu` in the URL — a query parameter rather than a define, because the browser golden stand's whole saving -is one dart2js run serving eighty-two scenes and both browser backends. +is one dart2js run serving eighty-four scenes and both browser backends. **`flutter3d_shaders` is one text and no two backends take it the same way.** Impeller compiles the GLSL with `impellerc`; the WebGL2 generator translates it diff --git a/README.md b/README.md index 8b5be67b7..2e7c6d502 100644 --- a/README.md +++ b/README.md @@ -158,9 +158,12 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10914 tests across forty-three packages and nine applications. The +There are 10929 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other -half is rendered by the software backend, which is what makes 82 scenes +half is rendered by the software backend, which is what makes 84 scenes +There are 10895 tests across forty-two packages and nine applications. The +only ones that need a GPU are the Impeller half of the golden set. The other +half is rendered by the software backend, which is what makes 84 scenes checkable in a headless run. Several steps run in a browser. `flutter test --platform chrome` covers the two diff --git a/packages/flutter3d/README.md b/packages/flutter3d/README.md index c0bd401a8..b95dfb025 100644 --- a/packages/flutter3d/README.md +++ b/packages/flutter3d/README.md @@ -104,7 +104,7 @@ What works today: `CUBICSPLINE` with authored tangents), slerped rotations, an `AnimationPlayer` with play/pause/seek/speed and once/loop/ping-pong, and the decoded node hierarchy rebuilt on instantiation so an animated parent carries its subtree; -- 1621 tests covering projection, scene, sorting, debug draw, raycasting, +- 1622 tests covering projection, scene, sorting, debug draw, raycasting, animation, skinning, lighting, render targets, BVH, LOD, glTF, OBJ and `.f3d`, plus a real frame drawn through `flutter3d_cpu`'s software rasteriser for the ones that need one, all without a GPU. The geometry the engine is written in @@ -322,7 +322,7 @@ lib/src/engine/assets/ glTF, OBJ and .f3d decoders, isolate loading, ca example/lib/ the demo, and the frame capture hook skills/ the conventions, as agent skills — see below bin/skills.dart what copies them into a project that uses this -test/ 1621 tests, all runnable without a GPU +test/ 1622 tests, all runnable without a GPU ``` The GLSL is not here. Every shader this package draws with lives in diff --git a/packages/flutter3d/example/lib/src/spike/backend_web.dart b/packages/flutter3d/example/lib/src/spike/backend_web.dart index dc0c77486..c70739e85 100644 --- a/packages/flutter3d/example/lib/src/spike/backend_web.dart +++ b/packages/flutter3d/example/lib/src/spike/backend_web.dart @@ -13,7 +13,7 @@ import 'golden_store.dart'; /// /// Read from the URL, exactly the way the golden scene and the record/compare /// direction are read, and for exactly the same arithmetic: one dart2js run -/// serves eighty-two scenes because the scene is a query parameter, and one +/// serves eighty-four scenes because the scene is a query parameter, and one /// dart2js run serves the browser backends because this is one too. A /// `--dart-define` here would be a build per backend, which is the whole saving /// of the browser golden stand spent on a single word. diff --git a/packages/flutter3d/example/lib/src/spike/golden_runner.dart b/packages/flutter3d/example/lib/src/spike/golden_runner.dart index 1e07851cd..b28eced5d 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_runner.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_runner.dart @@ -122,7 +122,7 @@ final class GoldenRunner { scene, // The store's answer wins where it has one. A desktop run takes the // direction from the environment and a browser run from the URL, for the - // same reason on both: one build has to serve eighty-two scenes in both + // same reason on both: one build has to serve eighty-four scenes in both // directions, and anything the compiler sees is another build. update: updateOverride ?? diff --git a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart index 3314e3479..3b8914c8e 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart @@ -1179,6 +1179,25 @@ final List kGoldenScenes = [ stage: GoldenStages.decalFloor, configure: GoldenStages.decalFloorSettings, ), + + // P4. A mirror in the floor, and a monitor showing a second camera. + const GoldenScene( + name: 'planar-mirror', + source: 'Cube', + lights: {'none'}, + bloom: false, + ground: false, + stage: GoldenStages.planarMirror, + configure: GoldenStages.planarMirrorSettings, + ), + const GoldenScene( + name: 'render-texture', + source: 'Cube', + lights: {'none'}, + bloom: false, + ground: false, + stage: GoldenStages.renderTexture, + ), ]; /// Looks up a scene by name. diff --git a/packages/flutter3d/example/lib/src/spike/golden_stages.dart b/packages/flutter3d/example/lib/src/spike/golden_stages.dart index 0af34b6b9..cb85ba64f 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_stages.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_stages.dart @@ -1715,4 +1715,157 @@ abstract final class GoldenStages { static RenderSettings decalFloorSettings(RenderSettings settings) => settings.copyWith(decals: const DecalSettings(enabled: true)); + // ------------------------------------------------------------------ P4 + + /// The floor `planar-mirror` and `render-texture` stand on: grey, lit. + static MeshNode _p4Floor(GraphicsDevice device) => _slab( + device, + Vector3(14.0, 0.2, 14.0), + Vector3(0.0, -0.1, 0.0), + Material( + name: 'floor', + lighting: LightingModel.lambert, + baseColor: Vector4(0.45, 0.45, 0.45, 1.0), + ), + ); + + /// What both P4 scenes look at: a red box, a blue ball and a yellow post, + /// lit by the sun, so a picture of them has shading and shadows to get + /// right and no two of its sides look alike. + static List _p4Props(GraphicsDevice device) => [ + _slab( + device, + Vector3(0.8, 0.8, 0.8), + Vector3(-0.7, 0.4, -0.3), + Material(name: 'box', baseColor: Vector4(0.8, 0.15, 0.1, 1.0)), + ), + _sphere( + device, + Vector3(0.6, 0.45, 0.2), + Material(name: 'ball', baseColor: Vector4(0.15, 0.3, 0.85, 1.0)), + radius: 0.45, + ), + _slab( + device, + Vector3(0.25, 1.6, 0.25), + Vector3(0.2, 0.8, -1.2), + Material(name: 'post', baseColor: Vector4(0.9, 0.75, 0.15, 1.0)), + ), + ]; + + /// `planar-mirror`: a black mirror set into the floor, the props standing + /// on and around it — `planar_reflection_test.dart`'s mirror under a sun. + /// + /// A slightly blue tint, so the reflection cannot be mistaken for a hole + /// in the floor showing the world upside down, and the reflection at half + /// the view's resolution, which is the default and the path a game takes. + static Future planarMirror(GoldenStage stage) async { + final device = stage.device; + stage.sun.setLocalForward(Vector3(-0.6, -1.0, -0.4).normalized()); + stage.scene.ambientIntensity = 0.2; + final mirror = MeshNode( + DeviceMesh.upload( + device, + const PlaneShape(width: 3.2, depth: 2.4).build(), + ), + Material( + name: 'mirror', + lighting: LightingModel.unlit, + baseColor: Vector4(0.02, 0.02, 0.03, 1.0), + ), + name: 'mirror', + )..setPosition(0.0, 0.005, 0.3); + return GoldenStaged( + nodes: [ + _p4Floor(device), + mirror, + ..._p4Props(device), + PlanarReflectorNode( + surfaces: [mirror], + tint: Vector3(0.85, 0.9, 1.0), + name: 'reflector', + )..setPosition(0.0, 0.005, 0.0), + ], + everyFrame: (_, _) => + _look(stage.camera, Vector3(0.4, 2.2, 4.6), Vector3(0.0, 0.4, -0.2)), + ); + } + + static RenderSettings planarMirrorSettings(RenderSettings settings) => + settings.copyWith( + planarReflections: const PlanarReflectionSettings(enabled: true), + ); + + /// `render-texture`: a monitor on a stand showing what a camera off to the + /// side sees of the props — `planar_reflection_test.dart`'s screen. + /// + /// The screen is unlit, so it shows the picture as the camera took it, and + /// left out of the camera's own picture, which would otherwise hold the + /// screen's back. + static Future renderTexture(GoldenStage stage) async { + final device = stage.device; + stage.sun.setLocalForward(Vector3(-0.6, -1.0, -0.4).normalized()); + stage.scene.ambientIntensity = 0.2; + final watcher = CameraNode(name: 'watcher') + ..projection = const PerspectiveProjection( + fovYRadians: math.pi / 4, + near: 0.1, + far: 50.0, + ) + ..setPosition(-3.2, 1.6, 2.0) + ..lookAt(Vector3(0.0, 0.5, -0.4)); + final picture = RenderTexture.create( + device, + camera: watcher, + width: 128, + height: 96, + clearColor: Vector4(0.35, 0.45, 0.6, 1.0), + ); + // A plane stood up to face the eye: its v runs down the screen, the way + // a picture's rows do. A box's sides run v up and would show it upside + // down, as they would a photograph. + const turn = -0.5; + final facing = Quaternion.axisAngle(Vector3(0.0, 1.0, 0.0), turn); + final screen = MeshNode( + DeviceMesh.upload( + device, + const PlaneShape(width: 1.6, depth: 1.2).build(), + ), + Material( + name: 'screen', + lighting: LightingModel.unlit, + albedo: picture.texture, + ), + name: 'screen', + )..setPosition(1.9, 1.3, -0.6); + screen.setRotation( + facing * Quaternion.axisAngle(Vector3(1.0, 0.0, 0.0), math.pi / 2), + ); + final back = _slab( + device, + Vector3(1.7, 1.3, 0.04), + Vector3(1.9 + 0.03 * math.sin(-turn), 1.3, -0.6 - 0.03 * math.cos(turn)), + Material(name: 'bezel', baseColor: Vector4(0.1, 0.1, 0.12, 1.0)), + )..setRotation(facing); + final stand = _slab( + device, + Vector3(0.12, 0.7, 0.12), + Vector3(1.9, 0.35, -0.6), + Material(name: 'stand', baseColor: Vector4(0.2, 0.2, 0.22, 1.0)), + ); + picture.excluded.addAll([screen, back, stand]); + stage.scene.addRenderTexture(picture); + return GoldenStaged( + nodes: [ + _p4Floor(device), + ..._p4Props(device), + screen, + back, + stand, + watcher, + ], + everyFrame: (_, _) => + _look(stage.camera, Vector3(0.6, 2.0, 4.8), Vector3(0.6, 0.7, -0.4)), + ); + } } diff --git a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart index 7b38e2dd1..ea128d7fe 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart @@ -40,7 +40,7 @@ void reportLine(String message) { /// The scene named in the page's URL, if any. /// /// A run-time choice here, where the desktop path takes a compile-time define. -/// The reason is arithmetic: the suite is eighty-two scenes, and rebuilding +/// The reason is arithmetic: the suite is eighty-four scenes, and rebuilding /// the bundle for each is seventy-eight dart2js runs to compare seventy-eight /// pictures. One build and seventy-eight navigations is the same information /// in a fraction of the time. @@ -58,7 +58,7 @@ String? get sceneOverride { /// Whether this run records rather than compares, from the page's URL. /// /// A run-time choice for the same reason [sceneOverride] is one: the suite is -/// eighty-two scenes and rebuilding for each would be seventy-eight dart2js +/// eighty-four scenes and rebuilding for each would be seventy-eight dart2js /// runs. One build serves both directions, and the URL says which. bool get updateOverride => Uri.base.queryParameters['update'] == '1'; diff --git a/packages/flutter3d/test/ambient_test.dart b/packages/flutter3d/test/ambient_test.dart index 7ba0651cc..004f19b0b 100644 --- a/packages/flutter3d/test/ambient_test.dart +++ b/packages/flutter3d/test/ambient_test.dart @@ -141,7 +141,7 @@ void main() { // approximately, bit for bit. // // Mutation: default either end of the hemisphere to anything but white. - // Eighty-two goldens move at once. + // Eighty-four goldens move at once. final lit = await _pixels(_ball(), _noSky); final top = _band(lit, 0.15, 0.35); final bottom = _band(lit, 0.65, 0.85); diff --git a/packages/flutter3d/test/anisotropy_test.dart b/packages/flutter3d/test/anisotropy_test.dart index 48e124c20..916134f1b 100644 --- a/packages/flutter3d/test/anisotropy_test.dart +++ b/packages/flutter3d/test/anisotropy_test.dart @@ -24,7 +24,7 @@ /// which spends the taps and throws away what they bought — contrast drops /// *below* the trilinear baseline and the first assertion fails; and letting /// a sampler that asked for one reach the new path at all, which would move a -/// backend eighty-two golden scenes are recorded on. +/// backend eighty-four golden scenes are recorded on. /// /// **One they do not catch, said rather than hidden:** taking the taps along /// the *short* axis instead of the long one passes every line here. The @@ -213,7 +213,7 @@ void main() { }); test('asking for one tap is the old sampler exactly', () async { - // What makes this safe to add to the backend eighty-two golden scenes are + // What makes this safe to add to the backend eighty-four golden scenes are // recorded on. Not "close": the same bytes, because with `anisotropy` at // one the new path is not reached at all. expect(await _floorAt(1), await _floorAt(1)); diff --git a/packages/flutter3d/test/auto_batch_test.dart b/packages/flutter3d/test/auto_batch_test.dart index c6a94e767..fbae1707a 100644 --- a/packages/flutter3d/test/auto_batch_test.dart +++ b/packages/flutter3d/test/auto_batch_test.dart @@ -22,7 +22,7 @@ /// three backends.** It computes in Dart doubles; Impeller, WebGL and WebGPU /// compute in 32-bit floats, where those two expressions have far less room /// before they part. Nothing here can run them, so the setting stays off by -/// default — which is also what keeps the eighty-two goldens where they are. +/// default — which is also what keeps the eighty-four goldens where they are. library; import 'dart:math' as math; @@ -191,7 +191,7 @@ void main() { }); test('nothing is batched unless it is asked for', () async { - // Which is what keeps the eighty-two goldens where they are: the default + // Which is what keeps the eighty-four goldens where they are: the default // frame is the frame it always was, to the byte. final device = _device(); final scene = _field(device, count: 100); diff --git a/packages/flutter3d/test/binding_contract_test.dart b/packages/flutter3d/test/binding_contract_test.dart index 727332379..a4f913132 100644 --- a/packages/flutter3d/test/binding_contract_test.dart +++ b/packages/flutter3d/test/binding_contract_test.dart @@ -192,4 +192,58 @@ void main() { expect(ran, contains('decals')); expect(unbound, isEmpty, reason: unbound.join('\n')); }); + + test('nor does a planar reflection, or a camera into a texture', () { + // `P4`: the mirrored camera draws the lit scene, the reflection is laid + // over a floor through its own stage with its picture and its block, and + // a render texture's light is encoded through a full-screen stage. All + // three under a shadowed sun, which is what a lit capture binds most of. + // + // Mutation: binding the reflection without its block, or the floor's + // albedo slot to a stage that dropped it, is a violation here and a + // crash on Metal. + final ran = []; + final unbound = _unbound((FakeBackend device, Renderer renderer) { + final floor = MeshNode( + DeviceMesh.upload(device, const PlaneShape(width: 6, depth: 6).build()), + Material(name: 'floor'), + ); + final camera = CameraNode() + ..setPosition(0.0, 3.0, 4.0) + ..lookAt(Vector3.zero()); + final scene = Scene() + ..add(floor) + ..add( + MeshNode( + DeviceMesh.upload( + device, + CuboidShape(size: Vector3.all(0.6)).build(), + ), + Material(name: 'box'), + )..setPosition(0.0, 0.8, 0.0), + ) + ..add(PlanarReflectorNode(surfaces: [floor])) + ..add( + LightNode(intensity: 3.0) + ..setPosition(2.0, 3.0, 4.0) + ..lookAt(Vector3.zero()), + ) + ..add(camera) + ..addRenderTexture( + RenderTexture.create(device, camera: camera, width: 16, height: 16), + ); + final result = renderer.render( + width: 64, + height: 64, + scene: scene, + views: [RenderView(camera: camera)], + settings: const RenderSettings( + planarReflections: PlanarReflectionSettings(enabled: true), + ), + ); + ran.addAll(result.passes.map((p) => p.name)); + }); + expect(ran, containsAll(['planar reflections', 'render textures'])); + expect(unbound, isEmpty, reason: unbound.join('\n')); + }); } diff --git a/packages/flutter3d/test/fog_test.dart b/packages/flutter3d/test/fog_test.dart index a925c045b..d1a9226e5 100644 --- a/packages/flutter3d/test/fog_test.dart +++ b/packages/flutter3d/test/fog_test.dart @@ -151,7 +151,7 @@ void main() { test('no fog is byte-identical to the fog nobody asked for', () async { // The early return at zero density is not an optimisation. The golden sets - // — eighty-two scenes, zero-pixel threshold, three backends — are all + // — eighty-four scenes, zero-pixel threshold, three backends — are all // recorded with the default `FogSettings()`, and this is the property that // lets them stay recorded. final none = FogSettings(); diff --git a/packages/flutter3d/test/goldens/planar-mirror.png b/packages/flutter3d/test/goldens/planar-mirror.png new file mode 100644 index 0000000000000000000000000000000000000000..ea98c6147f57ff728b9280264aac1c96e5ec9a47 GIT binary patch literal 25348 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 DBdR!{ literal 0 HcmV?d00001 diff --git a/packages/flutter3d/test/goldens/render-texture.png b/packages/flutter3d/test/goldens/render-texture.png new file mode 100644 index 0000000000000000000000000000000000000000..fd4f68b6b3894fd2e2a20fbb573531416ee99d59 GIT binary patch literal 25437 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 _ramp(LookSettings look, {int width = 256}) async { void main() { test('every default is an exact identity', () async { - // The promise eighty-two goldens rest on. Neutral for these three is + // The promise eighty-four goldens rest on. Neutral for these three is // (0,0,0) added, (1,1,1) as an exponent and (1,1,1) multiplied, and the // composite has to take all three without moving a byte. final plain = await _ramp(const LookSettings()); diff --git a/packages/flutter3d/test/halation_test.dart b/packages/flutter3d/test/halation_test.dart index 5d7a64038..1490acdab 100644 --- a/packages/flutter3d/test/halation_test.dart +++ b/packages/flutter3d/test/halation_test.dart @@ -87,7 +87,7 @@ void main() { test('zero is an exact identity, and is the default', () async { // The multiplier is one on every channel at zero, so the chain comes out - // byte for byte as it did — which is what eighty-two goldens need. + // byte for byte as it did — which is what eighty-four goldens need. expect(const BloomSettings().halation, 0.0); final a = await _glow(halation: 0.0); final b = await _glow(halation: 0.0); diff --git a/packages/flutter3d/test/irradiance_bounce_test.dart b/packages/flutter3d/test/irradiance_bounce_test.dart index c75b642f6..0afe0f01f 100644 --- a/packages/flutter3d/test/irradiance_bounce_test.dart +++ b/packages/flutter3d/test/irradiance_bounce_test.dart @@ -136,7 +136,7 @@ double _tint(Uint8List rgba) { void main() { test('off, the picture is the bytes it was', () async { // The clause every recorded frame depends on. A field is null by default, - // so this is the path eighty-two goldens take. + // so this is the path eighty-four goldens take. final a = await _draw(field: false, redWall: true); final b = await _draw(field: false, redWall: true); expect(a, orderedEquals(b)); diff --git a/packages/flutter3d/test/masked_shadow_test.dart b/packages/flutter3d/test/masked_shadow_test.dart index 5d9aec39d..99374de13 100644 --- a/packages/flutter3d/test/masked_shadow_test.dart +++ b/packages/flutter3d/test/masked_shadow_test.dart @@ -223,7 +223,7 @@ void main() { test('a caster with no map keeps the shadow it always cast', () async { // **The half that lets this land.** A material that is not cut out goes // through the stage it has always gone through, with no sampler in the - // pipeline and no texture bound per draw, so the eighty-two goldens + // pipeline and no texture bound per draw, so the eighty-four goldens // recorded against that stage cannot move. Asking for MASK with no map to // read is the same case: there is nothing to cut out. final plain = await _frame(masked: false, withTexture: false); diff --git a/packages/flutter3d/test/material_vertex_stage_test.dart b/packages/flutter3d/test/material_vertex_stage_test.dart index 9528561a0..6dd0ec67b 100644 --- a/packages/flutter3d/test/material_vertex_stage_test.dart +++ b/packages/flutter3d/test/material_vertex_stage_test.dart @@ -172,7 +172,7 @@ void main() { // **The half that lets this land**, and the reason the seam is a null field // rather than a new required one: a material saying nothing about its vertex // stage goes through the pipeline it has always gone through, which is also - // why the eighty-two goldens cannot move. + // why the eighty-four goldens cannot move. final withSeam = await _draw(lighting: _plain); final device = CpuDevice( diff --git a/packages/flutter3d/test/per_view_exposure_test.dart b/packages/flutter3d/test/per_view_exposure_test.dart index 87c546ec5..29a3e2db6 100644 --- a/packages/flutter3d/test/per_view_exposure_test.dart +++ b/packages/flutter3d/test/per_view_exposure_test.dart @@ -157,7 +157,7 @@ void main() { // **The strongest form of "this costs nothing where it is not wanted".** // One view is the whole frame, so its rectangle is the whole histogram // and its exposure is the frame's. The composite takes its single-draw - // path, and eighty-two goldens stay where they are. + // path, and eighty-four goldens stay where they are. final off = await frame( const RenderSettings(autoExposure: AutoExposureSettings(enabled: true)), ); diff --git a/packages/flutter3d/test/render_scale_test.dart b/packages/flutter3d/test/render_scale_test.dart index 163deb70e..7147698b5 100644 --- a/packages/flutter3d/test/render_scale_test.dart +++ b/packages/flutter3d/test/render_scale_test.dart @@ -36,7 +36,7 @@ import 'package:vector_math/vector_math.dart'; void main() { test('one is the whole resolution, and is the default', () { - // Eighty-two goldens are recorded at the size they asked for. A default + // Eighty-four goldens are recorded at the size they asked for. A default // that shrank anything would move every one of them. expect(const RenderSettings().renderScale, 1.0); expect(_drawn(1.0), (width: 64, height: 64)); diff --git a/packages/flutter3d/test/sharpen_test.dart b/packages/flutter3d/test/sharpen_test.dart index 6d6d8d3a6..88ca2f974 100644 --- a/packages/flutter3d/test/sharpen_test.dart +++ b/packages/flutter3d/test/sharpen_test.dart @@ -95,7 +95,7 @@ int _steepest(List row) { void main() { test('zero is an exact identity, and is the default', () async { - // Eighty-two goldens go through this pass. The shader returns the centre + // Eighty-four goldens go through this pass. The shader returns the centre // untouched at zero rather than running a kernel that rounds to nothing, // because "rounds to nothing" is a claim about the target's bit depth. expect(const AntiAliasSettings().sharpen, 0.0); diff --git a/packages/flutter3d/test/sky_procedural_test.dart b/packages/flutter3d/test/sky_procedural_test.dart index 1ace3e97b..bbf8144f1 100644 --- a/packages/flutter3d/test/sky_procedural_test.dart +++ b/packages/flutter3d/test/sky_procedural_test.dart @@ -167,7 +167,7 @@ double _mean(Uint8List rgba, {required int channel}) { void main() { _hardEdgedSun(); test('no sky is byte-identical to the sky nobody asked for', () async { - // The property eighty-two goldens rest on: a frame with no sky in it is + // The property eighty-four goldens rest on: a frame with no sky in it is // the frame this renderer has always drawn. // // Mutation: drop the early return. The sky is drawn into every scene that diff --git a/packages/flutter3d/test/ssao_blur_test.dart b/packages/flutter3d/test/ssao_blur_test.dart index 99f1ad190..305fe5d37 100644 --- a/packages/flutter3d/test/ssao_blur_test.dart +++ b/packages/flutter3d/test/ssao_blur_test.dart @@ -75,7 +75,7 @@ void main() { test('no taps is no pass, and is the default', () async { // The occlusion is already off by default, and this is a second // full-screen pass over it — so nothing pays for either until two things - // are switched on. Eighty-two goldens depend on the frame being what it + // are switched on. Eighty-four goldens depend on the frame being what it // was. expect(const AmbientOcclusionSettings().blurTaps, 0); expect(await _frame(blurTaps: 0), await _frame(blurTaps: 0)); diff --git a/packages/flutter3d_conformance/CHANGELOG.md b/packages/flutter3d_conformance/CHANGELOG.md index a054a117b..b0c269904 100644 --- a/packages/flutter3d_conformance/CHANGELOG.md +++ b/packages/flutter3d_conformance/CHANGELOG.md @@ -2,6 +2,11 @@ **The linking check names `Decal`**, so a backend whose bundle lacks it fails the check rather than the first frame with a decal in it. +**The linking check names `PlanarReflection` and `RenderTextureEncode`**: +the first through the three mesh vertex stages it is drawn over a +reflector's surfaces with, the second through the full-screen one, so a +backend whose bundle lacks either fails the check rather than the first +frame with a reflector or a render texture in it. ## 0.8.1 diff --git a/packages/flutter3d_conformance/lib/src/shader_link_checks.dart b/packages/flutter3d_conformance/lib/src/shader_link_checks.dart index 1e6038f6e..8f99e65ef 100644 --- a/packages/flutter3d_conformance/lib/src/shader_link_checks.dart +++ b/packages/flutter3d_conformance/lib/src/shader_link_checks.dart @@ -89,12 +89,18 @@ Future checkLinking(GraphicsDevice device) async { // // The x-ray stage reaches the same three, for the same reason. It declares // one output where the others declare two, which is the pairing least like - // the rest of this table and the one most worth linking here. + // the rest of this table and the one most worth linking here. `P4`'s + // planar reflection is drawn over its surfaces the same way, and declares + // one output for the same reason. for (final vertex in [ 'MeshVertex', 'MeshSkinnedVertex', 'MeshInstancedVertex', - ]) ...<(String, String)>[(vertex, 'ObjectId'), (vertex, 'Xray')], + ]) ...<(String, String)>[ + (vertex, 'ObjectId'), + (vertex, 'Xray'), + (vertex, 'PlanarReflection'), + ], // `R1`: the three stages a moved node is drawn through into the velocity // buffer, each with the one fragment stage that differences them. // `R4`: and with the stage that marks a blended surface reactive, which @@ -115,6 +121,8 @@ Future checkLinking(GraphicsDevice device) async { // `C3`: the surface buffer reduced for the occlusion readback. 'DepthPyramid', 'ProbePrefilter', + // `P4`: a render texture's light into the bytes a material reads. + 'RenderTextureEncode', 'BloomThreshold', 'BloomDownsample', 'BloomUpsample', diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index ec2ded81f..b6b7cf763 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -32,6 +32,30 @@ front of a decal is drawn over it. It needs three colour attachments and turns multisampling off, as every reader of the surface buffer does. +- **Planar reflections — `P4`.** A `PlanarReflectorNode` is a plane (its + node's origin, its local +Y the side it is seen from) and the meshes that + lie in it. Each frame, for each view that sees the plane's front, the + scene is drawn again through the view's camera mirrored in the plane, with + the projection's near plane moved onto the plane (`obliqueNearPlane`), so + nothing below it is reflected up through it. The picture, at `resolution` + of the view (half by default), is laid over the surfaces in the scene pass + straight after the opaque half, through the new `PlanarReflection` stage: + read by the pixel's place on screen, weighted by Schlick's Fresnel from + `reflectance` (one for a mirror, about 0.02 for water), tinted and fogged. + The surfaces keep their own materials and the surface buffer keeps + describing them. `RenderSettings.planarReflections` is off by default; a + frame without a visible reflector culls the pass either way. +- **A public camera into a texture — `P4`.** `RenderTexture.create` makes a + texture for a camera to draw into; `Scene.addRenderTexture` has it drawn + every frame (or once, and again after `invalidate`) before the scene, so + a material shows the picture in the frame it was taken. The pass is the + reflector's with an ordinary camera: meshes with the frame's lights, + shadows and sky, and no post chain. The texture holds sRGB bytes with the + top of the picture in its first row on every backend, as an uploaded image + does, so it goes into an albedo or emissive slot as one. +- `mirrorAcrossPlane`, `obliqueNearPlane` and `planeInEyeSpace` are public + in `mirror_view.dart`, and two passes join `RenderSettings.passOrder` + before `scene`: `render textures` and `planar reflections`. - **A reload reaches the contributors too.** `Renderer.relinkShaders` asks every `PassContributor` to drop what it linked, through the new `PassContributor.relinkShaders`, which does nothing by default. diff --git a/packages/flutter3d_core/lib/flutter3d_core.dart b/packages/flutter3d_core/lib/flutter3d_core.dart index a7a02cbc4..87cfe94dd 100644 --- a/packages/flutter3d_core/lib/flutter3d_core.dart +++ b/packages/flutter3d_core/lib/flutter3d_core.dart @@ -92,6 +92,7 @@ export 'src/engine/render/fullscreen_effect.dart'; export 'src/engine/render/key_sort.dart'; export 'src/engine/render/material.dart'; export 'src/engine/render/mesh_overlay.dart'; +export 'src/engine/render/mirror_view.dart'; export 'src/engine/render/pass_contribution.dart'; export 'src/engine/render/pass_contributor.dart'; export 'src/engine/render/probe_faces.dart'; @@ -99,6 +100,7 @@ export 'src/engine/render/procedural_texture.dart'; export 'src/engine/render/quality_table.dart'; export 'src/engine/render/render_list.dart'; export 'src/engine/render/render_node.dart'; +export 'src/engine/render/render_texture.dart'; export 'src/engine/render/render_view.dart'; export 'src/engine/render/renderer.dart'; export 'src/engine/render/shadow_slots.dart'; @@ -128,6 +130,7 @@ export 'src/engine/scene/occlusion/occlusion_buffer.dart'; export 'src/engine/scene/occlusion/occlusion_test.dart'; export 'src/engine/scene/occlusion/software_occlusion.dart'; export 'src/engine/scene/orbit_controller.dart'; +export 'src/engine/scene/planar_reflector_node.dart'; export 'src/engine/scene/posed_mesh.dart'; export 'src/engine/scene/projection.dart'; export 'src/engine/scene/raycaster.dart'; diff --git a/packages/flutter3d_core/lib/src/engine/render/frame_plan.dart b/packages/flutter3d_core/lib/src/engine/render/frame_plan.dart index 469f52bea..2c6d8fda2 100644 --- a/packages/flutter3d_core/lib/src/engine/render/frame_plan.dart +++ b/packages/flutter3d_core/lib/src/engine/render/frame_plan.dart @@ -119,4 +119,16 @@ abstract final class FrameResourceIds { /// the world that reflects it. static ResourceId reflectionProbe(int index) => ResourceId('reflection_probe_$index'); + + /// What the scene's `RenderTexture`s were drawn into — `P4`. + /// + /// One name for all of them, and what it stands for is the order: the + /// scene reads it optionally, so every camera into a texture draws before + /// the materials that show its picture. The pictures themselves are each + /// texture's own and outlive the frame. + static const ResourceId renderTextures = ResourceId('render_textures'); + + /// The mirrored pictures of the scene's planar reflectors — `P4`, one + /// name for all of them and every view, for [renderTextures]' reason. + static const ResourceId planarReflections = ResourceId('planar_reflections'); } diff --git a/packages/flutter3d_core/lib/src/engine/render/mirror_view.dart b/packages/flutter3d_core/lib/src/engine/render/mirror_view.dart new file mode 100644 index 000000000..5bee98c1c --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/render/mirror_view.dart @@ -0,0 +1,96 @@ +import 'package:vector_math/vector_math.dart'; + +/// The affine reflection of world space across the plane through [point] +/// with unit normal [normal] — `P4`. +/// +/// `x' = x - 2 (n·x - n·p) n`. Its own inverse, and it reverses handedness, +/// so whatever is drawn through it winds the other way round. +Matrix4 mirrorAcrossPlane(Vector3 normal, Vector3 point) { + final n = normal.normalized(); + final d = -n.dot(point); + return Matrix4( + 1.0 - 2.0 * n.x * n.x, + -2.0 * n.y * n.x, + -2.0 * n.z * n.x, + 0.0, + -2.0 * n.x * n.y, + 1.0 - 2.0 * n.y * n.y, + -2.0 * n.z * n.y, + 0.0, + -2.0 * n.x * n.z, + -2.0 * n.y * n.z, + 1.0 - 2.0 * n.z * n.z, + 0.0, + -2.0 * d * n.x, + -2.0 * d * n.y, + -2.0 * d * n.z, + 1.0, + ); +} + +/// [projection] with its near plane moved onto [plane], or null where that +/// cannot be done — `P4`'s clip plane. +/// +/// **The clip plane is the projection's own near plane, and not a plane the +/// shaders test.** No stage here has a clip distance to write, and adding +/// one would be an output on every vertex stage and a branch on every +/// backend for the one pass that wants it. Lengyel's oblique frustum +/// (*Oblique View Frustum Depth Projection and Clipping*, 2005) gets the same +/// cut out of the rasteriser's own near clip, for the price of the depth +/// precision the reflection does not need. +/// +/// [plane] is in the eye space [projection] maps from, as `(n, d)` with +/// `n·v + d >= 0` on the side that is kept; the eye has to be on the other +/// side, which is what a mirrored camera below a mirror is. The matrix is the +/// engine's: near at depth nought and far at one, the convention every +/// camera builds for before `toDepthRange` turns it into a device's. +/// +/// What it costs is the far plane, which tilts to pass through the frustum's +/// far corner on the plane's side: something far off to the other side can +/// be cut by it. A reflection is mostly of what is near the mirror. +/// +/// Null when the plane does not face away from the eye — a camera behind +/// the mirror, or level with it — since there is then nothing in front of +/// the mirror for the near plane to stand on. +Matrix4? obliqueNearPlane(Matrix4 projection, Vector4 plane) { + // The eye is the origin of its own space, so `d` is which side it is on. + if (plane.w >= 0.0) return null; + // The far corner of the frustum on the plane's side, in eye space: where + // the clip-space plane leans, at the far plane. + final inverse = Matrix4.copy(projection); + if (inverse.invert() == 0.0) return null; + final clipPlane = inverse.transposed().transform(plane.clone()); + final corner = inverse.transform( + Vector4(_sign(clipPlane.x), _sign(clipPlane.y), 1.0, 1.0), + ); + // The fourth row of the projection takes `corner` to w = 1 by + // construction; the third becomes the plane, scaled so the far plane + // `w - z = 0` still passes through it. + final along = plane.dot(corner); + if (along <= 0.0) return null; + final row = plane.scaled(1.0 / along); + return Matrix4.copy(projection)..setRow(2, row); +} + +/// The plane through [point] with [normal], in the eye space [view] maps +/// world space into, as `obliqueNearPlane` takes it — [offset] metres +/// further along the normal's opposite, so a surface standing in the plane +/// keeps its foot in the picture. +Vector4 planeInEyeSpace( + Matrix4 view, + Vector3 normal, + Vector3 point, { + double offset = 0.0, +}) { + final n = normal.normalized(); + final world = Vector4(n.x, n.y, n.z, -n.dot(point) + offset); + // A plane goes through the inverse transpose of what moves the points. + final inverse = Matrix4.copy(view)..invert(); + return inverse.transposed().transform(world); +} + +double _sign(double value) => value > 0.0 + ? 1.0 + : value < 0.0 + ? -1.0 + : 0.0; diff --git a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart index b9c78f358..853623e89 100644 --- a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart +++ b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart @@ -847,6 +847,23 @@ final class DecalSettings { DecalSettings(enabled: enabled ?? this.enabled); } +/// `P4`'s planar reflections: every visible `PlanarReflectorNode` gets the +/// world drawn again through a mirrored camera, and its surfaces show it. +/// +/// **Off by default, and not because a frame without a reflector would +/// pay.** With none in the scene the pass is inactive and culled whatever +/// this says. It is off because one reflector is the scene drawn a second +/// time per view, which is a cost a caller turns on, the way they turn on +/// every other effect that doubles a pass. +final class PlanarReflectionSettings { + const PlanarReflectionSettings({this.enabled = false}); + + final bool enabled; + + PlanarReflectionSettings copyWith({bool? enabled}) => + PlanarReflectionSettings(enabled: enabled ?? this.enabled); +} + /// Which viewport shading a frame is drawn with — `gfx-43n`, `44n`, `45n`. /// /// **A final class with const instances rather than an enum**, the shape @@ -1169,6 +1186,7 @@ final class RenderSettings { this.volumetricFog = const VolumetricFogSettings(), this.depthOfField = const DepthOfFieldSettings(), this.motionBlur = const MotionBlurSettings(), + this.planarReflections = const PlanarReflectionSettings(), this.decals = const DecalSettings(), this.viewportShading = const ViewportShadingSettings(), this.transparency = TransparencyMode.sorted, @@ -1288,7 +1306,7 @@ final class RenderSettings { /// eight-bit answer — `auto_batch_test.dart` holds a hundred cubes, turned and /// scaled, to byte equality. Impeller, WebGL and WebGPU compute in 32-bit /// floats, where those expressions have far less room before they part, and - /// nothing headless can run them. So the eighty-two goldens keep the frame + /// nothing headless can run them. So the eighty-four goldens keep the frame /// they have, and an application that wants the draw calls back asks. /// /// Shadows and picking are unaffected: both walk the scene themselves and @@ -1377,6 +1395,10 @@ final class RenderSettings { /// `R6`'s motion blur along the velocity buffer. final MotionBlurSettings motionBlur; + /// `P4`'s planar reflections, each a picture taken through a mirrored + /// camera before the scene. + final PlanarReflectionSettings planarReflections; + /// `P3`'s projected decals, painted onto the scene before its glass. final DecalSettings decals; @@ -1681,6 +1703,7 @@ final class RenderSettings { VolumetricFogSettings? volumetricFog, DepthOfFieldSettings? depthOfField, MotionBlurSettings? motionBlur, + PlanarReflectionSettings? planarReflections, DecalSettings? decals, ViewportShadingSettings? viewportShading, TransparencyMode? transparency, @@ -1728,6 +1751,7 @@ final class RenderSettings { volumetricFog: volumetricFog ?? this.volumetricFog, depthOfField: depthOfField ?? this.depthOfField, motionBlur: motionBlur ?? this.motionBlur, + planarReflections: planarReflections ?? this.planarReflections, decals: decals ?? this.decals, viewportShading: viewportShading ?? this.viewportShading, transparency: transparency ?? this.transparency, @@ -1805,6 +1829,10 @@ final class RenderSettings { 'shadow moments', // `L4`: the irradiance field's probes, a few a frame. 'irradiance update', + // `P4`: what the scene's cameras into textures and its planar reflectors + // see, drawn before the scene that shows them. + 'render textures', + 'planar reflections', // Reflection probes are registered here, one per probe in the scene, and // are named by index rather than by a constant — see [probePassName]. 'scene', @@ -2056,7 +2084,7 @@ final class DisplayTransform { /// **Everything here defaults to doing nothing, exactly.** Not nearly nothing: /// a vignette of zero multiplies by one and grain of zero adds zero, so a scene /// that asks for none of it composites to the same bytes it did before this -/// existed. Eighty-two goldens depend on that being exact, and the composite +/// existed. Eighty-four goldens depend on that being exact, and the composite /// pass already keeps the same promise for ambient occlusion. /// /// Applied in the composite rather than as passes of their own, which is the diff --git a/packages/flutter3d_core/lib/src/engine/render/render_texture.dart b/packages/flutter3d_core/lib/src/engine/render/render_texture.dart new file mode 100644 index 000000000..5394375fb --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/render/render_texture.dart @@ -0,0 +1,120 @@ +import 'package:flutter3d_hardware/flutter3d_hardware.dart'; +import 'package:vector_math/vector_math.dart'; + +import '../scene/camera_node.dart'; +import '../scene/mesh_node.dart'; + +/// A camera that draws into a texture rather than onto the screen — `P4`: +/// a security monitor, a portal, a minimap, a mirror at an angle no plane +/// would give. +/// +/// **The same pass as a planar reflection's**, pointed through an ordinary +/// camera instead of a mirrored one: the scene's meshes with the frame's +/// lights and shadows, its sky behind them, drawn before the frame's own +/// scene so a material can show the picture in the same frame it was taken. +/// No post chain runs on it, and nothing a contributor draws — particles, +/// splats — is in it. +/// +/// [texture] holds sRGB bytes, the way a picture loaded from a file does, so +/// it goes into a material's albedo or emissive slot as one: an unlit quad +/// shows exactly what the camera saw, and a lit one shows it lit again, like +/// a printed photograph. The light is multiplied by [exposure] and clipped +/// at one; tone mapping is left to the frame that shows it, which would +/// otherwise apply it twice. Its first row is the top of the picture on +/// every backend, as an uploaded image's is, so a mesh shows it the right +/// way up where it would show a photograph the right way up: a +/// `PlaneShape` stood up, yes; the sides of a `CuboidShape`, whose v runs +/// up, no. +/// +/// Owned by whoever made it: add it to a scene with +/// `Scene.addRenderTexture` to have it drawn, and give [texture] back with +/// `Renderer.releaseTextureAfterFrame` when it is done with. +final class RenderTexture { + /// A texture of [width] × [height] for [camera] to draw into, made on + /// [device]. + factory RenderTexture.create( + GraphicsDevice device, { + required CameraNode camera, + required int width, + required int height, + int layerMask = ~0, + Vector4? clearColor, + double exposure = 1.0, + bool refreshEveryFrame = true, + }) { + if (width < 1 || height < 1) { + throw ArgumentError( + 'a RenderTexture of ${width}x$height has no pixels to draw into; ' + 'ask for at least one each way', + ); + } + return RenderTexture._( + device.createTexture( + RenderTargetSpec( + width: width, + height: height, + format: TextureFormat.r8g8b8a8UNormInt, + ), + ), + camera: camera, + layerMask: layerMask, + clearColor: clearColor ?? Vector4(0.055, 0.062, 0.078, 1.0), + exposure: exposure, + refreshEveryFrame: refreshEveryFrame, + ); + } + + RenderTexture._( + this.texture, { + required this.camera, + required this.layerMask, + required this.clearColor, + required this.exposure, + required this.refreshEveryFrame, + }); + + /// What the camera drew, as sRGB bytes. Whatever the allocation held until + /// the first frame draws it; see [isDrawn]. + final TextureHandle texture; + + int get width => texture.width; + int get height => texture.height; + + /// Where the picture is taken from, with its own projection; the aspect is + /// the texture's. + CameraNode camera; + + /// Which layers the camera draws. + int layerMask; + + /// What shows where nothing was drawn and the sky is off, sRGB-encoded as + /// `RenderView.clearColor` is. + Vector4 clearColor; + + /// What the light is multiplied by before it is clipped and encoded. + double exposure; + + /// Whether every frame draws it again. False draws it once and then only + /// after [invalidate]: a portrait of a room nobody changes is one picture, + /// not sixty a second. + bool refreshEveryFrame; + + /// Meshes the camera leaves out — the screen showing the picture, most + /// often, which would otherwise show itself. + final Set excluded = {}; + + /// Asks for the picture to be drawn again on the next frame. + void invalidate() => _generation++; + + int _generation = 0; + int _drawnGeneration = -1; + + /// Whether [texture] holds a picture rather than an allocation's contents. + bool get isDrawn => _drawnGeneration >= 0; + + /// Whether the next frame has to draw it. + bool get isDue => refreshEveryFrame || _drawnGeneration != _generation; + + /// Called by the renderer once the picture is in [texture]. + void markDrawn() => _drawnGeneration = _generation; +} diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer.dart b/packages/flutter3d_core/lib/src/engine/render/renderer.dart index 71a3e8728..8a320bd08 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer.dart @@ -22,6 +22,7 @@ import '../scene/morph_state.dart'; import '../scene/occlusion/hi_z_occlusion.dart'; import '../scene/occlusion/occlusion_test.dart'; import '../scene/occlusion/software_occlusion.dart'; +import '../scene/planar_reflector_node.dart'; import '../scene/projection.dart'; import '../scene/reflection_probe_node.dart'; import '../scene/scene.dart'; @@ -42,6 +43,7 @@ import 'frame_work_budget.dart'; import 'identity_indices.dart'; import 'light_clusters.dart'; import 'material.dart'; +import 'mirror_view.dart'; import 'object_id_frame.dart'; import 'pass_contributor.dart'; import 'probe_faces.dart'; @@ -49,6 +51,7 @@ import 'procedural_texture.dart'; import 'render_list.dart'; import 'render_node.dart'; import 'render_settings.dart'; +import 'render_texture.dart'; import 'render_view.dart'; import 'scene_colour_chain.dart'; import 'shadow_slots.dart'; @@ -73,6 +76,7 @@ part 'renderer_irradiance_pass.dart'; part 'renderer_light_list.dart'; part 'renderer_mesh_encode.dart'; part 'renderer_pick_pass.dart'; +part 'renderer_planar_pass.dart'; part 'renderer_post_pass.dart'; part 'renderer_probe_pass.dart'; part 'renderer_resources.dart'; @@ -577,10 +581,13 @@ final class Renderer implements RenderServices { probe.capture, probe.filtered, ], + for (final planar in _planarStates.values) ...planar.textures, ]) { if (texture != null) device.releaseTexture(texture); } _probeStates.clear(); + _planarStates.clear(); + _planarMaterial.extraTextures.clear(); _fallbackAlbedo = null; _fallbackNormal = null; _fallbackBlack = null; @@ -816,6 +823,28 @@ final class Renderer implements RenderServices { final vm.Vector3 _probePosition = vm.Vector3.zero(); PipelineHandle? _probePrefilterPipeline; + /// The planar reflectors this renderer has drawn, by node: a picture per + /// view, kept across frames — see `renderer_planar_pass.dart`. + final Map _planarStates = + {}; + + /// What a reflector's surfaces are drawn again with: its picture, its view + /// and its reflectance, written per reflector, and the surface's own + /// sidedness written per draw. Held for the x-ray materials' reason. + final PlanarReflectionInfoBlock _planarInfo = PlanarReflectionInfoBlock(); + late final Material _planarMaterial = Material( + name: 'planar reflection', + lighting: LightingModel.planarReflection, + depthWrite: false, + depthCompare: CompareFunction.lessEqual, + parameterBlock: _planarInfo.name, + parameters: _planarInfo.members, + extraTextures: {}, + ); + + /// A render texture's exposure, for the encode into its bytes. + final RenderTextureInfoBlock _renderTextureInfo = RenderTextureInfoBlock(); + PipelineHandle? _debugLinePipeline; /// A 0, 1, 2, … index buffer for the debug overlay. @@ -1530,7 +1559,7 @@ final class Renderer implements RenderServices { /// `gfx-76n`'s strength, in x. Neutral is zero, which the composite reads as /// a multiplier of exactly one — the same arrangement the occlusion's - /// strength has, and for the same reason: eighty-two goldens go through this + /// strength has, and for the same reason: eighty-four goldens go through this /// block and "off" has to be a number the shader cancels, not one it nearly /// cancels. Float32List get _compositeContact => _compositeInfo.contact; @@ -2487,6 +2516,8 @@ final class Renderer implements RenderServices { required _ShadowMapNode shadow, required List<_ReflectionProbeNode> probes, required _IrradianceUpdateNode irradiance, + required _RenderTextureNode renderTextures, + required _PlanarReflectionNode planarReflections, required _SceneNode scene, required _BloomNode bloom, required _CompositeNode composite, @@ -2538,6 +2569,12 @@ final class Renderer implements RenderServices { // `L4`: beside the probes, for their reason — it draws the lit scene and // the scene reads what it writes. graph.addNode(irradiance); + // `P4`: beside the probes, for their reason — each draws the lit scene + // through a camera of its own, and the scene optionally reads what each + // provides. Registered whatever the scene holds, so the names are known. + graph + ..addNode(renderTextures) + ..addNode(planarReflections); graph ..addNode(scene) // `P3`: onto the opaque half, so the copy the glass reads holds the @@ -3635,6 +3672,18 @@ final class Renderer implements RenderServices { clearColor: ordered.first.clearColor, ), ], + renderTextures: _RenderTextureNode( + this, + scene: scene, + shadowCaster: shadowCaster, + ), + planarReflections: _PlanarReflectionNode( + this, + scene: scene, + views: ordered, + settings: settings, + shadowCaster: shadowCaster, + ), scene: _SceneNode( this, scene: scene, @@ -4079,6 +4128,7 @@ final class Renderer implements RenderServices { // probe that left the scene since last frame gives its cubes back here, // and this frame's one whole-cube capture is up for claiming again. _retireProbesNotIn(scene); + _retireReflectorsNotIn(scene); _wholeProbeCaptured = false; final probeNodes = <_ReflectionProbeNode>[ for (var i = 0; i < scene.probes.length; i++) @@ -4126,6 +4176,18 @@ final class Renderer implements RenderServices { shadowCaster: shadowCaster, clearColor: ordered.first.clearColor, ), + renderTextures: _RenderTextureNode( + this, + scene: scene, + shadowCaster: shadowCaster, + ), + planarReflections: _PlanarReflectionNode( + this, + scene: scene, + views: ordered, + settings: settings, + shadowCaster: shadowCaster, + ), scene: sceneNode, bloom: bloomNode, composite: compositeNode, diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart index f56673c8f..039dd0177 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_frame_nodes.dart @@ -542,6 +542,149 @@ final class _ReflectionProbeNode extends RenderNode { } } +/// Every [RenderTexture] the scene holds, drawn through its own camera — +/// `P4`. +/// +/// **One node for all of them**, with one name the scene optionally reads: +/// the name is what orders them before the materials that show them, and +/// the pictures are each texture's own, held by whoever made it rather than +/// by the frame. So the resource the node keeps stands for the order and is +/// a texture nothing samples through it. +/// +/// It optionally reads the shadow maps for a probe's reason: its pictures are +/// lit and shadowed the way the world is. +final class _RenderTextureNode extends RenderNode { + _RenderTextureNode( + this._renderer, { + required this.scene, + required this.shadowCaster, + }); + + final Renderer _renderer; + final Scene scene; + final int shadowCaster; + + @override + String get name => 'render textures'; + + @override + bool get isActive => scene.renderTextures.isNotEmpty; + + @override + List get optionalReads => const [ + FrameResourceIds.shadowMap, + FrameResourceIds.shadowMoments, + FrameResourceIds.cubeShadow, + FrameResourceIds.cubeShadowStatic, + ]; + + @override + List get keeps => const [ + FrameResourceIds.renderTextures, + ]; + + @override + void execute(NodeFrame frame) { + final shadows = SceneShadows.from(frame, casterIndex: shadowCaster); + for (final texture in scene.renderTextures) { + if (!texture.isDue) continue; + developer.Timeline.startSync('Renderer.renderTexture'); + _renderer._drawRenderTexture( + resources: frame.resources, + scene: scene, + texture: texture, + settings: frame.settings, + shadows: shadows, + passState: frame.state, + ); + developer.Timeline.finishSync(); + } + frame.resources.provide( + FrameResourceIds.renderTextures, + _renderer.fallbackBlack, + ); + } +} + +/// Every visible [PlanarReflectorNode]'s mirrored picture, a picture per view +/// — `P4`. +/// +/// One node and one name for all of them, for [_RenderTextureNode]'s reason: +/// a reflector has a picture per view, and the scene pass finds this view's +/// in the renderer's own state, where it is kept between frames. What the +/// name carries is that the pictures were drawn this frame, before the scene. +final class _PlanarReflectionNode extends RenderNode { + _PlanarReflectionNode( + this._renderer, { + required this.scene, + required this.views, + required this.settings, + required this.shadowCaster, + }); + + final Renderer _renderer; + final Scene scene; + + /// Every view, by priority — each gets its own mirrored camera. + final List views; + final RenderSettings settings; + final int shadowCaster; + + @override + String get name => 'planar reflections'; + + /// Asked of the scene when the graph is built, so a frame whose reflectors + /// are all hidden draws no picture for any of them. + @override + bool get isActive => + settings.planarReflections.enabled && + scene.reflectors.any( + (reflector) => + reflector.visibleInHierarchy && reflector.surfaces.isNotEmpty, + ); + + @override + List get optionalReads => const [ + FrameResourceIds.shadowMap, + FrameResourceIds.shadowMoments, + FrameResourceIds.cubeShadow, + FrameResourceIds.cubeShadowStatic, + ]; + + @override + List get keeps => const [ + FrameResourceIds.planarReflections, + ]; + + @override + void execute(NodeFrame frame) { + final shadows = SceneShadows.from(frame, casterIndex: shadowCaster); + for (final reflector in scene.reflectors) { + if (!reflector.visibleInHierarchy || reflector.surfaces.isEmpty) { + continue; + } + developer.Timeline.startSync('Renderer.planarReflection'); + _renderer._drawPlanarReflection( + resources: frame.resources, + scene: scene, + reflector: reflector, + state: _renderer._planarStates[reflector] ??= _PlanarState(), + views: views, + width: frame.width, + height: frame.height, + settings: frame.settings, + shadows: shadows, + passState: frame.state, + ); + developer.Timeline.finishSync(); + } + frame.resources.provide( + FrameResourceIds.planarReflections, + _renderer.fallbackBlack, + ); + } +} + /// The world, as a graph node — the pass everything else is ordered around. /// /// It writes `hdr_colour` and `surface_buffer`, which is what took those two @@ -648,6 +791,10 @@ final class _SceneNode extends RenderNode { // `L4`: the irradiance atlas the GPU keeps, for the same reason — the // lit draws read it when it is there and the bake when it is not. FrameResourceIds.irradianceAtlas, + // `P4`: what the cameras into textures drew, which a material here may + // show, and the mirrored pictures the reflectors' surfaces lay on. + FrameResourceIds.renderTextures, + FrameResourceIds.planarReflections, ]; /// **Both names always, including on a device that cannot attach the diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart index f4be28a5d..685fda932 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart @@ -21,12 +21,21 @@ part of 'renderer.dart'; /// material already carries it — `depthWrite` and `depthCompare` are the two /// fields a backdrop asked for, and a silhouette wants exactly those two. final class _DrawOverride { - const _DrawOverride({required this.material, required this.blend}); + const _DrawOverride({ + required this.material, + required this.blend, + this.fogged = false, + }); final Material material; /// Null is blending off, as it is on `setBlend`. final BlendState? blend; + + /// Whether the frame's fog reaches the draw. Not for a silhouette, which + /// is a marker; yes for a planar reflection, which is light leaving a + /// surface and crosses the same air the surface's own light does. + final bool fogged; } /// Writes [transform] into [out] at [at] as the two rows `MapUv` reads — `C8`. @@ -661,7 +670,7 @@ extension _MeshEncode on Renderer { _fogInfo.name, declared: material.lighting.usesFogInfo, )) { - final fog = override == null && material.fogged + final fog = (override?.fogged ?? material.fogged) ? settings.fog : const FogSettings(); _fogData[0] = fog.resolvedColor.x; diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart new file mode 100644 index 000000000..b44416bfb --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart @@ -0,0 +1,406 @@ +/// Pictures of the scene taken before the scene: a [RenderTexture]'s camera +/// and a planar reflector's mirrored one — `P4`. +/// +/// A `part` of `renderer.dart` — see `renderer_shadow_pass.dart` for why. +/// Both draw every mesh through `_encodeNode`, as a probe's capture does, so +/// a picture of the world has the world's materials, lights and shadows. +/// +/// **One way of drawing a view into a texture, and two things pointed +/// through it.** [_captureView] is the probe face's pass with a 2D target, a +/// frustum to cull by and a layer mask: the opaque half, the sky, the +/// blended half. A render texture points it through its own camera and then +/// encodes the light into sRGB bytes for a material to read; a reflector +/// points it through the view's camera mirrored in its plane, with the near +/// plane moved onto the plane, and keeps the light as it is for its +/// surfaces to lay over themselves in the scene pass. +part of 'renderer.dart'; + +/// What the renderer holds for one planar reflector between frames: a +/// picture per view, kept so a frame does not allocate one. +final class _PlanarState { + /// Indexed by the view's place in the frame's priority order. + final List textures = []; + + /// Which of [textures] this frame drew. A view seeing the plane from + /// behind draws nothing, and the picture kept from an earlier frame must + /// not be laid on. + final List drawn = []; + + /// The frame [drawn] describes, so a frame whose pass was culled lays + /// nothing on either. + int frame = -1; +} + +extension _PlanarPasses on Renderer { + /// Lets go of the pictures of every reflector no longer in [scene]. + void _retireReflectorsNotIn(Scene scene) { + if (_planarStates.isEmpty) return; + final live = scene.reflectors.toSet(); + _planarStates.removeWhere((reflector, state) { + if (live.contains(reflector)) return false; + state.textures.forEach(_destroyAfterFrame); + return true; + }); + } + + /// Draws [scene] into [colour] through [viewProjection], as a camera at + /// [eye] sees it. + /// + /// [skyViewProjection] is the same view without anything done to its depth, + /// which is what the sky's rays are taken from: a reflector's near plane + /// tilts the depth and leaves every ray where it was, and the rays are all + /// the sky reads. [mirrored] says the matrix reverses the winding. + void _captureView({ + required FrameResources resources, + required Scene scene, + required TextureHandle colour, + required vm.Matrix4 viewProjection, + required vm.Matrix4 skyViewProjection, + required vm.Vector3 eye, + required int layerMask, + required Set excluded, + required bool mirrored, + required RenderSettings settings, + required SceneShadows shadows, + required FramePassState passState, + required vm.Vector4 clearColor, + }) { + final rect = ScreenRect.of(colour); + final depth = resources.transient( + RenderTargetSpec( + width: rect.width, + height: rect.height, + format: device.defaultDepthStencilFormat, + storageMode: StorageMode.deviceTransient, + ), + ); + final pass = device.beginRenderPass( + RenderPassDescriptor( + label: _passLabel, + colors: [ + ColorTarget( + texture: colour, + clearValue: Renderer._srgbToLinear(clearColor), + ), + ], + depth: DepthTarget(texture: depth), + ), + ); + // Every lane the scene pass sets, set here too: the capture runs before + // it, and a lane left as the last frame's scene pass wrote it is a + // picture that depends on what was drawn a frame ago. + _targetOrigin[0] = _rowsFromBottom(colour); + _targetOrigin[1] = 0.0; + _targetOrigin[2] = settings.energyCompensation ? 1.0 : 0.0; + _targetOrigin[3] = -1.0; + pass.setState( + Renderer._kSceneViewState.copyWith( + viewport: rect, + scissor: rect, + polygonMode: PolygonMode.fill, + ), + ); + passState + ..depthCompare = CompareFunction.less + ..invalidatePipeline(); + + _cameraData[0] = eye.x; + _cameraData[1] = eye.y; + _cameraData[2] = eye.z; + viewAxisOf(skyViewProjection, _forward); + _forwardData[0] = _forward.x; + _forwardData[1] = _forward.y; + _forwardData[2] = _forward.z; + + final frustum = vm.Frustum.matrix(viewProjection); + void encodeHalf({required bool blended}) { + for (final node in scene.meshes) { + if (!node.visibleInHierarchy || !node.shadowCasting.drawsColour) { + continue; + } + if (node.drawsTransparent != blended) continue; + if ((node.layerMask & layerMask) == 0) continue; + if (excluded.contains(node)) continue; + if (node.frustumCulled && + !frustum.intersectsWithAabb3(node.worldBounds)) { + continue; + } + final mesh = node.mesh; + if (mesh is! DrawableGeometry || mesh.indexCount == 0) continue; + _encodeNode( + encoder: pass, + node: node, + scene: scene, + settings: settings, + viewProjection: viewProjection, + shadows: shadows, + lights: lights, + shadowSlots: _shadowSlots, + state: passState, + mirrored: mirrored, + ); + } + } + + encodeHalf(blended: false); + _encodeSky( + pass: pass, + settings: settings, + viewProjection: skyViewProjection, + state: passState, + ); + encodeHalf(blended: true); + + pass.submit(); + passState.invalidatePipeline(); + } + + /// Draws [texture]'s camera into it: the light into a picture of the + /// frame's own, and that picture encoded into the texture's bytes. + void _drawRenderTexture({ + required FrameResources resources, + required Scene scene, + required RenderTexture texture, + required RenderSettings settings, + required SceneShadows shadows, + required FramePassState passState, + }) { + final light = resources.transient( + RenderTargetSpec( + width: texture.width, + height: texture.height, + format: hdrFormat, + ), + ); + final camera = texture.camera; + final eye = camera.readWorldPosition(); + final viewProjection = _viewProjection( + camera, + texture.width / texture.height, + ); + _captureView( + resources: resources, + scene: scene, + colour: light, + viewProjection: viewProjection, + skyViewProjection: viewProjection, + eye: eye, + layerMask: texture.layerMask, + excluded: texture.excluded, + mirrored: false, + settings: settings, + shadows: shadows, + passState: passState, + clearColor: texture.clearColor, + ); + final encode = shaders['RenderTextureEncode']; + if (encode == null) { + throw StateError( + 'the scene holds a RenderTexture but the bundle has no ' + '"RenderTextureEncode" entry. Rebuild the backend\'s shader bundle — ' + 'for the web backends that means re-running tool/generate_shaders.dart.', + ); + } + _renderTextureInfo.params + ..[0] = texture.exposure + ..[1] = device.framebufferOrigin == FramebufferOrigin.bottomLeft + ? 1.0 + : 0.0; + drawFullscreen( + FullscreenDraw( + target: texture.texture, + fragment: encode, + textures: {'source_texture': light}, + uniforms: >{ + _renderTextureInfo.name: _renderTextureInfo.members, + }, + ), + ); + texture.markDrawn(); + } + + /// Draws [reflector]'s mirrored picture for every view in [views] that + /// sees the front of its plane, into [state]'s textures. + /// + /// The view's own camera and projection, reflected in the plane: a world + /// point is mirrored first and then seen as the view sees it, so the + /// picture lines up texel for pixel with the view and a surface in the + /// plane reads it by where it is on screen. The near plane is moved onto + /// the plane (see `obliqueNearPlane`), so what is below it — the floor's + /// underside, the pool's bed — is cut away rather than reflected up + /// through it. + void _drawPlanarReflection({ + required FrameResources resources, + required Scene scene, + required PlanarReflectorNode reflector, + required _PlanarState state, + required List views, + required int width, + required int height, + required RenderSettings settings, + required SceneShadows shadows, + required FramePassState passState, + }) { + final normal = reflector.readPlaneNormal(); + final point = reflector.readWorldPosition(); + final mirror = mirrorAcrossPlane(normal, point); + final excluded = {...reflector.surfaces, ...reflector.excluded}; + while (state.textures.length < views.length) { + state.textures.add(null); + state.drawn.add(false); + } + state.frame = _frameIndex; + for (var i = 0; i < views.length; i++) { + state.drawn[i] = false; + final view = views[i]; + final camera = view.camera; + final eye = camera.readWorldPosition(); + // From behind, or level with it: no reflection to see. + if (normal.dot(eye - point) <= 0.0) continue; + + final rect = Renderer._viewportPixels( + view.viewportFraction, + width, + height, + ); + final pictureWidth = math.max( + 1, + (rect.width * reflector.resolution).round(), + ); + final pictureHeight = math.max( + 1, + (rect.height * reflector.resolution).round(), + ); + final kept = state.textures[i]; + final TextureHandle picture; + if (kept != null && + kept.width == pictureWidth && + kept.height == pictureHeight) { + picture = kept; + } else { + _destroyAfterFrame(kept); + picture = device.createTexture( + RenderTargetSpec( + width: pictureWidth, + height: pictureHeight, + format: hdrFormat, + ), + ); + state.textures[i] = picture; + } + + final vm.Matrix4 mirroredView = camera.viewMatrix * mirror; + final projection = camera.projection.toMatrix(rect.width / rect.height); + final clipped = + obliqueNearPlane( + projection, + planeInEyeSpace( + mirroredView, + normal, + point, + offset: reflector.clipOffset, + ), + ) ?? + projection; + _captureView( + resources: resources, + scene: scene, + colour: picture, + viewProjection: toDepthRange(clipped * mirroredView, device.depthRange), + skyViewProjection: toDepthRange( + projection * mirroredView, + device.depthRange, + ), + eye: mirror.transform3(eye), + layerMask: view.layerMask & reflector.reflectedLayers, + excluded: excluded, + mirrored: true, + settings: settings, + shadows: shadows, + passState: passState, + clearColor: view.clearColor, + ); + state.drawn[i] = true; + } + } + + /// Lays every reflector's picture for view [viewNumber] over the surfaces + /// that show it, inside the scene pass and straight after its opaque half. + /// + /// After the opaque half because the test is `lessEqual` against what the + /// surface itself wrote: a fragment of the second draw passes exactly + /// where the surface was not hidden. A blended surface writes no depth and + /// is drawn after this, so on water that blends its picture lies under the + /// water rather than on it. + void _encodePlanarReflections({ + required PassEncoder encoder, + required Scene scene, + required RenderView view, + required int viewNumber, + required ScreenRect rect, + required vm.Frustum frustum, + required RenderSettings settings, + required vm.Matrix4 viewProjection, + required SceneShadows shadows, + required FramePassState state, + }) { + if (!settings.planarReflections.enabled || _planarStates.isEmpty) return; + for (final reflector in scene.reflectors) { + final planar = _planarStates[reflector]; + if (planar == null || + planar.frame != _frameIndex || + viewNumber >= planar.drawn.length || + !planar.drawn[viewNumber]) { + continue; + } + final picture = planar.textures[viewNumber]!; + _planarInfo.view + ..[0] = rect.x.toDouble() + ..[1] = rect.y.toDouble() + ..[2] = rect.width.toDouble() + ..[3] = rect.height.toDouble(); + _planarInfo.params + ..[0] = reflector.reflectance + ..[1] = reflector.strength + // The rows the scene pass set for its own target, which is this one. + ..[2] = _targetOrigin[0]; + _planarInfo.tint + ..[0] = reflector.tint.x + ..[1] = reflector.tint.y + ..[2] = reflector.tint.z; + _planarMaterial.extraTextures['reflection_texture'] = picture; + for (final surface in reflector.surfaces) { + if (!surface.visibleInHierarchy || + !surface.shadowCasting.drawsColour || + (surface.layerMask & view.layerMask) == 0) { + continue; + } + if (surface.frustumCulled && + !frustum.intersectsWithAabb3(surface.worldBounds)) { + continue; + } + final mesh = surface.mesh; + if (mesh is! DrawableGeometry || mesh.indexCount == 0) continue; + // The surface's own answer to which faces exist, as the x-ray's + // marks take it. + _planarMaterial.doubleSided = surface.material.doubleSided; + _encodeNode( + encoder: encoder, + node: surface, + scene: scene, + settings: settings, + viewProjection: viewProjection, + shadows: shadows, + lights: lights, + shadowSlots: _shadowSlots, + state: state, + override: _DrawOverride( + material: _planarMaterial, + blend: BlendState.alphaBlend, + fogged: surface.material.fogged, + ), + ); + } + } + } +} diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart index fc4093641..6a787f9cd 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart @@ -205,7 +205,7 @@ extension _PostPasses on Renderer { _fxaaParams[3] = settings.blend.clamp(0.0, 1.0); // `gfx-29n`. Zero exactly when nobody asked: the shader returns the // centre untouched at zero rather than running a kernel that rounds to - // nothing, and eighty-two goldens depend on that being the same bytes. + // nothing, and eighty-four goldens depend on that being the same bytes. // // After a temporal resolve the robust kernel, at the resolve's own // strength — `R2`: what softens a resolved picture is the history, and @@ -1678,7 +1678,7 @@ extension _PostPasses on Renderer { // itself — then one draw per view, scissored to its own rectangle, so the // exposure in the uniform is the one that view metered. With per-view // metering off, or with a single view, this is the one full-frame draw it - // has always been and the bytes are the bytes eighty-two goldens hold. + // has always been and the bytes are the bytes eighty-four goldens hold. final perView = settings.autoExposure.enabled && settings.autoExposure.perView && diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart index 9d6732f9c..fa5101dd4 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart @@ -340,6 +340,21 @@ extension _ScenePasses on Renderer { } else { encodeHalf(opaque); } + // `P4`: each reflector's picture over its surfaces, while the depth + // buffer holds exactly what the opaque half left — see + // `renderer_planar_pass.dart`. Nothing at all without a reflector. + _encodePlanarReflections( + encoder: pass, + scene: scene, + view: view, + viewNumber: viewNumber, + rect: viewRect, + frustum: frustum, + settings: settings, + viewProjection: viewProjection, + shadows: shadows, + state: passState, + ); // Between the two halves, which is the one place it can go. After the // opaque half, so every pixel already covered by geometry fails the depth // test before the sky's fragment stage runs — the software rasteriser diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart index 82768778c..cf7eecdf2 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart @@ -1063,7 +1063,7 @@ extension _ShadowPasses on Renderer { final skinned = skeleton != null; // `gfx-60n`. A cut-out caster goes through a stage with a sampler in // it; everything else keeps the stage it has always had, which is why - // the masked half costs the common path nothing and why eighty-two + // the masked half costs the common path nothing and why eighty-four // goldens recorded against the plain stage cannot move. final masked = maskedShadowShader != shadowShader && _castsMasked(node); final kind = diff --git a/packages/flutter3d_core/lib/src/engine/scene/planar_reflector_node.dart b/packages/flutter3d_core/lib/src/engine/scene/planar_reflector_node.dart new file mode 100644 index 000000000..e2b7a569e --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/scene/planar_reflector_node.dart @@ -0,0 +1,95 @@ +import 'package:vector_math/vector_math.dart'; + +import 'mesh_node.dart'; +import 'scene.dart'; +import 'scene_node.dart'; + +/// A flat mirror or a still pool — `P4`: the world drawn again by a camera +/// mirrored in this node's plane, and laid over the [surfaces] that lie in +/// it. +/// +/// **The plane is the node's own, not the surfaces'.** It passes through the +/// node's origin with its local +Y as the normal, the side the reflection is +/// seen from; a camera behind it sees no reflection, as one below a pool +/// sees none. The surfaces are whatever stands in that plane — a quad, a +/// water mesh, the floor of a hall — and keep their own materials: the +/// reflection is drawn over each of them where it won the depth test, so a +/// surface is lit, textured and shadowed as it was, with the mirrored world +/// on top in proportion to [reflectance] and the angle. +/// +/// **What it costs is a second picture of the scene per view**, at +/// [resolution] of the view's pixels, drawn before the scene with the frame's +/// lights and shadows and its sky. Nothing in it is reflected again, and +/// neither the particles nor anything else a contributor draws reaches it. +/// +/// Drawn only while `RenderSettings.planarReflections` is on. +final class PlanarReflectorNode extends SceneNode { + PlanarReflectorNode({ + Iterable surfaces = const [], + this.reflectance = 1.0, + this.strength = 1.0, + Vector3? tint, + this.resolution = 0.5, + this.clipOffset = 0.01, + this.reflectedLayers = ~0, + super.name, + }) : surfaces = {...surfaces}, + tint = tint ?? Vector3(1.0, 1.0, 1.0), + assert(reflectance >= 0.0 && reflectance <= 1.0, 'F0 is a fraction'), + assert(strength >= 0.0, 'a reflection cannot take light away'), + assert( + resolution > 0.0 && resolution <= 1.0, + 'a fraction of the view, and not nothing', + ); + + /// The meshes the reflection is laid over. Left out of the reflection + /// themselves: they stand in the plane, where the clip would cut them in + /// half. + final Set surfaces; + + /// Meshes the mirrored camera leaves out, beyond [surfaces]. + final Set excluded = {}; + + /// How much is reflected looking straight at the plane, Schlick's F0: one + /// for a mirror, which then reflects everything at every angle, and about + /// 0.02 for water, which reflects little looking down and nearly all at a + /// grazing angle. + double reflectance; + + /// What the Fresnel term is multiplied by before the reflection is laid + /// on; one lays it on as the angle says. + double strength; + + /// Linear light the reflection is multiplied by: a tinted glass, a dark + /// pool. + final Vector3 tint; + + /// The reflection's size as a fraction of its view's, each way. Half by + /// default, which is a quarter of the pixels; a still mirror that fills the + /// screen wants one. + double resolution; + + /// How far below the plane the mirrored camera's near plane sits, in + /// metres. A little, so a wall standing on the floor keeps its foot in the + /// reflection rather than a seam of whatever the clip left. + double clipOffset; + + /// Which layers the mirrored camera draws, intersected with each view's. + /// Not [layerMask], which is this node's own layer like any node's. + int reflectedLayers; + + /// The plane's normal in world space: the node's local +Y, normalised. + Vector3 readPlaneNormal([Vector3? out]) { + final result = out ?? Vector3.zero(); + final m = worldMatrix.storage; + result.setValues(m[4], m[5], m[6]); + if (result.length2 > 0.0) result.normalize(); + return result; + } + + @override + void onAttachedToScene(Scene scene) => scene.registerReflector(this); + + @override + void onDetachedFromScene(Scene scene) => scene.unregisterReflector(this); +} diff --git a/packages/flutter3d_core/lib/src/engine/scene/scene.dart b/packages/flutter3d_core/lib/src/engine/scene/scene.dart index f2cceb619..471a6dcfd 100644 --- a/packages/flutter3d_core/lib/src/engine/scene/scene.dart +++ b/packages/flutter3d_core/lib/src/engine/scene/scene.dart @@ -3,12 +3,14 @@ import 'dart:collection'; import 'package:flutter3d_hardware/flutter3d_hardware.dart'; import 'package:vector_math/vector_math.dart'; +import '../render/render_texture.dart'; import 'camera_node.dart'; import 'decal_node.dart'; import 'irradiance_field.dart'; import 'light_node.dart'; import 'lod_group.dart'; import 'mesh_node.dart'; +import 'planar_reflector_node.dart'; import 'reflection_probe_node.dart'; import 'scene_node.dart'; @@ -75,6 +77,14 @@ final class Scene { /// frame, and the order they were attached in breaks ties between equal /// [DecalNode.order]s. final List _decals = []; + /// Planar reflectors, in attachment order — `P4`. A registry for the + /// probes' reason: the renderer draws each of them a picture every frame. + final List _reflectors = []; + + /// Cameras drawing into textures — `P4`. Not nodes, so not registered by + /// attaching anything: a [RenderTexture] is added here, and is drawn for as + /// long as it stays. + final List _renderTextures = []; /// Everything drawable currently in the scene, in attachment order. /// @@ -115,6 +125,25 @@ final class Scene { late final List _decalsView = UnmodifiableListView( _decals, ); + List get reflectors => _reflectorsView; + late final List _reflectorsView = + UnmodifiableListView(_reflectors); + + List get renderTextures => _renderTexturesView; + late final List _renderTexturesView = + UnmodifiableListView(_renderTextures); + + /// Has [texture]'s camera draw into it every frame, before the scene — or + /// once, if it does not refresh every frame. Adding it twice draws it once. + void addRenderTexture(RenderTexture texture) { + if (!_renderTextures.contains(texture)) _renderTextures.add(texture); + } + + /// Stops drawing [texture]: for a game switching a monitor off, or taking + /// a portal down, before it gives the texture back. Nothing here calls it; + /// the texture itself is its owner's to give back. + void removeRenderTexture(RenderTexture texture) => + _renderTextures.remove(texture); /// Ambient light applied where no direct light reaches. /// @@ -212,6 +241,8 @@ final class Scene { _lodGroups.clear(); _probes.clear(); _decals.clear(); + _reflectors.clear(); + _renderTextures.clear(); // Back to front, because `SceneNode.remove` takes the last child without // searching for it or shifting the rest; front to back shifted the whole @@ -245,6 +276,10 @@ final class Scene { void registerDecal(DecalNode node) => _decals.add(node); void unregisterDecal(DecalNode node) => _decals.remove(node); + void registerReflector(PlanarReflectorNode node) => _reflectors.add(node); + + void unregisterReflector(PlanarReflectorNode node) => + _reflectors.remove(node); /// First light of the given type, or null. /// diff --git a/packages/flutter3d_core/lib/src/formats/lighting_model.dart b/packages/flutter3d_core/lib/src/formats/lighting_model.dart index ac7679f7e..c4ffda2c7 100644 --- a/packages/flutter3d_core/lib/src/formats/lighting_model.dart +++ b/packages/flutter3d_core/lib/src/formats/lighting_model.dart @@ -90,6 +90,25 @@ final class LightingModel { usesMaterialParameters: false, ); + /// A planar reflector's surface drawn again with the mirrored picture over + /// it — `P4`, the stage `renderer_planar_pass.dart` draws with. + /// + /// Absent from [builtIn] for [xray]'s reason: it is not a way to light a + /// material but a second draw over one, and a material asking for it would + /// be a surface with nothing under its reflection. It reads no maps, no + /// lights and no `FragInfo` — the eye is the fog block's — and declares no + /// surface buffer, so it leaves what the surface below it wrote there. + static const LightingModel planarReflection = LightingModel( + 'Planar reflection', + 'PlanarReflection', + usesFragInfo: false, + usesFogInfo: true, + usesAlbedoTexture: false, + usesMaterialMaps: false, + usesMetallicRoughnessMap: false, + usesMaterialParameters: false, + ); + /// A line of constant screen width — `gfx-86n`: [unlit]'s fragment stage /// behind the engine's own `PolylineVertex`, which widens `buildPolyline`'s /// geometry by the number of pixels each point carries. diff --git a/packages/flutter3d_core/test/engine/mirror_view_test.dart b/packages/flutter3d_core/test/engine/mirror_view_test.dart new file mode 100644 index 000000000..099f730bd --- /dev/null +++ b/packages/flutter3d_core/test/engine/mirror_view_test.dart @@ -0,0 +1,104 @@ +/// The arithmetic of `P4`'s mirrored camera: a reflection across a plane, +/// and a projection whose near plane is that plane. +/// +/// Tolerances are float32's: vector_math's matrices store their elements in +/// a `Float32List`. +library; + +import 'package:flutter3d_core/flutter3d_core.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +/// [point] in clip space through [projection] after [view]. +Vector4 _clip(Matrix4 projection, Matrix4 view, Vector3 point) => projection + .transform(view.transform(Vector4(point.x, point.y, point.z, 1.0))); + +/// Window depth of [point] through [projection] after [view]. +double _depth(Matrix4 projection, Matrix4 view, Vector3 point) { + final clip = _clip(projection, view, point); + return clip.z / clip.w; +} + +void main() { + test('the reflection mirrors a point across a tilted plane and undoes ' + 'itself', () { + final normal = Vector3(0.3, 1.0, -0.2)..normalize(); + final point = Vector3(1.0, 2.0, -0.5); + final mirror = mirrorAcrossPlane(normal, point); + final p = Vector3(-2.0, 4.0, 3.0); + final reflected = mirror.transform3(p.clone()); + // The two are the same distance either side, along the normal. + // Mutation: dropping the translation reflects across a plane through the + // origin, and the distances disagree by twice the plane's offset. + expect( + normal.dot(reflected - point), + closeTo(-normal.dot(p - point), 1e-5), + ); + expect( + (reflected - p).normalized().cross(normal).length, + closeTo(0.0, 1e-5), + ); + expect(mirror.transform3(reflected).distanceTo(p), closeTo(0.0, 1e-5)); + expect(mirror.determinant(), closeTo(-1.0, 1e-5)); + }); + + group('obliqueNearPlane', () { + final camera = CameraNode() + ..setPositionFrom(Vector3(0.0, 3.0, 5.0)) + ..lookAt(Vector3.zero()); + final normal = Vector3(0.0, 1.0, 0.0); + final mirrored = + camera.viewMatrix * mirrorAcrossPlane(normal, Vector3.zero()) + as Matrix4; + final projection = camera.projection.toMatrix(1.0); + final clipped = obliqueNearPlane( + projection, + planeInEyeSpace(mirrored, normal, Vector3.zero()), + )!; + + test( + 'cuts at the plane: what is below it is in front of the near plane', + () { + // Mutation: the ordinary projection keeps both of these in [0, 1]. + expect(_depth(clipped, mirrored, Vector3(0.0, -1.5, 1.8)), lessThan(0)); + expect( + _depth(clipped, mirrored, Vector3(0.0, -0.01, 0.0)), + lessThan(0), + ); + expect( + _depth(clipped, mirrored, Vector3(0.0, 1.0, 0.0)), + inExclusiveRange(0.0, 1.0), + ); + expect( + _depth(clipped, mirrored, Vector3(2.0, 0.0, -1.0)), + closeTo(0.0, 1e-6), + ); + }, + ); + + test('moves only depth: every point lands on the same pixel', () { + for (final point in [ + Vector3(0.0, 1.0, 0.0), + Vector3(1.5, 0.5, -2.0), + Vector3(-1.0, 2.0, 1.0), + ]) { + final a = _clip(projection, mirrored, point); + final b = _clip(clipped, mirrored, point); + expect(a.x / a.w, closeTo(b.x / b.w, 1e-5)); + expect(a.y / a.w, closeTo(b.y / b.w, 1e-5)); + } + }); + + test('refuses a plane the eye stands on the kept side of', () { + // The ordinary camera, not the mirrored one: it is above the plane, + // which is the side kept, so no near plane can stand on it. + expect( + obliqueNearPlane( + projection, + planeInEyeSpace(camera.viewMatrix, normal, Vector3.zero()), + ), + isNull, + ); + }); + }); +} diff --git a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart index 43f828415..45cb2d271 100644 --- a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart +++ b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart @@ -157,7 +157,7 @@ void main() { }); test('an uncompressed file is untouched by any of this', () { - // The path eighty-two goldens and every existing asset take. A level with + // The path eighty-four goldens and every existing asset take. A level with // no supercompression is handed back as a view on the file's own bytes, // with nothing copied and nothing decoded. final file = buildKtx2( diff --git a/packages/flutter3d_cpu/CHANGELOG.md b/packages/flutter3d_cpu/CHANGELOG.md index dea0081e7..eeb56a691 100644 --- a/packages/flutter3d_cpu/CHANGELOG.md +++ b/packages/flutter3d_cpu/CHANGELOG.md @@ -20,6 +20,21 @@ them, could not depend on it. Nothing else changed. `post/decal.frag` line for line, the mip level of each picture chosen from the same footprint the GLSL computes by hand from neighbouring texels of the surface buffer. +**`P4`'s two stages.** `PlanarReflectionShader` mirrors +`planar_reflection.frag` and `RenderTextureEncodeShader` mirrors +`render_texture_encode.frag`; `planar_reflection_test.dart` holds ten +claims about both, and the `planar-mirror` and `render-texture` goldens are +in this set. + +**A triangle is clipped to the depth range, as a GPU clips it.** The +rasteriser cut triangles at `w` and nowhere else, so a fragment in front of +the near plane or past the far one was drawn wherever its `w` was positive. +Nothing showed it while every near plane was parallel to the screen and a +little way off the eye; the mirrored camera stands its near plane on the +mirror, and what was below the mirror came up through it here and on no +other backend. A fragment with a window depth outside `[0, 1]` is dropped +now, which is the same cut as clipping the triangle, because that depth is +linear across the screen. No golden of this set moved. ## 0.8.2 diff --git a/packages/flutter3d_cpu/lib/src/cpu_encoder.dart b/packages/flutter3d_cpu/lib/src/cpu_encoder.dart index 66097dbd6..61b08598e 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_encoder.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_encoder.dart @@ -1066,6 +1066,13 @@ final class CpuEncoder implements CommandEncoder { final b2 = w0 / area; final z = _asStored(sz0 * b0 + sz1 * b1 + sz2 * b2); + // The depth clip every GPU does and the clipper above does not: a + // fragment outside `[0, 1]` is in front of the near plane or past + // the far one. Window depth is linear across the screen, so dropping + // the fragment is the same cut as clipping the triangle. It mattered + // once a near plane stopped being parallel to the screen — `P4`'s + // mirrored camera stands its near plane on the mirror. + if (z < 0.0 || z > 1.0) continue; final index = y * target.width + x; final fate = _fateOf(stencil, stencilState, index, z, depth); final op = _operationFor(fate, stencilState); diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart index 1c04f6515..c4de7acd5 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart @@ -60,6 +60,7 @@ import 'cpu_shaders_lit.dart'; import 'cpu_shaders_mesh_vertex.dart'; import 'cpu_shaders_motion_blur.dart'; import 'cpu_shaders_particles.dart'; +import 'cpu_shaders_planar.dart'; import 'cpu_shaders_polyline.dart'; import 'cpu_shaders_post.dart'; import 'cpu_shaders_probe.dart'; @@ -87,6 +88,7 @@ export 'cpu_shaders_lit.dart'; export 'cpu_shaders_mesh_vertex.dart'; export 'cpu_shaders_motion_blur.dart'; export 'cpu_shaders_particles.dart'; +export 'cpu_shaders_planar.dart'; export 'cpu_shaders_post.dart'; export 'cpu_shaders_probe.dart'; export 'cpu_shaders_reactive.dart'; @@ -149,6 +151,7 @@ Map builtinCpuShaders() { 'FullscreenVertex': const CpuStage.vertex(FullscreenVertexShader()), 'Unlit': const CpuStage.fragment(UnlitShader()), 'Xray': const CpuStage.fragment(XrayShader()), + 'PlanarReflection': const CpuStage.fragment(PlanarReflectionShader()), 'Lambert': const CpuStage.fragment(LambertShader()), 'BlinnPhong': const CpuStage.fragment(BlinnPhongShader()), 'Pbr': const CpuStage.fragment(PbrShader()), @@ -234,6 +237,7 @@ Map builtinCpuShaders() { ShadowDistanceMaskedShader(), ), 'ProbePrefilter': const CpuStage.fragment(ProbePrefilterShader()), + 'RenderTextureEncode': const CpuStage.fragment(RenderTextureEncodeShader()), 'MrtProbe': const CpuStage.fragment(MrtProbeShader()), 'WboitResolve': const CpuStage.fragment(WboitResolveShader()), 'SceneColourCopy': const CpuStage.fragment(SceneColourCopyShader()), diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_planar.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_planar.dart new file mode 100644 index 000000000..03d8fc7de --- /dev/null +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_planar.dart @@ -0,0 +1,83 @@ +/// `P4`'s two stages: `planar_reflection.frag`, which lays a mirrored picture +/// over a reflector's surface, and `render_texture_encode.frag`, which turns +/// what a camera into a texture saw into sRGB bytes. +library; + +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:vector_math/vector_math.dart'; + +import 'cpu_shader.dart'; +import 'cpu_shaders_color.dart'; +import 'cpu_shaders_layout.dart'; + +/// `planar_reflection.frag`: the texel of the reflection under this pixel, +/// by its place in the view, weighted by Schlick's Fresnel and fogged. +/// +/// Writes no surface: the GLSL declares `F3D_NO_SURFACE_BUFFER`, and the +/// surface underneath keeps describing itself, as under `XrayShader`. +final class PlanarReflectionShader implements CpuFragmentShader { + const PlanarReflectionShader(); + + @override + Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) { + final picture = b.textures['reflection_texture']; + if (picture == null) return Vector4.zero(); + final view = b.vec4('PlanarReflectionInfo', 'view', Vector4(0, 0, 1, 1)); + final params = b.vec4('PlanarReflectionInfo', 'params', Vector4.zero()); + final tint = b.vec4('PlanarReflectionInfo', 'tint', Vector4.zero()); + + // `FragCoordFromTop`, and the texture's rows turned back where its first + // row is the bottom of the picture. + final rows = params.z; + final pixelY = rows > 0.0 ? rows - c.coord.y : c.coord.y; + final u = (c.coord.x - view.x) / view.z; + final top = (pixelY - view.y) / view.w; + final texel = picture.sample(u, rows > 0.0 ? 1.0 - top : top); + final reflected = Vector3( + texel.x * tint.x, + texel.y * tint.y, + texel.z * tint.z, + ); + + final normal = Vector3(v[kVNormal], v[kVNormal + 1], v[kVNormal + 2]) + ..normalize(); + if (!c.frontFacing) normal.negate(); + final eye = b.vec4('FogInfo', 'eye', Vector4.zero()); + final toEye = Vector3( + eye.x - v[kVWorld], + eye.y - v[kVWorld + 1], + eye.z - v[kVWorld + 2], + )..normalize(); + final cosine = normal.dot(toEye).clamp(0.0, 1.0); + final f0 = params.x; + final grazing = 1.0 - cosine; + final grazing2 = grazing * grazing; + final fresnel = f0 + (1.0 - f0) * grazing2 * grazing2 * grazing; + final alpha = (fresnel * params.y).clamp(0.0, 1.0); + + final fogged = applyFog(reflected, v, b); + return Vector4(fogged.x * alpha, fogged.y * alpha, fogged.z * alpha, alpha); + } +} + +/// `render_texture_encode.frag`: the light times the exposure, clipped to +/// one and encoded as sRGB. +final class RenderTextureEncodeShader implements CpuFragmentShader { + const RenderTextureEncodeShader(); + + @override + Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) { + final source = b.textures['source_texture']; + if (source == null) return Vector4(0.0, 0.0, 0.0, 1.0); + final params = b.vec4('RenderTextureInfo', 'params', Vector4.zero()); + final exposure = params.x; + // Turned over where the backend draws its first row at the bottom; this + // one draws it at the top and never is. + final light = source.sample(v[0], params.y > 0.5 ? 1.0 - v[1] : v[1]); + double encode(double channel) => + toSrgb(math.min(math.max(channel * exposure, 0.0), 1.0)); + return Vector4(encode(light.x), encode(light.y), encode(light.z), 1.0); + } +} diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart index 274ec3fa1..fcb409b68 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart @@ -163,7 +163,7 @@ final class CompositeShader implements CpuFragmentShader { // Skipped at exactly one, which is what both settings off comes to: a // multiply by one is exact, so this is a shortcut rather than a difference, - // and it keeps the frames eighty-two goldens hold untouched by arithmetic + // and it keeps the frames eighty-four goldens hold untouched by arithmetic // they never used to go through. if (shade != 1.0) colour.scale(shade); diff --git a/packages/flutter3d_cpu/lib/src/cpu_texture.dart b/packages/flutter3d_cpu/lib/src/cpu_texture.dart index a1f142b86..96e0758d7 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_texture.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_texture.dart @@ -300,7 +300,7 @@ final class BoundTexture { // from the short one. // // **A sampler that did not ask is untouched**, which is what makes this - // safe to add to a backend eighty-two golden scenes are recorded on: with + // safe to add to a backend eighty-four golden scenes are recorded on: with // `anisotropy` at one — the default everywhere in this engine — the // arithmetic below is not reached and the bytes are the ones that were // recorded. `anisotropic-floor` is the one scene that asks. diff --git a/packages/flutter3d_cpu/lib/src/portable_log2.dart b/packages/flutter3d_cpu/lib/src/portable_log2.dart index d8c33ba73..0380b6e24 100644 --- a/packages/flutter3d_cpu/lib/src/portable_log2.dart +++ b/packages/flutter3d_cpu/lib/src/portable_log2.dart @@ -10,7 +10,7 @@ /// twenty-five five. `anisotropic-floor` is the scene built to sit exactly /// there: 2059 of its 172800 pixels differed between a reference recorded on /// macOS and the same code run on a Linux runner, and nothing else in -/// eighty-two scenes differed at all. +/// eighty-four scenes differed at all. /// /// **The same rule the simulation already keeps.** `flutter3d_sim`'s /// `Portable` exists for this, and a step in that package is forbidden from diff --git a/packages/flutter3d_cpu/test/cross_backend_test.dart b/packages/flutter3d_cpu/test/cross_backend_test.dart index 1dec8bde0..213b3057e 100644 --- a/packages/flutter3d_cpu/test/cross_backend_test.dart +++ b/packages/flutter3d_cpu/test/cross_backend_test.dart @@ -310,6 +310,11 @@ const Map _budgets = { // of a picture's texel or a box's face is decided in float32 here and in // half floats and the GPU's own arithmetic there. 'decal-floor': 0.08, + // `P4`. 0.841% and 0.962% measured, every pixel on a silhouette edge, + // which Impeller multisamples and this rasteriser does not: the inside of + // the mirror's reflection and of the monitor's picture agree. + 'planar-mirror': 0.9, + 'render-texture': 1.0, }; /// How far apart two channels may be before the pixel counts as differing. diff --git a/packages/flutter3d_cpu/test/goldens/planar-mirror.png b/packages/flutter3d_cpu/test/goldens/planar-mirror.png new file mode 100644 index 0000000000000000000000000000000000000000..d3b1bb18a7e77033ea87dadbfc5483a9e3854121 GIT binary patch literal 22543 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 P{4yCDnA|MUcaHim!(}Ev literal 0 HcmV?d00001 diff --git a/packages/flutter3d_cpu/test/goldens/render-texture.png b/packages/flutter3d_cpu/test/goldens/render-texture.png new file mode 100644 index 0000000000000000000000000000000000000000..0c68fb5f52b950cb0298a51fffd33579af860dc6 GIT binary patch literal 22278 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 QYzw+>WO}8-z~RMz0I 'scene probe'; + + @override + List get reads => const [FrameResourceIds.hdrColour]; + + @override + List get writes => const [FrameResourceIds.hdrColour]; + + @override + void execute(NodeFrame frame) { + final scene = frame.resources.texture(FrameResourceIds.hdrColour); + last = _device.readHdrPixels(scene); + frame.resources.provide(FrameResourceIds.hdrColour, scene); + } +} + +typedef _Stage = ({ + CpuDevice device, + Renderer renderer, + Scene scene, + CameraNode camera, + _SceneProbe probe, +}); + +_Stage _stage({Vector3? from}) { + final device = CpuDevice( + width: _size, + height: _size, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final camera = CameraNode() + ..setPositionFrom(from ?? Vector3(0.0, 3.0, 5.0)) + ..lookAt(Vector3.zero()); + final scene = Scene()..add(camera); + final probe = _SceneProbe(device); + final renderer = Renderer.create(device: device)..addNode(probe); + return ( + device: device, + renderer: renderer, + scene: scene, + camera: camera, + probe: probe, + ); +} + +Material _unlit(double r, double g, double b) => + Material(lighting: LightingModel.unlit, baseColor: Vector4(r, g, b, 1.0)); + +MeshNode _cube(_Stage it, Material material, Vector3 at, {double size = 1}) => + it.scene.add( + MeshNode( + DeviceMesh.upload( + it.device, + CuboidShape(size: Vector3.all(size)).build(), + ), + material, + )..setPositionFrom(at), + ); + +/// A black floor eight metres square at y = 0, which shows nothing of its +/// own: under a reflector of F0 one, every pixel of it is the reflection. +MeshNode _floor(_Stage it, {Material? material}) => it.scene.add( + MeshNode( + DeviceMesh.upload( + it.device, + const PlaneShape(width: 8.0, depth: 8.0).build(), + ), + material ?? _unlit(0.0, 0.0, 0.0), + ), +); + +const RenderSettings _on = RenderSettings( + bloom: BloomSettings(enabled: false), + planarReflections: PlanarReflectionSettings(enabled: true), +); + +FrameResult _render(_Stage it, [RenderSettings settings = _on]) => + it.renderer.render( + width: _size, + height: _size, + scene: it.scene, + views: [RenderView(camera: it.camera)], + settings: settings, + ); + +Float32List _scene(_Stage it, [RenderSettings settings = _on]) { + _render(it, settings); + return Float32List.fromList(it.probe.last!); +} + +/// Every pixel whose colour [a] and [b] disagree on by more than [by]. +List _changed(Float32List a, Float32List b, {double by = 1e-4}) => [ + for (var p = 0; p < _size * _size; p++) + if ([0, 1, 2].any((c) => (a[p * 4 + c] - b[p * 4 + c]).abs() > by)) p, +]; + +/// How many pixels are mostly [channel] and nothing else. +int _count(Float32List frame, int channel) => [ + for (var p = 0; p < _size * _size; p++) + if (frame[p * 4 + channel] > 0.3 && + [ + 0, + 1, + 2, + ].where((c) => c != channel).every((c) => frame[p * 4 + c] < 0.1)) + p, +].length; + +void main() { + group('planar reflections', () { + test('off by default: the pass does not run and the frame is the same', () { + final it = _stage(); + final floor = _floor(it); + _cube(it, _unlit(1.0, 0.1, 0.1), Vector3(0.0, 1.0, 0.0)); + final without = _scene(it, const RenderSettings()); + it.scene.add(PlanarReflectorNode(surfaces: [floor])); + final result = _render(it, const RenderSettings()); + // Mutation: an `isActive` that forgets the setting draws the scene a + // second time on every frame with a reflector in it. + expect( + result.passes.map((p) => p.name), + isNot(contains('planar reflections')), + ); + expect(_changed(it.probe.last!, without), isEmpty); + }); + + test( + 'on, with the reflector hidden, nothing is drawn and nothing moves', + () { + final it = _stage(); + final floor = _floor(it); + _cube(it, _unlit(1.0, 0.1, 0.1), Vector3(0.0, 1.0, 0.0)); + final without = _scene(it); + it.scene.add( + PlanarReflectorNode(surfaces: [floor])..visible = false, + ); + final result = _render(it); + // Mutation: asking whether the scene holds a reflector rather than + // whether one is visible draws a picture nobody is shown. + expect( + result.passes.map((p) => p.name), + isNot(contains('planar reflections')), + ); + expect(_changed(it.probe.last!, without), isEmpty); + }, + ); + + test('a mirror shows the world mirrored in its plane', () { + final it = _stage(); + final floor = _floor(it); + _cube(it, _unlit(1.0, 0.1, 0.1), Vector3(0.0, 1.0, 0.0)); + _cube(it, _unlit(0.1, 0.2, 1.0), Vector3(1.4, 0.4, -0.8), size: 0.8); + it.scene.add( + PlanarReflectorNode(surfaces: [floor], resolution: 1.0), + ); + final mirrored = _scene(it); + + // The same world with every mesh mirrored in y = 0 and no floor: what + // the mirror ought to show, seen directly. + final reference = _stage(); + _cube(reference, _unlit(1.0, 0.1, 0.1), Vector3(0.0, 1.0, 0.0)); + _cube(reference, _unlit(1.0, 0.1, 0.1), Vector3(0.0, -1.0, 0.0)); + _cube( + reference, + _unlit(0.1, 0.2, 1.0), + Vector3(1.4, 0.4, -0.8), + size: 0.8, + ); + _cube( + reference, + _unlit(0.1, 0.2, 1.0), + Vector3(1.4, -0.4, -0.8), + size: 0.8, + ); + final seen = _scene(reference); + + // The reflection is there at all: twice the red the cube alone has. + expect(_count(mirrored, 0), greaterThan(_count(seen, 0) ~/ 2 + 20)); + // And it is where the mirrored world is, texel for pixel: what differs + // is a pixel or two along the edges, where a triangle drawn through + // the mirror and the same triangle mirrored in its vertices round + // their edges apart. + // + // Mutation: a mirror built from the camera's own view (no reflection) + // shows the cubes from above on the floor and moves hundreds of + // pixels; a picture read with its rows the wrong way up moves them too. + expect(_changed(mirrored, seen, by: 0.05).length, lessThan(12)); + }); + + test('what is below the plane is not reflected up through it', () { + final it = _stage(); + final floor = _floor(it); + // Under the floor, on the line from the mirrored camera to the middle + // of the mirror, where it would cover the whole reflection. + _cube(it, _unlit(0.1, 1.0, 0.1), Vector3(0.0, -1.5, 1.8)); + _cube(it, _unlit(1.0, 0.1, 0.1), Vector3(0.0, 1.0, 0.0)); + it.scene.add( + PlanarReflectorNode(surfaces: [floor], resolution: 1.0), + ); + final frame = _scene(it); + // Mutation: drawing the mirrored camera with its ordinary projection + // rather than `obliqueNearPlane` covers the mirror in green. + expect(_count(frame, 1), 0); + expect(_count(frame, 0), greaterThan(40)); + }); + + test('water reflects more at a grazing angle than looking down', () { + final it = _stage(from: Vector3(0.0, 1.2, 6.0)); + // A tall wall behind the pool, so its reflection runs from the far + // edge, seen at a grazing angle, to near the camera, seen steeply. + _cube(it, _unlit(1.0, 1.0, 1.0), Vector3(0.0, 3.0, -4.2), size: 6.0); + final floor = _floor(it, material: _unlit(0.0, 0.0, 0.0)); + final reflector = it.scene.add( + PlanarReflectorNode( + surfaces: [floor], + reflectance: 0.02, + resolution: 1.0, + ), + ); + final water = _scene(it); + reflector.reflectance = 1.0; + final mirror = _scene(it); + + // Under the mirror a floor pixel is the reflection; under the water it + // is the reflection times the Fresnel term over black, so the ratio of + // the two is the term. + final terms = <(int, double)>[ + for (var y = 0; y < _size; y++) + if (mirror[(y * _size + _size ~/ 2) * 4] > 0.5) + ( + y, + water[(y * _size + _size ~/ 2) * 4] / + mirror[(y * _size + _size ~/ 2) * 4], + ), + ]; + final floorRows = terms.where((t) => t.$1 > _size ~/ 2).toList(); + expect(floorRows.length, greaterThan(4)); + // Mutation: a term without the angle — F0 alone — is 0.02 on every row. + expect(floorRows.first.$2, greaterThan(floorRows.last.$2 * 2)); + for (final (_, term) in floorRows) { + expect(term, inInclusiveRange(0.02 - 1e-3, 1.0)); + } + }); + + test('a camera behind the plane sees no reflection', () { + final it = _stage(from: Vector3(0.0, -3.0, 5.0)); + final floor = _floor( + it, + material: _unlit(0.3, 0.3, 0.3)..doubleSided = true, + ); + _cube(it, _unlit(1.0, 0.1, 0.1), Vector3(0.0, 1.0, 0.0)); + final without = _scene(it); + it.scene.add( + PlanarReflectorNode(surfaces: [floor], resolution: 1.0), + ); + // Mutation: without the check, the near plane gives up on the eye's + // side and the mirrored camera draws through the plane anyway. + expect(_changed(_scene(it), without), isEmpty); + }); + }); + + group('render textures', () { + test('a scene without one does not run the pass', () { + final it = _stage(); + _cube(it, _unlit(1.0, 0.1, 0.1), Vector3.zero()); + final result = _render(it, const RenderSettings()); + expect( + result.passes.map((p) => p.name), + isNot(contains('render textures')), + ); + }); + + test('holds the camera\'s picture as the sRGB a picture is painted in', () { + final it = _stage(from: Vector3(0.0, 0.0, 3.0)); + // An unlit cube emits its base colour as linear light, so a picture + // encoded right hands the sRGB it was painted with back, unchanged. + _cube(it, _unlit(0.9, 0.3, 0.1), Vector3.zero(), size: 1.0); + final texture = RenderTexture.create( + it.device, + camera: it.camera, + width: _size, + height: _size, + clearColor: Vector4(0.2, 0.4, 0.6, 1.0), + ); + it.scene.addRenderTexture(texture); + _render(it); + final picture = it.device.readHdrPixels(texture.texture); + expect(texture.isDrawn, isTrue); + final centre = (_size ~/ 2 * _size + _size ~/ 2) * 4; + // Mutation: leaving the light linear turns 0.3 into 0.07 and 0.1 into + // 0.01; an exposure applied twice, or a tone curve, moves all three. + expect(picture[centre], closeTo(0.9, 1e-3)); + expect(picture[centre + 1], closeTo(0.3, 1e-3)); + expect(picture[centre + 2], closeTo(0.1, 1e-3)); + // And where nothing was drawn, the clear colour as it was given. + expect(picture[0], closeTo(0.2, 1e-3)); + expect(picture[1], closeTo(0.4, 1e-3)); + expect(picture[2], closeTo(0.6, 1e-3)); + }); + + test('a material shows the picture in the frame it was taken', () { + final it = _stage(from: Vector3(0.0, 0.0, 3.0)); + // The camera into the texture looks at a red cube far off to the side. + final watcher = CameraNode() + ..setPosition(20.0, 0.0, 3.0) + ..lookAt(Vector3(20.0, 0.0, 0.0)); + it.scene.add(watcher); + _cube(it, _unlit(1.0, 0.0, 0.0), Vector3(20.0, 0.0, 0.0), size: 3.0); + final texture = RenderTexture.create( + it.device, + camera: watcher, + width: 16, + height: 16, + ); + it.scene.addRenderTexture(texture); + // A screen filling the view, showing it unlit. + _cube( + it, + Material(lighting: LightingModel.unlit, albedo: texture.texture), + Vector3.zero(), + size: 2.0, + ); + final frame = _scene(it); + final middle = (_size ~/ 2 * _size + _size ~/ 2) * 4; + // Mutation: drawing the textures after the scene shows whatever the + // allocation held on the first frame, which here is black. + expect(frame[middle], closeTo(1.0, 0.02)); + expect(frame[middle + 1], lessThan(0.02)); + }); + + test('one that does not refresh is drawn once, and again on asking', () { + final it = _stage(); + _cube(it, _unlit(0.5, 0.5, 0.5), Vector3.zero()); + final texture = RenderTexture.create( + it.device, + camera: it.camera, + width: 8, + height: 8, + refreshEveryFrame: false, + ); + it.scene.addRenderTexture(texture); + int drawn() => _render( + it, + ).passes.firstWhere((p) => p.name == 'render textures').drawCalls; + // Mutation: ignoring `refreshEveryFrame` draws the picture every frame. + expect(drawn(), greaterThan(0)); + expect(drawn(), 0); + texture.invalidate(); + expect(drawn(), greaterThan(0)); + expect(drawn(), 0); + }); + }); +} diff --git a/packages/flutter3d_impeller/test/gpu_formats_test.dart b/packages/flutter3d_impeller/test/gpu_formats_test.dart index 3756e831b..bfd0b7913 100644 --- a/packages/flutter3d_impeller/test/gpu_formats_test.dart +++ b/packages/flutter3d_impeller/test/gpu_formats_test.dart @@ -2,7 +2,7 @@ /// /// The hazard this exists for: a wrong mapping compiles, runs, and renders /// wrong only for the values a scene happens to use. The goldens cover the -/// values the eighty-two scenes exercise and nothing else — `BlendFactor` has +/// values the eighty-four scenes exercise and nothing else — `BlendFactor` has /// fifteen members and the engine draws with two of them — so a mistake in the /// other thirteen would be found by a user rather than by us. /// diff --git a/packages/flutter3d_shaders/CHANGELOG.md b/packages/flutter3d_shaders/CHANGELOG.md index 60275b22f..98d60ea27 100644 --- a/packages/flutter3d_shaders/CHANGELOG.md +++ b/packages/flutter3d_shaders/CHANGELOG.md @@ -17,6 +17,14 @@ projected boxes, reading four pictures, over the point the surface buffer names under each pixel. It writes a factor and a term for two blends, the albedo swapped under the light the albedo buffer lets it read back, and the colour an unlit surface and an emissive decal add. +**Two stages for `P4`.** `lighting/planar_reflection.frag` lays a mirrored +picture over a reflector's surface, read by the fragment's place in its view +and weighted by Schlick's Fresnel; it declares no surface buffer, as +`xray.frag` does not. `post/render_texture_encode.frag` turns a camera's +light into the sRGB bytes a material's map is read as, turning the rows over +where the backend draws its first row at the bottom. Both are in the bundle, +in `kRequiredShaders` and in `stageBindings`, `uniformBlocks` and +`typed_blocks.dart`. ## 0.8.2 diff --git a/packages/flutter3d_shaders/lib/flutter3d_shaders.dart b/packages/flutter3d_shaders/lib/flutter3d_shaders.dart index a2bc66cb6..0df4147b0 100644 --- a/packages/flutter3d_shaders/lib/flutter3d_shaders.dart +++ b/packages/flutter3d_shaders/lib/flutter3d_shaders.dart @@ -70,6 +70,8 @@ const List kRequiredShaders = [ (name: 'PbrLayered', fragment: true), (name: 'PolylineVertex', fragment: false), (name: 'ProbePrefilter', fragment: true), + (name: 'PlanarReflection', fragment: true), + (name: 'RenderTextureEncode', fragment: true), (name: 'Reflections', fragment: true), (name: 'Ssao', fragment: true), (name: 'Splat', fragment: true), diff --git a/packages/flutter3d_shaders/lib/stage_bindings.dart b/packages/flutter3d_shaders/lib/stage_bindings.dart index a4a7e4688..ae4c6dccd 100644 --- a/packages/flutter3d_shaders/lib/stage_bindings.dart +++ b/packages/flutter3d_shaders/lib/stage_bindings.dart @@ -306,6 +306,10 @@ stageBindings = blocks, Set samplers})>{ 'sheen_texture', }, ), + 'PlanarReflection': ( + blocks: {'FogInfo', 'PlanarReflectionInfo'}, + samplers: {'reflection_texture'}, + ), 'PolylineVertex': ( blocks: {'FrameInfo', 'MaterialParams'}, samplers: {}, @@ -331,6 +335,10 @@ stageBindings = blocks, Set samplers})>{ 'surface_texture', }, ), + 'RenderTextureEncode': ( + blocks: {'RenderTextureInfo'}, + samplers: {'source_texture'}, + ), 'SceneColourCopy': ( blocks: {'SceneCopyInfo'}, samplers: {'source_texture'}, diff --git a/packages/flutter3d_shaders/lib/typed_blocks.dart b/packages/flutter3d_shaders/lib/typed_blocks.dart index cf050585c..0f896fe1b 100644 --- a/packages/flutter3d_shaders/lib/typed_blocks.dart +++ b/packages/flutter3d_shaders/lib/typed_blocks.dart @@ -898,6 +898,27 @@ final class ParticleMeshInfoBlock extends UniformBlock { }; } +/// `PlanarReflectionInfo`. +final class PlanarReflectionInfoBlock extends UniformBlock { + PlanarReflectionInfoBlock() : super('PlanarReflectionInfo'); + + /// `view`: Vector4, at byte 0. + final Float32List view = Float32List(4); + + /// `params`: Vector4, at byte 16. + final Float32List params = Float32List(4); + + /// `tint`: Vector4, at byte 32. + final Float32List tint = Float32List(4); + + @override + late final Map members = { + 'view': view, + 'params': params, + 'tint': tint, + }; +} + /// `PointShadow`. final class PointShadowBlock extends UniformBlock { PointShadowBlock() : super('PointShadow'); @@ -1039,6 +1060,19 @@ final class ReflectionInfoBlock extends UniformBlock { }; } +/// `RenderTextureInfo`. +final class RenderTextureInfoBlock extends UniformBlock { + RenderTextureInfoBlock() : super('RenderTextureInfo'); + + /// `params`: Vector4, at byte 0. + final Float32List params = Float32List(4); + + @override + late final Map members = { + 'params': params, + }; +} + /// `SceneCopyInfo`. final class SceneCopyInfoBlock extends UniformBlock { SceneCopyInfoBlock() : super('SceneCopyInfo'); diff --git a/packages/flutter3d_shaders/lib/uniform_blocks.dart b/packages/flutter3d_shaders/lib/uniform_blocks.dart index 132f4a171..7db282981 100644 --- a/packages/flutter3d_shaders/lib/uniform_blocks.dart +++ b/packages/flutter3d_shaders/lib/uniform_blocks.dart @@ -1359,6 +1359,18 @@ uniformBlocks = >>{ 'params3': (offset: 2560, byteLength: 16, elements: 1, type: 'Vector4'), }, }, + 'PlanarReflection': >{ + 'FogInfo': { + 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), + 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), + 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + }, + 'PlanarReflectionInfo': { + 'view': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), + 'params': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), + 'tint': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + }, + }, 'PolylineVertex': >{ 'FrameInfo': { 'mvp': (offset: 0, byteLength: 64, elements: 1, type: 'Matrix'), @@ -1427,6 +1439,11 @@ uniformBlocks = >>{ ), }, }, + 'RenderTextureEncode': >{ + 'RenderTextureInfo': { + 'params': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), + }, + }, 'SceneColourCopy': >{ 'SceneCopyInfo': { 'params': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), diff --git a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json index 87d771228..b4db1399b 100644 --- a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json +++ b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json @@ -11,6 +11,10 @@ "type": "fragment", "file": "shaders/lighting/xray.frag" }, + "PlanarReflection": { + "type": "fragment", + "file": "shaders/lighting/planar_reflection.frag" + }, "Lambert": { "type": "fragment", "file": "shaders/lighting/lambert.frag" @@ -263,6 +267,10 @@ "type": "fragment", "file": "shaders/post/probe_prefilter.frag" }, + "RenderTextureEncode": { + "type": "fragment", + "file": "shaders/post/render_texture_encode.frag" + }, "ShadowDistance": { "type": "fragment", "file": "shaders/lighting/shadow_distance.frag" diff --git a/packages/flutter3d_shaders/shaders/lighting/planar_reflection.frag b/packages/flutter3d_shaders/shaders/lighting/planar_reflection.frag new file mode 100644 index 000000000..849298381 --- /dev/null +++ b/packages/flutter3d_shaders/shaders/lighting/planar_reflection.frag @@ -0,0 +1,84 @@ +#version 460 core + +// `P4`: a planar reflector's surface, drawn a second time over itself with +// the picture a mirrored camera took of the world above it. +// +// **Laid over the surface rather than inside its lighting.** The reflection +// is read by the pixel's place on screen, not by anything the material +// knows, so the stage that reads it needs nothing a lit model has: no maps, +// no lights, no shadows. Putting it inside the lit models would have been a +// sampler and a block added to six stages that every other draw pays to +// declare, and a seventh header permutation to keep byte-identical. As a +// draw of its own it costs only the surfaces that reflect, and a frame +// without a reflector compiles and binds exactly what it did. +// +// Drawn with the depth test `lessEqual` and no depth write, straight after +// the opaque half, so it lands on exactly the pixels the surface itself won +// and nothing in front of the surface is painted. Blended source-over with +// the colour premultiplied, as every blended draw here is. +// +// **No second output**, for the reason `xray.frag` gives at length: a blend +// protects attachment zero only, and only on the backends whose `setBlend` +// honours an attachment index. The surface buffer keeps describing the +// surface underneath, which is the truth about it. +// +// **And not `lib/surface.glsl`**, which `xray.frag` does include: that header +// declares the base colour sampler for every stage that takes it, and a stage +// that never reads it has it dropped from the Metal function while reflection +// still reports it, which is the slot `metal_bindings_test.dart` exists to +// catch. The two things wanted from `FragInfo` come another way: the eye is +// `FogInfo`'s, and the target's rows ride in this stage's own block. +#define F3D_NO_SURFACE_BUFFER +#include +#include + +/// What the mirrored camera saw, in linear light, the size of the view it +/// was taken for or a fraction of it. +uniform sampler2D reflection_texture; + +uniform PlanarReflectionInfo { + /// xy: the view's top-left corner in pixels of the target, counted from + /// the top. zw: the view's size in pixels. + vec4 view; + + /// x: the reflectance straight on, Schlick's F0 — one for a mirror, about + /// 0.02 for water. y: the strength the reflection is laid on with, which + /// scales the whole Fresnel term. z: the target's rows where its first row + /// is the bottom of the picture, nought where it is the top — see + /// `FragCoordFromTop`. w unused. + vec4 params; + + /// rgb: a linear tint the reflected light is multiplied by. w unused. + vec4 tint; +} +planar_info; + +void main() { + // The same pixel the mirrored camera drew, by place on screen: its + // projection is the view's own, so the texel under this fragment is the + // reflected point behind it. Counted from the top on every backend, and + // turned back where the texture's first row is the bottom of the picture. + float rows = planar_info.params.z; + vec2 pixel = FragCoordFromTop(rows); + vec2 uv = (pixel - planar_info.view.xy) / planar_info.view.zw; + if (rows > 0.0) uv.y = 1.0 - uv.y; + vec3 reflected = + textureLod(reflection_texture, uv, 0.0).rgb * planar_info.tint.rgb; + + // Schlick's Fresnel on the geometric normal, facing the eye: water reflects + // a little looking down into it and nearly everything at a grazing angle, + // and a mirror's F0 of one makes the term one everywhere. + vec3 n = normalize(v_normal); + if (!gl_FrontFacing) n = -n; + vec3 v = normalize(fog_info.eye.xyz - v_world_position); + float cosine = clamp(dot(n, v), 0.0, 1.0); + float f0 = planar_info.params.x; + float grazing = 1.0 - cosine; + float grazing2 = grazing * grazing; + float fresnel = f0 + (1.0 - f0) * grazing2 * grazing2 * grazing; + float alpha = clamp(fresnel * planar_info.params.y, 0.0, 1.0); + + // Fogged as the surface is: the reflection is light leaving the surface + // and crosses the same air to the eye. Premultiplied for the blend. + frag_color = vec4(ApplyFog(reflected) * alpha, alpha); +} diff --git a/packages/flutter3d_shaders/shaders/post/render_texture_encode.frag b/packages/flutter3d_shaders/shaders/post/render_texture_encode.frag new file mode 100644 index 000000000..d45e81f02 --- /dev/null +++ b/packages/flutter3d_shaders/shaders/post/render_texture_encode.frag @@ -0,0 +1,50 @@ +#version 460 core + +// `P4`: what a `RenderTexture`'s camera saw, turned from linear light into +// the sRGB bytes a material's map is read as. +// +// **Encoded because every map is decoded.** A material reads its base colour +// and its emission through `SrgbToLinear`, so a texture holding linear light +// would be darkened a second time in every midtone by whatever showed it. In +// eight bits rather than half floats for the same reason: it is a picture, +// sampled like any other picture. +// +// **Exposure, and no tone map.** The texture is drawn into a frame that is +// tone mapped itself, so a curve applied here would be applied twice; what +// is above one after the camera's own exposure is clipped, as a screen +// clips it. +precision highp float; + +in vec2 v_uv; + +out vec4 frag_color; + +uniform sampler2D source_texture; + +uniform RenderTextureInfo { + /// x: the exposure the light is multiplied by before it is clipped. y: one + /// where the backend's first row is the bottom of a picture it draws. zw + /// unused. + vec4 params; +} +encode_info; + +vec3 LinearToSrgb(vec3 linear) { + return mix( + linear * 12.92, + 1.055 * pow(max(linear, vec3(0.0)), vec3(1.0 / 2.4)) - vec3(0.055), + step(vec3(0.0031308), linear)); +} + +void main() { + // **Written with the top of the picture in the first row, on every + // backend**, which is how a picture loaded from a file is uploaded and so + // how every material reads its maps. WebGL2 draws a picture with its + // bottom in the first row, so there the rows are turned over on the way + // through; a copy that kept them would show a monitor upside down in the + // browser alone. + vec2 uv = v_uv; + if (encode_info.params.y > 0.5) uv.y = 1.0 - uv.y; + vec3 light = textureLod(source_texture, uv, 0.0).rgb * encode_info.params.x; + frag_color = vec4(LinearToSrgb(clamp(light, vec3(0.0), vec3(1.0))), 1.0); +} diff --git a/packages/flutter3d_webgl/CHANGELOG.md b/packages/flutter3d_webgl/CHANGELOG.md index 27f8da04d..a8537c8da 100644 --- a/packages/flutter3d_webgl/CHANGELOG.md +++ b/packages/flutter3d_webgl/CHANGELOG.md @@ -12,6 +12,9 @@ what the package needs. A workspace that supports Flutter 3.44, Flame's among them, could not depend on it. Nothing else changed. **The generated tables carry the decal stage** of `flutter3d_shaders`. +**The generated tables carry `PlanarReflection` and +`RenderTextureEncode`**, and the `planar-mirror` and `render-texture` +goldens are in this set. ## 0.8.2 diff --git a/packages/flutter3d_webgl/lib/engine_shaders.dart b/packages/flutter3d_webgl/lib/engine_shaders.dart index db8235691..e5be6f451 100644 --- a/packages/flutter3d_webgl/lib/engine_shaders.dart +++ b/packages/flutter3d_webgl/lib/engine_shaders.dart @@ -6041,6 +6041,460 @@ void main() { WriteSurface(s.albedo, s.alpha); } +''', + 'PlanarReflection': r'''#version 300 es +precision highp float; +precision highp int; +precision highp sampler2D; +precision highp samplerCube; + +// `P4`: a planar reflector's surface, drawn a second time over itself with +// the picture a mirrored camera took of the world above it. +// +// **Laid over the surface rather than inside its lighting.** The reflection +// is read by the pixel's place on screen, not by anything the material +// knows, so the stage that reads it needs nothing a lit model has: no maps, +// no lights, no shadows. Putting it inside the lit models would have been a +// sampler and a block added to six stages that every other draw pays to +// declare, and a seventh header permutation to keep byte-identical. As a +// draw of its own it costs only the surfaces that reflect, and a frame +// without a reflector compiles and binds exactly what it did. +// +// Drawn with the depth test `lessEqual` and no depth write, straight after +// the opaque half, so it lands on exactly the pixels the surface itself won +// and nothing in front of the surface is painted. Blended source-over with +// the colour premultiplied, as every blended draw here is. +// +// **No second output**, for the reason `xray.frag` gives at length: a blend +// protects attachment zero only, and only on the backends whose `setBlend` +// honours an attachment index. The surface buffer keeps describing the +// surface underneath, which is the truth about it. +// +// **And not `lib/surface.glsl`**, which `xray.frag` does include: that header +// declares the base colour sampler for every stage that takes it, and a stage +// that never reads it has it dropped from the Metal function while reflection +// still reports it, which is the slot `metal_bindings_test.dart` exists to +// catch. The two things wanted from `FragInfo` come another way: the eye is +// `FogInfo`'s, and the target's rows ride in this stage's own block. +#define F3D_NO_SURFACE_BUFFER +// --- lib/color.glsl --- +// Colour space helpers and the fragment output interface. +// +// Split out of surface.glsl so a shader that needs no material inputs — the +// normals debug view — can avoid DECLARING the FragInfo uniform block at all. +// That matters more than it looks: reflection metadata reports a block as +// present merely because it was declared, even when the compiled shader binds +// no such buffer, so a declared-but-unused block is indistinguishable from a +// used one until Metal crashes on the bind. + +#ifndef COLOR_GLSL_ +#define COLOR_GLSL_ + +precision highp float; + +const float kPi = 3.14159265359; + +// One varying set shared by every fragment shader, matching mesh.vert. +// +// All five are declared here, including the two the debug models never read: a +// fragment shader whose `in` block disagrees with the vertex shader's `out` +// block fails to link, and there is no partial-match rule to lean on. +in vec3 v_world_position; +in vec3 v_normal; +in vec2 v_texcoord; +in vec4 v_tangent; +in vec4 v_color; + +/// Where this fragment is in the level's lightmap. Zero from every vertex +/// stage but `mesh_lightmapped.vert`, and read only by the lit models, which +/// sample a one-texel black there when a material has no map. +in vec2 v_lightmap_uv; + +layout(location = 0) out vec4 frag_color; + +// The second attachment: what a screen-space effect needs to know about the +// surface it is looking at. World-space normal in rgb, and in a the depth along +// the view axis in world metres — not a window depth; `WriteSurfaceGeometry` +// says at length why not. +// +// Depth travels here rather than in a depth texture because flutter_gpu cannot +// sample one — the same reason the shadow pass writes its depth into a colour +// target. See ARCHITECTURE.md §2. +// +// Guarded, because not every stage that includes this header draws into a +// two-attachment target. The shadow pass draws into one, and a pipeline +// declaring an output its target has no slot for is a mismatch worth avoiding +// rather than discovering. +#ifndef F3D_NO_SURFACE_BUFFER +layout(location = 1) out vec4 frag_surface; + +/// The surface's own colour, sRGB-encoded, alpha one where a surface was +/// drawn — `L5`. The third attachment, present only when a pass reads it (the +/// indirect light does) and the device opens three; like the surface buffer, +/// written unconditionally and discarded when absent. Stored in the surface +/// buffer's format rather than eight bits a channel, and `Renderer` says why. +layout(location = 2) out vec4 frag_albedo; +#endif + +/// What [frag_albedo] carries: the lit models set it in `ReadSurface`, and a +/// stage that reflects nothing — unlit, the debug views — leaves it black, +/// which is what light bounced onto it would come to. +vec3 g_albedo = vec3(0.0); + +/// Octahedral encoding: a unit vector in two channels instead of three. +/// +/// Worth the arithmetic because the fourth channel is already spent on depth, +/// and without a free channel there is nowhere to put roughness — which is the +/// difference between a reflection that knows stone from a mirror and one that +/// does not. The error is well under a degree, far below anything a reflection +/// off rough stone would show. +vec2 EncodeOctahedral(vec3 n) { + n /= abs(n.x) + abs(n.y) + abs(n.z); + vec2 e = n.xy; + if (n.z < 0.0) { + e = (1.0 - abs(n.yx)) * vec2(n.x >= 0.0 ? 1.0 : -1.0, + n.y >= 0.0 ? 1.0 : -1.0); + } + return e * 0.5 + 0.5; +} + +/// Where a debug pass leaves the picture it wants shown instead of the normal. +/// +/// Declared here, in the header every lit shader includes **first**, and +/// written from surface.glsl, which is included after. The alternative was a +/// new member on a shared uniform block; a global costs nothing and moves no +/// offsets. It is read at the moment the surface buffer is written, which +/// happens after the lighting loop has run, so the value is there by then. +vec3 g_debug_surface = vec3(0.0); +bool g_debug_surface_on = false; + +/// Whether [WriteSurface] weights the colour by its alpha: set by +/// `ReadSurface` for a material that blends, and false for everything else. +/// +/// **The blend takes its source as premultiplied**, so a blended surface has +/// to hand it the colour times the alpha — a pane at a fifth of opaque adds a +/// fifth of its light, not all of it. glTF's blend mode is Porter and Duff's +/// over on straight colour, and this is the one place that turns the lit +/// radiance into what that means. An opaque or masked surface keeps its +/// colour whole: its alpha is not a coverage, and nothing blends it. +/// A global for the reason [g_debug_surface] is one. +bool g_premultiply = false; + +// **A stage that needs none of this must be able to declare none of it.** On +// Vulkan both stages' descriptors are merged into one set layout, and two +// bindings with the same number in it is not a layout the specification +// allows. A driver may accept it anyway; a Galaxy A55's refuses the pipeline +// with `ErrorUnknown` and no other word, which is how the shadow pass came to +// build everywhere except there — its only uniform block was this one, and it +// landed on the same binding as the vertex stage's first. +#ifndef F3D_NO_FOG + +/// Distance fog, in its own block rather than folded into FragInfo. +/// +/// Its own because color.glsl is included before FragInfo is declared, and +/// because appending to a block that half a dozen shaders already share is a +/// way to move offsets nobody expected to move. Three vec4s is a cheap price +/// for not touching any of that. +layout(std140) uniform FogInfo { + /// rgb: linear fog colour. w: density per metre, zero for no fog. + vec4 fog; + + /// xyz: camera position in world space. Duplicated from FragInfo so this + /// block stands alone; a vec3 is cheaper than a coupling. + vec4 eye; + + /// xyz: the direction the camera looks, as a unit vector in world space. + /// w: what a transparent draw writes under weighted blended transparency — + /// `R8`, see `WriteWeightedBlended`. Zero for every other draw. + /// + /// Here rather than in a block of its own because it answers the same + /// question [eye] does — where the camera is and which way it faces — and + /// this is the block `color.glsl` can see. + vec4 forward; +} +fog_info; + +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face. +float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } + +/// How far this fragment is *along the view axis*, in world metres. +/// +/// What the surface buffer's alpha holds. Depth rather than distance, and the +/// difference only shows on an orthographic camera — where the rays through +/// the pixels are parallel instead of meeting at the eye, so a distance from +/// the eye names a sphere that the pixel's ray crosses somewhere the reader +/// cannot solve for. A depth along the axis names a plane, which every ray +/// crosses exactly once. See `WorldAtDepth` in `post/ssao.frag` for the +/// reconstruction both projections share. +float ViewDepth() { + return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); +} + +#else // F3D_NO_FOG + +// The same two questions, answered without the block: a stage that declares no +// fog has no eye position to measure from either. Stubs rather than a guard at +// every call site, so that what includes this file reads the same whichever +// way it was compiled. +float EyeDistance() { return 0.0; } +float ViewDepth() { return 0.0; } + +#endif // F3D_NO_FOG + +/// sRGB to linear. Textures are authored in sRGB, but lighting is only correct +/// in linear space; skipping this is what makes naive renderers look muddy. +vec3 SrgbToLinear(vec3 srgb) { + return mix( + srgb / 12.92, + pow((srgb + vec3(0.055)) / 1.055, vec3(2.4)), + step(vec3(0.04045), srgb)); +} + +/// Linear to sRGB. The render target is a plain UNorm format rather than an +/// sRGB one, so the encode has to happen here. +vec3 LinearToSrgb(vec3 linear) { + return mix( + linear * 12.92, + 1.055 * pow(linear, vec3(1.0 / 2.4)) - vec3(0.055), + step(vec3(0.0031308), linear)); +} + +/// Writes scene-referred linear light into the HDR target. +/// +/// No tone map and no sRGB encode: those moved into the composite pass, which +/// is the entire point of rendering into `r16g16b16a16Float` first. Applying +/// them here meant every model wrote display-referred colour into an 8-bit +/// buffer, so anything above display white was gone before post-processing +/// could see it — and bloom is a function of exactly that. +/// +/// Exposure moved with them, for the same reason: it belongs on the same side +/// of the display transform as the tone map. +/// Records the geometry of this fragment for whatever runs after the scene. +/// +/// Called from the same place that writes colour, so a surface cannot be lit +/// into the frame without also describing itself — which is the failure that +/// leaves a screen-space effect reflecting whatever was in the buffer before. +/// +/// rg: octahedral normal. b: perceptual roughness. a: **depth along the view +/// axis, in world metres** — see [ViewDepth]. +/// +/// **Not `gl_FragCoord.z`, and that is a defect this channel carried until it +/// was looked at.** Window depth crowds every distant surface into the top of +/// its range — with a near plane of a tenth of a metre, everything past twenty +/// metres lives in the last half a hundredth of `[0, 1]` — and this attachment +/// is a half float, whose steps up there are about five ten-thousandths. So two +/// surfaces half a metre apart at twenty metres stored the *same* number, and +/// every screen-space pass that compares against this channel decided whole +/// bands of pixels by rounding. The occlusion pass drew them: vertical stripes +/// along the lines of constant depth on any wall receding from the camera, on +/// both GPU backends. The software rasteriser kept the channel at full +/// precision and drew the effect correctly, so it was the one that looked +/// wrong against the other two. +/// +/// A depth in metres has none of that: the exponent carries the range and the +/// mantissa carries the same relative precision everywhere, which at twenty +/// metres is a centimetre. Both numbers are measured in +/// `flutter3d/test/surface_depth_test.dart`. +/// +/// Zero still means nothing was drawn. The attachment is cleared to zero and +/// nothing is drawn in front of the near plane. +void WriteSurfaceGeometry(float roughness) { +#ifndef F3D_NO_SURFACE_BUFFER + // `L5`: the surface's colour, whatever the surface buffer ends up holding. + frag_albedo = vec4(LinearToSrgb(clamp(g_albedo, vec3(0.0), vec3(1.0))), 1.0); + // A debug pass takes the buffer over rather than getting one of its own. + // The surface buffer already has an attachment, a viewer and a golden; a + // second one would need all three built before it could answer anything. + if (g_debug_surface_on) { + frag_surface = vec4(g_debug_surface, ViewDepth()); + return; + } + // Reversed on a back face, as the lit normal is, so the occlusion and + // reflection passes see the side of a double-sided surface that faces them. + vec3 geometric = normalize(v_normal); + if (!gl_FrontFacing) geometric = -geometric; + frag_surface = vec4(EncodeOctahedral(geometric), + clamp(roughness, 0.0, 1.0), ViewDepth()); +#endif +} + +/// Fades [color] toward the fog with distance from the eye. +/// +/// Exponential rather than linear, because linear fog has a visible plane +/// where it starts and a dungeon corridor is exactly where that shows. +vec3 ApplyFog(vec3 color) { +#ifdef F3D_NO_FOG + return color; +#else + float density = fog_info.fog.w; + if (density <= 0.0) return color; + float d = EyeDistance(); + return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); +#endif +} + +/// How much a transparent fragment counts for against the others over its +/// pixel — `R8`. McGuire and Bavoil's depth weight (their equation 9): a near +/// layer outweighs a far one, which is all the ordering a weighted average +/// can keep. [alpha] multiplies it, as theirs does, so a faint layer counts +/// faintly. Depth along the view axis, in metres, the surface buffer's. +float WeightedBlendedWeight(float alpha) { + float z = abs(ViewDepth()); + float near = z / 5.0; + float far = z / 200.0; + float far3 = far * far * far; + return alpha * + clamp(10.0 / (1e-5 + near * near + far3 * far3), 1e-2, 3e3); +} + +/// What a transparent draw writes when the frame composites transparency +/// order-independently — `R8`. `fog_info.forward.w` says which: +/// +/// - 0: [frag_color] as it stands, the sorted blend's source. Every opaque +/// draw, and every draw in a frame that sorts. +/// - 1: the accumulation target's share — the colour, which the engine keeps +/// premultiplied, and the alpha, both times the weight. Added. +/// - 2: the revealage target's — the alpha alone, in every channel, which the +/// blend multiplies the target by one minus of. +/// - 3: both at once, the second into attachment one, where the surface +/// buffer would be; the pass that asks has no surface buffer attached. +/// +/// Selects rather than returns, because a phi of constants is what +/// SPIRV-Cross refuses. At nought the branch is not taken and [frag_color] +/// is untouched, which is what keeps a sorting frame byte-identical. +void WriteWeightedBlended() { +#ifndef F3D_NO_FOG + float mode = fog_info.forward.w; + if (mode > 0.5) { + float alpha = frag_color.a; + float weight = WeightedBlendedWeight(alpha); + vec4 accumulate = vec4(frag_color.rgb * weight, alpha * weight); + bool revealage = mode > 1.5 && mode < 2.5; + frag_color = revealage ? vec4(alpha) : accumulate; +#ifndef F3D_NO_SURFACE_BUFFER + if (mode > 2.5) frag_surface = vec4(alpha); +#endif + } +#endif +} + +/// The fog is mixed in before the weight, so a thin distant pane adds a thin +/// share of the fog too rather than all of it. Times one when nothing blends, +/// which is exact, so an opaque draw writes what it always wrote. +void WriteSurface(vec3 linearColor, float alpha, float roughness) { + float weight = g_premultiply ? alpha : 1.0; + frag_color = vec4(ApplyFog(linearColor) * weight, alpha); + WriteSurfaceGeometry(roughness); + WriteWeightedBlended(); +} + +/// For a stage with no material to speak of. +/// +/// Fully rough, which is the honest default: a surface that cannot say how +/// polished it is should not be reflected off. +void WriteSurface(vec3 linearColor, float alpha) { + WriteSurface(linearColor, alpha, 1.0); +} + +/// Writes a value that is already display-referred. +/// +/// For debug output, where the colour is not a light value at all: a normal +/// encoded as RGB means nothing after a tone curve. Converting to linear here +/// means the composite pass's sRGB encode hands the original back unchanged, +/// provided the view also turns tone mapping and exposure off — which is what +/// `RenderSettings.tonemap` is for. +void WriteDisplayColor(vec3 displayColor, float alpha) { + frag_color = vec4(SrgbToLinear(displayColor), alpha); + WriteSurfaceGeometry(1.0); +} + +#endif // COLOR_GLSL_ + +// --- lib/frag_coord.glsl --- +// Where a fragment sits, counted from the top of its target on every backend. + +#ifndef FRAG_COORD_GLSL_ +#define FRAG_COORD_GLSL_ + +/// `gl_FragCoord.xy` with row zero at the top of the picture. +/// +/// [rows] is the target's height where the backend's row zero is the bottom +/// of the picture, and zero where it is the top. WebGL2 is the first kind: +/// window coordinates start at the lower left, and the engine draws the +/// picture upright there rather than mirroring every projection. Metal, +/// WebGPU and the software rasteriser are the second. +/// +/// **Why a pattern cares and a picture does not.** Every screen-space pattern +/// in the engine — the Bayer dither, the grain, the jitter a ray march starts +/// from, the rotation of a shadow kernel — is a function of the pixel's row. +/// Read from the bottom, the same frame gets the pattern turned upside down, +/// and a four-row Bayer cell lands on different rows unless the height is a +/// multiple of four. The picture underneath is identical; the pattern on top +/// of it is not, and a comparison across backends counts every pixel it +/// moved. +vec2 FragCoordFromTop(float rows) { + return rows > 0.0 ? vec2(gl_FragCoord.x, rows - gl_FragCoord.y) + : gl_FragCoord.xy; +} + +#endif // FRAG_COORD_GLSL_ + + +/// What the mirrored camera saw, in linear light, the size of the view it +/// was taken for or a fraction of it. +uniform sampler2D reflection_texture; + +layout(std140) uniform PlanarReflectionInfo { + /// xy: the view's top-left corner in pixels of the target, counted from + /// the top. zw: the view's size in pixels. + vec4 view; + + /// x: the reflectance straight on, Schlick's F0 — one for a mirror, about + /// 0.02 for water. y: the strength the reflection is laid on with, which + /// scales the whole Fresnel term. z: the target's rows where its first row + /// is the bottom of the picture, nought where it is the top — see + /// `FragCoordFromTop`. w unused. + vec4 params; + + /// rgb: a linear tint the reflected light is multiplied by. w unused. + vec4 tint; +} +planar_info; + +void main() { + // The same pixel the mirrored camera drew, by place on screen: its + // projection is the view's own, so the texel under this fragment is the + // reflected point behind it. Counted from the top on every backend, and + // turned back where the texture's first row is the bottom of the picture. + float rows = planar_info.params.z; + vec2 pixel = FragCoordFromTop(rows); + vec2 uv = (pixel - planar_info.view.xy) / planar_info.view.zw; + if (rows > 0.0) uv.y = 1.0 - uv.y; + vec3 reflected = + textureLod(reflection_texture, uv, 0.0).rgb * planar_info.tint.rgb; + + // Schlick's Fresnel on the geometric normal, facing the eye: water reflects + // a little looking down into it and nearly everything at a grazing angle, + // and a mirror's F0 of one makes the term one everywhere. + vec3 n = normalize(v_normal); + if (!gl_FrontFacing) n = -n; + vec3 v = normalize(fog_info.eye.xyz - v_world_position); + float cosine = clamp(dot(n, v), 0.0, 1.0); + float f0 = planar_info.params.x; + float grazing = 1.0 - cosine; + float grazing2 = grazing * grazing; + float fresnel = f0 + (1.0 - f0) * grazing2 * grazing2 * grazing; + float alpha = clamp(fresnel * planar_info.params.y, 0.0, 1.0); + + // Fogged as the surface is: the reflection is light leaving the surface + // and crosses the same air to the eye. Premultiplied for the blend. + frag_color = vec4(ApplyFog(reflected) * alpha, alpha); +} + ''', 'Lambert': r'''#version 300 es precision highp float; @@ -29580,6 +30034,62 @@ void main() { frag_color = vec4(weight > 0.0 ? sum / weight : vec3(0.0), 1.0); } +''', + 'RenderTextureEncode': r'''#version 300 es +precision highp float; +precision highp int; +precision highp sampler2D; +precision highp samplerCube; + +// `P4`: what a `RenderTexture`'s camera saw, turned from linear light into +// the sRGB bytes a material's map is read as. +// +// **Encoded because every map is decoded.** A material reads its base colour +// and its emission through `SrgbToLinear`, so a texture holding linear light +// would be darkened a second time in every midtone by whatever showed it. In +// eight bits rather than half floats for the same reason: it is a picture, +// sampled like any other picture. +// +// **Exposure, and no tone map.** The texture is drawn into a frame that is +// tone mapped itself, so a curve applied here would be applied twice; what +// is above one after the camera's own exposure is clipped, as a screen +// clips it. +precision highp float; + +in vec2 v_uv; + +layout(location = 0) out vec4 frag_color; + +uniform sampler2D source_texture; + +layout(std140) uniform RenderTextureInfo { + /// x: the exposure the light is multiplied by before it is clipped. y: one + /// where the backend's first row is the bottom of a picture it draws. zw + /// unused. + vec4 params; +} +encode_info; + +vec3 LinearToSrgb(vec3 linear) { + return mix( + linear * 12.92, + 1.055 * pow(max(linear, vec3(0.0)), vec3(1.0 / 2.4)) - vec3(0.055), + step(vec3(0.0031308), linear)); +} + +void main() { + // **Written with the top of the picture in the first row, on every + // backend**, which is how a picture loaded from a file is uploaded and so + // how every material reads its maps. WebGL2 draws a picture with its + // bottom in the first row, so there the rows are turned over on the way + // through; a copy that kept them would show a monitor upside down in the + // browser alone. + vec2 uv = v_uv; + if (encode_info.params.y > 0.5) uv.y = 1.0 - uv.y; + vec3 light = textureLod(source_texture, uv, 0.0).rgb * encode_info.params.x; + frag_color = vec4(LinearToSrgb(clamp(light, vec3(0.0), vec3(1.0))), 1.0); +} + ''', 'ShadowDistance': r'''#version 300 es precision highp float; diff --git a/packages/flutter3d_webgl/test/cross_backend_test.dart b/packages/flutter3d_webgl/test/cross_backend_test.dart index ca73cb309..6d1024291 100644 --- a/packages/flutter3d_webgl/test/cross_backend_test.dart +++ b/packages/flutter3d_webgl/test/cross_backend_test.dart @@ -302,6 +302,12 @@ const Map _budgets = { 'window-interior': 0.01, // `P3`. 0 of 172800 measured against Impeller on 2026-10-01. 'decal-floor': 0.01, + // `P4`. 0.172% and 0.193% measured against Impeller on 2026-10-01, single + // pixels along silhouette edges, where the two multisample differently; + // the reflection and the monitor's picture, which this backend writes + // with its rows turned over, agree inside. + 'planar-mirror': 0.2, + 'render-texture': 0.22, }; /// Scenes budgeted before this set had a picture of them. diff --git a/packages/flutter3d_webgl/test/goldens/planar-mirror.png b/packages/flutter3d_webgl/test/goldens/planar-mirror.png new file mode 100644 index 0000000000000000000000000000000000000000..b95e8a5ba8e2e3ffd3fd3c922070ccb54fb579e2 GIT binary patch literal 25340 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

    QIhnz<@sEyLBT{WLn)4<vX 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 R%P`2qQUs~VR>^z_{vRH&)sz4L literal 0 HcmV?d00001 diff --git a/packages/flutter3d_webgl/test/lighting_models_test.dart b/packages/flutter3d_webgl/test/lighting_models_test.dart index fdab224e8..d7d83aa95 100644 --- a/packages/flutter3d_webgl/test/lighting_models_test.dart +++ b/packages/flutter3d_webgl/test/lighting_models_test.dart @@ -6,7 +6,7 @@ /// `lighting-unlit` came back as an empty frame — the background colour in every /// pixel — while the other five drew a sphere. The golden set found it, once /// there was a golden set; this says the same thing in one second instead of a -/// browser run over eighty-two scenes, and says it per model rather than per +/// browser run over eighty-four scenes, and says it per model rather than per /// picture. /// /// It deliberately does not compare against a reference. What a shader *should* diff --git a/packages/flutter3d_webgl/tool/golden_web.sh b/packages/flutter3d_webgl/tool/golden_web.sh index 00b5e53ae..983f94962 100755 --- a/packages/flutter3d_webgl/tool/golden_web.sh +++ b/packages/flutter3d_webgl/tool/golden_web.sh @@ -16,7 +16,7 @@ # the same one either way, so this script has no reason to read it. # # **One build for the whole suite, and for both browser backends.** The scene is -# a query parameter rather than a compile-time define, so the eighty-two scenes +# a query parameter rather than a compile-time define, so the eighty-four scenes # are one dart2js run and a navigation each, rather than a dart2js run each. # That is the only reason this is minutes rather than an hour, and it is why the # backend arrives the same way: a define per backend would have spent the saving diff --git a/packages/flutter3d_webgpu/CHANGELOG.md b/packages/flutter3d_webgpu/CHANGELOG.md index 4626fd568..99947dc3c 100644 --- a/packages/flutter3d_webgpu/CHANGELOG.md +++ b/packages/flutter3d_webgpu/CHANGELOG.md @@ -14,6 +14,10 @@ what the package needs. A workspace that supports Flutter 3.44, Flame's among them, could not depend on it. Nothing else changed. **The generated tables carry the decal stage** of `flutter3d_shaders`, through glslang and naga like every other stage. +**The generated tables carry `PlanarReflection` and +`RenderTextureEncode`**, through glslang and naga like every other stage, +and the `planar-mirror` and `render-texture` goldens are in this set, both 0 +of 172800 pixels from Impeller. ## 0.8.2 diff --git a/packages/flutter3d_webgpu/lib/engine_shaders.dart b/packages/flutter3d_webgpu/lib/engine_shaders.dart index 0361ca8e7..104131e53 100644 --- a/packages/flutter3d_webgpu/lib/engine_shaders.dart +++ b/packages/flutter3d_webgpu/lib/engine_shaders.dart @@ -5044,6 +5044,221 @@ fn main(@location(6) v_world_position: vec3, @location(4) v_texcoord: vec2< ), ], ), + 'PlanarReflection': WebGpuStage( + wgsl: r''' +struct FogInfo { + fog: vec4, + eye: vec4, + forward: vec4, +} + +struct PlanarReflectionInfo { + view: vec4, + params: vec4, + tint: vec4, +} + +var g_albedo: vec3; +var g_debug_surface: vec3; +var g_debug_surface_on: bool; +var g_premultiply: bool; +var v_world_position_1: vec3; +@group(1) @binding(0) +var fog_info: FogInfo; +var gl_FragCoord_1: vec4; +@group(1) @binding(1) +var planar_info: PlanarReflectionInfo; +@group(1) @binding(2) +var reflection_texture_tex: texture_2d; +@group(1) @binding(3) +var reflection_texture_smp: sampler; +var v_normal_1: vec3; +var gl_FrontFacing_1: bool; +var frag_color: vec4; +var v_texcoord_1: vec2; +var v_tangent_1: vec4; +var v_color_1: vec4; +var v_lightmap_uv_1: vec2; + +fn EyeDistance_u0028_() -> f32 { + let _e29 = v_world_position_1; + let _e31 = fog_info.eye; + return distance(_e29, _e31.xyz); +} + +fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { + var density: f32; + var d: f32; + + let _e34 = fog_info.fog[3u]; + density = _e34; + let _e35 = density; + if (_e35 <= 0f) { + let _e37 = (*color); + return _e37; + } + let _e38 = EyeDistance_u0028_(); + d = _e38; + let _e40 = fog_info.fog; + let _e42 = (*color); + let _e43 = density; + let _e45 = d; + return mix(_e40.xyz, _e42, vec3(clamp(exp((-(_e43) * _e45)), 0f, 1f))); +} + +fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { + var local: vec2; + + let _e31 = (*rows); + if (_e31 > 0f) { + let _e34 = gl_FragCoord_1[0u]; + let _e35 = (*rows); + let _e37 = gl_FragCoord_1[1u]; + local = vec2(_e34, (_e35 - _e37)); + } else { + let _e40 = gl_FragCoord_1; + local = _e40.xy; + } + let _e42 = local; + return _e42; +} + +fn main_1() { + var rows_1: f32; + var pixel: vec2; + var param: f32; + var uv: vec2; + var reflected: vec3; + var n: vec3; + var v: vec3; + var cosine: f32; + var f0_: f32; + var grazing: f32; + var grazing2_: f32; + var fresnel: f32; + var alpha: f32; + var param_1: vec3; + + g_albedo = vec3(0f, 0f, 0f); + g_debug_surface = vec3(0f, 0f, 0f); + g_debug_surface_on = false; + g_premultiply = false; + let _e45 = planar_info.params[2u]; + rows_1 = _e45; + let _e46 = rows_1; + param = _e46; + let _e47 = FragCoordFromTop_u0028_f1_u003b((¶m)); + pixel = _e47; + let _e48 = pixel; + let _e50 = planar_info.view; + let _e54 = planar_info.view; + uv = ((_e48 - _e50.xy) / _e54.zw); + let _e57 = rows_1; + if (_e57 > 0f) { + let _e60 = uv[1u]; + uv[1u] = (1f - _e60); + } + let _e63 = uv; + let _e64 = textureSampleLevel(reflection_texture_tex, reflection_texture_smp, _e63, 0f); + let _e67 = planar_info.tint; + reflected = (_e64.xyz * _e67.xyz); + let _e70 = v_normal_1; + n = normalize(_e70); + let _e72 = gl_FrontFacing_1; + if !(_e72) { + let _e74 = n; + n = -(_e74); + } + let _e77 = fog_info.eye; + let _e79 = v_world_position_1; + v = normalize((_e77.xyz - _e79)); + let _e82 = n; + let _e83 = v; + cosine = clamp(dot(_e82, _e83), 0f, 1f); + let _e88 = planar_info.params[0u]; + f0_ = _e88; + let _e89 = cosine; + grazing = (1f - _e89); + let _e91 = grazing; + let _e92 = grazing; + grazing2_ = (_e91 * _e92); + let _e94 = f0_; + let _e95 = f0_; + let _e97 = grazing2_; + let _e99 = grazing2_; + let _e101 = grazing; + fresnel = (_e94 + ((((1f - _e95) * _e97) * _e99) * _e101)); + let _e104 = fresnel; + let _e107 = planar_info.params[1u]; + alpha = clamp((_e104 * _e107), 0f, 1f); + let _e110 = reflected; + param_1 = _e110; + let _e111 = ApplyFog_u0028_vf3_u003b((¶m_1)); + let _e112 = alpha; + let _e113 = (_e111 * _e112); + let _e114 = alpha; + frag_color = vec4(_e113.x, _e113.y, _e113.z, _e114); + return; +} + +@fragment +fn main(@location(6) v_world_position: vec3, @builtin(position) gl_FragCoord: vec4, @location(2) v_normal: vec3, @builtin(front_facing) gl_FrontFacing: bool, @location(4) v_texcoord: vec2, @location(3) v_tangent: vec4, @location(0) v_color: vec4, @location(1) v_lightmap_uv: vec2) -> @location(0) vec4 { + v_world_position_1 = v_world_position; + gl_FragCoord_1 = gl_FragCoord; + v_normal_1 = v_normal; + gl_FrontFacing_1 = gl_FrontFacing; + v_texcoord_1 = v_texcoord; + v_tangent_1 = v_tangent; + v_color_1 = v_color; + v_lightmap_uv_1 = v_lightmap_uv; + main_1(); + let _e17 = frag_color; + return _e17; +} +''', + attributes: [], + blocks: [ + WebGpuBlock( + name: 'FogInfo', + group: 1, + binding: 0, + sizeInBytes: 48, + members: [ + WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), + WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), + WebGpuBlockMember( + name: 'forward', + offsetInBytes: 32, + sizeInBytes: 16, + ), + ], + ), + WebGpuBlock( + name: 'PlanarReflectionInfo', + group: 1, + binding: 1, + sizeInBytes: 48, + members: [ + WebGpuBlockMember(name: 'view', offsetInBytes: 0, sizeInBytes: 16), + WebGpuBlockMember( + name: 'params', + offsetInBytes: 16, + sizeInBytes: 16, + ), + WebGpuBlockMember(name: 'tint', offsetInBytes: 32, sizeInBytes: 16), + ], + ), + ], + samplers: [ + WebGpuSampler( + name: 'reflection_texture', + group: 1, + textureBinding: 2, + samplerBinding: 3, + dimension: WebGpuTextureDimension.twoDimensional, + ), + ], + ), 'Lambert': WebGpuStage( wgsl: r''' struct Surface { @@ -40703,6 +40918,85 @@ fn main(@location(5) v_uv: vec2) -> @location(0) vec4 { ), ], ), + 'RenderTextureEncode': WebGpuStage( + wgsl: r''' +struct RenderTextureInfo { + params: vec4, +} + +var v_uv_1: vec2; +@group(1) @binding(0) +var encode_info: RenderTextureInfo; +@group(1) @binding(1) +var source_texture_tex: texture_2d; +@group(1) @binding(2) +var source_texture_smp: sampler; +var frag_color: vec4; + +fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { + let _e22 = (*linear); + let _e24 = (*linear); + let _e29 = (*linear); + return mix((_e22 * 12.92f), ((pow(max(_e24, vec3(0f, 0f, 0f)), vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e29)); +} + +fn main_1() { + var uv: vec2; + var light: vec3; + var param: vec3; + + let _e24 = v_uv_1; + uv = _e24; + let _e27 = encode_info.params[1u]; + if (_e27 > 0.5f) { + let _e30 = uv[1u]; + uv[1u] = (1f - _e30); + } + let _e33 = uv; + let _e34 = textureSampleLevel(source_texture_tex, source_texture_smp, _e33, 0f); + let _e38 = encode_info.params[0u]; + light = (_e34.xyz * _e38); + let _e40 = light; + param = clamp(_e40, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e42 = LinearToSrgb_u0028_vf3_u003b((¶m)); + frag_color = vec4(_e42.x, _e42.y, _e42.z, 1f); + return; +} + +@fragment +fn main(@location(5) v_uv: vec2) -> @location(0) vec4 { + v_uv_1 = v_uv; + main_1(); + let _e3 = frag_color; + return _e3; +} +''', + attributes: [], + blocks: [ + WebGpuBlock( + name: 'RenderTextureInfo', + group: 1, + binding: 0, + sizeInBytes: 16, + members: [ + WebGpuBlockMember( + name: 'params', + offsetInBytes: 0, + sizeInBytes: 16, + ), + ], + ), + ], + samplers: [ + WebGpuSampler( + name: 'source_texture', + group: 1, + textureBinding: 1, + samplerBinding: 2, + dimension: WebGpuTextureDimension.twoDimensional, + ), + ], + ), 'ShadowDistance': WebGpuStage( wgsl: r''' struct ShadowLight { diff --git a/packages/flutter3d_webgpu/test/cross_backend_test.dart b/packages/flutter3d_webgpu/test/cross_backend_test.dart index 362a20a0f..ee081dcdb 100644 --- a/packages/flutter3d_webgpu/test/cross_backend_test.dart +++ b/packages/flutter3d_webgpu/test/cross_backend_test.dart @@ -227,6 +227,9 @@ const Map _budgets = { 'window-interior': 0.01, // `P3`, 0 of 172800 measured on 2026-10-01. 'decal-floor': 0.01, + // `P4`. 0 of 172800 measured against Impeller on 2026-10-01. + 'planar-mirror': 0.01, + 'render-texture': 0.01, }; /// Scenes this set holds no picture of, and why the picture was refused. diff --git a/packages/flutter3d_webgpu/test/goldens/planar-mirror.png b/packages/flutter3d_webgpu/test/goldens/planar-mirror.png new file mode 100644 index 0000000000000000000000000000000000000000..b7974e170aea128ceee2ce80e6c2ccc1202947c0 GIT binary patch literal 25361 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 -

    +

    Writing a fifth one is a documented job rather than an archaeology exercise: Writing a HAL backend covers the whole contract, the ten semantics that appear in no signature, the conformance suite you can run before compiling a single shader, and the ninety shader entry points your bundle has to answer to.

    ### What the HAL actually names diff --git a/site/content/core/backends.md b/site/content/core/backends.md index 43bc1fe97..d5d26929c 100644 --- a/site/content/core/backends.md +++ b/site/content/core/backends.md @@ -20,7 +20,7 @@ What could **not** be written from the contract is the shaders. That limit is re
  • The semantics that are part of the contract and appear in no signature
  • What the 0.8 members ask, compute among them, and which backend answers yes
  • The conformance suite, and how to run it before you have a single shader
  • -
  • The eighty-eight shader entry points your bundle must answer to
  • +
  • The ninety shader entry points your bundle must answer to
  • @@ -398,14 +398,15 @@ for (final RequiredShader shader in kRequiredShaders) { } ``` -Eighty-three entry points. `kRequiredShaders` and the bundle manifest are kept in step with each other by `flutter3d_shaders/test/manifest_test.dart`, and this table is kept in step with both by the `the site names every shader a bundle must answer to` rule. It was eleven names short for as long as nothing compared it with anything, and a missing name is not a degraded picture: `Renderer.create` throws on the first one it cannot find. +Eighty-five entry points. `kRequiredShaders` and the bundle manifest are kept in step with each other by `flutter3d_shaders/test/manifest_test.dart`, and this table is kept in step with both by the `the site names every shader a bundle must answer to` rule. It was eleven names short for as long as nothing compared it with anything, and a missing name is not a degraded picture: `Renderer.create` throws on the first one it cannot find. | Stage | Names | |---|---| | Vertex | `MeshVertex`, `MeshSkinnedVertex`, `MeshInstancedVertex`, `MeshLightmappedVertex`, `FullscreenVertex`, `DebugLineVertex`, `ParticleVertex`, `ParticleMeshVertex`, `PolylineVertex`, `ShadowTileResetVertex`, `SkyVertex`, `SkyCubeVertex`, `ImpostorVertex`, `VelocityVertex`, `VelocitySkinnedVertex`, `VelocityInstancedVertex` | -| Lighting | `Unlit`, `Lambert`, `BlinnPhong`, `Pbr`, `Toon`, `Normals`, `PbrLayered`, `Impostor` | +| Lighting | `Unlit`, `Lambert`, `BlinnPhong`, `Pbr`, `Toon`, `Normals`, `PbrLayered`, `Impostor`, `PlanarReflection` | | Shadows | `ShadowDepth`, `ShadowDistance`, `ShadowDepthMasked`, `ShadowDistanceMasked`, `ShadowTileReset`, `EvsmFilter`, `ShadowCopy` | | Post | `BloomThreshold`, `BloomDownsample`, `BloomUpsample`, `Composite`, `Fxaa`, `SmaaEdges`, `SmaaWeights`, `SmaaBlend`, `LensFlare`, `Reflections`, `Ssao`, `SsaoBlur`, `ContactShadow`, `ContactShadowResolve`, `Splat`, `LightShafts`, `DepthOfField`, `ViewportShade`, `Luminance`, `ProbePrefilter`, `CameraVelocity`, `Velocity`, `VelocityTileMax`, `VelocityNeighborMax`, `MotionBlur`, `TemporalResolve`, `TemporalAccumulate`, `Reactive`, `Easu`, `LocalExposure`, `LocalExposureBlur`, `VolumetricFog`, `VolumetricFogUpsample`, `DepthPyramid`, `SceneColourCopy`, `WboitResolve`, `DofTileMax`, `Decal` | +| Post | `BloomThreshold`, `BloomDownsample`, `BloomUpsample`, `Composite`, `Fxaa`, `Reflections`, `Ssao`, `SsaoBlur`, `ContactShadow`, `ContactShadowResolve`, `Splat`, `LightShafts`, `DepthOfField`, `ViewportShade`, `Luminance`, `ProbePrefilter`, `CameraVelocity`, `Velocity`, `VelocityTileMax`, `VelocityNeighborMax`, `MotionBlur`, `TemporalResolve`, `TemporalAccumulate`, `Reactive`, `Easu`, `LocalExposure`, `LocalExposureBlur`, `VolumetricFog`, `VolumetricFogUpsample`, `DepthPyramid`, `SceneColourCopy`, `WboitResolve`, `DofTileMax`, `RenderTextureEncode` | | Particles | `Particle`, `ParticleTextured`, `ParticleMesh`, `ParticleSixWay`, `ReactiveSprite`, `SplatHashed`, `ParticleSoft`, `ParticleTexturedSoft`, `ParticleSixWaySoft` | | Sky | `Sky`, `SkyCube` | | Debug | `DebugLine`, `MrtProbe`, `ObjectId`, `Xray` | diff --git a/site/content/core/index.md b/site/content/core/index.md index 5ccb1e9e1..f77f8951f 100644 --- a/site/content/core/index.md +++ b/site/content/core/index.md @@ -25,7 +25,7 @@ Nothing in this section is genre knowledge. That property is what made the secon |---|---| | `flutter3d_impeller` | `flutter_gpu`, over Metal and Vulkan. The production one | | `flutter3d_webgl` | WebGL2 in the browser. Runs all three games at a fixed resolution and a lower frame rate, and is what an ordinary web build opens | -| `flutter3d_cpu` | Nothing: it rasterises in Dart, so 82 golden scenes stay checkable with no GPU in the room | +| `flutter3d_cpu` | Nothing: it rasterises in Dart, so 84 golden scenes stay checkable with no GPU in the room | | `flutter3d_webgpu` | WebGPU in a browser that has an adapter. Reached by `--dart-define=FLUTTER3D_WEBGPU=true`, because a build that can try it ships it | `flutter3d_conformance` is the suite each of them passed before it belonged in that table, and [Writing a HAL backend](/core/backends/) is the guide for writing the fifth. diff --git a/site/content/core/rendering.md b/site/content/core/rendering.md index 0085e9c97..77a84f3df 100644 --- a/site/content/core/rendering.md +++ b/site/content/core/rendering.md @@ -1,5 +1,5 @@ --- -description: Render views, the pass order, instanced batches, precomputed visibility, baked lightmaps, HDR and tone mapping, auto and local exposure, HDR output, bloom, cascaded and point shadows, the sky, reflection probes, fog, screen-space reflections, ambient occlusion, colour grading, temporal anti-aliasing, glass and transparency, material layers, occlusion culling, the frame budget, picking by pixel, and the frame graph that schedules them. +description: Render views, the pass order, instanced batches, precomputed visibility, baked lightmaps, HDR and tone mapping, auto and local exposure, HDR output, bloom, cascaded and point shadows, the sky, reflection probes, planar reflections and cameras into textures, fog, screen-space reflections, ambient occlusion, colour grading, temporal anti-aliasing, glass and transparency, material layers, occlusion culling, the frame budget, picking by pixel, and the frame graph that schedules them. --- # The frame @@ -487,7 +487,7 @@ It is marched at half resolution and brought up by depth, so a halo behind a pil ```dart SkySettings( - enabled: true, // off by default: eighty-two goldens are recorded against none + enabled: true, // off by default: eighty-four goldens are recorded against none zenith: Vector3(0.10, 0.22, 0.52), horizon: Vector3(0.42, 0.50, 0.62), nadir: Vector3(0.06, 0.06, 0.07), // what fills the frame looking down at nothing @@ -534,6 +534,43 @@ What a rolling one costs was measured on the racing demo's frame (the player's c

    A cube map is addressed by a left-handed table (on the +X face, column zero looks along +Z), and a right-handed camera puts every face's left on the right, so each view is drawn through a mirror and the winding is flipped with it; a backend whose row zero is at the bottom negates y as well, which makes the two a half turn and leaves the winding alone. Nothing in a picture says whether a face is mirrored, which is why the face table is tested by projecting known directions on both origins, and why the conformance suite clears one face of one level and reads it back through the very stage that fills the chain.

    +## Planar reflections + +A probe is a cube seen from one point, which is right for a ball and wrong for a floor: the reflection in a floor depends on where you stand. A `PlanarReflectorNode` draws the world again through the view's own camera mirrored in a plane, so a mirror, a polished hall or a still pool shows what is actually above it from where the eye actually is. + +```dart +final mirror = MeshNode(DeviceMesh.upload(device, const PlaneShape(width: 3, depth: 2).build()), floorMaterial); +scene + ..add(mirror) + ..add(PlanarReflectorNode(surfaces: [mirror], reflectance: 1.0)); // 0.02 for water +renderer.render(/* … */, settings: const RenderSettings( + planarReflections: PlanarReflectionSettings(enabled: true), +)); +``` + +{{golden planar-mirror | A black mirror set into a lit floor, with a red box, a blue ball and a yellow post standing on and around it. The reflection is the scene drawn through the camera mirrored in the floor, lit and shadowed by the same sun, at half the view's resolution and tinted slightly blue.}} + +The plane goes through the node's origin, with its local +Y as the side it is seen from; a camera behind it sees no reflection. The surfaces keep their own materials, and the reflection is laid over each of them straight after the opaque half of the scene pass, on exactly the pixels the surface won, weighted by Schlick's Fresnel from `reflectance`. With one the surface is a mirror at every angle; with water's 0.02 it reflects little looking down and nearly everything along the horizon. The picture is read by the pixel's place on screen, which is why it is drawn per view and at the view's own projection, at `resolution` of its size (half by default). + +Nothing below the plane may come up through it, and no stage here has a clip distance to write. So the mirrored camera's near plane is moved onto the mirror itself, the oblique frustum of Lengyel's 2005 paper: the rasteriser's own near clip does the cut, and what it costs is the far plane, which tilts. `clipOffset` lowers the cut a centimetre so a wall standing on the floor keeps its foot in the reflection. The software backend used to clip only at the eye and had to learn to drop fragments outside the depth range, as every GPU does, before it drew the same picture. + +It is off by default and costs a second drawing of the scene per view while it is on. Neither particles nor anything else a contributor draws is in the reflection, nothing is reflected twice, and a surface that blends is drawn after its reflection, so on transparent water the picture lies under the water rather than on it. + +## Cameras into textures + +The same pass, pointed through an ordinary camera, is public as `RenderTexture`: a monitor, a portal, a minimap. + +```dart +final feed = RenderTexture.create(device, camera: securityCamera, width: 256, height: 192); +feed.excluded.add(monitor); // a screen must not film itself +scene.addRenderTexture(feed); +monitor.material = Material(lighting: LightingModel.unlit, albedo: feed.texture); +``` + +{{golden render-texture | The same props on the same floor, and a monitor on a stand showing what a second camera off to the left sees of them, drawn earlier in the same frame.}} + +It is drawn before the scene, so a material shows the picture in the frame it was taken. The texture holds sRGB bytes with the top of the picture in its first row on every backend, the way an uploaded image does, so it goes into an albedo or emissive slot like any picture: unlit, it shows what the camera saw; lit, it is lit again, like a printed photograph. The light is multiplied by `exposure` and clipped, and tone mapping is left to the frame that shows it. `refreshEveryFrame: false` draws it once and then only after `invalidate()`. No post chain runs on it. + ## The look Grading, vignette, grain and dispersion, applied inside the composite after the tone map. Everything defaults to doing nothing *exactly*: a vignette of zero multiplies by one and grain of zero adds zero, so a scene that asks for none of it composites to the same bytes it did before this existed. diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 7085f1540..99df283e5 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10914 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-two scenes stay checkable in a headless run. +There are 10929 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-four scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/packages.md b/site/content/reference/packages.md index 7f453d86c..4e801ee3a 100644 --- a/site/content/reference/packages.md +++ b/site/content/reference/packages.md @@ -60,7 +60,7 @@ A rasteriser written in Dart. `CpuDevice` implements the same HAL, plus PNG outp Not a fallback. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. -It is how eighty-two golden scenes are checkable with no GPU in the room, and it is a dev dependency of every game because three shipped bugs would have been caught by rendering a single frame in a test. It stopped being *only* a dev dependency once `flutter3d_app` started reaching for it as the runtime fallback when Impeller will not start, which makes it a real production path now, if a last-resort one. +It is how eighty-four golden scenes are checkable with no GPU in the room, and it is a dev dependency of every game because three shipped bugs would have been caught by rendering a single frame in a test. It stopped being *only* a dev dependency once `flutter3d_app` started reaching for it as the runtime fallback when Impeller will not start, which makes it a real production path now, if a last-resort one. ### `flutter3d_conformance` The suite any fourth backend would have to pass before it counted as one, plus the cross-backend comparison with per-scene budgets. diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index c8d97b2b2..405dbf3ee 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,18 +1,18 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10914 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10929 tests need a GPU. --- # Testing -10914 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10929 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| -| `flutter3d` | 1621 | | | | +| `flutter3d` | 1622 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | | `flutter3d_sim` | 591 | | `pad_input` | 67 | -| `flutter3d_lab` | 15 | | `flutter3d_core` | 746 | +| `flutter3d_lab` | 15 | | `flutter3d_core` | 750 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | | `flutter3d_game_shooter` | 340 | | `flutter3d_audio_core` | 55 | @@ -20,7 +20,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_racing` | 223 | | `flutter3d_webgl` | 62 | | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 63 | | `apps/flutter3d_demo_platformer` | 202 | | `flutter3d_impeller` | 64 | -| `flutter3d_cpu` | 347 | | `apps/flutter3d_demo_strategy` | 50 | +| `flutter3d_cpu` | 357 | | `apps/flutter3d_demo_strategy` | 50 | | `apps/flutter3d_editor` | 224 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10888 rather than 10914: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10903 rather than 10929: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. @@ -52,7 +52,7 @@ tool/ci.sh # shaders, analyze, every test ## Four independent golden sets, not one -Eighty-two scenes are rendered four times: through Impeller, through the software rasteriser in `flutter3d_cpu`, through WebGL2, and through WebGPU, the last two in a driven browser. Each backend is held to zero differing pixels against its own set, with a per-channel tolerance of 8. +Eighty-four scenes are rendered four times: through Impeller, through the software rasteriser in `flutter3d_cpu`, through WebGL2, and through WebGPU, the last two in a driven browser. Each backend is held to zero differing pixels against its own set, with a per-channel tolerance of 8. The browser sets are recorded when a branch lands, not beside it (`golden_web.sh` holds one fixed port for the whole of its run), so a new scene is in the other sets for as long as that takes. Which scenes, and what they are waiting for, is `_provisional` in `flutter3d_webgl/test/cross_backend_test.dart`: the comparison is skipped with the reason printed instead of quietly missing, and the check beside it fails the moment a reference lands and the name is still there. @@ -65,7 +65,7 @@ The browser sets are recorded when a branch lands, not beside it (`golden_web.sh {{golden3 shadow-teapot | One scene, three sets: a GPU through Metal, a rasteriser written in Dart, and a browser. The pictures on this site are the Impeller set.}}
    -

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps eighty-two scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    +

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps eighty-four scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    `cross_backend_test.dart` compares them with per-scene budgets, and any new backend has to pass `flutter3d_conformance` before it counts as one. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index ea7c03c34..e8c030bfc 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -229,6 +229,8 @@ instead. | P2 | Lens flare, lens distortion, `.cube` LUT import | `bloom_pass.dart` flares, `screen_distortion.dart`, `post_process/color_lut.dart` | halation, strip LUT grading (`doc/postprocessing-parity.md`, `gfx-30n` partial) | S–M | | P3 | Projected box decals painting a material | `decal.dart` (0.24) | done, off by default; see below | M | | P4 | Planar reflections; public render-to-texture camera | `components/planar_reflector_component.dart`, `render_texture.dart` | SSR, probes; no public RTT | M | +| P3 | Projected box decals painting a material | `decal.dart` (0.24) | none | M | +| P4 | Planar reflections; public render-to-texture camera | `components/planar_reflector_component.dart`, `render_texture.dart` | done, reflections off by default; see below | M | | P5 | Physical sky (atmospheric scattering), stars, height fog | `sky_sources.dart` `PhysicalSkySource` | gradient sky, cubemap, `Atmosphere` presets | M | | P6 | Debug views (material channels, UV, NaN, split wipe) and render stats | `render/debug_view.dart`, `render_stats.dart` (0.24) | viewport shading, NaN scan in `sim_mcp` | S | | P7 | Orthographic everywhere, per-draw ranges, alpha to coverage, render order | 0.24 commits | orthographic exists; check every effect | S | @@ -283,6 +285,58 @@ emission and fog are scaled with the light it borrowed, both stated in `decal.frag`; a device with two colour attachments draws none; a layer mask per decal; decals in the level format, the editor and the MCP tools; the comparison page row, with the 0.9.0 revision (decision 19). +**P4. Planar reflections and a camera into a texture.** *Done:* one pass, +`_captureView` in `renderer_planar_pass.dart`, draws the scene's meshes into +a 2D target through any matrix, with the frame's lights and shadows and the +sky behind them, culled by the frustum and a layer mask: the reflection +probe's capture, generalised. Two things point through it, as two graph +nodes before `scene` (`render textures`, `planar reflections`), each keeping +one name the scene optionally reads so they are ordered before what shows +them. +A `PlanarReflectorNode` is a plane (its origin, local +Y the front) and the +meshes lying in it. Per view that sees the front, the view's camera is +mirrored in the plane (`mirrorAcrossPlane`) and its near plane moved onto +the plane by Lengyel's oblique frustum (`obliqueNearPlane`), since no stage +has a clip distance: the rasteriser's near clip makes the cut, and the far +plane tilts. The picture, linear light at `resolution` of the view (half by +default), is laid over the surfaces inside the scene pass straight after +the opaque half, through `x-ray`'s override path with the new +`PlanarReflection` stage: depth `lessEqual`, no depth write, one output so +the surface buffer keeps describing the surface, read by the fragment's +place in its view, weighted by Schlick's Fresnel from `reflectance`, +tinted, fogged as the surface is. `RenderSettings.planarReflections` is off +by default (decision 18 and §10; the cost is a second scene per view). +`RenderTexture.create` makes an RGBA8 texture for a camera; added with +`Scene.addRenderTexture` it is drawn every frame, or once and after +`invalidate()`, then encoded by `RenderTextureEncode` into sRGB with +`exposure` and no tone curve, top row first on every backend (WebGL2 turns +its rows over), so a material's albedo or emissive slot shows it in the +frame it was taken. It is opt-in by construction: a scene without one culls +the pass. +The software rasteriser now drops fragments outside the depth range, as +GPUs clip them; it clipped at `w` only, and the oblique near plane was the +first to show it. No golden of any set moved. +CPU mirrors `PlanarReflectionShader`, `RenderTextureEncodeShader`; +`planar_reflection_test.dart` (ten claims: off by default, hidden reflector, +a mirror equals the world mirrored and seen directly, nothing below the +plane reflected, water's Fresnel by angle, a camera behind the plane, a +render texture's sRGB, shown in the same frame, refresh once and on asking, +no pass without one); `mirror_view_test.dart` (the reflection and the +oblique projection); the binding contract on `FakeBackend`; the linking +check. Goldens `planar-mirror` and `render-texture` in all four sets: +WebGPU at 0 of 172800 pixels from Impeller, WebGL at 0.17% and 0.19%, the +software set at 0.84% and 0.96%, all of it single pixels on silhouette +edges, where those two do not multisample as Impeller does. +*Left:* the reflection is one mirror deep (nothing is reflected in a +reflection) and holds no particles, splats or anything else a contributor +draws, nor decals or post effects; a blended surface draws over its +reflection rather than under it; no roughness blur or normal-map +distortion of the picture; a reflector's surfaces must lie in its plane, +which nothing checks; one picture per reflector per view, so a split screen +pays per view; a render texture has no post chain, no multisampling and no +mips, and its aspect is the texture's; reflectors and render textures in +the level format, the editor and the MCP tools; the comparison page row, +with the 0.9.0 revision (decision 19). Already ours and not theirs, to keep: volumetric fog, weighted blended OIT, motion blur, local exposure and HDR out, EVSM, impostors, Hi-Z and software diff --git a/tool/ci.sh b/tool/ci.sh index 60f510af1..e5fad087a 100755 --- a/tool/ci.sh +++ b/tool/ci.sh @@ -9,7 +9,7 @@ # # What it does NOT do, stated so the gap is not mistaken for coverage: # -# * The Impeller half of the golden set. Those eighty-two scenes render through +# * The Impeller half of the golden set. Those eighty-four scenes render through # flutter_gpu and need a real device, so they run from # packages/flutter3d/tool/golden.sh on a machine with a GPU. The software # half runs here, and cross_backend_test.dart compares the two committed From fc4db1c87b44e61dc63ea53b0ec6b30b6a8c7f65 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 04:53:34 +0300 Subject: [PATCH 033/141] The sky becomes air, with stars at night and fog that lies low A physical sky on SkySettings.physical: single Rayleigh and Mie scattering marched per pixel, the sun's disc dimmed by the air in front of it, stars once the sun is down, and a ground lit by the sun below the horizon. Off unless set. The same model runs in sky_physical.frag, in the software stage and in PhysicalSky.look, which a fog colour and the tests read. Fog gains a height falloff, so it thickens towards the ground; with none it is the flat fog to the byte. Two golden scenes, dusk and night, in all four suites. The sky's varyings join the existing sky family on WebGPU, which now has twelve names in it. The model test compares towards and away from the sun at the same height above the horizon, and the drawn frames are odd-sized so the centre pixel looks along the camera's axis. --- ARCHITECTURE.md | 8 +- README.md | 9 +- doc/plan-status.json | 3 - packages/flutter3d/README.md | 4 +- .../example/lib/src/spike/backend_web.dart | 2 +- .../example/lib/src/spike/golden_runner.dart | 2 +- .../example/lib/src/spike/golden_scenes.dart | 23 + .../example/lib/src/spike/golden_stages.dart | 113 + .../lib/src/spike/golden_store_web.dart | 4 +- packages/flutter3d/test/ambient_test.dart | 2 +- packages/flutter3d/test/anisotropy_test.dart | 4 +- packages/flutter3d/test/atmosphere_test.dart | 18 + packages/flutter3d/test/auto_batch_test.dart | 4 +- packages/flutter3d/test/fog_test.dart | 2 +- .../test/goldens/sky-physical-dusk.png | Bin 0 -> 89684 bytes .../test/goldens/sky-physical-night.png | Bin 0 -> 29073 bytes packages/flutter3d/test/grade_test.dart | 2 +- packages/flutter3d/test/halation_test.dart | 2 +- .../test/irradiance_bounce_test.dart | 2 +- .../flutter3d/test/masked_shadow_test.dart | 2 +- .../test/material_vertex_stage_test.dart | 2 +- .../test/per_view_exposure_test.dart | 2 +- .../flutter3d/test/render_scale_test.dart | 2 +- packages/flutter3d/test/sharpen_test.dart | 2 +- .../flutter3d/test/sky_physical_test.dart | 410 + .../flutter3d/test/sky_procedural_test.dart | 2 +- packages/flutter3d/test/ssao_blur_test.dart | 2 +- packages/flutter3d_conformance/CHANGELOG.md | 2 + .../lib/src/shader_link_checks.dart | 2 + packages/flutter3d_core/CHANGELOG.md | 20 + .../flutter3d_core/lib/flutter3d_core.dart | 1 + .../lib/src/engine/render/physical_sky.dart | 350 + .../src/engine/render/render_settings.dart | 60 +- .../lib/src/engine/render/renderer.dart | 74 +- .../engine/render/renderer_mesh_encode.dart | 6 +- .../src/engine/render/renderer_post_pass.dart | 4 +- .../engine/render/renderer_shadow_pass.dart | 2 +- .../src/engine/render/renderer_sky_pass.dart | 96 +- .../lib/src/engine/render/sky_settings.dart | 42 + .../src/engine/render/splat_contributor.dart | 2 +- .../lib/src/engine/scene/atmosphere.dart | 20 +- .../formats/ktx2_supercompression_test.dart | 2 +- packages/flutter3d_cpu/CHANGELOG.md | 3 + .../lib/src/cpu_shaders_builtin.dart | 4 + .../lib/src/cpu_shaders_color.dart | 13 +- .../lib/src/cpu_shaders_post.dart | 2 +- .../lib/src/cpu_shaders_sky.dart | 251 +- .../flutter3d_cpu/lib/src/cpu_texture.dart | 2 +- .../flutter3d_cpu/lib/src/portable_log2.dart | 2 +- .../test/cross_backend_test.dart | 7 + .../test/goldens/sky-physical-dusk.png | Bin 0 -> 88492 bytes .../test/goldens/sky-physical-night.png | Bin 0 -> 27427 bytes .../test/gpu_formats_test.dart | 2 +- packages/flutter3d_particles/CHANGELOG.md | 5 + .../lib/src/mesh_particle_contributor.dart | 4 +- .../lib/src/particle_contributor.dart | 4 +- packages/flutter3d_shaders/CHANGELOG.md | 9 + .../lib/flutter3d_shaders.dart | 2 + .../flutter3d_shaders/lib/stage_bindings.dart | 2 + .../shaders/flutter3d.shaderbundle.json | 8 + .../flutter3d_shaders/shaders/lib/color.glsl | 21 +- .../shaders/sky_physical.frag | 202 + .../shaders/sky_physical.vert | 63 + packages/flutter3d_webgl/CHANGELOG.md | 2 + .../flutter3d_webgl/lib/engine_shaders.dart | 588 +- .../test/cross_backend_test.dart | 4 + .../test/engine_parity_test.dart | 1 + .../test/goldens/sky-physical-dusk.png | Bin 0 -> 89705 bytes .../test/goldens/sky-physical-night.png | Bin 0 -> 29147 bytes .../test/lighting_models_test.dart | 2 +- packages/flutter3d_webgl/tool/golden_web.sh | 2 +- packages/flutter3d_webgpu/CHANGELOG.md | 2 + .../flutter3d_webgpu/lib/engine_shaders.dart | 6833 +++++++++-------- .../lib/src/glsl_to_wgsl.dart | 8 +- .../test/cross_backend_test.dart | 3 + .../test/goldens/sky-physical-dusk.png | Bin 0 -> 89667 bytes .../test/goldens/sky-physical-night.png | Bin 0 -> 29084 bytes packages/flutter3d_webgpu/test/open_test.dart | 1 + .../test/wgsl_pipeline_test.dart | 18 +- site/content/core/architecture.md | 4 +- site/content/core/backends.md | 6 +- site/content/core/index.md | 2 +- site/content/core/rendering.md | 2 +- site/content/quickstart.md | 2 +- site/content/reference/packages.md | 2 +- site/content/reference/testing.md | 12 +- tasks/0.9-engine-roadmap.md | 50 + tool/ci.sh | 2 +- 88 files changed, 6344 insertions(+), 3119 deletions(-) create mode 100644 packages/flutter3d/test/goldens/sky-physical-dusk.png create mode 100644 packages/flutter3d/test/goldens/sky-physical-night.png create mode 100644 packages/flutter3d/test/sky_physical_test.dart create mode 100644 packages/flutter3d_core/lib/src/engine/render/physical_sky.dart create mode 100644 packages/flutter3d_cpu/test/goldens/sky-physical-dusk.png create mode 100644 packages/flutter3d_cpu/test/goldens/sky-physical-night.png create mode 100644 packages/flutter3d_shaders/shaders/sky_physical.frag create mode 100644 packages/flutter3d_shaders/shaders/sky_physical.vert create mode 100644 packages/flutter3d_webgl/test/goldens/sky-physical-dusk.png create mode 100644 packages/flutter3d_webgl/test/goldens/sky-physical-night.png create mode 100644 packages/flutter3d_webgpu/test/goldens/sky-physical-dusk.png create mode 100644 packages/flutter3d_webgpu/test/goldens/sky-physical-night.png diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index aaad1327e..b1d9ce995 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10929 tests** across 43 packages and 10 applications | +| Unit tests | **10941 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | @@ -2731,7 +2731,7 @@ material parameter — is the case `doc/boundary-0.5.0.md` worked through for set belongs to whoever builds on the package rather than to the package itself. -**Golden render tests.** 84 scenes against **four complete independent +**Golden render tests.** 86 scenes against **four complete independent reference sets** — Impeller, the software rasteriser, WebGL2 and WebGPU — each held to zero differing pixels against its own set, with a per-channel tolerance of 8. @@ -3143,7 +3143,7 @@ metres. The directional light's cascades fit the view up to that distance and nothing beyond it casts — a level whose far end matters visually wants the number raised, and pays for it in texels. -**The web backend draws all eighty-four golden scenes the way Impeller does**, +**The web backend draws all eighty-six golden scenes the way Impeller does**, between 0.01% and 0.42% of pixels differing by more than 8 per channel — the silhouette's worth of disagreement two rasterisers always have. Two of those numbers fell when the minification filter learned to read a sampler's @@ -3259,7 +3259,7 @@ and charge each of them the bytes, and neither is a change to make on somebody else's behalf. A build that wants it says so in one flag, and the engine's own example takes the same answer from `?backend=webgpu` in the URL — a query parameter rather than a define, because the browser golden stand's whole saving -is one dart2js run serving eighty-four scenes and both browser backends. +is one dart2js run serving eighty-six scenes and both browser backends. **`flutter3d_shaders` is one text and no two backends take it the same way.** Impeller compiles the GLSL with `impellerc`; the WebGL2 generator translates it diff --git a/README.md b/README.md index 2e7c6d502..c105fdab4 100644 --- a/README.md +++ b/README.md @@ -158,12 +158,15 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10929 tests across forty-three packages and nine applications. The +There are 10941 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other -half is rendered by the software backend, which is what makes 84 scenes +half is rendered by the software backend, which is what makes 86 scenes There are 10895 tests across forty-two packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other -half is rendered by the software backend, which is what makes 84 scenes +half is rendered by the software backend, which is what makes 86 scenes +There are 10892 tests across forty-two packages and nine applications. The +only ones that need a GPU are the Impeller half of the golden set. The other +half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. Several steps run in a browser. `flutter test --platform chrome` covers the two diff --git a/doc/plan-status.json b/doc/plan-status.json index 5e4aba497..076b54c66 100644 --- a/doc/plan-status.json +++ b/doc/plan-status.json @@ -537,9 +537,6 @@ "anim-18": { "RetargetClip": "регистр из текста строки, а не имя символа: функция называется retargetClip (строчная r) в packages/flutter3d_model_core/lib/src/retarget_clip.dart, уже существовала до S7 и используется без изменений экраном 14 (RetargetClipJobRequest в modeler_cubit.dart/retarget_wiring.dart строит задание поверх неё же). Формулировка плана устарела в регистре первой буквы, а не в существовании функции" }, - "ui-34d": { - "postMessage": "имя браузерного API, а не символа в дереве: строка называет `TransferableTypedData`/`postMessage` как механизм воркера, который по её же собственному тексту не строился — оба измерения p0-07/p0-08 не сработали. В дереве это слово встречалось только в копиях flutter_soloud's own worker.dart.js под build/unit_test_assets, которые verify_plan пропускает; в свежем checkout (2026-09-14, ветка modeler-ui) его нет нигде, и это правильно" - }, "pro-sim-01": { "flutter3d_cloth": "the row's own history: the package existed under this name until doc/package-merge-plan.md §3.1 folded it into flutter3d_physics, and the row says so in the same sentence — the name is cited as what it used to be called, not as a claim that it still exists" }, diff --git a/packages/flutter3d/README.md b/packages/flutter3d/README.md index b95dfb025..969903d5f 100644 --- a/packages/flutter3d/README.md +++ b/packages/flutter3d/README.md @@ -104,7 +104,7 @@ What works today: `CUBICSPLINE` with authored tangents), slerped rotations, an `AnimationPlayer` with play/pause/seek/speed and once/loop/ping-pong, and the decoded node hierarchy rebuilt on instantiation so an animated parent carries its subtree; -- 1622 tests covering projection, scene, sorting, debug draw, raycasting, +- 1634 tests covering projection, scene, sorting, debug draw, raycasting, animation, skinning, lighting, render targets, BVH, LOD, glTF, OBJ and `.f3d`, plus a real frame drawn through `flutter3d_cpu`'s software rasteriser for the ones that need one, all without a GPU. The geometry the engine is written in @@ -322,7 +322,7 @@ lib/src/engine/assets/ glTF, OBJ and .f3d decoders, isolate loading, ca example/lib/ the demo, and the frame capture hook skills/ the conventions, as agent skills — see below bin/skills.dart what copies them into a project that uses this -test/ 1622 tests, all runnable without a GPU +test/ 1634 tests, all runnable without a GPU ``` The GLSL is not here. Every shader this package draws with lives in diff --git a/packages/flutter3d/example/lib/src/spike/backend_web.dart b/packages/flutter3d/example/lib/src/spike/backend_web.dart index c70739e85..4c966c993 100644 --- a/packages/flutter3d/example/lib/src/spike/backend_web.dart +++ b/packages/flutter3d/example/lib/src/spike/backend_web.dart @@ -13,7 +13,7 @@ import 'golden_store.dart'; /// /// Read from the URL, exactly the way the golden scene and the record/compare /// direction are read, and for exactly the same arithmetic: one dart2js run -/// serves eighty-four scenes because the scene is a query parameter, and one +/// serves eighty-six scenes because the scene is a query parameter, and one /// dart2js run serves the browser backends because this is one too. A /// `--dart-define` here would be a build per backend, which is the whole saving /// of the browser golden stand spent on a single word. diff --git a/packages/flutter3d/example/lib/src/spike/golden_runner.dart b/packages/flutter3d/example/lib/src/spike/golden_runner.dart index b28eced5d..8385776cb 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_runner.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_runner.dart @@ -122,7 +122,7 @@ final class GoldenRunner { scene, // The store's answer wins where it has one. A desktop run takes the // direction from the environment and a browser run from the URL, for the - // same reason on both: one build has to serve eighty-four scenes in both + // same reason on both: one build has to serve eighty-six scenes in both // directions, and anything the compiler sees is another build. update: updateOverride ?? diff --git a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart index 3b8914c8e..c0d0e5ce3 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart @@ -1198,6 +1198,29 @@ final List kGoldenScenes = [ ground: false, stage: GoldenStages.renderTexture, ), + + // P5. The physical sky at dusk over blocks standing in ground fog, and the + // same sky after dark with its stars out. + const GoldenScene( + name: 'sky-physical-dusk', + source: 'Cube', + lights: {'sun'}, + shadows: false, + bloom: false, + ground: false, + stage: GoldenStages.skyPhysicalDusk, + configure: GoldenStages.skyPhysicalDuskSettings, + ), + const GoldenScene( + name: 'sky-physical-night', + source: 'Cube', + lights: {'none'}, + shadows: false, + bloom: false, + ground: false, + stage: GoldenStages.skyPhysicalNight, + configure: GoldenStages.skyPhysicalNightSettings, + ), ]; /// Looks up a scene by name. diff --git a/packages/flutter3d/example/lib/src/spike/golden_stages.dart b/packages/flutter3d/example/lib/src/spike/golden_stages.dart index cb85ba64f..319823ea6 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_stages.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_stages.dart @@ -1868,4 +1868,117 @@ abstract final class GoldenStages { _look(stage.camera, Vector3(0.6, 2.0, 4.8), Vector3(0.6, 0.7, -0.4)), ); } + + // ------------------------------------------------------------------ P5 + + /// The sun for `sky-physical-dusk`, four degrees up and ahead of the camera. + static Vector3 get _duskSun => Vector3(0.35, 0.07, -0.93).normalized(); + + /// The sun for `sky-physical-night`, eight degrees below the horizon: the + /// glow it leaves behind on one side, and the stars out everywhere else. + static Vector3 get _nightSun => Vector3(0.6, -0.139, -0.79).normalized(); + + static const PhysicalSky _air = PhysicalSky(starBrightness: 2.0); + + /// A floor reaching to the far plane, and blocks of rising height going + /// away from the camera through ground fog — `sky_physical_test.dart`'s + /// claims, in one frame: the sky reddens towards the sun, the disc is + /// red, and the fog is thick at the foot of each block and thin at its top. + static Future skyPhysicalDusk(GoldenStage stage) async { + final device = stage.device; + final toSun = _duskSun; + stage.sun + ..setLocalForward(-toSun) + ..color.setFrom(_air.sunlight(toSun)) + ..intensity = 3.0; + final grey = Material( + baseColor: Vector4(0.55, 0.55, 0.55, 1.0), + roughness: 0.8, + ); + return GoldenStaged( + nodes: [ + _slab( + device, + Vector3(800.0, 0.1, 800.0), + Vector3(0.0, -0.05, 0.0), + Material(baseColor: Vector4(0.3, 0.32, 0.28, 1.0), roughness: 0.9), + ), + for (final (i, distance) in [6.0, 12.0, 24.0, 48.0].indexed) + _slab( + device, + Vector3(1.6, 1.5 + i * 1.5, 1.6), + Vector3(-2.5 + i * 1.8, 0.75 + i * 0.75, -distance), + grey, + ), + ], + everyFrame: (_, _) => _look( + stage.camera, + Vector3(0.0, 1.6, 4.0), + Vector3(0.6, 2.4, -20.0), + fovY: math.pi / 3, + far: 500.0, + ), + ); + } + + static RenderSettings skyPhysicalDuskSettings(RenderSettings settings) { + final sky = SkySettings( + enabled: true, + directionToSun: _duskSun, + sunAngularRadiusDegrees: 1.2, + sunSoftnessDegrees: 0.3, + sunIntensity: 2.0, + physical: _air, + ); + // The fog fades to the sky across the camera's line of sight, so the far + // blocks sink into the horizon rather than into a grey of their own. + final horizon = sky.sample(Vector3(-0.93, 0.02, -0.35)); + return settings.copyWith( + sky: sky, + // Under the default 1.6 the horizon a few degrees from a setting sun is + // white; half that keeps the band of colour in it readable. + exposure: 0.8, + fog: FogSettings(color: horizon, density: 0.035, heightFalloff: 0.5), + ); + } + + /// A floor and a block under a sky an hour after sunset, looking up past + /// the glow — `sky_physical_test.dart`'s stars. + static Future skyPhysicalNight(GoldenStage stage) async { + final device = stage.device; + stage.sun.visible = false; + stage.scene.ambientIntensity = 0.02; + return GoldenStaged( + nodes: [ + _slab( + device, + Vector3(800.0, 0.1, 800.0), + Vector3(0.0, -0.05, 0.0), + Material(baseColor: Vector4(0.2, 0.2, 0.2, 1.0)), + ), + _slab( + device, + Vector3(2.0, 3.0, 2.0), + Vector3(-3.0, 1.5, -10.0), + Material(baseColor: Vector4(0.2, 0.2, 0.2, 1.0)), + ), + ], + everyFrame: (_, _) => _look( + stage.camera, + Vector3(0.0, 1.6, 0.0), + Vector3(3.0, 5.0, -10.0), + fovY: math.pi / 3, + far: 500.0, + ), + ); + } + + static RenderSettings skyPhysicalNightSettings(RenderSettings settings) => + settings.copyWith( + sky: SkySettings( + enabled: true, + directionToSun: _nightSun, + physical: _air, + ), + ); } diff --git a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart index ea128d7fe..abb92a617 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart @@ -40,7 +40,7 @@ void reportLine(String message) { /// The scene named in the page's URL, if any. /// /// A run-time choice here, where the desktop path takes a compile-time define. -/// The reason is arithmetic: the suite is eighty-four scenes, and rebuilding +/// The reason is arithmetic: the suite is eighty-six scenes, and rebuilding /// the bundle for each is seventy-eight dart2js runs to compare seventy-eight /// pictures. One build and seventy-eight navigations is the same information /// in a fraction of the time. @@ -58,7 +58,7 @@ String? get sceneOverride { /// Whether this run records rather than compares, from the page's URL. /// /// A run-time choice for the same reason [sceneOverride] is one: the suite is -/// eighty-four scenes and rebuilding for each would be seventy-eight dart2js +/// eighty-six scenes and rebuilding for each would be seventy-eight dart2js /// runs. One build serves both directions, and the URL says which. bool get updateOverride => Uri.base.queryParameters['update'] == '1'; diff --git a/packages/flutter3d/test/ambient_test.dart b/packages/flutter3d/test/ambient_test.dart index 004f19b0b..349195e1b 100644 --- a/packages/flutter3d/test/ambient_test.dart +++ b/packages/flutter3d/test/ambient_test.dart @@ -141,7 +141,7 @@ void main() { // approximately, bit for bit. // // Mutation: default either end of the hemisphere to anything but white. - // Eighty-four goldens move at once. + // Eighty-six goldens move at once. final lit = await _pixels(_ball(), _noSky); final top = _band(lit, 0.15, 0.35); final bottom = _band(lit, 0.65, 0.85); diff --git a/packages/flutter3d/test/anisotropy_test.dart b/packages/flutter3d/test/anisotropy_test.dart index 916134f1b..f16541d01 100644 --- a/packages/flutter3d/test/anisotropy_test.dart +++ b/packages/flutter3d/test/anisotropy_test.dart @@ -24,7 +24,7 @@ /// which spends the taps and throws away what they bought — contrast drops /// *below* the trilinear baseline and the first assertion fails; and letting /// a sampler that asked for one reach the new path at all, which would move a -/// backend eighty-four golden scenes are recorded on. +/// backend eighty-six golden scenes are recorded on. /// /// **One they do not catch, said rather than hidden:** taking the taps along /// the *short* axis instead of the long one passes every line here. The @@ -213,7 +213,7 @@ void main() { }); test('asking for one tap is the old sampler exactly', () async { - // What makes this safe to add to the backend eighty-four golden scenes are + // What makes this safe to add to the backend eighty-six golden scenes are // recorded on. Not "close": the same bytes, because with `anisotropy` at // one the new path is not reached at all. expect(await _floorAt(1), await _floorAt(1)); diff --git a/packages/flutter3d/test/atmosphere_test.dart b/packages/flutter3d/test/atmosphere_test.dart index 972a90b98..b8896f1b0 100644 --- a/packages/flutter3d/test/atmosphere_test.dart +++ b/packages/flutter3d/test/atmosphere_test.dart @@ -32,6 +32,24 @@ void main() { expect(dusk.fog.density, closeTo(0.01, 1e-6)); }); + test('a height fog blends with the rest of the air and reaches the fog', () { + // `P5`. Mutation: leave the two new fields out of `lerp`, or out of the + // `fog` getter. A morning mist that is meant to lift through the day + // either never lifts or never lies on the ground at all. + final mist = Atmosphere( + sky: Vector3(0.6, 0.6, 0.6), + fogDensity: 0.05, + fogHeightFalloff: 0.2, + fogBaseHeight: 2.0, + sunColor: Vector3(1.0, 1.0, 1.0), + ); + final half = Atmosphere.lerp(_noon(), mist, 0.5); + expect(half.fogHeightFalloff, closeTo(0.1, 1e-9)); + expect(half.fogBaseHeight, closeTo(1.0, 1e-9)); + expect(half.fog.heightFalloff, closeTo(0.1, 1e-9)); + expect(half.fog.baseHeight, closeTo(1.0, 1e-9)); + }); + test('a day goes round, blending the last key back into the first', () { final day = AtmosphereCycle(<(double, Atmosphere)>[ (0.0, _noon()), diff --git a/packages/flutter3d/test/auto_batch_test.dart b/packages/flutter3d/test/auto_batch_test.dart index fbae1707a..64ae42457 100644 --- a/packages/flutter3d/test/auto_batch_test.dart +++ b/packages/flutter3d/test/auto_batch_test.dart @@ -22,7 +22,7 @@ /// three backends.** It computes in Dart doubles; Impeller, WebGL and WebGPU /// compute in 32-bit floats, where those two expressions have far less room /// before they part. Nothing here can run them, so the setting stays off by -/// default — which is also what keeps the eighty-four goldens where they are. +/// default — which is also what keeps the eighty-six goldens where they are. library; import 'dart:math' as math; @@ -191,7 +191,7 @@ void main() { }); test('nothing is batched unless it is asked for', () async { - // Which is what keeps the eighty-four goldens where they are: the default + // Which is what keeps the eighty-six goldens where they are: the default // frame is the frame it always was, to the byte. final device = _device(); final scene = _field(device, count: 100); diff --git a/packages/flutter3d/test/fog_test.dart b/packages/flutter3d/test/fog_test.dart index d1a9226e5..e4c9e1406 100644 --- a/packages/flutter3d/test/fog_test.dart +++ b/packages/flutter3d/test/fog_test.dart @@ -151,7 +151,7 @@ void main() { test('no fog is byte-identical to the fog nobody asked for', () async { // The early return at zero density is not an optimisation. The golden sets - // — eighty-four scenes, zero-pixel threshold, three backends — are all + // — eighty-six scenes, zero-pixel threshold, three backends — are all // recorded with the default `FogSettings()`, and this is the property that // lets them stay recorded. final none = FogSettings(); diff --git a/packages/flutter3d/test/goldens/sky-physical-dusk.png b/packages/flutter3d/test/goldens/sky-physical-dusk.png new file mode 100644 index 0000000000000000000000000000000000000000..ef9f113eabdc47a91d61d17cff2d018d0db1ec60 GIT binary patch literal 89684 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 z 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 literal 0 HcmV?d00001 diff --git a/packages/flutter3d/test/goldens/sky-physical-night.png b/packages/flutter3d/test/goldens/sky-physical-night.png new file mode 100644 index 0000000000000000000000000000000000000000..9c04163e2d715dad2dd8b7f2b93892d821afe07c GIT binary patch literal 29073 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 literal 0 HcmV?d00001 diff --git a/packages/flutter3d/test/grade_test.dart b/packages/flutter3d/test/grade_test.dart index 836201e54..f9ae62257 100644 --- a/packages/flutter3d/test/grade_test.dart +++ b/packages/flutter3d/test/grade_test.dart @@ -63,7 +63,7 @@ Future> _ramp(LookSettings look, {int width = 256}) async { void main() { test('every default is an exact identity', () async { - // The promise eighty-four goldens rest on. Neutral for these three is + // The promise eighty-six goldens rest on. Neutral for these three is // (0,0,0) added, (1,1,1) as an exponent and (1,1,1) multiplied, and the // composite has to take all three without moving a byte. final plain = await _ramp(const LookSettings()); diff --git a/packages/flutter3d/test/halation_test.dart b/packages/flutter3d/test/halation_test.dart index 1490acdab..fe11cec0b 100644 --- a/packages/flutter3d/test/halation_test.dart +++ b/packages/flutter3d/test/halation_test.dart @@ -87,7 +87,7 @@ void main() { test('zero is an exact identity, and is the default', () async { // The multiplier is one on every channel at zero, so the chain comes out - // byte for byte as it did — which is what eighty-four goldens need. + // byte for byte as it did — which is what eighty-six goldens need. expect(const BloomSettings().halation, 0.0); final a = await _glow(halation: 0.0); final b = await _glow(halation: 0.0); diff --git a/packages/flutter3d/test/irradiance_bounce_test.dart b/packages/flutter3d/test/irradiance_bounce_test.dart index 0afe0f01f..9ba28c138 100644 --- a/packages/flutter3d/test/irradiance_bounce_test.dart +++ b/packages/flutter3d/test/irradiance_bounce_test.dart @@ -136,7 +136,7 @@ double _tint(Uint8List rgba) { void main() { test('off, the picture is the bytes it was', () async { // The clause every recorded frame depends on. A field is null by default, - // so this is the path eighty-four goldens take. + // so this is the path eighty-six goldens take. final a = await _draw(field: false, redWall: true); final b = await _draw(field: false, redWall: true); expect(a, orderedEquals(b)); diff --git a/packages/flutter3d/test/masked_shadow_test.dart b/packages/flutter3d/test/masked_shadow_test.dart index 99374de13..bee52e65d 100644 --- a/packages/flutter3d/test/masked_shadow_test.dart +++ b/packages/flutter3d/test/masked_shadow_test.dart @@ -223,7 +223,7 @@ void main() { test('a caster with no map keeps the shadow it always cast', () async { // **The half that lets this land.** A material that is not cut out goes // through the stage it has always gone through, with no sampler in the - // pipeline and no texture bound per draw, so the eighty-four goldens + // pipeline and no texture bound per draw, so the eighty-six goldens // recorded against that stage cannot move. Asking for MASK with no map to // read is the same case: there is nothing to cut out. final plain = await _frame(masked: false, withTexture: false); diff --git a/packages/flutter3d/test/material_vertex_stage_test.dart b/packages/flutter3d/test/material_vertex_stage_test.dart index 6dd0ec67b..5b39b8bf7 100644 --- a/packages/flutter3d/test/material_vertex_stage_test.dart +++ b/packages/flutter3d/test/material_vertex_stage_test.dart @@ -172,7 +172,7 @@ void main() { // **The half that lets this land**, and the reason the seam is a null field // rather than a new required one: a material saying nothing about its vertex // stage goes through the pipeline it has always gone through, which is also - // why the eighty-four goldens cannot move. + // why the eighty-six goldens cannot move. final withSeam = await _draw(lighting: _plain); final device = CpuDevice( diff --git a/packages/flutter3d/test/per_view_exposure_test.dart b/packages/flutter3d/test/per_view_exposure_test.dart index 29a3e2db6..f7fd07c01 100644 --- a/packages/flutter3d/test/per_view_exposure_test.dart +++ b/packages/flutter3d/test/per_view_exposure_test.dart @@ -157,7 +157,7 @@ void main() { // **The strongest form of "this costs nothing where it is not wanted".** // One view is the whole frame, so its rectangle is the whole histogram // and its exposure is the frame's. The composite takes its single-draw - // path, and eighty-four goldens stay where they are. + // path, and eighty-six goldens stay where they are. final off = await frame( const RenderSettings(autoExposure: AutoExposureSettings(enabled: true)), ); diff --git a/packages/flutter3d/test/render_scale_test.dart b/packages/flutter3d/test/render_scale_test.dart index 7147698b5..5fa871466 100644 --- a/packages/flutter3d/test/render_scale_test.dart +++ b/packages/flutter3d/test/render_scale_test.dart @@ -36,7 +36,7 @@ import 'package:vector_math/vector_math.dart'; void main() { test('one is the whole resolution, and is the default', () { - // Eighty-four goldens are recorded at the size they asked for. A default + // Eighty-six goldens are recorded at the size they asked for. A default // that shrank anything would move every one of them. expect(const RenderSettings().renderScale, 1.0); expect(_drawn(1.0), (width: 64, height: 64)); diff --git a/packages/flutter3d/test/sharpen_test.dart b/packages/flutter3d/test/sharpen_test.dart index 88ca2f974..672474a9a 100644 --- a/packages/flutter3d/test/sharpen_test.dart +++ b/packages/flutter3d/test/sharpen_test.dart @@ -95,7 +95,7 @@ int _steepest(List row) { void main() { test('zero is an exact identity, and is the default', () async { - // Eighty-four goldens go through this pass. The shader returns the centre + // Eighty-six goldens go through this pass. The shader returns the centre // untouched at zero rather than running a kernel that rounds to nothing, // because "rounds to nothing" is a claim about the target's bit depth. expect(const AntiAliasSettings().sharpen, 0.0); diff --git a/packages/flutter3d/test/sky_physical_test.dart b/packages/flutter3d/test/sky_physical_test.dart new file mode 100644 index 000000000..e92e66568 --- /dev/null +++ b/packages/flutter3d/test/sky_physical_test.dart @@ -0,0 +1,410 @@ +/// The physical sky and the height fog — `P5`. +/// +/// flutter test test/sky_physical_test.dart +/// +/// The sky exists three times: in `sky_physical.frag`, in `PhysicalSky.look` +/// for the things that cannot ask a GPU, and in the software rasteriser's +/// `SkyPhysicalShader`. The frames here are drawn by the third and checked +/// against the second, which is what catches a varying read from the wrong +/// lane: the picture stays a plausible sky and only the comparison moves. +/// +/// The rest are claims about the air that hold whatever the coefficients are +/// tuned to: a sunset is red towards the sun, the stars come out at night and +/// not at noon, and fog that lies on the ground is thinner looking up. +library; + +import 'dart:io'; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:flutter3d/flutter3d.dart' hide Material; +import 'package:flutter3d/flutter3d.dart' as engine show Material; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:flutter3d_cpu/testing.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +// Odd, so that the centre pixel's centre is the camera's axis. With an even +// size it sits half a pixel off, which near the horizon at dusk is enough +// height for the blue to move by more than the comparison allows. +const int _width = 97; +const int _height = 73; + +/// A unit vector [degrees] above the horizon, towards +x. +Vector3 _sunAt(double degrees) { + final radians = degrees * math.pi / 180.0; + return Vector3(math.cos(radians), math.sin(radians), 0.0); +} + +SkySettings _sky({ + bool enabled = true, + double sunDegrees = 30.0, + double sunIntensity = 0.0, + PhysicalSky air = const PhysicalSky(), +}) => SkySettings( + enabled: enabled, + directionToSun: _sunAt(sunDegrees), + sunAngularRadiusDegrees: 2.0, + sunSoftnessDegrees: 0.5, + sunIntensity: sunIntensity, + physical: air, +); + +({CpuDevice device, Renderer renderer}) _engine() { + final it = cpuTestDevice(width: _width, height: _height); + return ( + device: it.device, + renderer: Renderer.create( + device: it.device, + fallbackAlbedo: it.albedo, + fallbackNormal: it.normal, + ), + ); +} + +CameraNode _camera(Vector3 looking) { + final camera = CameraNode( + projection: const PerspectiveProjection( + fovYRadians: 1.0, + near: 0.3, + far: 500.0, + ), + )..lookAt(looking); + return camera; +} + +Future _draw( + ({CpuDevice device, Renderer renderer}) it, + CameraNode camera, { + SkySettings sky = const SkySettings(), +}) async { + final scene = Scene()..add(camera); + final result = it.renderer.render( + width: _width, + height: _height, + scene: scene, + views: [ + RenderView(camera: camera, clearColor: Vector4(0.0, 0.0, 0.0, 1.0)), + ], + settings: RenderSettings( + sky: sky, + bloom: const BloomSettings(enabled: false), + tonemap: false, + ), + ); + final pixels = await it.device.readPixels(result.frame); + expect(pixels, isNotNull, reason: 'the frame could not be read back'); + return pixels!.buffer.asUint8List(); +} + +/// The pixel at the centre of [frame], which is where the camera points. +Vector3 _centre(Uint8List frame) { + final at = ((_height ~/ 2) * _width + _width ~/ 2) * 4; + return Vector3( + frame[at] / 255.0, + frame[at + 1] / 255.0, + frame[at + 2] / 255.0, + ); +} + +/// The composite pass's encode, which is where linear light becomes bytes. +double _linearToSrgb(double value) { + final clamped = value.clamp(0.0, 1.0); + return clamped <= 0.0031308 + ? clamped * 12.92 + : 1.055 * math.pow(clamped, 1.0 / 2.4).toDouble() - 0.055; +} + +/// [SkySettings.sample] in [direction], put through what the frame puts the +/// shader's answer through: the default exposure, then the encode. +Vector3 _expected(SkySettings sky, Vector3 direction) { + final linear = sky.sample(direction)..scale(1.6); + return Vector3( + _linearToSrgb(linear.x), + _linearToSrgb(linear.y), + _linearToSrgb(linear.z), + ); +} + +/// Pixels brighter than their four neighbours by [margin] in green: points +/// of light, which a smooth sky has none of. +int _points(Uint8List frame, {int margin = 30}) { + int at(int x, int y) => frame[(y * _width + x) * 4 + 1]; + var count = 0; + for (var y = 1; y < _height - 1; y++) { + for (var x = 1; x < _width - 1; x++) { + final here = at(x, y); + final around = math.max( + math.max(at(x - 1, y), at(x + 1, y)), + math.max(at(x, y - 1), at(x, y + 1)), + ); + if (here > around + margin) count++; + } + } + return count; +} + +void main() { + group('the sky', () { + test('switched off, it draws nothing, air or no air', () async { + // Mutation: test `physical != null` in the sky pass before `enabled`. + // Every golden scene that sets no sky would stay as it is, and a game + // that turned its sky off at night would keep drawing it. + final camera = _camera(Vector3(0.0, 0.2, 1.0)); + final off = await _draw(_engine(), camera); + final unset = await _draw(_engine(), camera, sky: _sky(enabled: false)); + expect(unset, off); + }); + + for (final (name, sunDegrees, looking, intensity) + in <(String, double, Vector3, double)>[ + ('noon, away from the sun', 50.0, Vector3(-1.0, 0.15, 0.3), 0.0), + ('noon, straight up', 50.0, Vector3(0.001, 1.0, 0.0), 0.0), + ('dusk, into the setting sun', 3.0, _sunAt(3.0), 4.0), + ('the ground, below the horizon', 40.0, Vector3(0.0, -0.4, 1.0), 0.0), + ]) { + test('the software stage draws what the model says: $name', () async { + // Mutation: read any lane of the vertices one place over in + // `SkyPhysicalShader` — the Mie scale height as the extinction, say. + // The sky stays a sky, a little hazier or a little bluer, and only + // this comparison sees it. + final sky = _sky(sunDegrees: sunDegrees, sunIntensity: intensity); + final frame = await _draw(_engine(), _camera(looking), sky: sky); + final drawn = _centre(frame); + final expected = _expected(sky, looking); + for (final channel in [0, 1, 2]) { + expect( + (drawn[channel] - expected[channel]).abs(), + lessThan(0.02), + reason: 'channel $channel: drawn $drawn, the model $expected', + ); + } + }); + } + + test('noon is blue overhead and dusk is red towards the sun', () { + // Mutation: give Rayleigh the same coefficient in every channel, or + // drop the extinction of the light on its way in. Either way the sky + // keeps its brightness and loses the colours that make it a time of day. + const air = PhysicalSky(); + final zenith = air.radiance(Vector3(0.0, 1.0, 0.0), _sunAt(50.0)); + expect(zenith.z, greaterThan(zenith.x * 2.5), reason: '$zenith'); + + final sunset = air.radiance(_sunAt(5.0), _sunAt(2.0)); + expect(sunset.x, greaterThan(sunset.z * 3.0), reason: '$sunset'); + + // Towards and away at the same height above the horizon: lower down + // the path through the air is longer and the sky brighter, so a pair + // at different heights measures the height as much as the sun. The + // model gives about a quarter; haze that scattered every way alike + // would give three quarters. + final towards = air.radiance(Vector3(1.0, 0.05, 0.0), _sunAt(2.0)); + final away = air.radiance(Vector3(-1.0, 0.05, 0.0), _sunAt(2.0)); + expect( + away.x + away.y + away.z, + lessThan((towards.x + towards.y + towards.z) / 3.0), + reason: 'the sky away from a setting sun is the dark half', + ); + }); + + test('the sun goes red as it sets, and out below the horizon', () { + // Mutation: draw the disc without the air in front of it, which is how + // the gradient sky draws its own. A noon sun at the horizon. + const air = PhysicalSky(); + final high = air.sunlight(_sunAt(60.0)); + final low = air.sunlight(_sunAt(2.0)); + expect(high.x / high.z, lessThan(1.3), reason: 'white at noon: $high'); + expect(low.x / low.z, greaterThan(4.0), reason: 'red at dusk: $low'); + expect(air.sunlight(_sunAt(-3.0)).length, 0.0); + + final sky = _sky(sunDegrees: 2.0, sunIntensity: 10.0); + final disc = sky.sample(_sunAt(2.0)); + final beside = sky.sample(_sunAt(8.0)); + expect( + disc.x - beside.x, + greaterThan((disc.z - beside.z) * 4.0), + reason: 'the disc adds red, not white: $disc against $beside', + ); + }); + + test('the ground is lit while the sun is up and dark once it is down', () { + // Mutation: drop the ground term. The lower half of an environment map + // built from the sky goes black at noon, and every floor that reflects + // it reflects a hole. + const air = PhysicalSky(); + final down = Vector3(0.3, -1.0, 0.0); + final day = air.look(down, _sunAt(40.0)); + final night = air.look(down, _sunAt(-15.0)); + expect(day.ground, isTrue); + expect(day.radiance.y, greaterThan(0.05), reason: '${day.radiance}'); + expect(night.radiance.y, lessThan(1e-4), reason: '${night.radiance}'); + }); + + test('stars come out at night, and not by day', () async { + // Mutation: drop the night fade, or test the sun's height the wrong + // way round. Stars at noon are points brighter than a smooth sky, and + // a night with no stars is a black frame. + final up = _camera(Vector3(0.2, 1.0, 0.4)); + final night = await _draw(_engine(), up, sky: _sky(sunDegrees: -20.0)); + final day = await _draw(_engine(), up, sky: _sky(sunDegrees: 40.0)); + final none = await _draw( + _engine(), + up, + sky: _sky( + sunDegrees: -20.0, + air: const PhysicalSky(starBrightness: 0.0), + ), + ); + expect(_points(night), greaterThan(5)); + expect(_points(day), 0); + expect(_points(none), 0); + }); + + test('the sample a probe or a fog colour takes has no stars in it', () { + // Mutation: add the stars to `SkySettings.sample`. An environment map + // thirty-two texels a side turns one star into a bright texel, and a + // night floor reflects it as a lamp. + final sky = _sky(sunDegrees: -20.0); + final starless = _sky( + sunDegrees: -20.0, + air: const PhysicalSky(starBrightness: 0.0), + ); + for (var i = 0; i < 64; i++) { + final direction = Vector3( + math.cos(i * 0.37), + 0.3 + (i % 7) * 0.1, + math.sin(i * 0.37), + ); + expect(sky.sample(direction), starless.sample(direction)); + } + }); + + test('the depth the sky sits at matches sky.vert', () { + // Read out of the shader source: the physical stage is the gradient's + // depth by a second copy of a literal, and a second copy is a second + // chance for the sky to sit in front of the world. + String depthIn(String file) => + RegExp(r'gl_Position = vec4\(position, ([0-9.]+), 1\.0\);') + .firstMatch( + File('../flutter3d_shaders/shaders/$file').readAsStringSync(), + )! + .group(1)!; + expect(depthIn('sky_physical.vert'), depthIn('sky.vert')); + }); + }); + + group('height fog', () { + test('thins upwards from its base height', () { + // Mutation: fold the base height in with the wrong sign. The fog lies + // on the ceiling. + const fog = FogSettings( + density: 0.1, + heightFalloff: 0.5, + baseHeight: 3.0, + ); + expect(fog.densityAt(3.0), closeTo(0.1, 1e-12)); + expect(fog.densityAt(5.0), closeTo(0.1 * math.exp(-1.0), 1e-12)); + expect(fog.densityAt(1.0), closeTo(0.1 * math.exp(1.0), 1e-12)); + expect(const FogSettings(density: 0.1).densityAt(1000.0), 0.1); + expect( + const FogSettings(density: 0.1, heightFalloff: -1.0).densityAt(9.0), + 0.1, + reason: 'a negative falloff is held at nought', + ); + }); + + test('a ray through it is fogged by the air it crosses', () async { + // The closed form in `ApplyFog` against the integral done the long + // way: a wall seen up a slope through a height fog has to come out as + // a wall seen through a flat fog of the mean density along that ray. + // + // Mutation: take `k` from the eye down to the surface instead of up to + // it, or drop the integral and fog by the density at the eye. The + // first fogs a hill as thick as a valley; the second is a flat fog. + const distance = 60.0; + final slope = Vector3(0.0, 0.25, 1.0).normalized(); + const height = FogSettings(density: 0.03, heightFalloff: 0.12); + + // The mean density over the ray, by the midpoint rule. + const steps = 4000; + var sum = 0.0; + for (var i = 0; i < steps; i++) { + sum += height.densityAt(slope.y * distance * (i + 0.5) / steps); + } + final flat = FogSettings(density: sum / steps); + + final up = await _wallMean(height, slope, distance); + final even = await _wallMean(flat, slope, distance); + final eye = await _wallMean( + const FogSettings(density: 0.03), + slope, + distance, + ); + expect((up - even).abs(), lessThan(1.5), reason: 'up $up, even $even'); + expect(up, greaterThan(eye + 20.0), reason: 'thinner than at the eye'); + }); + + test('with no falloff it is the flat fog, to the byte', () async { + // The property every golden with fog in it rests on. + final slope = Vector3(0.0, 0.25, 1.0).normalized(); + final a = await _wallMean(const FogSettings(density: 0.03), slope, 60.0); + final b = await _wallMean( + const FogSettings(density: 0.03, baseHeight: 40.0), + slope, + 60.0, + ); + expect(a, b); + }); + }); +} + +/// The mean of the red channel over a grey wall facing the camera, +/// [distance] metres along [looking], through [fog] of black. +Future _wallMean( + FogSettings fog, + Vector3 looking, + double distance, +) async { + final it = _engine(); + final scene = Scene(); + final camera = _camera(looking); + scene + ..add(camera) + ..add( + MeshNode( + DeviceMesh.upload( + it.device, + CuboidShape(size: Vector3(2.0, 2.0, 0.1)).build(), + ), + engine.Material( + name: 'wall', + baseColor: Vector4(0.5, 0.5, 0.5, 1.0), + lighting: LightingModel.unlit, + ), + name: 'wall', + )..setPosition( + looking.x * distance, + looking.y * distance, + looking.z * distance, + ), + ); + final result = it.renderer.render( + width: _width, + height: _height, + scene: scene, + views: [ + RenderView(camera: camera, clearColor: Vector4(0.0, 0.0, 0.0, 1.0)), + ], + settings: RenderSettings( + fog: fog.copyWith(color: Vector3.zero()), + bloom: const BloomSettings(enabled: false), + tonemap: false, + ), + ); + final pixels = (await it.device.readPixels( + result.frame, + ))!.buffer.asUint8List(); + // The centre pixel only: the wall is small at sixty metres, and the + // centre is where the ray is the one the integral was taken along. + return pixels[((_height ~/ 2) * _width + _width ~/ 2) * 4].toDouble(); +} diff --git a/packages/flutter3d/test/sky_procedural_test.dart b/packages/flutter3d/test/sky_procedural_test.dart index bbf8144f1..3c086e0fb 100644 --- a/packages/flutter3d/test/sky_procedural_test.dart +++ b/packages/flutter3d/test/sky_procedural_test.dart @@ -167,7 +167,7 @@ double _mean(Uint8List rgba, {required int channel}) { void main() { _hardEdgedSun(); test('no sky is byte-identical to the sky nobody asked for', () async { - // The property eighty-four goldens rest on: a frame with no sky in it is + // The property eighty-six goldens rest on: a frame with no sky in it is // the frame this renderer has always drawn. // // Mutation: drop the early return. The sky is drawn into every scene that diff --git a/packages/flutter3d/test/ssao_blur_test.dart b/packages/flutter3d/test/ssao_blur_test.dart index 305fe5d37..6d8ae33c4 100644 --- a/packages/flutter3d/test/ssao_blur_test.dart +++ b/packages/flutter3d/test/ssao_blur_test.dart @@ -75,7 +75,7 @@ void main() { test('no taps is no pass, and is the default', () async { // The occlusion is already off by default, and this is a second // full-screen pass over it — so nothing pays for either until two things - // are switched on. Eighty-four goldens depend on the frame being what it + // are switched on. Eighty-six goldens depend on the frame being what it // was. expect(const AmbientOcclusionSettings().blurTaps, 0); expect(await _frame(blurTaps: 0), await _frame(blurTaps: 0)); diff --git a/packages/flutter3d_conformance/CHANGELOG.md b/packages/flutter3d_conformance/CHANGELOG.md index b0c269904..8712b60e0 100644 --- a/packages/flutter3d_conformance/CHANGELOG.md +++ b/packages/flutter3d_conformance/CHANGELOG.md @@ -7,6 +7,8 @@ the first through the three mesh vertex stages it is drawn over a reflector's surfaces with, the second through the full-screen one, so a backend whose bundle lacks either fails the check rather than the first frame with a reflector or a render texture in it. +**The linking check pairs `SkyPhysicalVertex` with `SkyPhysical`**, whose +seven varyings are new on both sides. ## 0.8.1 diff --git a/packages/flutter3d_conformance/lib/src/shader_link_checks.dart b/packages/flutter3d_conformance/lib/src/shader_link_checks.dart index 8f99e65ef..2c2686e12 100644 --- a/packages/flutter3d_conformance/lib/src/shader_link_checks.dart +++ b/packages/flutter3d_conformance/lib/src/shader_link_checks.dart @@ -209,6 +209,8 @@ Future checkLinking(GraphicsDevice device) async { // and nothing ran them. ('SkyVertex', 'Sky'), ('SkyCubeVertex', 'SkyCube'), + // `P5`: the air's pair, whose seven varyings are new on both sides. + ('SkyPhysicalVertex', 'SkyPhysical'), ]; for (final (vertexName, fragmentName) in pairs) { diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index b6b7cf763..eb89ca26b 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -56,6 +56,26 @@ - `mirrorAcrossPlane`, `obliqueNearPlane` and `planeInEyeSpace` are public in `mirror_view.dart`, and two passes join `RenderSettings.passOrder` before `scene`: `render textures` and `planar reflections`. +- **A physical sky, with stars, and fog that lies on the ground.** + `SkySettings.physical` takes a `PhysicalSky`: the air's molecular and haze + scattering, their scale heights, the planet, the sunlight entering it, the + ground's albedo and the stars. With it set the sky is sunlight scattered + once along each view ray, so its colours come from where `directionToSun` + puts the sun — blue overhead at noon, red towards a setting sun, dark away + from it — and the disc is drawn white through the air in front of it, so it + reddens by itself. Stars come out as the sun goes down. `SkySettings.sample` + answers from the same model without the stars, so `EnvironmentMap.fromSky` + and the renderer's hemispheric ambient follow the sun too, and + `PhysicalSky.sunlight` is the colour a directional light standing for the + sun should have. A cube map still wins. Null, the default, draws the + gradient as before. +- **Height fog.** `FogSettings.heightFalloff` and `baseHeight` thin the fog + upwards by the law `VolumetricFogSettings` marches, integrated along the ray + in closed form. A falloff of nought, the default, is the flat fog to the + bit. `FogSettings.densityAt` and `copyWith` are new; `Atmosphere` carries + `fogHeightFalloff` and `fogBaseHeight` and blends them with the rest. + Particles and splats fog as a flat fog as thick as the air at the camera. + - **A reload reaches the contributors too.** `Renderer.relinkShaders` asks every `PassContributor` to drop what it linked, through the new `PassContributor.relinkShaders`, which does nothing by default. diff --git a/packages/flutter3d_core/lib/flutter3d_core.dart b/packages/flutter3d_core/lib/flutter3d_core.dart index 87cfe94dd..0f814170d 100644 --- a/packages/flutter3d_core/lib/flutter3d_core.dart +++ b/packages/flutter3d_core/lib/flutter3d_core.dart @@ -95,6 +95,7 @@ export 'src/engine/render/mesh_overlay.dart'; export 'src/engine/render/mirror_view.dart'; export 'src/engine/render/pass_contribution.dart'; export 'src/engine/render/pass_contributor.dart'; +export 'src/engine/render/physical_sky.dart' show PhysicalSky; export 'src/engine/render/probe_faces.dart'; export 'src/engine/render/procedural_texture.dart'; export 'src/engine/render/quality_table.dart'; diff --git a/packages/flutter3d_core/lib/src/engine/render/physical_sky.dart b/packages/flutter3d_core/lib/src/engine/render/physical_sky.dart new file mode 100644 index 000000000..87f8503fe --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/render/physical_sky.dart @@ -0,0 +1,350 @@ +/// The sky as air rather than as colours somebody chose — `P5`. +/// +/// `SkySettings` draws a gradient from three colours, which is right for a +/// level with a look and wrong for a day: dusk is a different three colours +/// that somebody has to pick, and the sun's disc stays the colour it was given +/// while the light it is meant to be goes orange. A [PhysicalSky] is the air +/// itself — how much it scatters, how thick it is and how fast it thins — and +/// the colours come out of where the sun is: blue overhead at noon, a white +/// band at the horizon, red at sunset towards the sun and dark away from it, +/// and stars once the sun is far enough down. +/// +/// Set on `SkySettings.physical`, beside the gradient and the cube map rather +/// than instead of them, and off unless it is set. +library; + +import 'dart:math' as math; + +import 'package:vector_math/vector_math.dart'; + +/// The air of a planet, for `SkySettings.physical`. +/// +/// **Single scattering by molecules and by haze, marched per pixel.** Light +/// from the sun is scattered once on its way to the eye: by molecules, after +/// Rayleigh, in proportion to the inverse fourth power of the wavelength, which +/// is why the sky is blue and the sun red through a long path; and by haze, +/// after Mie, white and mostly forwards, which is the glow round the sun. +/// Light scattered twice is not modelled, and that shows at the horizon, which +/// is a pale cyan at noon rather than white. +/// +/// The defaults are the Earth's, from Bruneton's 2017 reference model: the +/// molecular coefficients for 680, 550 and 440 nm, a scale height of eight +/// kilometres for molecules and 1.2 for haze, and 60 km of air. Change them for +/// a thicker or hazier sky, or another planet. +/// +/// **Lengths are in metres**, as everything else in the engine is; the +/// renderer hands the shader kilometres, for the precision its docstring gives. +/// The world the camera stands in is flat and small beside the planet, so the +/// camera's own position does not move the sky: the eye is always [altitude] +/// metres above the ground, wherever the level puts it. +/// +/// **What the sky costs.** Sixteen samples along every view ray and eight +/// towards the sun from each, per pixel the sky covers — the sky is drawn +/// after the opaque geometry and only where nothing stands. On a desktop GPU +/// that is nothing to speak of; on a phone a frame that is mostly sky pays for +/// it. A precomputed table would make it a texture read, and is not done yet. +final class PhysicalSky { + const PhysicalSky({ + this.rayleigh, + this.rayleighScaleHeight = 8000.0, + this.mie = 3.996e-6, + this.mieAbsorption = 0.444e-6, + this.mieScaleHeight = 1200.0, + this.mieAnisotropy = 0.8, + this.planetRadius = 6360000.0, + this.atmosphereHeight = 60000.0, + this.altitude = 200.0, + this.sunIlluminance = 20.0, + this.groundAlbedo = 0.3, + this.starBrightness = 1.0, + this.starDensity = 0.02, + this.starCells = 160, + }); + + /// Molecular scattering at the ground, per metre, for red, green and blue. + /// Null takes [resolvedRayleigh], the Earth's. + final Vector3? rayleigh; + + Vector3 get resolvedRayleigh => rayleigh ?? _earthRayleigh; + + // A getter rather than a `static final`: a `Vector3` is mutable, and a shared + // one is a global that looks like a constant — see `SkySettings`. + static Vector3 get _earthRayleigh => Vector3(5.802e-6, 13.558e-6, 33.1e-6); + + /// The height over which the molecules thin by a factor of e, in metres. + final double rayleighScaleHeight; + + /// Haze: how much it scatters at the ground and how much more it absorbs, + /// per metre, the same in every channel. Raise [mie] for a hazy summer + /// afternoon; its glow round the sun widens and the horizon whitens. + final double mie; + final double mieAbsorption; + + /// The height over which the haze thins by a factor of e, in metres. + final double mieScaleHeight; + + /// Which way haze scatters: Henyey–Greenstein's g, held inside ±0.99. 0.8 is + /// strongly forwards, the bright glow a sun has through haze. + final double mieAnisotropy; + + final double planetRadius; + + /// How far above the ground the air ends, in metres. + final double atmosphereHeight; + + /// How high the eye is above the ground, in metres; held inside the air. + final double altitude; + + /// The sunlight entering the air, in the units the scene's light has. Twenty + /// puts a noon zenith near 0.1, 0.2, 0.4 and the horizon near one, which + /// sits beside a directional light of intensity two or three under the + /// default exposure. + final double sunIlluminance; + + /// How much of the sunlight that reaches the ground below the horizon it + /// sends back. The ground is what fills the lower half of an environment + /// map built from the sky; it is not a terrain, and it is lit by the sun + /// alone. + final double groundAlbedo; + + /// How bright the stars are, in the sky's own units; nought for none. They + /// fade in as the sun goes from six degrees above the horizon to eleven + /// below, and are dimmed by the air in front of them, so they thin towards + /// the horizon. + final double starBrightness; + + /// The share of the grid's cells that hold a star, from nought to one. + final double starDensity; + + /// How many cells across each face of the cube the stars are scattered on. + /// 160 puts about half a degree between neighbours. + final int starCells; + + /// [mieAnisotropy], held where the phase function stays finite. + double get resolvedAnisotropy => mieAnisotropy.clamp(-0.99, 0.99); + + /// The eye's distance from the planet's centre, in metres, held inside the + /// air: an eye above it would have no air to march through towards the + /// ground. + double get eyeRadius => + planetRadius + altitude.clamp(0.0, atmosphereHeight * 0.999); + + /// The light the air sends towards the eye along [direction], with the sun + /// along [toSun]: the scattered light, plus the ground where the ray meets + /// it. No disc and no stars. + /// + /// The same arithmetic `sky_physical.frag` runs, in double precision; the + /// lengths are kilometres here too, so that the two agree to the extent + /// single precision lets them. [direction] and [toSun] need not be + /// normalised. + Vector3 radiance(Vector3 direction, Vector3 toSun) => + look(direction, toSun).radiance; + + /// [radiance], with what the air lets through along the same ray and + /// whether the ray ends at the ground — what the disc and the stars are + /// dimmed by, and what decides whether there is a disc at all. + ({Vector3 radiance, Vector3 transmittance, bool ground}) look( + Vector3 direction, + Vector3 toSun, + ) { + final d = _unit(direction, Vector3(0.0, 0.0, 1.0)); + final s = _unit(toSun, Vector3(0.0, 1.0, 0.0)); + final k = PhysicalSkyKilometres(this); + final eyeR = k.eye; + + final ground = _meet(eyeR, d.y, k.planet); + final grounded = ground > 0.0; + final span = grounded ? ground : _leave(eyeR, d.y, k.top); + + var seenR = 0.0, seenM = 0.0; + final molecules = Vector3.zero(); + final haze = Vector3.zero(); + for (var i = 0; i < _viewSteps; i++) { + final a0 = i / _viewSteps; + final a1 = (i + 1) / _viewSteps; + final t = span * 0.5 * (a0 * a0 + a1 * a1); + final stride = span * (a1 * a1 - a0 * a0); + final p = Vector3(d.x * t, eyeR + d.y * t, d.z * t); + final r = p.length; + final h = r - k.planet; + final airR = math.exp(-h / k.rayleighHeight) * stride; + final airM = math.exp(-h / k.mieHeight) * stride; + seenR += airR; + seenM += airM; + if (_meet(r, p.dot(s) / r, k.planet) > 0.0) continue; + final (sunR, sunM) = _sunwardAir(p, s, k); + final through = k.through( + seenR - 0.5 * airR + sunR, + seenM - 0.5 * airM + sunM, + ); + molecules.addScaled(through, airR); + haze.addScaled(through, airM); + } + + final mu = d.dot(s); + final g = resolvedAnisotropy; + final gg = g * g; + final rayleighPhase = 3.0 / (16.0 * math.pi) * (1.0 + mu * mu); + final miePhase = + 3.0 / + (8.0 * math.pi) * + ((1.0 - gg) * (1.0 + mu * mu)) / + ((2.0 + gg) * math.pow(1.0 + gg - 2.0 * g * mu, 1.5)); + + final colour = Vector3( + molecules.x * k.rayleigh.x * rayleighPhase + haze.x * k.mie * miePhase, + molecules.y * k.rayleigh.y * rayleighPhase + haze.y * k.mie * miePhase, + molecules.z * k.rayleigh.z * rayleighPhase + haze.z * k.mie * miePhase, + )..scale(sunIlluminance); + final seen = k.through(seenR, seenM); + + if (grounded) { + final p = Vector3(d.x * span, eyeR + d.y * span, d.z * span); + final lit = p.normalized().dot(s); + if (lit > 0.0) { + final (sunR, sunM) = _sunwardAir(p, s, k); + final sun = k.through(sunR, sunM); + final scale = groundAlbedo / math.pi * lit * sunIlluminance; + colour + ..x += seen.x * sun.x * scale + ..y += seen.y * sun.y * scale + ..z += seen.z * sun.z * scale; + } + } + + return (radiance: colour, transmittance: seen, ground: grounded); + } + + /// What the air leaves of white sunlight on its way down to the eye, from + /// a sun along [toSun]: the colour a directional light standing for the sun + /// should have, with [sunIlluminance] left out. Nought once the sun is + /// below the horizon. + Vector3 sunlight(Vector3 toSun) { + final s = _unit(toSun, Vector3(0.0, 1.0, 0.0)); + final k = PhysicalSkyKilometres(this); + final eye = Vector3(0.0, k.eye, 0.0); + if (_meet(k.eye, s.y, k.planet) > 0.0) return Vector3.zero(); + final (r, m) = _sunwardAir(eye, s, k); + return k.through(r, m); + } + + PhysicalSky copyWith({ + Vector3? rayleigh, + double? rayleighScaleHeight, + double? mie, + double? mieAbsorption, + double? mieScaleHeight, + double? mieAnisotropy, + double? planetRadius, + double? atmosphereHeight, + double? altitude, + double? sunIlluminance, + double? groundAlbedo, + double? starBrightness, + double? starDensity, + int? starCells, + }) => PhysicalSky( + rayleigh: rayleigh ?? this.rayleigh, + rayleighScaleHeight: rayleighScaleHeight ?? this.rayleighScaleHeight, + mie: mie ?? this.mie, + mieAbsorption: mieAbsorption ?? this.mieAbsorption, + mieScaleHeight: mieScaleHeight ?? this.mieScaleHeight, + mieAnisotropy: mieAnisotropy ?? this.mieAnisotropy, + planetRadius: planetRadius ?? this.planetRadius, + atmosphereHeight: atmosphereHeight ?? this.atmosphereHeight, + altitude: altitude ?? this.altitude, + sunIlluminance: sunIlluminance ?? this.sunIlluminance, + groundAlbedo: groundAlbedo ?? this.groundAlbedo, + starBrightness: starBrightness ?? this.starBrightness, + starDensity: starDensity ?? this.starDensity, + starCells: starCells ?? this.starCells, + ); + + // The march's lengths, which the shader has as constants too. + static const int _viewSteps = 16; + static const int _lightSteps = 8; + + /// `SunwardAir` from `sky_physical.frag`: the air between [p] and space + /// towards [s], as lengths at ground density, molecules then haze. + static (double, double) _sunwardAir( + Vector3 p, + Vector3 s, + PhysicalSkyKilometres k, + ) { + final r = p.length; + final span = _leave(r, p.dot(s) / r, k.top); + final stride = span / _lightSteps; + var airR = 0.0, airM = 0.0; + for (var j = 0; j < _lightSteps; j++) { + final u = (j + 0.5) * stride; + final qx = p.x + s.x * u; + final qy = p.y + s.y * u; + final qz = p.z + s.z * u; + final h = math.sqrt(qx * qx + qy * qy + qz * qz) - k.planet; + airR += math.exp(-h / k.rayleighHeight) * stride; + airM += math.exp(-h / k.mieHeight) * stride; + } + return (airR, airM); + } + + /// `Leave`: how far a ray from radius [r] at cosine [mu] runs before it + /// leaves a sphere of [radius], negative when it misses. + static double _leave(double r, double mu, double radius) { + final b = r * mu; + final d = b * b - (r - radius) * (r + radius); + return d < 0.0 ? -1.0 : -b + math.sqrt(d); + } + + /// `Meet`: how far the same ray runs before it meets the sphere from + /// outside, negative when it does not. + static double _meet(double r, double mu, double radius) { + final b = r * mu; + final d = b * b - (r - radius) * (r + radius); + return d < 0.0 ? -1.0 : -b - math.sqrt(d); + } + + static Vector3 _unit(Vector3 v, Vector3 otherwise) { + final length = v.length; + return length > 0.0 ? v / length : otherwise; + } +} + +/// A [PhysicalSky]'s numbers in the units the shader takes them in: +/// kilometres for lengths, per kilometre for coefficients. +/// +/// One conversion for both readers, so that the renderer, which writes these +/// onto the sky's vertices, and [PhysicalSky.look], which marches with them, +/// cannot convert differently. Not exported: it is the shader's view of the +/// settings, not a thing to set. +final class PhysicalSkyKilometres { + PhysicalSkyKilometres(PhysicalSky sky) + : rayleigh = sky.resolvedRayleigh * 1000.0, + rayleighHeight = sky.rayleighScaleHeight / 1000.0, + mie = sky.mie * 1000.0, + mieExtinction = (sky.mie + sky.mieAbsorption) * 1000.0, + mieHeight = sky.mieScaleHeight / 1000.0, + planet = sky.planetRadius / 1000.0, + top = (sky.planetRadius + sky.atmosphereHeight) / 1000.0, + eye = sky.eyeRadius / 1000.0; + + /// Molecular scattering per km, and its scale height in km. + final Vector3 rayleigh; + final double rayleighHeight; + + /// Haze scattering and extinction per km, and its scale height in km. + final double mie; + final double mieExtinction; + final double mieHeight; + + /// The radii of the ground, the top of the air and the eye, in km. + final double planet; + final double top; + final double eye; + + /// `Through`: what a length of air lets through, per channel. + Vector3 through(double molecules, double haze) => Vector3( + math.exp(-(rayleigh.x * molecules + mieExtinction * haze)), + math.exp(-(rayleigh.y * molecules + mieExtinction * haze)), + math.exp(-(rayleigh.z * molecules + mieExtinction * haze)), + ); +} diff --git a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart index 853623e89..e7ba2de53 100644 --- a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart +++ b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart @@ -1010,13 +1010,26 @@ final class ViewportShadingSettings { ); } -/// Distance fog. +/// Distance fog, and fog that lies on the ground. /// /// Exponential per metre, which is what the level format already stores. A /// linear fog has a visible plane where it begins, and a dungeon corridor is /// exactly where that shows. +/// +/// **Height fog — `P5`.** With [heightFalloff] above nought the air thins +/// upwards, `density · e^(−heightFalloff · (y − baseHeight))`, the same law +/// `VolumetricFogSettings` marches; here it is integrated along the ray in +/// closed form instead, so it costs an exponential more than the flat fog and +/// no pass. A valley fills, a hilltop stands out of it, and a camera looking +/// up sees less fog than one looking along the ground, which a flat fog cannot +/// say. Nought, the default, is the flat fog and the same numbers to the bit. final class FogSettings { - const FogSettings({this.color, this.density = 0.0}); + const FogSettings({ + this.color, + this.density = 0.0, + this.heightFalloff = 0.0, + this.baseHeight = 0.0, + }); /// Linear, not sRGB: it is mixed with scene light before the display /// transform, and an sRGB value here reads as a fog too bright at the near @@ -1036,6 +1049,45 @@ final class FogSettings { bool get enabled => density > 0.0; + /// How fast the fog thins with height, per metre. Nought is the same fog at + /// every height; 0.1 halves it every seven metres, a mist in a valley; 0.01 + /// every seventy, the haze over a landscape. Negative is held at nought. + final double heightFalloff; + + /// The height at which the fog is [density] thick, in world metres. + final double baseHeight; + + /// How thick the fog is at height [y], per metre. + /// + /// What the shader is handed at the eye: the fog block has one spare lane, + /// which carries the falloff, so the base height is folded into the + /// density here rather than sent. Particles and splats, which fog by + /// distance alone, take the same number and so fog as a flat fog as thick + /// as the air at the camera — exact for the flat fog, and an approximation + /// once it has a falloff. + double densityAt(double y) { + final falloff = resolvedHeightFalloff; + if (falloff == 0.0) return density; + // Held where single precision keeps it: past e^±80 the shader's float + // is nought or infinite, and either is a frame of nothing but fog. + final exponent = (-falloff * (y - baseHeight)).clamp(-80.0, 80.0); + return density * math.exp(exponent); + } + + double get resolvedHeightFalloff => heightFalloff > 0.0 ? heightFalloff : 0.0; + + FogSettings copyWith({ + vm.Vector3? color, + double? density, + double? heightFalloff, + double? baseHeight, + }) => FogSettings( + color: color ?? this.color, + density: density ?? this.density, + heightFalloff: heightFalloff ?? this.heightFalloff, + baseHeight: baseHeight ?? this.baseHeight, + ); + static final vm.Vector3 _defaultColor = vm.Vector3(0.05, 0.05, 0.05); } @@ -1306,7 +1358,7 @@ final class RenderSettings { /// eight-bit answer — `auto_batch_test.dart` holds a hundred cubes, turned and /// scaled, to byte equality. Impeller, WebGL and WebGPU compute in 32-bit /// floats, where those expressions have far less room before they part, and - /// nothing headless can run them. So the eighty-four goldens keep the frame + /// nothing headless can run them. So the eighty-six goldens keep the frame /// they have, and an application that wants the draw calls back asks. /// /// Shadows and picking are unaffected: both walk the scene themselves and @@ -2084,7 +2136,7 @@ final class DisplayTransform { /// **Everything here defaults to doing nothing, exactly.** Not nearly nothing: /// a vignette of zero multiplies by one and grain of zero adds zero, so a scene /// that asks for none of it composites to the same bytes it did before this -/// existed. Eighty-four goldens depend on that being exact, and the composite +/// existed. Eighty-six goldens depend on that being exact, and the composite /// pass already keeps the same promise for ambient occlusion. /// /// Applied in the composite rather than as passes of their own, which is the diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer.dart b/packages/flutter3d_core/lib/src/engine/render/renderer.dart index 8a320bd08..edda738f7 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer.dart @@ -46,6 +46,7 @@ import 'material.dart'; import 'mirror_view.dart'; import 'object_id_frame.dart'; import 'pass_contributor.dart'; +import 'physical_sky.dart'; import 'probe_faces.dart'; import 'procedural_texture.dart'; import 'render_list.dart'; @@ -937,6 +938,7 @@ final class Renderer implements RenderServices { _probePrefilterPipeline = null; _skyPipeline = null; _skyCubePipeline = null; + _skyPhysicalPipeline = null; _cubeShadowPipeline = null; _skinnedCubeShadowPipeline = null; _instancedCubeShadowPipeline = null; @@ -1190,6 +1192,9 @@ final class Renderer implements RenderServices { /// The textured half of the same pair, built only if a cube is ever set. PipelineHandle? _skyCubePipeline; + /// The air's, built only if `SkySettings.physical` is ever set — `P5`. + PipelineHandle? _skyPhysicalPipeline; + /// World space to the shadow camera's clip space, rebuilt each frame the /// light or the scene moves. final vm.Matrix4 _shadowMatrix = vm.Matrix4.identity(); @@ -1559,7 +1564,7 @@ final class Renderer implements RenderServices { /// `gfx-76n`'s strength, in x. Neutral is zero, which the composite reads as /// a multiplier of exactly one — the same arrangement the occlusion's - /// strength has, and for the same reason: eighty-four goldens go through this + /// strength has, and for the same reason: eighty-six goldens go through this /// block and "off" has to be a number the shader cancels, not one it nearly /// cancels. Float32List get _compositeContact => _compositeInfo.contact; @@ -2149,7 +2154,20 @@ final class Renderer implements RenderServices { var upX = 1.0, upY = 1.0, upZ = 1.0; var downX = 1.0, downY = 1.0, downZ = 1.0; - if (sky.enabled) { + final air = sky.cubemap == null ? sky.physical : null; + if (sky.enabled && air != null) { + // `P5`: the same halves, out of the air. Taken off the scattered light + // with no disc, for the reason above, and a horizon averaged round the + // compass, because towards the sun and away from it differ by a factor + // of five at dusk and a surface faces neither. + final up = _physicalAmbient(air, sky.resolvedDirectionToSun); + upX = up.up.x; + upY = up.up.y; + upZ = up.up.z; + downX = up.down.x; + downY = up.down.y; + downZ = up.down.z; + } else if (sky.enabled) { final zenith = sky.resolvedZenith; final horizon = sky.resolvedHorizon; final nadir = sky.resolvedNadir; @@ -2176,6 +2194,55 @@ final class Renderer implements RenderServices { _ambientSky[3] = settings.diffuseModel == DiffuseModel.eon ? 1.0 : 0.0; } + /// The two ends of the hemispheric ambient under a physical sky: half the + /// zenith and half the horizon, and half the horizon and half the ground, + /// as [_updateAmbient] takes them from a gradient. + /// + /// Kept from frame to frame while the air and the sun do not change: it is + /// ten marches of the sky, which is nothing once and something every frame + /// on a phone. + ({vm.Vector3 up, vm.Vector3 down}) _physicalAmbient( + PhysicalSky air, + vm.Vector3 toSun, + ) { + final kept = _physicalAmbientKept; + if (kept != null && + identical(kept.air, air) && + kept.toSun.x == toSun.x && + kept.toSun.y == toSun.y && + kept.toSun.z == toSun.z) { + return (up: kept.up, down: kept.down); + } + final zenith = air.radiance(vm.Vector3(0.0, 1.0, 0.0), toSun); + final ground = air.radiance(vm.Vector3(0.0, -1.0, 0.0), toSun); + final horizon = vm.Vector3.zero(); + const around = 8; + for (var i = 0; i < around; i++) { + final angle = 2.0 * math.pi * i / around; + // A degree above the horizon rather than on it: exactly level, the ray + // grazes the ground's sphere and which side it lands is rounding. + horizon.addScaled( + air.radiance( + vm.Vector3(math.cos(angle), 0.0175, math.sin(angle)), + toSun, + ), + 1.0 / around, + ); + } + final up = (zenith + horizon)..scale(0.5); + final down = (horizon + ground)..scale(0.5); + _physicalAmbientKept = ( + air: air, + toSun: vm.Vector3.copy(toSun), + up: up, + down: down, + ); + return (up: up, down: down); + } + + ({PhysicalSky air, vm.Vector3 toSun, vm.Vector3 up, vm.Vector3 down})? + _physicalAmbientKept; + /// Per atlas row: xyz the direction a spot aims, w the tangent of half its /// frustum — or w negative when the row belongs to a point light. /// @@ -3085,7 +3152,8 @@ final class Renderer implements RenderServices { } /// Floats per vertex: two of clip position, three of ray, then six vec4s of - /// preset — or one vec4 of tint for the cube. + /// preset — the gradient's or the air's, the same size — or one vec4 of + /// tint for the cube. static const int _kSkyVertexFloats = 2 + 3 + 6 * 4; static const int _kSkyCubeVertexFloats = 2 + 3 + 4; diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart index 685fda932..72121b0c0 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart @@ -676,8 +676,12 @@ extension _MeshEncode on Renderer { _fogData[0] = fog.resolvedColor.x; _fogData[1] = fog.resolvedColor.y; _fogData[2] = fog.resolvedColor.z; - _fogData[3] = fog.density; _fogInfo.eye.setAll(0, _cameraData); + // `P5`: the density at the eye, and the falloff in the eye's spare lane, + // which is all `ApplyFog` needs to integrate a height fog along the ray. + // A flat fog writes its density and nought, as it always did. + _fogData[3] = fog.densityAt(_cameraData[1]); + _fogInfo.eye[3] = fog.resolvedHeightFalloff; encoder.bindBlock(fragmentShader, _fogInfo); } diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart index 6a787f9cd..e345de193 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart @@ -205,7 +205,7 @@ extension _PostPasses on Renderer { _fxaaParams[3] = settings.blend.clamp(0.0, 1.0); // `gfx-29n`. Zero exactly when nobody asked: the shader returns the // centre untouched at zero rather than running a kernel that rounds to - // nothing, and eighty-four goldens depend on that being the same bytes. + // nothing, and eighty-six goldens depend on that being the same bytes. // // After a temporal resolve the robust kernel, at the resolve's own // strength — `R2`: what softens a resolved picture is the history, and @@ -1678,7 +1678,7 @@ extension _PostPasses on Renderer { // itself — then one draw per view, scissored to its own rectangle, so the // exposure in the uniform is the one that view metered. With per-view // metering off, or with a single view, this is the one full-frame draw it - // has always been and the bytes are the bytes eighty-four goldens hold. + // has always been and the bytes are the bytes eighty-six goldens hold. final perView = settings.autoExposure.enabled && settings.autoExposure.perView && diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart index cf7eecdf2..9a0224ed1 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart @@ -1063,7 +1063,7 @@ extension _ShadowPasses on Renderer { final skinned = skeleton != null; // `gfx-60n`. A cut-out caster goes through a stage with a sampler in // it; everything else keeps the stage it has always had, which is why - // the masked half costs the common path nothing and why eighty-four + // the masked half costs the common path nothing and why eighty-six // goldens recorded against the plain stage cannot move. final masked = maskedShadowShader != shadowShader && _castsMasked(node); final kind = diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_sky_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_sky_pass.dart index 5a83b6c79..6e866ec4f 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_sky_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_sky_pass.dart @@ -29,21 +29,28 @@ extension _SkyPass on Renderer { final sky = settings.sky; if (!sky.enabled) return; - // Two fragment stages behind one vertex stage: the ray is the same either - // way, and which one runs is decided by whether there is a cube to sample. + // Three fragment stages: the ray is the same in each, and which one runs + // is decided by whether there is a cube to sample, and failing that, + // whether there is air to scatter through. A cube wins, as + // `SkySettings.physical` says. final cubemap = sky.cubemap; final textured = cubemap != null; - final fragmentName = textured ? 'SkyCube' : 'Sky'; + final air = textured ? null : sky.physical; + final fragmentName = textured + ? 'SkyCube' + : (air != null ? 'SkyPhysical' : 'Sky'); - // Two vertex stages, one per fragment stage: the layout each draw carries - // is derived from the stage's own declarations, and the gradient and the - // cube want different things on their vertices. See `sky.vert`. + // A vertex stage per fragment stage: the layout each draw carries is + // derived from the stage's own declarations, and each sky wants + // different things on its vertices. See `sky.vert`. // // Through the renderer's own library rather than `device.shaders`, like // every other stage it resolves by name: a bundle handed in as // `materials:` wins any name it shares with the engine's, and a sky it // replaced — or reloaded, see `relinkShaders` — was otherwise never seen. - final vertexName = textured ? 'SkyCubeVertex' : 'SkyVertex'; + final vertexName = textured + ? 'SkyCubeVertex' + : (air != null ? 'SkyPhysicalVertex' : 'SkyVertex'); final vertex = shaders[vertexName]; final fragment = shaders[fragmentName]; if (vertex == null || fragment == null) { @@ -75,6 +82,8 @@ extension _SkyPass on Renderer { pass.bindPipeline( textured ? (_skyCubePipeline ??= device.createPipeline(vertex, fragment)) + : air != null + ? (_skyPhysicalPipeline ??= device.createPipeline(vertex, fragment)) : (_skyPipeline ??= device.createPipeline(vertex, fragment)), ); // The tracker described a pipeline this just replaced; the next mesh has to @@ -95,7 +104,12 @@ extension _SkyPass on Renderer { return; } - pass.bindVertexData(_skyVertexBytes(inverse, sky, textured: false), 3); + pass.bindVertexData( + air != null + ? _skyPhysicalVertexBytes(inverse, sky, air) + : _skyVertexBytes(inverse, sky, textured: false), + 3, + ); pass.bindIndexBuffer(_identityIndices(3), IndexType.int32, 3); pass.draw(); @@ -199,6 +213,72 @@ extension _SkyPass on Renderer { return ByteData.sublistView(data, 0, 3 * stride); } + /// The three corners of the physical sky's triangle — `P5`. + /// + /// The gradient's stride and the gradient's buffer: six vec4s after the ray + /// in both, and `sky_physical.vert` lists what each holds. Lengths go over + /// in kilometres, converted by `PhysicalSkyKilometres` so the Dart model + /// marches with the very numbers the shader does. + ByteData _skyPhysicalVertexBytes( + vm.Matrix4 inverse, + SkySettings sky, + PhysicalSky air, + ) { + final data = _skyVertexData; + const corners = [-1.0, -1.0, 3.0, -1.0, -1.0, 3.0]; + const stride = Renderer._kSkyVertexFloats; + final k = PhysicalSkyKilometres(air); + final toSun = sky.resolvedDirectionToSun.normalized(); + + for (var i = 0; i < 3; i++) { + final x = corners[i * 2]; + final y = corners[i * 2 + 1]; + var at = i * stride; + + data[at++] = x; + data[at++] = y; + + _skyCornerRay(inverse, x, y, _skyRay); + data[at++] = _skyRay.x; + data[at++] = _skyRay.y; + data[at++] = _skyRay.z; + + data[at++] = k.rayleigh.x; + data[at++] = k.rayleigh.y; + data[at++] = k.rayleigh.z; + data[at++] = k.rayleighHeight; + + data[at++] = k.mie; + data[at++] = k.mieExtinction; + data[at++] = k.mieHeight; + data[at++] = air.resolvedAnisotropy; + + data[at++] = toSun.x; + data[at++] = toSun.y; + data[at++] = toSun.z; + data[at++] = air.sunIlluminance; + + data[at++] = k.planet; + data[at++] = k.top; + data[at++] = k.eye; + data[at++] = air.groundAlbedo; + + data[at++] = air.starBrightness; + data[at++] = air.starDensity; + data[at++] = air.starCells.toDouble(); + data[at++] = 0.0; + + // The disc as the gradient sends it, inner cosine and the width of the + // edge — see `_skyVertexBytes` for why not the outer cosine. + data[at++] = sky.discInnerCosine; + data[at++] = sky.discInnerCosine - sky.discOuterCosine; + data[at++] = sky.sunIntensity; + data[at++] = 0.0; + } + + return ByteData.sublistView(data, 0, 3 * stride); + } + /// The world-space view ray at one clip-space corner. /// /// Two points on the same eye ray, subtracted. The depths are 0.5 and 1.0 diff --git a/packages/flutter3d_core/lib/src/engine/render/sky_settings.dart b/packages/flutter3d_core/lib/src/engine/render/sky_settings.dart index ccf193306..d6dc5c54e 100644 --- a/packages/flutter3d_core/lib/src/engine/render/sky_settings.dart +++ b/packages/flutter3d_core/lib/src/engine/render/sky_settings.dart @@ -34,6 +34,8 @@ import 'dart:math' as math; import 'package:flutter3d_hardware/flutter3d_hardware.dart'; import 'package:vector_math/vector_math.dart'; +import 'physical_sky.dart'; + /// What the sky looks like, and whether there is one. final class SkySettings { const SkySettings({ @@ -50,6 +52,7 @@ final class SkySettings { this.sunIntensity = 0.0, this.cubemap, this.tint, + this.physical, }); /// Whether to draw at all. False emits nothing — see the library docstring. @@ -137,6 +140,26 @@ final class SkySettings { static Vector3 get _defaultTint => Vector3(1.0, 1.0, 1.0); + /// The air to scatter the sun through instead of evaluating the gradient — + /// `P5`. Null, the default, draws the gradient. + /// + /// When this is set the colours of the sky come from the air and where the + /// sun is: [zenith], [horizon], [nadir], [sunColor], [glowExponent] and + /// [glowStrength] are not read. [directionToSun] places the sun, and the + /// disc keeps its size, softness and [sunIntensity], drawn white and dimmed + /// by the air in front of it — so it reddens towards the horizon by itself, + /// and is gone below it. Stars come out as the sun goes down; see + /// [PhysicalSky.starBrightness]. + /// + /// A [cubemap] still wins: a photographed sky is a sky somebody chose. + /// + /// [sample] answers from the same model, so an environment map built from + /// the sky, a fog colour picked off its horizon and the ambient the renderer + /// takes from it all follow the sun too. The stars are the one part [sample] + /// leaves out: an environment map thirty-two texels a side would turn each + /// into a bright texel somewhere it does not belong. + final PhysicalSky? physical; + /// The colour of the sky in [direction], on the CPU. /// /// The same arithmetic the shader runs, for the things that cannot ask the @@ -145,6 +168,8 @@ final class SkySettings { /// /// [direction] need not be normalised. Vector3 sample(Vector3 direction) { + final air = physical; + if (air != null) return _samplePhysical(air, direction); final length = direction.length; if (length <= 0.0) return Vector3.copy(resolvedHorizon); final x = direction.x / length; @@ -175,6 +200,21 @@ final class SkySettings { return colour; } + /// [sample] for a physical sky: the scattered light, and the disc where the + /// ray reaches space, white and dimmed by the air it crossed. + Vector3 _samplePhysical(PhysicalSky air, Vector3 direction) { + final seen = air.look(direction, resolvedDirectionToSun); + final colour = seen.radiance; + if (seen.ground || sunIntensity <= 0.0) return colour; + final length = direction.length; + if (length <= 0.0) return colour; + final towards = direction.dot(resolvedDirectionToSun.normalized()) / length; + final edge = + _smoothstep(discOuterCosine, discInnerCosine, towards) * sunIntensity; + if (edge > 0.0) colour.addScaled(seen.transmittance, edge); + return colour; + } + /// Cosine of the disc's angular radius, and of the radius plus its soft edge. /// /// Cosines rather than angles because that is what a dot product answers, and @@ -201,6 +241,7 @@ final class SkySettings { double? sunIntensity, TextureHandle? cubemap, Vector3? tint, + PhysicalSky? physical, }) => SkySettings( enabled: enabled ?? this.enabled, zenith: zenith ?? this.zenith, @@ -216,6 +257,7 @@ final class SkySettings { sunIntensity: sunIntensity ?? this.sunIntensity, cubemap: cubemap ?? this.cubemap, tint: tint ?? this.tint, + physical: physical ?? this.physical, ); } diff --git a/packages/flutter3d_core/lib/src/engine/render/splat_contributor.dart b/packages/flutter3d_core/lib/src/engine/render/splat_contributor.dart index 732014d80..4e399e5d3 100644 --- a/packages/flutter3d_core/lib/src/engine/render/splat_contributor.dart +++ b/packages/flutter3d_core/lib/src/engine/render/splat_contributor.dart @@ -618,7 +618,7 @@ final class SplatContributor extends PassContributor { ..[0] = colour.x ..[1] = colour.y ..[2] = colour.z - ..[3] = fog.density; + ..[3] = fog.densityAt(eye.y); _eye ..[0] = eye.x ..[1] = eye.y diff --git a/packages/flutter3d_core/lib/src/engine/scene/atmosphere.dart b/packages/flutter3d_core/lib/src/engine/scene/atmosphere.dart index 32b431ffb..ed7a8d005 100644 --- a/packages/flutter3d_core/lib/src/engine/scene/atmosphere.dart +++ b/packages/flutter3d_core/lib/src/engine/scene/atmosphere.dart @@ -22,6 +22,8 @@ final class Atmosphere { required Vector3 sky, Vector3? fogColor, this.fogDensity = 0.0, + this.fogHeightFalloff = 0.0, + this.fogBaseHeight = 0.0, required Vector3 sunColor, this.sunIntensity = 2.0, Vector3? ambientColor, @@ -38,9 +40,16 @@ final class Atmosphere { /// end of the world meets the sky without a seam. final Vector3 fogColor; - /// How thick the fog is, per metre. + /// How thick the fog is, per metre, at [fogBaseHeight]. final double fogDensity; + /// How fast the fog thins upwards, per metre, and the height at which it is + /// [fogDensity] thick — `FogSettings.heightFalloff` and `baseHeight`. Nought + /// is fog at every height alike, which every atmosphere was before `P5`; a + /// morning that burns off is a falloff blended up through the day. + final double fogHeightFalloff; + final double fogBaseHeight; + final Vector3 sunColor; final double sunIntensity; final Vector3 ambientColor; @@ -55,6 +64,8 @@ final class Atmosphere { sky: mix(a.sky, b.sky), fogColor: mix(a.fogColor, b.fogColor), fogDensity: blend(a.fogDensity, b.fogDensity), + fogHeightFalloff: blend(a.fogHeightFalloff, b.fogHeightFalloff), + fogBaseHeight: blend(a.fogBaseHeight, b.fogBaseHeight), sunColor: mix(a.sunColor, b.sunColor), sunIntensity: blend(a.sunIntensity, b.sunIntensity), ambientColor: mix(a.ambientColor, b.ambientColor), @@ -63,7 +74,12 @@ final class Atmosphere { } /// The fog this atmosphere draws with, for `RenderSettings.fog`. - FogSettings get fog => FogSettings(color: fogColor, density: fogDensity); + FogSettings get fog => FogSettings( + color: fogColor, + density: fogDensity, + heightFalloff: fogHeightFalloff, + baseHeight: fogBaseHeight, + ); /// Puts this atmosphere on [scene]'s ambient light and on [sun], and /// writes the sky into [clearColor] if given (a view's clear colour is diff --git a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart index 45cb2d271..d6b2d4f3f 100644 --- a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart +++ b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart @@ -157,7 +157,7 @@ void main() { }); test('an uncompressed file is untouched by any of this', () { - // The path eighty-four goldens and every existing asset take. A level with + // The path eighty-six goldens and every existing asset take. A level with // no supercompression is handed back as a view on the file's own bytes, // with nothing copied and nothing decoded. final file = buildKtx2( diff --git a/packages/flutter3d_cpu/CHANGELOG.md b/packages/flutter3d_cpu/CHANGELOG.md index eeb56a691..718f2f86b 100644 --- a/packages/flutter3d_cpu/CHANGELOG.md +++ b/packages/flutter3d_cpu/CHANGELOG.md @@ -35,6 +35,9 @@ mirror, and what was below the mirror came up through it here and on no other backend. A fragment with a window depth outside `[0, 1]` is dropped now, which is the same cut as clipping the triangle, because that depth is linear across the screen. No golden of this set moved. +**`SkyPhysicalShader`**, the software `SkyPhysical`, with the stars' hash +rounded to single precision as the GPU computes it, and **height fog in +`applyFog`**, as `color.glsl` has it. ## 0.8.2 diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart index c4de7acd5..1d4750659 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart @@ -262,6 +262,10 @@ Map builtinCpuShaders() { 'SkyCubeVertex': const CpuStage.vertex(SkyCubeVertexShader()), 'Sky': const CpuStage.fragment(SkyShader()), 'SkyCube': const CpuStage.fragment(SkyCubeShader()), + // `P5`. The same pass-through vertex stage as the gradient's: see + // [SkyPhysicalShader] for why one class answers to both names. + 'SkyPhysicalVertex': const CpuStage.vertex(SkyVertexShader()), + 'SkyPhysical': const CpuStage.fragment(SkyPhysicalShader()), }; for (final name in kUnimplementedCpuVertexShaders) { diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_color.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_color.dart index 763520771..6c4c88690 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_color.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_color.dart @@ -192,11 +192,22 @@ void writeSurface( /// has a visible plane where it starts. The early return at zero density is /// not an optimisation — it is what keeps a scene with no fog byte-identical, /// which is what the golden sets are recorded against. +/// +/// A height fog (`P5`) is the same fog with its density averaged over the ray +/// in closed form, from the falloff in `eye.w`; nought there skips it, as the +/// GLSL does, for the same reason the early return is there. Vector3 applyFog(Vector3 colour, Float32List v, ShaderBindings b) { final fog = b.vec4('FogInfo', 'fog', Vector4.zero()); - final density = fog.w; + var density = fog.w; if (density <= 0.0) return colour; + final falloff = b.vec4('FogInfo', 'eye', Vector4.zero()).w; + if (falloff > 0.0) { + final eyeY = b.vec4('FogInfo', 'eye', Vector4.zero()).y; + final k = falloff * (v[kVWorld + 1] - eyeY); + density *= k.abs() > 1e-3 ? (1.0 - math.exp(-k)) / k : 1.0 - 0.5 * k; + } + final t = math.exp(-density * eyeDistance(v, b)).clamp(0.0, 1.0); return Vector3( diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart index fcb409b68..588a165e0 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart @@ -163,7 +163,7 @@ final class CompositeShader implements CpuFragmentShader { // Skipped at exactly one, which is what both settings off comes to: a // multiply by one is exact, so this is a shortcut rather than a difference, - // and it keeps the frames eighty-four goldens hold untouched by arithmetic + // and it keeps the frames eighty-six goldens hold untouched by arithmetic // they never used to go through. if (shade != 1.0) colour.scale(shade); diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_sky.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_sky.dart index 6f73b0447..26cc530e7 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_sky.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_sky.dart @@ -1,5 +1,5 @@ -/// The sky, gradient and cube-mapped: `sky.vert`, `sky_cube.vert`, `sky.frag` -/// and `sky_cube.frag`. +/// The sky, gradient, cube-mapped and physical: `sky.vert`, `sky_cube.vert`, +/// `sky.frag`, `sky_cube.frag` and `sky_physical.frag`. library; import 'dart:math' as math; @@ -156,3 +156,250 @@ final class SkyCubeShader implements CpuFragmentShader { ); } } + +/// `sky_physical.frag`: sunlight scattered once by molecules and by haze along +/// the view ray, the disc and the stars through what the air leaves, and the +/// ground below the horizon — `P5`. +/// +/// Its vertex stage is [SkyVertexShader] under a second name: the GLSL has a +/// `sky_physical.vert` of its own because varyings are matched by name there, +/// but both pass the same twenty-seven floats straight through, and a copy of +/// a loop that copies would be a transcription of nothing. +/// +/// Transcribed from the GLSL line for line, beside a third copy in +/// `PhysicalSky.look`, for the reason [SkyShader] gives. Doubles here and +/// single precision there; the march is smooth enough that the two agree to +/// well under a step of eight bits. The one place that is not smooth is the +/// stars' hash, where a `fract` decides whether a cell holds a star, and there +/// every product is rounded to single precision, as the GPU computes it. +final class SkyPhysicalShader implements CpuFragmentShader { + const SkyPhysicalShader(); + + static const int _viewSteps = 16; + static const int _lightSteps = 8; + + @override + Vector4? run(Float32List v, ShaderBindings b, FragmentContext c) { + final length = math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + final scale = length > 0.0 ? 1.0 / length : 0.0; + final dx = v[0] * scale, dy = v[1] * scale, dz = v[2] * scale; + + final air = _Air(v); + final sx = v[11], sy = v[12], sz = v[13]; + final eyeR = air.eye; + + final ground = _meet(eyeR, dy, air.planet); + final grounded = ground > 0.0; + final span = grounded ? ground : _leave(eyeR, dy, air.top); + + var seenR = 0.0, seenM = 0.0; + var molR = 0.0, molG = 0.0, molB = 0.0; + var hazeR = 0.0, hazeG = 0.0, hazeB = 0.0; + for (var i = 0; i < _viewSteps; i++) { + final a0 = i / _viewSteps; + final a1 = (i + 1) / _viewSteps; + final t = span * 0.5 * (a0 * a0 + a1 * a1); + final stride = span * (a1 * a1 - a0 * a0); + final px = dx * t, py = eyeR + dy * t, pz = dz * t; + final r = math.sqrt(px * px + py * py + pz * pz); + final h = r - air.planet; + final airR = math.exp(-h / air.rayleighHeight) * stride; + final airM = math.exp(-h / air.mieHeight) * stride; + seenR += airR; + seenM += airM; + if (_meet(r, (px * sx + py * sy + pz * sz) / r, air.planet) > 0.0) { + continue; + } + final (sunR, sunM) = air.sunward(px, py, pz, sx, sy, sz); + final mr = seenR - 0.5 * airR + sunR; + final mm = seenM - 0.5 * airM + sunM; + final tr = air.through(0, mr, mm); + final tg = air.through(1, mr, mm); + final tb = air.through(2, mr, mm); + molR += airR * tr; + molG += airR * tg; + molB += airR * tb; + hazeR += airM * tr; + hazeG += airM * tg; + hazeB += airM * tb; + } + + final mu = dx * sx + dy * sy + dz * sz; + final g = v[10]; + final gg = g * g; + final rayleighPhase = 3.0 / (16.0 * math.pi) * (1.0 + mu * mu); + final miePhase = + 3.0 / + (8.0 * math.pi) * + ((1.0 - gg) * (1.0 + mu * mu)) / + ((2.0 + gg) * math.pow(1.0 + gg - 2.0 * g * mu, 1.5)); + + final sun = v[14]; + var r = sun * (molR * v[3] * rayleighPhase + hazeR * v[7] * miePhase); + var gr = sun * (molG * v[4] * rayleighPhase + hazeG * v[7] * miePhase); + var bl = sun * (molB * v[5] * rayleighPhase + hazeB * v[7] * miePhase); + final seen0 = air.through(0, seenR, seenM); + final seen1 = air.through(1, seenR, seenM); + final seen2 = air.through(2, seenR, seenM); + + if (grounded) { + final px = dx * span, py = eyeR + dy * span, pz = dz * span; + final pl = math.sqrt(px * px + py * py + pz * pz); + final lit = (px * sx + py * sy + pz * sz) / pl; + if (lit > 0.0) { + final (sunR, sunM) = air.sunward(px, py, pz, sx, sy, sz); + final k = v[18] / math.pi * lit * sun; + r += seen0 * air.through(0, sunR, sunM) * k; + gr += seen1 * air.through(1, sunR, sunM) * k; + bl += seen2 * air.through(2, sunR, sunM) * k; + } + } else { + // The disc, as `sky.frag` and [SkyShader] draw it, dimmed by the air. + final disc = + (v[24] > 0.0 + ? smoothstep(v[23] - v[24], v[23], mu) + : (mu >= v[23] ? 1.0 : 0.0)) * + v[25]; + r += seen0 * disc; + gr += seen1 * disc; + bl += seen2 * disc; + + final night = 1.0 - smoothstep(-0.2, 0.1, sy); + if (v[19] > 0.0 && night > 0.0) { + final star = v[19] * night * _starField(dx, dy, dz, v[20], v[21]); + r += seen0 * star; + gr += seen1 * star; + bl += seen2 * star; + } + } + + return Vector4(r, gr, bl, 1.0); + } + + /// `StarField`: a grid of cells on each face of a cube, a share [density] + /// of them holding a star. + static double _starField( + double dx, + double dy, + double dz, + double density, + double cells, + ) { + final ax = dx.abs(), ay = dy.abs(), az = dz.abs(); + final double face, u, w; + if (ax >= ay && ax >= az) { + face = dx > 0.0 ? 0.0 : 1.0; + u = dz / ax; + w = dy / ax; + } else if (ay >= az) { + face = dy > 0.0 ? 2.0 : 3.0; + u = dx / ay; + w = dz / ay; + } else { + face = dz > 0.0 ? 4.0 : 5.0; + u = dx / az; + w = dy / az; + } + final gu = (u * 0.5 + 0.5) * cells; + final gw = (w * 0.5 + 0.5) * cells; + final cu = gu.floorToDouble(), cw = gw.floorToDouble(); + if (_hash(cu, cw, face) >= density) return 0.0; + final centreU = _hash(cu + 17.0, cw, face) * 0.6 + 0.2; + final centreW = _hash(cu, cw + 29.0, face) * 0.6 + 0.2; + final ou = gu - cu - centreU, ow = gw - cw - centreW; + final off = math.sqrt(ou * ou + ow * ow); + final magnitude = _hash(cu, cw, face + 13.0); + return (1.0 - smoothstep(0.0, 0.35, off)) * + (0.15 + 0.85 * magnitude * magnitude * magnitude); + } + + /// `Hash`, with every step rounded to single precision as the GPU takes it: + /// a `fract` of a product in the thousands keeps three decimals, and those + /// are the ones that decide whether a cell has a star. + static double _hash(double x, double y, double z) { + final c = _f32(0.1031); + var px = _fract(_f32(x * c)); + var py = _fract(_f32(y * c)); + var pz = _fract(_f32(z * c)); + final k = _f32(31.32); + final dot = _f32( + _f32(_f32(px * _f32(pz + k)) + _f32(py * _f32(py + k))) + + _f32(pz * _f32(px + k)), + ); + px = _f32(px + dot); + py = _f32(py + dot); + pz = _f32(pz + dot); + return _fract(_f32(_f32(px + py) * pz)); + } + + static double _fract(double x) => _f32(x - x.floorToDouble()); + + static final Float32List _round = Float32List(1); + + static double _f32(double x) { + _round[0] = x; + return _round[0]; + } + + static double _leave(double r, double mu, double radius) { + final b = r * mu; + final d = b * b - (r - radius) * (r + radius); + return d < 0.0 ? -1.0 : -b + math.sqrt(d); + } + + static double _meet(double r, double mu, double radius) { + final b = r * mu; + final d = b * b - (r - radius) * (r + radius); + return d < 0.0 ? -1.0 : -b - math.sqrt(d); + } +} + +/// The air off the varyings: `v_rayleigh`, `v_mie` and `v_planet`. +final class _Air { + _Air(this.v) + : rayleighHeight = v[6], + mieExtinction = v[8], + mieHeight = v[9], + planet = v[15], + top = v[16], + eye = v[17]; + + final Float32List v; + final double rayleighHeight; + final double mieExtinction; + final double mieHeight; + final double planet; + final double top; + final double eye; + + /// `Through`, one channel at a time. + double through(int channel, double molecules, double haze) => + math.exp(-(v[3 + channel] * molecules + mieExtinction * haze)); + + /// `SunwardAir`. + (double, double) sunward( + double px, + double py, + double pz, + double sx, + double sy, + double sz, + ) { + final r = math.sqrt(px * px + py * py + pz * pz); + final span = SkyPhysicalShader._leave( + r, + (px * sx + py * sy + pz * sz) / r, + top, + ); + final stride = span / SkyPhysicalShader._lightSteps; + var airR = 0.0, airM = 0.0; + for (var j = 0; j < SkyPhysicalShader._lightSteps; j++) { + final u = (j + 0.5) * stride; + final qx = px + sx * u, qy = py + sy * u, qz = pz + sz * u; + final h = math.sqrt(qx * qx + qy * qy + qz * qz) - planet; + airR += math.exp(-h / rayleighHeight) * stride; + airM += math.exp(-h / mieHeight) * stride; + } + return (airR, airM); + } +} diff --git a/packages/flutter3d_cpu/lib/src/cpu_texture.dart b/packages/flutter3d_cpu/lib/src/cpu_texture.dart index 96e0758d7..1723733da 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_texture.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_texture.dart @@ -300,7 +300,7 @@ final class BoundTexture { // from the short one. // // **A sampler that did not ask is untouched**, which is what makes this - // safe to add to a backend eighty-four golden scenes are recorded on: with + // safe to add to a backend eighty-six golden scenes are recorded on: with // `anisotropy` at one — the default everywhere in this engine — the // arithmetic below is not reached and the bytes are the ones that were // recorded. `anisotropic-floor` is the one scene that asks. diff --git a/packages/flutter3d_cpu/lib/src/portable_log2.dart b/packages/flutter3d_cpu/lib/src/portable_log2.dart index 0380b6e24..5e22444e8 100644 --- a/packages/flutter3d_cpu/lib/src/portable_log2.dart +++ b/packages/flutter3d_cpu/lib/src/portable_log2.dart @@ -10,7 +10,7 @@ /// twenty-five five. `anisotropic-floor` is the scene built to sit exactly /// there: 2059 of its 172800 pixels differed between a reference recorded on /// macOS and the same code run on a Linux runner, and nothing else in -/// eighty-four scenes differed at all. +/// eighty-six scenes differed at all. /// /// **The same rule the simulation already keeps.** `flutter3d_sim`'s /// `Portable` exists for this, and a step in that package is forbidden from diff --git a/packages/flutter3d_cpu/test/cross_backend_test.dart b/packages/flutter3d_cpu/test/cross_backend_test.dart index 213b3057e..3b28c42dd 100644 --- a/packages/flutter3d_cpu/test/cross_backend_test.dart +++ b/packages/flutter3d_cpu/test/cross_backend_test.dart @@ -279,6 +279,13 @@ const Map _budgets = { 'rough-metals': 0.26, 'scan-chunks': 0.57, 'sheen-fabric': 0.39, + // `P5`. 0.451% measured, the boxes' silhouettes and the row where the + // floor meets the far plane: Impeller multisamples both and the software + // rasteriser does not. The sky itself agrees, march and all. + 'sky-physical-dusk': 0.5, + // 0.028% measured, edges of the block; every star is where Impeller put it, + // because the hash is rounded to single precision here as it is there. + 'sky-physical-night': 0.04, 'smoke-six-way': 0.01, 'splat-gltf': 0.02, // 0.011% measured, since the hash reads the pixel the same way up and diff --git a/packages/flutter3d_cpu/test/goldens/sky-physical-dusk.png b/packages/flutter3d_cpu/test/goldens/sky-physical-dusk.png new file mode 100644 index 0000000000000000000000000000000000000000..482de97b46da8cb1e5d24a8ef5c7fe9c8cc01431 GIT binary patch literal 88492 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 z zP;*hu`eH{aORSf)k;tHZ5xl+Akx#CZCWwa+*j^>Ai-4h@pKR?@bbi03<%iuAhNn9* zx;pL^|70p9vlsGrtpKjS(0+sIxv(XnG2q7R8nvZBm+0$no)3+x7%4}$n+Wzz$_$N1 zHk27*jqtc6SUHjB^OX4ft?|NxyWjcG7%R%}aWTa$50Q|^ZBGRwkrl%#0z-_ugnq$L zJMZQlJVCPfl`o=_F~V^&r>V9yzs?QP86As|hhK@7mHDQddeP`+Q2BYPn2{puEALX* z>PgY>RMiAG`Qfn}@Bj_llYXE-VhuCSbD7oGu6*X`Sw9}~oH8Sy&Cba6_o>UPSWiS= z>@IF-yTJ<^8e@3;Y}ywRt{qjlf$AY}yX0;ww~?`2^8uZ8;cd05;18l5@j#B1s9w>3IHH@~S&8*45VXD?e! z)DOETd(&MO82zJNILLzc07;{yy*pZoJt=IoboV%Pv3+SP!uxys1Xr<1aGTdUd{9t2 zPvP=u0|pgd1*khx`4%8m)99#(@a&!j|HJ8Wrg&J*eQ)JNU9Aehv|A!t$@2Pz-zmce z>K9BEfHW7@C$ohlmKF*yj5H_C5Yhy5{FT`MT=RR!`3-JMp4`~s zKz+2>Dj{^{RhzusPhn5@Wm&Zlg+^7Q5b>OS!25&ZPTF?z@s&Sf+)HX6oQc}W$eTYg zaJksshMbK$w{ya4g(bfFz3Es<(>WJMQDx+fTLZy_{K9<#FRs+j-{UX~Zd?Hy#dky9 zQWom>cq$@wiN5^9rtOm1l+yqF7mdax1h(d~nHXs9XiaKtF%ve! zX&E85o?af9zlpl)l5Yu6S}Y9hpSX%ee&g?C6oua+EgL={H%~!S2Xs=E))lA$%~+2D zgBsW(080OVEkN}6*yML*uUg4S%ccLJ={*10eE&XN)f!br)mFRJnzf1|_NwBG+N(&5 zS~W_M#I8MJ@2F9f+AC(&3QEMRnIiU1tORkpAN>A->&bP!uJb%U@8f-(9{9JaPnlGH zjJs=H?5<8WI0X%x1w4AUMQTTD?)cAx{m_yS+HCm}aAa(Q(Vm~llF#6bC)0@O>>7Kx zxHix?eqqL|JI(a-HjDFn*lrrZExP{6D<6Koy*EyAw@x$uiv0A96m_6L^gv!R2K;yN z45qVvhF!Y*p!G zG7*LIaU9Qaz>t)x+ApCKQm>pUi~Hh)if)%bA-Dy)c=h<2dix(`U3v(?vTy#!YnwXh zHX)al@_8kZDM?3mQNPSPLz!!dFOD;nih<8tFG-&lT7~BeKV*R5;8wM2d60B$n7Z(m zgb?GTV)9LpQ4*9)7aUu?-h6d^{)DpvBbP_jh}Du~bIpw->b4oD2u;@{A)c9O@s71Ox^7a&4o}O{W~^cP`GDqA@pzxA%+cp)b*wu;T8`vLHOxX=HOcTuVE+J-?a_MyhGkfy$aavE>iD233(vv3f-~h^N(6z;(~`?vQ2wuz}Tm9 z3&T05LYE;bFrw^WVG`u zaP@X!YB$k~d-p#+0}Q-{5rCA?ODpujXsL~v+?$gT$^x*E3H~!QZfliO35PeQ})~*_-fAY}?84+xn?e}XzPVBCJ z0OVzi))l@z?)YD3_J%j&LaH+i)clis+{(|q8V^M)#&CN90X&}~seQ-qbiY)uDuh{M zH22&Jv-AO&B!6(6Df#>}m_vZC#$1od%+?3g6U-m)k8cbYFz!iW)w)QZi6!>j?$?0S zgj9W~U~|2%s890qBze4uAm2y0G1)hHgre^4$$waFg`W?b)*=ZB;78$w+}17vtt@IzrR z{*v`q8iDr_Twr{3H4o#daQ-m@QhG_h#enESAe}W#M%3h7j;ndJ)qf*yT7fUqHDllN z{n`BRjh}mU)N4^)u(Yc=0&~o=<$-;(S7B3NDkasM@MM7=;%L zNh6rA^#yQf*v0{9imThkO@cmy1|=ZW&-X?I$HnsEQ|yVaBQ=N4ZiXM*9$dz<8EK$T z?`CD4J+DMq@eIJcE(^(V5ysj4{2}tny=pH9mLEkq)`8okr&sjB>=m0U+KN0Cw3ti` z-SD^{mLDRwQ*@s04@CmjxVswYQShKia5uo@vFNO0$5LW7pDbFmO!s6e&)GA~)A86O zKE-D#KdDv2Tn;nj<%?jzeU9a0QP$%V1-xaI_O7$X=pSp{V!_E_yZv2fpR(iBvk#sY zj-t@sKP2jNO~^qo9MN;>tdn<-W>?OKq23vrDnb*id`$XqFMHAzzUQbL24_`rEdFU zgE6i~4zgv5Nl;G>4Mtf6EA9WUF^nuvi&<6BH3htY_cbMC1Mgm>Kf1s;CFmauhhkx* zTl3sohuC`5r0y^SqAj_(mbqDuX_Oz#(&~2A;#4Lt@wlQR46qHX#Cx3I6u4)_ebavm zLF^^%E&@3r(}_nmQ2U;~Kae%@cm;I`Qaye1(`7fBX2abZ?7!d~@|R!zdwdur(boV%S=D0en5u1F1vv|7T)Ibi4S!S|AU zsOa++*E(X!{NxynuT~G_Pb*JJw{kkgEe>i0#AC2Y+Mtb;)f%xANknb-M*}qz;2@0s-s80Nu ztV#t*PjJ)44`U4j(nL);8xPJUuINJWnfWmfEUT6CC9_J1cYC+yW(cV^yfk|mmTUTi zhAB(M{Kd=jxpT18V;t`@r6bUg{(&kp88~0l0Je7IBv=>ivx5AoLYIsNYT-%5O79hO zj-%gOQ(13(ko%=LVj2h;fXOB2q^hS|8bc$Kt&^rv;~XsH(Zhng4d`H63A|BYNSIRF z3~ncBXBhDAo2X5Jese7y{+U8Oq(o8VrWXWieHpY*^I(h_UpI`+^NUuoa{(s?D9YAfu&@OyFlx|zeUGieqBmuL7`Y1+CP?yEXYm&4emesCxfVYE1>=AgIrfNgKP4oAyHq{o zF3Q9N7?C~h7m0Mk1 zRk0G+&fHkJYBoxq*cB$vc$Hr*`5?M#!l|dI7KPuaN9DpW4xU zo+S51HC55*mx`%Q7*ub~d;K)Lo75|mXBX$D6BH{&b3=Ts2quTYcs@8IJb`0(c(S zPoA`}OHy#Bc~HHF*h8;XqB&x)SrOFXW$GgY<0n`-!kdzW(S2;8;j@{K0d^|C$-0~V z1#_v?{j~|hf=jsfXb3sVzaL{JB!g7(KWJn(%_rhALu@-@19l}FRytG*t#@e3^|7iK zf4ky0+oG;Bu|tGy&}0<|zU1Sd-k`GW-!6eY!!d**^z(cMZ#Nsb75?c?xr`&Qmg7J6 z7f-wE^B%STv5N_#Wa1Cf%bKtXinoFT98e7kV86n0*rF@bk%D3?@2z91aXoy5jeKb! z3zZko)4(d}gTzv{o>!Y0tkNw}+mCBkigb>fDoe9Eb$z^5s=N|1g=RAQnYON|5OAF! zsFjw@rwoz!HnWov$nvJk<;an)Rvc82yJ3_dbK%^?#5WU8;dnia`HuZPNU`Noq{tUw zdQz)>L4v|LM~ZmC-8(}ga4Ar97)?Ue`XFQ6=k}0$Cv-o5C|^_avA-_Om&o5Q&{Y!h zFE3WyYOnTJ1eJuoo$+7es=>Zf#IZw~i60rrp0)995}V zVXkmd!nYHX2EKe51w z1mq+kKzcLvM#I(q{kzi#hE^Df#&jwnlcG z1WC!evO*e{i=!-A(;JXk={8LC0--JWIcp9my@E3UMO4YogZ^R{mG}S4_QRdabOxe! zinqTSG}8ji2~{^nJ;2w|u*8$EN74Z;Sw5Xd)A&V(8|d}PE)7O5BhP)Z9wbqrf(`<^ zy>njTWBT?a{D5CQByoz&923`n228xX)&t_K{^ zxVZBo#rohme<})bTQca_H^lRe#Cg1gSE}+itS{iO zPSJm_8oT9Zc(bL^%uaIoaS(jG2+TijOxv5eik?ppqceg0!hnSi_Vdq6mP z=;MuE(*Q8r?BWR-YBbzCM}CUvw{qlV9x@yTund|^kk3bR5!k1UJ`-^0#0klKcucq? zt0=c^eULm|Bu`HYQWhWVS+^j%?eL;2vG`!jC}CG0RnIM$3)`SU>Z5Y73Jqj(EeZB1=I@2tRookI z$*O5hY{(G^x#40@Bl5yMPTsNYucD-_+RaDZxtEFU^(^eC9x{UdP#s+x`LF+(=Sebh ze&O)trb+AiR&YO>|NCDOF_ll3XO8&cBf4|2O$$B20!g(MgS5%#&2un@ixM6uKe643 zKrjqhx7OF6V~*~My?A^)#WfHScXyT%@K+WBv*o@wmJwcNXJvZ4Y+Br)kZWq_m@3QZc zzr|K*>hU%ICa=PiDX?TMp4iC+FhqY#UK&O>;c+X1i_SZ8i~rUNJF>c_&@)Ln%-kfG zUy4U4Jf?1vkXyEM)h%HTYn+|nz3axugDIG%&OD+WG5qgm$B(*!Y<@{lZXwZ)FY#l%c(+~bZ{9-l?JXgA9g zn_&ZOaZJo-FTa1w_E185c3k)fdS_eR0I|Zr+Ty8)TBy1e`cuHaxOSdiX(^$A6In~n zdY-*Ph!zX}N8e{aMm9fxqjWTqQcQRS#fY%WV%k8Q|lxW-9MmCJIve zE%NlytH!VjLXs3=b}zVqqdt)}Zc*f5(Hi;URk{oHK=j#B!Wq*z>K#OtR}j^WQ4{;2RBisE%>iNdGm!NSzM%B_^@+U$nqK-1z0wMd*&;a&+Z8a}8eVsg< zr5iP(Wx5mf4F9#rMlJdSLoic1S6?ZK_mf!}(!YJ%3NRGTt>+yyGn&0Bq zlko_**Xpl6W}!G*-{>)`Q;yPxwQqYP1~=}bilpjBD{ft+QYV|}Y>Rk3X>F(Z(%9w# z1FL_`(&VP$%xfP;dk5WG5ky|)gIdEu-n+kcBf~&s*Hfb*JQ3V23rnn(5A67X)dAw2 zJvpPAic>S^xlAgPPTLH^R`}$`&Y_JR=z@raMW?0QfWPZbPRci~!+Vs?gTycUgB0*L zZMw8zYtBDLFT18UimGNzvxD`1y^*2h_m6jNo!~|dYoW1Vje=X>Z#bQ8BfX2a8*aJM zRf35~4V*-(puNv}J3xc;u+~s1-8l8-c`6;gF#k~2_CWQ)red77LGO=!SYy#@;h_J9 z@(jrw3s>=FF8QyGHU-f)xOI}P=U%nkhmvVq9E7{&vt%3qC&cx6mcnNzWM%nRnyR4h zID>6zz&0y9+Z)v|jcXF)N#yeC9dHL>;_nOrcGgIPgs%fSK>I=m7M)S;uVEr09EPIY zt6}d2NVzXDhf2tXZ+&uV-4N=$Hx)^qpPQUt5 z-TaioDXdoqwx9r^_fmlga2KF#T<6YEC^OqpQ3O4@Lf;e`cqogW?&Z$~{baUoPhxIF z+jGC%k#NV!KFX><%4XWAP5xB-9_J*-)&3y~sL}aM@q~_Zlq7}t0qB1)8|PRrai%ox z&;Jq2gay5lX4mVAJzhBlpvHeStges<%9VtJ#=PiO3`0RvO!pZru_3vj{OMBX zq#d%YC%`7<@z;roBGbkN8mEkdDFJca6Hjl?ui47cKgK)(TvIFZ+eQ+62FYW!o?{g9 z<@AoH{S+J?J!>|-QEHR}ET#t{3bggZ^^W1IWf*V7P2ZFD;?6Ijt~?~un#@2n0Yt^u znS@IzdM+9!V*p?SJ`N$&B*8SRWSq6ZF_paIXQ7m`_8psY#^E{|q#TG25Tw1$A6skB zH6{SNl>W}&+Q5${<=6;hqa^b{sPa8wi+BM$UhyBPrz#l>RN`(of~Tq;&~T~6%4LhA zf}CFGry3ujFEt%@9^As)E7PRUv7xI&CF7|-a+D{sRWsUq@S*pU z((Bu&2GXD+T&?zN`a?g`)#|KFIu`Pk$zmj}uB4L$sXu&+gc73jf z9=O4(C)mt(yH%dR9|6!JgkiaRZ#ajLco3bK#P}#h8ec}3V_M7G67Py|Cgh|W7A>?&&V9(j`&R>HfClb5ww?{Q+*<}r3vqZ*{4Z_EOuj2feQlB?)%~_)0F&Up6;Y)%H zZuOj#g?Ku^NfVK(93MberYHw{rAPKb4uRSLwUS^Bj{adn%W;M>d{xI81m||nD|fIK z3_;^(5{k7`h$TwSwyBe!7;o!brRj-Ay$|9KuqD{;pol`a&&%|2CF|>g)_>kZ^SvG; zt&(?1oxBURCY z5Nhq#DP->7X1eP-7$8Xs`v7mO4?wjbhDpdirfKwB#i7dJzYv3FF)DFBi>^Y|eMp`+ zv^5K}LI%FFt-IaA77IrUADG{C?~)FYA!fJ))&2{_v~6GQH7A}ET32R1yCr?@gkz$L zKO4N|>%h%E{b?C?%#qxY_~!G8jJjy#8=lfj@0!sYf671aM)>2;d!W{zQ1#3u8d#Ot zWSCO%f$Y2@W7-dZjAs0$9(yP@2T`Iix|y<0xCLbcR-;uRgBw&{dWcj;N{9PC#d804 z6wPdH8kn%w*pkaZP0J43t5(b1hlctZ!ILR-R708ox4-4)v4Uekj$-b)oI8G~L!GL^ zY8>cOsX9v~#kArN&bUE}IcLf*HnDm6mZz}^sx92o0e#1dzGm(3K&yn{PLu3(UxW!5 zA)o(9*67NK?|yMsQmbmTQGK3%#hbJefhbd9-1|aG z1In#+JquJrOu>KU2Ab8*E0xM@#g}m6MgfSG(sqVqD=Y7ob zNQ;>%v8HC9P|9$}_o;7XC1P5YzdvN20$SM@n9(cLtg_=KcraM??K8e>`%jjifdx!e^gr5;e=%DT~XniPe0Cl6jL0*HG1Ni6U^`nQ_9cfxQ-r0b-IGw&2f!nCOQlY6~_>Y$rZic?s zuytdEIQZ6)`l?9HD8Z)x*5iDuFk(IE*MtQRW(WMsJ;;~kz{QicSf}Df@FV`)8Za$? zpt!mA`1t}|L?6sPiVQ!?^aC)>^0N|01YPsZpF24p(coKIQ4w6Xccf#VH+i>Eit$>H zWHfK3-H`07lUcP$1L00@bkBxgI_G4Uj~X;QQP0LZ@>vNFIyiuQ{C+v-=32*>^^Bbf zx97@y|L$lpLOybCoAT*17BuLmr0FqcU)#Tm=nMyb#y-cGqx6rrR$TZ?R~AmlxZmIT zec&x{k_BASqO(6tmQh)4+?8sA&&C&(rH(r6WS-m1$ed4QEhv;lqBO2mB+aqBrgO=xWev4XCTRl!6;@u${ z^N`GHp+>|;G+WvXTP+8&Lpk-NKJRVU81CEUUvDJqRr|IJ^WFTpnKF+}CYfe-9^|jn zf@>xP*=!k2%?;v%q>62$$h?H9Akc%3h{=s@rYl{a+hA{yRX6gNC{huk(0c}62rofM zFCU3B#8Q)$pFP)U_^e`|7~qF^GjXpR&ep0%kWQ-R@&5Z#0Mt+ajhTq>^E#|uQlIm~ zN%!cd4^Sb_N}L>})^Evrfd2lq0rxQMu<@Y9w83C-iPJ{8?FYFR1zr7%BhM87yM|Lo zM?t1_3~EFFe7LGTey}#fYB~DTAn%LiAaWZtfT%u3zKx|HPdgPKe|*(3dh*gr zja)$@h;Bswdl;O8LV=o?PunO4VLW_(_kS!vl88%hEo2Y;D-*`=tnxdY76-=&PE%Vr zM}CS;Vyd$ad2RGAJbXq1HoT`hI&yD3Z@xAZh~m$t6=7KpE}-`MaF#!t|A2$sd$A5- zUQ`l~Qd}2(j*hM>z<(tuH*b`8#9;<5d*=ysFmLg=1w>=>hj(4p`K?Her#xI3F>;%lS*0FQ-__0vf0gue~kI zqM;mZC-*}t$F17tqx?sul-UOwyll-5rJmFiPum9?|6Mrz3-t7567kQT)|X@pw`#l2O~fUZEWzrUOz$otZ74jHPZUZr+kd@-eY6l>{CS*%gL>6@K3m;P*3?LvY3(uZ{9W9CyBB(7@2fPbyIs3> z4-?3(#jN$#w^hU6CAXkGhONm83_Om5+;P~6p}YI@kLs;Q__xvszpeh7H^8<#Fy}h< zF+W>HZliKV^MbKmIN)^l`=BaxrN&jwM!v-^$=3hH6G-s&0tYXWH<^Ku3o@zwfRnw6 z)Ob(DyR`Zx0&jlLZY$Y9__)+=yVTt1sOfhBNQYXTcc&sFNgKUL(T{ijCf7r z=iWW%BA@B5=;HSm$tNuu{oDwX_``AnD&cO>sk!{evH!2K(NUHX0W{^gm-J z%3dhFM7&*}d!FH%u?s>xNlti@hS*4O1NXt#pDbP=&M@^Fv45Lx>@de!g2`!@F~urt z-|!UhDTBU=jjYkVp^I+ViFUPI9vdq1Ad84y5H+ijs{3GDC$bbHAVbm1O9@e-4whCU%tetNmg?&9@{i~>udR~x5fH+qSRmEMVu`DRM!AzZ=Fn( zZH{jyJ;%Hv6L?5~% zp@1{3hi>Fez^Ar6)wg7g0XwpkPdG(4c!$gDFZ}?U#)_7QqmIg2+BkEJoshn=%CvaI zDVd7dyv~UfD1kW1G1;>x;~(ugZFU5OWH+c@Sk4&%&;JbcGaQy83^%6&-W~)N=4`QO zbX$eL!ok2z2fT|nF9P>?l+%JZ-bclB^{-F(Zh!)Mr>Ty|%Hp{8W-O4r!O+Tl0A7oc zcMGD;bFrUL;_ynWT|qi7$gW~pF8`eQ*7oo!7apkU^9=X!Ooi9fk9?|-zhSKT!>O_S zYQ%?(ruzmD{$mxuk=PWc+L;nQp7(@ z=l(JDvX@hrW1}rwZkxIx@+$H2OypSedyu)<3%Hh?OQ2q7^Za|m?mV_-edsIHgjXO9 zpk2Rt{t_?mrFk`T>LecSoy8#jSc@}0Y0>&mhLB-3*P;&}(6(~|O|`0mChd})V~Zyd z6}7w`8)_PL;V-U~W2p6&oGjgs7qH4D@7Ps+p!}QOKTXL@u9h*H+*UVnt^b%5W-KZiWF{57f~MUM=$(p;!(N_AUI|FnPk zi-4zEmVkc>$=-+?&s|k+G7Sr8@Bxdpsnt()ko6eEY*=h+ht>nW2;466GUMDZjg`55 zuGkdBOYPIh94(Hx0QTE%fmktgpe7sUaOC_>5L)v73pa##kLnJ4&^<0EZW+_QtYoZ z)C{fiN7sPGL|!!|URiQ28$W1TGv@Sc1G74F(Ma}dz<~|_HA@(#9c?!mPqp|W83%v2 ze+M}J4B{HuxaHXEoVWrEkV|lB*0~`{aD`k`6sUn0xaZro&pEZociWS5ANd!3an856 z|L&sdQqsoBCe7b)K8Q7%39&^_Rg{eJZhkkrjj_=n?fP_9=RQzr?(+G8z!^8IC6i@h zZnr5g?NxE^Zer=?0oxwao-wK~4u}NILa)`34Q7;-0x;GN_R7Zb>iIMJ=b>vYvn*z) z9`=&l$F#DotU8t%p^tetd#7zDs}dlU?9yXM2K=ko)amjpsg z^$y8X0BN+>^RxFV?fiUSwU~teW+XM3nZ#&KirXUoxH*0T3-bU15*0f;O~9}4o#72r zT9hCCbn9YjGL+uJam89w6be@Qe}bFXj^%o(bU?R)n2sGaK>l)((D(V3vzyBP9|C|P zd|w&OU=C*lZgZamM0Bv)$_y*X6g13 z{g8+PKNl{9p8mZ@#%G+WO*#(z@7iRn6U5Nu$WjL`H%3{jT<6EXjw%6*JQl6b?Vt7v z;}n~i-iZFB`JTs+_R~jPoF=#Q7E7dtWd>kX3P8u=$a!j^c%w&h`I#|a&CP%scrUp> z;`C1J(#&a{tin=SWvf$CZ~tXL&KV}C+x~V1pYDwjX0O(4>`Vs7la=J}KoEfa#6dp* z?`tuh!w`2(x&JtVs6& z#%|dwFw=$~zXnM1FebD5RI~l8YFQDidM<4vX>dLD%#7qsOA#%d4~Ha6q+Ib1s8u>i zMELum4c)d%9aYv#qy6RK5em_Yk`Iy4jhm7FL}P40~yl8t|!-{i1kiiN8- z%{y%w+iY+H8Xv9({`z29;i zSl}AvrMOwFbHzQ6-oDz5XY!vt(F*~WCF=ZX4dXuMQJ{1tEA|gGbyWxh;=wa#Mx8tK zg$4-WKPzQdDTNuz9&D!vYK2fj2GgD$?|b|f&%#^LnnjVd9^sXXqem9Vz9PGr%<1{$7mdINQ5?#A~ zy<`mlln=|3|?&5k7hhQf~*U@^TaqPJ_~)EOiL`)Lj2<(`d8 z66CYG_)=~Y{ehpC!jJDb;czIP3pW+>{>Wf#%c{m-5nSS3$2nhoWaY#3+d_+xKS-&S zMB0oSTY@#s5}_q|9&!_QGN}Q4^~eiPW8F{h) zVLmO*u4H|zwGkP7D$01S(!eTVz+Dr9An_CyT7yb>kn>`v`leiGEHG_N5nZ?98aBK| zv8$KuC*1mM;KbgrzC7?R@4(#e^Wt4gx<93T)VZcpa5pL`P}3RwDWw%0M{P_sTdw?c zp4YpEmG5!gklva~##h(2TZYQ$JRS~<$G2FeA7vkjN&n}b*g%R!N3AnL7@SS~xcQ42 zWwKnZ#C8->Dd!&fOW{E6d>2b0`{xR}FC&lv7M1EgvYna%%mx9FX<(+GyCiK4xT+>o z!0`L&{SJvP`0@|z)i;#d43|lY9G44NF)lw5z(3NM*?nFy)Khr=<=R_II*oIF-W?SV zYLH&ux|GxM|INhU$G+Ki>b==x%{+{mqN0UmkouMYz##Td-m4N?}+qKALm+8k8 z8jtVk+yTOJyX;b5E$(U= zA1e{1Jk|&^?O{8!;x;~uA#DZSelmunGhvLgA^Mvb~YDCdxEd!&Et`f&KC z*voBIz0y~Y=q~nlg9&zDM*M~TT&~`XY7CeVz6l4qlE<4}$cPsbY@_FryW8}*9Id|g6n&n`X4djhSP$Tsd7r`G?*^dV z=7+5F3ENt~o^M9LL_%wy2EvyGoY+1u-fGHU1FvpcTUa#^f9*LmKV#!_19eU`gW|nA zThHNrK8Zd%PFo+@2zZE^=uXq+vzhwDOic0RwjO2mO?u~Pg?zR-<`XAKY|F=4Z-leh z#h)q9BcrTL!8gYw@;u+@mUn=&D0m-JOP+}NAIsHh;aagyQr)}bjj`@aRr!E*)k&`^ z#cfcs;OFe0rkq@L zWmqhX5Mj60;HWUV^-FE6a2qPd%RNfN$j1FCav_*QdE2Ed_HJZVR9Q_M-_hmoFlJ}WB%vAbV%V_TJTIm6i< zL;^UKHkf!El;h+%9q;6zsl=x*L)+U3nUn@OZVo?flRm1-rgRqOE+3RSeJt2X@%Xar zR1-X^3WT4PW8e8jA~tA*GUqnpv}W*Xst|EA_HkcCW>UdI86RpvOnCV=3nRAB_gUAO zf5tI0OeSkdE0SlnAImng@uZka%lps+E&U~NzD^p{LM5BMX=}bP+*VL4f2uQ#y~n(e z*cb1|^R&zOrhP1_bR|HewtwEf87q6w*^3;=@pg1_Le0hy!Xi;;))tUIDGi$TuBWFo z^%GoOaan4Jg-Bb?dS<=3mJq@(DTzd@2OAJ_wO2qH34V$fp+ zpnO~k)q2PGns5=OBVsguh_$i4a#Uc9zNe^XqYaB|H+Z1eMY>3Bw`1zRTa;Jd zRzeUkkY~#BSxq1J08k_y--X&nr}Da87j||$`UyHJjK&}E%;ciGHUhrnfYKh+wjkUrl3lG3=7rsAnT~ZJE(lnZ{c1%$>@q)W^H%qm!8WUw%UH6%7$}o zSGcWnSGoLL?13CkyYua_Zj8PK@<0Ilp_ke)lK6yg8`OSN9X~1T{Lv{3DOW6mL^HxD z7F_i;M5?h?c&~ik z+9|h_Xnmf(R?2QU2j75q89b`ysQ@9K517j*(oR3~Ix>&rJ_Dz5sTYwA8B88#R3@ppJ9q`$N^`PPC!Aib;rq@gQQ1WG zkVLHRl4DMJh|P!MQZ2L9+d0hsKWr(ZS^b8dq{vR`hkTxrN@DTkz1{u?*m+!Mwvf|a z4QiVV+Ld#+;E0R);9m2Qhe>^iIxa6w5jX#PIOL{ReOS$iZaZlxw0;Q~^6s~9mmIly zGOg%cv)@2#NR5VJY?Qz87ZDRu%GH%y2hPv{p$w)@^5QEgzv)krD4}E$+p{c+l#S+H zdH}bZc`^Su$G-d;F6?FRjo2wjo z=}>|pK@ahQ*I%O`hpmM|+rIce2g0$!iG#LXGE%hFSq4_4bSZIzye9%MX%DY3@)gJ8)G|VW4x0sAm75 zXnA8r_QIk(8b`G#h{08>_rrFIA{%JpOYYxPb13!u&=azwgwqN8Ub!bd6e?%E5k8wd zX6+lq#rA`IB7@TV{>ON=5O=7Y$hNK(laO8Us9NtNtfAG(tw+58e54}=9T_=JxxUI~db)_TZqxf|J%)Z$yA~hsd}e#6ou9Kz)-U#$+bj9ZC;T^;KG>G$;46r-PHImd zGuP53^|~z@S7}pc}zSBMRmhLvvyj?mArB2jZR!`GgE0jQI~F#WUcjt&ah9u|j#^fr!?hz_f#-Z3q=`xvEflIE3* z15NP{&o>Syk2#IosMXd++>27*R991-N6Ik2OD^0i5Fjw}`?->1P$m)KljL;{vf5V} zEPp0^X!0K+e$)4oVe=_$H+kQa0u}BglTL;5=d4^|^cj~Y;O_)H2w9+M4L}*cgW*(O zSZ`2v?(CQDK3A*5jG5B#pklF+ItN-TGIW+y zYMCpMe6^{shWmVVz1z`I<*QTu1oNY@Gc22tM3~!?X+a%Ly>j@1I8j&mFif2B6+r!V#oBsVR`mre*-QafF)H1oz1;YEVVAHPl$ z+`&VrljNETfXee$$CwsIl045= zFEC2aS!439bqnA`doMHLtKA`QUN}f2WW%jCV)tnMS1*LuSF!AvB!K4dvwKsc4{3Ls z+d#ZC4CZ6W4(}c{2wJtD-{Q6CglG(@hQB=;yf@(leL2;?R(4~XMSi}8yVeUjD1CsNt$yz_-!s(G`nxc!pK!fHpYFP+_*hOc0PLt&K;8Z0~CIqDDiWD)R0$*v6 zB4@&V0w=gKdD!zTdt#LfY3jM&sTV$XJ;^;x*kCu-{Y@Z%Wq-@_SYAI)yQR?{l3N0{ zggVkC{E1xkqptk8)ZAe$9OGGrFcG4+;I!Zy`$N4RxLVE4;$kLz=@|b2wz|!1y4(N)EA?8x$oVf#;bKC7uS9W~qf%3wM^!2~1-}m7lqwma!uToRbfA}>>QPB zggB#78?Tl?#1tpzTAu|TKlY3V%amU4B?`0y52MI;^VgsL9GgOD3Z|!g2jwz_1veu~ z5yns`kI~;^)t!Uz%CmbjMLf>l5>*tln+a6HbO7crCgGo|^sN<_s$1-aKBslB#VH5n;VszHBrn;>IWVYyz)Y77itoMN2&uDFF3KUdA zni*#d+ew6_h#Em1HK>{a*2?>4#5|qNPV-H0te(2ccgovRI(P zlV>-UaRGYEb^5eEmj#9p!oi1dP@~m)5ZCe3?6hv`h{jiYn*U<~RA@6n6drVBYjf7i z(&n7w_RTIy@Zk`p-=q<)*-(^Y`|WS9ys7{zN@Lay$4#NVgY1x)?@jcf8YS=xYZxh7 z&g^!~3)A|~MxDw*#tEPYVS`j3P`nwm{eB4OhpABnmE-^;UJ4;HKDf6FZYYO0o2Ko~ z)K@nqeAuSDVjlSdR6I9|Gw+;1c7AgIKLDOUVZTN@KJ;a>tuMg2=Vtul51t==vQN(& zpKWs!lpYCEpKdQ?VLWlX5ji%#;v4{u!bd4Z^I&;-hd(EG03ps)nvB|Ok`LR5W>n0G zM}U}Fm+^udwgP#zh}l?ZfKpL~NsV46VU6Q0LTunT4YsJ*A>avrKlymE{MLM2et-Q} zg)i5GLVgIJ;r1Gg>(2UOz2?3!_u0m}sK}WAHmv&>5>5+;nE^6Yg#1Lm(nWlLk#_Ri z5bA`yYI#Jk>}Hm(5)VF&!j?}*lGDouREDl9){^By2o>!PVG%Nu5NSw}&- zPe}OYxpmLK=`&7CABxA<^G}MB?R*P>To@rYNTSQ&t3Z$>-z>t>&K;5=L=6RWK3d?# z`5q!Q=;v#K8zfF)-5rwa`cL86#H>+hNB_{w+4#8mg+ZgyMnltRHP30WoSs*o5wDNl zr;WZAW^#Z)OMiKj`*<1sj>g#>AoQ_QzkyjRGR^j}CBshRq2($vJ+ylV&E=z#WmryU zE;e~y%=4oy7lj|?L!lJ?VBn+E!#`P(|PM9hJCiAtFOTu~{gmKpo zFaO58i{Gls@dKnO`Nc35Om#~6Y=2CqtO1v#Og9KxQ!R&oPlKUFKdi}DY4vgXK8xt$ ztK0bD1s1Y`OfO$BB3nLZj7S#{vpIf9yZ+%50y>_)P8>X}~mobrZA3EyA#=FB>eD103&eJeud(Z&nV_ zd`3!_jJLJz!|`g?BU%i+|AO3UJer%iIDfU2Ff!(H%lw;0H~#coFC#N=|1e&^1DGNU z+1_^^LTER?&*}pmSRQ?lK=PM>CdmICpRSbKy^m4Pt+1Y5d$4}qf3)9Y+!xQAug_Qp zjx-&?F4~9c&tNFI=; z_t$cLp}G8HSommj7%tzgAmv5;`4Wn9y5-3yoY!KS@cTr_cn-LHFFc~4T`A@A=d`>_ z8lMvazl7^48XCs0yFu$yk65S=(`2xxAjnfR@qlt-SL_246{LvSPe?SlNh~(j2=8y0 z0>mkZzn(MZ_G`Nn_>X7@x4&8vs9g1-Qof2LPe?P7sJ-`G%$$+!@Lc;-qmg|*@cgLH z_;}7&HGlkF+QY~h%fZdX<6M6Sp(Nv#NSfbAWg*_fScu)f$K%+v7oL-^6>sz!&GW9@ zVNE^}%7Ua(I1WL?{gI;1+}sPvHXR-Em2$%e{8E$X`DhSv^qcoB2d1HtIcn<27L*aA zex~v`F6CIZ0X!Feq{!E@JPtqvWqxly(plVGiO{B`sEluL8#$<%AOw&eNE1Im<=Rqc z3t#vUB@i*8mk;{j*q} z3hFsu%&+6GfqGzuj_DU+D49h;9WYeDtZhT>-Y4Bc$wi z-{07jU^!@!nfP2zK5+6`7G!VqyhEahDEsy@QtgCQ=kp(Bm;}+IP#|;Y!0atTy+zJA zPlefj6B*-r7(D)^T-+~Mk328tcS5SwZ0rM>NDG7>jTa(gLAYBHZLRMvn%Mw}c0$vP z_vgT)K~wXi@XfTEjpe<@w~IozBJSJ|L&kx}*)F99bxsz_D8xt^1ZLlvsZ|L_gcBKr zZrw`iW^0fxwnLddF2EF?mPoI2OEt(@4UI0`Y*%C#N*kyli1ZvO6S5?quM22K$ACy1D;03{dv3;pf;-LBN~N0n;QKBh{iwS}+G(1QQ1V z%rJGa>Eh4LM!pO(yX2-RNE#8^9TN9VXh!|Xejrj4)tY=ethvTw=2Z*hBI6qU97~9L|fb04mvcHN_qCU#T4dB0zMu@92Qlfv0`PnEiNqx&*w6pqb z4J;p>&;zM;D!p%UFE1mk-s2DEScWnm^wMW|`j+ zbVF-ufOv@>HAN)s>m4mHY$$|!rT(gfunu4$)UsYj(y5*s@E6{Hgb1DQA+o$$h(2FM zz#rh#ElBgaJVTWOv;>$(bK5BGslph3m-EU7u`FRZD9CwN{?s7j&vMSwg-kO1Xtboteh>m0BF)9cJ1g8T&WPSFrhGYd@+kkKoxn&;HX9P(a=qzm#BB&X&a zKt8QclaMwrSL(O%AX^T$Ba~ds&>k6wWUj0!Q9r?FJG*g^`kva8e}JrS?sh)=eB}}k z@Rf7Gh(F#XnmFbwez*9Y=1Y9mosg&Jq%=T&F`EbTn>cRibFP{z3U3%+N1T(N>m?$p zEKFGkSYJWL*ZM&OI8bTE{Yh?&P5Uf`eUET^Vfzi_YbSFwn4PCVe*C?EZychUecfpP zX&IyNy?zNxj#>I-3-U$&VSV&7Ed%xmUtBlp4TRg$qovbsDaiY34Fb2M%b=?>Zt zSHIb(k(9n!`Q zaeM1IUPhj=NL`tD)8|V}RFJ(1QpW*peYW)6e(;<(+csp{SuY6*Me;PBz0b8n+;u$E z=dJV02(KbtJGR##)Pa#M8$PHW5_a8t&G^9cN##`~jF58?a?CVoK1f)q(5}(YRNl9d zuOi?E5g%@_QsGQK;Ks*0VOR&Q6TNQJ#~W{!A|Z-GCFd`Nu(!{0z2W{Rw-JI7W|t|n z95NY$1zm?vG#A_=EH$^HGFU8wGA@aP8=o+qFD8D${YNCJfm({tyJ+gfIwI_h$CD8& zAY(9F=Q-g-vD^8#=JcjbD5*u2*dDyB+>??KFgw!_JRrdu&!L=5!eEf@Dp8C!q^R zlC(bGPen+>?+tPdA!=?GPNC(|&*K{3h=vx#OTzWW#~b;EzVFe^SRea(@%J2GUnc3p z%>Ltc1|KJEqa4R2$W;w(&H3M0Ha!xiKS08-gwOHr6WyeP(oOB#KpTz|wU?WN`?;j$ z-0}VUollx$a@e4n`-_$L9+_r2%Yfg9aJ}}X2<;EM_ZfdoxIqi$hn@!}Wnt02E|!Pp zNSFGQV;xOA2duteT8n@L(Pz5xa62*w&;Ub2RpiP-?Q}j?eVo}YU|xV=(_AM=+l!NP ztRx#c&GB^3x%fD^|H%Y;q*#2ySl-pYul3qY&YaW|q4o7XYG+oUi5=QWW?f)%icHJz z9fBVCdlSX2_4)78J~!k480lhr4AxQ9hvQ>S|9yTYtuu{QZr_t5O*lY`bldk1NLPjI zDEaP@t_iY5TBDQ)e4?+Wf`CWT6Y4=9@yGD59ihAq5e@t7P&kKB$NdNVGPw%4_axQd*#eeP#4YmOx*nk45+g&G&Jc2CkfD4T-I6>G z{JtT063YRBjSg9(1);g#5&5is|JUeJ4{MOJt3Ld@m*4t?jmwGO_ucROyhGBRk2&vx z2IQiPRHKATv@Rlv;&V;kKv;R@Tx{=C1x@Z<-Y>YpDmF4a2cM=2P4nkP*dhC?QOC|@ z6Zs0#Az?1Uo%qK+&F?dkO+*VR2tnk-1IkN;X7qj{*%TIO85nJ}v?2eghP6xhuf81M zud(|Fr*8I*ZQKfF6ry@JwIwq4PZz@e_upM8Z;g6BAXic<$PBH|jq011nS4$e|7rF~ zaG8+J8R5_(iBYpa6m0Xp^SM4DS4E=rW+2OHOkpOIR+t^NSIpf8w z=F@o2%yTRAB&6v}nSD_A&ks?ys1pz()%H~c8nTX%w;*KHyrI5o8R(HKwqt??pv!Oc zJ6mjk`wCK*aS6*r6SX|Ff6SyigjtY0rLL6d^G0Zw-}~o7ED>4HJ4hGHf#~I{zZpoz zKHCA`*&Xmc`HF{{qw(e{pgSS#dzgHfGj$>1fwrdL$px@lfSo->3bALIaf>=X>T#X(NQl!XR!r2sHC~pm{p^h$+>} zk%aG}e7WkYcT=-(H<0J0gy>}*Ey9)G(OkWO{V*a%WSq3OPc6Wo9F6RSB4Pxacg!(N zHW~GCvoAqp&iuq@Y4ZNo_Gr{Keq{s~9cWmBv~|;kY;m70zE_ZVwv!Ml`AD664Y)g& zL-tt;8d( zT7&@T(h-<{aew5vaQmm$=k1ppzto-L>ye|CH?MO)p#n1Y4>JgoEk0qo;YyG*TBblq=GU13IXOR|WtG0rsqDw>y4x6N zuw(juN}n(aaYe|^@6`9YSWND^AD`3YqfB=xB2%-yAip`1yV{MZA~7fr*;XRyte13e zVw>;zj7e!A!>H#6zJ9tZTjncwGu)+3&#MpmbvHGPqw#pYg!HOHUGv8gCL8jf%5ZrDV? zZ0Ak;vRxH{cfM=&?Z&Ex`8DkqC^sm4QYkYUSHphmxMO`(&tbey2=xslOz%be6%ng& zbs-E$oZhR*SOcU6`e-fB%fA)fjO_sgjrI9@nN!vi2xnG`K&gr40`qKHF0N}*IuXIb ze0TW^5^j8>m{NNI`H@=*zRH9BDBdeO(711CpLWPrEGlYY6pIq^se+tSs1P!?YDrMv zc`IBE-bW!O7K|caRGA^UBSO=a6o`IKgxVlvP(pTd!`4}us_nAt6N}c=>*=RK=NUPw z4@HsgQ*<#R-6jQO@Ln`DNB6N$m(5XSQ^*m8(moU#Aar6}bhAM~$d2{{Qq>!hjD-;S zD7P*`4@kN1FCtDtqJZSMerz9Zf+wFXBSU6B*`HL}EXK17&|@Dw3fo#g`b@VVO;!>O zkhRwb(WPp3pSb=GN(Nd=N;yWf;W#}a;f5)yG6v<1t~|(3$k~kP8((mTj5YQ5QF*fC z4OY9H?;Uu47cCD4LKu;)@D*Y=H$L4&LLii&T<0H%d}H4W(jjvqDH*oXtB^NPjrv;c; zwSHJH6xuV5k-D_k+=uMrB}mwc5YO?{fUp$eD%24vXZfEP@IuU?L%PK`(IaDJCW?#| zITKTB5NXz5eYA|UF`tpJD86QDU)7k4?~>XNKGSCWHny9X>J8ElrqqoOAcaxq<5>Se z!zJNKOrrPMB4VfOFgG)IEU!nd?9*XoOzIJ#IAc>?NM`c-n=r1WUs68~I3mo>mrjVN zAQG;J^3Sfng4mmA<+eOf8W=&VkOgE5^EVC%d|>`)kI=K`8}WXgCYV$nmY*oxu$@I& zuuM1egLI>aQjZ8z_dBllPWU*7AJ8sbhspkV|7omD>;pu67EC$k6WjAdR|JdYB~nCw zOx+RXMcRv(6$O7A#GlnYrq7EKDdpMKF7+WR(&g)QvtBc%z5Y<92EHdpgFF>Jx@A3q zE|-B>2PACQx#D^Flp-r70^)9vF!_KVk***}Mv@jx{q>I}o1~tTeMGvB2;seOrGrn< zWD(r^oGseNNY`%8)n3Apkfe#-sP{ovBn$l!Cl(@AMv8~$<6M6VgE~GeRA8Ks$Q#xJ z`a}uI*KrP7Po2p{94Zj`QVxZ1j1fE31@@-N6c85ZDa!^*3ik-Pc+o8$UB()YK0*qXXsJq z^QHF7es7Q~3B4u0S3Jjz`Dr}Gy+-VZk$Cjm*T?RovDN2XqpcBXqjI%!Iy<3h;1eb! zy{=Cx!qFxeKMwq|kV`n;hDdyS6!edLy1OFYHd;@SUW0U9!)&6bW77fC_{+z%fA~tc zPm@&;%=eM*r1s-ihos#PK4?T_k4Uz$1Wc&E^I1=tVb7Q6dL`Of1FQT!SXUZDGEq5d(~4AWFS_9_m32h2zHj+? zltZgo5upkLMv%&+NoezSNPZIUi;uPOy(a%MqD`vdc2}faktic$jkP%;?cRc5{5<$z zrW$bM;~(bbQeFo7y72P(W8~{XjKN;Le2KvxnYl@xHJ51U4PBSg;BHlK4n@5pX_}pUB^@c1)$T295Pt+&Ppgg`&!~z*6!bwdk5VZ~a<7-i;9@{53 zv-svH$Qd-Ad!$>DA-DI`pLdWhZ`L{c{zd)xTzsCpiza?T=50T3kTIJ5kI}%0EK!(a zdu#t++5*m3^|fMu)htCq*@msP2>FHu0^0{dl$kvXRg9Uy@jv;975NV7XY14L@}uu| zS0szVQ1dh5^;Cto)u?84q-f_kobBl2zS@y#8Q$Ui2zLwL``^ zO`sGQ`G74ZJDD0HSnD-7-z;jTgYqc9B4}ksEpn7VsV(g@T-?6xESY#g>IYAqruz%G5!AM`Z%M@fxi1uw{%t$-OujZelAFp9jPlMPIF*7nn zi$$)N5}gT9WNQ7$l*w$%^GL`Wk!eU-d@klyMYwMuUse*H5UvW(bN+dgzxr@Hnm3MsPj`0qs2bp-O?av zr*WCRLr3`o=$`-f=G{AFtmD!v&B!OG$@q%0z(^NC51NS`0V0AO3F5$$%k6oq;eQziUo9s4@BK^MyZR?u^tC3A3!wyOx{r0byieh7=zN_|`%=MRAO@ zL)Xy$9e!6tNt-f`xOB+8o98>EYc&3Nqg4vGG#AbCUKUhdpF@74cAh1N<^Uz)mxM9uC+9O7^SX-pb z$ey0Z953!<&axktJ+KU)SYkmfAoEb}x_SplRzhO~pn*M+@v zPxN(CducZ$^Y_*67LEzy-I=RL`}~AlE3)(YOFvM_Iw50RcTN6b+y1lv=wF>6&$rV5 z>c9CfX*@f#GYj#YS2AzMb^MqM5!Vls6v-H05B@&i% zz$|3hPSGPN_XQ?0PNorvp?rT)k&MCLrpd?v1wjT97_|w>CGtiD7K5xv%FgXIKqkT5 zNysk}vau<|$L;kpv$?v_Sppga*CS^%`wg0WWCB}}-R8}g^(fMXJ4{K)`#AMTmp8eN z_lRg=JB$-4fykT+LN#d**PF@!AlKQIBlDz)Ugx7jw6H&@p2GC{{Bw?qn88wmLVN#W zIw8PnlpC-NgfAj(5XKRm%*Zu`Z~9CTx#l)kVdpnqp)XN{70F}~>V%98eg~v&xvvy? zGSUS^8Nw#}RiQO-aVn36E4K&eKwgY|2{NlTAVRuBawwcP^e|v@$L9Ag~mPgw_D3FhEr<*B-Ity_WUL=%U=?domgmigb zVXQ~+gVx-PW>}-&ZBLuy^97TJA7*2Lus11B>p9hn`%>0V^n&N_)rB&60&s_bAvEKq+C+7VV^eaFJ|*IMLW7l#^2fAf7r0Uw{2h9 z&tv%pY355iD2$Jq4uC(h5Lxhkh3Z=^c7BwP#@mwLEvV-X8IzBi<%t(GhR?52|GjBUUy*y1cP%ge$`kJo8h2>_=N#T4W)kro+*=>-5~hX$7HMWA zAFtPATn{okhWE%!HM$qf%kVWHL|QSZV%`gYDCd-Yazp4#`_*hSC1f3ayD@%>)Z!*5 zj@kozNbz<1qMVb*ClNa=LRlSH#hDi4UH33+!&deW#iNV9Jbm5)Ac8qbW%4j~{& zE!zDj|L#%H(H_J8*J$vVcV{}y`8gaP5=O<`cFS|K029 z{-crh4gU#C8Ma4+9U*65SQRO^NHD&-B*?Qtf-j_+`Cs`I`2yd+MaG;jg&eof+UV~A z{o(fvz9L2I^Lh^vvmsK&b{V!qqp@o_x!#QA56)vEVBhV6Xagxy;7lUUU}A!3^&{>) z+KHTz*`p7YqmB6plRLB>@gl4b%|@8FMfdKI^ol!{CoFEWP3MWrk*c&mr1Ap7K78o$ z9vl&xJkCRY_i@2~;dp8}r~LT)DSt%!8AhS{qQ?GIL>t4@_=J;7SD$uK<_z+0xGV!T zct!4~07AKB;@BQVvL(MDqRzZ4M^gCc!uM$vb|3H+qO`nxgC0!3%3G}b=%*LOr-k&y zVU!RHi|MUGmSRKXLkamiAX{KwigZayr11h}GmvENPe!~4WK76Yf45i{$n;<3PP1_d zUxRuODJyMeV|>`&f3b1-gjv}aZK_yYa9?D~(a|8R8>C#7DJ)+Q@3uNm@ZKYmMlfVm z*s1xqK6l*;e6)yo_tL{E)h7(=Q{RuHZU*bxK??0tjK0QpgL3Gy*>D`w?K@wZ@U}>I z($n^}*&yA^##ou(zOEpVWuJ3jn|NP^U*#I)-(T_j3-G+^8CUrO%AZ&H*p9JRmOHjh zTQ(uTQTfm>>Iawqx_Cg_v+d*h9ou8Z?a%SrHYb!np#9U^Y1FP$THE`gKj8QI&HQhr z7gK)u2RA%lP@eQZ+Vkqa=bzaBQTsn_Q~gugGi}+&#PQnpF`>O*joB`^8lEPK2~Pn$@GAU-@3bcfb1Y|3SWeJ%2q*#I;wx7sw6X$27|^ z5-t{qZzMByZcOy`f`6TT4BPuNk*~dwp&LoYz~llk)rell4jK z^o#Q2dQSS1bGFTv?1u&N&Q{V;{im-QY#w^GV1Gfk% zo{)70hUwkQuB60KC<&Yi%>%AWOwAK7+BZ1gmB==&&xm|AY8fu&koJeJEBRdqi$FI) zwxMDDDF2&CSbVstB8qzeeDs8P#ru01J+b-$@+Jqk=-Q@>_oJd*IU4EbAQ_34}&^Yd&w)fZNJLY~> zGqwp0e3X#>eOk=ff|%0+A?EJG`<(xzY=SSF+KoyiT0nCBkQrFkt1q9txhpL#c?m}t z60XuOz8~ge{@_PN(i)|o=5CC_4#S0x6C92D#7)Rd6(>6@O{UP@m;wPsF?hfn(R_!| zu&9y&^Btv=AcVv$$>)Swh@|=8u081Ewn#VJk?j4_PiSTS!Q+>^y9gOnB1#|@3LRyF z(tpgaM}?w%QCjN7^}_u1vJ&Y(n~Bp`ZqPnd4)lgCrmc)ODqSf>*+(2oP@I$1OHis$ zxm<$MMm1ZRPuhj*Gqhi!?QQyHRT(sozn0pCcspihWH!d5At4mks!U#eU|`Ed#$POY*7NmdYDs5eelE zg`P)IujH51Z{pt|+^IdIv_CBO(A)n>}iJL(?h`HDCp$|5A{!l`4+=@_eO}X`P_xVGm z15H0PIYQuizP8p!$_gS%LH>R>HT#SOvF;RdzPp{d=WvheLk#4)rM~9U30x<7tUPqWzY)S$IpZo=1N~S@eee`GQavWSILf(_QkjQr_lsp@vEU=&l-Q#m@!Q zfth|nJslF}bH;d21t=W|`drBfWz#_ZnSBoX0?i(He=IsY2g^uCH73_c_#CN0Cs5c) zdBqH}7?C0}Cd~zyFd=sSJr_43U*pXnNYifGr#BpJXrH&cn7dE?xjI6Z4VbD>jNA>r zD_l0i;QFE+dSndRjmn6ZGif|z^Vybjyg6?n#H_Y2g`-}VX+??h`JLl1euAt$Fb~bv zdZY>i2D2Z7H@o!5fH@Ur_9qHmq-R_P&{!;fN?ZEU0x9`dqZaH3>i-G^T%RM-ko>`e z(C3OsTIydQqbr55Gex8M5yTVB)yrqgMgng|%y;HzW$q_hg-jJX8!QWU%zsL) z{XGCM(A@CkGo?I2x&zJuqp#R!#9)5peIMueBq+|4rNz^qH~Ditf$p?cU_ojpw8Ps; zNH$WGXQOHV0SO1DK(GLoh!_`SK2i=H%o@07;~bsmKCJPPxt_OlPS(!P(=vXSL=LZ0dYq29N<=EvuLZkN}W zuagz#Up_fneZ&!K4*bt>IQkX?NKB{=Q=^thIT5Fr^R)Hq>nyX6ylKIpkE#G=KNc3yb@-m$hIjrlo5F! zm5h`|sPtSvm-DrZRDImI94Qy((*${@Qqs*Y`ifw)8q`ZdQjvA815~`e663jZw^=r>s0pA7YjnhWB^Ud!vPJ1`E>IJe9{YWlBGRuuYZ-rdp5?&&6(4fr^A#U*i?A;wyNgdX*IzDh zOS5mR$3?|$2>{$pek_hHGB|yXw9)bd2stK7EkTTweP38X*!UbzI=ve9>46x5^c=Yi zL3+ZB{dc{Fl0F3EP&n2{=JvW*!xeDWxqD9XA<2{ZD>`Ok8(J*4TvrmhIl zq&yVHkPpriqqe9@z>xKgN?tzyIc-0rUq`Bp=AW6ti=J=NzMf)wglSG;>WTZvC}S)v5uv8AOr@HTvFAsmOzh3+aJrMQ^_#7dRx|W52wxh~PnO<*L-TcLB1xD$&I-c3zDZLbHE1okEGQocX`d}-FHIc~Ug zV-BnZkcjc33#6l)P?4`eJ%FS+8d?#yAZxi%D)Vm&_}G#^g$Me0jsAQw@udYEO&zbH z8H+Y=z5JuJ`}%!$?P<$}{?m~pME*RksmjIl5hBL^>pat?AQ&Jv5cy_%r_>kgL6P?0 za!0K9-5e|rOdBiT zSU!LwM8Yx7gp3h+0z$?8w|QN9^gR1?an}msji_nk_QWtT=s?Rrtb17h=o25qIEwoc z+Er=J_I&!K8G+0!nIHx#`KbBkieTZ$*CAgu=c0KNkgp;m&{Wh6>e7$u)pmtYP-MT@ zybFsIkq-(nY$rUm-f?7|ztx^h_wQuDteT|9b&LKfz0O1ZNux0IO{mr(xzzR~Gc%WVJg624{>UCrr?{Y>>9eY!4W1Yy2|jMc&-K3=3#i-M@_ z!Z*(5Z4Ha_OiIf^mbx+b(J4fTCc68Am5f0^#n*9}xg$}TEXYR-F1&B6yf%}59{ z(zW>g^(RakG=E=cWZn!UEJzvNZ;@*tV@{JlWI9ZcEzv`ifx$>_5ARl%ca>u##Bsjl z0}H0K_wSEON%_cnV?X)LU|)c5X9`=YPwUt9qr%P2)Cu)kBs-=-fvvDE!t;iPfduKQ zN{d=%8l*eeqA?0W2IOf{FO--2#Uch_yHs2n^Rxtc>m@UJT7cV=G?&*PX;4ahe+2?Y zWI$2CFo+cyd-L$791wcMtX(4kApn;#L_iVsDm6a3p~_$_iT)&77T zf#Zw$<=k)VFI2L8JE?M}!tEgRj!K?y$4xhIv9PG=5g8XG{+?1uN`)CHQEWLu#B175 zug}L9gm@~8*ZzP&FhZ3ps4UBh37OGu{N_a$kat6w0-kC2^sc5fBSdT>=C*9X2X7wG69>^P`M~4zF@LO z?T_VJNaZNY!Oa~?fiqDYca!;brATSu^>}WZkf@Y9w0%RykIdc;(+XwF;CY=FJ!jrB zskVG%YPG$pJfJYn?SM?qgy~se8T;Mz%6*#afO*6djp;CpJ9hJDM26ho6pD#(lsOWz z6sG)ygq4ZM0t{c_HUETkXGOj!>@l@rj%ZDUEg@YzhTA9y>|zDvE6CWQ+#N@WZ0LAQ zd7gn+`aTIWylYy_^sSjzkh0->F`w7C$hl~o&m!zPB&=U4Qs2C!92Kp~o{^801&U=q zh7r=l^~{!lOtE|&FRoAAeBeWNI^>llQVxExgqHpL=@+o`4OJ;j9r59fQuy4NRJt++ z*>>egfK_UiCq$~}Q>&Ran799q!mtSxN@jofo%)|loe{8@aluE!=V4DbI4KQZWg0m? zDZE6OwPtA^Ulv|^V^md|p4t~el!P20<<UJMWa-P|Ug_!5PFXTdsXk++!C1HrtEoMtz0zR-R+@H2e+}k4+mTVz5aXccZeK5p zg*=TnDd9=ub(1kKjT!xgfE#3+-&+)6D9E!zu>J3-Sz~#KU>h@6_qz%alf?*wSMuxc z2JHm|iOAigUY}J(tn~X0Qszy;ivxuoT^4dey3oFf?YKd@fh2FnSKCJjnuKOrNT%`c zINxS6pMcDZFqHXGr6AkIOpfV_bPazmLbJ~A$bazPPiUBzAJOXzn81zsucJq$V@y=& z?%e;r&RQh9o97`6I3FV%7RDS6n?jU)xRO8LyY4R~#5*ozWCiJ_aGk)^d!b&Cc&X=F z@v#5k%OGSCV+eFg_rWJ?GTvA}?lXQ*h1(0c&3mDX!g0y`k!j#izwd8C!p8I82zOVC zWx)qM-2I5imTs1UfG};3BI8C0Fk^%q^3R2<3gv-FmX(iGLfD;F&n3V5^Eoej^eytG z&-0FoaNqu&1DzQ8f{+PjnSouDKkkdO7TN3~tT2iVxFaGx&rQfXHvtR#T!VzfY13wT zcjaq)Q{#U?uGlZp)Y;4n`O$|=?MrEdLTsxLBaI$uLEK$^jnGyhx(~nC`u&`7{!U7g z@%X)?gvEMjgz>PLUJyb_r`hj@p1(%FJeHRG(Z5diQ+f;=$Yh=Qbj;n?6A4ikDkyw6 zu44maixmnd8Z*B|_BP(HhaaI90ZJUAVW5J+>slCl88H`j4~ z5BO2`#`2Z-h}lnI?rDp3A)!Ltg+T}j`w)bP>%2j@1rdLU5ZhJwHExM>D#*Cp*j3H$ zM#y$@KaPQ3irgoU1FUoM@mhj}Exul(04%-UvdNE1EzHZ_W}hQ8YsFmuLU$JB|oEsK^IFGGNlPA3Q5cH>7tjlJVb`EzZuzv@H`09 zmnOtMVH-2f#k%k=h_^&Tv#=#vkuL75m->^)4c@m=X&2HP5b1M@`CXPYxeg*j=5>_8Ce0?a#yCTo(Ltgw`1I120U^th4LblZY8%q#ua8gQa3^=_b?eKlr z-c1ckL~~bm(msETust0%E(EOKz1^!YC;nXmiGumz2LOijwf>%qe4T$M^H;7tQWjv& z>Gy)fGyjx&sC+^mARN@JTZH*yCWh_Oc6xpTWUk|7N#kWEy9rD)p$Q4!AoG+xRVbm5 z*Y>nR>=B8Aa2`BQB(&|#`Wm5$o7Y5n+^Aex(?2+_*Ns=4Tfd>Pe@M3P%z#Dq5d04#JvAqWMw&O8ClT(X?_48rSo=)f? zV#RqmAzvK#0sZObe?tE@tp|*%%=)Ix4=Yn(i2>xl)gnXf0gX18@L!jgU zXLlW)p@SO`0}A6Jq;PX90t`)<&j}-PB=fFZ1Rn*F@IonyKI}&5A+l}CW1(ElpybmS zV&(=-h?u)79WcII%)6>QXnR_r)*$EFUI0d-gn0gwFl~PaWyT~w2~*Sdh|r8hE9*|j z7m%!33RhCTx^?MD4lFqwoG+wGOeTdW zAz>GSfQa3PI(@#-{JU64mhusihLAyo%)h75H$k@0eh{XH?Tovda~_K@M}&*d>32jh z@Eny!<8`%V9@Twwe(uQD%ikia4#-%LE-PIkG|7j%%6lLs2od{6OeCv_*|U^kCf@%dJtwIbV8{_lLp*8FbJPrd;RC#py8c+_m$r+s~i89574WX4Dt znK&R1KuRurDX{~*}~p~o;AYtZrDgrFiV{36&8OGFWdwH&n!;K>5Y2g*?O<=2m}M?=6; zgF-mxBiC)#51+~lVUo85qc`_yuMHNIVZ-U;I(yodbm!m|8+3G-q3sX(YVk@A*yBSNn4 zy_B`Z^{DVhWbVp=8C(x3^+b9hZ4HbvP5p?Lf09#>vRu>p6m6|Sn-TZSBX?`tr>!r! zhAWY3ZXQrvS4FzWBSX_@%-8-m8==yEe^i7*_?i#GaCGfbCf}Hyu5OQyvK85Z>G5oGrEs&;OkYUyU2GAC_EpoRLjw+6qX;7T1H>HTqx(F8N8sc zyY&$tZUXy!-G-w~JiZ!S5Q~rTyx@IqmgH5rY2edlz|33ovj~k8%KRP)X*_ac`5pvRmy}m8^0rAQAwo_O`s>l@cO-0hX7ve$h zUZ^{wlykfWKI5!sWqpR{mscns*6PFjv4r9d>KaW?f(09kD> z*U#@^dnPxFZ@!}UaI>-|%$q`dxb)me*tFwv{XV6B|C>_3mUC(+DNTkx>!!cRbWk|r zIH?698v2@mN#g^7c|QKwPE^uDhUPdR(v9`h%s+2_?EfaAzsv8qsZP)T{M5c&xUDiD zIbLtl?k3$IaE?}msF;@#d0xWPE<|fCBuDyaLs+)-0N?EhVVe-%g>VK?AyPGLpD`kj zdWxADg?M9bHm`@*9SUtiw!9%|zh^%R0~J%F)qG@O!T0s|oyZv`I)n|<&j$aV18wnr>Qrt3Sr3cvVb{R16t>=8 zLbf<>P(FbjGxiA&v(FcFcb}O^b``#1_LGmm1Hy48OXVpO2p^`GgGkAj>c|_QR=x3Q z%KKB^f6?%jCWvOV(q7n9#Qb|7us`p7tt2cL?-@g(D?+LuvJ1UPbJa=pNLZE6QF z>nQ;_Q_@u4klw#ckNt7A10WEYXue0k&H;a1``WruuKy=*6>D?VKH-4X(=^p5cd zq}vM7?m51P)o=anO3h)d%~0Db0Y#7#BsVjwlV=2Z%Y8 zhF)37Pdzq5y&!QwvIcSyl$>F^AY(-2vk~GbXFu)h>(cD-?#f&|#pUJ9NUlLtVNIVj zRG*3}ej}sR0PwigGno9b96&}KfRI8}nY1E1LSU2+d06d_N&N*`YPlxDw>)o+!wn4*wywR|KMKS{Ax`5X?g#x;WbvUc5qkUZKKf`8v082hVrJQ&)B7a6kI0wl zGTzaDK+3^fZfLT+G(N>|()KmE9r65U^lK6NIlppi`Qc8G$NNZjiJ2 zSBbJd61r4gBb3cJ;0k|_4JR}TQ5Uw3z=#1fLbZEe%+Ii4Vk2%r!UT+zb|4sL<_0N; zki|$*e7xm%KzQNL)Au*f{(N8UNg-5_^w>qHkk3|xP|g2w@q5g-01-=j4BM;7bp_%^ zp;?6N(m*ftIpd`zml6Qqzoz!!9nq%9S^AB_H;}Uhf;Jm31dO(B8f+EDfVeCM7lbAF z@(h?V+0q5Aw@6roP%>*gpDaoN3ISLKMEZ$=(`NgKQW&?_Y`CpB0?J< zLif*e-=I(jU#%kJVG-TT$W)Q*CG_I`j}+#%^W3iI8z0b5Ma)QNImtJ6J*PYr*(MUV zjl%Mynz<1)Oi{fZ-KRRffBXBr zf1m4u$9NUy7#S=UO#UY{Ac$|#mV4pd6u+WOU@m?O<(Ev>2*uv z4J%In>;K_@dt2*K^KHwN$sC8#!^S?+i)Nwyz*W(Xe@9=h{fGbj-v@IfmYU)(L3*oVGwd_Oa7 z7+*8!i>I9GbD@vRhkh2}Lhgy0V!&n;3!gki^C6o#Up4aSh$<9M!n6oicz$z*4ZRPvuQLV~2ip4fB9k(F4(GKOj7Kc9AJ7BpLnix3BQm1E zs_zF*xgvl!VA)Qm^xO2+o;jQKXh7ejzR~HLzmZHnbr#_xux&$Tpz_@c=XxC3k!-P__OGcbA3I`(yAgV&k2CqNS@ z!KUj+kn-Lcof}cQJ|2)_01*j)`L1o3zFL7FWH`{IEFnLB(e?|pHP5yDj$YCprMve2 zl>Yqs`l89Hs^`HD>x&@{AzO4dx5#^Djwcx#YudQ_Q zdTq}kSnH7W5SgJGY;tQo2mSkal-;t~O_W+1@`XHRH>!MU%a!d9hTcc8^ToL7M*<$XV0f(J@aL zwVJ|y&S76)aL$`78Cy2pjZlDbe8-HasVWCQrCrcP6tn$edLdw9lE5T-WGO-;FU-qr z3htle+6K%vKPa&QNu%(~)XU$*4v39du4BJLJkI}%ve4aa&Y!O2X#OrWd=BF>h5?O2 z7o~ldjBE}7o9#F1C2IcJDE2! zJ=Is{>&^K)&1ackmR*Me-bdQVe4Ji!Q<*T6k<4eU{b+%}%VSO{3RSaxPyawjzmvWY zFe*76GRyVb(}~>1Fk4?6K6HVXo&d5e$TyX3QrZ^tjS#Z5zfyT~r0miOyM~a!KKWLMD-cuA5iAbK-h!Xcsn<>G>ud!tp@6 zUwY1wF;{Miki#2w4ZZOSApw z)EfHpk3ZDM$mX6dZ95Zx`UBs++50@-NqKWu;d^XQZQfZ&X!@(;!Iy84&w*$_qNr)! z&}~XWxLPXch8v+{b4dT*hPX|7l>G=?>9y*EzV%7V1f6`;Rf+S>mdq`Dz$C0U6bELv zcKtY$R%Pr@PSLI6Lg)*8Wzaktt(o)wME^Vg*1V#`InZRqc!<*C<3~vNdJhrs%Jqun zky|U6YVQLsN^XFG6v9|9=69vafM3-00qc-1#lh=qec3Ux^~gNGes!bS`r4l(790rY zm6irl5>_e!X1KjqB3}{$u3P{@z4FuVv0u)Q#1-uUc?*NIzeuI-|ic(R7}q zSV4Gdei9*y(U-U=*OIde>pf(ft;t162uy`4Zd!;o#>sTES3h`9VcaJn+J$>odIJ-| z#arlD+}6`A^88*3yfJ=%LdKkqN1Bl|AYnjksbl!&L?!5j6kRA>5YxWCay=9Bjo@9@ zw5#%#^aLhte~r|b_O?$P;LXB|2lAfY%fwv)=3L}?pj4$WO(ba~wD>D(7`lPV+lqD2 zxRVI^p^^cJ8j!jR1rq9lEJ=wrMXVWsWwh@qQssQFH19ySV7mC+2auI{MZUtfIz;Qj zK8{2_AYbi2@@W^MAS7Mr)A+d=TDdOpAu}srV?q5zIHHm+NOWQU5iH1=QamE}dY^s3 zno<LVtfHLh>V@;?9gY632B6997Hy?%l|g3Cilij7`wKk9 zbS270u9uhocBl}Pt06wdY9<6`XZB2!Qu=#nN5D3)UVr{R?JFXE%60wGc)HRI?Fr^P zfO&&G8GX0~vEcp#?T1buKz>8vPr?bz*|dHD`J&HRgd`(l^a&d=+s+*o*Qyf#^6|!g z`+Dw>o0$7U7i#ID351R6sh5}IYS;rcGEmdS4Tm}Nl zz^#x7^bH!2j;S(cK#!l%Bz%o^2Lt+)n)^@<5sh7&&FAH-jvG#_AgXnU*7R|-Y~C|% zh>U)nc6|yzIk~$#w{K|L5qmenhL7T{jlQ}Rn&=fl-V*a7h{M<4LZO66crmhw_2%<3^$Q>L1;q4oDc!38R|;??gugP4x*9HKEzuJA`iXTjnz@ z2ZVG6K;`_AP(*M8&o|^x@WtkEgZVK)!nofUJo&cE_UIoUDHHQGAm0)V4MLxhEm{`Y zjfqO;LS+J!p42vdyo@Xk>!KGt$9d8ByYl25u!-jPUVu?(&f+6V4asB|Ay8|~t0XiZ zkTYuTpuycDJa4zrI!+tr{Q{7@6JEnC4|8 zz|=*Wa*RS_DX@x+IlyPoekCO1^RM|yc%rbgn2&KOncN`f`8%pTSmzH`k+qn`2sTBu&vilFku-K13Ate0`ner?eyBtS_76^QXSA)chH#;5tcW z4#>lge4&!<>p^_NC_EP+Q;aQ9WX(ucepjFH^?RQGH5T@Qd|PE^T{nw`uy2$cRD!{r zk5V}LrAM&{nZfU%t&Z|LD2s}08zGMQsU3=h2iI$09!?)Dn^gmmq8T0MQ$l+}h=tey z;Rwn`C;=*O4oEl7`%*Y`MY;vanz4EkiP8V?8=i8i~3M5kzBlwRO-f^%t+3i3gXMm3$I@QpQ;_Cum~_Ai+@FOr|p zid5c3>WC0T6MjO@?;vZDHFiIXZ>JIMs>~0f9ans6^R6URYDQ6Z^4utPpzF}@qenxp zua^+`+Hyy*-WhyDW9s`}f$ygA=#eS&i)Q-C$H}R(2hWe@#Y8^59=q~f*e{4CMy6=x zJ*m$xCjlkHg-#0H1{nh~o5;W9W--(DiTh|_e-T;5B^5ai2EvGibIpgyHr_*@vnZtz zT`fY|%KHdvK-j=x49`CxzklKnkL;)rvCKRmhMvCayjsUCPej%w4*7#35$eC zN4vSze;d`_bH86LDr10c|5SapbR5&mG?}alaS4EB4JAWOoVf(=cYc(_t34V?`w3k z$QV)LjMi5o;(FSI#s{Pz-tEo;#*u6)KiIxdb`-1}plYmt1%Cmm8Ym0$f=^qFyobOA z1kj&f3w^4-m~(N)hXFQp9;FC6)Rk`jO0;L^o6^ZSAU)K8>e;E?(165YQ4uo+GYKI9 z0n&sND6s5ApHnZfr2*Mem2P@0Gqqn8U3x)a@t7qtDdoLZok^cJH_Rs$9Py4_A?|(QdBWad@rg0x5lO&4|2vZ}o zYFctOtS8Q|=lo%QoXNLH8F^cScrgG>1GoH!H19_som9%QzVrc`v~La(cBq7q^2$OF zr!Op9aii9W=cAAmWJW^r#WC_I59Gg!*+ZhA2~_O>u9?C4i_{08#-B%dawD17A3{>L z>!R^Vd2&O-xoc$Td;=GZ=e0i9URL;BqeSt1<#^%U z=mV4(7Z9H>ZCK7TEuMs3771(0LHL&L9(vE`EH9D{K&)aoF z;~EA{Eq{mp5rW?>y`N~+B-Lz%^9GyF%u4@HnteyNsVfJHqz#yoBk!L^OYbwC%%T&z z+~4#eH9oUW$s!qw0fjriYD35$+xh3xc73(%pZ(Ln6hxNe56VNMu)9*ReF!Dsg%AiJ zxA@8yAYfExQdzu&Js?aU?<@~2EvWnk@&(_jD#a}Vh2LGdvc_jC2(_oH5*PK%dK5T;3lat--5~I+52B9`$XbzYKrXQ;hTBekx{g@s z`Jnevs4iGvgd~@gqlmZ(ghlX(mxCk{2lWACfbPJPY_ub&+}uxF+dh{g;HLm3J?8~C zo(!T>^C7f-a+dB>3S0vm?=dNR@nF39sXsK+hNcdhPjWAyd71Tp`k2?k(Fbq}M9W1f zbgYd&E${z+6FJhjiJt&jr`@83jODEK2ri%J&n?86rWzmF|^*4eph4)U2_LkfvJB85$vaqb>dC(|M$-BFtRAdc#kH zP!U<0zOUN>k)DoIQ@_+l2-{%2M*HP1WI-?m*~0q<&mFm>lCB7uh54ia%Z$#BeLFH> zEi8n_{Y#l67A8RIM*ZXMh?W+AKk}ncT$wMCVZI;dY@t1^P!8mJh>l*8KgN?m*DCGr z_75WYTAsnWScoLmCoRa;jH8Qf;Qog3>C(doBG7UW>m6jJup_c0q5M(At=8#ozt}LF z``X-|+U9g$z9+kaTAapoFG#J@SpMEkeOv0?nRfMQIlH5+)=&AQe=Xg#qmGM=n~tM> zC$;B%hkA7WTwD!jPK1DOXuEsu(zZvH{T$8p?nHg?N;vvJisF6IjK zbp`dGPHVeN);sm*igNDqZOh*`>|ew2Ih2p%)8*eA)*+U&YrQ7z(R!?-abAq~t!)$v zAcXeC#G&5q2oLo`_}|ucNB!3ByCvhgV4QTF;k-q?TlkgMi}R=mce60f@f|Qur*Ss= zePLS0w`o7*EAxr{SXyqsF81o5FI}$-eC3iK=TGa;^=nz~mVTVdk@7mZ)A1Rt|FeFS z8;~6!iZuUP{tlXeY6_a7W)+Pf=N)Rm^LGBLfWe}M!y*01y-_4Z3Nv@<;% zhf447(QE5QKXSQ7m}MP_tGmYe z3%gVNoHYg(l7%JCVS`T=-rt7Ee*k}iK+UV13BRK|i=v(_=YJx9Xv&{J(|#q3kUsAN zM$xBguuky<2h2a&eHO^n-GN1@N;ET_`|W+c$tT6XH2kN-|IYToO<9Y3Z?;~j6)-2RclN8;oC z_4FG^`dwsgKP%6frCsL2mOffEtRfA|O-abu0PzwsWkmjG)7H$dYUC|K5Y3%5^L-(v zy=`w`vK>+uq_Yp@X}+v?FrvBm3Dxt2fD3{%$fv$de9yAKjd?cvVN_2bqC@$`qW3T! zcPY-sjnkI^03ZNKL_t(1B2ePbFiymeLWmp?u8hNWM6C1s?zlT&F7@QT(sFirzcXj^ z`XeOn>lEjqL)f@39f|uquN;SM+3#*2sr;(*2K#XJ?Fv4P^Ah{*MSo`KwGM_aPC;_z zs%7=HbkW7GTQE|+wb_S|S)kD?5^r3(sQ9?Tq?(xp4=>1l#{l?dl<2s{nosLf_-^XH z#_&NLXz^+AcF#QG3ec0|k-iFU{|#>I>$BnGsh1z97KM!F#BJ0I}nv)k8vaX&Kp z)rB;!8(r^vvywpV7&qhho9Wo}aw$$C%lZL@f&H_le}pVKIGM|8W?eM$1H;QF1^F@CFaZkKHvFrzJk7Yg_4|v}3S6*jU`2sSu z@cCjO*7ay_+pzz#T*<%q_hpBLy)E(~&a*UK)8dlu1@mi0GfKQTN5yT0him#0j8fY$0 z_3h1?p9_7i*Y1B;4ORNl)pJZ;(@`2VA4Mi_(X8nmX+-e*8}xQ9Ly)Wq{q*~r ztr>@HT<*qsJXjcb91i2y{oeTAZ=YkJRD*Q09$puie`~uAKHajeshobEiVt@4zFCll zoHE@<&E4{caX#}CUMG8R`wWfMB;BbhcQ)^hngAd*hZb=IOlUrOznisrADD6ZO7AE7rhVU`7a>`=CUTv9x(5A? zqeqB@0r4#ve~dA3M3cx?E=|CFV9AWqTL9$?f;Ecr$D?|3?W{kS@9pMz^&e<31H@#K z-;ps{5;m2W{Lk*&1<}(qz9qW4Id@ISOORdgJK(Peq`Sf5M9R?#8F#;{57~G9Vm=xc z89>%#&kCfx0cnfn1IvefIN%(#=g^~01<4ARO8N0hVwY&O6QVmIMAYb1bKk5PQYwgc1JX@Rue0G>*8gVe8arxU ztl(g!&_7{X0?%CE_LgayyoJq;*}9 zu&z`8-2$nm`PJ$9M9<$t8!*Wh0pIz`1kn-?IwBlFAfnvvCch$Qk3PObz=jBO@w_FX zv!;C=!t2^OfDcJIh$yMu>KjEQ-y_FO7PlPVec}TLU-Weo_FJgLbVwe zUT-GrN7KVPD&_V0t1lV*+pR+_2g=*r|FS<|T~PbPg0!3QsK~o7Klz8zIzl<+F_&wx%v7?Io0k!V4tMH7b^Q2HT7B&~GuQ3I`r%|cVn{_YC_>-^Dq)q;fk zXx&YS=C7Y30`>jDi1F&b!G3M~Cq7!bP^}pswG>fEL>|YN$SmQj z??pd9A@L2*3Hb(6W@NvSQBipw<-DVPXpxBMVv#4YFEpAtqNbxkG_QFBnec;?CE^gR z9@;*PPgr^kqp5X1d0)@!6Jwud(L|c)uG_TJoFn@9kb=fwN^8{T30ZGxXF;eC*m6r( zjhSfC%#O797LT;u)2f_wG{)8aGO`7;-1p!Ad0Kqe9kSk@8XvK-WNtfrgMf%P&vRM- zvaXB}?zp9W9DaKgH29=rU#P`~H24mg`u<7Y6zfHNz&c*rSUIM3PKfylK~+SQ>z04- zkS6yV*MCJ^>Wk%axSbp8TaY$LSrKULPw}Z!EDF|BEP5$C<9Cc6@5V(DT|s`G*?G6) z)FangU$Ic&{GEKvvQBCJ>bz?EItx#kcS(x{OZ(G+Basu&ht-UZuo$LuTSu04TlDZ6dhss_M`1CpCW90z=YIbU8E%it~ZbHD;tcCmhIHEhZHyo2?@Pa;zJhe2T(Z!}4PN@ek8*Xpb7fD& z7g>-faB(=No0dZK@gj0|p~z9zBy5H%VfbEJ5|PhS{fLzP2Z$L+L|c(?#tIJM6q+RS6HCQx!{*gwjw#D`WhNc%4s+b3NW-8KeH$QT_p~K5AsWZ$i4Gk` zloO%jt2Kb2!YuY1)2oJ3Po}?yw1WvM?Ev`_M$rmySF9VEM_R8{1GZQ%q#N45`%gYt z7Z4Q%|El?ze+Yyx$^|PmP#)}97oKQ-<#spg)kf@>XdhY`eSMM8VWva6PbhW9ZUD>q zTef}LWaZk;P=e~sEVZng-5P?({;nTA#5&`4G@himJdaeq-g`PM5csD zDeA+~QbYhyip~Ltkjs9$G!GxHTb{t2kL?RffWPn2(mGFZgB~JJw#*Euht6@DFei9! zsKmEE-u}H%e$9Bi6~S7b^Q9ZFFq--JzSaKVdckt=WV_#%^!{P~ys zQKFe$DBay~g~%Dr%?ahosTTrQBviqYTI2zg7ajBVZNn$qXv3Y6u#cL6xLO{st7jx! z`E`wPlJ%;lm z#b@0hy2I}a`niwuc;HLmV_fQkm7Bzt@{9H~3G%IaZgiJu@$>VA?JD!wAm?_x`gwhR z{=Ba4_E~wq#Bk`o%YB0V+{f~s+V?&AcDqi>KC1oyj32{nxnq(MA-HCqX`YK3s96aT zJ|dv)cEnr^Xs$!^0M=793u|cd;U$gzJRxQ5KSJ;HmA{+fXNwNBtLmr;7uqL3X|+6{ zrk3=UQQuj9%=3XBOph1`7s9?S#4Pt+gl%Zv%a*=_w7Gt?v{-2N+j2ev`HGL%JB*tH z&4v2eDIQ<9%c2Htv_EXubvI-^5%Tr+U_Ds>&iU?qu3F!|CJ@~%q{;hsR9`XpSQRM~ zvX}n6iW9TC-av)rUkLX0c~jp4elgVNs>mD7mREH2^1GaMz4?Au^K&_OjN^eHe+Nq* zu9rjfeIllc6meZ|;BPPf)JHR$KHm+}MIWvr-N?s2WQ;z3tuKd|*K=w&2%F%i`aO5v zSK&jx{+96HrIy3SJ!sZ^((vjpT6(S7C;AD&-N`pHfmD8bC*Kyyv2;63)F8G-Sr$!{ zHHUIH=7UDjY0g_Ti55vXEl624LX85}bR&I;N<033qv7;)wIp;1Cfk3apEa{qQ$yiN z2=q-_-nX~k{|)IINZSV90qu+4V#3h(N%Q`#dA&oJy@jNmU)?`=6VOe2dJX&mQ9MMM zYJc_#QJfG&S#J-BAk_*r4?)iO4n|*NJ|9AE2I$+D^W4j7c zX8N;ePQkjJW+bJ)vxn$Kp9q69i%3elR3l=&+odV>jR zoI&UF7}Rk?V?e`Tx*xA+`kj>P>5%FTZRyh@VQ%(4Bu(_Lnjr=r{nSS-(X$wM@!E%s zSQj7{0}Wc&kFH0(YuPpPg0SIZ^}MN#pNmhGe8|)uxsl4a{)pn%bqYv~5$x%PETP@E zs?pAP^_x;2Z$xS})nAW)m{DnV zWq=R(2dNj+A#LmKt)Gt4X?++S?c=K!5h#DVvLZ-1lpCto1o?vHA?oew6HA99w+>l4^457hAzzbq z(A_0H)N*Da^u4}?mNw|0?alKN$hKMp6ydui439vj@QzO#?CoTqwk>Kmu>-`O|9G=oVu?~PM` zzl&JjK~(kmQ#ADJdDg#=2LvhcN0`VuAIMngB_C{e(+W!&Xv zy?vaQ!~DIquJ&(S7r$X0e|sO^t%Gm!hg;;H^ydi)or+ zdONs%gWGpU2NKKY4wYUDeP=U9)ok%bHnMCSqMa3KZitRVA8{aR*xVhGPT%e|4$XRY zWX8T&Ku$yIL&PZ2$-zv!sA%X<@o9$7RuOU+oy^FZ>iKCjbd)FcZSVIrUY614F`Pr^ z&)3~ed$hhSh)58yj=M9Q4cwT0)?GC8;OJ#UwqDHg1#$0ra>KN@z@`m{f zH_ThXmf;CJ;?d^K8vkE8CG!Zk%jvi73jN*b2_gJ6-(&hb4o=$%L2jXcgh>Ca^v!m} zxI#b>b?~R-#_9OhIyHE&QQ3Yn&&BNbO@x5s5KOy6G(SWvWnaFFNKW$u&+Xy))BDtp z@BE(SInv%G#-%>@_ZLMY=Q#AIMl=dq-cw|Iuz;vVpzFuG^Hh&4b>k)Fbpc@Ax_yfe z`uxAM@A5r+DzEJO!#=mB-9JFer9GzYGMh2k?Z@MHPRez52SfPnjZf&M@D?-B>ErZw zAFG!mQbF$vsT0kMm)d1bx(@Vr)2H&V-giGySZMwDIeu~t7uD;JeH7OZN^kaYAUzs- z7vvhi6A%s1tzL=kljc4A^=8;gC-YjdUSgbNeU$aq%-4o--Z&q=U#|U=fjj->3f-s8 ztX!xMh+=JNH!|xqW0rk~^>mj-fcX>qb1HA68FZ%^cB#2k1DC|iisQR4%bM~Bs&_5! zm$x^{Sy>+ap8ec%bSUyg{tdsc(x1IIw^zp5pZomm&Et&Y;O{4YSk&$f?alHw@f&=9 zU!Miy{_j{XG~S!nJI}8MfZ@E=a(H~|`r&oe2dMqO<-J?3Z9Q`xR@~p8!0v(l9>@Xg zzE zajrGzGxwW~9nbzg#Xd9*kbinMUj5U$5__WcB|oLvk^d?0NbR+>>*&?r$8AJE7GFy{ zzvkc`mQRpjHBMwc_XtPn4I(%qg)fvEVV&rQWe|c~WsCUoE#zFrPotZCzRD#hqgT@Z zFrYhoJJkzkcWq-gPM^0q+g7au4nERx-wCOfCj}MXjt_LwJYKW&S@rmYp{}RLo$2|v zAz%Nzm-1OZiPwAh;7Iu7All!->A*+*cE)Rb^9_EPZy&7Y=N0mF_OkOom0WARo!4cb z)vA-J`ozlj>+Q4pdxz&$(0}Q-U+d!!>%G{KQ{f!er`#q^-*Bbh5K`Ty9ytT|9`9Ff z2hW^qFg=oLk`u0JD2JLIt={=pmoR>V2oC9)BcdEI+!lQ;vP55&$WQMb%>%4Amb^Cf z;*WFGN_V8^xQ%~*T5@_lFjp=)Rq`h7B|o_pDfZjMe%X*>=HThpq})En+t!pb)%wGp z=li?~UY;cGhbjbEszloK}ZL3dpLFMfWUU`TGc1AibN{FR(^#~}Uc5$24$d7j_% z9k;)uXMMLhPw_9~#HYL^EjjMuyREn2ck+ju2y}qAlra}P*6AncbIK3AQhG1@4)xPr1mSBgSLkodTC`%NTa_+DB|2*}eL zq^ZtUs` zadRx-oso{8prMM;B#>$R22xXHByp@5=x1*FR=LxBlkl3j(l7+nPrGj{X?wysw@x z_JC?1fB4V;{@Tu1$p3ekoWA5V~CfXfRjc3 z(B9`sg6TgvcnjP##(cVePVHSV<1Ad$3Bqs8O-;U$P7r#acA-D5kDC{=N%OSn3*bjL zAL3)vKk`$_3Bl45fVjQY9&h2Rymfz)Ka*S2`RPfU_OE_Dk0<&u>%Yqh%ZGMSmJ%Vi z5kX$uPG;qubH)}iR1wshw~HGYQ!FHfd{R9;mfF$H)2)(Qqo75VndJYVTuuv|*Ds-e zeu;=TV3Gg3*XA3d-$y01Z)m@$x7L>3?ADDsi*y1c>%`f!n+F`rX(V550u*15W z2jNCj5#|I-7=&_(#85Pn~B;g{BoV(c|(XW<6i_+T=?{lB{q+Y6ce`Ct4Mb%^s!(6_Lk5c{Q*4vLJWJF)Tz<~z;P z$iu$@7$aP@s18qoQx#FnZbC&j7E#;)X-@*9zP-T zDF?Zr2mE(Hu5P|v#-DoNJtOf+@}QuI<)Nip2RT@g&CeiE^~;X0xfyKwXHe#m=~y3j zQ#ChovYoxDo%5rt|a1MybGXh@a$KNt!{GkXK z=h=T8JF8cFlGAN1>m@dFPV2=Y2D4sTkp1Y1iFK~dUdd^g@zVMgBx{^9JJC&lxUS2H zUy%%t!^fRQPMb}@%Ik=j4x3q-TV4IQ_iVe=s}Zo)N80tLe1z>cOnsV1brNFd>*ThA zpS<~ou^TVGMg) z38BHF2^sHc$%yZzpSFy6(0}yz{?6Qt|NJliN}(QURQuZ^VMK%mNo%fE+h+ymlZg_c z8uoBL%74kPbdc;lAyY-pj1*jdvmc?i?T0gmpk?TDI&pX&@;uou_J?u;#2P3^^fv+H z1GUfi4;^`)TCEZ?b1(oaSTY z<&kzqwD(AP$t|C3F>-w`=j+=q{SJOV7PA*&sO5N(v$NgSRwMx3Gn*Y7FfdQcJqCFk_r2=`}|^|X&EOV<(5%Q;{@=N$8Kt@;5!&%)`_{2bz1yLCn~5)7v2 z1!6+vkM)w?`1v_Ex_{p1IBPtIbfZL=k?QRba>zGfb=~CLyzRW6&GYWWBcK%fZDzly z#*1oPp5JlvReo;#soG&g>i?abuibpddIo-f`-lJ8$Dh?_;3*WXLkXDU7AU@fV{=tO^I0^eO0P+5bRzy8ei38}t;P!%cKlaX(nZ$j2D`j7tJ---YCFaJHv zHzPa!G}V4_5>X;kwddJjv}b;`g^iSNa=e0Mk$*MUu>9?Ey=!wvgr3%MhhWI|!<%au zK8X)Oj$OwUB#qAlo+H9;h@fG$yK$LcJ>Jjr={S)x$N(q*3VI3o6iCUgZ?b#rdpa1bc?PYJmEW>;j_HxR+nP2W5@9w7dQ|_s3TVE)_>A!@0q|rz&>7_EJ>oDh@ ze#}p6L94(9C5z@w(bJ{EtQE>Ur~(O*}UJ`Are; za74N3@+Qy0h^8%7zU8KC+)Uce#6OZh{J;P7Kk1vj*Kdx7%gW`~KKksWEfP2c3*T0L z|E~SZ%5yHgE-7++XS@n2o6RU~O0a1Vl6)}!xqk@))IfKe{xJW;CZF1;Y2-~YBjA0G z7x{Oa*nZQNO%(dgn>F)K^OH?b`)RFcukw=m)W2-v8bmbPzw{^au$wXQlQ;K-^rfD= zV`0~i@tBP^P47Wz(*XacNG@-d%ireuy6G;ruA99_u7~l;+%E(JGTl>ucboOvt?NzO zZzHt-xXs&~{3%~81u=C$A1+dvJ3Ic}C?_>bLZg63=th4}bTr{(&``455hKnyxtzP` zb*X3jdggyBPyL+6$J|~)SLKub$yjJH*7I{lSIu*7#wj=FBj_;ov|rdo!EQGCo%Vrt zDaVFQ?Je6mF5ZZ|c!yo`GFtXJHs2(#DCDxq@#-rv&UyOvyK!PD-zmrJ4`>{F9E<(4 zbIbgxU3z~KvWZ}a$Nti|YF9tTsOsIkCtTIV03ZNK zL_t(Pbb+Qd(lj})cXEb`ycEeCk>1--osa1({W>;JG$_{r)dbneV~w|H%i%>`2$ zq|64o*~x_?bNs9bQOD1ltl6BwKXA|p8O|j~)seBZ{&i>Z_l>f@Hp?h*4 zZT5l4b49)oQAmWrx5I)FP6R8_MOqs~MFGEtY_=u* zcW6Kyw{#S=M=;X9G2Mh8rQL}ITfaP)@hGP}_zf-LoMeLiLp5!5~~zRtmKdmWX`9pP|k-r<~V1C$oXvP zz?@Gx8)3|8#%64@!`Cn0zu|sA-mm+q9C$dSX1#%VEE>7^jpfZz4p z^hjgl(U-ZX!m>Y2*Riv83b50e*_%r3+?vYZHAf{SOmWdjvrZnuh4u|I$+gA}+?SyT z^$vN8R;ORKRWYnJ8s*ho_Ie8eXv_G1h%x$MJerj;#Qk;2KTRdV`IB9L(n*R3$=-C0qqHu1x1p!KCy-p3C4IjaEP8?h~+^ zSN5-)Y}U_<6A6EzniA6$Vg=4Ds_e0_dz9|qoTO5RJo1TpK&uFZOY<3jwDgd)blVN_ zQnf8VsnfPs;^QA7b_I71?$>>-lfeqw%Y3LvQOY^axY({BH###sUfHsJ;XM6j43P}uF0`cLHZH7q#yCKA5`l?SQZ@NsDu(8Bjwc|hKqC(`ewUBi;~;E=xn$pQX^vh z?|UHkWC0iM@CCgC0DPGNhn2an6F=Y5@ZR7E{l%`Z`17GE&?^^V4Cll63*rd3-2Be} z02GhA5W0jDZrESeo|kfcin?nWgq1YndnuN9vTHE(<1rxX)ti@>e@j{j%C%Qd`xd)p zUv^v1wcsD&;fdTbZ7khujt#BsEz42JN*i&4M~8os9N-SGKI;moZLdzY#>8YFpE1aX zs=`N;ZHuT+sSaer08-9}`j_g``;nT!^l|rZ>55f43!?cNNeWukXQr>|kD8)kCI8yy zdL~`kjl=QgTX7Vq)Y8wPFUb&AkIa1KQP>a^5O5ZpZ;+}2t zMJykyR0}uvpZ!B$!E<%qwRSwulD(NvnoSXZdh|Xz@i)859^PfG_&WYQtoGfyXxkJA zddqnJaxdi5wBboBD#~4C(1-PZ{#(U0EZzbQ|aPo~W3p`)c|V37_K#u4$3?I&*h$TzFe- z>a&pY+OuB)0F{^BAIF4Xoxb^jx4Pkh`UZoKaOqpTTOGDRYJ~9~5m}36g;9Q2kx$UM zB7b}Ljb!_`=f02*%*}CVeT+zR;;hcwj+aBvpb@OCo<6myriEZVCL*;QpKx5dt5HzJNknHf0T7Nb&_ zH&hYa0cX))h$|^R9C5`4`{uDq5IhxoLKjL@-cgRkz zO^K0`*XlHK612v}9$eI%qW28)k-P~pq!GfJceRx{P_CJ;XG2wU0&8h=}(-l|ILmGWlsQ8?+(fIeia--H>NoRwQt5i%q&m-VrG^N;_f>3QiF)!n&? zE*}g9Mu92Yr^4=t4#^lL{EN$(*^M5s;s3WM@TbIbnN(5+;~^ksl-xUnr<3M?y#Hc1 z6YK+aL_-NSZe$CYY)i|`~5Gn z6p3#XFAa2T_(0D2p@@I#*>S6zCK=H-wBEZA1pz1g%}rDo0`HPDJq98&_^3IG3G#TQ z&8WTKfQt*&iC8l!_|w{Jj2Ay#;`Hw7w=~#~gHP#&t7&rmqy4z5JyAYueTJoJfy3t? zA_r%srq#Z0LE`i4HvT9-Jem-ambvz}a=tozxF^)*k!W>Y3|J%>)1{iSEpl1fv*AJh z?7^65{~*2CkWPd#Hj8=e_h_-zaBF z!s-U!3V4q$)e)vGyR|1>1eX*~P{ZoC_O8Tek9+N_0Qh?m`l_{JQyd#ntV+vI8k~Cy zNXw^Apc3e8F5~a{@RjZz&+H+^!wzP-biFxK*YXnHDwUuM{4Ji-T}XNA(b4PLMAO0r z)SMjlcx}w*LUv|jdvfogudsb}9sz zYg}Q-s^sT)Mh)`go{ef2Zk|6lUxY}g(T#={C%2sm*j~E>)L0oP{oB2A%$Jq5I|MlX zU%kKs{wVF98xYe^S>0DnW-sO+hc)#|i3DULYfZMC-IHJPC<%2gg@w0in4TB5WFALY zx?7wIjeLyHTD6sA_vTJHJYGH8votVb zu6z+7^UoQA)szL8(7puAqgVDtw(&k^TV{gahG&P1ojX(PoUvA^W_!J zt4^unF-C6ZSFN;O%^1Eq_jz2elOy!oOZ(Ta=87LZce-j#q*nE2@fr1UXS~McLZmUm zvcJE~es7c+EdJ@@D(y0lWwZ=)&8${Ph5Bl)tZ0Aw@a$&T1(^x~7fyIbZ{Rk!>ha7|`mS^{1%y%?WUX1O1uS3-rNyH{%Dx zhEx5aiR|P@%TF$#)&Z3Kf3S#PK4$Cj*Qe}v0H?kBFqrn;s6G`+k zjkj4I+-CU`qZ8-BzXjq9!FKe!EeD4n{A=_!uShM%G4mMm=E`C&dgkBpw=$k1TAJhd zl&!|zAVj8P%{4~`ZeIbH4hXOvr2jdN&F|JHpG<8_nr~KmOWBhLU;Ftd$99lpH-@<; zBN+YT!6~S}9@&gP%(G^h__EN$qd_B!82ctOatA+rfB3lf1@EV$QwtZtIqUpxUa}Zd z<(c{TpNdJs_me~){OI`v`yMujFdm(N3oOLVkXzQTBm5jwa%MrH36B&4P?|D4_kU6+|=!9*xkq>9>?H%U9^F76WWIP zl+j*LzW=z_te~>c)He^dn0)*~+W>zb_bEy#EZwJR>-d(PM)~sU;m)a^o%9Kw-Pxk{ z+|PN1+|$8fxJzZ-YrEC4%x;aCsuve-wd~4_N$oMno0CPQN#K>{2@U7;o&=YCi^ z9dK0Y9pQH(vG;m#`O~NEWjWQe+K~S; zLvd~O2BYW^g%rKuT7={wM{~(6VLP5*66+m$XbS5=iel%`@t7voEwW)ybgZgnHT-$WU0dgUWF>v|ll9H`+|uo)%%11xheVIqU>VYypw|f(?-xHcEZI~wd207WMy+V} z({@mc4ED2Sqm>-MAC7wBhEp-hzIuWu`u&YBq`FinC;d9Ae)Ab{7#tVji}(5qEUNPT zwu&n8T$J58^?qwia^N%N;z$Rs2v%QpDthd7lk#JUH@>mi_m6G8?rs_2a#^o+4H9RB z!slvCm!kzNFlk%(anTFuIa+;YhgS|K1WwvXpLfa34$|-EkWlMbEBAMh>V{>%WBhda z0yyEw`KGZj9y2)eJ2aPW((T#Du<7T+)+$D1{by&{q6w z`1x8>-m>AOv(V)Pi!&Cm>0k>##qT+;OcYovX%h{=)q;4=y>S@WXW3 zc&c}O(8bti_Dc}29fau>L>nD|Z^PcKh=QxkL( z+6|bI8$Vf;InlanaLKk{{zu37ppzcW=M?k34v=+6GwH)w)$Zh5!$AT}1$K){%k1Q{ z!5$IByP|t@U9ItdEu18g88L4lYNW=}Aya}W0*+)YQHd#XB<5p)) z_%ku(3NXUF1Z9vcL%ZTcHDi?0-#<%l-|ty4thv^mOS3bjk;kF^@MNa_9Gs;_E@oYY zqO?JSlh_iFR6q@9LfxZz_x5w(YRi;0-@4jECB!fAUORZ^8+%ASKE$DcF9ZL*6jCr2 zXcDluYd?Hn<}~yS?3bxs;>*;rHa*Lm()X_wAlaX+i=H8!IG@1e)FyNn?|EyAK;SHpCN&PL1J0COp-%7*;=u!7PnWk4nG*^vaEcwrzj0C=zHLCsQ@gm5E9dyrrbcz|h@5>wc#Yyx5LJ8%cbnDFase6F2Olf7=r}?>sG0mBe}^$ zHZ{?2aD9KNecVWopJ@m*n0FDxETKA#{C z1nzB~+_TuPV3;&CzS7AATCDVB?ig_S8}VMg?B8_1Q8Wg&Qw-l4-c%z9VT1^ug@n% ze7>J%xP!U`;)i_R29FY5oCY_bH6Du zJ1J|t^@=~L`dZ0IXOJU7?8$tgqT6lW=28Uu!5_BRjh~J29w1=IuBwo1Ray?{w&~wp z*!tDrKt7sTP??}0!S&m2FyYmngFHs#JWT=U%7W+Rb-@)nFVHjPqak7pQS;v4g$O+%!Q;;H{8xR zcSPl4%WdA>m{`b#T+~2Y!JQd}TD>2dk4xFlR4`)&&QD3bz_y8vjv0|I2m=kB6U8*8 zrvBrfR%*`^cotxd5uDcLa4q5@gF1 zjs5^K>m?S#SCFrKMD`DFLF0dOYf2^EcOomtmD@}Edzv6N@5Js=Pdy)LTp8OP^S@v= zfoUGK(Qgqn&4A$mPHol8W2<)fvQR?c&bqsuAjuAMTSll7m9wsB0Fpi6;0e%13Tj>S zk(Mr?cY7f`ne2>Vz)Q`B){q@IeFALIF=6+}9sjQ875pRVGq$6^h6$I*p`(DIo3f^4D@pBqaykNEe>>&3DP zDq3z*zChgwh27EMIink>F3xsdJDJ!h>1l`7y)%hv)SYnVZPw4eN&Ux>N7n_K!D8P4 zgujvkK~j#Xklw{J;9iqeL=lof73V+9)|xhY4z_hTppT4hK*Qp=%*OmPdD`YhZx2{Q zA7jOmXMDe(@JF8mSB+cT%ir=!yzC1ik`Q3TXVfld&!TwU^A&O+>jP3R`k&{cqVXp( z5JPdf#>Y~{9`chVW8rU&PXM$!Ow*Y|Mnn92N!Q?(suJL-{J;yNM_RApO_7MWc5*8UmaE1a#7rr?N|A#pSzbtF;})Feud_$j zyGKQOIh8tO&>~MRefs+%JTg4qQ+t3ztAv*bIs}1xUyeb&gzdM^`nQpZ(jO{vh)0O&cQ+YC zqtc;t;A7Ms35^vX>U;+AoWk46RoUg^(1b35L!a|ro1J1xP2OiMG1Bw1v*Q56|L*Z4 zlY4(n8$~fL=RrT~M8fk`dFF-w^(?p0y9~6&Mw%+1MXpU1ZGXJBCdh zKkH0yh96H6Vc4&KY@-H@qx+ok1A>#tUHnIMv1rHINH1hU{WkAJmEodqj($YKt%X-P zBdZFBkMx_~PYu9}SiJZs0S`7E1&^fSewA0xt@{BH@{i2;j&H+wK7n=)TT|>l8EFW5 z%c1S)0|5^|G#X{5GC!{`q*Qi}Q`47o=;)0qLwzA_#gEpS{{&P)((4MRbi}i=D`xBz zR^#3*GM=%1FMLoz$y6fSW@@EZ)p@<#SG=3z+$m@~h{x0{@N8hVA@3)uQxZ1r@C`C^ z8B9amz3IXk9MPtcRc!QA$f?G_S@x(g$9l;nScLsL@*J3IKC^Qyf*}7R?kR-sn&LnI zQcQd-q}A^Pu-BpL^eO#{VLi&KQZI`k`H~5(0nxkG#@VrfzY=O2+ctxOo-n;+%QamZ zL0=@&`*JL0PGxZ1K8t8;>bv2m+Z9`6kZnCo#eggOq=Zr%rNMvgZXOY`c|!L9(6w-x zYuE1m*4><+5=;#rZtkQ5Z_}h3&EOIAm}yAF|8D^(gFfRoO@(t?ldn{!BfYr@iMOQn z2b6B2EvS`{V{et#iG;i z1kZ_fC6Q4D)vd86YQWZ7C!=)Ohby&+K z^X47xN$g~n<51NM=L}%-V84!zr|;QUIuUC?Kfj4!q&|?}4I#Jxd5`)%&stF7IZ$}^ zoUXwb^V0wZgiMgcywh2pAeIdpH&w^DSQ`fR%=)u$Gft{{w^#-D`G+5Cz!QINe*zz1 zv0~I>XlFCyQL}O#h;M}`iSD2&6B*d% z{djU$8+C#t%Kn3=?tK_G{2>p`^{b%yrX{a#>^0p-GPUH+vEp(|7Ef0#_wzNp2*K^X zuR80befS7rrFhPJo`-e`y=s0muK5D)t*kCi-1qz$4!E;{pLYMf+mikEIiA9OuPFIq z0|3p74V+*)`EA9&axc*M3_6sPQ7Ho)Md!Q@dk?+K^?;de$Bem!{EeKzJR=8v6?Vg$ zTC9t)_Ya;RTm!+-xPBNDu>1{b6bMDk?w$<_C`Wf;!e+nKcyysJ4;k)%MNdj>5vRnZ zi4OICm=Vd7#efH2HqtRK$mMGHD-Z`kC25~vh@Uhlj5Qz&wb$6&wG0Prn`!xTtSoGxiXIo^#P3@tF6a}lgY>Rwf?-_ zjcQVB?4aI9s8fI9M{N0O!{H;x0+Ct2Vd+`fY)=-o1S)d-vg0;FADzSgTk0F`zOgkZp203Kd5&}fqI$;%CN;UsX zjpgr^|IMxf4J~&DUvS&ZXdAT_M}LYBwZ_p?cdyo5qGzJ7nyyJM*t50JU#)d(IzsLR z(GUAThZfB1gnHH>NdKh!eeWQd*<*T4o<}$b4UX8b{3<82lCOVGNQb5kbqn9n6K;4#~0L+e*oPXy#rPoC-&I};N1mp_UeE@eX7_)L@A+D^%G zd%y>3VE;w+|H5R@k)og5G5KzD{eBV_0 z>HSx>W@YfNvrGR?)S|eN38WFa5F_?tQnk zqn1%TO1m*jbOCt@_c!KdkdZq*7(8EIF}RjmDP04-=No&(-2tM~OEL7v@un?F3nI)? znvheHjMWGtd`-nE_zSu@=kv>EY+#4ZSHcjz03VTkKcux5f}gUAx6t7H3Z)Ped!+CU zt0JQgD~%bYdJj;47?zp_F>U{DsqQKeT9etXs~UZ+OI5df-e=z)VdhruRV{yQr_N$7 zB3y&&Dbd-URTB!|N{1Lvw+0%~P;e`ynK)EY8m3(j!o6n#j*q4Eg{W}aN?AoeJ-PNQ zQ?o5!@Mk1kk3HzDFU`SB9i_A?>46xm*YQG-=JQHctru=Y?EF%jmidGFWA?tf`!7ON z`c0#<=%_D2-K2uQrPpN7*!0z4tvOT6uk@ZZ`oaroK7H70ZR5yL#jcks?ZfZB$J@7t z|HE`lXfxhAx7+i;&TLCi)N%senIW!GYPZUW9bv-fiVOz7SG{X~Fbdws zZwl>okJ$m9$Qn;6rs{}-o?!gn-@QTj3J~I9m^=yS%PnubFlyFm+_AXl(}ev7;7mMO z*;lf6?(#}_Yh6%6?$0#A=jEbbM)AA-!c3cWm6ZF#s0ch%*aqp-nhJrQ-Zd2eick#0 zZ@Mqv%1>sYrl5a^fFo7CrIekGq18NJ%`%<`9+tTrr0l`q+*6D^Is9xaIeqpocrR$M zxq$g?w?^B_65P|h<{GBvOWd6yc?V7A%$dcvb_)dY1Lt#{)G|C$PYOD{Su4{SY;4r( zw#zkr7rxC#P)0`yJqhxY!?2yI%N~FRaL`01akkOdlGjIqL_%6 z?gtIBKc>PALRdpV-mmp<38y2G(ixWnz}NT#9!6GqhJRi&ck(Ylm)~8!&B6wY7{?y0 z#69Jd$o0^@d%+K8*<#pL#tvgT7s(@rDaR5hg`2NfQr6)pcbl_GpJDvrkBxS+W#Ze` zKIUJTmT>=em06Y(-S$ETLK3mkhK&}IZ(wpmPw8FmM-3)W66ik*d8h4!q(nk|fMBG| zhNqV6?dE$ktM0atb`{!ilVw5|9}nD(HIC)6)sRQYni#d1<<9e#WfQP(aUt;ygI~`s zm!dLpae$w}$vH$s8)sYJbMBmZ877D3-+2>R74K+89cD4OUDv@^P{54pbVHUnU z$-|kQgD)i+KJjYs862K|K@s2h(zt4ZP=R&5v5_J;9NQo#(=_(4(}^Vu-T(s+6U2Nf zkk;zyw?FC$*JAe_xPZ&?BJ8Pr=3qy|zF*V8080ssAo}{ZgDhD)CuI^OP&zs8`0aCJ z8YwqRWt&Ao%Y4yPujz%-to%a`?0?wTPh{$&JK`dhc#u-Plq0&PMEihWL2|I%0i1u4 z2pfHtUitJWh>1VwY=3n$6P4%}S2tN|ev_xpGm~B&`R|X3I`oN&;UcgtcTyI_?_LAuArsHCNi1PzioQM1Sao_nBfN7l z6?GHdJK&c>E=`2tK)xGyZ>oxTy{9jHzWE4u^Y%k}ZJ;tTX4JW;u5k7W&xwLv9%g_) zYl5oMavc1oBAppx1Yh)$uRw<&lzPw&2@fw}ScHQUFQ&l_Lqwy9JqY&r&ubfIW|~Vk zIt)gi;}R#JQp5}E8hV49#O&HCiUH;~JwkiIWhI?y9)TvCMWCRo606Pa799qT{R?;( z_(L$Z@_;@;j2~RMLReR0Q7iF}_uLutlVA-AKauI4u-?Ap?7I2Bg9?$6S5?l; z6uWrXL%3p6u{@9KYBBzoSIYBKXz(>eRFwUt&xn`y9|4AZ=dRw;ej+ zlUn-?_T}l)Duz|2b$t3T|IYz7YA(^BY9e2b)|%Hn7=lb5!(0!yL|Sl#OtUSf%SEe4 z3*phR2i~Ga<=#EN>dT5 z9C3(kygI5x@bn5gvdXA~v^)afO@$*LI%uCt-v$J#>@E614g``EX*_NcN6%L6F5h78 zX|%GdwpW8v{h_z!cwe^jE7$oTdm^^09GAV3w%rU)UGQyhd(Q4Ya7X)#1aC8 zfnmzM(NzGl9e=s73H67lMRj>0#bbh8ZVMOsmI%;8K+O+Ot*hiGov2mLEOYd-@a zW8PAR_80E9UzF=E3cB8&ou|F9@}JD;LLqnDiuWKR;x8h=E@0#un$+$%4qjK;w4(r5 z?B>NE!D9l<u2+x9%siFfEb&Ukk-;;nURgtb6=0x(?hQ&*4fI!N zECH)*GW}j64jPf;mf7u(i#BmWsiGu{LFxfn{KC0qN2;mINd4OAmL3 zvgz8xefWKkNz|O)5Bx0gfgo;XuQ`oll%Ol4T9K$t4`T*()PUqent{jOXE!dVGTyEN zh7591dl^(YuWY)*F4pg6a>(nT0V#1_;EG(K+KJ zsOqHCpJYjL9DLF193#~F7krpsZ7Ww(vi!uPl_G0uWo{3^uJx>RG@8~u23S@7MSP#j z>o*1Efv2{f2vm}yq8bmGEm5O~zZ($t3GO=G{l`5LfAN}y8`fO3$A`~e6eCD6bDS9i zg$Xe`XPMm}d{+dRM&M14EQ5}JRpMZMg3fg940^`Pvozn(oiA~pboFStKuFiTI^)zp ziQZkk8cQChIS0rFh2H9~ky?>12tO{ew4rjb4RX(>=wh z3Ax(#!>AlcnY-oB>!muBi?-mEz-!2FGrtbeRlv!J89e`VWlaX<`=QRF^)uG=rf=Q$&xD81$KmO%guZrA}c_wzpkO+g6YlW;X z{HeJ2>h%vyaNB9zE|&4w5?isHm>kD{@i9(=Zbo+u;N1uA)Pj!ID7|Ndt6X@pU6lBe z`mRQ_ne>W3f2FB6e8pmuaNVJKyR)in!J6r-HaEa6Fg?4w%`qGBzFoBuZZC?g47Qvf z)S22}xa_CsY8^(Ti0|CH)RwLlTb_>W|B#Ec6%)46bS#mO93KjuZ$Nm#(eFV$cwmnYmF z2FhP4sNt&8b-eiCFio_k$ChDHuqkj{!VU2e9)msyKUW>RZdiPq|5&Tm>hR^+4C%AL z+`8F)L{u#I@d8mjUjDg-1eceb3I(}bIw!iiBN^dEUWN-|=l^r)3RgTA@cVK^S0kC< zX$@AAD*mm*aa@C|uUF=wcy!Zaackw4U_+3>53x-k?sOzb*L@BeS20rQ{xXFV^znUCCk6|IEb4;FnMty#Rx#6PX-z0q4575tZ1n1*;PTir8l zOFx|-uR6GQtCVKU8xT~jx9VjCHXQZgW$4gPXIR%syT%k=6xN>#`hJTPi@q<_i7GmXmAuBlqkH15ZcC73(!U znP-EVCh=eR_|>G(&>tc}=0ni}*khXY⩽6YYO5dF-@r-QBWc^gkv7L!^KCfYl4^ z#xIvENc~+Z`j==P+X>mZ{e^S>p(^a^2NBsa-=(e964B)YePuICCxkZY^7KYoTK%l4bs7ZGEQWIgpm;c{kN&NNBOi0q~*66GWS>JWLnwmQ!(uDP^Ir zyIZbx8VW{khftHMMxe-U;NJC`Nh&i9gfUo2P-~cEt(J`Cqkjdb-Rv^zBDF6AIDH0N zOT0h24`vPg++k=Av&RcPJzujI!;55Ot{+dS zn(5IUy%KtZ%VQcFATZj#v06y+z8P}I=Hzm4Lsr%P@}KNm*jHqpL;%P7*_-dwToaXY zC6!Lw^Z>%9z;rQtlPtRhA!p>Xv*=Rz!~OaMt3iTW6#s8^`+dQT_^L4rI@_17WkUDS z5+608!EbPiK`5SUNAIKNdW{b7Z9NhG9BmS$v03caZjD_%AU$RfR@!sTrxRIH#%;`L z{oEH!u2&iws6G3>FK@qkbXB>@XyAp7?a<_1j)i#}R^gV@i zn4vX6?SNToz-*n}e)7tntwu@~_ zaIt50&ifo>yB@GUdDMN?MaASgP{$||jq~Bx8Eq-EWMCXh+dnKEr{f}8tCx?E37enH zn$wf{1C#WRG7WY4zFmupS?cW-wNGgga$jGdSydm1tx&Hq!#@CSO!FAfhU#@RPv3~Z zbjDT-zGSztpXS~Q0(=kQJy7Kg&UoP)ZQ8x8LX6A0#sL9_XdV7^1ExROOAoFeQ85vC z(G=H)Ft$Y4fa@ieoN3#z0@Er=pbeY5oy;Y=6{s~Jh+D9@=M*Lzd*asS8{m8x%au8B z8!9#whjP~gIvD}AjgKvaKlH4=7cN~Vjk;N*(Ce?uIlTLx<(L8UzmV|}v)i6Tf2&%V z1-+soct7*}=3B}GQVzciIVS5DOZNE$K*u0_Gf{}3oHa118!Iqs$^2{~^-ZKYXeDb4 zewr*X#+xr@Ww;TiR^pZQc$~;e&G}O1?Oq zP?Q=UK~c~u0t;NaKm69~yA!7tWM2?~PT^U16!UZF4u9B)xh1j#%p!iTlK=AM)+tROXZ3d5+Acl!n!^{@WUOM_%CVz_L z{o+H7k?NO5`tjQ;ylB(}*WhsU?Of>%jdh~#;3Ke_Lno(1xr6jr97~fkDEi&a_P(#c zAgoc{1wBF2GVN9lnKhZ`GXEEd6d zz5zYp2ed&iWHH8#!OWVrQIAe2U1yP#QOx?wCc>e%I2vuI;YNZ@w$qz)gt6U*&3ezE zh<>n8b+A}-iCtWZflJG4S$}>r9cAoo&W_uvY%Qi&U!rG(l47O|t`$~lk_si-&YKhk zN9V=Oi$bfD8~41#*(~cqgQbfk2Ur<$SFWH^djGs_mQOPdNmKOtt+W|Y+PIat@d4o=+j-B<>#I2+VJg7|c%A~Rk@xZmC@SawX7dcX$ zJrMOMQ(|V)%>6ecefMTfovGEVSXYdiLy-^8^7Dz+b<2t_>KH3l97MgVH8rI#&7E93 zYjinmA_pO-nf9*B%X~p9WAg#h-{>{1j;R!KO#ufQFRqi0;$(#P1C^;Bcfi9YUqC$V zn?vNrMsSValf5pM@(o(?0&T%=+$uYPZV<5pq6st==sc)^hJw ziHLTB`$kL%-{o!v#G|$G{2Vr~umDLgs{NtmEqb{(LCtlwhjL~;H?5tiM-ZhG< zQE_{gxK_o-9_vkRlBWZ~sc`g9w)-)kj@Gdvk<;R2yB#le)+6?__qbCA+0r-F4m5WUCt66jnUQUVz9^v|K z)`H8@F*_LX#I385x9N;Hggm-t-s^zz;`oRa`cZ%l?Bo4147ohC<(GaqUio_w><;^W z-20eVyf3EY*TRDCV-u}J@#7-Oq(@an%afzWy|wd}En0*#m+P)J)RhV#ddwSs9G~KL zb$Ws1z42q;N5AW3V?RUy9!&FY6T#`+DlSGXdY_S9y`|ZEg*Gr^w8tIg7OsNfR(jz+ zFOOQ^!=bOX9XlqpX=bSRux3uC=h5CZUuO-d&}p!-w$t{6Luc-2&wsWxjeHSYW{A_r z9o_3^D!j)s)uf7{bu?pf)Ty>-L^{Jzb|Yw4JT_>wq$^; z6i!csEK}a~hg{_HS}|?zFe%G=VeZ9ijOmiPBy2L-Ofn-?W1ti2J-|Ff_2%GE>z$MP z;msO`tbSjdUza<9;KTF!+<#LEwX^l>LLrxnVoj-$y6R0Wx_QJj?9`(uCk`ZEl=K4c z$gB1<(?F+~I(snx{{kr&*66~IS$`F>rjwh~_-DV=z@J_Dy}{3){NvW~G~g{=hVZ^s z1uw}RZ9gd6I1KbRrM#W+fu3veK$rS>px1ZiNvAy8FFDEF{N|H}Ri}K(pX29h+)Me~ z&Tl!B5a+@p^X36LMp5uFhr8L7)k)g+*uN1(YLCv-<%spzPZZJ+p-ee00TUaS_Hwj` zsO@RHPM+B7ae;0O9O$+Ay+>Pzvl(v&AI4sHbea#??>vFHaG&mTjJlU^qKBd1a z`1z=iYBO|MMo2_~k#Hn>8AGPgSi;fG&~tSi=(HMs5gli`UC`)kc62|{q~A=xGhfa1 zCFE|9G^o>fv8?}OPXshB_}lPx*UH{am)u)sKH#t4Gst#YN!mlPs$1iL`zDLx%PyUjO1%sP&}Mg) ztj|QNTuD#w(cx$_UI)55$*E{NAF$ukN#t%_hRXGJozi}n_a|p}x*t{k%K8`LhwXeK z;CGNM4*fJzkX2RFZ0 z(;zqYd}qHNsU~|c_VEOXew9uWP7+=8@iw#W7zh7)-+F8$n&pC?FLX}8?Ra{hQ9 z*^b4K=_Q3sFE4UIcldjXVD`vNBWg>>n=~!P_h{0_H`1+Tnj*Q)$wWnCq*%$P(uYOH z21#J#$YMIo*C6BZcjm8(fU}*m9J4)Bgbe-E{dZY6pb^@%(D|YLAbe{%zViz|9 z-tcGnDV%xoHT&kC&j&~;_vaVDvWMFou^-`U$o2Vq!meM}f4aCM3xI=0MaFBvJeT+0 zfb4qQ*jI&=uIzo7_XA{o^EtZn3;(0@`SO0CLDB{PH~hWJPtxBLeoTpYu}cL4n@IyQ8O8cu2~Y4|gZ^Dr36<{|^hM0Sy- z+6UTKWsf`kC3{tVs`1@;q2nC}UG--WXUj-q%?CFh*}OKJZ+ZLr_#W~a z2N8)PptaD4JoJ5OmItqYgIFCh4lu+EWE^fCZwx%IPlJ@j-g&b++dK0+{AotkYClhC zM1VYx%?T*bicqt?d;cHRe8qp`3JXK0^)321Io2aTvYbkG;Lj)jh|T8)+|?=v-Q~H^ z-O;U}X%VdFiGKChgx2SQlRMGTk$rmph@SJkFWZ~6mbdMnlu?wT?k#PNE!Fq(ys@Vy z_LJ;u*I$S|mHOlf?d)?uo`0{D-UDp>_a^NFa%A>B#U?a952k~jH)-GUa~f|gSZq(v zWjubafvr}f*SUKdgPo!O1g^!Eqj>#smr{xaQ%e3g#xp`HBKy7TmyaQ&X_ zK|W@^l6(sJb$G9Q*PpYGt-X}~DCl$ahdcX$@&h2qt1F==lEuh7N2Irr+!eB=KqcQs zdxs97X_`-HMM9T!R~nGqRgUzF^iZSHsL!bA3;D1sKGV?LBV#hdFCb%z+#m2$*g22a zWIRR`<=qHD2&sP)E8CnP^#e#(bHo;@-sgxd5>B_R*ElqPUf4g{A98_ZZm&Q*4&gYz zL3RwA@Vm%(I@tn|&;AeV0XduulEbR{Z%$6@1zL_`llRg04y#6j?27`29~8nOscjNr^t_ZMqV`?@TBC z6#7f_Mfr}rWv`e{EUlSK(>N`H6Tg<92Yft|&rSc1FE|FfnLp2O<$Q6|YtjQbBXj%u zl*4tKvy*6*BX*nrb$fiiy^~iz`s9oCk~ZJ% zdimLRTrQDoGjwNq=AmR$k8fFXXG^Q5j4}?4Pgxz&^pALuhBu z)P7FG+wawQ+x|N}kKUy|f7fh!e)CRd0Kz9)4$R0DEr1gJJeggd|6M7=ww4ZGJ>(PIyJfnVo z)!!5UcH5uu*BpPZ^1~uoq4A@%B7d0IP5;&^PHs^5qF88eulkK0ISe8#VY zjL$C8e@xp*Y5ZzQey+%4(EVr7LzpJAA^P(n4dSzr(pTwER`{pT_cbz^B?xbe-G2XrS^@+cek&1 zzxop)zoPRhdtbC`-?hJ9JwM3DAmda@``xeHZ1ugd$6nZToNhn6^*H>+#vX6h$ClrM zezN|`^`Yxg*UOkMm}wV!SJKHt;ZL6MU+M?=?}dMP)ZdGquOZzcSBIc*%cqFqB4WHM zwVy*E7mki!L~Dd}L09v5O51sBzpGeX zyI!Zqb?eqK`LX>G;^~B-d=K~>=|CxG=B=RH;rXOvPg5C}w3biXSL6SJ@+6O^^sWr0 zV^2HzDw0?F$@n++rO&tho%nU_M`TTl-LH=+pVtVh|DrzjPx@NzgW8+2VSn=6zL$KT zBX&910E8RVK9>>s@7o-)yobC)#^roA^K<+H^IXKH2Y>_klZ_ z#j~TxonII$RD47dU@IUE%#UH zZ`z0Wx0@WZ()~pDf^MEp_Pnw0%HBih8~$m(dC4Q^j4#*orvLfe_{U+tk@~AkuiwI6 z=6rsXA2$(=2D(v1Q20p*>fO%)c{L1ePIOK^{Te#%=`+G^SF|F)rN@@^d0QqsF!Q}S z3D^aerumIs+(hh&U*zwS9~ximjK^y{9!?w(5=P(=C?^wPj4??f${NER5tlMmvzx;rrWSC8dOJ=yv|W`iOFpf_bG z9iGhaMC@+BQ$(%`-64cE0hEtJrXk33 zm6}tw$Sg(X6SMAN5|<6`4~Gkc0J~s?Yy49%Bz>c^JgnTs7E z|78w%2ydS0hdiG&RsUYZS0S&?bdQ3TNaUB&QPcf0a@TSmEA#}noEdZpf1-%D=A-y`-p z;@N(KJY&&ak#BFg{-8fM<6YdQuD?-hGk-7$S5M{HH~p(VO5`o$Tl&QRH2+#s0{CBaio`{x|yQr};xWcn{crj)2MqHn#}!n~kO-io{^!+wF0t6MW*+6Mc^q1yb-A8irsDO5G1Zhndkf3tOMpH??2x-ybgLh z<&;l3dXzKUgQ0(7skh6?Zg8HCCjU{)^H3h;Cnpog=Z&14o$tya|5oV(^+mr2tYt4~ zQ!0{}Hg}+-39Zi$<6f1g`Bd}2mB&}hnZCTmGaR4J@tYO@GCF zcKoG&U|i+T$$L9qiM+Xk@?;_SxvjrDNeHdqrN11h^Z2=)`^KzUG{K4UL0th&)k49J`5Mxy8z*f$TpM?PvhrE$4~ma2NVx-1@xQuisilJlrt8jYjeg9852Lyev?CH{azwW)Qi#A z_m|nuZs;KgrJt14kLJ|rHhnGEGFSf;xf>*pe3cITAyWU4K)^=oy?8zY&IjM%@@}Lx zK1!$e=&JhK-|s|M@5$NvieygocSf#H^`r9<`H#QiZOnJZ-{!2FpWlvOA?dy3Yospr z-*^0P-ZM`8wu|>1ufLnP({mS@yU70bz2q%YDRYvC9PvPwyKlfU?-eJy$QcuIk9zz` zw=+NPJz|}=m7gm$r}{KU{0mA#l3(PfRr0yWslGFQIo`&;^*!F|_Pf)!TVMPhFh=q_ zq^bd_2?!J7NYF1mc#GCs+9eGLf%$wu_n%Kcg0367UC)Y|jM`d~p(@y+{jAsaniCZyx;DXcdq$#7`U7XHe-{Pe z-*-9V)W7n7dT;tgualxY|1Aytb)n1gl%FrsP4%CiKe!VhwAw?mkIG&)^iG~C|KoU0 z{DY{4e5a$ly+{++#*$E;{pkHGyT=P&Jia$Zjf*bWC;3qIb!U9BK;ub zz~dtV;*#~th|lJTK`;)nkYWhIDuVI~)!WlwN5-mXnCC;}NAtSrUyz$c&Zsv+(i{4e z13l1WH@GmxuGlUNEhi}PW-dF)PvwhIIoRd$i)a1C3Au0Rae_lRU@S-IyUkI$Ncr{~ zSG4^F-IP`&@S^*HF592Rdz0=5x@!MbdF|+ve&5p1Q>4GMrw!e4k1Oo$roSX_!{7P4 zqiMJrNzeX&Jge4S`tIn1JXVfSc zT)d||H%07EkZw=)7qZ&skPD>TAN4f$?QRu25# zAX&ASq>nEk_gj5_oRIA4z0vfZX{+h35%`II6L}^i7nd|5h_BLdPxqfhTm4k}BnWq= z36Y6ByVAEvc{U^iT7V`%UuaKe{>hMqCVMORP5dE0hFvVvrv`F_a9hzqiM$JFuD~|s zh}8~Vju`v9&Ji!Up*)r0Do1Q{#nY}9!rKXS%|{Rl8WJ8aPd@g@7)#>kCWmYkG&lP) zr(EZltDN%@S)b?vc`tk6lYm6i3Bx4~IaHRfE|fD}<;b`4i)<&;4kO!*r9~dM<%dS< z<1;t*C`x$VD59?ESvF6H(q!{}jr#yuJ*MNE=y;!QZ_@3uz8zl-Kxs9QcaSFQ+vpD_ zK>Z)|OZ~^9dA@!+u2g#rphM_X$|kJqMc3Qac!%|t`q%aJ#NMU;QU20C+e6M7a{ER1 z`FBAUnq%DM^rU@vIl6Do87sYOpK{7k)U&0Z&oKx41~S{xdUEhMC%ws0tGk2G`y4UUmmIKD2q74)uZU!efVx#MI@PDhIH2FINV)um zuFN+mFwq?h6Or2uQvO*qAy=W#8^W1BA=gApPJGC(sXQMd|LQh%qKAIaj^X W^xhWRq}lcW0000ThTPV^y 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 literal 0 HcmV?d00001 diff --git a/packages/flutter3d_impeller/test/gpu_formats_test.dart b/packages/flutter3d_impeller/test/gpu_formats_test.dart index bfd0b7913..aaa1d8ed0 100644 --- a/packages/flutter3d_impeller/test/gpu_formats_test.dart +++ b/packages/flutter3d_impeller/test/gpu_formats_test.dart @@ -2,7 +2,7 @@ /// /// The hazard this exists for: a wrong mapping compiles, runs, and renders /// wrong only for the values a scene happens to use. The goldens cover the -/// values the eighty-four scenes exercise and nothing else — `BlendFactor` has +/// values the eighty-six scenes exercise and nothing else — `BlendFactor` has /// fifteen members and the engine draws with two of them — so a mistake in the /// other thirteen would be found by a user rather than by us. /// diff --git a/packages/flutter3d_particles/CHANGELOG.md b/packages/flutter3d_particles/CHANGELOG.md index fc1ba6a2e..5f814277f 100644 --- a/packages/flutter3d_particles/CHANGELOG.md +++ b/packages/flutter3d_particles/CHANGELOG.md @@ -1,5 +1,10 @@ ## Unreleased +* **Height fog, as thick as it is at the camera.** Both contributors hand + their stages `FogSettings.densityAt` the camera's height rather than the + density at the fog's base, so a height fog does not leave particles fogged + as though they stood on the ground. + * **Particles follow a shader reload.** `ParticleContributor` and `MeshParticleContributor` drop their pipelines when the renderer relinks, through `flutter3d_core`'s `PassContributor.relinkShaders`. diff --git a/packages/flutter3d_particles/lib/src/mesh_particle_contributor.dart b/packages/flutter3d_particles/lib/src/mesh_particle_contributor.dart index 9d79f5627..3b3409621 100644 --- a/packages/flutter3d_particles/lib/src/mesh_particle_contributor.dart +++ b/packages/flutter3d_particles/lib/src/mesh_particle_contributor.dart @@ -205,8 +205,10 @@ final class MeshParticleContributor extends PassContributor { _fog[0] = colour.x; _fog[1] = colour.y; _fog[2] = colour.z; - _fog[3] = fog.density; view.camera.readWorldPosition(_eye); + // The air as thick as it is at the camera: a height fog's falloff is not + // integrated here — `FogSettings.densityAt` says what that costs. + _fog[3] = fog.densityAt(_eye.y); _eyeData[0] = _eye.x; _eyeData[1] = _eye.y; _eyeData[2] = _eye.z; diff --git a/packages/flutter3d_particles/lib/src/particle_contributor.dart b/packages/flutter3d_particles/lib/src/particle_contributor.dart index de68a9c75..1c887b571 100644 --- a/packages/flutter3d_particles/lib/src/particle_contributor.dart +++ b/packages/flutter3d_particles/lib/src/particle_contributor.dart @@ -186,8 +186,10 @@ final class ParticleContributor extends PassContributor { _fog[0] = colour.x; _fog[1] = colour.y; _fog[2] = colour.z; - _fog[3] = fog.density; view.camera.readWorldPosition(_eye); + // The air as thick as it is at the camera: a height fog's falloff is not + // integrated here — `FogSettings.densityAt` says what that costs. + _fog[3] = fog.densityAt(_eye.y); _eyeData[0] = _eye.x; _eyeData[1] = _eye.y; _eyeData[2] = _eye.z; diff --git a/packages/flutter3d_shaders/CHANGELOG.md b/packages/flutter3d_shaders/CHANGELOG.md index 98d60ea27..1c93fde92 100644 --- a/packages/flutter3d_shaders/CHANGELOG.md +++ b/packages/flutter3d_shaders/CHANGELOG.md @@ -25,6 +25,15 @@ light into the sRGB bytes a material's map is read as, turning the rows over where the backend draws its first row at the bottom. Both are in the bundle, in `kRequiredShaders` and in `stageBindings`, `uniformBlocks` and `typed_blocks.dart`. +**`SkyPhysical` and `SkyPhysicalVertex`**, the physical sky: single +scattering by molecules and haze marched per pixel, sixteen samples along the +view and eight towards the sun from each, the disc and the stars dimmed by the +air, and the ground below the horizon. The air travels on the vertices, as the +gradient's preset does. + +**`ApplyFog` integrates a height fog.** `FogInfo.eye.w` carries the falloff +and `FogInfo.fog.w` the density at the eye; a falloff of nought takes the old +path unchanged. ## 0.8.2 diff --git a/packages/flutter3d_shaders/lib/flutter3d_shaders.dart b/packages/flutter3d_shaders/lib/flutter3d_shaders.dart index 0df4147b0..96c705348 100644 --- a/packages/flutter3d_shaders/lib/flutter3d_shaders.dart +++ b/packages/flutter3d_shaders/lib/flutter3d_shaders.dart @@ -111,6 +111,8 @@ const List kRequiredShaders = [ (name: 'SkyCube', fragment: true), (name: 'SkyVertex', fragment: false), (name: 'SkyCubeVertex', fragment: false), + (name: 'SkyPhysical', fragment: true), + (name: 'SkyPhysicalVertex', fragment: false), (name: 'Toon', fragment: true), (name: 'Unlit', fragment: true), (name: 'VertexTextureProbe', fragment: true), diff --git a/packages/flutter3d_shaders/lib/stage_bindings.dart b/packages/flutter3d_shaders/lib/stage_bindings.dart index ae4c6dccd..58711b695 100644 --- a/packages/flutter3d_shaders/lib/stage_bindings.dart +++ b/packages/flutter3d_shaders/lib/stage_bindings.dart @@ -362,6 +362,8 @@ stageBindings = blocks, Set samplers})>{ 'Sky': (blocks: {}, samplers: {}), 'SkyCube': (blocks: {}, samplers: {'sky_texture'}), 'SkyCubeVertex': (blocks: {}, samplers: {}), + 'SkyPhysical': (blocks: {}, samplers: {}), + 'SkyPhysicalVertex': (blocks: {}, samplers: {}), 'SkyVertex': (blocks: {}, samplers: {}), 'SmaaBlend': ( blocks: {'SmaaInfo'}, diff --git a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json index b4db1399b..e0d7e087d 100644 --- a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json +++ b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json @@ -315,6 +315,14 @@ "type": "fragment", "file": "shaders/sky_cube.frag" }, + "SkyPhysicalVertex": { + "type": "vertex", + "file": "shaders/sky_physical.vert" + }, + "SkyPhysical": { + "type": "fragment", + "file": "shaders/sky_physical.frag" + }, "Luminance": { "type": "fragment", "file": "shaders/post/luminance.frag" diff --git a/packages/flutter3d_shaders/shaders/lib/color.glsl b/packages/flutter3d_shaders/shaders/lib/color.glsl index b646208a5..d569388a8 100644 --- a/packages/flutter3d_shaders/shaders/lib/color.glsl +++ b/packages/flutter3d_shaders/shaders/lib/color.glsl @@ -116,11 +116,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -246,6 +248,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -253,6 +265,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } diff --git a/packages/flutter3d_shaders/shaders/sky_physical.frag b/packages/flutter3d_shaders/shaders/sky_physical.frag new file mode 100644 index 000000000..fcd7cbb2a --- /dev/null +++ b/packages/flutter3d_shaders/shaders/sky_physical.frag @@ -0,0 +1,202 @@ +#version 460 core + +// The sky as air: sunlight scattered once by molecules (Rayleigh) and by haze +// (Mie) along the view ray, the sun's disc seen through what the air leaves of +// it, stars at night, and the ground below the horizon. +// +// **Marched per pixel rather than read from a precomputed table.** A table is +// what a sky this size usually becomes, and it is two render passes into +// float targets before the scene, plus a sampler the sky's pipeline would have +// to be measured taking — `sky.vert` says why nothing is assumed about what +// reaches it. Sixteen samples along the view and eight towards the sun at each +// is 144 exponentials a pixel, paid only where no geometry covers the sky, +// because the stage runs after the opaque half and fails the depth test under +// it. `SkySettings.physical` documents the cost where a caller will read it. +// +// **Single scattering only**, and that is visible: the horizon at noon comes +// out a pale cyan rather than white, because light scattered twice is what +// whitens it. Measured against a 128 × 64 march of the same model, the +// march here is within two per cent of it at the zenith and four at the +// horizon. Two choices buy that. The view samples are spaced quadratically, +// short near the eye where a horizontal ray spends its densest air; spaced +// evenly, sixteen of them put the noon horizon's blue at less than half of +// what it is. And each sample is dimmed by the air up to its middle rather +// than its far end, which was another fifteen per cent. +// +// The same arithmetic runs in Dart twice: `PhysicalSky.radiance`, for a fog +// colour, a sunlight colour and an environment map built from the sky, and +// `SkyPhysicalShader` in the software rasteriser. A test evaluates both. +precision highp float; + +in vec3 v_ray; +in vec4 v_rayleigh; +in vec4 v_mie; +in vec4 v_sun; +in vec4 v_planet; +in vec4 v_stars; +in vec4 v_disc; + +layout(location = 0) out vec4 frag_color; + +// No surface output, for the reason `sky.frag` records at length: on Impeller, +// a second output in a single-attachment pass took the process down. + +const int kViewSteps = 16; +const int kLightSteps = 8; +const float kPi = 3.14159265; + +/// How far a ray from radius [r], at cosine [mu] to the local up, runs before +/// it leaves a sphere of [radius]; negative when it misses. +/// +/// `(r - radius) * (r + radius)` rather than `r * r - radius * radius`: the +/// two squares are 4·10⁷ km² apiece and their difference near the ground is a +/// few hundred, which the subtraction of the squares loses most of in single +/// precision. +float Leave(float r, float mu, float radius) { + float b = r * mu; + float d = b * b - (r - radius) * (r + radius); + return d < 0.0 ? -1.0 : -b + sqrt(d); +} + +/// How far the same ray runs before it meets the ground, negative when it +/// does not. +float Meet(float r, float mu, float radius) { + float b = r * mu; + float d = b * b - (r - radius) * (r + radius); + return d < 0.0 ? -1.0 : -b - sqrt(d); +} + +/// The air between [p] and space towards [s]: x molecules, y haze, each as a +/// length of air at ground density. +vec2 SunwardAir(vec3 p, vec3 s) { + float r = length(p); + float span = Leave(r, dot(p, s) / r, v_planet.y); + float stride = span / float(kLightSteps); + vec2 air = vec2(0.0); + for (int j = 0; j < kLightSteps; j++) { + vec3 q = p + s * ((float(j) + 0.5) * stride); + float h = length(q) - v_planet.x; + air += exp(-h / vec2(v_rayleigh.w, v_mie.z)) * stride; + } + return air; +} + +/// What a length of air lets through, per channel. +vec3 Through(vec2 air) { + return exp(-(v_rayleigh.rgb * air.x + vec3(v_mie.y * air.y))); +} + +/// A hash of three small whole numbers into [0, 1), with no sine in it. +/// +/// `fract(sin(x) * 43758.5)` is the usual one and it is wrong here: the sine +/// of a large argument is computed differently by every GPU and by the +/// software rasteriser, so the stars would be in different places on each +/// backend. This is products and `fract`s of numbers below a few thousand, +/// which single precision answers alike everywhere to the last few bits. +float Hash(vec3 p) { + p = fract(p * 0.1031); + p += dot(p, p.zyx + 31.32); + return fract((p.x + p.y) * p.z); +} + +/// The stars in direction [d]: a grid of cells on each face of a cube, a share +/// [v_stars.y] of them holding a star at a place and a brightness of its own. +float StarField(vec3 d) { + vec3 a = abs(d); + float face; + vec2 uv; + if (a.x >= a.y && a.x >= a.z) { + face = d.x > 0.0 ? 0.0 : 1.0; + uv = d.zy / a.x; + } else if (a.y >= a.z) { + face = d.y > 0.0 ? 2.0 : 3.0; + uv = d.xz / a.y; + } else { + face = d.z > 0.0 ? 4.0 : 5.0; + uv = d.xy / a.z; + } + vec2 grid = (uv * 0.5 + 0.5) * v_stars.z; + vec2 cell = floor(grid); + vec3 key = vec3(cell, face); + if (Hash(key) >= v_stars.y) return 0.0; + vec2 centre = vec2(Hash(key + vec3(17.0, 0.0, 0.0)), + Hash(key + vec3(0.0, 29.0, 0.0))) * 0.6 + 0.2; + float off = length(grid - cell - centre); + float magnitude = Hash(key + vec3(0.0, 0.0, 13.0)); + return (1.0 - smoothstep(0.0, 0.35, off)) * + (0.15 + 0.85 * magnitude * magnitude * magnitude); +} + +void main() { + vec3 d = normalize(v_ray); + vec3 s = v_sun.xyz; + vec3 eye = vec3(0.0, v_planet.z, 0.0); + + float ground = Meet(v_planet.z, d.y, v_planet.x); + bool grounded = ground > 0.0; + float span = grounded ? ground : Leave(v_planet.z, d.y, v_planet.y); + + vec2 seenAir = vec2(0.0); + vec3 molecules = vec3(0.0); + vec3 haze = vec3(0.0); + for (int i = 0; i < kViewSteps; i++) { + float a0 = float(i) / float(kViewSteps); + float a1 = float(i + 1) / float(kViewSteps); + float t = span * 0.5 * (a0 * a0 + a1 * a1); + float stride = span * (a1 * a1 - a0 * a0); + vec3 p = eye + d * t; + float r = length(p); + vec2 air = exp(-(r - v_planet.x) / vec2(v_rayleigh.w, v_mie.z)) * stride; + seenAir += air; + // In the planet's shadow: this sample is lit by nothing. + if (Meet(r, dot(p, s) / r, v_planet.x) > 0.0) continue; + vec3 through = Through(seenAir - 0.5 * air + SunwardAir(p, s)); + molecules += air.x * through; + haze += air.y * through; + } + + float mu = dot(d, s); + float g = v_mie.w; + float gg = g * g; + float rayleighPhase = 3.0 / (16.0 * kPi) * (1.0 + mu * mu); + // Cornette–Shanks, which is Henyey–Greenstein with the Rayleigh term's + // shape folded in. g is held below one by the renderer, so the base of the + // power never reaches nought. + float miePhase = 3.0 / (8.0 * kPi) * ((1.0 - gg) * (1.0 + mu * mu)) / + ((2.0 + gg) * pow(1.0 + gg - 2.0 * g * mu, 1.5)); + + vec3 colour = v_sun.w * (molecules * v_rayleigh.rgb * rayleighPhase + + haze * (v_mie.x * miePhase)); + vec3 seen = Through(seenAir); + + if (grounded) { + // The ground, lit by what the air leaves of the sun and seen through the + // air in front of it. Lambertian, and with no light from the sky itself, + // so a ground in the sun's shadow is black; it is what fills the lower + // half of an environment map built from this sky, not a terrain. + vec3 p = eye + d * span; + float lit = dot(normalize(p), s); + if (lit > 0.0) { + colour += seen * Through(SunwardAir(p, s)) * + (v_planet.w / kPi * lit * v_sun.w); + } + } else { + // The disc, through the air in front of it: this is what turns it red + // at sunset with no colour anybody chose. `sky.frag` explains the + // guard and why the soft edge travels as a difference. + float disc = v_disc.y > 0.0 + ? smoothstep(v_disc.x - v_disc.y, v_disc.x, mu) + : step(v_disc.x, mu); + colour += seen * (disc * v_disc.z); + + // Stars, as the sun goes down: a fade from the sun six degrees above + // the horizon to eleven below, rather than the sky's own brightness, + // which would need the exposure this stage does not know. + float night = 1.0 - smoothstep(-0.2, 0.1, s.y); + if (v_stars.x > 0.0 && night > 0.0) { + colour += seen * (v_stars.x * night * StarField(d)); + } + } + + frag_color = vec4(colour, 1.0); +} diff --git a/packages/flutter3d_shaders/shaders/sky_physical.vert b/packages/flutter3d_shaders/shaders/sky_physical.vert new file mode 100644 index 000000000..8d6d64688 --- /dev/null +++ b/packages/flutter3d_shaders/shaders/sky_physical.vert @@ -0,0 +1,63 @@ +#version 460 core + +// Vertex stage for the physical sky: the gradient sky's triangle, carrying the +// air instead of three colours. +// +// **Its own stage rather than `sky.vert` read under other names.** The data +// travels the same way and for the same reason — `sky.vert` sets out what was +// measured: on Impeller a uniform block never reaches the sky's pipeline and a +// vertex attribute does — and it is the same size, six vec4s after the ray. But +// a varying is matched by name between the stages, and a fragment stage reading +// `v_zenith` as a scattering coefficient is one nobody could maintain. +// +// The depth is `sky.vert`'s 0.999999, for `sky.vert`'s reason: the far plane +// less a hair, so the pass's own `less` passes against a cleared buffer and +// fails against anything already drawn. +// +// Lengths are in kilometres here, not metres. The planet's radius is 6360 of +// them, and in metres its square is 4·10¹³, which single precision holds to +// within a few units — enough to put the horizon a pixel off where the ray +// grazes it. The renderer converts; see `Renderer._skyVertexBytes`. +precision highp float; + +layout(location = 0) in vec2 position; + +// The world-space view ray at this corner. +layout(location = 1) in vec3 corner_ray; + +/// rgb: Rayleigh scattering at the ground, per km. a: its scale height, km. +layout(location = 2) in vec4 rayleigh; +/// x: Mie scattering at the ground, per km. y: Mie extinction, per km. +/// z: its scale height, km. w: Henyey–Greenstein's g. +layout(location = 3) in vec4 mie; +/// xyz: unit vector pointing at the sun. w: the sunlight entering the air. +layout(location = 4) in vec4 sun; +/// x: the planet's radius. y: the top of the air's. z: the eye's, all km. +/// w: the ground's albedo. +layout(location = 5) in vec4 planet; +/// x: how bright the stars are. y: the share of cells holding one. +/// z: cells across a face of the cube they are scattered on. w: unused. +layout(location = 6) in vec4 stars; +/// x: cosine of the disc's angular radius. y: how much softer its edge is, +/// as a difference of cosines. z: how bright the disc is. w: unused. +layout(location = 7) in vec4 disc; + +out vec3 v_ray; +out vec4 v_rayleigh; +out vec4 v_mie; +out vec4 v_sun; +out vec4 v_planet; +out vec4 v_stars; +out vec4 v_disc; + +void main() { + v_ray = corner_ray; + v_rayleigh = rayleigh; + v_mie = mie; + v_sun = sun; + v_planet = planet; + v_stars = stars; + v_disc = disc; + + gl_Position = vec4(position, 0.999999, 1.0); +} diff --git a/packages/flutter3d_webgl/CHANGELOG.md b/packages/flutter3d_webgl/CHANGELOG.md index a8537c8da..ca6e9e446 100644 --- a/packages/flutter3d_webgl/CHANGELOG.md +++ b/packages/flutter3d_webgl/CHANGELOG.md @@ -15,6 +15,8 @@ them, could not depend on it. Nothing else changed. **The generated tables carry `PlanarReflection` and `RenderTextureEncode`**, and the `planar-mirror` and `render-texture` goldens are in this set. +**The generated tables carry `SkyPhysical`, `SkyPhysicalVertex` and the +height fog in `ApplyFog`.** ## 0.8.2 diff --git a/packages/flutter3d_webgl/lib/engine_shaders.dart b/packages/flutter3d_webgl/lib/engine_shaders.dart index e5be6f451..efba3f680 100644 --- a/packages/flutter3d_webgl/lib/engine_shaders.dart +++ b/packages/flutter3d_webgl/lib/engine_shaders.dart @@ -2307,6 +2307,71 @@ void main() { gl_Position = vec4(position, 0.999999, 1.0); } +''', + 'SkyPhysicalVertex': r'''#version 300 es + +// Vertex stage for the physical sky: the gradient sky's triangle, carrying the +// air instead of three colours. +// +// **Its own stage rather than `sky.vert` read under other names.** The data +// travels the same way and for the same reason — `sky.vert` sets out what was +// measured: on Impeller a uniform block never reaches the sky's pipeline and a +// vertex attribute does — and it is the same size, six vec4s after the ray. But +// a varying is matched by name between the stages, and a fragment stage reading +// `v_zenith` as a scattering coefficient is one nobody could maintain. +// +// The depth is `sky.vert`'s 0.999999, for `sky.vert`'s reason: the far plane +// less a hair, so the pass's own `less` passes against a cleared buffer and +// fails against anything already drawn. +// +// Lengths are in kilometres here, not metres. The planet's radius is 6360 of +// them, and in metres its square is 4·10¹³, which single precision holds to +// within a few units — enough to put the horizon a pixel off where the ray +// grazes it. The renderer converts; see `Renderer._skyVertexBytes`. +precision highp float; + +layout(location = 0) in vec2 position; + +// The world-space view ray at this corner. +layout(location = 1) in vec3 corner_ray; + +/// rgb: Rayleigh scattering at the ground, per km. a: its scale height, km. +layout(location = 2) in vec4 rayleigh; +/// x: Mie scattering at the ground, per km. y: Mie extinction, per km. +/// z: its scale height, km. w: Henyey–Greenstein's g. +layout(location = 3) in vec4 mie; +/// xyz: unit vector pointing at the sun. w: the sunlight entering the air. +layout(location = 4) in vec4 sun; +/// x: the planet's radius. y: the top of the air's. z: the eye's, all km. +/// w: the ground's albedo. +layout(location = 5) in vec4 planet; +/// x: how bright the stars are. y: the share of cells holding one. +/// z: cells across a face of the cube they are scattered on. w: unused. +layout(location = 6) in vec4 stars; +/// x: cosine of the disc's angular radius. y: how much softer its edge is, +/// as a difference of cosines. z: how bright the disc is. w: unused. +layout(location = 7) in vec4 disc; + +out vec3 v_ray; +out vec4 v_rayleigh; +out vec4 v_mie; +out vec4 v_sun; +out vec4 v_planet; +out vec4 v_stars; +out vec4 v_disc; + +void main() { + v_ray = corner_ray; + v_rayleigh = rayleigh; + v_mie = mie; + v_sun = sun; + v_planet = planet; + v_stars = stars; + v_disc = disc; + + gl_Position = vec4(position, 0.999999, 1.0); +} + ''', 'VertexTextureProbeVertex': r'''#version 300 es @@ -2659,11 +2724,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -2789,6 +2856,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -2796,6 +2873,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -4442,11 +4524,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -4572,6 +4656,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -4579,6 +4673,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -6196,11 +6295,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -6326,6 +6427,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -6333,6 +6444,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -6665,11 +6781,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -6795,6 +6913,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -6802,6 +6930,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -9144,11 +9277,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -9274,6 +9409,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -9281,6 +9426,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -11658,11 +11808,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -11788,6 +11940,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -11795,6 +11957,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -15004,11 +15171,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -15134,6 +15303,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -15141,6 +15320,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -18318,11 +18502,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -18448,6 +18634,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -18455,6 +18651,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -20780,11 +20981,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -20910,6 +21113,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -20917,6 +21130,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -23093,11 +23311,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -23223,6 +23443,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -23230,6 +23460,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -30234,11 +30469,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -30364,6 +30601,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -30371,6 +30618,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -30617,11 +30869,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -30747,6 +31001,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -30754,6 +31018,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -30995,11 +31264,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -31125,6 +31396,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -31132,6 +31413,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -31656,6 +31942,214 @@ void main() { frag_color = vec4(linear * v_tint.rgb, 1.0); } +''', + 'SkyPhysical': r'''#version 300 es +precision highp float; +precision highp int; +precision highp sampler2D; +precision highp samplerCube; + +// The sky as air: sunlight scattered once by molecules (Rayleigh) and by haze +// (Mie) along the view ray, the sun's disc seen through what the air leaves of +// it, stars at night, and the ground below the horizon. +// +// **Marched per pixel rather than read from a precomputed table.** A table is +// what a sky this size usually becomes, and it is two render passes into +// float targets before the scene, plus a sampler the sky's pipeline would have +// to be measured taking — `sky.vert` says why nothing is assumed about what +// reaches it. Sixteen samples along the view and eight towards the sun at each +// is 144 exponentials a pixel, paid only where no geometry covers the sky, +// because the stage runs after the opaque half and fails the depth test under +// it. `SkySettings.physical` documents the cost where a caller will read it. +// +// **Single scattering only**, and that is visible: the horizon at noon comes +// out a pale cyan rather than white, because light scattered twice is what +// whitens it. Measured against a 128 × 64 march of the same model, the +// march here is within two per cent of it at the zenith and four at the +// horizon. Two choices buy that. The view samples are spaced quadratically, +// short near the eye where a horizontal ray spends its densest air; spaced +// evenly, sixteen of them put the noon horizon's blue at less than half of +// what it is. And each sample is dimmed by the air up to its middle rather +// than its far end, which was another fifteen per cent. +// +// The same arithmetic runs in Dart twice: `PhysicalSky.radiance`, for a fog +// colour, a sunlight colour and an environment map built from the sky, and +// `SkyPhysicalShader` in the software rasteriser. A test evaluates both. +precision highp float; + +in vec3 v_ray; +in vec4 v_rayleigh; +in vec4 v_mie; +in vec4 v_sun; +in vec4 v_planet; +in vec4 v_stars; +in vec4 v_disc; + +layout(location = 0) out vec4 frag_color; + +// No surface output, for the reason `sky.frag` records at length: on Impeller, +// a second output in a single-attachment pass took the process down. + +const int kViewSteps = 16; +const int kLightSteps = 8; +const float kPi = 3.14159265; + +/// How far a ray from radius [r], at cosine [mu] to the local up, runs before +/// it leaves a sphere of [radius]; negative when it misses. +/// +/// `(r - radius) * (r + radius)` rather than `r * r - radius * radius`: the +/// two squares are 4·10⁷ km² apiece and their difference near the ground is a +/// few hundred, which the subtraction of the squares loses most of in single +/// precision. +float Leave(float r, float mu, float radius) { + float b = r * mu; + float d = b * b - (r - radius) * (r + radius); + return d < 0.0 ? -1.0 : -b + sqrt(d); +} + +/// How far the same ray runs before it meets the ground, negative when it +/// does not. +float Meet(float r, float mu, float radius) { + float b = r * mu; + float d = b * b - (r - radius) * (r + radius); + return d < 0.0 ? -1.0 : -b - sqrt(d); +} + +/// The air between [p] and space towards [s]: x molecules, y haze, each as a +/// length of air at ground density. +vec2 SunwardAir(vec3 p, vec3 s) { + float r = length(p); + float span = Leave(r, dot(p, s) / r, v_planet.y); + float stride = span / float(kLightSteps); + vec2 air = vec2(0.0); + for (int j = 0; j < kLightSteps; j++) { + vec3 q = p + s * ((float(j) + 0.5) * stride); + float h = length(q) - v_planet.x; + air += exp(-h / vec2(v_rayleigh.w, v_mie.z)) * stride; + } + return air; +} + +/// What a length of air lets through, per channel. +vec3 Through(vec2 air) { + return exp(-(v_rayleigh.rgb * air.x + vec3(v_mie.y * air.y))); +} + +/// A hash of three small whole numbers into [0, 1), with no sine in it. +/// +/// `fract(sin(x) * 43758.5)` is the usual one and it is wrong here: the sine +/// of a large argument is computed differently by every GPU and by the +/// software rasteriser, so the stars would be in different places on each +/// backend. This is products and `fract`s of numbers below a few thousand, +/// which single precision answers alike everywhere to the last few bits. +float Hash(vec3 p) { + p = fract(p * 0.1031); + p += dot(p, p.zyx + 31.32); + return fract((p.x + p.y) * p.z); +} + +/// The stars in direction [d]: a grid of cells on each face of a cube, a share +/// [v_stars.y] of them holding a star at a place and a brightness of its own. +float StarField(vec3 d) { + vec3 a = abs(d); + float face; + vec2 uv; + if (a.x >= a.y && a.x >= a.z) { + face = d.x > 0.0 ? 0.0 : 1.0; + uv = d.zy / a.x; + } else if (a.y >= a.z) { + face = d.y > 0.0 ? 2.0 : 3.0; + uv = d.xz / a.y; + } else { + face = d.z > 0.0 ? 4.0 : 5.0; + uv = d.xy / a.z; + } + vec2 grid = (uv * 0.5 + 0.5) * v_stars.z; + vec2 cell = floor(grid); + vec3 key = vec3(cell, face); + if (Hash(key) >= v_stars.y) return 0.0; + vec2 centre = vec2(Hash(key + vec3(17.0, 0.0, 0.0)), + Hash(key + vec3(0.0, 29.0, 0.0))) * 0.6 + 0.2; + float off = length(grid - cell - centre); + float magnitude = Hash(key + vec3(0.0, 0.0, 13.0)); + return (1.0 - smoothstep(0.0, 0.35, off)) * + (0.15 + 0.85 * magnitude * magnitude * magnitude); +} + +void main() { + vec3 d = normalize(v_ray); + vec3 s = v_sun.xyz; + vec3 eye = vec3(0.0, v_planet.z, 0.0); + + float ground = Meet(v_planet.z, d.y, v_planet.x); + bool grounded = ground > 0.0; + float span = grounded ? ground : Leave(v_planet.z, d.y, v_planet.y); + + vec2 seenAir = vec2(0.0); + vec3 molecules = vec3(0.0); + vec3 haze = vec3(0.0); + for (int i = 0; i < kViewSteps; i++) { + float a0 = float(i) / float(kViewSteps); + float a1 = float(i + 1) / float(kViewSteps); + float t = span * 0.5 * (a0 * a0 + a1 * a1); + float stride = span * (a1 * a1 - a0 * a0); + vec3 p = eye + d * t; + float r = length(p); + vec2 air = exp(-(r - v_planet.x) / vec2(v_rayleigh.w, v_mie.z)) * stride; + seenAir += air; + // In the planet's shadow: this sample is lit by nothing. + if (Meet(r, dot(p, s) / r, v_planet.x) > 0.0) continue; + vec3 through = Through(seenAir - 0.5 * air + SunwardAir(p, s)); + molecules += air.x * through; + haze += air.y * through; + } + + float mu = dot(d, s); + float g = v_mie.w; + float gg = g * g; + float rayleighPhase = 3.0 / (16.0 * kPi) * (1.0 + mu * mu); + // Cornette–Shanks, which is Henyey–Greenstein with the Rayleigh term's + // shape folded in. g is held below one by the renderer, so the base of the + // power never reaches nought. + float miePhase = 3.0 / (8.0 * kPi) * ((1.0 - gg) * (1.0 + mu * mu)) / + ((2.0 + gg) * pow(1.0 + gg - 2.0 * g * mu, 1.5)); + + vec3 colour = v_sun.w * (molecules * v_rayleigh.rgb * rayleighPhase + + haze * (v_mie.x * miePhase)); + vec3 seen = Through(seenAir); + + if (grounded) { + // The ground, lit by what the air leaves of the sun and seen through the + // air in front of it. Lambertian, and with no light from the sky itself, + // so a ground in the sun's shadow is black; it is what fills the lower + // half of an environment map built from this sky, not a terrain. + vec3 p = eye + d * span; + float lit = dot(normalize(p), s); + if (lit > 0.0) { + colour += seen * Through(SunwardAir(p, s)) * + (v_planet.w / kPi * lit * v_sun.w); + } + } else { + // The disc, through the air in front of it: this is what turns it red + // at sunset with no colour anybody chose. `sky.frag` explains the + // guard and why the soft edge travels as a difference. + float disc = v_disc.y > 0.0 + ? smoothstep(v_disc.x - v_disc.y, v_disc.x, mu) + : step(v_disc.x, mu); + colour += seen * (disc * v_disc.z); + + // Stars, as the sun goes down: a fade from the sun six degrees above + // the horizon to eleven below, rather than the sky's own brightness, + // which would need the exposure this stage does not know. + float night = 1.0 - smoothstep(-0.2, 0.1, s.y); + if (v_stars.x > 0.0 && night > 0.0) { + colour += seen * (v_stars.x * night * StarField(d)); + } + } + + frag_color = vec4(colour, 1.0); +} + ''', 'Luminance': r'''#version 300 es precision highp float; @@ -31954,11 +32448,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -32084,6 +32580,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -32091,6 +32597,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } @@ -32850,11 +33361,13 @@ bool g_premultiply = false; /// way to move offsets nobody expected to move. Three vec4s is a cheap price /// for not touching any of that. layout(std140) uniform FogInfo { - /// rgb: linear fog colour. w: density per metre, zero for no fog. + /// rgb: linear fog colour. w: density per metre at the eye, zero for no + /// fog. vec4 fog; /// xyz: camera position in world space. Duplicated from FragInfo so this - /// block stands alone; a vec3 is cheaper than a coupling. + /// block stands alone; a vec3 is cheaper than a coupling. w: how fast the + /// fog thins upwards, per metre — `P5`, see [ApplyFog]; nought is flat fog. vec4 eye; /// xyz: the direction the camera looks, as a unit vector in world space. @@ -32980,6 +33493,16 @@ void WriteSurfaceGeometry(float roughness) { /// /// Exponential rather than linear, because linear fog has a visible plane /// where it starts and a dungeon corridor is exactly where that shows. +/// +/// **Height fog — `P5`.** `fog_info.fog.w` is the density at the eye and +/// `fog_info.eye.w` how fast it falls off upwards, per metre. The density +/// along the ray is then `density · e^(−falloff · Δy · t)` for t from nought +/// to one, and its mean over the ray is `(1 − e^(−k)) / k` with +/// `k = falloff · Δy`: an exact integral, so a fog lying on the ground costs +/// one exponential here and no march. Below a thousandth `k` is answered by +/// the first two terms of the same series, because the quotient there is +/// two nearly equal numbers divided by a small one. A falloff of nought skips +/// all of it, which keeps a flat fog the same to the bit. vec3 ApplyFog(vec3 color) { #ifdef F3D_NO_FOG return color; @@ -32987,6 +33510,11 @@ vec3 ApplyFog(vec3 color) { float density = fog_info.fog.w; if (density <= 0.0) return color; float d = EyeDistance(); + float falloff = fog_info.eye.w; + if (falloff > 0.0) { + float k = falloff * (v_world_position.y - fog_info.eye.y); + density *= abs(k) > 1e-3 ? (1.0 - exp(-k)) / k : 1.0 - 0.5 * k; + } return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); #endif } diff --git a/packages/flutter3d_webgl/test/cross_backend_test.dart b/packages/flutter3d_webgl/test/cross_backend_test.dart index 6d1024291..95d1b11e1 100644 --- a/packages/flutter3d_webgl/test/cross_backend_test.dart +++ b/packages/flutter3d_webgl/test/cross_backend_test.dart @@ -259,6 +259,10 @@ const Map _budgets = { 'sheen-fabric': 0.23, // 0.000% measured. It was 2.039%, and not sampling: the puff's hash used // 64-bit integers, so a browser baked a different sheet. + // `P5`. 11 of 172800 measured against Impeller on 2026-10-01, the floor's + // far edge; the night sky 0. + 'sky-physical-dusk': 0.01, + 'sky-physical-night': 0.01, 'smoke-six-way': 0.01, 'splat-gltf': 0.01, // 0.001% measured, since the hash reads the pixel from the top on every diff --git a/packages/flutter3d_webgl/test/engine_parity_test.dart b/packages/flutter3d_webgl/test/engine_parity_test.dart index 08bd3c061..27c7230d6 100644 --- a/packages/flutter3d_webgl/test/engine_parity_test.dart +++ b/packages/flutter3d_webgl/test/engine_parity_test.dart @@ -3180,6 +3180,7 @@ void _linkTests() { ('ParticleMeshVertex', 'ParticleMesh'), ('SkyVertex', 'Sky'), ('SkyCubeVertex', 'SkyCube'), + ('SkyPhysicalVertex', 'SkyPhysical'), ]; for (final (vertex, fragment) in pairs) { diff --git a/packages/flutter3d_webgl/test/goldens/sky-physical-dusk.png b/packages/flutter3d_webgl/test/goldens/sky-physical-dusk.png new file mode 100644 index 0000000000000000000000000000000000000000..9e76fb1c3ec06609d51161dd4612f64bccc9bda0 GIT binary patch literal 89705 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 z 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≪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 literal 0 HcmV?d00001 diff --git a/packages/flutter3d_webgl/test/lighting_models_test.dart b/packages/flutter3d_webgl/test/lighting_models_test.dart index d7d83aa95..b5a4e3b10 100644 --- a/packages/flutter3d_webgl/test/lighting_models_test.dart +++ b/packages/flutter3d_webgl/test/lighting_models_test.dart @@ -6,7 +6,7 @@ /// `lighting-unlit` came back as an empty frame — the background colour in every /// pixel — while the other five drew a sphere. The golden set found it, once /// there was a golden set; this says the same thing in one second instead of a -/// browser run over eighty-four scenes, and says it per model rather than per +/// browser run over eighty-six scenes, and says it per model rather than per /// picture. /// /// It deliberately does not compare against a reference. What a shader *should* diff --git a/packages/flutter3d_webgl/tool/golden_web.sh b/packages/flutter3d_webgl/tool/golden_web.sh index 983f94962..af4fa6922 100755 --- a/packages/flutter3d_webgl/tool/golden_web.sh +++ b/packages/flutter3d_webgl/tool/golden_web.sh @@ -16,7 +16,7 @@ # the same one either way, so this script has no reason to read it. # # **One build for the whole suite, and for both browser backends.** The scene is -# a query parameter rather than a compile-time define, so the eighty-four scenes +# a query parameter rather than a compile-time define, so the eighty-six scenes # are one dart2js run and a navigation each, rather than a dart2js run each. # That is the only reason this is minutes rather than an hour, and it is why the # backend arrives the same way: a define per backend would have spent the saving diff --git a/packages/flutter3d_webgpu/CHANGELOG.md b/packages/flutter3d_webgpu/CHANGELOG.md index 99947dc3c..e057b47f1 100644 --- a/packages/flutter3d_webgpu/CHANGELOG.md +++ b/packages/flutter3d_webgpu/CHANGELOG.md @@ -18,6 +18,8 @@ through glslang and naga like every other stage. `RenderTextureEncode`**, through glslang and naga like every other stage, and the `planar-mirror` and `render-texture` goldens are in this set, both 0 of 172800 pixels from Impeller. +**The generated tables carry `SkyPhysical`, `SkyPhysicalVertex` and the +height fog in `ApplyFog`**, through glslang and naga like every other stage. ## 0.8.2 diff --git a/packages/flutter3d_webgpu/lib/engine_shaders.dart b/packages/flutter3d_webgpu/lib/engine_shaders.dart index 104131e53..f69b6bc75 100644 --- a/packages/flutter3d_webgpu/lib/engine_shaders.dart +++ b/packages/flutter3d_webgpu/lib/engine_shaders.dart @@ -3546,11 +3546,11 @@ struct gl_PerVertex { } struct VertexOutput { - @location(4) member: vec3, - @location(7) member_1: vec4, + @location(6) member: vec3, + @location(11) member_1: vec4, @location(2) member_2: vec4, - @location(3) member_3: vec4, - @location(5) member_4: vec4, + @location(4) member_3: vec4, + @location(9) member_4: vec4, @location(1) member_5: vec4, @location(0) member_6: vec4, @builtin(position) gl_Position: vec4, @@ -3670,8 +3670,8 @@ struct gl_PerVertex { } struct VertexOutput { - @location(4) member: vec3, - @location(6) member_1: vec4, + @location(6) member: vec3, + @location(10) member_1: vec4, @builtin(position) gl_Position: vec4, } @@ -3724,6 +3724,130 @@ fn main(@location(1) corner_ray: vec3, @location(2) tint: vec4, @locat blocks: [], samplers: [], ), + 'SkyPhysicalVertex': WebGpuStage( + wgsl: r''' +struct gl_PerVertex { + @builtin(position) gl_Position: vec4, + gl_PointSize: f32, + gl_ClipDistance: array, + gl_CullDistance: array, +} + +struct VertexOutput { + @location(6) member: vec3, + @location(7) member_1: vec4, + @location(3) member_2: vec4, + @location(9) member_3: vec4, + @location(5) member_4: vec4, + @location(8) member_5: vec4, + @location(0) member_6: vec4, + @builtin(position) gl_Position: vec4, +} + +var v_ray: vec3; +var corner_ray_1: vec3; +var v_rayleigh: vec4; +var rayleigh_1: vec4; +var v_mie: vec4; +var mie_1: vec4; +var v_sun: vec4; +var sun_1: vec4; +var v_planet: vec4; +var planet_1: vec4; +var v_stars: vec4; +var stars_1: vec4; +var v_disc: vec4; +var disc_1: vec4; +var unnamed: gl_PerVertex = gl_PerVertex(vec4(0f, 0f, 0f, 1f), 1f, array(), array()); +var position_1: vec2; + +fn main_1() { + let _e20 = corner_ray_1; + v_ray = _e20; + let _e21 = rayleigh_1; + v_rayleigh = _e21; + let _e22 = mie_1; + v_mie = _e22; + let _e23 = sun_1; + v_sun = _e23; + let _e24 = planet_1; + v_planet = _e24; + let _e25 = stars_1; + v_stars = _e25; + let _e26 = disc_1; + v_disc = _e26; + let _e27 = position_1; + unnamed.gl_Position = vec4(_e27.x, _e27.y, 0.999999f, 1f); + return; +} + +@vertex +fn main(@location(1) corner_ray: vec3, @location(2) rayleigh: vec4, @location(3) mie: vec4, @location(4) sun: vec4, @location(5) planet: vec4, @location(6) stars: vec4, @location(7) disc: vec4, @location(0) position: vec2) -> VertexOutput { + corner_ray_1 = corner_ray; + rayleigh_1 = rayleigh; + mie_1 = mie; + sun_1 = sun; + planet_1 = planet; + stars_1 = stars; + disc_1 = disc; + position_1 = position; + main_1(); + let _e25 = v_ray; + let _e26 = v_rayleigh; + let _e27 = v_mie; + let _e28 = v_sun; + let _e29 = v_planet; + let _e30 = v_stars; + let _e31 = v_disc; + let _e32 = unnamed.gl_Position; + return VertexOutput(_e25, _e26, _e27, _e28, _e29, _e30, _e31, _e32); +} +''', + attributes: [ + WebGpuAttribute( + name: 'position', + location: 0, + format: VertexFormat.float32x2, + ), + WebGpuAttribute( + name: 'corner_ray', + location: 1, + format: VertexFormat.float32x3, + ), + WebGpuAttribute( + name: 'rayleigh', + location: 2, + format: VertexFormat.float32x4, + ), + WebGpuAttribute( + name: 'mie', + location: 3, + format: VertexFormat.float32x4, + ), + WebGpuAttribute( + name: 'sun', + location: 4, + format: VertexFormat.float32x4, + ), + WebGpuAttribute( + name: 'planet', + location: 5, + format: VertexFormat.float32x4, + ), + WebGpuAttribute( + name: 'stars', + location: 6, + format: VertexFormat.float32x4, + ), + WebGpuAttribute( + name: 'disc', + location: 7, + format: VertexFormat.float32x4, + ), + ], + blocks: [], + samplers: [], + ), 'VertexTextureProbeVertex': WebGpuStage( wgsl: r''' struct ProbeInfo { @@ -4120,10 +4244,10 @@ var v_tangent_1: vec4; var v_lightmap_uv_1: vec2; fn ViewDepth_u0028_() -> f32 { - let _e75 = v_world_position_1; - let _e77 = fog_info.eye; - let _e81 = fog_info.forward; - return dot((_e75 - _e77.xyz), _e81.xyz); + let _e76 = v_world_position_1; + let _e78 = fog_info.eye; + let _e82 = fog_info.forward; + return dot((_e76 - _e78.xyz), _e82.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -4132,22 +4256,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e80 = ViewDepth_u0028_(); - z = abs(_e80); - let _e82 = z; - near = (_e82 / 5f); - let _e84 = z; - far = (_e84 / 200f); - let _e86 = far; + let _e81 = ViewDepth_u0028_(); + z = abs(_e81); + let _e83 = z; + near = (_e83 / 5f); + let _e85 = z; + far = (_e85 / 200f); let _e87 = far; - let _e89 = far; - far3_ = ((_e86 * _e87) * _e89); - let _e91 = (*alpha); - let _e92 = near; + let _e88 = far; + let _e90 = far; + far3_ = ((_e87 * _e88) * _e90); + let _e92 = (*alpha); let _e93 = near; - let _e96 = far3_; + let _e94 = near; let _e97 = far3_; - return (_e91 * clamp((10f / ((0.00001f + (_e92 * _e93)) + (_e96 * _e97))), 0.01f, 3000f)); + let _e98 = far3_; + return (_e92 * clamp((10f / ((0.00001f + (_e93 * _e94)) + (_e97 * _e98))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -4159,39 +4283,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e84 = fog_info.forward[3u]; - mode = _e84; - let _e85 = mode; - if (_e85 > 0.5f) { - let _e88 = frag_color[3u]; - alpha_1 = _e88; - let _e89 = alpha_1; - param = _e89; - let _e90 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e90; - let _e91 = frag_color; - let _e93 = weight; - let _e94 = (_e91.xyz * _e93); - let _e95 = alpha_1; - let _e96 = weight; - accumulate = vec4(_e94.x, _e94.y, _e94.z, (_e95 * _e96)); - let _e102 = mode; - let _e104 = mode; - revealage = ((_e102 > 1.5f) && (_e104 < 2.5f)); - let _e107 = revealage; - if _e107 { - let _e108 = alpha_1; - local = vec4(_e108); + let _e85 = fog_info.forward[3u]; + mode = _e85; + let _e86 = mode; + if (_e86 > 0.5f) { + let _e89 = frag_color[3u]; + alpha_1 = _e89; + let _e90 = alpha_1; + param = _e90; + let _e91 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e91; + let _e92 = frag_color; + let _e94 = weight; + let _e95 = (_e92.xyz * _e94); + let _e96 = alpha_1; + let _e97 = weight; + accumulate = vec4(_e95.x, _e95.y, _e95.z, (_e96 * _e97)); + let _e103 = mode; + let _e105 = mode; + revealage = ((_e103 > 1.5f) && (_e105 < 2.5f)); + let _e108 = revealage; + if _e108 { + let _e109 = alpha_1; + local = vec4(_e109); } else { - let _e110 = accumulate; - local = _e110; + let _e111 = accumulate; + local = _e111; } - let _e111 = local; - frag_color = _e111; - let _e112 = mode; - if (_e112 > 2.5f) { - let _e114 = alpha_1; - frag_surface = vec4(_e114); + let _e112 = local; + frag_color = _e112; + let _e113 = mode; + if (_e113 > 2.5f) { + let _e115 = alpha_1; + frag_surface = vec4(_e115); } } return; @@ -4200,29 +4324,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e78 = (*n)[0u]; - let _e81 = (*n)[1u]; - let _e85 = (*n)[2u]; - let _e88 = (*n); - (*n) = (_e88 / vec3(((abs(_e78) + abs(_e81)) + abs(_e85)))); - let _e91 = (*n); - e = _e91.xy; - let _e94 = (*n)[2u]; - if (_e94 < 0f) { - let _e96 = (*n); - let _e102 = (*n)[0u]; - let _e106 = (*n)[1u]; - e = ((vec2(1f) - abs(_e96.yx)) * vec2(select(-1f, 1f, (_e102 >= 0f)), select(-1f, 1f, (_e106 >= 0f)))); + let _e79 = (*n)[0u]; + let _e82 = (*n)[1u]; + let _e86 = (*n)[2u]; + let _e89 = (*n); + (*n) = (_e89 / vec3(((abs(_e79) + abs(_e82)) + abs(_e86)))); + let _e92 = (*n); + e = _e92.xy; + let _e95 = (*n)[2u]; + if (_e95 < 0f) { + let _e97 = (*n); + let _e103 = (*n)[0u]; + let _e107 = (*n)[1u]; + e = ((vec2(1f) - abs(_e97.yx)) * vec2(select(-1f, 1f, (_e103 >= 0f)), select(-1f, 1f, (_e107 >= 0f)))); } - let _e111 = e; - return ((_e111 * 0.5f) + vec2(0.5f)); + let _e112 = e; + return ((_e112 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e76 = (*linear); - let _e78 = (*linear); - let _e82 = (*linear); - return mix((_e76 * 12.92f), ((pow(_e78, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e82)); + let _e77 = (*linear); + let _e79 = (*linear); + let _e83 = (*linear); + return mix((_e77 * 12.92f), ((pow(_e79, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e83)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -4230,57 +4354,81 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e79 = g_albedo; - param_1 = clamp(_e79, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e81 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e81.x, _e81.y, _e81.z, 1f); - let _e86 = g_debug_surface_on; - if _e86 { - let _e87 = g_debug_surface; - let _e88 = ViewDepth_u0028_(); - frag_surface = vec4(_e87.x, _e87.y, _e87.z, _e88); + let _e80 = g_albedo; + param_1 = clamp(_e80, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e82 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e82.x, _e82.y, _e82.z, 1f); + let _e87 = g_debug_surface_on; + if _e87 { + let _e88 = g_debug_surface; + let _e89 = ViewDepth_u0028_(); + frag_surface = vec4(_e88.x, _e88.y, _e88.z, _e89); return; } - let _e93 = v_normal_1; - geometric = normalize(_e93); - let _e95 = gl_FrontFacing_1; - if !(_e95) { - let _e97 = geometric; - geometric = -(_e97); - } - let _e99 = geometric; - param_2 = _e99; - let _e100 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e101 = (*roughness); - let _e103 = ViewDepth_u0028_(); - frag_surface = vec4(_e100.x, _e100.y, clamp(_e101, 0f, 1f), _e103); + let _e94 = v_normal_1; + geometric = normalize(_e94); + let _e96 = gl_FrontFacing_1; + if !(_e96) { + let _e98 = geometric; + geometric = -(_e98); + } + let _e100 = geometric; + param_2 = _e100; + let _e101 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e102 = (*roughness); + let _e104 = ViewDepth_u0028_(); + frag_surface = vec4(_e101.x, _e101.y, clamp(_e102, 0f, 1f), _e104); return; } fn EyeDistance_u0028_() -> f32 { - let _e75 = v_world_position_1; - let _e77 = fog_info.eye; - return distance(_e75, _e77.xyz); + let _e76 = v_world_position_1; + let _e78 = fog_info.eye; + return distance(_e76, _e78.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; + var falloff: f32; + var k: f32; + var local_1: f32; - let _e80 = fog_info.fog[3u]; - density = _e80; - let _e81 = density; - if (_e81 <= 0f) { - let _e83 = (*color); - return _e83; + let _e84 = fog_info.fog[3u]; + density = _e84; + let _e85 = density; + if (_e85 <= 0f) { + let _e87 = (*color); + return _e87; + } + let _e88 = EyeDistance_u0028_(); + d = _e88; + let _e91 = fog_info.eye[3u]; + falloff = _e91; + let _e92 = falloff; + if (_e92 > 0f) { + let _e94 = falloff; + let _e96 = v_world_position_1[1u]; + let _e99 = fog_info.eye[1u]; + k = (_e94 * (_e96 - _e99)); + let _e102 = k; + if (abs(_e102) > 0.001f) { + let _e105 = k; + let _e109 = k; + local_1 = ((1f - exp(-(_e105))) / _e109); + } else { + let _e111 = k; + local_1 = (1f - (0.5f * _e111)); + } + let _e114 = local_1; + let _e115 = density; + density = (_e115 * _e114); } - let _e84 = EyeDistance_u0028_(); - d = _e84; - let _e86 = fog_info.fog; - let _e88 = (*color); - let _e89 = density; - let _e91 = d; - return mix(_e86.xyz, _e88, vec3(clamp(exp((-(_e89) * _e91)), 0f, 1f))); + let _e118 = fog_info.fog; + let _e120 = (*color); + let _e121 = density; + let _e123 = d; + return mix(_e118.xyz, _e120, vec3(clamp(exp((-(_e121) * _e123)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -4288,18 +4436,18 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e81 = g_premultiply; - let _e82 = (*alpha_2); - weight_1 = select(1f, _e82, _e81); - let _e84 = (*linearColor); - param_3 = _e84; - let _e85 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e86 = weight_1; - let _e87 = (_e85 * _e86); - let _e88 = (*alpha_2); - frag_color = vec4(_e87.x, _e87.y, _e87.z, _e88); - let _e93 = (*roughness_1); - param_4 = _e93; + let _e82 = g_premultiply; + let _e83 = (*alpha_2); + weight_1 = select(1f, _e83, _e82); + let _e85 = (*linearColor); + param_3 = _e85; + let _e86 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e87 = weight_1; + let _e88 = (_e86 * _e87); + let _e89 = (*alpha_2); + frag_color = vec4(_e88.x, _e88.y, _e88.z, _e89); + let _e94 = (*roughness_1); + param_4 = _e94; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; @@ -4310,25 +4458,25 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b(linearColor_1: ptr> var param_6: f32; var param_7: f32; - let _e80 = (*linearColor_1); - param_5 = _e80; - let _e81 = (*alpha_3); - param_6 = _e81; + let _e81 = (*linearColor_1); + param_5 = _e81; + let _e82 = (*alpha_3); + param_6 = _e82; param_7 = 1f; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_5), (¶m_6), (¶m_7)); return; } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e76 = (*srgb); - let _e79 = (*srgb); - let _e84 = (*srgb); - return mix((_e76 / vec3(12.92f)), pow(((_e79 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e84)); + let _e77 = (*srgb); + let _e80 = (*srgb); + let _e85 = (*srgb); + return mix((_e77 / vec3(12.92f)), pow(((_e80 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e85)); } fn MaterialLodBias_u0028_() -> f32 { - let _e77 = frag_info.target_origin[1u]; - return _e77; + let _e78 = frag_info.target_origin[1u]; + return _e78; } fn ReadSurface_u0028_() -> Surface { @@ -4340,78 +4488,78 @@ fn ReadSurface_u0028_() -> Surface { var anchored: vec3; var noise: f32; - let _e82 = v_texcoord_1; - let _e83 = MaterialLodBias_u0028_(); - let _e84 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e82, _e83); - texel = _e84; - let _e85 = texel; - param_8 = _e85.xyz; - let _e87 = SrgbToLinear_u0028_vf3_u003b((¶m_8)); - let _e89 = frag_info.base_color; - param_9 = _e89.xyz; - let _e91 = SrgbToLinear_u0028_vf3_u003b((¶m_9)); - let _e93 = v_color_1; - s.albedo = ((_e87 * _e91) * _e93.xyz); - let _e98 = texel[3u]; - let _e101 = frag_info.base_color[3u]; - let _e104 = v_color_1[3u]; - s.alpha = ((_e98 * _e101) * _e104); - let _e108 = s.albedo; - g_albedo = _e108; - let _e111 = frag_info.material2_[0u]; - cutoff = _e111; - let _e112 = cutoff; - if (_e112 >= 0f) { - let _e115 = s.alpha; - let _e116 = cutoff; - if (_e115 < _e116) { + let _e83 = v_texcoord_1; + let _e84 = MaterialLodBias_u0028_(); + let _e85 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e83, _e84); + texel = _e85; + let _e86 = texel; + param_8 = _e86.xyz; + let _e88 = SrgbToLinear_u0028_vf3_u003b((¶m_8)); + let _e90 = frag_info.base_color; + param_9 = _e90.xyz; + let _e92 = SrgbToLinear_u0028_vf3_u003b((¶m_9)); + let _e94 = v_color_1; + s.albedo = ((_e88 * _e92) * _e94.xyz); + let _e99 = texel[3u]; + let _e102 = frag_info.base_color[3u]; + let _e105 = v_color_1[3u]; + s.alpha = ((_e99 * _e102) * _e105); + let _e109 = s.albedo; + g_albedo = _e109; + let _e112 = frag_info.material2_[0u]; + cutoff = _e112; + let _e113 = cutoff; + if (_e113 >= 0f) { + let _e116 = s.alpha; + let _e117 = cutoff; + if (_e116 < _e117) { discard; } } else { - let _e118 = cutoff; - if (_e118 < -1.5f) { - let _e120 = v_world_position_1; - anchored = floor((_e120 * 16f)); - let _e123 = anchored; - noise = fract((sin(dot(_e123, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e129 = s.alpha; - let _e130 = noise; - if (_e129 < _e130) { + let _e119 = cutoff; + if (_e119 < -1.5f) { + let _e121 = v_world_position_1; + anchored = floor((_e121 * 16f)); + let _e124 = anchored; + noise = fract((sin(dot(_e124, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e130 = s.alpha; + let _e131 = noise; + if (_e130 < _e131) { discard; } } } - let _e132 = cutoff; - let _e134 = cutoff; - g_premultiply = ((_e132 < 0f) && (_e134 > -0.75f)); - let _e137 = v_normal_1; - s.n = normalize(_e137); - let _e140 = gl_FrontFacing_1; - if !(_e140) { - let _e143 = s.n; - s.n = -(_e143); - } - let _e147 = frag_info.camera_position; - let _e149 = v_world_position_1; - s.v = normalize((_e147.xyz - _e149)); - let _e154 = s.n; - let _e156 = s.v; - s.n_dot_v = max(dot(_e154, _e156), 0.0001f); - let _e162 = frag_info.material[0u]; - s.metallic = clamp(_e162, 0f, 1f); - let _e167 = frag_info.material[1u]; - s.roughness = clamp(_e167, 0.02f, 1f); - let _e171 = frag_info.ambient_ground; - let _e174 = frag_info.ambient_sky; - let _e178 = s.n[1u]; - let _e185 = frag_info.material[2u]; - s.ambient = (mix(_e171.xyz, _e174.xyz, vec3(((_e178 * 0.5f) + 0.5f))) * _e185); - let _e190 = frag_info.frame_params[0u]; - s.exposure = max(_e190, 0f); + let _e133 = cutoff; + let _e135 = cutoff; + g_premultiply = ((_e133 < 0f) && (_e135 > -0.75f)); + let _e138 = v_normal_1; + s.n = normalize(_e138); + let _e141 = gl_FrontFacing_1; + if !(_e141) { + let _e144 = s.n; + s.n = -(_e144); + } + let _e148 = frag_info.camera_position; + let _e150 = v_world_position_1; + s.v = normalize((_e148.xyz - _e150)); + let _e155 = s.n; + let _e157 = s.v; + s.n_dot_v = max(dot(_e155, _e157), 0.0001f); + let _e163 = frag_info.material[0u]; + s.metallic = clamp(_e163, 0f, 1f); + let _e168 = frag_info.material[1u]; + s.roughness = clamp(_e168, 0.02f, 1f); + let _e172 = frag_info.ambient_ground; + let _e175 = frag_info.ambient_sky; + let _e179 = s.n[1u]; + let _e186 = frag_info.material[2u]; + s.ambient = (mix(_e172.xyz, _e175.xyz, vec3(((_e179 * 0.5f) + 0.5f))) * _e186); + let _e191 = frag_info.frame_params[0u]; + s.exposure = max(_e191, 0f); s.occlusion = 1f; s.emissive = vec3(0f, 0f, 0f); - let _e195 = s; - return _e195; + let _e196 = s; + return _e196; } fn main_1() { @@ -4423,13 +4571,13 @@ fn main_1() { g_debug_surface = vec3(0f, 0f, 0f); g_debug_surface_on = false; g_premultiply = false; - let _e78 = ReadSurface_u0028_(); - s_1 = _e78; + let _e79 = ReadSurface_u0028_(); + s_1 = _e79; g_albedo = vec3(0f, 0f, 0f); - let _e80 = s_1.albedo; - param_10 = _e80; - let _e82 = s_1.alpha; - param_11 = _e82; + let _e81 = s_1.albedo; + param_10 = _e81; + let _e83 = s_1.alpha; + param_11 = _e83; WriteSurface_u0028_vf3_u003b_f1_u003b((¶m_10), (¶m_11)); return; } @@ -4647,10 +4795,10 @@ var v_tangent_1: vec4; var v_lightmap_uv_1: vec2; fn ViewDepth_u0028_() -> f32 { - let _e67 = v_world_position_1; - let _e69 = fog_info.eye; - let _e73 = fog_info.forward; - return dot((_e67 - _e69.xyz), _e73.xyz); + let _e68 = v_world_position_1; + let _e70 = fog_info.eye; + let _e74 = fog_info.forward; + return dot((_e68 - _e70.xyz), _e74.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -4659,22 +4807,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e72 = ViewDepth_u0028_(); - z = abs(_e72); - let _e74 = z; - near = (_e74 / 5f); - let _e76 = z; - far = (_e76 / 200f); - let _e78 = far; + let _e73 = ViewDepth_u0028_(); + z = abs(_e73); + let _e75 = z; + near = (_e75 / 5f); + let _e77 = z; + far = (_e77 / 200f); let _e79 = far; - let _e81 = far; - far3_ = ((_e78 * _e79) * _e81); - let _e83 = (*alpha); - let _e84 = near; + let _e80 = far; + let _e82 = far; + far3_ = ((_e79 * _e80) * _e82); + let _e84 = (*alpha); let _e85 = near; - let _e88 = far3_; + let _e86 = near; let _e89 = far3_; - return (_e83 * clamp((10f / ((0.00001f + (_e84 * _e85)) + (_e88 * _e89))), 0.01f, 3000f)); + let _e90 = far3_; + return (_e84 * clamp((10f / ((0.00001f + (_e85 * _e86)) + (_e89 * _e90))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -4686,35 +4834,35 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e76 = fog_info.forward[3u]; - mode = _e76; - let _e77 = mode; - if (_e77 > 0.5f) { - let _e80 = frag_color[3u]; - alpha_1 = _e80; - let _e81 = alpha_1; - param = _e81; - let _e82 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e82; - let _e83 = frag_color; - let _e85 = weight; - let _e86 = (_e83.xyz * _e85); - let _e87 = alpha_1; - let _e88 = weight; - accumulate = vec4(_e86.x, _e86.y, _e86.z, (_e87 * _e88)); - let _e94 = mode; - let _e96 = mode; - revealage = ((_e94 > 1.5f) && (_e96 < 2.5f)); - let _e99 = revealage; - if _e99 { - let _e100 = alpha_1; - local = vec4(_e100); + let _e77 = fog_info.forward[3u]; + mode = _e77; + let _e78 = mode; + if (_e78 > 0.5f) { + let _e81 = frag_color[3u]; + alpha_1 = _e81; + let _e82 = alpha_1; + param = _e82; + let _e83 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e83; + let _e84 = frag_color; + let _e86 = weight; + let _e87 = (_e84.xyz * _e86); + let _e88 = alpha_1; + let _e89 = weight; + accumulate = vec4(_e87.x, _e87.y, _e87.z, (_e88 * _e89)); + let _e95 = mode; + let _e97 = mode; + revealage = ((_e95 > 1.5f) && (_e97 < 2.5f)); + let _e100 = revealage; + if _e100 { + let _e101 = alpha_1; + local = vec4(_e101); } else { - let _e102 = accumulate; - local = _e102; + let _e103 = accumulate; + local = _e103; } - let _e103 = local; - frag_color = _e103; + let _e104 = local; + frag_color = _e104; } return; } @@ -4724,29 +4872,53 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { } fn EyeDistance_u0028_() -> f32 { - let _e67 = v_world_position_1; - let _e69 = fog_info.eye; - return distance(_e67, _e69.xyz); + let _e68 = v_world_position_1; + let _e70 = fog_info.eye; + return distance(_e68, _e70.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; + var falloff: f32; + var k: f32; + var local_1: f32; - let _e72 = fog_info.fog[3u]; - density = _e72; - let _e73 = density; - if (_e73 <= 0f) { - let _e75 = (*color); - return _e75; + let _e76 = fog_info.fog[3u]; + density = _e76; + let _e77 = density; + if (_e77 <= 0f) { + let _e79 = (*color); + return _e79; + } + let _e80 = EyeDistance_u0028_(); + d = _e80; + let _e83 = fog_info.eye[3u]; + falloff = _e83; + let _e84 = falloff; + if (_e84 > 0f) { + let _e86 = falloff; + let _e88 = v_world_position_1[1u]; + let _e91 = fog_info.eye[1u]; + k = (_e86 * (_e88 - _e91)); + let _e94 = k; + if (abs(_e94) > 0.001f) { + let _e97 = k; + let _e101 = k; + local_1 = ((1f - exp(-(_e97))) / _e101); + } else { + let _e103 = k; + local_1 = (1f - (0.5f * _e103)); + } + let _e106 = local_1; + let _e107 = density; + density = (_e107 * _e106); } - let _e76 = EyeDistance_u0028_(); - d = _e76; - let _e78 = fog_info.fog; - let _e80 = (*color); - let _e81 = density; - let _e83 = d; - return mix(_e78.xyz, _e80, vec3(clamp(exp((-(_e81) * _e83)), 0f, 1f))); + let _e110 = fog_info.fog; + let _e112 = (*color); + let _e113 = density; + let _e115 = d; + return mix(_e110.xyz, _e112, vec3(clamp(exp((-(_e113) * _e115)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -4754,18 +4926,18 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_2: f32; - let _e73 = g_premultiply; - let _e74 = (*alpha_2); - weight_1 = select(1f, _e74, _e73); - let _e76 = (*linearColor); - param_1 = _e76; - let _e77 = ApplyFog_u0028_vf3_u003b((¶m_1)); - let _e78 = weight_1; - let _e79 = (_e77 * _e78); - let _e80 = (*alpha_2); - frag_color = vec4(_e79.x, _e79.y, _e79.z, _e80); - let _e85 = (*roughness_1); - param_2 = _e85; + let _e74 = g_premultiply; + let _e75 = (*alpha_2); + weight_1 = select(1f, _e75, _e74); + let _e77 = (*linearColor); + param_1 = _e77; + let _e78 = ApplyFog_u0028_vf3_u003b((¶m_1)); + let _e79 = weight_1; + let _e80 = (_e78 * _e79); + let _e81 = (*alpha_2); + frag_color = vec4(_e80.x, _e80.y, _e80.z, _e81); + let _e86 = (*roughness_1); + param_2 = _e86; WriteSurfaceGeometry_u0028_f1_u003b((¶m_2)); WriteWeightedBlended_u0028_(); return; @@ -4776,25 +4948,25 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b(linearColor_1: ptr> var param_4: f32; var param_5: f32; - let _e72 = (*linearColor_1); - param_3 = _e72; - let _e73 = (*alpha_3); - param_4 = _e73; + let _e73 = (*linearColor_1); + param_3 = _e73; + let _e74 = (*alpha_3); + param_4 = _e74; param_5 = 1f; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_3), (¶m_4), (¶m_5)); return; } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e68 = (*srgb); - let _e71 = (*srgb); - let _e76 = (*srgb); - return mix((_e68 / vec3(12.92f)), pow(((_e71 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e76)); + let _e69 = (*srgb); + let _e72 = (*srgb); + let _e77 = (*srgb); + return mix((_e69 / vec3(12.92f)), pow(((_e72 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e77)); } fn MaterialLodBias_u0028_() -> f32 { - let _e69 = frag_info.target_origin[1u]; - return _e69; + let _e70 = frag_info.target_origin[1u]; + return _e70; } fn ReadSurface_u0028_() -> Surface { @@ -4806,78 +4978,78 @@ fn ReadSurface_u0028_() -> Surface { var anchored: vec3; var noise: f32; - let _e74 = v_texcoord_1; - let _e75 = MaterialLodBias_u0028_(); - let _e76 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e74, _e75); - texel = _e76; - let _e77 = texel; - param_6 = _e77.xyz; - let _e79 = SrgbToLinear_u0028_vf3_u003b((¶m_6)); - let _e81 = frag_info.base_color; - param_7 = _e81.xyz; - let _e83 = SrgbToLinear_u0028_vf3_u003b((¶m_7)); - let _e85 = v_color_1; - s.albedo = ((_e79 * _e83) * _e85.xyz); - let _e90 = texel[3u]; - let _e93 = frag_info.base_color[3u]; - let _e96 = v_color_1[3u]; - s.alpha = ((_e90 * _e93) * _e96); - let _e100 = s.albedo; - g_albedo = _e100; - let _e103 = frag_info.material2_[0u]; - cutoff = _e103; - let _e104 = cutoff; - if (_e104 >= 0f) { - let _e107 = s.alpha; - let _e108 = cutoff; - if (_e107 < _e108) { + let _e75 = v_texcoord_1; + let _e76 = MaterialLodBias_u0028_(); + let _e77 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e75, _e76); + texel = _e77; + let _e78 = texel; + param_6 = _e78.xyz; + let _e80 = SrgbToLinear_u0028_vf3_u003b((¶m_6)); + let _e82 = frag_info.base_color; + param_7 = _e82.xyz; + let _e84 = SrgbToLinear_u0028_vf3_u003b((¶m_7)); + let _e86 = v_color_1; + s.albedo = ((_e80 * _e84) * _e86.xyz); + let _e91 = texel[3u]; + let _e94 = frag_info.base_color[3u]; + let _e97 = v_color_1[3u]; + s.alpha = ((_e91 * _e94) * _e97); + let _e101 = s.albedo; + g_albedo = _e101; + let _e104 = frag_info.material2_[0u]; + cutoff = _e104; + let _e105 = cutoff; + if (_e105 >= 0f) { + let _e108 = s.alpha; + let _e109 = cutoff; + if (_e108 < _e109) { discard; } } else { - let _e110 = cutoff; - if (_e110 < -1.5f) { - let _e112 = v_world_position_1; - anchored = floor((_e112 * 16f)); - let _e115 = anchored; - noise = fract((sin(dot(_e115, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e121 = s.alpha; - let _e122 = noise; - if (_e121 < _e122) { + let _e111 = cutoff; + if (_e111 < -1.5f) { + let _e113 = v_world_position_1; + anchored = floor((_e113 * 16f)); + let _e116 = anchored; + noise = fract((sin(dot(_e116, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e122 = s.alpha; + let _e123 = noise; + if (_e122 < _e123) { discard; } } } - let _e124 = cutoff; - let _e126 = cutoff; - g_premultiply = ((_e124 < 0f) && (_e126 > -0.75f)); - let _e129 = v_normal_1; - s.n = normalize(_e129); - let _e132 = gl_FrontFacing_1; - if !(_e132) { - let _e135 = s.n; - s.n = -(_e135); - } - let _e139 = frag_info.camera_position; - let _e141 = v_world_position_1; - s.v = normalize((_e139.xyz - _e141)); - let _e146 = s.n; - let _e148 = s.v; - s.n_dot_v = max(dot(_e146, _e148), 0.0001f); - let _e154 = frag_info.material[0u]; - s.metallic = clamp(_e154, 0f, 1f); - let _e159 = frag_info.material[1u]; - s.roughness = clamp(_e159, 0.02f, 1f); - let _e163 = frag_info.ambient_ground; - let _e166 = frag_info.ambient_sky; - let _e170 = s.n[1u]; - let _e177 = frag_info.material[2u]; - s.ambient = (mix(_e163.xyz, _e166.xyz, vec3(((_e170 * 0.5f) + 0.5f))) * _e177); - let _e182 = frag_info.frame_params[0u]; - s.exposure = max(_e182, 0f); + let _e125 = cutoff; + let _e127 = cutoff; + g_premultiply = ((_e125 < 0f) && (_e127 > -0.75f)); + let _e130 = v_normal_1; + s.n = normalize(_e130); + let _e133 = gl_FrontFacing_1; + if !(_e133) { + let _e136 = s.n; + s.n = -(_e136); + } + let _e140 = frag_info.camera_position; + let _e142 = v_world_position_1; + s.v = normalize((_e140.xyz - _e142)); + let _e147 = s.n; + let _e149 = s.v; + s.n_dot_v = max(dot(_e147, _e149), 0.0001f); + let _e155 = frag_info.material[0u]; + s.metallic = clamp(_e155, 0f, 1f); + let _e160 = frag_info.material[1u]; + s.roughness = clamp(_e160, 0.02f, 1f); + let _e164 = frag_info.ambient_ground; + let _e167 = frag_info.ambient_sky; + let _e171 = s.n[1u]; + let _e178 = frag_info.material[2u]; + s.ambient = (mix(_e164.xyz, _e167.xyz, vec3(((_e171 * 0.5f) + 0.5f))) * _e178); + let _e183 = frag_info.frame_params[0u]; + s.exposure = max(_e183, 0f); s.occlusion = 1f; s.emissive = vec3(0f, 0f, 0f); - let _e187 = s; - return _e187; + let _e188 = s; + return _e188; } fn main_1() { @@ -4889,12 +5061,12 @@ fn main_1() { g_debug_surface = vec3(0f, 0f, 0f); g_debug_surface_on = false; g_premultiply = false; - let _e70 = ReadSurface_u0028_(); - s_1 = _e70; - let _e72 = s_1.albedo; - param_8 = _e72; - let _e74 = s_1.alpha; - param_9 = _e74; + let _e71 = ReadSurface_u0028_(); + s_1 = _e71; + let _e73 = s_1.albedo; + param_8 = _e73; + let _e75 = s_1.alpha; + param_9 = _e75; WriteSurface_u0028_vf3_u003b_f1_u003b((¶m_8), (¶m_9)); return; } @@ -5081,46 +5253,70 @@ var v_color_1: vec4; var v_lightmap_uv_1: vec2; fn EyeDistance_u0028_() -> f32 { - let _e29 = v_world_position_1; - let _e31 = fog_info.eye; - return distance(_e29, _e31.xyz); + let _e31 = v_world_position_1; + let _e33 = fog_info.eye; + return distance(_e31, _e33.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; + var falloff: f32; + var k: f32; + var local: f32; - let _e34 = fog_info.fog[3u]; - density = _e34; - let _e35 = density; - if (_e35 <= 0f) { - let _e37 = (*color); - return _e37; + let _e39 = fog_info.fog[3u]; + density = _e39; + let _e40 = density; + if (_e40 <= 0f) { + let _e42 = (*color); + return _e42; + } + let _e43 = EyeDistance_u0028_(); + d = _e43; + let _e46 = fog_info.eye[3u]; + falloff = _e46; + let _e47 = falloff; + if (_e47 > 0f) { + let _e49 = falloff; + let _e51 = v_world_position_1[1u]; + let _e54 = fog_info.eye[1u]; + k = (_e49 * (_e51 - _e54)); + let _e57 = k; + if (abs(_e57) > 0.001f) { + let _e60 = k; + let _e64 = k; + local = ((1f - exp(-(_e60))) / _e64); + } else { + let _e66 = k; + local = (1f - (0.5f * _e66)); + } + let _e69 = local; + let _e70 = density; + density = (_e70 * _e69); } - let _e38 = EyeDistance_u0028_(); - d = _e38; - let _e40 = fog_info.fog; - let _e42 = (*color); - let _e43 = density; - let _e45 = d; - return mix(_e40.xyz, _e42, vec3(clamp(exp((-(_e43) * _e45)), 0f, 1f))); + let _e73 = fog_info.fog; + let _e75 = (*color); + let _e76 = density; + let _e78 = d; + return mix(_e73.xyz, _e75, vec3(clamp(exp((-(_e76) * _e78)), 0f, 1f))); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local: vec2; + var local_1: vec2; - let _e31 = (*rows); - if (_e31 > 0f) { - let _e34 = gl_FragCoord_1[0u]; - let _e35 = (*rows); - let _e37 = gl_FragCoord_1[1u]; - local = vec2(_e34, (_e35 - _e37)); + let _e33 = (*rows); + if (_e33 > 0f) { + let _e36 = gl_FragCoord_1[0u]; + let _e37 = (*rows); + let _e39 = gl_FragCoord_1[1u]; + local_1 = vec2(_e36, (_e37 - _e39)); } else { - let _e40 = gl_FragCoord_1; - local = _e40.xy; + let _e42 = gl_FragCoord_1; + local_1 = _e42.xy; } - let _e42 = local; - return _e42; + let _e44 = local_1; + return _e44; } fn main_1() { @@ -5143,61 +5339,61 @@ fn main_1() { g_debug_surface = vec3(0f, 0f, 0f); g_debug_surface_on = false; g_premultiply = false; - let _e45 = planar_info.params[2u]; - rows_1 = _e45; - let _e46 = rows_1; - param = _e46; - let _e47 = FragCoordFromTop_u0028_f1_u003b((¶m)); - pixel = _e47; - let _e48 = pixel; - let _e50 = planar_info.view; - let _e54 = planar_info.view; - uv = ((_e48 - _e50.xy) / _e54.zw); - let _e57 = rows_1; - if (_e57 > 0f) { - let _e60 = uv[1u]; - uv[1u] = (1f - _e60); - } - let _e63 = uv; - let _e64 = textureSampleLevel(reflection_texture_tex, reflection_texture_smp, _e63, 0f); - let _e67 = planar_info.tint; - reflected = (_e64.xyz * _e67.xyz); - let _e70 = v_normal_1; - n = normalize(_e70); - let _e72 = gl_FrontFacing_1; - if !(_e72) { - let _e74 = n; - n = -(_e74); - } - let _e77 = fog_info.eye; - let _e79 = v_world_position_1; - v = normalize((_e77.xyz - _e79)); - let _e82 = n; - let _e83 = v; - cosine = clamp(dot(_e82, _e83), 0f, 1f); - let _e88 = planar_info.params[0u]; - f0_ = _e88; - let _e89 = cosine; - grazing = (1f - _e89); - let _e91 = grazing; - let _e92 = grazing; - grazing2_ = (_e91 * _e92); - let _e94 = f0_; - let _e95 = f0_; - let _e97 = grazing2_; + let _e47 = planar_info.params[2u]; + rows_1 = _e47; + let _e48 = rows_1; + param = _e48; + let _e49 = FragCoordFromTop_u0028_f1_u003b((¶m)); + pixel = _e49; + let _e50 = pixel; + let _e52 = planar_info.view; + let _e56 = planar_info.view; + uv = ((_e50 - _e52.xy) / _e56.zw); + let _e59 = rows_1; + if (_e59 > 0f) { + let _e62 = uv[1u]; + uv[1u] = (1f - _e62); + } + let _e65 = uv; + let _e66 = textureSampleLevel(reflection_texture_tex, reflection_texture_smp, _e65, 0f); + let _e69 = planar_info.tint; + reflected = (_e66.xyz * _e69.xyz); + let _e72 = v_normal_1; + n = normalize(_e72); + let _e74 = gl_FrontFacing_1; + if !(_e74) { + let _e76 = n; + n = -(_e76); + } + let _e79 = fog_info.eye; + let _e81 = v_world_position_1; + v = normalize((_e79.xyz - _e81)); + let _e84 = n; + let _e85 = v; + cosine = clamp(dot(_e84, _e85), 0f, 1f); + let _e90 = planar_info.params[0u]; + f0_ = _e90; + let _e91 = cosine; + grazing = (1f - _e91); + let _e93 = grazing; + let _e94 = grazing; + grazing2_ = (_e93 * _e94); + let _e96 = f0_; + let _e97 = f0_; let _e99 = grazing2_; - let _e101 = grazing; - fresnel = (_e94 + ((((1f - _e95) * _e97) * _e99) * _e101)); - let _e104 = fresnel; - let _e107 = planar_info.params[1u]; - alpha = clamp((_e104 * _e107), 0f, 1f); - let _e110 = reflected; - param_1 = _e110; - let _e111 = ApplyFog_u0028_vf3_u003b((¶m_1)); - let _e112 = alpha; - let _e113 = (_e111 * _e112); + let _e101 = grazing2_; + let _e103 = grazing; + fresnel = (_e96 + ((((1f - _e97) * _e99) * _e101) * _e103)); + let _e106 = fresnel; + let _e109 = planar_info.params[1u]; + alpha = clamp((_e106 * _e109), 0f, 1f); + let _e112 = reflected; + param_1 = _e112; + let _e113 = ApplyFog_u0028_vf3_u003b((¶m_1)); let _e114 = alpha; - frag_color = vec4(_e113.x, _e113.y, _e113.z, _e114); + let _e115 = (_e113 * _e114); + let _e116 = alpha; + frag_color = vec4(_e115.x, _e115.y, _e115.z, _e116); return; } @@ -5566,21 +5762,45 @@ fn EyeDistance_u0028_() -> f32 { fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; + var falloff: f32; + var k: f32; + var local_1: f32; - let _e180 = fog_info.fog[3u]; - density = _e180; - let _e181 = density; - if (_e181 <= 0f) { - let _e183 = (*color); - return _e183; + let _e183 = fog_info.fog[3u]; + density = _e183; + let _e184 = density; + if (_e184 <= 0f) { + let _e186 = (*color); + return _e186; + } + let _e187 = EyeDistance_u0028_(); + d = _e187; + let _e190 = fog_info.eye[3u]; + falloff = _e190; + let _e191 = falloff; + if (_e191 > 0f) { + let _e193 = falloff; + let _e195 = v_world_position_1[1u]; + let _e198 = fog_info.eye[1u]; + k = (_e193 * (_e195 - _e198)); + let _e201 = k; + if (abs(_e201) > 0.001f) { + let _e204 = k; + let _e208 = k; + local_1 = ((1f - exp(-(_e204))) / _e208); + } else { + let _e210 = k; + local_1 = (1f - (0.5f * _e210)); + } + let _e213 = local_1; + let _e214 = density; + density = (_e214 * _e213); } - let _e184 = EyeDistance_u0028_(); - d = _e184; - let _e186 = fog_info.fog; - let _e188 = (*color); - let _e189 = density; - let _e191 = d; - return mix(_e186.xyz, _e188, vec3(clamp(exp((-(_e189) * _e191)), 0f, 1f))); + let _e217 = fog_info.fog; + let _e219 = (*color); + let _e220 = density; + let _e222 = d; + return mix(_e217.xyz, _e219, vec3(clamp(exp((-(_e220) * _e222)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -5664,7 +5884,7 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_1: vec2; + var local_2: vec2; var atlas: vec2; let _e184 = point_shadow.params[0u]; @@ -5673,8 +5893,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr(0.16666667f, 0.16666667f)); let _e200 = point_shadow.params3_[0u]; @@ -5832,19 +6052,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_2: vec2; + var local_3: vec2; let _e177 = (*rows); if (_e177 > 0f) { let _e180 = gl_FragCoord_1[0u]; let _e181 = (*rows); let _e183 = gl_FragCoord_1[1u]; - local_2 = vec2(_e180, (_e181 - _e183)); + local_3 = vec2(_e180, (_e181 - _e183)); } else { let _e186 = gl_FragCoord_1; - local_2 = _e186.xy; + local_3 = _e186.xy; } - let _e188 = local_2; + let _e188 = local_3; return _e188; } @@ -5883,7 +6103,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_4: vec3; var lit: f32; var param_31: vec2; var param_32: vec2; @@ -5900,8 +6120,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_43: f32; var param_44: f32; - var phi_2146_: bool; - var phi_2207_: bool; + var phi_2181_: bool; + var phi_2242_: bool; let _e229 = (*lightIndex); let _e233 = point_shadow.slots[_e229][0u]; @@ -5966,13 +6186,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e329); - phi_2146_ = _e330; + phi_2181_ = _e330; if _e330 { let _e332 = a[0u]; let _e334 = a[2u]; - phi_2146_ = (_e332 >= _e334); + phi_2181_ = (_e332 >= _e334); } - let _e337 = phi_2146_; + let _e337 = phi_2181_; if _e337 { let _e339 = toFragment[0u]; face = select(1i, 0i, (_e339 > 0f)); @@ -6002,12 +6222,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2207_ = _e380; + phi_2242_ = _e380; if !(_e380) { let _e383 = ndc[1u]; - phi_2207_ = (abs(_e383) > 1f); + phi_2242_ = (abs(_e383) > 1f); } - let _e387 = phi_2207_; + let _e387 = phi_2242_; if _e387 { return 1f; } @@ -6057,15 +6277,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_4 = vec3(0f, 0f, 1f); } else { let _e442 = radius_1; let _e445 = point_shadow.params2_[2u]; let _e449 = blocker_1; let _e450 = range_3; - local_3 = vec3(clamp((_e442 / max(_e445, 0.000001f)), 0f, 1f), clamp((_e449 / _e450), 0f, 1f), 0f); + local_4 = vec3(clamp((_e442 / max(_e445, 0.000001f)), 0f, 1f), clamp((_e449 / _e450), 0f, 1f), 0f); } - let _e454 = local_3; + let _e454 = local_4; g_debug_surface = _e454; } let _e455 = radius_1; @@ -6257,8 +6477,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_4: mat4x4; var local_5: mat4x4; + var local_6: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -6272,8 +6492,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_6: f32; var local_7: f32; + var local_8: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -6307,11 +6527,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_63: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3242_: bool; - var phi_3253_: bool; - var phi_3414_: bool; - var phi_3421_: bool; - var phi_3428_: bool; + var phi_3276_: bool; + var phi_3287_: bool; + var phi_3448_: bool; + var phi_3455_: bool; + var phi_3462_: bool; let _e243 = frag_info.shadow_params[3u]; strength_1 = _e243; @@ -6332,25 +6552,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e264 = cascadeCount; let _e265 = (_e264 > 1i); - phi_3242_ = _e265; + phi_3276_ = _e265; if _e265 { let _e266 = viewDistance; let _e269 = frag_info.shadow_cascades[0u]; - phi_3242_ = (_e266 > _e269); + phi_3276_ = (_e266 > _e269); } - let _e272 = phi_3242_; + let _e272 = phi_3276_; if _e272 { cascade = 1i; } let _e273 = cascadeCount; let _e274 = (_e273 > 2i); - phi_3253_ = _e274; + phi_3287_ = _e274; if _e274 { let _e275 = viewDistance; let _e278 = frag_info.shadow_cascades[1u]; - phi_3253_ = (_e275 > _e278); + phi_3287_ = (_e275 > _e278); } - let _e281 = phi_3253_; + let _e281 = phi_3287_; if _e281 { cascade = 2i; } @@ -6375,20 +6595,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e290 = which; if (_e290 == 0i) { let _e293 = frag_info.shadow_matrix; - local_4 = _e293; + local_5 = _e293; } else { let _e294 = which; if (_e294 == 1i) { let _e297 = frag_info.shadow_matrix_far; - local_5 = _e297; + local_6 = _e297; } else { let _e299 = frag_info.shadow_matrix_farthest; - local_5 = _e299; + local_6 = _e299; } - let _e300 = local_5; - local_4 = _e300; + let _e300 = local_6; + local_5 = _e300; } - let _e301 = local_4; + let _e301 = local_5; matrix = _e301; let _e304 = matrix[0][0u]; let _e307 = matrix[1][0u]; @@ -6438,24 +6658,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e388 * 0.5f) + 0.5f), (0.5f - (_e392 * 0.5f))); let _e397 = inTile[0u]; let _e398 = (_e397 < 0f); - phi_3414_ = _e398; + phi_3448_ = _e398; if !(_e398) { let _e401 = inTile[0u]; - phi_3414_ = (_e401 > 1f); + phi_3448_ = (_e401 > 1f); } - let _e404 = phi_3414_; - phi_3421_ = _e404; + let _e404 = phi_3448_; + phi_3455_ = _e404; if !(_e404) { let _e407 = inTile[1u]; - phi_3421_ = (_e407 < 0f); + phi_3455_ = (_e407 < 0f); } - let _e410 = phi_3421_; - phi_3428_ = _e410; + let _e410 = phi_3455_; + phi_3462_ = _e410; if !(_e410) { let _e413 = inTile[1u]; - phi_3428_ = (_e413 > 1f); + phi_3462_ = (_e413 > 1f); } - let _e416 = phi_3428_; + let _e416 = phi_3462_; if _e416 { continue; } @@ -6512,20 +6732,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e485 = cascade; if (_e485 == 0i) { let _e489 = frag_info.shadow_bias[0u]; - local_6 = _e489; + local_7 = _e489; } else { let _e490 = cascade; if (_e490 == 1i) { let _e494 = frag_info.shadow_bias[1u]; - local_7 = _e494; + local_8 = _e494; } else { let _e497 = frag_info.shadow_bias[2u]; - local_7 = _e497; + local_8 = _e497; } - let _e498 = local_7; - local_6 = _e498; + let _e498 = local_8; + local_7 = _e498; } - let _e499 = local_6; + let _e499 = local_7; bias = _e499; let _e502 = frag_info.shadow_params[0u]; let _e505 = frag_info.shadow_cascades[3u]; @@ -6770,7 +6990,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_8: vec3; + var local_9: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -6812,15 +7032,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e225 > 0f) { let _e227 = (*mirror); let _e228 = t_1; - local_8 = (_e227 * _e228); + local_9 = (_e227 * _e228); } else { let _e230 = toPlane; let _e231 = n_2; let _e232 = toPlane; let _e233 = n_2; - local_8 = (_e230 - (_e231 * dot(_e232, _e233))); + local_9 = (_e230 - (_e231 * dot(_e232, _e233))); } - let _e237 = local_8; + let _e237 = local_9; onPlane = _e237; } let _e238 = onPlane; @@ -6854,7 +7074,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_9: f32; + var local_10: f32; let _e184 = (*a_1); let _e185 = (*a_1); @@ -6876,11 +7096,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> let _e209 = axis; let _e210 = (*n_3); let _e213 = len; - local_9 = ((_e208 * dot(_e209, _e210)) / _e213); + local_10 = ((_e208 * dot(_e209, _e210)) / _e213); } else { - local_9 = 0f; + local_10 = 0f; } - let _e215 = local_9; + let _e215 = local_10; return _e215; } @@ -6891,15 +7111,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_10: i32; + var local_11: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_11: f32; + var local_12: f32; var aAbove: bool; var bAbove: bool; - var local_12: f32; + var local_13: f32; var param_67: vec3; var param_68: vec3; var param_69: vec3; @@ -6919,12 +7139,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr 0.000000000001f) { let _e226 = ha; let _e227 = d_3; - local_11 = (_e226 / _e227); + local_12 = (_e226 / _e227); } else { - local_11 = 0f; + local_12 = 0f; } - let _e229 = local_11; + let _e229 = local_12; q = (_e219 + ((_e220 - _e221) * _e229)); let _e232 = ha; aAbove = (_e232 > 0f); @@ -6967,11 +7187,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_13: f32; var local_14: f32; var local_15: f32; + var local_16: f32; let _e181 = (*slot_1); let _e182 = (*slot_1); @@ -7018,28 +7238,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 let _e186 = lane; if (_e186 == 0i) { let _e189 = (*four)[0u]; - local_13 = _e189; + local_14 = _e189; } else { let _e190 = lane; if (_e190 == 1i) { let _e193 = (*four)[1u]; - local_14 = _e193; + local_15 = _e193; } else { let _e194 = lane; if (_e194 == 2i) { let _e197 = (*four)[2u]; - local_15 = _e197; + local_16 = _e197; } else { let _e199 = (*four)[3u]; - local_15 = _e199; + local_16 = _e199; } - let _e200 = local_15; - local_14 = _e200; + let _e200 = local_16; + local_15 = _e200; } - let _e201 = local_14; - local_13 = _e201; + let _e201 = local_15; + local_14 = _e201; } - let _e202 = local_13; + let _e202 = local_14; return _e202; } @@ -7150,12 +7370,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_16: f32; + var local_17: f32; var param_81: i32; var param_82: i32; var v_1: f32; var u_1: f32; - var local_17: f32; + var local_18: f32; var param_83: f32; var param_84: i32; var type_39: f32; @@ -7165,12 +7385,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_18: f32; + var local_19: f32; var param_85: array, 4>; var param_86: vec3; var area: f32; var radiance: f32; - var local_19: f32; + var local_20: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -7183,7 +7403,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_91: vec3; var toPoint: vec3; var pointDistance: f32; - var local_20: vec3; + var local_21: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; @@ -7218,14 +7438,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e250 = slot_5; param_81 = _e250; let _e251 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_16 = _e251; + local_17 = _e251; } else { let _e252 = slot_5; param_82 = _e252; let _e253 = LightListRow_u0028_i1_u003b((¶m_82)); - local_16 = _e253; + local_17 = _e253; } - let _e254 = local_16; + let _e254 = local_17; listRow = _e254; let _e255 = listRow; let _e259 = light_list_info.list[2u]; @@ -7253,14 +7473,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e285 = listRow; param_83 = _e285; let _e286 = InSlots_u0028_f1_u003b((¶m_83)); - local_17 = select(1f, 0f, _e286); + local_18 = select(1f, 0f, _e286); } else { let _e288 = slot_5; param_84 = _e288; let _e289 = LightListScale_u0028_i1_u003b((¶m_84)); - local_17 = _e289; + local_18 = _e289; } - let _e290 = local_17; + let _e290 = local_18; let _e292 = color_1[3u]; color_1[3u] = (_e292 * _e290); } @@ -7297,16 +7517,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e331, cross(_e332, _e333)) >= 0f); let _e337 = behind; if _e337 { - local_18 = 0f; + local_19 = 0f; } else { let _e338 = corners_1; param_85 = _e338; let _e340 = (*s_3).n; param_86 = _e340; let _e341 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_18 = _e341; + local_19 = _e341; } - let _e342 = local_18; + let _e342 = local_19; formFactor = _e342; let _e343 = halfWidth_1; let _e344 = halfHeight_1; @@ -7314,11 +7534,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e348 = area; if (_e348 > 0.000000001f) { let _e350 = area; - local_19 = (1f / _e350); + local_20 = (1f / _e350); } else { - local_19 = 0f; + local_20 = 0f; } - let _e352 = local_19; + let _e352 = local_20; radiance = _e352; let _e353 = toCentre; distance_2 = length(_e353); @@ -7361,12 +7581,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e394 > 0.000001f) { let _e396 = toPoint; let _e397 = pointDistance; - local_20 = (_e396 / vec3(_e397)); + local_21 = (_e396 / vec3(_e397)); } else { let _e401 = (*s_3).n; - local_20 = _e401; + local_21 = _e401; } - let _e402 = local_20; + let _e402 = local_21; light_3.l = _e402; let _e405 = light_3.l; let _e407 = (*s_3).v; @@ -7462,7 +7682,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_21: f32; + var local_22: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -7490,15 +7710,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e235 = clip_1[3u]; let _e236 = near_1; if (_e235 <= _e236) { - local_21 = 0f; + local_22 = 0f; } else { let _e239 = clip_1[3u]; let _e240 = near_1; let _e245 = light_list_info.cluster_depth[1u]; let _e249 = grid[2u]; - local_21 = clamp(floor((log((_e239 / _e240)) * _e245)), 0f, (_e249 - 1f)); + local_22 = clamp(floor((log((_e239 / _e240)) * _e245)), 0f, (_e249 - 1f)); } - let _e252 = local_21; + let _e252 = local_22; tz = _e252; let _e253 = tx; let _e254 = ty; @@ -7552,7 +7772,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_98: Surface; var visibility: f32; var param_99: i32; - var local_22: f32; + var local_23: f32; var param_100: Surface; var param_101: LightSample; var param_102: i32; @@ -7602,11 +7822,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e214 = i_8; param_105 = _e214; let _e215 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_22 = (_e210 * _e215); + local_23 = (_e210 * _e215); } else { - local_22 = 1f; + local_23 = 1f; } - let _e217 = local_22; + let _e217 = local_23; visibility = _e217; let _e218 = visibility; if (_e218 <= 0f) { @@ -7878,7 +8098,7 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var param_120: f32; var param_121: vec2; - var local_23: vec3; + var local_24: vec3; let _e222 = irradiance_info.origin; origin_2 = _e222.xyz; @@ -8053,11 +8273,11 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr 0f) { let _e450 = total_2; let _e451 = weights; - local_23 = (_e450 / vec3(_e451)); + local_24 = (_e450 / vec3(_e451)); } else { - local_23 = vec3(0f, 0f, 0f); + local_24 = vec3(0f, 0f, 0f); } - let _e454 = local_23; + let _e454 = local_24; return _e454; } @@ -8864,21 +9084,45 @@ fn EyeDistance_u0028_() -> f32 { fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; + var falloff: f32; + var k: f32; + var local_1: f32; - let _e181 = fog_info.fog[3u]; - density = _e181; - let _e182 = density; - if (_e182 <= 0f) { - let _e184 = (*color); - return _e184; + let _e184 = fog_info.fog[3u]; + density = _e184; + let _e185 = density; + if (_e185 <= 0f) { + let _e187 = (*color); + return _e187; + } + let _e188 = EyeDistance_u0028_(); + d = _e188; + let _e191 = fog_info.eye[3u]; + falloff = _e191; + let _e192 = falloff; + if (_e192 > 0f) { + let _e194 = falloff; + let _e196 = v_world_position_1[1u]; + let _e199 = fog_info.eye[1u]; + k = (_e194 * (_e196 - _e199)); + let _e202 = k; + if (abs(_e202) > 0.001f) { + let _e205 = k; + let _e209 = k; + local_1 = ((1f - exp(-(_e205))) / _e209); + } else { + let _e211 = k; + local_1 = (1f - (0.5f * _e211)); + } + let _e214 = local_1; + let _e215 = density; + density = (_e215 * _e214); } - let _e185 = EyeDistance_u0028_(); - d = _e185; - let _e187 = fog_info.fog; - let _e189 = (*color); - let _e190 = density; - let _e192 = d; - return mix(_e187.xyz, _e189, vec3(clamp(exp((-(_e190) * _e192)), 0f, 1f))); + let _e218 = fog_info.fog; + let _e220 = (*color); + let _e221 = density; + let _e223 = d; + return mix(_e218.xyz, _e220, vec3(clamp(exp((-(_e221) * _e223)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -8972,7 +9216,7 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_1: vec2; + var local_2: vec2; var atlas: vec2; let _e185 = point_shadow.params[0u]; @@ -8981,8 +9225,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr(0.16666667f, 0.16666667f)); let _e201 = point_shadow.params3_[0u]; @@ -9140,19 +9384,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_2: vec2; + var local_3: vec2; let _e178 = (*rows); if (_e178 > 0f) { let _e181 = gl_FragCoord_1[0u]; let _e182 = (*rows); let _e184 = gl_FragCoord_1[1u]; - local_2 = vec2(_e181, (_e182 - _e184)); + local_3 = vec2(_e181, (_e182 - _e184)); } else { let _e187 = gl_FragCoord_1; - local_2 = _e187.xy; + local_3 = _e187.xy; } - let _e189 = local_2; + let _e189 = local_3; return _e189; } @@ -9191,7 +9435,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_4: vec3; var lit: f32; var param_31: vec2; var param_32: vec2; @@ -9208,8 +9452,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_43: f32; var param_44: f32; - var phi_2149_: bool; - var phi_2210_: bool; + var phi_2184_: bool; + var phi_2245_: bool; let _e230 = (*lightIndex); let _e234 = point_shadow.slots[_e230][0u]; @@ -9274,13 +9518,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e330); - phi_2149_ = _e331; + phi_2184_ = _e331; if _e331 { let _e333 = a[0u]; let _e335 = a[2u]; - phi_2149_ = (_e333 >= _e335); + phi_2184_ = (_e333 >= _e335); } - let _e338 = phi_2149_; + let _e338 = phi_2184_; if _e338 { let _e340 = toFragment[0u]; face = select(1i, 0i, (_e340 > 0f)); @@ -9310,12 +9554,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2210_ = _e381; + phi_2245_ = _e381; if !(_e381) { let _e384 = ndc[1u]; - phi_2210_ = (abs(_e384) > 1f); + phi_2245_ = (abs(_e384) > 1f); } - let _e388 = phi_2210_; + let _e388 = phi_2245_; if _e388 { return 1f; } @@ -9365,15 +9609,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_4 = vec3(0f, 0f, 1f); } else { let _e443 = radius_1; let _e446 = point_shadow.params2_[2u]; let _e450 = blocker_1; let _e451 = range_3; - local_3 = vec3(clamp((_e443 / max(_e446, 0.000001f)), 0f, 1f), clamp((_e450 / _e451), 0f, 1f), 0f); + local_4 = vec3(clamp((_e443 / max(_e446, 0.000001f)), 0f, 1f), clamp((_e450 / _e451), 0f, 1f), 0f); } - let _e455 = local_3; + let _e455 = local_4; g_debug_surface = _e455; } let _e456 = radius_1; @@ -9565,8 +9809,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_4: mat4x4; var local_5: mat4x4; + var local_6: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -9580,8 +9824,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_6: f32; var local_7: f32; + var local_8: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -9615,11 +9859,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_63: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3269_: bool; - var phi_3280_: bool; - var phi_3441_: bool; - var phi_3448_: bool; - var phi_3455_: bool; + var phi_3303_: bool; + var phi_3314_: bool; + var phi_3475_: bool; + var phi_3482_: bool; + var phi_3489_: bool; let _e244 = frag_info.shadow_params[3u]; strength_1 = _e244; @@ -9640,25 +9884,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e265 = cascadeCount; let _e266 = (_e265 > 1i); - phi_3269_ = _e266; + phi_3303_ = _e266; if _e266 { let _e267 = viewDistance; let _e270 = frag_info.shadow_cascades[0u]; - phi_3269_ = (_e267 > _e270); + phi_3303_ = (_e267 > _e270); } - let _e273 = phi_3269_; + let _e273 = phi_3303_; if _e273 { cascade = 1i; } let _e274 = cascadeCount; let _e275 = (_e274 > 2i); - phi_3280_ = _e275; + phi_3314_ = _e275; if _e275 { let _e276 = viewDistance; let _e279 = frag_info.shadow_cascades[1u]; - phi_3280_ = (_e276 > _e279); + phi_3314_ = (_e276 > _e279); } - let _e282 = phi_3280_; + let _e282 = phi_3314_; if _e282 { cascade = 2i; } @@ -9683,20 +9927,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e291 = which; if (_e291 == 0i) { let _e294 = frag_info.shadow_matrix; - local_4 = _e294; + local_5 = _e294; } else { let _e295 = which; if (_e295 == 1i) { let _e298 = frag_info.shadow_matrix_far; - local_5 = _e298; + local_6 = _e298; } else { let _e300 = frag_info.shadow_matrix_farthest; - local_5 = _e300; + local_6 = _e300; } - let _e301 = local_5; - local_4 = _e301; + let _e301 = local_6; + local_5 = _e301; } - let _e302 = local_4; + let _e302 = local_5; matrix = _e302; let _e305 = matrix[0][0u]; let _e308 = matrix[1][0u]; @@ -9746,24 +9990,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e389 * 0.5f) + 0.5f), (0.5f - (_e393 * 0.5f))); let _e398 = inTile[0u]; let _e399 = (_e398 < 0f); - phi_3441_ = _e399; + phi_3475_ = _e399; if !(_e399) { let _e402 = inTile[0u]; - phi_3441_ = (_e402 > 1f); + phi_3475_ = (_e402 > 1f); } - let _e405 = phi_3441_; - phi_3448_ = _e405; + let _e405 = phi_3475_; + phi_3482_ = _e405; if !(_e405) { let _e408 = inTile[1u]; - phi_3448_ = (_e408 < 0f); + phi_3482_ = (_e408 < 0f); } - let _e411 = phi_3448_; - phi_3455_ = _e411; + let _e411 = phi_3482_; + phi_3489_ = _e411; if !(_e411) { let _e414 = inTile[1u]; - phi_3455_ = (_e414 > 1f); + phi_3489_ = (_e414 > 1f); } - let _e417 = phi_3455_; + let _e417 = phi_3489_; if _e417 { continue; } @@ -9820,20 +10064,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e486 = cascade; if (_e486 == 0i) { let _e490 = frag_info.shadow_bias[0u]; - local_6 = _e490; + local_7 = _e490; } else { let _e491 = cascade; if (_e491 == 1i) { let _e495 = frag_info.shadow_bias[1u]; - local_7 = _e495; + local_8 = _e495; } else { let _e498 = frag_info.shadow_bias[2u]; - local_7 = _e498; + local_8 = _e498; } - let _e499 = local_7; - local_6 = _e499; + let _e499 = local_8; + local_7 = _e499; } - let _e500 = local_6; + let _e500 = local_7; bias = _e500; let _e503 = frag_info.shadow_params[0u]; let _e506 = frag_info.shadow_cascades[3u]; @@ -10078,7 +10322,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_8: vec3; + var local_9: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -10120,15 +10364,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e226 > 0f) { let _e228 = (*mirror); let _e229 = t_1; - local_8 = (_e228 * _e229); + local_9 = (_e228 * _e229); } else { let _e231 = toPlane; let _e232 = n_2; let _e233 = toPlane; let _e234 = n_2; - local_8 = (_e231 - (_e232 * dot(_e233, _e234))); + local_9 = (_e231 - (_e232 * dot(_e233, _e234))); } - let _e238 = local_8; + let _e238 = local_9; onPlane = _e238; } let _e239 = onPlane; @@ -10162,7 +10406,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_9: f32; + var local_10: f32; let _e185 = (*a_1); let _e186 = (*a_1); @@ -10184,11 +10428,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> let _e210 = axis; let _e211 = (*n_3); let _e214 = len; - local_9 = ((_e209 * dot(_e210, _e211)) / _e214); + local_10 = ((_e209 * dot(_e210, _e211)) / _e214); } else { - local_9 = 0f; + local_10 = 0f; } - let _e216 = local_9; + let _e216 = local_10; return _e216; } @@ -10199,15 +10443,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_10: i32; + var local_11: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_11: f32; + var local_12: f32; var aAbove: bool; var bAbove: bool; - var local_12: f32; + var local_13: f32; var param_67: vec3; var param_68: vec3; var param_69: vec3; @@ -10227,12 +10471,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr 0.000000000001f) { let _e227 = ha; let _e228 = d_3; - local_11 = (_e227 / _e228); + local_12 = (_e227 / _e228); } else { - local_11 = 0f; + local_12 = 0f; } - let _e230 = local_11; + let _e230 = local_12; q = (_e220 + ((_e221 - _e222) * _e230)); let _e233 = ha; aAbove = (_e233 > 0f); @@ -10275,11 +10519,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_13: f32; var local_14: f32; var local_15: f32; + var local_16: f32; let _e182 = (*slot_1); let _e183 = (*slot_1); @@ -10326,28 +10570,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 let _e187 = lane; if (_e187 == 0i) { let _e190 = (*four)[0u]; - local_13 = _e190; + local_14 = _e190; } else { let _e191 = lane; if (_e191 == 1i) { let _e194 = (*four)[1u]; - local_14 = _e194; + local_15 = _e194; } else { let _e195 = lane; if (_e195 == 2i) { let _e198 = (*four)[2u]; - local_15 = _e198; + local_16 = _e198; } else { let _e200 = (*four)[3u]; - local_15 = _e200; + local_16 = _e200; } - let _e201 = local_15; - local_14 = _e201; + let _e201 = local_16; + local_15 = _e201; } - let _e202 = local_14; - local_13 = _e202; + let _e202 = local_15; + local_14 = _e202; } - let _e203 = local_13; + let _e203 = local_14; return _e203; } @@ -10458,12 +10702,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_16: f32; + var local_17: f32; var param_81: i32; var param_82: i32; var v_1: f32; var u_1: f32; - var local_17: f32; + var local_18: f32; var param_83: f32; var param_84: i32; var type_39: f32; @@ -10473,12 +10717,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_18: f32; + var local_19: f32; var param_85: array, 4>; var param_86: vec3; var area: f32; var radiance: f32; - var local_19: f32; + var local_20: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -10491,7 +10735,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_91: vec3; var toPoint: vec3; var pointDistance: f32; - var local_20: vec3; + var local_21: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; @@ -10526,14 +10770,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e251 = slot_5; param_81 = _e251; let _e252 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_16 = _e252; + local_17 = _e252; } else { let _e253 = slot_5; param_82 = _e253; let _e254 = LightListRow_u0028_i1_u003b((¶m_82)); - local_16 = _e254; + local_17 = _e254; } - let _e255 = local_16; + let _e255 = local_17; listRow = _e255; let _e256 = listRow; let _e260 = light_list_info.list[2u]; @@ -10561,14 +10805,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e286 = listRow; param_83 = _e286; let _e287 = InSlots_u0028_f1_u003b((¶m_83)); - local_17 = select(1f, 0f, _e287); + local_18 = select(1f, 0f, _e287); } else { let _e289 = slot_5; param_84 = _e289; let _e290 = LightListScale_u0028_i1_u003b((¶m_84)); - local_17 = _e290; + local_18 = _e290; } - let _e291 = local_17; + let _e291 = local_18; let _e293 = color_1[3u]; color_1[3u] = (_e293 * _e291); } @@ -10605,16 +10849,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e332, cross(_e333, _e334)) >= 0f); let _e338 = behind; if _e338 { - local_18 = 0f; + local_19 = 0f; } else { let _e339 = corners_1; param_85 = _e339; let _e341 = (*s_3).n; param_86 = _e341; let _e342 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_18 = _e342; + local_19 = _e342; } - let _e343 = local_18; + let _e343 = local_19; formFactor = _e343; let _e344 = halfWidth_1; let _e345 = halfHeight_1; @@ -10622,11 +10866,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e349 = area; if (_e349 > 0.000000001f) { let _e351 = area; - local_19 = (1f / _e351); + local_20 = (1f / _e351); } else { - local_19 = 0f; + local_20 = 0f; } - let _e353 = local_19; + let _e353 = local_20; radiance = _e353; let _e354 = toCentre; distance_2 = length(_e354); @@ -10669,12 +10913,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e395 > 0.000001f) { let _e397 = toPoint; let _e398 = pointDistance; - local_20 = (_e397 / vec3(_e398)); + local_21 = (_e397 / vec3(_e398)); } else { let _e402 = (*s_3).n; - local_20 = _e402; + local_21 = _e402; } - let _e403 = local_20; + let _e403 = local_21; light_3.l = _e403; let _e406 = light_3.l; let _e408 = (*s_3).v; @@ -10770,7 +11014,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_21: f32; + var local_22: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -10798,15 +11042,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e236 = clip_1[3u]; let _e237 = near_1; if (_e236 <= _e237) { - local_21 = 0f; + local_22 = 0f; } else { let _e240 = clip_1[3u]; let _e241 = near_1; let _e246 = light_list_info.cluster_depth[1u]; let _e250 = grid[2u]; - local_21 = clamp(floor((log((_e240 / _e241)) * _e246)), 0f, (_e250 - 1f)); + local_22 = clamp(floor((log((_e240 / _e241)) * _e246)), 0f, (_e250 - 1f)); } - let _e253 = local_21; + let _e253 = local_22; tz = _e253; let _e254 = tx; let _e255 = ty; @@ -10860,7 +11104,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_98: Surface; var visibility: f32; var param_99: i32; - var local_22: f32; + var local_23: f32; var param_100: Surface; var param_101: LightSample; var param_102: i32; @@ -10910,11 +11154,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e215 = i_8; param_105 = _e215; let _e216 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_22 = (_e211 * _e216); + local_23 = (_e211 * _e216); } else { - local_22 = 1f; + local_23 = 1f; } - let _e218 = local_22; + let _e218 = local_23; visibility = _e218; let _e219 = visibility; if (_e219 <= 0f) { @@ -11202,7 +11446,7 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var param_120: f32; var param_121: vec2; - var local_23: vec3; + var local_24: vec3; let _e223 = irradiance_info.origin; origin_2 = _e223.xyz; @@ -11377,11 +11621,11 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr 0f) { let _e451 = total_2; let _e452 = weights; - local_23 = (_e451 / vec3(_e452)); + local_24 = (_e451 / vec3(_e452)); } else { - local_23 = vec3(0f, 0f, 0f); + local_24 = vec3(0f, 0f, 0f); } - let _e455 = local_23; + let _e455 = local_24; return _e455; } @@ -12202,21 +12446,45 @@ fn EyeDistance_u0028_() -> f32 { fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; + var falloff: f32; + var k: f32; + var local_1: f32; - let _e200 = fog_info.fog[3u]; - density = _e200; - let _e201 = density; - if (_e201 <= 0f) { - let _e203 = (*color); - return _e203; + let _e203 = fog_info.fog[3u]; + density = _e203; + let _e204 = density; + if (_e204 <= 0f) { + let _e206 = (*color); + return _e206; + } + let _e207 = EyeDistance_u0028_(); + d = _e207; + let _e210 = fog_info.eye[3u]; + falloff = _e210; + let _e211 = falloff; + if (_e211 > 0f) { + let _e213 = falloff; + let _e215 = v_world_position_1[1u]; + let _e218 = fog_info.eye[1u]; + k = (_e213 * (_e215 - _e218)); + let _e221 = k; + if (abs(_e221) > 0.001f) { + let _e224 = k; + let _e228 = k; + local_1 = ((1f - exp(-(_e224))) / _e228); + } else { + let _e230 = k; + local_1 = (1f - (0.5f * _e230)); + } + let _e233 = local_1; + let _e234 = density; + density = (_e234 * _e233); } - let _e204 = EyeDistance_u0028_(); - d = _e204; - let _e206 = fog_info.fog; - let _e208 = (*color); - let _e209 = density; - let _e211 = d; - return mix(_e206.xyz, _e208, vec3(clamp(exp((-(_e209) * _e211)), 0f, 1f))); + let _e237 = fog_info.fog; + let _e239 = (*color); + let _e240 = density; + let _e242 = d; + return mix(_e237.xyz, _e239, vec3(clamp(exp((-(_e240) * _e242)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -12276,7 +12544,7 @@ fn FonAverage_u0028_f1_u003b(r_1: ptr) -> f32 { fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, r_2: ptr, mu_i: ptr, mu_o: ptr, l_dot_v: ptr) -> vec3 { var s: f32; var s_over_t: f32; - var local_1: f32; + var local_2: f32; var af: f32; var single: vec3; var average_1: f32; @@ -12298,12 +12566,12 @@ fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr, alpha_4: ptr) -> f32 { var a: f32; - var k: f32; + var k_1: f32; let _e199 = (*n_dot_h); let _e200 = (*alpha_4); @@ -12448,9 +12716,9 @@ fn D_GGX_u0028_f1_u003b_f1_u003b(n_dot_h: ptr, alpha_4: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_2: vec2; + var local_3: vec2; var atlas: vec2; let _e204 = point_shadow.params[0u]; @@ -12625,8 +12893,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr(0.16666667f, 0.16666667f)); let _e220 = point_shadow.params3_[0u]; @@ -12784,19 +13052,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_3: vec2; + var local_4: vec2; let _e197 = (*rows); if (_e197 > 0f) { let _e200 = gl_FragCoord_1[0u]; let _e201 = (*rows); let _e203 = gl_FragCoord_1[1u]; - local_3 = vec2(_e200, (_e201 - _e203)); + local_4 = vec2(_e200, (_e201 - _e203)); } else { let _e206 = gl_FragCoord_1; - local_3 = _e206.xy; + local_4 = _e206.xy; } - let _e208 = local_3; + let _e208 = local_4; return _e208; } @@ -12835,7 +13103,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_5: vec3; var lit: f32; var param_57: vec2; var param_58: vec2; @@ -12852,8 +13120,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_69: f32; var param_70: f32; - var phi_2490_: bool; - var phi_2551_: bool; + var phi_2525_: bool; + var phi_2586_: bool; let _e249 = (*lightIndex); let _e253 = point_shadow.slots[_e249][0u]; @@ -12918,13 +13186,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e349); - phi_2490_ = _e350; + phi_2525_ = _e350; if _e350 { let _e352 = a_1[0u]; let _e354 = a_1[2u]; - phi_2490_ = (_e352 >= _e354); + phi_2525_ = (_e352 >= _e354); } - let _e357 = phi_2490_; + let _e357 = phi_2525_; if _e357 { let _e359 = toFragment[0u]; face = select(1i, 0i, (_e359 > 0f)); @@ -12954,12 +13222,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2551_ = _e400; + phi_2586_ = _e400; if !(_e400) { let _e403 = ndc[1u]; - phi_2551_ = (abs(_e403) > 1f); + phi_2586_ = (abs(_e403) > 1f); } - let _e407 = phi_2551_; + let _e407 = phi_2586_; if _e407 { return 1f; } @@ -13009,15 +13277,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_5 = vec3(0f, 0f, 1f); } else { let _e462 = radius_1; let _e465 = point_shadow.params2_[2u]; let _e469 = blocker_1; let _e470 = range_3; - local_4 = vec3(clamp((_e462 / max(_e465, 0.000001f)), 0f, 1f), clamp((_e469 / _e470), 0f, 1f), 0f); + local_5 = vec3(clamp((_e462 / max(_e465, 0.000001f)), 0f, 1f), clamp((_e469 / _e470), 0f, 1f), 0f); } - let _e474 = local_4; + let _e474 = local_5; g_debug_surface = _e474; } let _e475 = radius_1; @@ -13209,8 +13477,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_5: mat4x4; var local_6: mat4x4; + var local_7: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -13224,8 +13492,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_7: f32; var local_8: f32; + var local_9: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -13259,11 +13527,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_89: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3608_: bool; - var phi_3619_: bool; - var phi_3780_: bool; - var phi_3787_: bool; - var phi_3794_: bool; + var phi_3642_: bool; + var phi_3653_: bool; + var phi_3814_: bool; + var phi_3821_: bool; + var phi_3828_: bool; let _e263 = frag_info.shadow_params[3u]; strength_1 = _e263; @@ -13284,25 +13552,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e284 = cascadeCount; let _e285 = (_e284 > 1i); - phi_3608_ = _e285; + phi_3642_ = _e285; if _e285 { let _e286 = viewDistance; let _e289 = frag_info.shadow_cascades[0u]; - phi_3608_ = (_e286 > _e289); + phi_3642_ = (_e286 > _e289); } - let _e292 = phi_3608_; + let _e292 = phi_3642_; if _e292 { cascade = 1i; } let _e293 = cascadeCount; let _e294 = (_e293 > 2i); - phi_3619_ = _e294; + phi_3653_ = _e294; if _e294 { let _e295 = viewDistance; let _e298 = frag_info.shadow_cascades[1u]; - phi_3619_ = (_e295 > _e298); + phi_3653_ = (_e295 > _e298); } - let _e301 = phi_3619_; + let _e301 = phi_3653_; if _e301 { cascade = 2i; } @@ -13327,20 +13595,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e310 = which; if (_e310 == 0i) { let _e313 = frag_info.shadow_matrix; - local_5 = _e313; + local_6 = _e313; } else { let _e314 = which; if (_e314 == 1i) { let _e317 = frag_info.shadow_matrix_far; - local_6 = _e317; + local_7 = _e317; } else { let _e319 = frag_info.shadow_matrix_farthest; - local_6 = _e319; + local_7 = _e319; } - let _e320 = local_6; - local_5 = _e320; + let _e320 = local_7; + local_6 = _e320; } - let _e321 = local_5; + let _e321 = local_6; matrix = _e321; let _e324 = matrix[0][0u]; let _e327 = matrix[1][0u]; @@ -13390,24 +13658,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e408 * 0.5f) + 0.5f), (0.5f - (_e412 * 0.5f))); let _e417 = inTile[0u]; let _e418 = (_e417 < 0f); - phi_3780_ = _e418; + phi_3814_ = _e418; if !(_e418) { let _e421 = inTile[0u]; - phi_3780_ = (_e421 > 1f); + phi_3814_ = (_e421 > 1f); } - let _e424 = phi_3780_; - phi_3787_ = _e424; + let _e424 = phi_3814_; + phi_3821_ = _e424; if !(_e424) { let _e427 = inTile[1u]; - phi_3787_ = (_e427 < 0f); + phi_3821_ = (_e427 < 0f); } - let _e430 = phi_3787_; - phi_3794_ = _e430; + let _e430 = phi_3821_; + phi_3828_ = _e430; if !(_e430) { let _e433 = inTile[1u]; - phi_3794_ = (_e433 > 1f); + phi_3828_ = (_e433 > 1f); } - let _e436 = phi_3794_; + let _e436 = phi_3828_; if _e436 { continue; } @@ -13464,20 +13732,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e505 = cascade; if (_e505 == 0i) { let _e509 = frag_info.shadow_bias[0u]; - local_7 = _e509; + local_8 = _e509; } else { let _e510 = cascade; if (_e510 == 1i) { let _e514 = frag_info.shadow_bias[1u]; - local_8 = _e514; + local_9 = _e514; } else { let _e517 = frag_info.shadow_bias[2u]; - local_8 = _e517; + local_9 = _e517; } - let _e518 = local_8; - local_7 = _e518; + let _e518 = local_9; + local_8 = _e518; } - let _e519 = local_7; + let _e519 = local_8; bias = _e519; let _e522 = frag_info.shadow_params[0u]; let _e525 = frag_info.shadow_cascades[3u]; @@ -13719,7 +13987,7 @@ fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_2: ptr>, b: ptr; var len: f32; var angle_1: f32; - var local_9: vec3; + var local_10: vec3; let _e201 = (*a_2); let _e202 = (*b); @@ -13734,11 +14002,11 @@ fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_2: ptr>, b: ptr(0f, 0f, 0f); + local_10 = vec3(0f, 0f, 0f); } - let _e219 = local_9; + let _e219 = local_10; return _e219; } @@ -13755,7 +14023,7 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: var along: vec3; var alongLength: f32; var t1_: vec3; - var local_10: vec3; + var local_11: vec3; var t2_: vec3; var minv: mat3x3; var i_7: i32; @@ -13772,7 +14040,7 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: var param_106: vec3; var len_1: f32; var z_1: f32; - var local_11: f32; + var local_12: f32; var sphere: f32; var param_107: f32; var param_108: f32; @@ -13809,13 +14077,13 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: if (_e263 > 0.00001f) { let _e265 = along; let _e266 = alongLength; - local_10 = (_e265 / vec3(_e266)); + local_11 = (_e265 / vec3(_e266)); } else { let _e269 = (*n_2); let _e271 = (*n_2)[2u]; - local_10 = normalize(cross(_e269, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e271) < 0.999f))))); + local_11 = normalize(cross(_e269, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e271) < 0.999f))))); } - let _e278 = local_10; + let _e278 = local_11; t1_ = _e278; let _e279 = (*n_2); let _e280 = t1_; @@ -13879,11 +14147,11 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: if (_e353 > 0.000000001f) { let _e356 = f_2[2u]; let _e357 = len_1; - local_11 = (_e356 / _e357); + local_12 = (_e356 / _e357); } else { - local_11 = 0f; + local_12 = 0f; } - let _e359 = local_11; + let _e359 = local_12; z_1 = _e359; let _e360 = z_1; let _e363 = len_1; @@ -13907,7 +14175,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_12: vec3; + var local_13: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -13949,15 +14217,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e245 > 0f) { let _e247 = (*mirror); let _e248 = t_1; - local_12 = (_e247 * _e248); + local_13 = (_e247 * _e248); } else { let _e250 = toPlane; let _e251 = n_3; let _e252 = toPlane; let _e253 = n_3; - local_12 = (_e250 - (_e251 * dot(_e252, _e253))); + local_13 = (_e250 - (_e251 * dot(_e252, _e253))); } - let _e257 = local_12; + let _e257 = local_13; onPlane = _e257; } let _e258 = onPlane; @@ -13991,7 +14259,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> var angle_2: f32; var axis_1: vec3; var len_2: f32; - var local_13: f32; + var local_14: f32; let _e204 = (*a_3); let _e205 = (*a_3); @@ -14013,11 +14281,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> let _e229 = axis_1; let _e230 = (*n_4); let _e233 = len_2; - local_13 = ((_e228 * dot(_e229, _e230)) / _e233); + local_14 = ((_e228 * dot(_e229, _e230)) / _e233); } else { - local_13 = 0f; + local_14 = 0f; } - let _e235 = local_13; + let _e235 = local_14; return _e235; } @@ -14028,15 +14296,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr; var b_2: vec3; - var local_14: i32; + var local_15: i32; var ha: f32; var hb: f32; var d_4: f32; var q: vec3; - var local_15: f32; + var local_16: f32; var aAbove: bool; var bAbove: bool; - var local_16: f32; + var local_17: f32; var param_110: vec3; var param_111: vec3; var param_112: vec3; @@ -14056,12 +14324,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr 0.000000000001f) { let _e246 = ha; let _e247 = d_4; - local_15 = (_e246 / _e247); + local_16 = (_e246 / _e247); } else { - local_15 = 0f; + local_16 = 0f; } - let _e249 = local_15; + let _e249 = local_16; q = (_e239 + ((_e240 - _e241) * _e249)); let _e252 = ha; aAbove = (_e252 > 0f); @@ -14104,11 +14372,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_17: f32; var local_18: f32; var local_19: f32; + var local_20: f32; let _e201 = (*slot_1); let _e202 = (*slot_1); @@ -14155,28 +14423,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 let _e206 = lane; if (_e206 == 0i) { let _e209 = (*four)[0u]; - local_17 = _e209; + local_18 = _e209; } else { let _e210 = lane; if (_e210 == 1i) { let _e213 = (*four)[1u]; - local_18 = _e213; + local_19 = _e213; } else { let _e214 = lane; if (_e214 == 2i) { let _e217 = (*four)[2u]; - local_19 = _e217; + local_20 = _e217; } else { let _e219 = (*four)[3u]; - local_19 = _e219; + local_20 = _e219; } - let _e220 = local_19; - local_18 = _e220; + let _e220 = local_20; + local_19 = _e220; } - let _e221 = local_18; - local_17 = _e221; + let _e221 = local_19; + local_18 = _e221; } - let _e222 = local_17; + let _e222 = local_18; return _e222; } @@ -14287,12 +14555,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_20: f32; + var local_21: f32; var param_124: i32; var param_125: i32; var v_2: f32; var u_1: f32; - var local_21: f32; + var local_22: f32; var param_126: f32; var param_127: i32; var type_42: f32; @@ -14302,12 +14570,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_2: array, 4>; var behind: bool; var formFactor: f32; - var local_22: f32; + var local_23: f32; var param_128: array, 4>; var param_129: vec3; var area: f32; var radiance: f32; - var local_23: f32; + var local_24: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -14320,7 +14588,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_134: vec3; var toPoint: vec3; var pointDistance: f32; - var local_24: vec3; + var local_25: vec3; var param_135: vec3; var param_136: vec3; var param_137: f32; @@ -14359,14 +14627,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e274 = slot_5; param_124 = _e274; let _e275 = ClusterRow_u0028_i1_u003b((¶m_124)); - local_20 = _e275; + local_21 = _e275; } else { let _e276 = slot_5; param_125 = _e276; let _e277 = LightListRow_u0028_i1_u003b((¶m_125)); - local_20 = _e277; + local_21 = _e277; } - let _e278 = local_20; + let _e278 = local_21; listRow = _e278; let _e279 = listRow; let _e283 = light_list_info.list[2u]; @@ -14394,14 +14662,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e309 = listRow; param_126 = _e309; let _e310 = InSlots_u0028_f1_u003b((¶m_126)); - local_21 = select(1f, 0f, _e310); + local_22 = select(1f, 0f, _e310); } else { let _e312 = slot_5; param_127 = _e312; let _e313 = LightListScale_u0028_i1_u003b((¶m_127)); - local_21 = _e313; + local_22 = _e313; } - let _e314 = local_21; + let _e314 = local_22; let _e316 = color_1[3u]; color_1[3u] = (_e316 * _e314); } @@ -14438,16 +14706,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e355, cross(_e356, _e357)) >= 0f); let _e361 = behind; if _e361 { - local_22 = 0f; + local_23 = 0f; } else { let _e362 = corners_2; param_128 = _e362; let _e364 = (*s_5).n; param_129 = _e364; let _e365 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_128), (¶m_129)); - local_22 = _e365; + local_23 = _e365; } - let _e366 = local_22; + let _e366 = local_23; formFactor = _e366; let _e367 = halfWidth_1; let _e368 = halfHeight_1; @@ -14455,11 +14723,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e372 = area; if (_e372 > 0.000000001f) { let _e374 = area; - local_23 = (1f / _e374); + local_24 = (1f / _e374); } else { - local_23 = 0f; + local_24 = 0f; } - let _e376 = local_23; + let _e376 = local_24; radiance = _e376; let _e377 = toCentre; distance_2 = length(_e377); @@ -14502,12 +14770,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e418 > 0.000001f) { let _e420 = toPoint; let _e421 = pointDistance; - local_24 = (_e420 / vec3(_e421)); + local_25 = (_e420 / vec3(_e421)); } else { let _e425 = (*s_5).n; - local_24 = _e425; + local_25 = _e425; } - let _e426 = local_24; + let _e426 = local_25; light_3.l = _e426; let _e429 = light_3.l; let _e431 = (*s_5).v; @@ -14614,7 +14882,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_25: f32; + var local_26: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -14642,15 +14910,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e255 = clip_1[3u]; let _e256 = near_1; if (_e255 <= _e256) { - local_25 = 0f; + local_26 = 0f; } else { let _e259 = clip_1[3u]; let _e260 = near_1; let _e265 = light_list_info.cluster_depth[1u]; let _e269 = grid[2u]; - local_25 = clamp(floor((log((_e259 / _e260)) * _e265)), 0f, (_e269 - 1f)); + local_26 = clamp(floor((log((_e259 / _e260)) * _e265)), 0f, (_e269 - 1f)); } - let _e272 = local_25; + let _e272 = local_26; tz = _e272; let _e273 = tx; let _e274 = ty; @@ -14704,7 +14972,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_145: Surface; var visibility: f32; var param_146: i32; - var local_26: f32; + var local_27: f32; var param_147: Surface; var param_148: LightSample; var param_149: i32; @@ -14754,11 +15022,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e234 = i_9; param_152 = _e234; let _e235 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_150), (¶m_151), (¶m_152)); - local_26 = (_e230 * _e235); + local_27 = (_e230 * _e235); } else { - local_26 = 1f; + local_27 = 1f; } - let _e237 = local_26; + let _e237 = local_27; visibility = _e237; let _e238 = visibility; if (_e238 <= 0f) { @@ -15073,7 +15341,7 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var param_172: f32; var param_173: vec2; - var local_27: vec3; + var local_28: vec3; let _e242 = irradiance_info.origin; origin_2 = _e242.xyz; @@ -15248,11 +15516,11 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr 0f) { let _e470 = total_2; let _e471 = weights; - local_27 = (_e470 / vec3(_e471)); + local_28 = (_e470 / vec3(_e471)); } else { - local_27 = vec3(0f, 0f, 0f); + local_28 = vec3(0f, 0f, 0f); } - let _e474 = local_27; + let _e474 = local_28; return _e474; } @@ -16232,21 +16500,45 @@ fn EyeDistance_u0028_() -> f32 { fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; + var falloff: f32; + var k: f32; + var local_1: f32; - let _e266 = fog_info.fog[3u]; - density = _e266; - let _e267 = density; - if (_e267 <= 0f) { - let _e269 = (*color); - return _e269; + let _e269 = fog_info.fog[3u]; + density = _e269; + let _e270 = density; + if (_e270 <= 0f) { + let _e272 = (*color); + return _e272; + } + let _e273 = EyeDistance_u0028_(); + d = _e273; + let _e276 = fog_info.eye[3u]; + falloff = _e276; + let _e277 = falloff; + if (_e277 > 0f) { + let _e279 = falloff; + let _e281 = v_world_position_1[1u]; + let _e284 = fog_info.eye[1u]; + k = (_e279 * (_e281 - _e284)); + let _e287 = k; + if (abs(_e287) > 0.001f) { + let _e290 = k; + let _e294 = k; + local_1 = ((1f - exp(-(_e290))) / _e294); + } else { + let _e296 = k; + local_1 = (1f - (0.5f * _e296)); + } + let _e299 = local_1; + let _e300 = density; + density = (_e300 * _e299); } - let _e270 = EyeDistance_u0028_(); - d = _e270; - let _e272 = fog_info.fog; - let _e274 = (*color); - let _e275 = density; - let _e277 = d; - return mix(_e272.xyz, _e274, vec3(clamp(exp((-(_e275) * _e277)), 0f, 1f))); + let _e303 = fog_info.fog; + let _e305 = (*color); + let _e306 = density; + let _e308 = d; + return mix(_e303.xyz, _e305, vec3(clamp(exp((-(_e306) * _e308)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -16298,7 +16590,7 @@ fn V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b(n_dot_v: ptr, alpha_4: ptr) -> f32 { var a: f32; - var k: f32; + var k_1: f32; let _e265 = (*n_dot_h); let _e266 = (*alpha_4); @@ -16308,9 +16600,9 @@ fn D_GGX_u0028_f1_u003b_f1_u003b(n_dot_h: ptr, alpha_4: ptr>, b: ptr; var len: f32; var angle: f32; - var local_1: vec3; + var local_2: vec3; let _e267 = (*a_1); let _e268 = (*b); @@ -16333,11 +16625,11 @@ fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr(0f, 0f, 0f); + local_2 = vec3(0f, 0f, 0f); } - let _e285 = local_1; + let _e285 = local_2; return _e285; } @@ -16366,7 +16658,7 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: var along: vec3; var alongLength: f32; var t1_: vec3; - var local_2: vec3; + var local_3: vec3; var t2_: vec3; var minv: mat3x3; var i: i32; @@ -16383,7 +16675,7 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: var param_18: vec3; var len_1: f32; var z_1: f32; - var local_3: f32; + var local_4: f32; var sphere: f32; var param_19: f32; var param_20: f32; @@ -16420,13 +16712,13 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: if (_e329 > 0.00001f) { let _e331 = along; let _e332 = alongLength; - local_2 = (_e331 / vec3(_e332)); + local_3 = (_e331 / vec3(_e332)); } else { let _e335 = (*n_1); let _e337 = (*n_1)[2u]; - local_2 = normalize(cross(_e335, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e337) < 0.999f))))); + local_3 = normalize(cross(_e335, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e337) < 0.999f))))); } - let _e344 = local_2; + let _e344 = local_3; t1_ = _e344; let _e345 = (*n_1); let _e346 = t1_; @@ -16490,11 +16782,11 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: if (_e419 > 0.000000001f) { let _e422 = f[2u]; let _e423 = len_1; - local_3 = (_e422 / _e423); + local_4 = (_e422 / _e423); } else { - local_3 = 0f; + local_4 = 0f; } - let _e425 = local_3; + let _e425 = local_4; z_1 = _e425; let _e426 = z_1; let _e429 = len_1; @@ -16644,7 +16936,7 @@ fn FonAverage_u0028_f1_u003b(r_1: ptr) -> f32 { fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, r_2: ptr, mu_i: ptr, mu_o: ptr, l_dot_v: ptr) -> vec3 { var s_1: f32; var s_over_t: f32; - var local_4: f32; + var local_5: f32; var af: f32; var single: vec3; var average_1: f32; @@ -16666,12 +16958,12 @@ fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_5: vec2; + var local_6: vec2; var atlas: vec2; let _e270 = point_shadow.params[0u]; @@ -17092,8 +17384,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv_1: ptr(0.16666667f, 0.16666667f)); let _e286 = point_shadow.params3_[0u]; @@ -17251,19 +17543,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_6: vec2; + var local_7: vec2; let _e263 = (*rows); if (_e263 > 0f) { let _e266 = gl_FragCoord_1[0u]; let _e267 = (*rows); let _e269 = gl_FragCoord_1[1u]; - local_6 = vec2(_e266, (_e267 - _e269)); + local_7 = vec2(_e266, (_e267 - _e269)); } else { let _e272 = gl_FragCoord_1; - local_6 = _e272.xy; + local_7 = _e272.xy; } - let _e274 = local_6; + let _e274 = local_7; return _e274; } @@ -17302,7 +17594,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_8: vec3; var lit: f32; var param_103: vec2; var param_104: vec2; @@ -17319,8 +17611,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_115: f32; var param_116: f32; - var phi_2602_: bool; - var phi_2663_: bool; + var phi_2637_: bool; + var phi_2698_: bool; let _e315 = (*lightIndex); let _e319 = point_shadow.slots[_e315][0u]; @@ -17385,13 +17677,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e415); - phi_2602_ = _e416; + phi_2637_ = _e416; if _e416 { let _e418 = a_2[0u]; let _e420 = a_2[2u]; - phi_2602_ = (_e418 >= _e420); + phi_2637_ = (_e418 >= _e420); } - let _e423 = phi_2602_; + let _e423 = phi_2637_; if _e423 { let _e425 = toFragment[0u]; face = select(1i, 0i, (_e425 > 0f)); @@ -17421,12 +17713,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2663_ = _e466; + phi_2698_ = _e466; if !(_e466) { let _e469 = ndc[1u]; - phi_2663_ = (abs(_e469) > 1f); + phi_2698_ = (abs(_e469) > 1f); } - let _e473 = phi_2663_; + let _e473 = phi_2698_; if _e473 { return 1f; } @@ -17476,15 +17768,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_8 = vec3(0f, 0f, 1f); } else { let _e528 = radius_1; let _e531 = point_shadow.params2_[2u]; let _e535 = blocker_1; let _e536 = range_3; - local_7 = vec3(clamp((_e528 / max(_e531, 0.000001f)), 0f, 1f), clamp((_e535 / _e536), 0f, 1f), 0f); + local_8 = vec3(clamp((_e528 / max(_e531, 0.000001f)), 0f, 1f), clamp((_e535 / _e536), 0f, 1f), 0f); } - let _e540 = local_7; + let _e540 = local_8; g_debug_surface = _e540; } let _e541 = radius_1; @@ -17676,8 +17968,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_8: mat4x4; var local_9: mat4x4; + var local_10: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -17691,8 +17983,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_10: f32; var local_11: f32; + var local_12: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -17726,11 +18018,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_135: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3823_: bool; - var phi_3834_: bool; - var phi_3995_: bool; - var phi_4002_: bool; - var phi_4009_: bool; + var phi_3857_: bool; + var phi_3868_: bool; + var phi_4029_: bool; + var phi_4036_: bool; + var phi_4043_: bool; let _e329 = frag_info.shadow_params[3u]; strength_1 = _e329; @@ -17751,25 +18043,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e350 = cascadeCount; let _e351 = (_e350 > 1i); - phi_3823_ = _e351; + phi_3857_ = _e351; if _e351 { let _e352 = viewDistance; let _e355 = frag_info.shadow_cascades[0u]; - phi_3823_ = (_e352 > _e355); + phi_3857_ = (_e352 > _e355); } - let _e358 = phi_3823_; + let _e358 = phi_3857_; if _e358 { cascade = 1i; } let _e359 = cascadeCount; let _e360 = (_e359 > 2i); - phi_3834_ = _e360; + phi_3868_ = _e360; if _e360 { let _e361 = viewDistance; let _e364 = frag_info.shadow_cascades[1u]; - phi_3834_ = (_e361 > _e364); + phi_3868_ = (_e361 > _e364); } - let _e367 = phi_3834_; + let _e367 = phi_3868_; if _e367 { cascade = 2i; } @@ -17794,20 +18086,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e376 = which; if (_e376 == 0i) { let _e379 = frag_info.shadow_matrix; - local_8 = _e379; + local_9 = _e379; } else { let _e380 = which; if (_e380 == 1i) { let _e383 = frag_info.shadow_matrix_far; - local_9 = _e383; + local_10 = _e383; } else { let _e385 = frag_info.shadow_matrix_farthest; - local_9 = _e385; + local_10 = _e385; } - let _e386 = local_9; - local_8 = _e386; + let _e386 = local_10; + local_9 = _e386; } - let _e387 = local_8; + let _e387 = local_9; matrix = _e387; let _e390 = matrix[0][0u]; let _e393 = matrix[1][0u]; @@ -17857,24 +18149,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e474 * 0.5f) + 0.5f), (0.5f - (_e478 * 0.5f))); let _e483 = inTile[0u]; let _e484 = (_e483 < 0f); - phi_3995_ = _e484; + phi_4029_ = _e484; if !(_e484) { let _e487 = inTile[0u]; - phi_3995_ = (_e487 > 1f); + phi_4029_ = (_e487 > 1f); } - let _e490 = phi_3995_; - phi_4002_ = _e490; + let _e490 = phi_4029_; + phi_4036_ = _e490; if !(_e490) { let _e493 = inTile[1u]; - phi_4002_ = (_e493 < 0f); + phi_4036_ = (_e493 < 0f); } - let _e496 = phi_4002_; - phi_4009_ = _e496; + let _e496 = phi_4036_; + phi_4043_ = _e496; if !(_e496) { let _e499 = inTile[1u]; - phi_4009_ = (_e499 > 1f); + phi_4043_ = (_e499 > 1f); } - let _e502 = phi_4009_; + let _e502 = phi_4043_; if _e502 { continue; } @@ -17931,20 +18223,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e571 = cascade; if (_e571 == 0i) { let _e575 = frag_info.shadow_bias[0u]; - local_10 = _e575; + local_11 = _e575; } else { let _e576 = cascade; if (_e576 == 1i) { let _e580 = frag_info.shadow_bias[1u]; - local_11 = _e580; + local_12 = _e580; } else { let _e583 = frag_info.shadow_bias[2u]; - local_11 = _e583; + local_12 = _e583; } - let _e584 = local_11; - local_10 = _e584; + let _e584 = local_12; + local_11 = _e584; } - let _e585 = local_10; + let _e585 = local_11; bias = _e585; let _e588 = frag_info.shadow_params[0u]; let _e591 = frag_info.shadow_cascades[3u]; @@ -18189,7 +18481,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_12: vec3; + var local_13: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -18231,15 +18523,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e311 > 0f) { let _e313 = (*mirror); let _e314 = t_1; - local_12 = (_e313 * _e314); + local_13 = (_e313 * _e314); } else { let _e316 = toPlane; let _e317 = n_3; let _e318 = toPlane; let _e319 = n_3; - local_12 = (_e316 - (_e317 * dot(_e318, _e319))); + local_13 = (_e316 - (_e317 * dot(_e318, _e319))); } - let _e323 = local_12; + let _e323 = local_13; onPlane = _e323; } let _e324 = onPlane; @@ -18273,7 +18565,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> var angle_2: f32; var axis_1: vec3; var len_2: f32; - var local_13: f32; + var local_14: f32; let _e270 = (*a_3); let _e271 = (*a_3); @@ -18295,11 +18587,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> let _e295 = axis_1; let _e296 = (*n_4); let _e299 = len_2; - local_13 = ((_e294 * dot(_e295, _e296)) / _e299); + local_14 = ((_e294 * dot(_e295, _e296)) / _e299); } else { - local_13 = 0f; + local_14 = 0f; } - let _e301 = local_13; + let _e301 = local_14; return _e301; } @@ -18310,15 +18602,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr; var b_2: vec3; - var local_14: i32; + var local_15: i32; var ha: f32; var hb: f32; var d_5: f32; var q: vec3; - var local_15: f32; + var local_16: f32; var aAbove: bool; var bAbove: bool; - var local_16: f32; + var local_17: f32; var param_139: vec3; var param_140: vec3; var param_141: vec3; @@ -18338,12 +18630,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr 0.000000000001f) { let _e312 = ha; let _e313 = d_5; - local_15 = (_e312 / _e313); + local_16 = (_e312 / _e313); } else { - local_15 = 0f; + local_16 = 0f; } - let _e315 = local_15; + let _e315 = local_16; q = (_e305 + ((_e306 - _e307) * _e315)); let _e318 = ha; aAbove = (_e318 > 0f); @@ -18386,11 +18678,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_17: f32; var local_18: f32; var local_19: f32; + var local_20: f32; let _e267 = (*slot_1); let _e268 = (*slot_1); @@ -18437,28 +18729,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 let _e272 = lane; if (_e272 == 0i) { let _e275 = (*four)[0u]; - local_17 = _e275; + local_18 = _e275; } else { let _e276 = lane; if (_e276 == 1i) { let _e279 = (*four)[1u]; - local_18 = _e279; + local_19 = _e279; } else { let _e280 = lane; if (_e280 == 2i) { let _e283 = (*four)[2u]; - local_19 = _e283; + local_20 = _e283; } else { let _e285 = (*four)[3u]; - local_19 = _e285; + local_20 = _e285; } - let _e286 = local_19; - local_18 = _e286; + let _e286 = local_20; + local_19 = _e286; } - let _e287 = local_18; - local_17 = _e287; + let _e287 = local_19; + local_18 = _e287; } - let _e288 = local_17; + let _e288 = local_18; return _e288; } @@ -18569,12 +18861,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_20: f32; + var local_21: f32; var param_153: i32; var param_154: i32; var v_2: f32; var u_1: f32; - var local_21: f32; + var local_22: f32; var param_155: f32; var param_156: i32; var type_45: f32; @@ -18584,12 +18876,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_2: array, 4>; var behind: bool; var formFactor: f32; - var local_22: f32; + var local_23: f32; var param_157: array, 4>; var param_158: vec3; var area: f32; var radiance: f32; - var local_23: f32; + var local_24: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -18602,7 +18894,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_163: vec3; var toPoint: vec3; var pointDistance: f32; - var local_24: vec3; + var local_25: vec3; var param_164: vec3; var param_165: vec3; var param_166: f32; @@ -18641,14 +18933,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e340 = slot_5; param_153 = _e340; let _e341 = ClusterRow_u0028_i1_u003b((¶m_153)); - local_20 = _e341; + local_21 = _e341; } else { let _e342 = slot_5; param_154 = _e342; let _e343 = LightListRow_u0028_i1_u003b((¶m_154)); - local_20 = _e343; + local_21 = _e343; } - let _e344 = local_20; + let _e344 = local_21; listRow = _e344; let _e345 = listRow; let _e349 = light_list_info.list[2u]; @@ -18676,14 +18968,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e375 = listRow; param_155 = _e375; let _e376 = InSlots_u0028_f1_u003b((¶m_155)); - local_21 = select(1f, 0f, _e376); + local_22 = select(1f, 0f, _e376); } else { let _e378 = slot_5; param_156 = _e378; let _e379 = LightListScale_u0028_i1_u003b((¶m_156)); - local_21 = _e379; + local_22 = _e379; } - let _e380 = local_21; + let _e380 = local_22; let _e382 = color_1[3u]; color_1[3u] = (_e382 * _e380); } @@ -18720,16 +19012,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e421, cross(_e422, _e423)) >= 0f); let _e427 = behind; if _e427 { - local_22 = 0f; + local_23 = 0f; } else { let _e428 = corners_2; param_157 = _e428; let _e430 = (*s_6).n; param_158 = _e430; let _e431 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_157), (¶m_158)); - local_22 = _e431; + local_23 = _e431; } - let _e432 = local_22; + let _e432 = local_23; formFactor = _e432; let _e433 = halfWidth_1; let _e434 = halfHeight_1; @@ -18737,11 +19029,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e438 = area; if (_e438 > 0.000000001f) { let _e440 = area; - local_23 = (1f / _e440); + local_24 = (1f / _e440); } else { - local_23 = 0f; + local_24 = 0f; } - let _e442 = local_23; + let _e442 = local_24; radiance = _e442; let _e443 = toCentre; distance_2 = length(_e443); @@ -18784,12 +19076,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e484 > 0.000001f) { let _e486 = toPoint; let _e487 = pointDistance; - local_24 = (_e486 / vec3(_e487)); + local_25 = (_e486 / vec3(_e487)); } else { let _e491 = (*s_6).n; - local_24 = _e491; + local_25 = _e491; } - let _e492 = local_24; + let _e492 = local_25; light_4.l = _e492; let _e495 = light_4.l; let _e497 = (*s_6).v; @@ -18898,7 +19190,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_25: f32; + var local_26: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -18926,15 +19218,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e321 = clip_1[3u]; let _e322 = near_1; if (_e321 <= _e322) { - local_25 = 0f; + local_26 = 0f; } else { let _e325 = clip_1[3u]; let _e326 = near_1; let _e331 = light_list_info.cluster_depth[1u]; let _e335 = grid[2u]; - local_25 = clamp(floor((log((_e325 / _e326)) * _e331)), 0f, (_e335 - 1f)); + local_26 = clamp(floor((log((_e325 / _e326)) * _e331)), 0f, (_e335 - 1f)); } - let _e338 = local_25; + let _e338 = local_26; tz = _e338; let _e339 = tx; let _e340 = ty; @@ -18988,7 +19280,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_174: Surface; var visibility: f32; var param_175: i32; - var local_26: f32; + var local_27: f32; var param_176: Surface; var param_177: LightSample; var param_178: i32; @@ -19038,11 +19330,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e300 = i_9; param_181 = _e300; let _e301 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_179), (¶m_180), (¶m_181)); - local_26 = (_e296 * _e301); + local_27 = (_e296 * _e301); } else { - local_26 = 1f; + local_27 = 1f; } - let _e303 = local_26; + let _e303 = local_27; visibility = _e303; let _e304 = visibility; if (_e304 <= 0f) { @@ -19166,31 +19458,31 @@ fn IorInfinite_u0028_() -> bool { } fn DispersionSpread_u0028_f1_u003b(ior: ptr) -> f32 { - var local_27: f32; + var local_28: f32; let _e263 = IorInfinite_u0028_(); if _e263 { - local_27 = 0f; + local_28 = 0f; } else { let _e264 = (*ior); let _e269 = layer_info.transmission[3u]; - local_27 = (((_e264 - 1f) * 0.025f) * _e269); + local_28 = (((_e264 - 1f) * 0.025f) * _e269); } - let _e271 = local_27; + let _e271 = local_28; return _e271; } fn RefractionIor_u0028_() -> f32 { - var local_28: f32; + var local_29: f32; let _e262 = IorInfinite_u0028_(); if _e262 { - local_28 = 10000f; + local_29 = 10000f; } else { let _e265 = layer_info.coat[2u]; - local_28 = max(_e265, 1f); + local_29 = max(_e265, 1f); } - let _e267 = local_28; + let _e267 = local_29; return _e267; } @@ -19202,11 +19494,11 @@ fn SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3 var lod_1: f32; var thin: bool; var red: vec3; - var local_29: vec3; - var green: vec3; var local_30: vec3; - var blue: vec3; + var green: vec3; var local_31: vec3; + var blue: vec3; + var local_32: vec3; var param_189: vec3; var param_190: f32; var param_191: vec3; @@ -19232,40 +19524,40 @@ fn SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3 let _e305 = thin; if _e305 { let _e307 = (*s_8).v; - local_29 = -(_e307); + local_30 = -(_e307); } else { let _e310 = (*s_8).v; let _e313 = (*s_8).n; let _e314 = ior_1; let _e315 = spread_1; - local_29 = refract(-(_e310), _e313, (1f / max((_e314 - _e315), 1f))); + local_30 = refract(-(_e310), _e313, (1f / max((_e314 - _e315), 1f))); } - let _e320 = local_29; + let _e320 = local_30; red = _e320; let _e321 = thin; if _e321 { let _e323 = (*s_8).v; - local_30 = -(_e323); + local_31 = -(_e323); } else { let _e326 = (*s_8).v; let _e329 = (*s_8).n; let _e330 = ior_1; - local_30 = refract(-(_e326), _e329, (1f / _e330)); + local_31 = refract(-(_e326), _e329, (1f / _e330)); } - let _e333 = local_30; + let _e333 = local_31; green = _e333; let _e334 = thin; if _e334 { let _e336 = (*s_8).v; - local_31 = -(_e336); + local_32 = -(_e336); } else { let _e339 = (*s_8).v; let _e342 = (*s_8).n; let _e343 = ior_1; let _e344 = spread_1; - local_31 = refract(-(_e339), _e342, (1f / (_e343 + _e344))); + local_32 = refract(-(_e339), _e342, (1f / (_e343 + _e344))); } - let _e348 = local_31; + let _e348 = local_32; blue = _e348; let _e349 = v_world_position_1; let _e350 = red; @@ -19298,11 +19590,11 @@ fn TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u var lod_2: f32; var thin_1: bool; var red_1: vec3; - var local_32: vec3; - var green_1: vec3; var local_33: vec3; - var blue_1: vec3; + var green_1: vec3; var local_34: vec3; + var blue_1: vec3; + var local_35: vec3; let _e274 = RefractionIor_u0028_(); ior_2 = _e274; @@ -19319,40 +19611,40 @@ fn TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u let _e288 = thin_1; if _e288 { let _e290 = (*s_9).v; - local_32 = -(_e290); + local_33 = -(_e290); } else { let _e293 = (*s_9).v; let _e296 = (*s_9).n; let _e297 = ior_2; let _e298 = spread_2; - local_32 = refract(-(_e293), _e296, (1f / max((_e297 - _e298), 1f))); + local_33 = refract(-(_e293), _e296, (1f / max((_e297 - _e298), 1f))); } - let _e303 = local_32; + let _e303 = local_33; red_1 = _e303; let _e304 = thin_1; if _e304 { let _e306 = (*s_9).v; - local_33 = -(_e306); + local_34 = -(_e306); } else { let _e309 = (*s_9).v; let _e312 = (*s_9).n; let _e313 = ior_2; - local_33 = refract(-(_e309), _e312, (1f / _e313)); + local_34 = refract(-(_e309), _e312, (1f / _e313)); } - let _e316 = local_33; + let _e316 = local_34; green_1 = _e316; let _e317 = thin_1; if _e317 { let _e319 = (*s_9).v; - local_34 = -(_e319); + local_35 = -(_e319); } else { let _e322 = (*s_9).v; let _e325 = (*s_9).n; let _e326 = ior_2; let _e327 = spread_2; - local_34 = refract(-(_e322), _e325, (1f / (_e326 + _e327))); + local_35 = refract(-(_e322), _e325, (1f / (_e326 + _e327))); } - let _e331 = local_34; + let _e331 = local_35; blue_1 = _e331; let _e332 = red_1; let _e333 = lod_2; @@ -19456,7 +19748,7 @@ fn FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b(film: ptr; - var local_35: vec3; + var local_36: vec3; let _e290 = (*film); let _e291 = (*thickness); @@ -19560,12 +19852,12 @@ fn FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b(film: ptr(1f, 1f, 1f); + local_36 = vec3(1f, 1f, 1f); } else { let _e430 = total_2; - local_35 = max(_e430, vec3(0f, 0f, 0f)); + local_36 = max(_e430, vec3(0f, 0f, 0f)); } - let _e432 = local_35; + let _e432 = local_36; return _e432; } @@ -19599,13 +19891,13 @@ fn ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_209: f32; var t_2: vec3; var usable: bool; - var local_36: vec3; + var local_37: vec3; var b_4: vec3; var turn_2: vec2; var along_1: vec3; - var local_37: f32; - var local_38: vec3; - var phi_4683_: bool; + var local_38: f32; + var local_39: vec3; + var phi_4717_: bool; let _e274 = (*s_11).n_dot_v; let _e278 = g_sheen_roughness; @@ -19631,11 +19923,11 @@ fn ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e308 = usable; if _e308 { let _e309 = t_2; - local_36 = normalize(_e309); + local_37 = normalize(_e309); } else { - local_36 = vec3(1f, 0f, 0f); + local_37 = vec3(1f, 0f, 0f); } - let _e311 = local_36; + let _e311 = local_37; t_2 = _e311; let _e313 = (*s_11).n; let _e314 = t_2; @@ -19654,30 +19946,30 @@ fn ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e332 = turn_2[1u]; along_1 = ((_e326 * _e328) + (_e330 * _e332)); let _e335 = usable; - phi_4683_ = _e335; + phi_4717_ = _e335; if _e335 { let _e336 = along_1; let _e337 = along_1; - phi_4683_ = (dot(_e336, _e337) > 0.000000000001f); + phi_4717_ = (dot(_e336, _e337) > 0.000000000001f); } - let _e341 = phi_4683_; + let _e341 = phi_4717_; if _e341 { let _e344 = layer_info.anisotropy[0u]; - local_37 = clamp(_e344, 0f, 1f); + local_38 = clamp(_e344, 0f, 1f); } else { - local_37 = 0f; + local_38 = 0f; } - let _e346 = local_37; + let _e346 = local_38; g_aniso = _e346; let _e347 = g_aniso; if (_e347 > 0f) { let _e349 = along_1; - local_38 = normalize(_e349); + local_39 = normalize(_e349); } else { let _e351 = t_2; - local_38 = _e351; + local_39 = _e351; } - let _e352 = local_38; + let _e352 = local_39; g_aniso_t = _e352; let _e354 = (*s_11).n; let _e355 = g_aniso_t; @@ -19790,16 +20082,16 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var det: f32; var flip: f32; var front: vec3; - var local_39: vec3; + var local_40: vec3; var turned: vec3; var turns: bool; - var local_40: vec3; var local_41: vec3; + var local_42: vec3; var sampled: vec3; - var phi_3502_: bool; - var phi_3509_: bool; - var phi_3516_: bool; - var phi_3523_: bool; + var phi_3536_: bool; + var phi_3543_: bool; + var phi_3550_: bool; + var phi_3557_: bool; param_214 = 2i; let _e277 = MapUv_u0028_i1_u003b((¶m_214)); @@ -19837,12 +20129,12 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e319 = gl_FrontFacing_1; if _e319 { let _e320 = b_5; - local_39 = _e320; + local_40 = _e320; } else { let _e321 = b_5; - local_39 = -(_e321); + local_40 = -(_e321); } - let _e323 = local_39; + let _e323 = local_40; front = _e323; let _e324 = t_3; let _e326 = m_2[3u]; @@ -19852,41 +20144,41 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ turned = (((_e324 * _e326) + (_e328 * _e330)) * _e333); let _e336 = m_2[1u]; let _e337 = (_e336 != 0f); - phi_3502_ = _e337; + phi_3536_ = _e337; if !(_e337) { let _e340 = m_2[2u]; - phi_3502_ = (_e340 != 0f); + phi_3536_ = (_e340 != 0f); } - let _e343 = phi_3502_; - phi_3509_ = _e343; + let _e343 = phi_3536_; + phi_3543_ = _e343; if !(_e343) { let _e346 = m_2[0u]; - phi_3509_ = (_e346 < 0f); + phi_3543_ = (_e346 < 0f); } - let _e349 = phi_3509_; - phi_3516_ = _e349; + let _e349 = phi_3543_; + phi_3550_ = _e349; if !(_e349) { let _e352 = m_2[3u]; - phi_3516_ = (_e352 < 0f); + phi_3550_ = (_e352 < 0f); } - let _e355 = phi_3516_; - phi_3523_ = _e355; + let _e355 = phi_3550_; + phi_3557_ = _e355; if _e355 { let _e356 = turned; let _e357 = turned; - phi_3523_ = (dot(_e356, _e357) > 0.000000000001f); + phi_3557_ = (dot(_e356, _e357) > 0.000000000001f); } - let _e361 = phi_3523_; + let _e361 = phi_3557_; turns = _e361; let _e362 = turns; if _e362 { let _e363 = turned; - local_40 = normalize(_e363); + local_41 = normalize(_e363); } else { let _e365 = t_3; - local_40 = _e365; + local_41 = _e365; } - let _e366 = local_40; + let _e366 = local_41; t_3 = _e366; let _e367 = turns; if _e367 { @@ -19894,12 +20186,12 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e370 = t_3; let _e373 = v_tangent_1[3u]; let _e375 = flip; - local_41 = ((cross(_e369, _e370) * _e373) * _e375); + local_42 = ((cross(_e369, _e370) * _e373) * _e375); } else { let _e377 = b_5; - local_41 = _e377; + local_42 = _e377; } - let _e378 = local_41; + let _e378 = local_42; b_5 = _e378; let _e379 = gl_FrontFacing_1; if !(_e379) { @@ -20063,7 +20355,7 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_5: ptr; var param_227: f32; var param_228: vec2; - var local_42: vec3; + var local_43: vec3; let _e308 = irradiance_info.origin; origin_2 = _e308.xyz; @@ -20238,11 +20530,11 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_5: ptr 0f) { let _e536 = total_3; let _e537 = weights; - local_42 = (_e536 / vec3(_e537)); + local_43 = (_e536 / vec3(_e537)); } else { - local_42 = vec3(0f, 0f, 0f); + local_43 = vec3(0f, 0f, 0f); } - let _e540 = local_42; + let _e540 = local_43; return _e540; } @@ -20295,9 +20587,9 @@ fn ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ var param_235: vec3; var ior_3: f32; var r_5: f32; - var local_43: f32; + var local_44: f32; var distance_5: f32; - var local_44: vec3; + var local_45: vec3; var fc: f32; let _e271 = v_texcoord_1; @@ -20320,13 +20612,13 @@ fn ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ ior_3 = max(_e293, 1f); let _e295 = IorInfinite_u0028_(); if _e295 { - local_43 = 1f; + local_44 = 1f; } else { let _e296 = ior_3; let _e298 = ior_3; - local_43 = ((_e296 - 1f) / (_e298 + 1f)); + local_44 = ((_e296 - 1f) / (_e298 + 1f)); } - let _e301 = local_43; + let _e301 = local_44; r_5 = _e301; let _e302 = r_5; let _e303 = r_5; @@ -20354,11 +20646,11 @@ fn ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ let _e352 = layer_info.attenuation; let _e355 = g_thickness; let _e356 = distance_5; - local_44 = pow(max(_e352.xyz, vec3(0.0001f, 0.0001f, 0.0001f)), vec3((_e355 / _e356))); + local_45 = pow(max(_e352.xyz, vec3(0.0001f, 0.0001f, 0.0001f)), vec3((_e355 / _e356))); } else { - local_44 = vec3(1f, 1f, 1f); + local_45 = vec3(1f, 1f, 1f); } - let _e360 = local_44; + let _e360 = local_45; g_transmittance = _e360; let _e363 = layer_info.iridescence[0u]; g_iridescence = clamp(_e363, 0f, 1f); @@ -20496,7 +20788,7 @@ fn main_1() { var multiple: vec3; var reflects: vec3; var through: vec3; - var local_45: vec3; + var local_46: vec3; var param_249: Surface; var param_250: f32; var coatPrefiltered: vec3; @@ -20684,7 +20976,7 @@ fn main_1() { reflects = ((mix(_e478, _e479, vec3(_e480)) * _e484) + vec3((_e486 * _e488))); let _e492 = SceneColourBound_u0028_(); if _e492 { - local_45 = vec3(0f, 0f, 0f); + local_46 = vec3(0f, 0f, 0f); } else { let _e493 = s_19; param_249 = _e493; @@ -20693,9 +20985,9 @@ fn main_1() { let _e495 = TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_f1_u003b((¶m_249), (¶m_250)); let _e496 = g_transmittance; let _e498 = reflects; - local_45 = ((_e495 * _e496) * (vec3(1f, 1f, 1f) - min(_e498, vec3(1f, 1f, 1f)))); + local_46 = ((_e495 * _e496) * (vec3(1f, 1f, 1f) - min(_e498, vec3(1f, 1f, 1f)))); } - let _e502 = local_45; + let _e502 = local_46; through = _e502; let _e503 = diffuseColor_1; let _e504 = through; @@ -21519,21 +21811,45 @@ fn EyeDistance_u0028_() -> f32 { fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; + var falloff: f32; + var k: f32; + var local_1: f32; - let _e182 = fog_info.fog[3u]; - density = _e182; - let _e183 = density; - if (_e183 <= 0f) { - let _e185 = (*color); - return _e185; + let _e185 = fog_info.fog[3u]; + density = _e185; + let _e186 = density; + if (_e186 <= 0f) { + let _e188 = (*color); + return _e188; + } + let _e189 = EyeDistance_u0028_(); + d = _e189; + let _e192 = fog_info.eye[3u]; + falloff = _e192; + let _e193 = falloff; + if (_e193 > 0f) { + let _e195 = falloff; + let _e197 = v_world_position_1[1u]; + let _e200 = fog_info.eye[1u]; + k = (_e195 * (_e197 - _e200)); + let _e203 = k; + if (abs(_e203) > 0.001f) { + let _e206 = k; + let _e210 = k; + local_1 = ((1f - exp(-(_e206))) / _e210); + } else { + let _e212 = k; + local_1 = (1f - (0.5f * _e212)); + } + let _e215 = local_1; + let _e216 = density; + density = (_e216 * _e215); } - let _e186 = EyeDistance_u0028_(); - d = _e186; - let _e188 = fog_info.fog; - let _e190 = (*color); - let _e191 = density; - let _e193 = d; - return mix(_e188.xyz, _e190, vec3(clamp(exp((-(_e191) * _e193)), 0f, 1f))); + let _e219 = fog_info.fog; + let _e221 = (*color); + let _e222 = density; + let _e224 = d; + return mix(_e219.xyz, _e221, vec3(clamp(exp((-(_e222) * _e224)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -21639,7 +21955,7 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_1: vec2; + var local_2: vec2; var atlas: vec2; let _e186 = point_shadow.params[0u]; @@ -21648,8 +21964,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr(0.16666667f, 0.16666667f)); let _e202 = point_shadow.params3_[0u]; @@ -21807,19 +22123,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_2: vec2; + var local_3: vec2; let _e179 = (*rows); if (_e179 > 0f) { let _e182 = gl_FragCoord_1[0u]; let _e183 = (*rows); let _e185 = gl_FragCoord_1[1u]; - local_2 = vec2(_e182, (_e183 - _e185)); + local_3 = vec2(_e182, (_e183 - _e185)); } else { let _e188 = gl_FragCoord_1; - local_2 = _e188.xy; + local_3 = _e188.xy; } - let _e190 = local_2; + let _e190 = local_3; return _e190; } @@ -21858,7 +22174,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_4: vec3; var lit: f32; var param_31: vec2; var param_32: vec2; @@ -21875,8 +22191,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_43: f32; var param_44: f32; - var phi_2149_: bool; - var phi_2210_: bool; + var phi_2184_: bool; + var phi_2245_: bool; let _e231 = (*lightIndex); let _e235 = point_shadow.slots[_e231][0u]; @@ -21941,13 +22257,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e331); - phi_2149_ = _e332; + phi_2184_ = _e332; if _e332 { let _e334 = a[0u]; let _e336 = a[2u]; - phi_2149_ = (_e334 >= _e336); + phi_2184_ = (_e334 >= _e336); } - let _e339 = phi_2149_; + let _e339 = phi_2184_; if _e339 { let _e341 = toFragment[0u]; face = select(1i, 0i, (_e341 > 0f)); @@ -21977,12 +22293,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2210_ = _e382; + phi_2245_ = _e382; if !(_e382) { let _e385 = ndc[1u]; - phi_2210_ = (abs(_e385) > 1f); + phi_2245_ = (abs(_e385) > 1f); } - let _e389 = phi_2210_; + let _e389 = phi_2245_; if _e389 { return 1f; } @@ -22032,15 +22348,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_4 = vec3(0f, 0f, 1f); } else { let _e444 = radius_1; let _e447 = point_shadow.params2_[2u]; let _e451 = blocker_1; let _e452 = range_3; - local_3 = vec3(clamp((_e444 / max(_e447, 0.000001f)), 0f, 1f), clamp((_e451 / _e452), 0f, 1f), 0f); + local_4 = vec3(clamp((_e444 / max(_e447, 0.000001f)), 0f, 1f), clamp((_e451 / _e452), 0f, 1f), 0f); } - let _e456 = local_3; + let _e456 = local_4; g_debug_surface = _e456; } let _e457 = radius_1; @@ -22232,8 +22548,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_4: mat4x4; var local_5: mat4x4; + var local_6: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -22247,8 +22563,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_6: f32; var local_7: f32; + var local_8: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -22282,11 +22598,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_63: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3269_: bool; - var phi_3280_: bool; - var phi_3441_: bool; - var phi_3448_: bool; - var phi_3455_: bool; + var phi_3303_: bool; + var phi_3314_: bool; + var phi_3475_: bool; + var phi_3482_: bool; + var phi_3489_: bool; let _e245 = frag_info.shadow_params[3u]; strength_1 = _e245; @@ -22307,25 +22623,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e266 = cascadeCount; let _e267 = (_e266 > 1i); - phi_3269_ = _e267; + phi_3303_ = _e267; if _e267 { let _e268 = viewDistance; let _e271 = frag_info.shadow_cascades[0u]; - phi_3269_ = (_e268 > _e271); + phi_3303_ = (_e268 > _e271); } - let _e274 = phi_3269_; + let _e274 = phi_3303_; if _e274 { cascade = 1i; } let _e275 = cascadeCount; let _e276 = (_e275 > 2i); - phi_3280_ = _e276; + phi_3314_ = _e276; if _e276 { let _e277 = viewDistance; let _e280 = frag_info.shadow_cascades[1u]; - phi_3280_ = (_e277 > _e280); + phi_3314_ = (_e277 > _e280); } - let _e283 = phi_3280_; + let _e283 = phi_3314_; if _e283 { cascade = 2i; } @@ -22350,20 +22666,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e292 = which; if (_e292 == 0i) { let _e295 = frag_info.shadow_matrix; - local_4 = _e295; + local_5 = _e295; } else { let _e296 = which; if (_e296 == 1i) { let _e299 = frag_info.shadow_matrix_far; - local_5 = _e299; + local_6 = _e299; } else { let _e301 = frag_info.shadow_matrix_farthest; - local_5 = _e301; + local_6 = _e301; } - let _e302 = local_5; - local_4 = _e302; + let _e302 = local_6; + local_5 = _e302; } - let _e303 = local_4; + let _e303 = local_5; matrix = _e303; let _e306 = matrix[0][0u]; let _e309 = matrix[1][0u]; @@ -22413,24 +22729,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e390 * 0.5f) + 0.5f), (0.5f - (_e394 * 0.5f))); let _e399 = inTile[0u]; let _e400 = (_e399 < 0f); - phi_3441_ = _e400; + phi_3475_ = _e400; if !(_e400) { let _e403 = inTile[0u]; - phi_3441_ = (_e403 > 1f); + phi_3475_ = (_e403 > 1f); } - let _e406 = phi_3441_; - phi_3448_ = _e406; + let _e406 = phi_3475_; + phi_3482_ = _e406; if !(_e406) { let _e409 = inTile[1u]; - phi_3448_ = (_e409 < 0f); + phi_3482_ = (_e409 < 0f); } - let _e412 = phi_3448_; - phi_3455_ = _e412; + let _e412 = phi_3482_; + phi_3489_ = _e412; if !(_e412) { let _e415 = inTile[1u]; - phi_3455_ = (_e415 > 1f); + phi_3489_ = (_e415 > 1f); } - let _e418 = phi_3455_; + let _e418 = phi_3489_; if _e418 { continue; } @@ -22487,20 +22803,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e487 = cascade; if (_e487 == 0i) { let _e491 = frag_info.shadow_bias[0u]; - local_6 = _e491; + local_7 = _e491; } else { let _e492 = cascade; if (_e492 == 1i) { let _e496 = frag_info.shadow_bias[1u]; - local_7 = _e496; + local_8 = _e496; } else { let _e499 = frag_info.shadow_bias[2u]; - local_7 = _e499; + local_8 = _e499; } - let _e500 = local_7; - local_6 = _e500; + let _e500 = local_8; + local_7 = _e500; } - let _e501 = local_6; + let _e501 = local_7; bias = _e501; let _e504 = frag_info.shadow_params[0u]; let _e507 = frag_info.shadow_cascades[3u]; @@ -22745,7 +23061,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_8: vec3; + var local_9: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -22787,15 +23103,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e227 > 0f) { let _e229 = (*mirror); let _e230 = t_1; - local_8 = (_e229 * _e230); + local_9 = (_e229 * _e230); } else { let _e232 = toPlane; let _e233 = n_2; let _e234 = toPlane; let _e235 = n_2; - local_8 = (_e232 - (_e233 * dot(_e234, _e235))); + local_9 = (_e232 - (_e233 * dot(_e234, _e235))); } - let _e239 = local_8; + let _e239 = local_9; onPlane = _e239; } let _e240 = onPlane; @@ -22829,7 +23145,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_9: f32; + var local_10: f32; let _e186 = (*a_1); let _e187 = (*a_1); @@ -22851,11 +23167,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> let _e211 = axis; let _e212 = (*n_3); let _e215 = len; - local_9 = ((_e210 * dot(_e211, _e212)) / _e215); + local_10 = ((_e210 * dot(_e211, _e212)) / _e215); } else { - local_9 = 0f; + local_10 = 0f; } - let _e217 = local_9; + let _e217 = local_10; return _e217; } @@ -22866,15 +23182,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_10: i32; + var local_11: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_11: f32; + var local_12: f32; var aAbove: bool; var bAbove: bool; - var local_12: f32; + var local_13: f32; var param_67: vec3; var param_68: vec3; var param_69: vec3; @@ -22894,12 +23210,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr 0.000000000001f) { let _e228 = ha; let _e229 = d_3; - local_11 = (_e228 / _e229); + local_12 = (_e228 / _e229); } else { - local_11 = 0f; + local_12 = 0f; } - let _e231 = local_11; + let _e231 = local_12; q = (_e221 + ((_e222 - _e223) * _e231)); let _e234 = ha; aAbove = (_e234 > 0f); @@ -22942,11 +23258,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_13: f32; var local_14: f32; var local_15: f32; + var local_16: f32; let _e183 = (*slot_1); let _e184 = (*slot_1); @@ -22993,28 +23309,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 let _e188 = lane; if (_e188 == 0i) { let _e191 = (*four)[0u]; - local_13 = _e191; + local_14 = _e191; } else { let _e192 = lane; if (_e192 == 1i) { let _e195 = (*four)[1u]; - local_14 = _e195; + local_15 = _e195; } else { let _e196 = lane; if (_e196 == 2i) { let _e199 = (*four)[2u]; - local_15 = _e199; + local_16 = _e199; } else { let _e201 = (*four)[3u]; - local_15 = _e201; + local_16 = _e201; } - let _e202 = local_15; - local_14 = _e202; + let _e202 = local_16; + local_15 = _e202; } - let _e203 = local_14; - local_13 = _e203; + let _e203 = local_15; + local_14 = _e203; } - let _e204 = local_13; + let _e204 = local_14; return _e204; } @@ -23125,12 +23441,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_16: f32; + var local_17: f32; var param_81: i32; var param_82: i32; var v_1: f32; var u_1: f32; - var local_17: f32; + var local_18: f32; var param_83: f32; var param_84: i32; var type_39: f32; @@ -23140,12 +23456,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_18: f32; + var local_19: f32; var param_85: array, 4>; var param_86: vec3; var area: f32; var radiance: f32; - var local_19: f32; + var local_20: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -23158,7 +23474,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_91: vec3; var toPoint: vec3; var pointDistance: f32; - var local_20: vec3; + var local_21: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; @@ -23193,14 +23509,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e252 = slot_5; param_81 = _e252; let _e253 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_16 = _e253; + local_17 = _e253; } else { let _e254 = slot_5; param_82 = _e254; let _e255 = LightListRow_u0028_i1_u003b((¶m_82)); - local_16 = _e255; + local_17 = _e255; } - let _e256 = local_16; + let _e256 = local_17; listRow = _e256; let _e257 = listRow; let _e261 = light_list_info.list[2u]; @@ -23228,14 +23544,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e287 = listRow; param_83 = _e287; let _e288 = InSlots_u0028_f1_u003b((¶m_83)); - local_17 = select(1f, 0f, _e288); + local_18 = select(1f, 0f, _e288); } else { let _e290 = slot_5; param_84 = _e290; let _e291 = LightListScale_u0028_i1_u003b((¶m_84)); - local_17 = _e291; + local_18 = _e291; } - let _e292 = local_17; + let _e292 = local_18; let _e294 = color_1[3u]; color_1[3u] = (_e294 * _e292); } @@ -23272,16 +23588,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e333, cross(_e334, _e335)) >= 0f); let _e339 = behind; if _e339 { - local_18 = 0f; + local_19 = 0f; } else { let _e340 = corners_1; param_85 = _e340; let _e342 = (*s_3).n; param_86 = _e342; let _e343 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_18 = _e343; + local_19 = _e343; } - let _e344 = local_18; + let _e344 = local_19; formFactor = _e344; let _e345 = halfWidth_1; let _e346 = halfHeight_1; @@ -23289,11 +23605,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e350 = area; if (_e350 > 0.000000001f) { let _e352 = area; - local_19 = (1f / _e352); + local_20 = (1f / _e352); } else { - local_19 = 0f; + local_20 = 0f; } - let _e354 = local_19; + let _e354 = local_20; radiance = _e354; let _e355 = toCentre; distance_2 = length(_e355); @@ -23336,12 +23652,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e396 > 0.000001f) { let _e398 = toPoint; let _e399 = pointDistance; - local_20 = (_e398 / vec3(_e399)); + local_21 = (_e398 / vec3(_e399)); } else { let _e403 = (*s_3).n; - local_20 = _e403; + local_21 = _e403; } - let _e404 = local_20; + let _e404 = local_21; light_3.l = _e404; let _e407 = light_3.l; let _e409 = (*s_3).v; @@ -23437,7 +23753,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_21: f32; + var local_22: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -23465,15 +23781,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e237 = clip_1[3u]; let _e238 = near_1; if (_e237 <= _e238) { - local_21 = 0f; + local_22 = 0f; } else { let _e241 = clip_1[3u]; let _e242 = near_1; let _e247 = light_list_info.cluster_depth[1u]; let _e251 = grid[2u]; - local_21 = clamp(floor((log((_e241 / _e242)) * _e247)), 0f, (_e251 - 1f)); + local_22 = clamp(floor((log((_e241 / _e242)) * _e247)), 0f, (_e251 - 1f)); } - let _e254 = local_21; + let _e254 = local_22; tz = _e254; let _e255 = tx; let _e256 = ty; @@ -23527,7 +23843,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_98: Surface; var visibility: f32; var param_99: i32; - var local_22: f32; + var local_23: f32; var param_100: Surface; var param_101: LightSample; var param_102: i32; @@ -23577,11 +23893,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e216 = i_8; param_105 = _e216; let _e217 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_22 = (_e212 * _e217); + local_23 = (_e212 * _e217); } else { - local_22 = 1f; + local_23 = 1f; } - let _e219 = local_22; + let _e219 = local_23; visibility = _e219; let _e220 = visibility; if (_e220 <= 0f) { @@ -23869,7 +24185,7 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var param_120: f32; var param_121: vec2; - var local_23: vec3; + var local_24: vec3; let _e224 = irradiance_info.origin; origin_2 = _e224.xyz; @@ -24044,11 +24360,11 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr 0f) { let _e452 = total_2; let _e453 = weights; - local_23 = (_e452 / vec3(_e453)); + local_24 = (_e452 / vec3(_e453)); } else { - local_23 = vec3(0f, 0f, 0f); + local_24 = vec3(0f, 0f, 0f); } - let _e456 = local_23; + let _e456 = local_24; return _e456; } @@ -41630,7 +41946,7 @@ fn main_1() { } @fragment -fn main(@location(4) v_ray: vec3, @location(7) v_zenith: vec4, @location(3) v_nadir: vec4, @location(2) v_horizon: vec4, @location(5) v_sun: vec4, @location(1) v_glow: vec4, @location(0) v_disc: vec4) -> @location(0) vec4 { +fn main(@location(6) v_ray: vec3, @location(11) v_zenith: vec4, @location(4) v_nadir: vec4, @location(2) v_horizon: vec4, @location(9) v_sun: vec4, @location(1) v_glow: vec4, @location(0) v_disc: vec4) -> @location(0) vec4 { v_ray_1 = v_ray; v_zenith_1 = v_zenith; v_nadir_1 = v_nadir; @@ -41676,7 +41992,7 @@ fn main_1() { } @fragment -fn main(@location(4) v_ray: vec3, @location(6) v_tint: vec4) -> @location(0) vec4 { +fn main(@location(6) v_ray: vec3, @location(10) v_tint: vec4) -> @location(0) vec4 { v_ray_1 = v_ray; v_tint_1 = v_tint; main_1(); @@ -41696,6 +42012,511 @@ fn main(@location(4) v_ray: vec3, @location(6) v_tint: vec4) -> @locat ), ], ), + 'SkyPhysical': WebGpuStage( + wgsl: r''' +var v_planet_1: vec4; +var v_rayleigh_1: vec4; +var v_mie_1: vec4; +var v_stars_1: vec4; +var v_ray_1: vec3; +var v_sun_1: vec4; +var v_disc_1: vec4; +var frag_color: vec4; + +fn Hash_u0028_vf3_u003b(p: ptr>) -> f32 { + let _e48 = (*p); + (*p) = fract((_e48 * 0.1031f)); + let _e51 = (*p); + let _e52 = (*p); + let _e57 = (*p); + (*p) = (_e57 + vec3(dot(_e51, (_e52.zyx + vec3(31.32f))))); + let _e61 = (*p)[0u]; + let _e63 = (*p)[1u]; + let _e66 = (*p)[2u]; + return fract(((_e61 + _e63) * _e66)); +} + +fn StarField_u0028_vf3_u003b(d: ptr>) -> f32 { + var a: vec3; + var face: f32; + var uv: vec2; + var grid: vec2; + var cell: vec2; + var key: vec3; + var param: vec3; + var centre: vec2; + var param_1: vec3; + var param_2: vec3; + var off: f32; + var magnitude: f32; + var param_3: vec3; + var phi_240_: bool; + + let _e61 = (*d); + a = abs(_e61); + let _e64 = a[0u]; + let _e66 = a[1u]; + let _e67 = (_e64 >= _e66); + phi_240_ = _e67; + if _e67 { + let _e69 = a[0u]; + let _e71 = a[2u]; + phi_240_ = (_e69 >= _e71); + } + let _e74 = phi_240_; + if _e74 { + let _e76 = (*d)[0u]; + face = select(1f, 0f, (_e76 > 0f)); + let _e79 = (*d); + let _e82 = a[0u]; + uv = (_e79.zy / vec2(_e82)); + } else { + let _e86 = a[1u]; + let _e88 = a[2u]; + if (_e86 >= _e88) { + let _e91 = (*d)[1u]; + face = select(3f, 2f, (_e91 > 0f)); + let _e94 = (*d); + let _e97 = a[1u]; + uv = (_e94.xz / vec2(_e97)); + } else { + let _e101 = (*d)[2u]; + face = select(5f, 4f, (_e101 > 0f)); + let _e104 = (*d); + let _e107 = a[2u]; + uv = (_e104.xy / vec2(_e107)); + } + } + let _e110 = uv; + let _e115 = v_stars_1[2u]; + grid = (((_e110 * 0.5f) + vec2(0.5f)) * _e115); + let _e117 = grid; + cell = floor(_e117); + let _e119 = cell; + let _e120 = face; + key = vec3(_e119.x, _e119.y, _e120); + let _e124 = key; + param = _e124; + let _e125 = Hash_u0028_vf3_u003b((¶m)); + let _e127 = v_stars_1[1u]; + if (_e125 >= _e127) { + return 0f; + } + let _e129 = key; + param_1 = (_e129 + vec3(17f, 0f, 0f)); + let _e131 = Hash_u0028_vf3_u003b((¶m_1)); + let _e132 = key; + param_2 = (_e132 + vec3(0f, 29f, 0f)); + let _e134 = Hash_u0028_vf3_u003b((¶m_2)); + centre = ((vec2(_e131, _e134) * 0.6f) + vec2(0.2f)); + let _e139 = grid; + let _e140 = cell; + let _e142 = centre; + off = length(((_e139 - _e140) - _e142)); + let _e145 = key; + param_3 = (_e145 + vec3(0f, 0f, 13f)); + let _e147 = Hash_u0028_vf3_u003b((¶m_3)); + magnitude = _e147; + let _e148 = off; + let _e151 = magnitude; + let _e153 = magnitude; + let _e155 = magnitude; + return ((1f - smoothstep(0f, 0.35f, _e148)) * (0.15f + (((0.85f * _e151) * _e153) * _e155))); +} + +fn Through_u0028_vf2_u003b(air: ptr>) -> vec3 { + let _e48 = v_rayleigh_1; + let _e51 = (*air)[0u]; + let _e54 = v_mie_1[1u]; + let _e56 = (*air)[1u]; + return exp(-(((_e48.xyz * _e51) + vec3((_e54 * _e56))))); +} + +fn Leave_u0028_f1_u003b_f1_u003b_f1_u003b(r: ptr, mu: ptr, radius: ptr) -> f32 { + var b: f32; + var d_1: f32; + var local: f32; + + let _e53 = (*r); + let _e54 = (*mu); + b = (_e53 * _e54); + let _e56 = b; + let _e57 = b; + let _e59 = (*r); + let _e60 = (*radius); + let _e62 = (*r); + let _e63 = (*radius); + d_1 = ((_e56 * _e57) - ((_e59 - _e60) * (_e62 + _e63))); + let _e67 = d_1; + if (_e67 < 0f) { + local = -1f; + } else { + let _e69 = b; + let _e71 = d_1; + local = (-(_e69) + sqrt(_e71)); + } + let _e74 = local; + return _e74; +} + +fn SunwardAir_u0028_vf3_u003b_vf3_u003b(p_1: ptr>, s: ptr>) -> vec2 { + var r_1: f32; + var span: f32; + var param_4: f32; + var param_5: f32; + var param_6: f32; + var stride: f32; + var air_1: vec2; + var j: i32; + var q: vec3; + var h: f32; + + let _e59 = (*p_1); + r_1 = length(_e59); + let _e61 = (*p_1); + let _e62 = (*s); + let _e64 = r_1; + let _e66 = r_1; + param_4 = _e66; + param_5 = (dot(_e61, _e62) / _e64); + let _e68 = v_planet_1[1u]; + param_6 = _e68; + let _e69 = Leave_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_4), (¶m_5), (¶m_6)); + span = _e69; + let _e70 = span; + stride = (_e70 / 8f); + air_1 = vec2(0f, 0f); + j = 0i; + loop { + let _e72 = j; + if (_e72 < 8i) { + let _e74 = (*p_1); + let _e75 = (*s); + let _e76 = j; + let _e79 = stride; + q = (_e74 + (_e75 * ((f32(_e76) + 0.5f) * _e79))); + let _e83 = q; + let _e86 = v_planet_1[0u]; + h = (length(_e83) - _e86); + let _e88 = h; + let _e91 = v_rayleigh_1[3u]; + let _e93 = v_mie_1[2u]; + let _e98 = stride; + let _e100 = air_1; + air_1 = (_e100 + (exp((vec2(-(_e88)) / vec2(_e91, _e93))) * _e98)); + continue; + } else { + break; + } + continuing { + let _e102 = j; + j = (_e102 + 1i); + } + } + let _e104 = air_1; + return _e104; +} + +fn Meet_u0028_f1_u003b_f1_u003b_f1_u003b(r_2: ptr, mu_1: ptr, radius_1: ptr) -> f32 { + var b_1: f32; + var d_2: f32; + var local_1: f32; + + let _e53 = (*r_2); + let _e54 = (*mu_1); + b_1 = (_e53 * _e54); + let _e56 = b_1; + let _e57 = b_1; + let _e59 = (*r_2); + let _e60 = (*radius_1); + let _e62 = (*r_2); + let _e63 = (*radius_1); + d_2 = ((_e56 * _e57) - ((_e59 - _e60) * (_e62 + _e63))); + let _e67 = d_2; + if (_e67 < 0f) { + local_1 = -1f; + } else { + let _e69 = b_1; + let _e71 = d_2; + local_1 = (-(_e69) - sqrt(_e71)); + } + let _e74 = local_1; + return _e74; +} + +fn main_1() { + var d_3: vec3; + var s_1: vec3; + var eye: vec3; + var ground: f32; + var param_7: f32; + var param_8: f32; + var param_9: f32; + var grounded: bool; + var span_1: f32; + var local_2: f32; + var param_10: f32; + var param_11: f32; + var param_12: f32; + var seenAir: vec2; + var molecules: vec3; + var haze: vec3; + var i: i32; + var a0_: f32; + var a1_: f32; + var t: f32; + var stride_1: f32; + var p_2: vec3; + var r_3: f32; + var air_2: vec2; + var param_13: f32; + var param_14: f32; + var param_15: f32; + var through: vec3; + var param_16: vec3; + var param_17: vec3; + var param_18: vec2; + var mu_2: f32; + var g: f32; + var gg: f32; + var rayleighPhase: f32; + var miePhase: f32; + var colour: vec3; + var seen: vec3; + var param_19: vec2; + var p_3: vec3; + var lit: f32; + var param_20: vec3; + var param_21: vec3; + var param_22: vec2; + var disc: f32; + var local_3: f32; + var night: f32; + var param_23: vec3; + + let _e95 = v_ray_1; + d_3 = normalize(_e95); + let _e97 = v_sun_1; + s_1 = _e97.xyz; + let _e100 = v_planet_1[2u]; + eye = vec3(0f, _e100, 0f); + let _e103 = v_planet_1[2u]; + param_7 = _e103; + let _e105 = d_3[1u]; + param_8 = _e105; + let _e107 = v_planet_1[0u]; + param_9 = _e107; + let _e108 = Meet_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_7), (¶m_8), (¶m_9)); + ground = _e108; + let _e109 = ground; + grounded = (_e109 > 0f); + let _e111 = grounded; + if _e111 { + let _e112 = ground; + local_2 = _e112; + } else { + let _e114 = v_planet_1[2u]; + param_10 = _e114; + let _e116 = d_3[1u]; + param_11 = _e116; + let _e118 = v_planet_1[1u]; + param_12 = _e118; + let _e119 = Leave_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12)); + local_2 = _e119; + } + let _e120 = local_2; + span_1 = _e120; + seenAir = vec2(0f, 0f); + molecules = vec3(0f, 0f, 0f); + haze = vec3(0f, 0f, 0f); + i = 0i; + loop { + let _e121 = i; + if (_e121 < 16i) { + let _e123 = i; + a0_ = (f32(_e123) / 16f); + let _e126 = i; + a1_ = (f32((_e126 + 1i)) / 16f); + let _e130 = span_1; + let _e132 = a0_; + let _e133 = a0_; + let _e135 = a1_; + let _e136 = a1_; + t = ((_e130 * 0.5f) * ((_e132 * _e133) + (_e135 * _e136))); + let _e140 = span_1; + let _e141 = a1_; + let _e142 = a1_; + let _e144 = a0_; + let _e145 = a0_; + stride_1 = (_e140 * ((_e141 * _e142) - (_e144 * _e145))); + let _e149 = eye; + let _e150 = d_3; + let _e151 = t; + p_2 = (_e149 + (_e150 * _e151)); + let _e154 = p_2; + r_3 = length(_e154); + let _e156 = r_3; + let _e158 = v_planet_1[0u]; + let _e162 = v_rayleigh_1[3u]; + let _e164 = v_mie_1[2u]; + let _e169 = stride_1; + air_2 = (exp((vec2(-((_e156 - _e158))) / vec2(_e162, _e164))) * _e169); + let _e171 = air_2; + let _e172 = seenAir; + seenAir = (_e172 + _e171); + let _e174 = p_2; + let _e175 = s_1; + let _e177 = r_3; + let _e179 = r_3; + param_13 = _e179; + param_14 = (dot(_e174, _e175) / _e177); + let _e181 = v_planet_1[0u]; + param_15 = _e181; + let _e182 = Meet_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_13), (¶m_14), (¶m_15)); + if (_e182 > 0f) { + continue; + } + let _e184 = seenAir; + let _e185 = air_2; + let _e188 = p_2; + param_16 = _e188; + let _e189 = s_1; + param_17 = _e189; + let _e190 = SunwardAir_u0028_vf3_u003b_vf3_u003b((¶m_16), (¶m_17)); + param_18 = ((_e184 - (_e185 * 0.5f)) + _e190); + let _e192 = Through_u0028_vf2_u003b((¶m_18)); + through = _e192; + let _e194 = air_2[0u]; + let _e195 = through; + let _e197 = molecules; + molecules = (_e197 + (_e195 * _e194)); + let _e200 = air_2[1u]; + let _e201 = through; + let _e203 = haze; + haze = (_e203 + (_e201 * _e200)); + continue; + } else { + break; + } + continuing { + let _e205 = i; + i = (_e205 + 1i); + } + } + let _e207 = d_3; + let _e208 = s_1; + mu_2 = dot(_e207, _e208); + let _e211 = v_mie_1[3u]; + g = _e211; + let _e212 = g; + let _e213 = g; + gg = (_e212 * _e213); + let _e215 = mu_2; + let _e216 = mu_2; + rayleighPhase = (0.059683103f * (1f + (_e215 * _e216))); + let _e220 = gg; + let _e222 = mu_2; + let _e223 = mu_2; + let _e228 = gg; + let _e230 = gg; + let _e232 = g; + let _e234 = mu_2; + miePhase = ((0.119366206f * ((1f - _e220) * (1f + (_e222 * _e223)))) / ((2f + _e228) * pow(((1f + _e230) - ((2f * _e232) * _e234)), 1.5f))); + let _e241 = v_sun_1[3u]; + let _e242 = molecules; + let _e243 = v_rayleigh_1; + let _e246 = rayleighPhase; + let _e248 = haze; + let _e250 = v_mie_1[0u]; + let _e251 = miePhase; + colour = ((((_e242 * _e243.xyz) * _e246) + (_e248 * (_e250 * _e251))) * _e241); + let _e256 = seenAir; + param_19 = _e256; + let _e257 = Through_u0028_vf2_u003b((¶m_19)); + seen = _e257; + let _e258 = grounded; + if _e258 { + let _e259 = eye; + let _e260 = d_3; + let _e261 = span_1; + p_3 = (_e259 + (_e260 * _e261)); + let _e264 = p_3; + let _e266 = s_1; + lit = dot(normalize(_e264), _e266); + let _e268 = lit; + if (_e268 > 0f) { + let _e270 = seen; + let _e271 = p_3; + param_20 = _e271; + let _e272 = s_1; + param_21 = _e272; + let _e273 = SunwardAir_u0028_vf3_u003b_vf3_u003b((¶m_20), (¶m_21)); + param_22 = _e273; + let _e274 = Through_u0028_vf2_u003b((¶m_22)); + let _e277 = v_planet_1[3u]; + let _e279 = lit; + let _e282 = v_sun_1[3u]; + let _e285 = colour; + colour = (_e285 + ((_e270 * _e274) * (((_e277 / 3.1415927f) * _e279) * _e282))); + } + } else { + let _e288 = v_disc_1[1u]; + if (_e288 > 0f) { + let _e291 = v_disc_1[0u]; + let _e293 = v_disc_1[1u]; + let _e296 = v_disc_1[0u]; + let _e297 = mu_2; + local_3 = smoothstep((_e291 - _e293), _e296, _e297); + } else { + let _e300 = v_disc_1[0u]; + let _e301 = mu_2; + local_3 = step(_e300, _e301); + } + let _e303 = local_3; + disc = _e303; + let _e304 = seen; + let _e305 = disc; + let _e307 = v_disc_1[2u]; + let _e310 = colour; + colour = (_e310 + (_e304 * (_e305 * _e307))); + let _e313 = s_1[1u]; + night = (1f - smoothstep(-0.2f, 0.1f, _e313)); + let _e317 = v_stars_1[0u]; + let _e319 = night; + if ((_e317 > 0f) && (_e319 > 0f)) { + let _e322 = seen; + let _e324 = v_stars_1[0u]; + let _e325 = night; + let _e327 = d_3; + param_23 = _e327; + let _e328 = StarField_u0028_vf3_u003b((¶m_23)); + let _e331 = colour; + colour = (_e331 + (_e322 * ((_e324 * _e325) * _e328))); + } + } + let _e333 = colour; + frag_color = vec4(_e333.x, _e333.y, _e333.z, 1f); + return; +} + +@fragment +fn main(@location(5) v_planet: vec4, @location(7) v_rayleigh: vec4, @location(3) v_mie: vec4, @location(8) v_stars: vec4, @location(6) v_ray: vec3, @location(9) v_sun: vec4, @location(0) v_disc: vec4) -> @location(0) vec4 { + v_planet_1 = v_planet; + v_rayleigh_1 = v_rayleigh; + v_mie_1 = v_mie; + v_stars_1 = v_stars; + v_ray_1 = v_ray; + v_sun_1 = v_sun; + v_disc_1 = v_disc; + main_1(); + let _e15 = frag_color; + return _e15; +} +''', + attributes: [], + blocks: [], + samplers: [], + ), 'Luminance': WebGpuStage( wgsl: r''' struct LuminanceInfo { @@ -43029,10 +43850,10 @@ var v_tangent_1: vec4; var v_lightmap_uv_1: vec2; fn ViewDepth_u0028_() -> f32 { - let _e166 = v_world_position_1; - let _e168 = fog_info.eye; - let _e172 = fog_info.forward; - return dot((_e166 - _e168.xyz), _e172.xyz); + let _e167 = v_world_position_1; + let _e169 = fog_info.eye; + let _e173 = fog_info.forward; + return dot((_e167 - _e169.xyz), _e173.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -43041,22 +43862,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e171 = ViewDepth_u0028_(); - z = abs(_e171); - let _e173 = z; - near = (_e173 / 5f); - let _e175 = z; - far = (_e175 / 200f); - let _e177 = far; + let _e172 = ViewDepth_u0028_(); + z = abs(_e172); + let _e174 = z; + near = (_e174 / 5f); + let _e176 = z; + far = (_e176 / 200f); let _e178 = far; - let _e180 = far; - far3_ = ((_e177 * _e178) * _e180); - let _e182 = (*alpha); - let _e183 = near; + let _e179 = far; + let _e181 = far; + far3_ = ((_e178 * _e179) * _e181); + let _e183 = (*alpha); let _e184 = near; - let _e187 = far3_; + let _e185 = near; let _e188 = far3_; - return (_e182 * clamp((10f / ((0.00001f + (_e183 * _e184)) + (_e187 * _e188))), 0.01f, 3000f)); + let _e189 = far3_; + return (_e183 * clamp((10f / ((0.00001f + (_e184 * _e185)) + (_e188 * _e189))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -43068,39 +43889,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e175 = fog_info.forward[3u]; - mode = _e175; - let _e176 = mode; - if (_e176 > 0.5f) { - let _e179 = frag_color[3u]; - alpha_1 = _e179; - let _e180 = alpha_1; - param = _e180; - let _e181 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e181; - let _e182 = frag_color; - let _e184 = weight; - let _e185 = (_e182.xyz * _e184); - let _e186 = alpha_1; - let _e187 = weight; - accumulate = vec4(_e185.x, _e185.y, _e185.z, (_e186 * _e187)); - let _e193 = mode; - let _e195 = mode; - revealage = ((_e193 > 1.5f) && (_e195 < 2.5f)); - let _e198 = revealage; - if _e198 { - let _e199 = alpha_1; - local = vec4(_e199); + let _e176 = fog_info.forward[3u]; + mode = _e176; + let _e177 = mode; + if (_e177 > 0.5f) { + let _e180 = frag_color[3u]; + alpha_1 = _e180; + let _e181 = alpha_1; + param = _e181; + let _e182 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e182; + let _e183 = frag_color; + let _e185 = weight; + let _e186 = (_e183.xyz * _e185); + let _e187 = alpha_1; + let _e188 = weight; + accumulate = vec4(_e186.x, _e186.y, _e186.z, (_e187 * _e188)); + let _e194 = mode; + let _e196 = mode; + revealage = ((_e194 > 1.5f) && (_e196 < 2.5f)); + let _e199 = revealage; + if _e199 { + let _e200 = alpha_1; + local = vec4(_e200); } else { - let _e201 = accumulate; - local = _e201; + let _e202 = accumulate; + local = _e202; } - let _e202 = local; - frag_color = _e202; - let _e203 = mode; - if (_e203 > 2.5f) { - let _e205 = alpha_1; - frag_surface = vec4(_e205); + let _e203 = local; + frag_color = _e203; + let _e204 = mode; + if (_e204 > 2.5f) { + let _e206 = alpha_1; + frag_surface = vec4(_e206); } } return; @@ -43109,29 +43930,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e169 = (*n)[0u]; - let _e172 = (*n)[1u]; - let _e176 = (*n)[2u]; - let _e179 = (*n); - (*n) = (_e179 / vec3(((abs(_e169) + abs(_e172)) + abs(_e176)))); - let _e182 = (*n); - e = _e182.xy; - let _e185 = (*n)[2u]; - if (_e185 < 0f) { - let _e187 = (*n); - let _e193 = (*n)[0u]; - let _e197 = (*n)[1u]; - e = ((vec2(1f) - abs(_e187.yx)) * vec2(select(-1f, 1f, (_e193 >= 0f)), select(-1f, 1f, (_e197 >= 0f)))); + let _e170 = (*n)[0u]; + let _e173 = (*n)[1u]; + let _e177 = (*n)[2u]; + let _e180 = (*n); + (*n) = (_e180 / vec3(((abs(_e170) + abs(_e173)) + abs(_e177)))); + let _e183 = (*n); + e = _e183.xy; + let _e186 = (*n)[2u]; + if (_e186 < 0f) { + let _e188 = (*n); + let _e194 = (*n)[0u]; + let _e198 = (*n)[1u]; + e = ((vec2(1f) - abs(_e188.yx)) * vec2(select(-1f, 1f, (_e194 >= 0f)), select(-1f, 1f, (_e198 >= 0f)))); } - let _e202 = e; - return ((_e202 * 0.5f) + vec2(0.5f)); + let _e203 = e; + return ((_e203 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e167 = (*linear); - let _e169 = (*linear); - let _e173 = (*linear); - return mix((_e167 * 12.92f), ((pow(_e169, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e173)); + let _e168 = (*linear); + let _e170 = (*linear); + let _e174 = (*linear); + return mix((_e168 * 12.92f), ((pow(_e170, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e174)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -43139,57 +43960,81 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e170 = g_albedo; - param_1 = clamp(_e170, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e172 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e172.x, _e172.y, _e172.z, 1f); - let _e177 = g_debug_surface_on; - if _e177 { - let _e178 = g_debug_surface; - let _e179 = ViewDepth_u0028_(); - frag_surface = vec4(_e178.x, _e178.y, _e178.z, _e179); + let _e171 = g_albedo; + param_1 = clamp(_e171, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e173 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e173.x, _e173.y, _e173.z, 1f); + let _e178 = g_debug_surface_on; + if _e178 { + let _e179 = g_debug_surface; + let _e180 = ViewDepth_u0028_(); + frag_surface = vec4(_e179.x, _e179.y, _e179.z, _e180); return; } - let _e184 = v_normal_1; - geometric = normalize(_e184); - let _e186 = gl_FrontFacing_1; - if !(_e186) { - let _e188 = geometric; - geometric = -(_e188); - } - let _e190 = geometric; - param_2 = _e190; - let _e191 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e192 = (*roughness); - let _e194 = ViewDepth_u0028_(); - frag_surface = vec4(_e191.x, _e191.y, clamp(_e192, 0f, 1f), _e194); + let _e185 = v_normal_1; + geometric = normalize(_e185); + let _e187 = gl_FrontFacing_1; + if !(_e187) { + let _e189 = geometric; + geometric = -(_e189); + } + let _e191 = geometric; + param_2 = _e191; + let _e192 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e193 = (*roughness); + let _e195 = ViewDepth_u0028_(); + frag_surface = vec4(_e192.x, _e192.y, clamp(_e193, 0f, 1f), _e195); return; } fn EyeDistance_u0028_() -> f32 { - let _e166 = v_world_position_1; - let _e168 = fog_info.eye; - return distance(_e166, _e168.xyz); + let _e167 = v_world_position_1; + let _e169 = fog_info.eye; + return distance(_e167, _e169.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; + var falloff: f32; + var k: f32; + var local_1: f32; - let _e171 = fog_info.fog[3u]; - density = _e171; - let _e172 = density; - if (_e172 <= 0f) { - let _e174 = (*color); - return _e174; + let _e175 = fog_info.fog[3u]; + density = _e175; + let _e176 = density; + if (_e176 <= 0f) { + let _e178 = (*color); + return _e178; + } + let _e179 = EyeDistance_u0028_(); + d = _e179; + let _e182 = fog_info.eye[3u]; + falloff = _e182; + let _e183 = falloff; + if (_e183 > 0f) { + let _e185 = falloff; + let _e187 = v_world_position_1[1u]; + let _e190 = fog_info.eye[1u]; + k = (_e185 * (_e187 - _e190)); + let _e193 = k; + if (abs(_e193) > 0.001f) { + let _e196 = k; + let _e200 = k; + local_1 = ((1f - exp(-(_e196))) / _e200); + } else { + let _e202 = k; + local_1 = (1f - (0.5f * _e202)); + } + let _e205 = local_1; + let _e206 = density; + density = (_e206 * _e205); } - let _e175 = EyeDistance_u0028_(); - d = _e175; - let _e177 = fog_info.fog; - let _e179 = (*color); - let _e180 = density; - let _e182 = d; - return mix(_e177.xyz, _e179, vec3(clamp(exp((-(_e180) * _e182)), 0f, 1f))); + let _e209 = fog_info.fog; + let _e211 = (*color); + let _e212 = density; + let _e214 = d; + return mix(_e209.xyz, _e211, vec3(clamp(exp((-(_e212) * _e214)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -43197,55 +44042,55 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e172 = g_premultiply; - let _e173 = (*alpha_2); - weight_1 = select(1f, _e173, _e172); - let _e175 = (*linearColor); - param_3 = _e175; - let _e176 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e177 = weight_1; - let _e178 = (_e176 * _e177); - let _e179 = (*alpha_2); - frag_color = vec4(_e178.x, _e178.y, _e178.z, _e179); - let _e184 = (*roughness_1); - param_4 = _e184; + let _e173 = g_premultiply; + let _e174 = (*alpha_2); + weight_1 = select(1f, _e174, _e173); + let _e176 = (*linearColor); + param_3 = _e176; + let _e177 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e178 = weight_1; + let _e179 = (_e177 * _e178); + let _e180 = (*alpha_2); + frag_color = vec4(_e179.x, _e179.y, _e179.z, _e180); + let _e185 = (*roughness_1); + param_4 = _e185; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; } fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s: ptr, light: ptr) -> vec3 { - let _e169 = (*s).albedo; - return _e169; + let _e170 = (*s).albedo; + return _e170; } fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e167 = (*i); - if (_e167 == 0i) { + let _e168 = (*i); + if (_e168 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e169 = (*i); - if (_e169 == 1i) { + let _e170 = (*i); + if (_e170 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e171 = (*i); - if (_e171 == 2i) { + let _e172 = (*i); + if (_e172 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e173 = (*i); - if (_e173 == 3i) { + let _e174 = (*i); + if (_e174 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e175 = (*i); - if (_e175 == 4i) { + let _e176 = (*i); + if (_e176 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e177 = (*i); - if (_e177 == 5i) { + let _e178 = (*i); + if (_e178 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e179 = (*i); - if (_e179 == 6i) { + let _e180 = (*i); + if (_e180 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -43255,48 +44100,48 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr; var param_5: i32; - let _e172 = (*i_1); - param_5 = _e172; - let _e173 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); - p = _e173; - let _e175 = p[0u]; - let _e176 = (*ca); - let _e179 = p[1u]; - let _e180 = (*sa); - let _e184 = p[0u]; - let _e185 = (*sa); - let _e188 = p[1u]; - let _e189 = (*ca); - let _e193 = (*radius); - return (vec2(((_e175 * _e176) - (_e179 * _e180)), ((_e184 * _e185) + (_e188 * _e189))) * _e193); + let _e173 = (*i_1); + param_5 = _e173; + let _e174 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); + p = _e174; + let _e176 = p[0u]; + let _e177 = (*ca); + let _e180 = p[1u]; + let _e181 = (*sa); + let _e185 = p[0u]; + let _e186 = (*sa); + let _e189 = p[1u]; + let _e190 = (*ca); + let _e194 = (*radius); + return (vec2(((_e176 * _e177) - (_e180 * _e181)), ((_e185 * _e186) + (_e189 * _e190))) * _e194); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_1: vec2; + var local_2: vec2; var atlas: vec2; - let _e175 = point_shadow.params[0u]; - inset = _e175; - let _e176 = (*uv); - let _e177 = (*offset); - let _e179 = inset; + let _e176 = point_shadow.params[0u]; + inset = _e176; + let _e177 = (*uv); + let _e178 = (*offset); let _e180 = inset; - local_1 = clamp((_e176 + _e177), vec2(_e179), vec2((1f - _e180))); - let _e185 = local_1; - let _e186 = (*tile); - atlas = ((_e185 + _e186) * vec2(0.16666667f, 0.16666667f)); - let _e191 = point_shadow.params3_[0u]; - if (_e191 > 0.5f) { - let _e194 = atlas[1u]; - atlas[1u] = (1f - _e194); - } - let _e197 = atlas; - let _e198 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e197, 0f); - let _e200 = atlas; - let _e201 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e200, 0f); - let _e204 = (*range); - return (min(_e198.x, _e201.x) * _e204); + let _e181 = inset; + local_2 = clamp((_e177 + _e178), vec2(_e180), vec2((1f - _e181))); + let _e186 = local_2; + let _e187 = (*tile); + atlas = ((_e186 + _e187) * vec2(0.16666667f, 0.16666667f)); + let _e192 = point_shadow.params3_[0u]; + if (_e192 > 0.5f) { + let _e195 = atlas[1u]; + atlas[1u] = (1f - _e195); + } + let _e198 = atlas; + let _e199 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e198, 0f); + let _e201 = atlas; + let _e202 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e201, 0f); + let _e205 = (*range); + return (min(_e199.x, _e202.x) * _e205); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { @@ -43306,24 +44151,24 @@ fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: pt var param_8: vec2; var param_9: f32; - let _e176 = (*uv_1); - param_6 = _e176; - let _e177 = (*offset_1); - param_7 = _e177; - let _e178 = (*tile_1); - param_8 = _e178; - let _e179 = (*range_1); - param_9 = _e179; - let _e180 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); - stored = _e180; - let _e181 = stored; - let _e182 = (*range_1); - if (_e181 >= (_e182 * 0.999f)) { + let _e177 = (*uv_1); + param_6 = _e177; + let _e178 = (*offset_1); + param_7 = _e178; + let _e179 = (*tile_1); + param_8 = _e179; + let _e180 = (*range_1); + param_9 = _e180; + let _e181 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); + stored = _e181; + let _e182 = stored; + let _e183 = (*range_1); + if (_e182 >= (_e183 * 0.999f)) { return 1f; } - let _e185 = (*receiver); - let _e186 = stored; - return select(1f, 0f, (_e185 > _e186)); + let _e186 = (*receiver); + let _e187 = stored; + return select(1f, 0f, (_e186 > _e187)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { @@ -43350,111 +44195,111 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u var world: f32; (*blockerOut) = -1f; - let _e197 = point_shadow.params2_[1u]; - lightRadius = _e197; - let _e200 = point_shadow.params2_[0u]; - minRadius = _e200; - let _e203 = point_shadow.params2_[2u]; - maxRadius = _e203; - let _e204 = lightRadius; - if (_e204 <= 0f) { - let _e206 = (*uv_2); - param_10 = _e206; + let _e198 = point_shadow.params2_[1u]; + lightRadius = _e198; + let _e201 = point_shadow.params2_[0u]; + minRadius = _e201; + let _e204 = point_shadow.params2_[2u]; + maxRadius = _e204; + let _e205 = lightRadius; + if (_e205 <= 0f) { + let _e207 = (*uv_2); + param_10 = _e207; param_11 = vec2(0f, 0f); - let _e207 = (*tile_2); - param_12 = _e207; - let _e208 = (*range_2); - param_13 = _e208; - let _e209 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); - (*blockerOut) = _e209; - let _e210 = minRadius; - return _e210; + let _e208 = (*tile_2); + param_12 = _e208; + let _e209 = (*range_2); + param_13 = _e209; + let _e210 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); + (*blockerOut) = _e210; + let _e211 = minRadius; + return _e211; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e211 = i_2; - if (_e211 < 8i) { - let _e213 = i_2; - param_14 = _e213; - let _e214 = (*ca_1); - param_15 = _e214; - let _e215 = (*sa_1); - param_16 = _e215; - let _e216 = maxRadius; - param_17 = _e216; - let _e217 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); - let _e218 = (*uv_2); - param_18 = _e218; - param_19 = _e217; - let _e219 = (*tile_2); - param_20 = _e219; - let _e220 = (*range_2); - param_21 = _e220; - let _e221 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); - stored_1 = _e221; - let _e222 = stored_1; - let _e223 = (*range_2); - if (_e222 >= (_e223 * 0.999f)) { + let _e212 = i_2; + if (_e212 < 8i) { + let _e214 = i_2; + param_14 = _e214; + let _e215 = (*ca_1); + param_15 = _e215; + let _e216 = (*sa_1); + param_16 = _e216; + let _e217 = maxRadius; + param_17 = _e217; + let _e218 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); + let _e219 = (*uv_2); + param_18 = _e219; + param_19 = _e218; + let _e220 = (*tile_2); + param_20 = _e220; + let _e221 = (*range_2); + param_21 = _e221; + let _e222 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); + stored_1 = _e222; + let _e223 = stored_1; + let _e224 = (*range_2); + if (_e223 >= (_e224 * 0.999f)) { continue; } - let _e226 = stored_1; - let _e227 = (*receiver_1); - if (_e226 >= _e227) { + let _e227 = stored_1; + let _e228 = (*receiver_1); + if (_e227 >= _e228) { continue; } - let _e229 = stored_1; - let _e230 = sum; - sum = (_e230 + _e229); - let _e232 = count; - count = (_e232 + 1f); + let _e230 = stored_1; + let _e231 = sum; + sum = (_e231 + _e230); + let _e233 = count; + count = (_e233 + 1f); continue; } else { break; } continuing { - let _e234 = i_2; - i_2 = (_e234 + 1i); + let _e235 = i_2; + i_2 = (_e235 + 1i); } } - let _e236 = count; - if (_e236 < 0.5f) { + let _e237 = count; + if (_e237 < 0.5f) { return -1f; } - let _e238 = sum; - let _e239 = count; - blocker = max((_e238 / _e239), 0.0001f); - let _e242 = blocker; - (*blockerOut) = _e242; - let _e243 = lightRadius; - let _e244 = (*receiver_1); - let _e245 = blocker; - let _e249 = blocker; - world = ((_e243 * max((_e244 - _e245), 0f)) / _e249); - let _e251 = world; - let _e252 = (*receiver_1); - let _e254 = (*tanHalf); - let _e257 = minRadius; - let _e258 = maxRadius; - return clamp((_e251 / ((2f * _e252) * _e254)), _e257, _e258); + let _e239 = sum; + let _e240 = count; + blocker = max((_e239 / _e240), 0.0001f); + let _e243 = blocker; + (*blockerOut) = _e243; + let _e244 = lightRadius; + let _e245 = (*receiver_1); + let _e246 = blocker; + let _e250 = blocker; + world = ((_e244 * max((_e245 - _e246), 0f)) / _e250); + let _e252 = world; + let _e253 = (*receiver_1); + let _e255 = (*tanHalf); + let _e258 = minRadius; + let _e259 = maxRadius; + return clamp((_e252 / ((2f * _e253) * _e255)), _e258, _e259); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_2: vec2; + var local_3: vec2; - let _e168 = (*rows); - if (_e168 > 0f) { - let _e171 = gl_FragCoord_1[0u]; - let _e172 = (*rows); - let _e174 = gl_FragCoord_1[1u]; - local_2 = vec2(_e171, (_e172 - _e174)); + let _e169 = (*rows); + if (_e169 > 0f) { + let _e172 = gl_FragCoord_1[0u]; + let _e173 = (*rows); + let _e175 = gl_FragCoord_1[1u]; + local_3 = vec2(_e172, (_e173 - _e175)); } else { - let _e177 = gl_FragCoord_1; - local_2 = _e177.xy; + let _e178 = gl_FragCoord_1; + local_3 = _e178.xy; } - let _e179 = local_2; - return _e179; + let _e180 = local_3; + return _e180; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -43492,7 +44337,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_4: vec3; var lit: f32; var param_31: vec2; var param_32: vec2; @@ -43509,260 +44354,260 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_43: f32; var param_44: f32; - var phi_2339_: bool; - var phi_2400_: bool; - - let _e220 = (*lightIndex); - let _e224 = point_shadow.slots[_e220][0u]; - slot = i32((_e224 + 0.5f)); - let _e227 = (*lightIndex); - let _e231 = point_shadow.slots[_e227][0u]; - if (_e231 < 0f) { + var phi_2374_: bool; + var phi_2435_: bool; + + let _e221 = (*lightIndex); + let _e225 = point_shadow.slots[_e221][0u]; + slot = i32((_e225 + 0.5f)); + let _e228 = (*lightIndex); + let _e232 = point_shadow.slots[_e228][0u]; + if (_e232 < 0f) { return 1f; } - let _e235 = point_shadow.params[2u]; - strength = _e235; - let _e236 = strength; - if (_e236 <= 0f) { + let _e236 = point_shadow.params[2u]; + strength = _e236; + let _e237 = strength; + if (_e237 <= 0f) { return 1f; } - let _e238 = slot; - let _e241 = point_shadow.lights[_e238]; - let _e243 = (*world_1); - toLight = (_e241.xyz - _e243); - let _e245 = toLight; - toLightLength = max(length(_e245), 0.000001f); - let _e248 = (*normal); - let _e249 = toLight; - let _e250 = toLightLength; - nDotL = max(dot(_e248, (_e249 / vec3(_e250))), 0.15f); - let _e255 = nDotL; + let _e239 = slot; + let _e242 = point_shadow.lights[_e239]; + let _e244 = (*world_1); + toLight = (_e242.xyz - _e244); + let _e246 = toLight; + toLightLength = max(length(_e246), 0.000001f); + let _e249 = (*normal); + let _e250 = toLight; + let _e251 = toLightLength; + nDotL = max(dot(_e249, (_e250 / vec3(_e251))), 0.15f); let _e256 = nDotL; - let _e261 = nDotL; + let _e257 = nDotL; let _e262 = nDotL; - slope = min((sqrt(max((1f - (_e255 * _e256)), 0f)) / (_e261 * _e262)), 8f); - let _e266 = toLightLength; - let _e268 = (*lightIndex); - let _e272 = point_shadow.slots[_e268][2u]; - let _e277 = point_shadow.params3_[1u]; - texel = (((2f * _e266) * max(_e272, 0.0001f)) * _e277); - let _e279 = (*world_1); - let _e280 = (*normal); - let _e281 = texel; - let _e285 = point_shadow.params[3u]; - let _e287 = slope; - origin = (_e279 + (((_e280 * _e281) * _e285) * (1f + _e287))); - let _e291 = origin; - let _e292 = slot; - let _e295 = point_shadow.lights[_e292]; - toFragment = (_e291 - _e295.xyz); - let _e298 = toFragment; - distance_ = length(_e298); - let _e300 = slot; - let _e304 = point_shadow.lights[_e300][3u]; - range_3 = max(_e304, 0.0001f); - let _e306 = distance_; - let _e307 = range_3; - if (_e306 >= _e307) { + let _e263 = nDotL; + slope = min((sqrt(max((1f - (_e256 * _e257)), 0f)) / (_e262 * _e263)), 8f); + let _e267 = toLightLength; + let _e269 = (*lightIndex); + let _e273 = point_shadow.slots[_e269][2u]; + let _e278 = point_shadow.params3_[1u]; + texel = (((2f * _e267) * max(_e273, 0.0001f)) * _e278); + let _e280 = (*world_1); + let _e281 = (*normal); + let _e282 = texel; + let _e286 = point_shadow.params[3u]; + let _e288 = slope; + origin = (_e280 + (((_e281 * _e282) * _e286) * (1f + _e288))); + let _e292 = origin; + let _e293 = slot; + let _e296 = point_shadow.lights[_e293]; + toFragment = (_e292 - _e296.xyz); + let _e299 = toFragment; + distance_ = length(_e299); + let _e301 = slot; + let _e305 = point_shadow.lights[_e301][3u]; + range_3 = max(_e305, 0.0001f); + let _e307 = distance_; + let _e308 = range_3; + if (_e307 >= _e308) { return 1f; } face = 0i; - let _e309 = (*lightIndex); - let _e313 = point_shadow.slots[_e309][1u]; - if (_e313 < 0.5f) { - let _e315 = toFragment; - a = abs(_e315); - let _e318 = a[0u]; - let _e320 = a[1u]; - let _e321 = (_e318 >= _e320); - phi_2339_ = _e321; - if _e321 { - let _e323 = a[0u]; - let _e325 = a[2u]; - phi_2339_ = (_e323 >= _e325); - } - let _e328 = phi_2339_; - if _e328 { - let _e330 = toFragment[0u]; - face = select(1i, 0i, (_e330 > 0f)); + let _e310 = (*lightIndex); + let _e314 = point_shadow.slots[_e310][1u]; + if (_e314 < 0.5f) { + let _e316 = toFragment; + a = abs(_e316); + let _e319 = a[0u]; + let _e321 = a[1u]; + let _e322 = (_e319 >= _e321); + phi_2374_ = _e322; + if _e322 { + let _e324 = a[0u]; + let _e326 = a[2u]; + phi_2374_ = (_e324 >= _e326); + } + let _e329 = phi_2374_; + if _e329 { + let _e331 = toFragment[0u]; + face = select(1i, 0i, (_e331 > 0f)); } else { - let _e334 = a[1u]; - let _e336 = a[2u]; - if (_e334 >= _e336) { - let _e339 = toFragment[1u]; - face = select(3i, 2i, (_e339 > 0f)); + let _e335 = a[1u]; + let _e337 = a[2u]; + if (_e335 >= _e337) { + let _e340 = toFragment[1u]; + face = select(3i, 2i, (_e340 > 0f)); } else { - let _e343 = toFragment[2u]; - face = select(5i, 4i, (_e343 > 0f)); + let _e344 = toFragment[2u]; + face = select(5i, 4i, (_e344 > 0f)); } } } - let _e346 = slot; - let _e348 = face; - let _e352 = point_shadow.faces[((_e346 * 6i) + _e348)]; - let _e353 = origin; - clip = (_e352 * vec4(_e353.x, _e353.y, _e353.z, 1f)); - let _e360 = clip[3u]; - if (_e360 <= 0f) { + let _e347 = slot; + let _e349 = face; + let _e353 = point_shadow.faces[((_e347 * 6i) + _e349)]; + let _e354 = origin; + clip = (_e353 * vec4(_e354.x, _e354.y, _e354.z, 1f)); + let _e361 = clip[3u]; + if (_e361 <= 0f) { return 1f; } - let _e362 = clip; - let _e365 = clip[3u]; - ndc = (_e362.xy / vec2(_e365)); - let _e369 = ndc[0u]; - let _e371 = (abs(_e369) > 1f); - phi_2400_ = _e371; - if !(_e371) { - let _e374 = ndc[1u]; - phi_2400_ = (abs(_e374) > 1f); - } - let _e378 = phi_2400_; - if _e378 { + let _e363 = clip; + let _e366 = clip[3u]; + ndc = (_e363.xy / vec2(_e366)); + let _e370 = ndc[0u]; + let _e372 = (abs(_e370) > 1f); + phi_2435_ = _e372; + if !(_e372) { + let _e375 = ndc[1u]; + phi_2435_ = (abs(_e375) > 1f); + } + let _e379 = phi_2435_; + if _e379 { return 1f; } - let _e380 = ndc[0u]; - let _e384 = ndc[1u]; - uv_3 = vec2(((_e380 * 0.5f) + 0.5f), (0.5f - (_e384 * 0.5f))); - let _e388 = face; - let _e390 = slot; - tile_3 = vec2(f32(_e388), f32(_e390)); - let _e393 = distance_; - let _e396 = point_shadow.params[1u]; - receiver_2 = (_e393 - _e396); - let _e400 = frag_info.target_origin[0u]; - param_22 = _e400; - let _e401 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); - noise = fract((52.982918f * fract(dot(_e401, vec2(0.06711056f, 0.00583715f))))); - let _e406 = noise; - angle = (_e406 * 6.2831855f); - let _e408 = angle; - ca_2 = cos(_e408); - let _e410 = angle; - sa_2 = sin(_e410); - let _e412 = (*lightIndex); - let _e416 = point_shadow.slots[_e412][2u]; - tanHalf_1 = max(_e416, 0.0001f); + let _e381 = ndc[0u]; + let _e385 = ndc[1u]; + uv_3 = vec2(((_e381 * 0.5f) + 0.5f), (0.5f - (_e385 * 0.5f))); + let _e389 = face; + let _e391 = slot; + tile_3 = vec2(f32(_e389), f32(_e391)); + let _e394 = distance_; + let _e397 = point_shadow.params[1u]; + receiver_2 = (_e394 - _e397); + let _e401 = frag_info.target_origin[0u]; + param_22 = _e401; + let _e402 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); + noise = fract((52.982918f * fract(dot(_e402, vec2(0.06711056f, 0.00583715f))))); + let _e407 = noise; + angle = (_e407 * 6.2831855f); + let _e409 = angle; + ca_2 = cos(_e409); + let _e411 = angle; + sa_2 = sin(_e411); + let _e413 = (*lightIndex); + let _e417 = point_shadow.slots[_e413][2u]; + tanHalf_1 = max(_e417, 0.0001f); blocker_1 = -1f; - let _e418 = uv_3; - param_23 = _e418; - let _e419 = tile_3; - param_24 = _e419; - let _e420 = range_3; - param_25 = _e420; - let _e421 = receiver_2; - param_26 = _e421; - let _e422 = ca_2; - param_27 = _e422; - let _e423 = sa_2; - param_28 = _e423; - let _e424 = tanHalf_1; - param_29 = _e424; - let _e425 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e426 = param_30; - blocker_1 = _e426; - radius_1 = _e425; - let _e429 = point_shadow.params2_[3u]; - if (_e429 > 0.5f) { + let _e419 = uv_3; + param_23 = _e419; + let _e420 = tile_3; + param_24 = _e420; + let _e421 = range_3; + param_25 = _e421; + let _e422 = receiver_2; + param_26 = _e422; + let _e423 = ca_2; + param_27 = _e423; + let _e424 = sa_2; + param_28 = _e424; + let _e425 = tanHalf_1; + param_29 = _e425; + let _e426 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); + let _e427 = param_30; + blocker_1 = _e427; + radius_1 = _e426; + let _e430 = point_shadow.params2_[3u]; + if (_e430 > 0.5f) { g_debug_surface_on = true; - let _e431 = radius_1; - if (_e431 < 0f) { - local_3 = vec3(0f, 0f, 1f); + let _e432 = radius_1; + if (_e432 < 0f) { + local_4 = vec3(0f, 0f, 1f); } else { - let _e433 = radius_1; - let _e436 = point_shadow.params2_[2u]; - let _e440 = blocker_1; - let _e441 = range_3; - local_3 = vec3(clamp((_e433 / max(_e436, 0.000001f)), 0f, 1f), clamp((_e440 / _e441), 0f, 1f), 0f); + let _e434 = radius_1; + let _e437 = point_shadow.params2_[2u]; + let _e441 = blocker_1; + let _e442 = range_3; + local_4 = vec3(clamp((_e434 / max(_e437, 0.000001f)), 0f, 1f), clamp((_e441 / _e442), 0f, 1f), 0f); } - let _e445 = local_3; - g_debug_surface = _e445; + let _e446 = local_4; + g_debug_surface = _e446; } - let _e446 = radius_1; - if (_e446 < 0f) { + let _e447 = radius_1; + if (_e447 < 0f) { return 1f; } - let _e448 = uv_3; - param_31 = _e448; + let _e449 = uv_3; + param_31 = _e449; param_32 = vec2(0f, 0f); - let _e449 = tile_3; - param_33 = _e449; - let _e450 = range_3; - param_34 = _e450; - let _e451 = receiver_2; - param_35 = _e451; - let _e452 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); - lit = _e452; - let _e453 = radius_1; - if (_e453 > 0f) { + let _e450 = tile_3; + param_33 = _e450; + let _e451 = range_3; + param_34 = _e451; + let _e452 = receiver_2; + param_35 = _e452; + let _e453 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); + lit = _e453; + let _e454 = radius_1; + if (_e454 > 0f) { i_3 = 0i; loop { - let _e455 = i_3; - if (_e455 < 8i) { - let _e457 = i_3; - param_36 = _e457; - let _e458 = ca_2; - param_37 = _e458; - let _e459 = sa_2; - param_38 = _e459; - let _e460 = radius_1; - param_39 = _e460; - let _e461 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - let _e462 = uv_3; - param_40 = _e462; - param_41 = _e461; - let _e463 = tile_3; - param_42 = _e463; - let _e464 = range_3; - param_43 = _e464; - let _e465 = receiver_2; - param_44 = _e465; - let _e466 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); - let _e467 = lit; - lit = (_e467 + _e466); + let _e456 = i_3; + if (_e456 < 8i) { + let _e458 = i_3; + param_36 = _e458; + let _e459 = ca_2; + param_37 = _e459; + let _e460 = sa_2; + param_38 = _e460; + let _e461 = radius_1; + param_39 = _e461; + let _e462 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); + let _e463 = uv_3; + param_40 = _e463; + param_41 = _e462; + let _e464 = tile_3; + param_42 = _e464; + let _e465 = range_3; + param_43 = _e465; + let _e466 = receiver_2; + param_44 = _e466; + let _e467 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); + let _e468 = lit; + lit = (_e468 + _e467); continue; } else { break; } continuing { - let _e469 = i_3; - i_3 = (_e469 + 1i); + let _e470 = i_3; + i_3 = (_e470 + 1i); } } - let _e471 = lit; - lit = (_e471 * 0.11111111f); + let _e472 = lit; + lit = (_e472 * 0.11111111f); } - let _e473 = lit; - let _e474 = strength; - return mix(1f, _e473, clamp(_e474, 0f, 1f)); + let _e474 = lit; + let _e475 = strength; + return mix(1f, _e474, clamp(_e475, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r: f32; var theta: f32; - let _e171 = (*i_4); - let _e174 = (*n_1); - r = sqrt(((f32(_e171) + 0.5f) / f32(_e174))); - let _e178 = (*i_4); - let _e181 = (*turn); - theta = ((f32(_e178) * 2.3999631f) + _e181); - let _e183 = r; - let _e184 = theta; - let _e186 = theta; - return (vec2(cos(_e184), sin(_e186)) * _e183); + let _e172 = (*i_4); + let _e175 = (*n_1); + r = sqrt(((f32(_e172) + 0.5f) / f32(_e175))); + let _e179 = (*i_4); + let _e182 = (*turn); + theta = ((f32(_e179) * 2.3999631f) + _e182); + let _e184 = r; + let _e185 = theta; + let _e187 = theta; + return (vec2(cos(_e185), sin(_e187)) * _e184); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; var param_45: f32; - let _e170 = frag_info.target_origin[0u]; - param_45 = _e170; - let _e171 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); - let _e174 = frag_info.target_origin[3u]; - at = (_e171 + vec2((5.588238f * max(_e174, 0f)))); - let _e179 = at; - return fract((52.982918f * fract(dot(_e179, vec2(0.06711056f, 0.00583715f))))); + let _e171 = frag_info.target_origin[0u]; + param_45 = _e171; + let _e172 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); + let _e175 = frag_info.target_origin[3u]; + at = (_e172 + vec2((5.588238f * max(_e175, 0f)))); + let _e180 = at; + return fract((52.982918f * fract(dot(_e180, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -43771,37 +44616,37 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_2: f32; - let _e168 = (*depth); - d_2 = ((2f * clamp(_e168, 0f, 1f)) - 1f); - let _e172 = d_2; - let _e175 = d_2; - return vec2(exp((40f * _e172)), -(exp((-5f * _e175)))); + let _e169 = (*depth); + d_2 = ((2f * clamp(_e169, 0f, 1f)) - 1f); + let _e173 = d_2; + let _e176 = d_2; + return vec2(exp((40f * _e173)), -(exp((-5f * _e176)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { @@ -43819,37 +44664,37 @@ fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr(0.004f, 0.0005f) * _e184); - let _e187 = scale[0u]; - let _e189 = scale[0u]; - let _e191 = (*moments_1); - param_47 = _e191.xy; - let _e194 = warped[0u]; - param_48 = _e194; - param_49 = (_e187 * _e189); - let _e195 = (*bleed_1); - param_50 = _e195; - let _e196 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); - positive = _e196; - let _e198 = scale[1u]; - let _e200 = scale[1u]; - let _e202 = (*moments_1); - param_51 = _e202.zw; - let _e205 = warped[1u]; - param_52 = _e205; - param_53 = (_e198 * _e200); - let _e206 = (*bleed_1); - param_54 = _e206; - let _e207 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); - negative = _e207; - let _e208 = positive; - let _e209 = negative; - return min(_e208, _e209); + let _e183 = (*depth_1); + param_46 = _e183; + let _e184 = EvsmWarp_u0028_f1_u003b((¶m_46)); + warped = _e184; + let _e185 = warped; + scale = (vec2(0.004f, 0.0005f) * _e185); + let _e188 = scale[0u]; + let _e190 = scale[0u]; + let _e192 = (*moments_1); + param_47 = _e192.xy; + let _e195 = warped[0u]; + param_48 = _e195; + param_49 = (_e188 * _e190); + let _e196 = (*bleed_1); + param_50 = _e196; + let _e197 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); + positive = _e197; + let _e199 = scale[1u]; + let _e201 = scale[1u]; + let _e203 = (*moments_1); + param_51 = _e203.zw; + let _e206 = warped[1u]; + param_52 = _e206; + param_53 = (_e199 * _e201); + let _e207 = (*bleed_1); + param_54 = _e207; + let _e208 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); + negative = _e208; + let _e209 = positive; + let _e210 = negative; + return min(_e209, _e210); } fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_1: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { @@ -43866,8 +44711,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_4: mat4x4; var local_5: mat4x4; + var local_6: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -43881,8 +44726,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_6: f32; var local_7: f32; + var local_8: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -43916,51 +44761,51 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_63: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_2719_: bool; - var phi_2730_: bool; - var phi_2891_: bool; - var phi_2898_: bool; - var phi_2905_: bool; - - let _e234 = frag_info.shadow_params[3u]; - strength_1 = _e234; - let _e235 = strength_1; - if (_e235 <= 0f) { + var phi_2754_: bool; + var phi_2765_: bool; + var phi_2926_: bool; + var phi_2933_: bool; + var phi_2940_: bool; + + let _e235 = frag_info.shadow_params[3u]; + strength_1 = _e235; + let _e236 = strength_1; + if (_e236 <= 0f) { return 1f; } - let _e237 = (*lightIndex_1); - let _e240 = frag_info.frame_params[2u]; - if (_e237 != i32((_e240 + 0.5f))) { + let _e238 = (*lightIndex_1); + let _e241 = frag_info.frame_params[2u]; + if (_e238 != i32((_e241 + 0.5f))) { return 1f; } - let _e246 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e246 + 0.5f)); - let _e249 = v_world_position_1; - let _e251 = frag_info.camera_position; - viewDistance = length((_e249 - _e251.xyz)); + let _e247 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e247 + 0.5f)); + let _e250 = v_world_position_1; + let _e252 = frag_info.camera_position; + viewDistance = length((_e250 - _e252.xyz)); cascade = 0i; - let _e255 = cascadeCount; - let _e256 = (_e255 > 1i); - phi_2719_ = _e256; - if _e256 { - let _e257 = viewDistance; - let _e260 = frag_info.shadow_cascades[0u]; - phi_2719_ = (_e257 > _e260); - } - let _e263 = phi_2719_; - if _e263 { + let _e256 = cascadeCount; + let _e257 = (_e256 > 1i); + phi_2754_ = _e257; + if _e257 { + let _e258 = viewDistance; + let _e261 = frag_info.shadow_cascades[0u]; + phi_2754_ = (_e258 > _e261); + } + let _e264 = phi_2754_; + if _e264 { cascade = 1i; } - let _e264 = cascadeCount; - let _e265 = (_e264 > 2i); - phi_2730_ = _e265; - if _e265 { - let _e266 = viewDistance; - let _e269 = frag_info.shadow_cascades[1u]; - phi_2730_ = (_e266 > _e269); + let _e265 = cascadeCount; + let _e266 = (_e265 > 2i); + phi_2765_ = _e266; + if _e266 { + let _e267 = viewDistance; + let _e270 = frag_info.shadow_cascades[1u]; + phi_2765_ = (_e267 > _e270); } - let _e272 = phi_2730_; - if _e272 { + let _e273 = phi_2765_; + if _e273 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -43971,232 +44816,232 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e273 = attempt; - if (_e273 < 3i) { - let _e275 = cascade; - let _e276 = attempt; - which = (_e275 + _e276); - let _e278 = which; - let _e279 = cascadeCount; - if (_e278 >= _e279) { + let _e274 = attempt; + if (_e274 < 3i) { + let _e276 = cascade; + let _e277 = attempt; + which = (_e276 + _e277); + let _e279 = which; + let _e280 = cascadeCount; + if (_e279 >= _e280) { break; } - let _e281 = which; - if (_e281 == 0i) { - let _e284 = frag_info.shadow_matrix; - local_4 = _e284; + let _e282 = which; + if (_e282 == 0i) { + let _e285 = frag_info.shadow_matrix; + local_5 = _e285; } else { - let _e285 = which; - if (_e285 == 1i) { - let _e288 = frag_info.shadow_matrix_far; - local_5 = _e288; + let _e286 = which; + if (_e286 == 1i) { + let _e289 = frag_info.shadow_matrix_far; + local_6 = _e289; } else { - let _e290 = frag_info.shadow_matrix_farthest; - local_5 = _e290; + let _e291 = frag_info.shadow_matrix_farthest; + local_6 = _e291; } - let _e291 = local_5; - local_4 = _e291; - } - let _e292 = local_4; - matrix = _e292; - let _e295 = matrix[0][0u]; - let _e298 = matrix[1][0u]; - let _e301 = matrix[2][0u]; - axisX = vec3(_e295, _e298, _e301); - let _e305 = matrix[0][1u]; - let _e308 = matrix[1][1u]; - let _e311 = matrix[2][1u]; - axisY = vec3(_e305, _e308, _e311); - let _e313 = axisX; - rowX = max(length(_e313), 0.000001f); - let _e316 = axisY; - rowY = max(length(_e316), 0.000001f); - let _e321 = frag_info.shadow_cascades[3u]; - let _e323 = rowX; - texelMetres = ((2f * _e321) / _e323); - let _e327 = frag_info.shadow_cascades[3u]; - let _e330 = (*s_1).n; - let _e331 = axisX; - let _e334 = rowX; + let _e292 = local_6; + local_5 = _e292; + } + let _e293 = local_5; + matrix = _e293; + let _e296 = matrix[0][0u]; + let _e299 = matrix[1][0u]; + let _e302 = matrix[2][0u]; + axisX = vec3(_e296, _e299, _e302); + let _e306 = matrix[0][1u]; + let _e309 = matrix[1][1u]; + let _e312 = matrix[2][1u]; + axisY = vec3(_e306, _e309, _e312); + let _e314 = axisX; + rowX = max(length(_e314), 0.000001f); + let _e317 = axisY; + rowY = max(length(_e317), 0.000001f); + let _e322 = frag_info.shadow_cascades[3u]; + let _e324 = rowX; + texelMetres = ((2f * _e322) / _e324); + let _e328 = frag_info.shadow_cascades[3u]; + let _e331 = (*s_1).n; + let _e332 = axisX; let _e335 = rowX; - let _e339 = (*s_1).n; - let _e340 = axisY; - let _e343 = rowY; + let _e336 = rowX; + let _e340 = (*s_1).n; + let _e341 = axisY; let _e344 = rowY; - reach = ((3f * _e327) * ((abs(dot(_e330, _e331)) / (_e334 * _e335)) + (abs(dot(_e339, _e340)) / (_e343 * _e344)))); - let _e351 = frag_info.shadow_params[2u]; - let _e352 = texelMetres; - flatOffset = min(_e351, _e352); - let _e354 = v_world_position_1; - let _e356 = (*s_1).n; - let _e357 = flatOffset; - let _e358 = reach; - origin_1 = (_e354 + (_e356 * (_e357 + _e358))); - let _e362 = matrix; - let _e363 = origin_1; - lightSpace = (_e362 * vec4(_e363.x, _e363.y, _e363.z, 1f)); - let _e370 = lightSpace[3u]; - if (_e370 <= 0f) { + let _e345 = rowY; + reach = ((3f * _e328) * ((abs(dot(_e331, _e332)) / (_e335 * _e336)) + (abs(dot(_e340, _e341)) / (_e344 * _e345)))); + let _e352 = frag_info.shadow_params[2u]; + let _e353 = texelMetres; + flatOffset = min(_e352, _e353); + let _e355 = v_world_position_1; + let _e357 = (*s_1).n; + let _e358 = flatOffset; + let _e359 = reach; + origin_1 = (_e355 + (_e357 * (_e358 + _e359))); + let _e363 = matrix; + let _e364 = origin_1; + lightSpace = (_e363 * vec4(_e364.x, _e364.y, _e364.z, 1f)); + let _e371 = lightSpace[3u]; + if (_e371 <= 0f) { continue; } - let _e372 = lightSpace; - let _e375 = lightSpace[3u]; - candidate = (_e372.xyz / vec3(_e375)); - let _e379 = candidate[0u]; - let _e383 = candidate[1u]; - inTile = vec2(((_e379 * 0.5f) + 0.5f), (0.5f - (_e383 * 0.5f))); - let _e388 = inTile[0u]; - let _e389 = (_e388 < 0f); - phi_2891_ = _e389; - if !(_e389) { - let _e392 = inTile[0u]; - phi_2891_ = (_e392 > 1f); - } - let _e395 = phi_2891_; - phi_2898_ = _e395; - if !(_e395) { - let _e398 = inTile[1u]; - phi_2898_ = (_e398 < 0f); - } - let _e401 = phi_2898_; - phi_2905_ = _e401; - if !(_e401) { - let _e404 = inTile[1u]; - phi_2905_ = (_e404 > 1f); - } - let _e407 = phi_2905_; - if _e407 { + let _e373 = lightSpace; + let _e376 = lightSpace[3u]; + candidate = (_e373.xyz / vec3(_e376)); + let _e380 = candidate[0u]; + let _e384 = candidate[1u]; + inTile = vec2(((_e380 * 0.5f) + 0.5f), (0.5f - (_e384 * 0.5f))); + let _e389 = inTile[0u]; + let _e390 = (_e389 < 0f); + phi_2926_ = _e390; + if !(_e390) { + let _e393 = inTile[0u]; + phi_2926_ = (_e393 > 1f); + } + let _e396 = phi_2926_; + phi_2933_ = _e396; + if !(_e396) { + let _e399 = inTile[1u]; + phi_2933_ = (_e399 < 0f); + } + let _e402 = phi_2933_; + phi_2940_ = _e402; + if !(_e402) { + let _e405 = inTile[1u]; + phi_2940_ = (_e405 > 1f); + } + let _e408 = phi_2940_; + if _e408 { continue; } - let _e409 = candidate[2u]; - if (_e409 > 1f) { - let _e411 = which; - let _e412 = cascadeCount; - if (_e411 < (_e412 - 1i)) { + let _e410 = candidate[2u]; + if (_e410 > 1f) { + let _e412 = which; + let _e413 = cascadeCount; + if (_e412 < (_e413 - 1i)) { continue; } candidate[2u] = 1f; } - let _e417 = inTile[0u]; - let _e418 = which; - let _e421 = cascadeCount; - let _e425 = inTile[1u]; - uv_4 = vec2(((_e417 + f32(_e418)) / f32(_e421)), _e425); - let _e427 = candidate; - projected = _e427; - let _e428 = which; - cascade = _e428; - let _e429 = texelMetres; - cascadeTexel = _e429; - let _e432 = matrix[0][2u]; - let _e435 = matrix[1][2u]; - let _e438 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e432, _e435, _e438)), 0.000001f)); - let _e444 = (*s_1).n; - let _e447 = matrix[0][2u]; - let _e450 = matrix[1][2u]; - let _e453 = matrix[2][2u]; - let _e457 = cascadeDepth; - cosSlope = clamp((abs(dot(_e444, vec3(_e447, _e450, _e453))) * _e457), 0.1f, 1f); - let _e460 = texelMetres; - let _e461 = cosSlope; + let _e418 = inTile[0u]; + let _e419 = which; + let _e422 = cascadeCount; + let _e426 = inTile[1u]; + uv_4 = vec2(((_e418 + f32(_e419)) / f32(_e422)), _e426); + let _e428 = candidate; + projected = _e428; + let _e429 = which; + cascade = _e429; + let _e430 = texelMetres; + cascadeTexel = _e430; + let _e433 = matrix[0][2u]; + let _e436 = matrix[1][2u]; + let _e439 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e433, _e436, _e439)), 0.000001f)); + let _e445 = (*s_1).n; + let _e448 = matrix[0][2u]; + let _e451 = matrix[1][2u]; + let _e454 = matrix[2][2u]; + let _e458 = cascadeDepth; + cosSlope = clamp((abs(dot(_e445, vec3(_e448, _e451, _e454))) * _e458), 0.1f, 1f); + let _e461 = texelMetres; let _e462 = cosSlope; - let _e468 = cosSlope; - let _e470 = cascadeDepth; - receiverSlope = (((_e460 * sqrt(max((1f - (_e461 * _e462)), 0f))) / _e468) / _e470); + let _e463 = cosSlope; + let _e469 = cosSlope; + let _e471 = cascadeDepth; + receiverSlope = (((_e461 * sqrt(max((1f - (_e462 * _e463)), 0f))) / _e469) / _e471); found = true; break; } else { break; } continuing { - let _e472 = attempt; - attempt = (_e472 + 1i); + let _e473 = attempt; + attempt = (_e473 + 1i); } } - let _e474 = found; - if !(_e474) { + let _e475 = found; + if !(_e475) { return 1f; } - let _e476 = cascade; - if (_e476 == 0i) { - let _e480 = frag_info.shadow_bias[0u]; - local_6 = _e480; + let _e477 = cascade; + if (_e477 == 0i) { + let _e481 = frag_info.shadow_bias[0u]; + local_7 = _e481; } else { - let _e481 = cascade; - if (_e481 == 1i) { - let _e485 = frag_info.shadow_bias[1u]; - local_7 = _e485; + let _e482 = cascade; + if (_e482 == 1i) { + let _e486 = frag_info.shadow_bias[1u]; + local_8 = _e486; } else { - let _e488 = frag_info.shadow_bias[2u]; - local_7 = _e488; - } - let _e489 = local_7; - local_6 = _e489; - } - let _e490 = local_6; - bias = _e490; - let _e493 = frag_info.shadow_params[0u]; - let _e496 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e493, _e496); - let _e498 = cascade; - let _e500 = cascadeCount; - let _e504 = texel_1; - tileLo = (vec2((f32(_e498) / f32(_e500)), 0f) + (_e504 * 0.5f)); - let _e507 = cascade; - let _e510 = cascadeCount; - let _e514 = texel_1; - tileHi = (vec2((f32((_e507 + 1i)) / f32(_e510)), 1f) - (_e514 * 0.5f)); - let _e519 = frag_info.ambient_ground[3u]; - softness = _e519; + let _e489 = frag_info.shadow_bias[2u]; + local_8 = _e489; + } + let _e490 = local_8; + local_7 = _e490; + } + let _e491 = local_7; + bias = _e491; + let _e494 = frag_info.shadow_params[0u]; + let _e497 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e494, _e497); + let _e499 = cascade; + let _e501 = cascadeCount; + let _e505 = texel_1; + tileLo = (vec2((f32(_e499) / f32(_e501)), 0f) + (_e505 * 0.5f)); + let _e508 = cascade; + let _e511 = cascadeCount; + let _e515 = texel_1; + tileHi = (vec2((f32((_e508 + 1i)) / f32(_e511)), 1f) - (_e515 * 0.5f)); + let _e520 = frag_info.ambient_ground[3u]; + softness = _e520; lit_1 = 0f; - let _e520 = softness; - if (_e520 < 0f) { - let _e522 = uv_4; - let _e523 = tileLo; - let _e524 = tileHi; - let _e526 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e522, _e523, _e524), 0f); - moments_2 = _e526; - let _e528 = projected[2u]; - let _e529 = bias; - let _e531 = softness; - let _e535 = moments_2; - param_55 = _e535; - param_56 = (_e528 - _e529); - param_57 = clamp((-(_e531) - 1f), 0f, 0.95f); - let _e536 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); - lit_1 = _e536; + let _e521 = softness; + if (_e521 < 0f) { + let _e523 = uv_4; + let _e524 = tileLo; + let _e525 = tileHi; + let _e527 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e523, _e524, _e525), 0f); + moments_2 = _e527; + let _e529 = projected[2u]; + let _e530 = bias; + let _e532 = softness; + let _e536 = moments_2; + param_55 = _e536; + param_56 = (_e529 - _e530); + param_57 = clamp((-(_e532) - 1f), 0f, 0.95f); + let _e537 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); + lit_1 = _e537; } else { - let _e537 = softness; - if (_e537 <= 0f) { + let _e538 = softness; + if (_e538 <= 0f) { y = -1i; loop { - let _e539 = y; - if (_e539 <= 1i) { + let _e540 = y; + if (_e540 <= 1i) { x = -1i; loop { - let _e541 = x; - if (_e541 <= 1i) { - let _e543 = uv_4; - let _e544 = x; - let _e546 = y; - let _e549 = texel_1; - let _e552 = tileLo; - let _e553 = tileHi; - let _e555 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e543 + (vec2(f32(_e544), f32(_e546)) * _e549)), _e552, _e553), 0f); - occluder = _e555.x; - let _e558 = projected[2u]; - let _e559 = bias; - let _e561 = occluder; - let _e564 = lit_1; - lit_1 = (_e564 + select(1f, 0f, ((_e558 - _e559) > _e561))); + let _e542 = x; + if (_e542 <= 1i) { + let _e544 = uv_4; + let _e545 = x; + let _e547 = y; + let _e550 = texel_1; + let _e553 = tileLo; + let _e554 = tileHi; + let _e556 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e544 + (vec2(f32(_e545), f32(_e547)) * _e550)), _e553, _e554), 0f); + occluder = _e556.x; + let _e559 = projected[2u]; + let _e560 = bias; + let _e562 = occluder; + let _e565 = lit_1; + lit_1 = (_e565 + select(1f, 0f, ((_e559 - _e560) > _e562))); continue; } else { break; } continuing { - let _e566 = x; - x = (_e566 + 1i); + let _e567 = x; + x = (_e567 + 1i); } } continue; @@ -44204,125 +45049,125 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e568 = y; - y = (_e568 + 1i); + let _e569 = y; + y = (_e569 + 1i); } } - let _e570 = lit_1; - lit_1 = (_e570 * 0.11111111f); + let _e571 = lit_1; + lit_1 = (_e571 * 0.11111111f); } else { - let _e572 = softness; - spread = tan(min(_e572, 0.5f)); - let _e575 = ShadowNoise_u0028_(); - turn_1 = (_e575 * 6.2831855f); - let _e577 = spread; - let _e579 = projected[2u]; - let _e581 = cascadeDepth; - let _e583 = cascadeTexel; - searchRadius = clamp((((_e577 * _e579) * _e581) / _e583), 1f, 16f); + let _e573 = softness; + spread = tan(min(_e573, 0.5f)); + let _e576 = ShadowNoise_u0028_(); + turn_1 = (_e576 * 6.2831855f); + let _e578 = spread; + let _e580 = projected[2u]; + let _e582 = cascadeDepth; + let _e584 = cascadeTexel; + searchRadius = clamp((((_e578 * _e580) * _e582) / _e584), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e586 = i_5; - if (_e586 < 16i) { - let _e588 = i_5; - param_58 = _e588; + let _e587 = i_5; + if (_e587 < 16i) { + let _e589 = i_5; + param_58 = _e589; param_59 = 16i; - let _e589 = turn_1; - param_60 = _e589; - let _e590 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); - disc = _e590; - let _e591 = bias; - let _e592 = disc; - let _e594 = searchRadius; - let _e598 = receiverSlope; - tapBias = (_e591 + (max(((length(_e592) * _e594) - 1.5f), 0f) * _e598)); - let _e601 = uv_4; - let _e602 = disc; - let _e603 = texel_1; - let _e605 = searchRadius; - let _e608 = tileLo; - let _e609 = tileHi; - let _e611 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e601 + ((_e602 * _e603) * _e605)), _e608, _e609), 0f); - occluder_1 = _e611.x; - let _e614 = projected[2u]; - let _e615 = tapBias; - let _e617 = occluder_1; - if ((_e614 - _e615) > _e617) { - let _e619 = occluder_1; - let _e620 = blockerSum; - blockerSum = (_e620 + _e619); - let _e622 = blockerCount; - blockerCount = (_e622 + 1f); + let _e590 = turn_1; + param_60 = _e590; + let _e591 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); + disc = _e591; + let _e592 = bias; + let _e593 = disc; + let _e595 = searchRadius; + let _e599 = receiverSlope; + tapBias = (_e592 + (max(((length(_e593) * _e595) - 1.5f), 0f) * _e599)); + let _e602 = uv_4; + let _e603 = disc; + let _e604 = texel_1; + let _e606 = searchRadius; + let _e609 = tileLo; + let _e610 = tileHi; + let _e612 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e602 + ((_e603 * _e604) * _e606)), _e609, _e610), 0f); + occluder_1 = _e612.x; + let _e615 = projected[2u]; + let _e616 = tapBias; + let _e618 = occluder_1; + if ((_e615 - _e616) > _e618) { + let _e620 = occluder_1; + let _e621 = blockerSum; + blockerSum = (_e621 + _e620); + let _e623 = blockerCount; + blockerCount = (_e623 + 1f); } continue; } else { break; } continuing { - let _e624 = i_5; - i_5 = (_e624 + 1i); + let _e625 = i_5; + i_5 = (_e625 + 1i); } } - let _e626 = blockerCount; - if (_e626 <= 0f) { + let _e627 = blockerCount; + if (_e627 <= 0f) { return 1f; } - let _e629 = projected[2u]; - let _e630 = blockerSum; - let _e631 = blockerCount; - let _e635 = cascadeDepth; - gap = (max((_e629 - (_e630 / _e631)), 0f) * _e635); - let _e637 = spread; - let _e638 = gap; - let _e640 = cascadeTexel; - radius_2 = clamp(((_e637 * _e638) / _e640), 1f, 16f); + let _e630 = projected[2u]; + let _e631 = blockerSum; + let _e632 = blockerCount; + let _e636 = cascadeDepth; + gap = (max((_e630 - (_e631 / _e632)), 0f) * _e636); + let _e638 = spread; + let _e639 = gap; + let _e641 = cascadeTexel; + radius_2 = clamp(((_e638 * _e639) / _e641), 1f, 16f); i_6 = 0i; loop { - let _e643 = i_6; - if (_e643 < 16i) { - let _e645 = turn_1; - let _e647 = i_6; - param_61 = _e647; + let _e644 = i_6; + if (_e644 < 16i) { + let _e646 = turn_1; + let _e648 = i_6; + param_61 = _e648; param_62 = 16i; - param_63 = (_e645 + 1f); - let _e648 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - disc_1 = _e648; - let _e649 = bias; - let _e650 = disc_1; - let _e652 = radius_2; - let _e656 = receiverSlope; - tapBias_1 = (_e649 + (max(((length(_e650) * _e652) - 1.5f), 0f) * _e656)); - let _e659 = uv_4; - let _e660 = disc_1; - let _e661 = texel_1; - let _e663 = radius_2; - let _e666 = tileLo; - let _e667 = tileHi; - let _e669 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e659 + ((_e660 * _e661) * _e663)), _e666, _e667), 0f); - occluder_2 = _e669.x; - let _e672 = projected[2u]; - let _e673 = tapBias_1; - let _e675 = occluder_2; - let _e678 = lit_1; - lit_1 = (_e678 + select(1f, 0f, ((_e672 - _e673) > _e675))); + param_63 = (_e646 + 1f); + let _e649 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); + disc_1 = _e649; + let _e650 = bias; + let _e651 = disc_1; + let _e653 = radius_2; + let _e657 = receiverSlope; + tapBias_1 = (_e650 + (max(((length(_e651) * _e653) - 1.5f), 0f) * _e657)); + let _e660 = uv_4; + let _e661 = disc_1; + let _e662 = texel_1; + let _e664 = radius_2; + let _e667 = tileLo; + let _e668 = tileHi; + let _e670 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e660 + ((_e661 * _e662) * _e664)), _e667, _e668), 0f); + occluder_2 = _e670.x; + let _e673 = projected[2u]; + let _e674 = tapBias_1; + let _e676 = occluder_2; + let _e679 = lit_1; + lit_1 = (_e679 + select(1f, 0f, ((_e673 - _e674) > _e676))); continue; } else { break; } continuing { - let _e680 = i_6; - i_6 = (_e680 + 1i); + let _e681 = i_6; + i_6 = (_e681 + 1i); } } - let _e682 = lit_1; - lit_1 = (_e682 * 0.0625f); + let _e683 = lit_1; + lit_1 = (_e683 * 0.0625f); } } - let _e684 = lit_1; - let _e685 = strength_1; - return mix(1f, _e684, clamp(_e685, 0f, 1f)); + let _e685 = lit_1; + let _e686 = strength_1; + return mix(1f, _e685, clamp(_e686, 0f, 1f)); } fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_2: ptr, index: ptr) -> f32 { @@ -44330,19 +45175,19 @@ fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_65: LightSample; var param_66: i32; - let _e172 = (*s_2); - param_64 = _e172; - let _e173 = (*light_2); - param_65 = _e173; - let _e174 = (*index); - param_66 = _e174; - let _e175 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); - return _e175; + let _e173 = (*s_2); + param_64 = _e173; + let _e174 = (*light_2); + param_65 = _e174; + let _e175 = (*index); + param_66 = _e175; + let _e176 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); + return _e176; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e167 = (*index_1); - return (_e167 < 8i); + let _e168 = (*index_1); + return (_e168 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -44350,26 +45195,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e171 = (*distance_1); let _e172 = (*distance_1); - attenuation = (1f / max((_e171 * _e172), 0.0001f)); - let _e176 = (*range_4); - if (_e176 > 0f) { - let _e178 = (*distance_1); - let _e179 = (*range_4); - ratio = (_e178 / _e179); - let _e181 = ratio; + let _e173 = (*distance_1); + attenuation = (1f / max((_e172 * _e173), 0.0001f)); + let _e177 = (*range_4); + if (_e177 > 0f) { + let _e179 = (*distance_1); + let _e180 = (*range_4); + ratio = (_e179 / _e180); let _e182 = ratio; - let _e184 = ratio; - let _e186 = ratio; - window = clamp((1f - (((_e181 * _e182) * _e184) * _e186)), 0f, 1f); - let _e190 = window; + let _e183 = ratio; + let _e185 = ratio; + let _e187 = ratio; + window = clamp((1f - (((_e182 * _e183) * _e185) * _e187)), 0f, 1f); let _e191 = window; - let _e193 = attenuation; - attenuation = (_e193 * (_e190 * _e191)); + let _e192 = window; + let _e194 = attenuation; + attenuation = (_e194 * (_e191 * _e192)); } - let _e195 = attenuation; - return _e195; + let _e196 = attenuation; + return _e196; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -44379,82 +45224,82 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_8: vec3; + var local_9: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; var u: f32; var v: f32; - let _e183 = (*halfWidth); - let _e184 = (*halfHeight); - n_2 = cross(_e183, _e184); - let _e186 = n_2; - nLen = length(_e186); - let _e188 = nLen; - if (_e188 < 0.000000000001f) { - let _e190 = (*centre); - return _e190; - } - let _e191 = nLen; - let _e192 = n_2; - n_2 = (_e192 / vec3(_e191)); - let _e195 = (*centre); - let _e196 = (*world_2); - toPlane = (_e195 - _e196); - let _e198 = (*mirror); - let _e199 = n_2; - denom = dot(_e198, _e199); - let _e201 = denom; - if (abs(_e201) < 0.00001f) { - let _e204 = toPlane; - let _e205 = n_2; - let _e206 = toPlane; - let _e207 = n_2; - onPlane = (_e204 - (_e205 * dot(_e206, _e207))); + let _e184 = (*halfWidth); + let _e185 = (*halfHeight); + n_2 = cross(_e184, _e185); + let _e187 = n_2; + nLen = length(_e187); + let _e189 = nLen; + if (_e189 < 0.000000000001f) { + let _e191 = (*centre); + return _e191; + } + let _e192 = nLen; + let _e193 = n_2; + n_2 = (_e193 / vec3(_e192)); + let _e196 = (*centre); + let _e197 = (*world_2); + toPlane = (_e196 - _e197); + let _e199 = (*mirror); + let _e200 = n_2; + denom = dot(_e199, _e200); + let _e202 = denom; + if (abs(_e202) < 0.00001f) { + let _e205 = toPlane; + let _e206 = n_2; + let _e207 = toPlane; + let _e208 = n_2; + onPlane = (_e205 - (_e206 * dot(_e207, _e208))); } else { - let _e211 = toPlane; - let _e212 = n_2; - let _e214 = denom; - t_1 = (dot(_e211, _e212) / _e214); - let _e216 = t_1; - if (_e216 > 0f) { - let _e218 = (*mirror); - let _e219 = t_1; - local_8 = (_e218 * _e219); + let _e212 = toPlane; + let _e213 = n_2; + let _e215 = denom; + t_1 = (dot(_e212, _e213) / _e215); + let _e217 = t_1; + if (_e217 > 0f) { + let _e219 = (*mirror); + let _e220 = t_1; + local_9 = (_e219 * _e220); } else { - let _e221 = toPlane; - let _e222 = n_2; - let _e223 = toPlane; - let _e224 = n_2; - local_8 = (_e221 - (_e222 * dot(_e223, _e224))); - } - let _e228 = local_8; - onPlane = _e228; - } - let _e229 = onPlane; - let _e230 = toPlane; - offset_2 = (_e229 - _e230); - let _e232 = (*halfWidth); + let _e222 = toPlane; + let _e223 = n_2; + let _e224 = toPlane; + let _e225 = n_2; + local_9 = (_e222 - (_e223 * dot(_e224, _e225))); + } + let _e229 = local_9; + onPlane = _e229; + } + let _e230 = onPlane; + let _e231 = toPlane; + offset_2 = (_e230 - _e231); let _e233 = (*halfWidth); - wLen2_ = max(dot(_e232, _e233), 0.000000000001f); - let _e236 = (*halfHeight); + let _e234 = (*halfWidth); + wLen2_ = max(dot(_e233, _e234), 0.000000000001f); let _e237 = (*halfHeight); - hLen2_ = max(dot(_e236, _e237), 0.000000000001f); - let _e240 = offset_2; - let _e241 = (*halfWidth); - let _e243 = wLen2_; - u = clamp((dot(_e240, _e241) / _e243), -1f, 1f); - let _e246 = offset_2; - let _e247 = (*halfHeight); - let _e249 = hLen2_; - v = clamp((dot(_e246, _e247) / _e249), -1f, 1f); - let _e252 = (*centre); - let _e253 = (*halfWidth); - let _e254 = u; - let _e257 = (*halfHeight); - let _e258 = v; - return ((_e252 + (_e253 * _e254)) + (_e257 * _e258)); + let _e238 = (*halfHeight); + hLen2_ = max(dot(_e237, _e238), 0.000000000001f); + let _e241 = offset_2; + let _e242 = (*halfWidth); + let _e244 = wLen2_; + u = clamp((dot(_e241, _e242) / _e244), -1f, 1f); + let _e247 = offset_2; + let _e248 = (*halfHeight); + let _e250 = hLen2_; + v = clamp((dot(_e247, _e248) / _e250), -1f, 1f); + let _e253 = (*centre); + let _e254 = (*halfWidth); + let _e255 = u; + let _e258 = (*halfHeight); + let _e259 = v; + return ((_e253 + (_e254 * _e255)) + (_e258 * _e259)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>, n_3: ptr>) -> f32 { @@ -44463,34 +45308,34 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_9: f32; + var local_10: f32; - let _e175 = (*a_1); let _e176 = (*a_1); - ua = (_e175 / vec3(max(length(_e176), 0.000000000001f))); - let _e181 = (*b); + let _e177 = (*a_1); + ua = (_e176 / vec3(max(length(_e177), 0.000000000001f))); let _e182 = (*b); - ub = (_e181 / vec3(max(length(_e182), 0.000000000001f))); - let _e187 = ua; - let _e188 = ub; - angle_1 = acos(clamp(dot(_e187, _e188), -1f, 1f)); - let _e192 = ua; - let _e193 = ub; - axis = cross(_e192, _e193); - let _e195 = axis; - len = length(_e195); - let _e197 = len; - if (_e197 > 0.000001f) { - let _e199 = angle_1; - let _e200 = axis; - let _e201 = (*n_3); - let _e204 = len; - local_9 = ((_e199 * dot(_e200, _e201)) / _e204); + let _e183 = (*b); + ub = (_e182 / vec3(max(length(_e183), 0.000000000001f))); + let _e188 = ua; + let _e189 = ub; + angle_1 = acos(clamp(dot(_e188, _e189), -1f, 1f)); + let _e193 = ua; + let _e194 = ub; + axis = cross(_e193, _e194); + let _e196 = axis; + len = length(_e196); + let _e198 = len; + if (_e198 > 0.000001f) { + let _e200 = angle_1; + let _e201 = axis; + let _e202 = (*n_3); + let _e205 = len; + local_10 = ((_e200 * dot(_e201, _e202)) / _e205); } else { - local_9 = 0f; + local_10 = 0f; } - let _e206 = local_9; - return _e206; + let _e207 = local_10; + return _e207; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr, 4>>, n_4: ptr>) -> f32 { @@ -44500,15 +45345,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_10: i32; + var local_11: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_11: f32; + var local_12: f32; var aAbove: bool; var bAbove: bool; - var local_12: f32; + var local_13: f32; var param_67: vec3; var param_68: vec3; var param_69: vec3; @@ -44521,191 +45366,191 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr(0f, 0f, 0f); i_7 = 0i; loop { - let _e189 = i_7; - if (_e189 < 4i) { - let _e191 = i_7; - let _e193 = (*corners)[_e191]; - a_2 = _e193; - let _e194 = i_7; - if (_e194 == 3i) { - local_10 = 0i; + let _e190 = i_7; + if (_e190 < 4i) { + let _e192 = i_7; + let _e194 = (*corners)[_e192]; + a_2 = _e194; + let _e195 = i_7; + if (_e195 == 3i) { + local_11 = 0i; } else { - let _e196 = i_7; - local_10 = (_e196 + 1i); - } - let _e198 = local_10; - let _e200 = (*corners)[_e198]; - b_1 = _e200; - let _e201 = a_2; - let _e202 = (*n_4); - ha = dot(_e201, _e202); - let _e204 = b_1; - let _e205 = (*n_4); - hb = dot(_e204, _e205); - let _e207 = ha; - let _e208 = hb; - d_3 = (_e207 - _e208); - let _e210 = a_2; - let _e211 = b_1; - let _e212 = a_2; - let _e214 = d_3; - if (abs(_e214) > 0.000000000001f) { - let _e217 = ha; - let _e218 = d_3; - local_11 = (_e217 / _e218); - } else { - local_11 = 0f; - } - let _e220 = local_11; - q = (_e210 + ((_e211 - _e212) * _e220)); - let _e223 = ha; - aAbove = (_e223 > 0f); - let _e225 = hb; - bAbove = (_e225 > 0f); - let _e227 = aAbove; - let _e228 = bAbove; - if (_e227 || _e228) { - let _e230 = aAbove; - let _e231 = a_2; - let _e232 = q; - let _e235 = bAbove; - let _e236 = b_1; - let _e237 = q; - param_67 = select(_e232, _e231, vec3(_e230)); - param_68 = select(_e237, _e236, vec3(_e235)); - let _e240 = (*n_4); - param_69 = _e240; - let _e241 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); - local_12 = _e241; + let _e197 = i_7; + local_11 = (_e197 + 1i); + } + let _e199 = local_11; + let _e201 = (*corners)[_e199]; + b_1 = _e201; + let _e202 = a_2; + let _e203 = (*n_4); + ha = dot(_e202, _e203); + let _e205 = b_1; + let _e206 = (*n_4); + hb = dot(_e205, _e206); + let _e208 = ha; + let _e209 = hb; + d_3 = (_e208 - _e209); + let _e211 = a_2; + let _e212 = b_1; + let _e213 = a_2; + let _e215 = d_3; + if (abs(_e215) > 0.000000000001f) { + let _e218 = ha; + let _e219 = d_3; + local_12 = (_e218 / _e219); } else { local_12 = 0f; } - let _e242 = local_12; - let _e243 = total; - total = (_e243 + _e242); - let _e245 = aAbove; - let _e246 = bAbove; - let _e249 = q; - let _e250 = exit; - exit = select(_e250, _e249, vec3((_e245 && !(_e246)))); - let _e253 = aAbove; - let _e255 = bAbove; - let _e257 = q; - let _e258 = entry; - entry = select(_e258, _e257, vec3((!(_e253) && _e255))); + let _e221 = local_12; + q = (_e211 + ((_e212 - _e213) * _e221)); + let _e224 = ha; + aAbove = (_e224 > 0f); + let _e226 = hb; + bAbove = (_e226 > 0f); + let _e228 = aAbove; + let _e229 = bAbove; + if (_e228 || _e229) { + let _e231 = aAbove; + let _e232 = a_2; + let _e233 = q; + let _e236 = bAbove; + let _e237 = b_1; + let _e238 = q; + param_67 = select(_e233, _e232, vec3(_e231)); + param_68 = select(_e238, _e237, vec3(_e236)); + let _e241 = (*n_4); + param_69 = _e241; + let _e242 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); + local_13 = _e242; + } else { + local_13 = 0f; + } + let _e243 = local_13; + let _e244 = total; + total = (_e244 + _e243); + let _e246 = aAbove; + let _e247 = bAbove; + let _e250 = q; + let _e251 = exit; + exit = select(_e251, _e250, vec3((_e246 && !(_e247)))); + let _e254 = aAbove; + let _e256 = bAbove; + let _e258 = q; + let _e259 = entry; + entry = select(_e259, _e258, vec3((!(_e254) && _e256))); continue; } else { break; } continuing { - let _e261 = i_7; - i_7 = (_e261 + 1i); + let _e262 = i_7; + i_7 = (_e262 + 1i); } } - let _e263 = exit; - param_70 = _e263; - let _e264 = entry; - param_71 = _e264; - let _e265 = (*n_4); - param_72 = _e265; - let _e266 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); - let _e267 = total; - total = (_e267 + _e266); - let _e269 = total; - return max((-(_e269) * 0.5f), 0f); + let _e264 = exit; + param_70 = _e264; + let _e265 = entry; + param_71 = _e265; + let _e266 = (*n_4); + param_72 = _e266; + let _e267 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); + let _e268 = total; + total = (_e268 + _e267); + let _e270 = total; + return max((-(_e270) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_13: f32; var local_14: f32; var local_15: f32; + var local_16: f32; - let _e172 = (*slot_1); let _e173 = (*slot_1); - lane = (_e172 - ((_e173 / 4i) * 4i)); - let _e177 = lane; - if (_e177 == 0i) { - let _e180 = (*four)[0u]; - local_13 = _e180; + let _e174 = (*slot_1); + lane = (_e173 - ((_e174 / 4i) * 4i)); + let _e178 = lane; + if (_e178 == 0i) { + let _e181 = (*four)[0u]; + local_14 = _e181; } else { - let _e181 = lane; - if (_e181 == 1i) { - let _e184 = (*four)[1u]; - local_14 = _e184; + let _e182 = lane; + if (_e182 == 1i) { + let _e185 = (*four)[1u]; + local_15 = _e185; } else { - let _e185 = lane; - if (_e185 == 2i) { - let _e188 = (*four)[2u]; - local_15 = _e188; + let _e186 = lane; + if (_e186 == 2i) { + let _e189 = (*four)[2u]; + local_16 = _e189; } else { - let _e190 = (*four)[3u]; - local_15 = _e190; + let _e191 = (*four)[3u]; + local_16 = _e191; } - let _e191 = local_15; - local_14 = _e191; + let _e192 = local_16; + local_15 = _e192; } - let _e192 = local_14; - local_13 = _e192; + let _e193 = local_15; + local_14 = _e193; } - let _e193 = local_13; - return _e193; + let _e194 = local_14; + return _e194; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { var param_73: vec4; var param_74: i32; - let _e169 = (*slot_2); - let _e173 = light_list_info.scales[(_e169 / 4i)]; - param_73 = _e173; - let _e174 = (*slot_2); - param_74 = _e174; - let _e175 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); - return _e175; + let _e170 = (*slot_2); + let _e174 = light_list_info.scales[(_e170 / 4i)]; + param_73 = _e174; + let _e175 = (*slot_2); + param_74 = _e175; + let _e176 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); + return _e176; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_3: vec4; var b_2: vec4; - let _e171 = light_list_info.slot_rows[0i]; - let _e172 = (*row); - a_3 = abs((_e171 - vec4(_e172))); - let _e178 = light_list_info.slot_rows[1i]; - let _e179 = (*row); - b_2 = abs((_e178 - vec4(_e179))); - let _e184 = a_3[0u]; - let _e186 = a_3[1u]; - let _e189 = a_3[2u]; - let _e191 = a_3[3u]; - let _e195 = b_2[0u]; - let _e197 = b_2[1u]; - let _e200 = b_2[2u]; - let _e202 = b_2[3u]; - return (min(min(min(_e184, _e186), min(_e189, _e191)), min(min(_e195, _e197), min(_e200, _e202))) < 0.5f); + let _e172 = light_list_info.slot_rows[0i]; + let _e173 = (*row); + a_3 = abs((_e172 - vec4(_e173))); + let _e179 = light_list_info.slot_rows[1i]; + let _e180 = (*row); + b_2 = abs((_e179 - vec4(_e180))); + let _e185 = a_3[0u]; + let _e187 = a_3[1u]; + let _e190 = a_3[2u]; + let _e192 = a_3[3u]; + let _e196 = b_2[0u]; + let _e198 = b_2[1u]; + let _e201 = b_2[2u]; + let _e203 = b_2[3u]; + return (min(min(min(_e185, _e187), min(_e190, _e192)), min(min(_e196, _e198), min(_e201, _e203))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { var param_75: vec4; var param_76: i32; - let _e169 = (*slot_3); - let _e173 = light_list_info.indices[(_e169 / 4i)]; - param_75 = _e173; - let _e174 = (*slot_3); - param_76 = _e174; - let _e175 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); - return _e175; + let _e170 = (*slot_3); + let _e174 = light_list_info.indices[(_e170 / 4i)]; + param_75 = _e174; + let _e175 = (*slot_3); + param_76 = _e175; + let _e176 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); + return _e176; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e168 = (*texel_2); - let _e172 = light_list_info.list[1u]; - let _e174 = (*row_1); - let _e178 = light_list_info.list[2u]; - let _e181 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e168 + 0.5f) * _e172), ((_e174 + 0.5f) * _e178)), 0f); - return _e181; + let _e169 = (*texel_2); + let _e173 = light_list_info.list[1u]; + let _e175 = (*row_1); + let _e179 = light_list_info.list[2u]; + let _e182 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e169 + 0.5f) * _e173), ((_e175 + 0.5f) * _e179)), 0f); + return _e182; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -44719,35 +45564,35 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var param_79: vec4; var param_80: i32; - let _e176 = g_cluster_offset; - let _e177 = (*slot_4); - entry_1 = (_e176 + f32(_e177)); - let _e180 = entry_1; - row_2 = floor((_e180 / 16f)); - let _e183 = entry_1; - let _e184 = row_2; - within = (_e183 - (_e184 * 16f)); - let _e187 = within; - texel_3 = floor((_e187 / 4f)); - let _e192 = light_list_info.cluster_depth[3u]; - let _e193 = row_2; - let _e195 = texel_3; - param_77 = _e195; - param_78 = (_e192 + _e193); - let _e196 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); - four_1 = _e196; - let _e197 = within; - let _e198 = texel_3; - let _e203 = four_1; - param_79 = _e203; - param_80 = i32(((_e197 - (_e198 * 4f)) + 0.5f)); - let _e204 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); - return _e204; + let _e177 = g_cluster_offset; + let _e178 = (*slot_4); + entry_1 = (_e177 + f32(_e178)); + let _e181 = entry_1; + row_2 = floor((_e181 / 16f)); + let _e184 = entry_1; + let _e185 = row_2; + within = (_e184 - (_e185 * 16f)); + let _e188 = within; + texel_3 = floor((_e188 / 4f)); + let _e193 = light_list_info.cluster_depth[3u]; + let _e194 = row_2; + let _e196 = texel_3; + param_77 = _e196; + param_78 = (_e193 + _e194); + let _e197 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); + four_1 = _e197; + let _e198 = within; + let _e199 = texel_3; + let _e204 = four_1; + param_79 = _e204; + param_80 = i32(((_e198 - (_e199 * 4f)) + 0.5f)); + let _e205 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); + return _e205; } fn Clustered_u0028_() -> bool { - let _e168 = light_list_info.cluster_grid[3u]; - return (_e168 > 0.5f); + let _e169 = light_list_info.cluster_grid[3u]; + return (_e169 > 0.5f); } fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_3: ptr) -> LightSample { @@ -44759,12 +45604,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_16: f32; + var local_17: f32; var param_81: i32; var param_82: i32; var v_1: f32; var u_1: f32; - var local_17: f32; + var local_18: f32; var param_83: f32; var param_84: i32; var type_39: f32; @@ -44774,12 +45619,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_18: f32; + var local_19: f32; var param_85: array, 4>; var param_86: vec3; var area: f32; var radiance: f32; - var local_19: f32; + var local_20: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -44792,7 +45637,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_91: vec3; var toPoint: vec3; var pointDistance: f32; - var local_20: vec3; + var local_21: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; @@ -44803,264 +45648,264 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e219 = (*index_2); - if (_e219 < 8i) { - let _e221 = (*index_2); - let _e224 = frag_info.light_position[_e221]; - position = _e224; - let _e225 = (*index_2); - let _e228 = frag_info.light_color[_e225]; - color_1 = _e228; - let _e229 = (*index_2); - let _e232 = frag_info.light_direction[_e229]; - direction = _e232; - let _e233 = (*index_2); - let _e236 = frag_info.light_cone[_e233]; - cone = _e236; + let _e220 = (*index_2); + if (_e220 < 8i) { + let _e222 = (*index_2); + let _e225 = frag_info.light_position[_e222]; + position = _e225; + let _e226 = (*index_2); + let _e229 = frag_info.light_color[_e226]; + color_1 = _e229; + let _e230 = (*index_2); + let _e233 = frag_info.light_direction[_e230]; + direction = _e233; + let _e234 = (*index_2); + let _e237 = frag_info.light_cone[_e234]; + cone = _e237; } else { - let _e237 = (*index_2); - slot_5 = (_e237 - 8i); - let _e239 = Clustered_u0028_(); - clustered = _e239; - let _e240 = clustered; - if _e240 { - let _e241 = slot_5; - param_81 = _e241; - let _e242 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_16 = _e242; + let _e238 = (*index_2); + slot_5 = (_e238 - 8i); + let _e240 = Clustered_u0028_(); + clustered = _e240; + let _e241 = clustered; + if _e241 { + let _e242 = slot_5; + param_81 = _e242; + let _e243 = ClusterRow_u0028_i1_u003b((¶m_81)); + local_17 = _e243; } else { - let _e243 = slot_5; - param_82 = _e243; - let _e244 = LightListRow_u0028_i1_u003b((¶m_82)); - local_16 = _e244; - } - let _e245 = local_16; - listRow = _e245; - let _e246 = listRow; - let _e250 = light_list_info.list[2u]; - v_1 = ((_e246 + 0.5f) * _e250); - let _e254 = light_list_info.list[1u]; - u_1 = _e254; - let _e255 = u_1; - let _e257 = v_1; - let _e259 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e255), _e257), 0f); - position = _e259; - let _e260 = u_1; - let _e262 = v_1; - let _e264 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e260), _e262), 0f); - color_1 = _e264; - let _e265 = u_1; - let _e267 = v_1; - let _e269 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e265), _e267), 0f); - direction = _e269; - let _e270 = u_1; - let _e272 = v_1; - let _e274 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e270), _e272), 0f); - cone = _e274; - let _e275 = clustered; - if _e275 { - let _e276 = listRow; - param_83 = _e276; - let _e277 = InSlots_u0028_f1_u003b((¶m_83)); - local_17 = select(1f, 0f, _e277); + let _e244 = slot_5; + param_82 = _e244; + let _e245 = LightListRow_u0028_i1_u003b((¶m_82)); + local_17 = _e245; + } + let _e246 = local_17; + listRow = _e246; + let _e247 = listRow; + let _e251 = light_list_info.list[2u]; + v_1 = ((_e247 + 0.5f) * _e251); + let _e255 = light_list_info.list[1u]; + u_1 = _e255; + let _e256 = u_1; + let _e258 = v_1; + let _e260 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e256), _e258), 0f); + position = _e260; + let _e261 = u_1; + let _e263 = v_1; + let _e265 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e261), _e263), 0f); + color_1 = _e265; + let _e266 = u_1; + let _e268 = v_1; + let _e270 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e266), _e268), 0f); + direction = _e270; + let _e271 = u_1; + let _e273 = v_1; + let _e275 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e271), _e273), 0f); + cone = _e275; + let _e276 = clustered; + if _e276 { + let _e277 = listRow; + param_83 = _e277; + let _e278 = InSlots_u0028_f1_u003b((¶m_83)); + local_18 = select(1f, 0f, _e278); } else { - let _e279 = slot_5; - param_84 = _e279; - let _e280 = LightListScale_u0028_i1_u003b((¶m_84)); - local_17 = _e280; - } - let _e281 = local_17; - let _e283 = color_1[3u]; - color_1[3u] = (_e283 * _e281); - } - let _e287 = position[3u]; - type_39 = _e287; - let _e288 = type_39; - if (_e288 > 2.5f) { - let _e290 = direction; - halfWidth_1 = _e290.xyz; - let _e292 = cone; - halfHeight_1 = _e292.xyz; - let _e294 = position; - let _e296 = v_world_position_1; - toCentre = (_e294.xyz - _e296); - let _e298 = toCentre; - let _e299 = halfWidth_1; - let _e301 = halfHeight_1; - corners_1[0i] = ((_e298 - _e299) - _e301); - let _e304 = toCentre; - let _e305 = halfWidth_1; - let _e307 = halfHeight_1; - corners_1[1i] = ((_e304 + _e305) - _e307); - let _e310 = toCentre; - let _e311 = halfWidth_1; - let _e313 = halfHeight_1; - corners_1[2i] = ((_e310 + _e311) + _e313); - let _e316 = toCentre; - let _e317 = halfWidth_1; - let _e319 = halfHeight_1; - corners_1[3i] = ((_e316 - _e317) + _e319); - let _e322 = toCentre; - let _e323 = halfWidth_1; - let _e324 = halfHeight_1; - behind = (dot(_e322, cross(_e323, _e324)) >= 0f); - let _e328 = behind; - if _e328 { - local_18 = 0f; + let _e280 = slot_5; + param_84 = _e280; + let _e281 = LightListScale_u0028_i1_u003b((¶m_84)); + local_18 = _e281; + } + let _e282 = local_18; + let _e284 = color_1[3u]; + color_1[3u] = (_e284 * _e282); + } + let _e288 = position[3u]; + type_39 = _e288; + let _e289 = type_39; + if (_e289 > 2.5f) { + let _e291 = direction; + halfWidth_1 = _e291.xyz; + let _e293 = cone; + halfHeight_1 = _e293.xyz; + let _e295 = position; + let _e297 = v_world_position_1; + toCentre = (_e295.xyz - _e297); + let _e299 = toCentre; + let _e300 = halfWidth_1; + let _e302 = halfHeight_1; + corners_1[0i] = ((_e299 - _e300) - _e302); + let _e305 = toCentre; + let _e306 = halfWidth_1; + let _e308 = halfHeight_1; + corners_1[1i] = ((_e305 + _e306) - _e308); + let _e311 = toCentre; + let _e312 = halfWidth_1; + let _e314 = halfHeight_1; + corners_1[2i] = ((_e311 + _e312) + _e314); + let _e317 = toCentre; + let _e318 = halfWidth_1; + let _e320 = halfHeight_1; + corners_1[3i] = ((_e317 - _e318) + _e320); + let _e323 = toCentre; + let _e324 = halfWidth_1; + let _e325 = halfHeight_1; + behind = (dot(_e323, cross(_e324, _e325)) >= 0f); + let _e329 = behind; + if _e329 { + local_19 = 0f; } else { - let _e329 = corners_1; - param_85 = _e329; - let _e331 = (*s_3).n; - param_86 = _e331; - let _e332 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_18 = _e332; - } - let _e333 = local_18; - formFactor = _e333; - let _e334 = halfWidth_1; - let _e335 = halfHeight_1; - area = (length(cross(_e334, _e335)) * 4f); - let _e339 = area; - if (_e339 > 0.000000001f) { - let _e341 = area; - local_19 = (1f / _e341); + let _e330 = corners_1; + param_85 = _e330; + let _e332 = (*s_3).n; + param_86 = _e332; + let _e333 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); + local_19 = _e333; + } + let _e334 = local_19; + formFactor = _e334; + let _e335 = halfWidth_1; + let _e336 = halfHeight_1; + area = (length(cross(_e335, _e336)) * 4f); + let _e340 = area; + if (_e340 > 0.000000001f) { + let _e342 = area; + local_20 = (1f / _e342); } else { - local_19 = 0f; + local_20 = 0f; } - let _e343 = local_19; - radiance = _e343; - let _e344 = toCentre; - distance_2 = length(_e344); - let _e347 = direction[3u]; - if (_e347 > 0f) { - let _e349 = distance_2; - let _e351 = direction[3u]; - ratio_1 = (_e349 / _e351); - let _e353 = ratio_1; + let _e344 = local_20; + radiance = _e344; + let _e345 = toCentre; + distance_2 = length(_e345); + let _e348 = direction[3u]; + if (_e348 > 0f) { + let _e350 = distance_2; + let _e352 = direction[3u]; + ratio_1 = (_e350 / _e352); let _e354 = ratio_1; - let _e356 = ratio_1; - let _e358 = ratio_1; - window_1 = clamp((1f - (((_e353 * _e354) * _e356) * _e358)), 0f, 1f); - let _e362 = window_1; + let _e355 = ratio_1; + let _e357 = ratio_1; + let _e359 = ratio_1; + window_1 = clamp((1f - (((_e354 * _e355) * _e357) * _e359)), 0f, 1f); let _e363 = window_1; - let _e365 = radiance; - radiance = (_e365 * (_e362 * _e363)); - } - let _e368 = (*s_3).v; - let _e371 = (*s_3).n; - mirror_1 = reflect(-(_e368), _e371); - let _e373 = position; - param_87 = _e373.xyz; - let _e375 = halfWidth_1; - param_88 = _e375; - let _e376 = halfHeight_1; - param_89 = _e376; - let _e377 = v_world_position_1; - param_90 = _e377; - let _e378 = mirror_1; - param_91 = _e378; - let _e379 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); - representative = _e379; - let _e380 = representative; - let _e381 = v_world_position_1; - toPoint = (_e380 - _e381); - let _e383 = toPoint; - pointDistance = length(_e383); - let _e385 = pointDistance; - if (_e385 > 0.000001f) { - let _e387 = toPoint; - let _e388 = pointDistance; - local_20 = (_e387 / vec3(_e388)); + let _e364 = window_1; + let _e366 = radiance; + radiance = (_e366 * (_e363 * _e364)); + } + let _e369 = (*s_3).v; + let _e372 = (*s_3).n; + mirror_1 = reflect(-(_e369), _e372); + let _e374 = position; + param_87 = _e374.xyz; + let _e376 = halfWidth_1; + param_88 = _e376; + let _e377 = halfHeight_1; + param_89 = _e377; + let _e378 = v_world_position_1; + param_90 = _e378; + let _e379 = mirror_1; + param_91 = _e379; + let _e380 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); + representative = _e380; + let _e381 = representative; + let _e382 = v_world_position_1; + toPoint = (_e381 - _e382); + let _e384 = toPoint; + pointDistance = length(_e384); + let _e386 = pointDistance; + if (_e386 > 0.000001f) { + let _e388 = toPoint; + let _e389 = pointDistance; + local_21 = (_e388 / vec3(_e389)); } else { - let _e392 = (*s_3).n; - local_20 = _e392; - } - let _e393 = local_20; - light_3.l = _e393; - let _e396 = light_3.l; - let _e398 = (*s_3).v; - light_3.h = normalize((_e396 + _e398)); - let _e402 = formFactor; - light_3.n_dot_l = _e402; - let _e405 = (*s_3).n; - let _e407 = light_3.h; - light_3.n_dot_h = max(dot(_e405, _e407), 0f); - let _e412 = (*s_3).v; - let _e414 = light_3.h; - light_3.v_dot_h = max(dot(_e412, _e414), 0f); - let _e418 = color_1; - let _e421 = color_1[3u]; - let _e423 = radiance; - light_3.radiance = ((_e418.xyz * _e421) * _e423); - let _e426 = light_3; - return _e426; - } - let _e427 = direction; - aim = normalize(_e427.xyz); + let _e393 = (*s_3).n; + local_21 = _e393; + } + let _e394 = local_21; + light_3.l = _e394; + let _e397 = light_3.l; + let _e399 = (*s_3).v; + light_3.h = normalize((_e397 + _e399)); + let _e403 = formFactor; + light_3.n_dot_l = _e403; + let _e406 = (*s_3).n; + let _e408 = light_3.h; + light_3.n_dot_h = max(dot(_e406, _e408), 0f); + let _e413 = (*s_3).v; + let _e415 = light_3.h; + light_3.v_dot_h = max(dot(_e413, _e415), 0f); + let _e419 = color_1; + let _e422 = color_1[3u]; + let _e424 = radiance; + light_3.radiance = ((_e419.xyz * _e422) * _e424); + let _e427 = light_3; + return _e427; + } + let _e428 = direction; + aim = normalize(_e428.xyz); attenuation_1 = 1f; - let _e430 = type_39; - if (_e430 < 0.5f) { - let _e432 = aim; - light_3.l = -(_e432); + let _e431 = type_39; + if (_e431 < 0.5f) { + let _e433 = aim; + light_3.l = -(_e433); } else { - let _e435 = position; - let _e437 = v_world_position_1; - toLight_1 = (_e435.xyz - _e437); - let _e439 = toLight_1; - distance_3 = length(_e439); - let _e441 = distance_3; - if (_e441 < 0.000001f) { - let _e444 = (*s_3).n; - light_3.l = _e444; - let _e447 = (*s_3).n; - light_3.h = _e447; + let _e436 = position; + let _e438 = v_world_position_1; + toLight_1 = (_e436.xyz - _e438); + let _e440 = toLight_1; + distance_3 = length(_e440); + let _e442 = distance_3; + if (_e442 < 0.000001f) { + let _e445 = (*s_3).n; + light_3.l = _e445; + let _e448 = (*s_3).n; + light_3.h = _e448; light_3.radiance = vec3(0f, 0f, 0f); light_3.n_dot_l = 0f; light_3.n_dot_h = 0f; light_3.v_dot_h = 0f; - let _e453 = light_3; - return _e453; - } - let _e454 = toLight_1; - let _e455 = distance_3; - light_3.l = (_e454 / vec3(_e455)); - let _e459 = distance_3; - param_92 = _e459; - let _e461 = direction[3u]; - param_93 = _e461; - let _e462 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); - attenuation_1 = _e462; - let _e463 = type_39; - if (_e463 > 1.5f) { - let _e465 = aim; - let _e467 = light_3.l; - cosAngle = dot(_e465, -(_e467)); - let _e470 = cosAngle; - let _e472 = cone[1u]; - let _e475 = cone[0u]; - let _e477 = cone[1u]; - let _e481 = attenuation_1; - attenuation_1 = (_e481 * clamp(((_e470 - _e472) / (_e475 - _e477)), 0f, 1f)); - } - } - let _e484 = light_3.l; - let _e486 = (*s_3).v; - light_3.h = normalize((_e484 + _e486)); - let _e491 = (*s_3).n; - let _e493 = light_3.l; - light_3.n_dot_l = max(dot(_e491, _e493), 0f); - let _e498 = (*s_3).n; - let _e500 = light_3.h; - light_3.n_dot_h = max(dot(_e498, _e500), 0f); - let _e505 = (*s_3).v; - let _e507 = light_3.h; - light_3.v_dot_h = max(dot(_e505, _e507), 0f); - let _e511 = color_1; - let _e514 = color_1[3u]; - let _e516 = attenuation_1; - light_3.radiance = ((_e511.xyz * _e514) * _e516); - let _e519 = light_3; - return _e519; + let _e454 = light_3; + return _e454; + } + let _e455 = toLight_1; + let _e456 = distance_3; + light_3.l = (_e455 / vec3(_e456)); + let _e460 = distance_3; + param_92 = _e460; + let _e462 = direction[3u]; + param_93 = _e462; + let _e463 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); + attenuation_1 = _e463; + let _e464 = type_39; + if (_e464 > 1.5f) { + let _e466 = aim; + let _e468 = light_3.l; + cosAngle = dot(_e466, -(_e468)); + let _e471 = cosAngle; + let _e473 = cone[1u]; + let _e476 = cone[0u]; + let _e478 = cone[1u]; + let _e482 = attenuation_1; + attenuation_1 = (_e482 * clamp(((_e471 - _e473) / (_e476 - _e478)), 0f, 1f)); + } + } + let _e485 = light_3.l; + let _e487 = (*s_3).v; + light_3.h = normalize((_e485 + _e487)); + let _e492 = (*s_3).n; + let _e494 = light_3.l; + light_3.n_dot_l = max(dot(_e492, _e494), 0f); + let _e499 = (*s_3).n; + let _e501 = light_3.h; + light_3.n_dot_h = max(dot(_e499, _e501), 0f); + let _e506 = (*s_3).v; + let _e508 = light_3.h; + light_3.v_dot_h = max(dot(_e506, _e508), 0f); + let _e512 = color_1; + let _e515 = color_1[3u]; + let _e517 = attenuation_1; + light_3.radiance = ((_e512.xyz * _e515) * _e517); + let _e520 = light_3; + return _e520; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -45071,65 +45916,65 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_21: f32; + var local_22: f32; var cell: f32; var row_3: f32; var header: vec4; var param_94: f32; var param_95: f32; - let _e181 = light_list_info.cluster_view_projection; - let _e182 = (*world_3); - clip_1 = (_e181 * vec4(_e182.x, _e182.y, _e182.z, 1f)); - let _e188 = clip_1; - let _e191 = clip_1[3u]; - ndc_1 = (_e188.xy / vec2(max(_e191, 0.000001f))); - let _e196 = light_list_info.cluster_grid; - grid = _e196.xyz; - let _e200 = light_list_info.cluster_depth[0u]; - near_1 = _e200; - let _e202 = ndc_1[0u]; - let _e206 = grid[0u]; - let _e210 = grid[0u]; - tx = clamp(floor((((_e202 * 0.5f) + 0.5f) * _e206)), 0f, (_e210 - 1f)); - let _e214 = ndc_1[1u]; - let _e218 = grid[1u]; - let _e222 = grid[1u]; - ty = clamp(floor((((_e214 * 0.5f) + 0.5f) * _e218)), 0f, (_e222 - 1f)); - let _e226 = clip_1[3u]; - let _e227 = near_1; - if (_e226 <= _e227) { - local_21 = 0f; + let _e182 = light_list_info.cluster_view_projection; + let _e183 = (*world_3); + clip_1 = (_e182 * vec4(_e183.x, _e183.y, _e183.z, 1f)); + let _e189 = clip_1; + let _e192 = clip_1[3u]; + ndc_1 = (_e189.xy / vec2(max(_e192, 0.000001f))); + let _e197 = light_list_info.cluster_grid; + grid = _e197.xyz; + let _e201 = light_list_info.cluster_depth[0u]; + near_1 = _e201; + let _e203 = ndc_1[0u]; + let _e207 = grid[0u]; + let _e211 = grid[0u]; + tx = clamp(floor((((_e203 * 0.5f) + 0.5f) * _e207)), 0f, (_e211 - 1f)); + let _e215 = ndc_1[1u]; + let _e219 = grid[1u]; + let _e223 = grid[1u]; + ty = clamp(floor((((_e215 * 0.5f) + 0.5f) * _e219)), 0f, (_e223 - 1f)); + let _e227 = clip_1[3u]; + let _e228 = near_1; + if (_e227 <= _e228) { + local_22 = 0f; } else { - let _e230 = clip_1[3u]; - let _e231 = near_1; - let _e236 = light_list_info.cluster_depth[1u]; - let _e240 = grid[2u]; - local_21 = clamp(floor((log((_e230 / _e231)) * _e236)), 0f, (_e240 - 1f)); - } - let _e243 = local_21; - tz = _e243; - let _e244 = tx; - let _e245 = ty; - let _e247 = grid[0u]; - let _e250 = tz; - let _e252 = grid[0u]; - let _e255 = grid[1u]; - cell = ((_e244 + (_e245 * _e247)) + ((_e250 * _e252) * _e255)); - let _e258 = cell; - row_3 = floor((_e258 / 4f)); - let _e261 = cell; - let _e262 = row_3; - let _e267 = light_list_info.cluster_depth[2u]; - let _e268 = row_3; - param_94 = (_e261 - (_e262 * 4f)); - param_95 = (_e267 + _e268); - let _e270 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); - header = _e270; - let _e272 = header[0u]; - g_cluster_offset = _e272; - let _e274 = header[1u]; - g_cluster_count = _e274; + let _e231 = clip_1[3u]; + let _e232 = near_1; + let _e237 = light_list_info.cluster_depth[1u]; + let _e241 = grid[2u]; + local_22 = clamp(floor((log((_e231 / _e232)) * _e237)), 0f, (_e241 - 1f)); + } + let _e244 = local_22; + tz = _e244; + let _e245 = tx; + let _e246 = ty; + let _e248 = grid[0u]; + let _e251 = tz; + let _e253 = grid[0u]; + let _e256 = grid[1u]; + cell = ((_e245 + (_e246 * _e248)) + ((_e251 * _e253) * _e256)); + let _e259 = cell; + row_3 = floor((_e259 / 4f)); + let _e262 = cell; + let _e263 = row_3; + let _e268 = light_list_info.cluster_depth[2u]; + let _e269 = row_3; + param_94 = (_e262 - (_e263 * 4f)); + param_95 = (_e268 + _e269); + let _e271 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); + header = _e271; + let _e273 = header[0u]; + g_cluster_offset = _e273; + let _e275 = header[1u]; + g_cluster_count = _e275; return; } @@ -45137,19 +45982,19 @@ fn LightCount_u0028_() -> i32 { var tail: f32; var param_96: vec3; - let _e170 = light_list_info.list[0u]; - tail = _e170; - let _e171 = Clustered_u0028_(); - if _e171 { - let _e172 = v_world_position_1; - param_96 = _e172; + let _e171 = light_list_info.list[0u]; + tail = _e171; + let _e172 = Clustered_u0028_(); + if _e172 { + let _e173 = v_world_position_1; + param_96 = _e173; FindCluster_u0028_vf3_u003b((¶m_96)); - let _e173 = g_cluster_count; - tail = _e173; + let _e174 = g_cluster_count; + tail = _e174; } - let _e176 = frag_info.frame_params[1u]; - let _e180 = tail; - return (clamp(i32((_e176 + 0.5f)), 0i, 8i) + clamp(i32((_e180 + 0.5f)), 0i, 24i)); + let _e177 = frag_info.frame_params[1u]; + let _e181 = tail; + return (clamp(i32((_e177 + 0.5f)), 0i, 8i) + clamp(i32((_e181 + 0.5f)), 0i, 24i)); } fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_4: ptr) -> vec3 { @@ -45161,7 +46006,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_98: Surface; var visibility: f32; var param_99: i32; - var local_22: f32; + var local_23: f32; var param_100: Surface; var param_101: LightSample; var param_102: i32; @@ -45172,93 +46017,93 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_107: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e184 = LightCount_u0028_(); - count_1 = _e184; + let _e185 = LightCount_u0028_(); + count_1 = _e185; i_8 = 0i; loop { - let _e185 = i_8; - if (_e185 < 32i) { - let _e187 = i_8; - let _e188 = count_1; - if (_e187 >= _e188) { + let _e186 = i_8; + if (_e186 < 32i) { + let _e188 = i_8; + let _e189 = count_1; + if (_e188 >= _e189) { break; } - let _e190 = i_8; - param_97 = _e190; - let _e191 = (*s_4); - param_98 = _e191; - let _e192 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); - light_4 = _e192; - let _e194 = light_4.n_dot_l; - if (_e194 <= 0f) { + let _e191 = i_8; + param_97 = _e191; + let _e192 = (*s_4); + param_98 = _e192; + let _e193 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); + light_4 = _e193; + let _e195 = light_4.n_dot_l; + if (_e195 <= 0f) { continue; } - let _e196 = i_8; - param_99 = _e196; - let _e197 = LightHasShadow_u0028_i1_u003b((¶m_99)); - if _e197 { - let _e198 = (*s_4); - param_100 = _e198; - let _e199 = light_4; - param_101 = _e199; - let _e200 = i_8; - param_102 = _e200; - let _e201 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); - let _e202 = v_world_position_1; - param_103 = _e202; - let _e204 = (*s_4).n; - param_104 = _e204; - let _e205 = i_8; - param_105 = _e205; - let _e206 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_22 = (_e201 * _e206); + let _e197 = i_8; + param_99 = _e197; + let _e198 = LightHasShadow_u0028_i1_u003b((¶m_99)); + if _e198 { + let _e199 = (*s_4); + param_100 = _e199; + let _e200 = light_4; + param_101 = _e200; + let _e201 = i_8; + param_102 = _e201; + let _e202 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); + let _e203 = v_world_position_1; + param_103 = _e203; + let _e205 = (*s_4).n; + param_104 = _e205; + let _e206 = i_8; + param_105 = _e206; + let _e207 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); + local_23 = (_e202 * _e207); } else { - local_22 = 1f; + local_23 = 1f; } - let _e208 = local_22; - visibility = _e208; - let _e209 = visibility; - if (_e209 <= 0f) { + let _e209 = local_23; + visibility = _e209; + let _e210 = visibility; + if (_e210 <= 0f) { continue; } - let _e211 = (*s_4); - param_106 = _e211; - let _e212 = light_4; - param_107 = _e212; - let _e213 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); - let _e215 = light_4.radiance; - let _e218 = light_4.n_dot_l; - let _e220 = visibility; - let _e222 = total_1; - total_1 = (_e222 + (((_e213 * _e215) * _e218) * _e220)); + let _e212 = (*s_4); + param_106 = _e212; + let _e213 = light_4; + param_107 = _e213; + let _e214 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); + let _e216 = light_4.radiance; + let _e219 = light_4.n_dot_l; + let _e221 = visibility; + let _e223 = total_1; + total_1 = (_e223 + (((_e214 * _e216) * _e219) * _e221)); continue; } else { break; } continuing { - let _e224 = i_8; - i_8 = (_e224 + 1i); + let _e225 = i_8; + i_8 = (_e225 + 1i); } } - let _e226 = total_1; - return _e226; + let _e227 = total_1; + return _e227; } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e167 = (*srgb); - let _e170 = (*srgb); - let _e175 = (*srgb); - return mix((_e167 / vec3(12.92f)), pow(((_e170 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e175)); + let _e168 = (*srgb); + let _e171 = (*srgb); + let _e176 = (*srgb); + return mix((_e168 / vec3(12.92f)), pow(((_e171 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e176)); } fn ImpostorRight_u0028_vf3_u003b(d_4: ptr>) -> vec3 { var up: vec3; - let _e169 = (*d_4)[1u]; - up = select(vec3(0f, 1f, 0f), vec3(0f, 0f, -1f), vec3((abs(_e169) > 0.999f))); - let _e174 = up; - let _e175 = (*d_4); - return normalize(cross(_e174, _e175)); + let _e170 = (*d_4)[1u]; + up = select(vec3(0f, 1f, 0f), vec3(0f, 0f, -1f), vec3((abs(_e170) > 0.999f))); + let _e175 = up; + let _e176 = (*d_4); + return normalize(cross(_e175, _e176)); } fn ImpostorDecode_u0028_vf2_u003b(uv_5: ptr>) -> vec3 { @@ -45268,25 +46113,25 @@ fn ImpostorDecode_u0028_vf2_u003b(uv_5: ptr>) -> vec3 { var folded: vec2; var xz: vec2; - let _e172 = (*uv_5); - p_1 = ((_e172 * 2f) - vec2(1f, 1f)); - let _e176 = p_1[0u]; - let _e180 = p_1[1u]; - y_1 = ((1f - abs(_e176)) - abs(_e180)); - let _e184 = p_1[0u]; - let _e188 = p_1[1u]; - s_5 = vec2(select(-1f, 1f, (_e184 >= 0f)), select(-1f, 1f, (_e188 >= 0f))); - let _e192 = p_1; - let _e196 = s_5; - folded = ((vec2(1f, 1f) - abs(_e192.yx)) * _e196); - let _e198 = y_1; - let _e200 = p_1; - let _e201 = folded; - xz = select(_e201, _e200, vec2((_e198 >= 0f))); - let _e205 = xz[0u]; - let _e206 = y_1; - let _e208 = xz[1u]; - return normalize(vec3(_e205, _e206, _e208)); + let _e173 = (*uv_5); + p_1 = ((_e173 * 2f) - vec2(1f, 1f)); + let _e177 = p_1[0u]; + let _e181 = p_1[1u]; + y_1 = ((1f - abs(_e177)) - abs(_e181)); + let _e185 = p_1[0u]; + let _e189 = p_1[1u]; + s_5 = vec2(select(-1f, 1f, (_e185 >= 0f)), select(-1f, 1f, (_e189 >= 0f))); + let _e193 = p_1; + let _e197 = s_5; + folded = ((vec2(1f, 1f) - abs(_e193.yx)) * _e197); + let _e199 = y_1; + let _e201 = p_1; + let _e202 = folded; + xz = select(_e202, _e201, vec2((_e199 >= 0f))); + let _e206 = xz[0u]; + let _e207 = y_1; + let _e209 = xz[1u]; + return normalize(vec3(_e206, _e207, _e209)); } fn ImpostorViewUv_u0028_vf2_u003b_vf3_u003b(cell_1: ptr>, offset_3: ptr>) -> vec2 { @@ -45295,27 +46140,27 @@ fn ImpostorViewUv_u0028_vf2_u003b_vf3_u003b(cell_1: ptr>, of var right: vec3; var param_109: vec3; var up_1: vec3; - var local_23: vec2; - - let _e174 = (*cell_1); - param_108 = (_e174 / vec2(7f)); - let _e177 = ImpostorDecode_u0028_vf2_u003b((¶m_108)); - d_5 = _e177; - let _e178 = d_5; - param_109 = _e178; - let _e179 = ImpostorRight_u0028_vf3_u003b((¶m_109)); - right = _e179; - let _e180 = d_5; - let _e181 = right; - up_1 = cross(_e180, _e181); - let _e183 = (*offset_3); - let _e184 = right; - let _e188 = (*offset_3); - let _e189 = up_1; - local_23 = vec2(((dot(_e183, _e184) * 0.5f) + 0.5f), (0.5f - (dot(_e188, _e189) * 0.5f))); - let _e194 = (*cell_1); - let _e195 = local_23; - return ((_e194 + clamp(_e195, vec2(0f, 0f), vec2(1f, 1f))) / vec2(8f)); + var local_24: vec2; + + let _e175 = (*cell_1); + param_108 = (_e175 / vec2(7f)); + let _e178 = ImpostorDecode_u0028_vf2_u003b((¶m_108)); + d_5 = _e178; + let _e179 = d_5; + param_109 = _e179; + let _e180 = ImpostorRight_u0028_vf3_u003b((¶m_109)); + right = _e180; + let _e181 = d_5; + let _e182 = right; + up_1 = cross(_e181, _e182); + let _e184 = (*offset_3); + let _e185 = right; + let _e189 = (*offset_3); + let _e190 = up_1; + local_24 = vec2(((dot(_e184, _e185) * 0.5f) + 0.5f), (0.5f - (dot(_e189, _e190) * 0.5f))); + let _e195 = (*cell_1); + let _e196 = local_24; + return ((_e195 + clamp(_e196, vec2(0f, 0f), vec2(1f, 1f))) / vec2(8f)); } fn ImpostorEncode_u0028_vf3_u003b(d_6: ptr>) -> vec2 { @@ -45324,28 +46169,28 @@ fn ImpostorEncode_u0028_vf3_u003b(d_6: ptr>) -> vec2 { var s_6: vec2; var folded_1: vec2; - let _e171 = (*d_6); - a_4 = abs(_e171); - let _e173 = (*d_6); - let _e176 = a_4[0u]; - let _e178 = a_4[1u]; - let _e181 = a_4[2u]; - p_2 = (_e173.xz / vec2(max(((_e176 + _e178) + _e181), 0.00000001f))); - let _e187 = p_2[0u]; - let _e191 = p_2[1u]; - s_6 = vec2(select(-1f, 1f, (_e187 >= 0f)), select(-1f, 1f, (_e191 >= 0f))); - let _e195 = p_2; - let _e199 = s_6; - folded_1 = ((vec2(1f, 1f) - abs(_e195.yx)) * _e199); - let _e202 = (*d_6)[1u]; - let _e204 = p_2; - let _e205 = folded_1; - return ((select(_e205, _e204, vec2((_e202 >= 0f))) * 0.5f) + vec2(0.5f, 0.5f)); + let _e172 = (*d_6); + a_4 = abs(_e172); + let _e174 = (*d_6); + let _e177 = a_4[0u]; + let _e179 = a_4[1u]; + let _e182 = a_4[2u]; + p_2 = (_e174.xz / vec2(max(((_e177 + _e179) + _e182), 0.00000001f))); + let _e188 = p_2[0u]; + let _e192 = p_2[1u]; + s_6 = vec2(select(-1f, 1f, (_e188 >= 0f)), select(-1f, 1f, (_e192 >= 0f))); + let _e196 = p_2; + let _e200 = s_6; + folded_1 = ((vec2(1f, 1f) - abs(_e196.yx)) * _e200); + let _e203 = (*d_6)[1u]; + let _e205 = p_2; + let _e206 = folded_1; + return ((select(_e206, _e205, vec2((_e203 >= 0f))) * 0.5f) + vec2(0.5f, 0.5f)); } fn MaterialLodBias_u0028_() -> f32 { - let _e168 = frag_info.target_origin[1u]; - return _e168; + let _e169 = frag_info.target_origin[1u]; + return _e169; } fn ReadSurface_u0028_() -> Surface { @@ -45357,78 +46202,78 @@ fn ReadSurface_u0028_() -> Surface { var anchored: vec3; var noise_1: f32; - let _e173 = v_texcoord_1; - let _e174 = MaterialLodBias_u0028_(); - let _e175 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e173, _e174); - texel_4 = _e175; - let _e176 = texel_4; - param_110 = _e176.xyz; - let _e178 = SrgbToLinear_u0028_vf3_u003b((¶m_110)); - let _e180 = frag_info.base_color; - param_111 = _e180.xyz; - let _e182 = SrgbToLinear_u0028_vf3_u003b((¶m_111)); - let _e184 = v_color_1; - s_7.albedo = ((_e178 * _e182) * _e184.xyz); - let _e189 = texel_4[3u]; - let _e192 = frag_info.base_color[3u]; - let _e195 = v_color_1[3u]; - s_7.alpha = ((_e189 * _e192) * _e195); - let _e199 = s_7.albedo; - g_albedo = _e199; - let _e202 = frag_info.material2_[0u]; - cutoff = _e202; - let _e203 = cutoff; - if (_e203 >= 0f) { - let _e206 = s_7.alpha; - let _e207 = cutoff; - if (_e206 < _e207) { + let _e174 = v_texcoord_1; + let _e175 = MaterialLodBias_u0028_(); + let _e176 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e174, _e175); + texel_4 = _e176; + let _e177 = texel_4; + param_110 = _e177.xyz; + let _e179 = SrgbToLinear_u0028_vf3_u003b((¶m_110)); + let _e181 = frag_info.base_color; + param_111 = _e181.xyz; + let _e183 = SrgbToLinear_u0028_vf3_u003b((¶m_111)); + let _e185 = v_color_1; + s_7.albedo = ((_e179 * _e183) * _e185.xyz); + let _e190 = texel_4[3u]; + let _e193 = frag_info.base_color[3u]; + let _e196 = v_color_1[3u]; + s_7.alpha = ((_e190 * _e193) * _e196); + let _e200 = s_7.albedo; + g_albedo = _e200; + let _e203 = frag_info.material2_[0u]; + cutoff = _e203; + let _e204 = cutoff; + if (_e204 >= 0f) { + let _e207 = s_7.alpha; + let _e208 = cutoff; + if (_e207 < _e208) { discard; } } else { - let _e209 = cutoff; - if (_e209 < -1.5f) { - let _e211 = v_world_position_1; - anchored = floor((_e211 * 16f)); - let _e214 = anchored; - noise_1 = fract((sin(dot(_e214, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e220 = s_7.alpha; - let _e221 = noise_1; - if (_e220 < _e221) { + let _e210 = cutoff; + if (_e210 < -1.5f) { + let _e212 = v_world_position_1; + anchored = floor((_e212 * 16f)); + let _e215 = anchored; + noise_1 = fract((sin(dot(_e215, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e221 = s_7.alpha; + let _e222 = noise_1; + if (_e221 < _e222) { discard; } } } - let _e223 = cutoff; - let _e225 = cutoff; - g_premultiply = ((_e223 < 0f) && (_e225 > -0.75f)); - let _e228 = v_normal_1; - s_7.n = normalize(_e228); - let _e231 = gl_FrontFacing_1; - if !(_e231) { - let _e234 = s_7.n; - s_7.n = -(_e234); - } - let _e238 = frag_info.camera_position; - let _e240 = v_world_position_1; - s_7.v = normalize((_e238.xyz - _e240)); - let _e245 = s_7.n; - let _e247 = s_7.v; - s_7.n_dot_v = max(dot(_e245, _e247), 0.0001f); - let _e253 = frag_info.material[0u]; - s_7.metallic = clamp(_e253, 0f, 1f); - let _e258 = frag_info.material[1u]; - s_7.roughness = clamp(_e258, 0.02f, 1f); - let _e262 = frag_info.ambient_ground; - let _e265 = frag_info.ambient_sky; - let _e269 = s_7.n[1u]; - let _e276 = frag_info.material[2u]; - s_7.ambient = (mix(_e262.xyz, _e265.xyz, vec3(((_e269 * 0.5f) + 0.5f))) * _e276); - let _e281 = frag_info.frame_params[0u]; - s_7.exposure = max(_e281, 0f); + let _e224 = cutoff; + let _e226 = cutoff; + g_premultiply = ((_e224 < 0f) && (_e226 > -0.75f)); + let _e229 = v_normal_1; + s_7.n = normalize(_e229); + let _e232 = gl_FrontFacing_1; + if !(_e232) { + let _e235 = s_7.n; + s_7.n = -(_e235); + } + let _e239 = frag_info.camera_position; + let _e241 = v_world_position_1; + s_7.v = normalize((_e239.xyz - _e241)); + let _e246 = s_7.n; + let _e248 = s_7.v; + s_7.n_dot_v = max(dot(_e246, _e248), 0.0001f); + let _e254 = frag_info.material[0u]; + s_7.metallic = clamp(_e254, 0f, 1f); + let _e259 = frag_info.material[1u]; + s_7.roughness = clamp(_e259, 0.02f, 1f); + let _e263 = frag_info.ambient_ground; + let _e266 = frag_info.ambient_sky; + let _e270 = s_7.n[1u]; + let _e277 = frag_info.material[2u]; + s_7.ambient = (mix(_e263.xyz, _e266.xyz, vec3(((_e270 * 0.5f) + 0.5f))) * _e277); + let _e282 = frag_info.frame_params[0u]; + s_7.exposure = max(_e282, 0f); s_7.occlusion = 1f; s_7.emissive = vec3(0f, 0f, 0f); - let _e286 = s_7; - return _e286; + let _e287 = s_7; + return _e287; } fn main_1() { @@ -45444,11 +46289,11 @@ fn main_1() { var f: vec2; var lower: bool; var c0_: vec2; - var local_24: vec2; + var local_25: vec2; var c1_: vec2; var c2_: vec2; var w: vec3; - var local_25: vec3; + var local_26: vec3; var uv0_: vec2; var param_114: vec2; var param_115: vec3; @@ -45467,7 +46312,7 @@ fn main_1() { var wa: vec3; var alpha_3: f32; var srgb_1: vec3; - var local_26: vec3; + var local_27: vec3; var worldRight: vec3; var worldFacing: vec3; var worldUp: vec3; @@ -45485,176 +46330,176 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e212 = ReadSurface_u0028_(); - s_8 = _e212; - let _e213 = v_color_1; - d_7 = normalize(_e213.xyz); - let _e216 = d_7; - param_112 = _e216; - let _e217 = ImpostorRight_u0028_vf3_u003b((¶m_112)); - right_1 = _e217; - let _e218 = d_7; - let _e219 = right_1; - up_2 = cross(_e218, _e219); - let _e221 = right_1; - let _e223 = v_texcoord_1[0u]; - let _e227 = up_2; - let _e229 = v_texcoord_1[1u]; - offset_4 = ((_e221 * ((_e223 * 2f) - 1f)) + (_e227 * (1f - (_e229 * 2f)))); - let _e234 = d_7; - param_113 = _e234; - let _e235 = ImpostorEncode_u0028_vf3_u003b((¶m_113)); - g = (_e235 * 7f); - let _e237 = g; - base = clamp(floor(_e237), vec2(0f, 0f), vec2(6f, 6f)); - let _e240 = g; - let _e241 = base; - f = (_e240 - _e241); - let _e244 = f[0u]; - let _e246 = f[1u]; - lower = ((_e244 + _e246) < 1f); - let _e249 = lower; - if _e249 { - let _e250 = base; - local_24 = _e250; - } else { + let _e213 = ReadSurface_u0028_(); + s_8 = _e213; + let _e214 = v_color_1; + d_7 = normalize(_e214.xyz); + let _e217 = d_7; + param_112 = _e217; + let _e218 = ImpostorRight_u0028_vf3_u003b((¶m_112)); + right_1 = _e218; + let _e219 = d_7; + let _e220 = right_1; + up_2 = cross(_e219, _e220); + let _e222 = right_1; + let _e224 = v_texcoord_1[0u]; + let _e228 = up_2; + let _e230 = v_texcoord_1[1u]; + offset_4 = ((_e222 * ((_e224 * 2f) - 1f)) + (_e228 * (1f - (_e230 * 2f)))); + let _e235 = d_7; + param_113 = _e235; + let _e236 = ImpostorEncode_u0028_vf3_u003b((¶m_113)); + g = (_e236 * 7f); + let _e238 = g; + base = clamp(floor(_e238), vec2(0f, 0f), vec2(6f, 6f)); + let _e241 = g; + let _e242 = base; + f = (_e241 - _e242); + let _e245 = f[0u]; + let _e247 = f[1u]; + lower = ((_e245 + _e247) < 1f); + let _e250 = lower; + if _e250 { let _e251 = base; - local_24 = (_e251 + vec2(1f, 1f)); - } - let _e253 = local_24; - c0_ = _e253; - let _e254 = base; - c1_ = (_e254 + vec2(1f, 0f)); - let _e256 = base; - c2_ = (_e256 + vec2(0f, 1f)); - let _e258 = lower; - if _e258 { - let _e260 = f[0u]; - let _e263 = f[1u]; - let _e266 = f[0u]; - let _e268 = f[1u]; - local_25 = vec3(((1f - _e260) - _e263), _e266, _e268); + local_25 = _e251; } else { - let _e271 = f[0u]; - let _e273 = f[1u]; - let _e277 = f[1u]; - let _e280 = f[0u]; - local_25 = vec3(((_e271 + _e273) - 1f), (1f - _e277), (1f - _e280)); - } - let _e283 = local_25; - w = _e283; - let _e284 = c0_; - param_114 = _e284; - let _e285 = offset_4; - param_115 = _e285; - let _e286 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_114), (¶m_115)); - uv0_ = _e286; - let _e287 = c1_; - param_116 = _e287; - let _e288 = offset_4; - param_117 = _e288; - let _e289 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_116), (¶m_117)); - uv1_ = _e289; - let _e290 = c2_; - param_118 = _e290; - let _e291 = offset_4; - param_119 = _e291; - let _e292 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_118), (¶m_119)); - uv2_ = _e292; - let _e293 = uv0_; - let _e294 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e293, 0f); - a0_ = _e294; - let _e295 = uv1_; - let _e296 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e295, 0f); - a1_ = _e296; - let _e297 = uv2_; - let _e298 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e297, 0f); - a2_ = _e298; - let _e299 = uv0_; - let _e300 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e299, 0f); - n0_ = _e300; - let _e301 = uv1_; - let _e302 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e301, 0f); - n1_ = _e302; - let _e303 = uv2_; - let _e304 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e303, 0f); - n2_ = _e304; - let _e305 = w; - let _e307 = a0_[3u]; - let _e309 = a1_[3u]; - let _e311 = a2_[3u]; - wa = (_e305 * vec3(_e307, _e309, _e311)); - let _e315 = wa[0u]; - let _e317 = wa[1u]; - let _e320 = wa[2u]; - alpha_3 = ((_e315 + _e317) + _e320); - let _e322 = alpha_3; - if (_e322 < 0.5f) { + let _e252 = base; + local_25 = (_e252 + vec2(1f, 1f)); + } + let _e254 = local_25; + c0_ = _e254; + let _e255 = base; + c1_ = (_e255 + vec2(1f, 0f)); + let _e257 = base; + c2_ = (_e257 + vec2(0f, 1f)); + let _e259 = lower; + if _e259 { + let _e261 = f[0u]; + let _e264 = f[1u]; + let _e267 = f[0u]; + let _e269 = f[1u]; + local_26 = vec3(((1f - _e261) - _e264), _e267, _e269); + } else { + let _e272 = f[0u]; + let _e274 = f[1u]; + let _e278 = f[1u]; + let _e281 = f[0u]; + local_26 = vec3(((_e272 + _e274) - 1f), (1f - _e278), (1f - _e281)); + } + let _e284 = local_26; + w = _e284; + let _e285 = c0_; + param_114 = _e285; + let _e286 = offset_4; + param_115 = _e286; + let _e287 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_114), (¶m_115)); + uv0_ = _e287; + let _e288 = c1_; + param_116 = _e288; + let _e289 = offset_4; + param_117 = _e289; + let _e290 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_116), (¶m_117)); + uv1_ = _e290; + let _e291 = c2_; + param_118 = _e291; + let _e292 = offset_4; + param_119 = _e292; + let _e293 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_118), (¶m_119)); + uv2_ = _e293; + let _e294 = uv0_; + let _e295 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e294, 0f); + a0_ = _e295; + let _e296 = uv1_; + let _e297 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e296, 0f); + a1_ = _e297; + let _e298 = uv2_; + let _e299 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e298, 0f); + a2_ = _e299; + let _e300 = uv0_; + let _e301 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e300, 0f); + n0_ = _e301; + let _e302 = uv1_; + let _e303 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e302, 0f); + n1_ = _e303; + let _e304 = uv2_; + let _e305 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e304, 0f); + n2_ = _e305; + let _e306 = w; + let _e308 = a0_[3u]; + let _e310 = a1_[3u]; + let _e312 = a2_[3u]; + wa = (_e306 * vec3(_e308, _e310, _e312)); + let _e316 = wa[0u]; + let _e318 = wa[1u]; + let _e321 = wa[2u]; + alpha_3 = ((_e316 + _e318) + _e321); + let _e323 = alpha_3; + if (_e323 < 0.5f) { discard; } - let _e324 = a0_; - let _e327 = wa[0u]; - let _e329 = a1_; - let _e332 = wa[1u]; - let _e335 = a2_; - let _e338 = wa[2u]; - let _e341 = alpha_3; - srgb_1 = ((((_e324.xyz * _e327) + (_e329.xyz * _e332)) + (_e335.xyz * _e338)) / vec3(_e341)); - let _e344 = n0_; - let _e349 = wa[0u]; - let _e351 = n1_; - let _e356 = wa[1u]; - let _e359 = n2_; - let _e364 = wa[2u]; - local_26 = (((((_e344.xyz * 2f) - vec3(1f, 1f, 1f)) * _e349) + (((_e351.xyz * 2f) - vec3(1f, 1f, 1f)) * _e356)) + (((_e359.xyz * 2f) - vec3(1f, 1f, 1f)) * _e364)); - let _e367 = local_26; - let _e368 = local_26; - let _e371 = local_26; - let _e372 = d_7; - local_26 = normalize(select(_e372, _e371, vec3((dot(_e367, _e368) > 0.000000000001f)))); - let _e376 = v_tangent_1; - worldRight = normalize(_e376.xyz); - let _e379 = v_normal_1; - worldFacing = normalize(_e379); - let _e381 = worldFacing; - let _e382 = worldRight; - worldUp = cross(_e381, _e382); - let _e384 = worldRight; - let _e385 = local_26; - let _e386 = right_1; - let _e389 = worldUp; - let _e390 = local_26; - let _e391 = up_2; - let _e395 = worldFacing; - let _e396 = local_26; - let _e397 = d_7; - s_8.n = normalize((((_e384 * dot(_e385, _e386)) + (_e389 * dot(_e390, _e391))) + (_e395 * dot(_e396, _e397)))); - let _e404 = s_8.n; - let _e406 = s_8.v; - s_8.n_dot_v = max(dot(_e404, _e406), 0.0001f); - let _e410 = srgb_1; - param_120 = _e410; - let _e411 = SrgbToLinear_u0028_vf3_u003b((¶m_120)); - let _e413 = frag_info.base_color; - param_121 = _e413.xyz; - let _e415 = SrgbToLinear_u0028_vf3_u003b((¶m_121)); - s_8.albedo = (_e411 * _e415); + let _e325 = a0_; + let _e328 = wa[0u]; + let _e330 = a1_; + let _e333 = wa[1u]; + let _e336 = a2_; + let _e339 = wa[2u]; + let _e342 = alpha_3; + srgb_1 = ((((_e325.xyz * _e328) + (_e330.xyz * _e333)) + (_e336.xyz * _e339)) / vec3(_e342)); + let _e345 = n0_; + let _e350 = wa[0u]; + let _e352 = n1_; + let _e357 = wa[1u]; + let _e360 = n2_; + let _e365 = wa[2u]; + local_27 = (((((_e345.xyz * 2f) - vec3(1f, 1f, 1f)) * _e350) + (((_e352.xyz * 2f) - vec3(1f, 1f, 1f)) * _e357)) + (((_e360.xyz * 2f) - vec3(1f, 1f, 1f)) * _e365)); + let _e368 = local_27; + let _e369 = local_27; + let _e372 = local_27; + let _e373 = d_7; + local_27 = normalize(select(_e373, _e372, vec3((dot(_e368, _e369) > 0.000000000001f)))); + let _e377 = v_tangent_1; + worldRight = normalize(_e377.xyz); + let _e380 = v_normal_1; + worldFacing = normalize(_e380); + let _e382 = worldFacing; + let _e383 = worldRight; + worldUp = cross(_e382, _e383); + let _e385 = worldRight; + let _e386 = local_27; + let _e387 = right_1; + let _e390 = worldUp; + let _e391 = local_27; + let _e392 = up_2; + let _e396 = worldFacing; + let _e397 = local_27; + let _e398 = d_7; + s_8.n = normalize((((_e385 * dot(_e386, _e387)) + (_e390 * dot(_e391, _e392))) + (_e396 * dot(_e397, _e398)))); + let _e405 = s_8.n; + let _e407 = s_8.v; + s_8.n_dot_v = max(dot(_e405, _e407), 0.0001f); + let _e411 = srgb_1; + param_120 = _e411; + let _e412 = SrgbToLinear_u0028_vf3_u003b((¶m_120)); + let _e414 = frag_info.base_color; + param_121 = _e414.xyz; + let _e416 = SrgbToLinear_u0028_vf3_u003b((¶m_121)); + s_8.albedo = (_e412 * _e416); s_8.alpha = 1f; - let _e420 = s_8.albedo; - g_albedo = _e420; - let _e422 = frag_info.ambient_ground; - let _e425 = frag_info.ambient_sky; - let _e429 = s_8.n[1u]; - let _e436 = frag_info.material[2u]; - s_8.ambient = (mix(_e422.xyz, _e425.xyz, vec3(((_e429 * 0.5f) + 0.5f))) * _e436); - let _e440 = s_8.albedo; - let _e442 = s_8.ambient; - ambient = (_e440 * _e442); - let _e444 = s_8; - param_122 = _e444; - let _e445 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_122)); - let _e446 = ambient; - param_123 = (_e445 + _e446); + let _e421 = s_8.albedo; + g_albedo = _e421; + let _e423 = frag_info.ambient_ground; + let _e426 = frag_info.ambient_sky; + let _e430 = s_8.n[1u]; + let _e437 = frag_info.material[2u]; + s_8.ambient = (mix(_e423.xyz, _e426.xyz, vec3(((_e430 * 0.5f) + 0.5f))) * _e437); + let _e441 = s_8.albedo; + let _e443 = s_8.ambient; + ambient = (_e441 * _e443); + let _e445 = s_8; + param_122 = _e445; + let _e446 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_122)); + let _e447 = ambient; + param_123 = (_e446 + _e447); param_124 = 1f; param_125 = 1f; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_123), (¶m_124), (¶m_125)); diff --git a/packages/flutter3d_webgpu/lib/src/glsl_to_wgsl.dart b/packages/flutter3d_webgpu/lib/src/glsl_to_wgsl.dart index d4cb9c576..2674cb490 100644 --- a/packages/flutter3d_webgpu/lib/src/glsl_to_wgsl.dart +++ b/packages/flutter3d_webgpu/lib/src/glsl_to_wgsl.dart @@ -199,16 +199,16 @@ Set scanVaryings( /// /// ## Why not simply sort every name in the manifest /// -/// Because there are seventeen of them and a location must be below +/// Because there are twenty-four of them and a location must be below /// `maxInterStageShaderVariables`, which is sixteen. One flat numbering runs -/// out by one name. +/// out eight names short. /// /// Two names only ever have to agree when some stage declares both — that is /// what makes them share a pipeline. So the names are grouped into families by /// exactly that relation and each family is numbered from zero: the mesh /// varyings and the sky varyings never meet in one stage, and there is no -/// pipeline in which their numbers could collide. Four families come out of the -/// manifest today, the largest with eight names in it. +/// pipeline in which their numbers could collide. Five families come out of the +/// manifest today, the largest, the sky's, with twelve names in it. /// /// Deterministic throughout — every list is sorted before it is numbered — /// because `tool/ci.sh` regenerates the shader table and diffs it, and a diff --git a/packages/flutter3d_webgpu/test/cross_backend_test.dart b/packages/flutter3d_webgpu/test/cross_backend_test.dart index ee081dcdb..cc9992a5c 100644 --- a/packages/flutter3d_webgpu/test/cross_backend_test.dart +++ b/packages/flutter3d_webgpu/test/cross_backend_test.dart @@ -201,6 +201,9 @@ const Map _budgets = { 'sheen-fabric': 0.01, // 0.000% measured. It was 2.039%, and not sampling: the puff's hash used // 64-bit integers, so a browser baked a different sheet. + // `P5`. 0 of 172800 measured against Impeller on 2026-10-01, both. + 'sky-physical-dusk': 0.01, + 'sky-physical-night': 0.01, 'smoke-six-way': 0.01, 'splat-gltf': 0.01, // Was 40.38, over a reference that was a black frame: the hashed splats' diff --git a/packages/flutter3d_webgpu/test/goldens/sky-physical-dusk.png b/packages/flutter3d_webgpu/test/goldens/sky-physical-dusk.png new file mode 100644 index 0000000000000000000000000000000000000000..85006fcdb4252ecf9bcc4f954964358e48147bc8 GIT binary patch literal 89667 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 literal 0 HcmV?d00001 diff --git a/packages/flutter3d_webgpu/test/goldens/sky-physical-night.png b/packages/flutter3d_webgpu/test/goldens/sky-physical-night.png new file mode 100644 index 0000000000000000000000000000000000000000..8ea20ac80d577765c214b5060d72da7d1064bdee GIT binary patch literal 29084 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 Nx=(fLp1Az?{{TCGngRd- literal 0 HcmV?d00001 diff --git a/packages/flutter3d_webgpu/test/open_test.dart b/packages/flutter3d_webgpu/test/open_test.dart index d3a34f500..6960496ed 100644 --- a/packages/flutter3d_webgpu/test/open_test.dart +++ b/packages/flutter3d_webgpu/test/open_test.dart @@ -39,6 +39,7 @@ const List<(String, String)> _pairs = <(String, String)>[ ('FullscreenVertex', 'Ssao'), ('SkyVertex', 'Sky'), ('SkyCubeVertex', 'SkyCube'), + ('SkyPhysicalVertex', 'SkyPhysical'), ('ParticleVertex', 'ParticleTextured'), ('DebugLineVertex', 'DebugLine'), ]; diff --git a/packages/flutter3d_webgpu/test/wgsl_pipeline_test.dart b/packages/flutter3d_webgpu/test/wgsl_pipeline_test.dart index 0c0470050..26df4a78a 100644 --- a/packages/flutter3d_webgpu/test/wgsl_pipeline_test.dart +++ b/packages/flutter3d_webgpu/test/wgsl_pipeline_test.dart @@ -139,11 +139,13 @@ void main() { ); }); } - // The twenty names the manifest has today: `R1`'s velocity stages - // brought v_current, v_previous and v_depth, a family of their own. - // Stated so that a shader adding a twenty-first has to come back and + // The twenty-four names the manifest has today: `R1`'s velocity stages + // brought v_current, v_previous and v_depth, a family of their own, and + // the physical sky brought v_rayleigh, v_mie, v_planet and v_stars into + // the sky's family, through the v_ray, v_sun and v_disc it shares. + // Stated so that a shader adding a twenty-fifth has to come back and // read the family rule. - expect(seen.length, 20); + expect(seen.length, 24); }); test('keeps every family inside WebGPU\'s sixteen locations', () { @@ -153,10 +155,10 @@ void main() { ), isTrue, ); - // Five families and a widest of eight. One flat numbering would have - // needed twenty. - expect(locations.length, 20); - expect(locations.values.toSet().length, 8); + // Five families and a widest of twelve, the sky's. One flat numbering + // would have needed twenty-four. + expect(locations.length, 24); + expect(locations.values.toSet().length, 12); }); test('gives every stage its own bind group', () { diff --git a/site/content/core/architecture.md b/site/content/core/architecture.md index 0412274b4..a3d0a7ab0 100644 --- a/site/content/core/architecture.md +++ b/site/content/core/architecture.md @@ -53,7 +53,7 @@ Each exists for a different reason, and none of them is a fallback for another. Its shaders are GLSL ES 3.00 generated from `flutter3d_shaders`. Nothing checked that the generated file was current for a long time, and it cost two failures that reported nothing: a uniform member the browser's copy had never heard of, and a sky shader from before the sky was rewritten. `tool/ci.sh` regenerates it and fails on the diff now, and the compiled Impeller bundle, which cannot be diffed, is held by a freshness rule instead. -**`flutter3d_cpu`** is the one that makes the agreement mean something. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. It is also what makes 84 golden scenes checkable in a headless run, and what caught three bugs that every simulation test passed. +**`flutter3d_cpu`** is the one that makes the agreement mean something. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. It is also what makes 86 golden scenes checkable in a headless run, and what caught three bugs that every simulation test passed. **`flutter3d_webgpu`** asks a question none of the other three could: whether the *shader* half of the contract is a seam. The first three all read one text (`impellerc` compiles the GLSL, the WebGL generator translates it, the software rasteriser transcribes it into Dart by hand), and WebGPU cannot, because WGSL is a different language and a browser will not take SPIR-V. So its table is generated down a second toolchain, the same manifest through `glslangValidator` and then `naga`, and all thirty-nine stages come out. What it declines it declines by name: no blend constant (WebGPU has no colour/alpha pair for one) and no wireframe (no polygon fill mode in the API at all). Two more used to be on that list. Rendering into a mip is on now and cost no code: a reflection probe convolves its own chain, and a face and a level are `baseArrayLayer` and `baseMipLevel` on an ordinary view here. Block compression is on too, and what it cost was the asking: a WebGPU device gets exactly the features it requested, and requesting one the adapter does not carry rejects the device instead of handing back a lesser one, so the adapter is asked, the intersection is requested, and the capability answers from what was granted. @@ -62,7 +62,7 @@ A browser build opens WebGL2 unless it says otherwise. That is a decision about Any new backend has to pass `flutter3d_conformance` before it counts as one. ### What the HAL actually names diff --git a/site/content/core/backends.md b/site/content/core/backends.md index d5d26929c..8d6e50f7f 100644 --- a/site/content/core/backends.md +++ b/site/content/core/backends.md @@ -20,7 +20,7 @@ What could **not** be written from the contract is the shaders. That limit is re
  • The semantics that are part of the contract and appear in no signature
  • What the 0.8 members ask, compute among them, and which backend answers yes
  • The conformance suite, and how to run it before you have a single shader
  • -
  • The ninety shader entry points your bundle must answer to
  • +
  • The ninety-two shader entry points your bundle must answer to
  • @@ -404,11 +404,13 @@ Eighty-five entry points. `kRequiredShaders` and the bundle manifest are kept in |---|---| | Vertex | `MeshVertex`, `MeshSkinnedVertex`, `MeshInstancedVertex`, `MeshLightmappedVertex`, `FullscreenVertex`, `DebugLineVertex`, `ParticleVertex`, `ParticleMeshVertex`, `PolylineVertex`, `ShadowTileResetVertex`, `SkyVertex`, `SkyCubeVertex`, `ImpostorVertex`, `VelocityVertex`, `VelocitySkinnedVertex`, `VelocityInstancedVertex` | | Lighting | `Unlit`, `Lambert`, `BlinnPhong`, `Pbr`, `Toon`, `Normals`, `PbrLayered`, `Impostor`, `PlanarReflection` | +| Vertex | `MeshVertex`, `MeshSkinnedVertex`, `MeshInstancedVertex`, `MeshLightmappedVertex`, `FullscreenVertex`, `DebugLineVertex`, `ParticleVertex`, `ParticleMeshVertex`, `PolylineVertex`, `ShadowTileResetVertex`, `SkyVertex`, `SkyCubeVertex`, `SkyPhysicalVertex`, `ImpostorVertex`, `VelocityVertex`, `VelocitySkinnedVertex`, `VelocityInstancedVertex` | +| Lighting | `Unlit`, `Lambert`, `BlinnPhong`, `Pbr`, `Toon`, `Normals`, `PbrLayered`, `Impostor` | | Shadows | `ShadowDepth`, `ShadowDistance`, `ShadowDepthMasked`, `ShadowDistanceMasked`, `ShadowTileReset`, `EvsmFilter`, `ShadowCopy` | | Post | `BloomThreshold`, `BloomDownsample`, `BloomUpsample`, `Composite`, `Fxaa`, `SmaaEdges`, `SmaaWeights`, `SmaaBlend`, `LensFlare`, `Reflections`, `Ssao`, `SsaoBlur`, `ContactShadow`, `ContactShadowResolve`, `Splat`, `LightShafts`, `DepthOfField`, `ViewportShade`, `Luminance`, `ProbePrefilter`, `CameraVelocity`, `Velocity`, `VelocityTileMax`, `VelocityNeighborMax`, `MotionBlur`, `TemporalResolve`, `TemporalAccumulate`, `Reactive`, `Easu`, `LocalExposure`, `LocalExposureBlur`, `VolumetricFog`, `VolumetricFogUpsample`, `DepthPyramid`, `SceneColourCopy`, `WboitResolve`, `DofTileMax`, `Decal` | | Post | `BloomThreshold`, `BloomDownsample`, `BloomUpsample`, `Composite`, `Fxaa`, `Reflections`, `Ssao`, `SsaoBlur`, `ContactShadow`, `ContactShadowResolve`, `Splat`, `LightShafts`, `DepthOfField`, `ViewportShade`, `Luminance`, `ProbePrefilter`, `CameraVelocity`, `Velocity`, `VelocityTileMax`, `VelocityNeighborMax`, `MotionBlur`, `TemporalResolve`, `TemporalAccumulate`, `Reactive`, `Easu`, `LocalExposure`, `LocalExposureBlur`, `VolumetricFog`, `VolumetricFogUpsample`, `DepthPyramid`, `SceneColourCopy`, `WboitResolve`, `DofTileMax`, `RenderTextureEncode` | | Particles | `Particle`, `ParticleTextured`, `ParticleMesh`, `ParticleSixWay`, `ReactiveSprite`, `SplatHashed`, `ParticleSoft`, `ParticleTexturedSoft`, `ParticleSixWaySoft` | -| Sky | `Sky`, `SkyCube` | +| Sky | `Sky`, `SkyCube`, `SkyPhysical` | | Debug | `DebugLine`, `MrtProbe`, `ObjectId`, `Xray` | | Probes | `VertexTextureProbeVertex`, `VertexTextureProbe`, `IrradianceConvolve`, `FieldDecay` | diff --git a/site/content/core/index.md b/site/content/core/index.md index f77f8951f..38be00eab 100644 --- a/site/content/core/index.md +++ b/site/content/core/index.md @@ -25,7 +25,7 @@ Nothing in this section is genre knowledge. That property is what made the secon |---|---| | `flutter3d_impeller` | `flutter_gpu`, over Metal and Vulkan. The production one | | `flutter3d_webgl` | WebGL2 in the browser. Runs all three games at a fixed resolution and a lower frame rate, and is what an ordinary web build opens | -| `flutter3d_cpu` | Nothing: it rasterises in Dart, so 84 golden scenes stay checkable with no GPU in the room | +| `flutter3d_cpu` | Nothing: it rasterises in Dart, so 86 golden scenes stay checkable with no GPU in the room | | `flutter3d_webgpu` | WebGPU in a browser that has an adapter. Reached by `--dart-define=FLUTTER3D_WEBGPU=true`, because a build that can try it ships it | `flutter3d_conformance` is the suite each of them passed before it belonged in that table, and [Writing a HAL backend](/core/backends/) is the guide for writing the fifth. diff --git a/site/content/core/rendering.md b/site/content/core/rendering.md index 77a84f3df..17b9f8116 100644 --- a/site/content/core/rendering.md +++ b/site/content/core/rendering.md @@ -487,7 +487,7 @@ It is marched at half resolution and brought up by depth, so a halo behind a pil ```dart SkySettings( - enabled: true, // off by default: eighty-four goldens are recorded against none + enabled: true, // off by default: eighty-six goldens are recorded against none zenith: Vector3(0.10, 0.22, 0.52), horizon: Vector3(0.42, 0.50, 0.62), nadir: Vector3(0.06, 0.06, 0.07), // what fills the frame looking down at nothing diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 99df283e5..1b5b797a9 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10929 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-four scenes stay checkable in a headless run. +There are 10941 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/packages.md b/site/content/reference/packages.md index 4e801ee3a..de7be7587 100644 --- a/site/content/reference/packages.md +++ b/site/content/reference/packages.md @@ -60,7 +60,7 @@ A rasteriser written in Dart. `CpuDevice` implements the same HAL, plus PNG outp Not a fallback. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. -It is how eighty-four golden scenes are checkable with no GPU in the room, and it is a dev dependency of every game because three shipped bugs would have been caught by rendering a single frame in a test. It stopped being *only* a dev dependency once `flutter3d_app` started reaching for it as the runtime fallback when Impeller will not start, which makes it a real production path now, if a last-resort one. +It is how eighty-six golden scenes are checkable with no GPU in the room, and it is a dev dependency of every game because three shipped bugs would have been caught by rendering a single frame in a test. It stopped being *only* a dev dependency once `flutter3d_app` started reaching for it as the runtime fallback when Impeller will not start, which makes it a real production path now, if a last-resort one. ### `flutter3d_conformance` The suite any fourth backend would have to pass before it counted as one, plus the cross-backend comparison with per-scene budgets. diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 405dbf3ee..0bd032c50 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,14 +1,14 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10929 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10941 tests need a GPU. --- # Testing -10929 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10941 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| -| `flutter3d` | 1622 | | | | +| `flutter3d` | 1634 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | | `flutter3d_sim` | 591 | | `pad_input` | 67 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10903 rather than 10929: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10915 rather than 10941: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. @@ -52,7 +52,7 @@ tool/ci.sh # shaders, analyze, every test ## Four independent golden sets, not one -Eighty-four scenes are rendered four times: through Impeller, through the software rasteriser in `flutter3d_cpu`, through WebGL2, and through WebGPU, the last two in a driven browser. Each backend is held to zero differing pixels against its own set, with a per-channel tolerance of 8. +Eighty-six scenes are rendered four times: through Impeller, through the software rasteriser in `flutter3d_cpu`, through WebGL2, and through WebGPU, the last two in a driven browser. Each backend is held to zero differing pixels against its own set, with a per-channel tolerance of 8. The browser sets are recorded when a branch lands, not beside it (`golden_web.sh` holds one fixed port for the whole of its run), so a new scene is in the other sets for as long as that takes. Which scenes, and what they are waiting for, is `_provisional` in `flutter3d_webgl/test/cross_backend_test.dart`: the comparison is skipped with the reason printed instead of quietly missing, and the check beside it fails the moment a reference lands and the name is still there. @@ -65,7 +65,7 @@ The browser sets are recorded when a branch lands, not beside it (`golden_web.sh {{golden3 shadow-teapot | One scene, three sets: a GPU through Metal, a rasteriser written in Dart, and a browser. The pictures on this site are the Impeller set.}}
    -

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps eighty-four scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    +

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps eighty-six scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    `cross_backend_test.dart` compares them with per-scene budgets, and any new backend has to pass `flutter3d_conformance` before it counts as one. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index e8c030bfc..dccf6bc39 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -337,6 +337,56 @@ pays per view; a render texture has no post chain, no multisampling and no mips, and its aspect is the texture's; reflectors and render textures in the level format, the editor and the MCP tools; the comparison page row, with the 0.9.0 revision (decision 19). +| P5 | Physical sky (atmospheric scattering), stars, height fog | `sky_sources.dart` `PhysicalSkySource` | done, off by default; see below | M | +| P6 | Debug views (material channels, UV, NaN, split wipe) and render stats | `render/debug_view.dart`, `render_stats.dart` (0.24) | viewport shading, NaN scan in `sim_mcp` | S | +| P7 | Orthographic everywhere, per-draw ranges, alpha to coverage, render order | 0.24 commits | orthographic exists; check every effect | S | + +**P5. Physical sky, stars, height fog.** *Done:* `SkySettings.physical` +takes a `PhysicalSky` (molecular and haze scattering with their scale +heights, haze absorption and anisotropy, planet and air radii, the eye's +altitude, the sunlight entering the air, a ground albedo, and the stars' +brightness, density and grid), beside the gradient and the cube map, which +still wins; null, the default, draws the gradient. `sky_physical.frag` marches +single scattering per pixel, sixteen view samples spaced quadratically and +eight towards the sun from each, in kilometres, with the planet's shadow; the +disc is drawn white through the air in front of it, so it reddens and goes +out by itself; the ground below the horizon is lit by what the air leaves of +the sun; stars on a hashed cube grid (no sine, so every backend puts them in +the same cells) fade in from the sun six degrees up to eleven below. The air +travels on the vertices like the gradient's preset, same stride. +`SkySettings.sample` answers from the Dart copy (`PhysicalSky.look`) without +the stars, so `EnvironmentMap.fromSky` and the hemispheric ambient follow the +sun (the ambient is kept while the air and the sun do not change); +`PhysicalSky.sunlight` gives a directional light its colour. Height fog: +`FogSettings.heightFalloff` and `baseHeight`, integrated along the ray in +closed form in `ApplyFog` (density at the eye in `fog.w`, falloff in the +spare `eye.w`), nought being the flat fog to the bit; `Atmosphere` carries +and blends both. CPU mirrors `SkyPhysicalShader` (the stars' hash rounded to +single precision) and `applyFog`; `sky_physical_test.dart` (eleven claims: +off draws nothing, the software stage against the model in four directions, +noon blue and dusk red, the disc reddening, the ground lit and dark, stars by +night only, no stars in `sample`, the vertex depth, and for the fog the +density law, the closed form against a numerical integral, and nought being +flat); the `sky-physical-dusk` and `sky-physical-night` goldens in all four +sets, WebGPU at 0 of 172800 pixels from Impeller, WebGL at 11 and 0, the +software set at 0.451% (silhouettes and the floor's far edge, which Impeller +multisamples) and 0.028%. +*Left:* switching the physical sky and the height fog on by default: the +goldens now exist in all four sets, which decision 18 asks for first, but +`SkySettings.enabled`, `physical` and `heightFalloff` all still default to +off, and turning them on re-records every scene that draws a sky or fog, so +it is its own change; a precomputed scattering table (`PhysicalSky`'s +docstring says "not done yet"), which would make the sky a texture read on +phones instead of a 16 × 8 march per pixel; multiple scattering (the noon +horizon is pale cyan, not white); a moon; ozone; the camera's height moving +the sky; height fog on particles and splats, which fog as a flat fog at the +camera's density; an HDR environment from the sky (`fromSky` is eight bits, +so a bright horizon clips); the sky and fog in the +level format, the editor and the MCP tools; timing on the Galaxy A55; the +comparison page row, with the 0.9.0 revision (decision 19). The unit tests +were written and analysed but not run in the session that wrote them; run +`flutter test test/sky_physical_test.dart` in `packages/flutter3d` and the +whole suite before relying on this row. Already ours and not theirs, to keep: volumetric fog, weighted blended OIT, motion blur, local exposure and HDR out, EVSM, impostors, Hi-Z and software diff --git a/tool/ci.sh b/tool/ci.sh index e5fad087a..41d33239f 100755 --- a/tool/ci.sh +++ b/tool/ci.sh @@ -9,7 +9,7 @@ # # What it does NOT do, stated so the gap is not mistaken for coverage: # -# * The Impeller half of the golden set. Those eighty-four scenes render through +# * The Impeller half of the golden set. Those eighty-six scenes render through # flutter_gpu and need a real device, so they run from # packages/flutter3d/tool/golden.sh on a machine with a GPU. The software # half runs here, and cross_backend_test.dart compares the two committed From dfeb7c2071ba282cac8baa9c675ce5d5100354a0 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 04:59:17 +0300 Subject: [PATCH 034/141] A draw can read one part of its index buffer PassEncoder.draw takes firstIndex and indexCount, so a buffer holding a model's every part is bound once and drawn a part at a time. Leaving both out draws the whole binding, as before. A window past the end is refused with the same RangeError on all four backends by indexWindow, rather than left to drivers that answer it differently. Traces record the window only when there is one, and a conformance check holds the behaviour. --- ARCHITECTURE.md | 2 +- README.md | 3 +- packages/flutter3d_conformance/CHANGELOG.md | 4 + .../lib/flutter3d_conformance.dart | 10 +- .../lib/src/core_checks.dart | 2 +- .../lib/src/draw_checks.dart | 152 ++++++++++++++++++ .../flutter3d_cpu/lib/src/cpu_encoder.dart | 19 ++- .../flutter3d_cpu/test/conformance_test.dart | 2 +- packages/flutter3d_hardware/CHANGELOG.md | 11 ++ .../lib/src/command_encoder.dart | 45 +++++- .../lib/src/testing_fake_pass.dart | 37 ++++- .../lib/src/testing_recorded.dart | 11 +- .../lib/src/trace/recording_device.dart | 10 +- .../lib/src/trace/trace.dart | 8 +- .../lib/src/trace/trace_event.dart | 22 ++- .../test/index_window_test.dart | 51 ++++++ .../test/trace_codec_test.dart | 14 +- .../lib/src/gpu_command_encoder.dart | 54 +++++-- .../lib/src/webgl_encoder.dart | 19 ++- .../lib/src/webgpu_encoder.dart | 9 +- site/content/core/backends.md | 2 +- site/content/quickstart.md | 2 +- site/content/reference/packages.md | 2 +- site/content/reference/testing.md | 8 +- tool/webgpu_spike/lib/src/webgpu_interop.dart | 6 +- .../lib/src/webgpu_spike_encoder.dart | 9 +- 26 files changed, 460 insertions(+), 54 deletions(-) create mode 100644 packages/flutter3d_hardware/test/index_window_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b1d9ce995..515a560c0 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10941 tests** across 43 packages and 10 applications | +| Unit tests | **10948 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index c105fdab4..bd6380685 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,8 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10941 tests across forty-three packages and nine applications. The +There are 10948 tests across forty-three packages and nine applications. The +There are 10887 tests across forty-two packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes There are 10895 tests across forty-two packages and nine applications. The diff --git a/packages/flutter3d_conformance/CHANGELOG.md b/packages/flutter3d_conformance/CHANGELOG.md index 8712b60e0..4580fc2b9 100644 --- a/packages/flutter3d_conformance/CHANGELOG.md +++ b/packages/flutter3d_conformance/CHANGELOG.md @@ -9,6 +9,10 @@ backend whose bundle lacks either fails the check rather than the first frame with a reflector or a render texture in it. **The linking check pairs `SkyPhysicalVertex` with `SkyPhysical`**, whose seven varyings are new on both sides. +**A window of the index buffer draws that window.** A new shader check draws +a window over the second of two triangles in one index buffer, then over the +first, then with no count, and asks that a window past the end of the binding +is refused with a `RangeError`. Thirty-two shader checks, forty-two in all. ## 0.8.1 diff --git a/packages/flutter3d_conformance/lib/flutter3d_conformance.dart b/packages/flutter3d_conformance/lib/flutter3d_conformance.dart index d595d5a37..073939e45 100644 --- a/packages/flutter3d_conformance/lib/flutter3d_conformance.dart +++ b/packages/flutter3d_conformance/lib/flutter3d_conformance.dart @@ -14,8 +14,8 @@ /// /// **Two tiers, and the split is a correction.** This file used to say it was /// shader-free as a whole, and that stopped being true the day a check needed a -/// pipeline: thirty-one of the forty-one link stages and draw. A new -/// backend following the old promise would have met thirty-one shader checks +/// pipeline: thirty-two of the forty-two link stages and draw. A new +/// backend following the old promise would have met thirty-two shader checks /// it could do nothing about, so the lists say which is which — [coreChecks] /// needs clears, uploads and readback alone, [shaderChecks] needs the bundle. /// The tiers answer "can this be asked yet", not "does this matter": the @@ -231,7 +231,7 @@ void runDeviceConformance({ /// **This list is why the two exist separately.** The library used to say it /// was shader-free as a whole, and it stopped being true the day the third /// check needed a pipeline — so a new backend, following the promise, would -/// have hit thirty-one shader checks it had no way to act on yet. Clears, +/// have hit thirty-two shader checks it had no way to act on yet. Clears, /// uploads and readback only: the answers here are the cheapest ones to get, /// and they are the ones worth having first. List get coreChecks => [ @@ -279,6 +279,10 @@ List get shaderChecks => [ run: checkReadbackOfRegion, ), (name: 'an instanced draw draws every instance', run: checkInstancedDraw), + ( + name: 'a window of the index buffer draws that window', + run: checkIndexWindowDraw, + ), ( name: 'a vertex stage can sample a texture', run: checkVertexTextureSampling, diff --git a/packages/flutter3d_conformance/lib/src/core_checks.dart b/packages/flutter3d_conformance/lib/src/core_checks.dart index c405f6ea3..5aa61ef5d 100644 --- a/packages/flutter3d_conformance/lib/src/core_checks.dart +++ b/packages/flutter3d_conformance/lib/src/core_checks.dart @@ -10,7 +10,7 @@ import '../flutter3d_conformance.dart'; /// **This list is why the two exist separately.** The library used to say it /// was shader-free as a whole, and it stopped being true the day the third /// check needed a pipeline — so a new backend, following the promise, would -/// have hit thirty-one shader checks it had no way to act on yet. Clears, +/// have hit thirty-two shader checks it had no way to act on yet. Clears, /// uploads and readback only: the answers here are the cheapest ones to get, /// and they are the ones worth having first. Future checkCapabilities(GraphicsDevice device) async { diff --git a/packages/flutter3d_conformance/lib/src/draw_checks.dart b/packages/flutter3d_conformance/lib/src/draw_checks.dart index 9199afe41..667c4d7a4 100644 --- a/packages/flutter3d_conformance/lib/src/draw_checks.dart +++ b/packages/flutter3d_conformance/lib/src/draw_checks.dart @@ -91,6 +91,158 @@ Future checkInstancedDraw(GraphicsDevice device) async { ); } +/// `draw(firstIndex:, indexCount:)` draws that window of the bound indices and +/// nothing else — `P7`. +/// +/// One index buffer holds two triangles, each covering the whole target, the +/// first red and the second green. A window over the second must read back +/// green and a window over the first red, and a window over both green, since +/// the second is drawn over the first. A backend that ignores the start reads +/// red where green was asked for; one that ignores the count reads green where +/// red was. +/// +/// **Four backends say where a window starts four different ways** — WebGPU +/// with a first index on the draw, WebGL2 with a byte offset, Impeller with a +/// narrower view of the buffer bound again, the software rasteriser by +/// counting — which is why it is asked of every device rather than of one. +/// +/// And a window past the end of the binding is refused with a [RangeError] +/// before anything reaches the driver, which left alone would answer four +/// different ways (`PassEncoder.draw` lists them). +Future checkIndexWindowDraw(GraphicsDevice device) async { + const size = 8; + final vertex = device.shaders['DebugLineVertex']; + final fragment = device.shaders['DebugLine']; + require( + vertex != null && fragment != null, + 'the debug-line stages are missing, so this cannot draw anything', + ); + final pipeline = device.createPipeline(vertex!, fragment!); + + final vertices = Float32List.fromList([ + -1, -1, 0.5, 1, 0, 0, 1, // + 3, -1, 0.5, 1, 0, 0, 1, + -1, 3, 0.5, 1, 0, 0, 1, + -1, -1, 0.5, 0, 1, 0, 1, + 3, -1, 0.5, 0, 1, 0, 1, + -1, 3, 0.5, 0, 1, 0, 1, + ]); + final indices = Uint16List.fromList([0, 1, 2, 3, 4, 5]); + + Future> colourOf({required int firstIndex, int? indexCount}) async { + final target = device.createTexture( + const RenderTargetSpec( + width: size, + height: size, + format: TextureFormat.r8g8b8a8UNormInt, + ), + ); + final pass = device.beginRenderPass( + RenderPassDescriptor( + colors: [ + ColorTarget( + texture: target, + loadAction: LoadAction.clear, + clearValue: Vector4.zero(), + ), + ], + ), + ); + pass + ..bindPipeline(pipeline) + ..setPrimitiveType(PrimitiveType.triangle) + ..setCullMode(CullMode.none) + ..bindUniformBlock(vertex, 'LineInfo', { + 'view_projection': Float32List.fromList(Matrix4.identity().storage), + }) + ..bindVertexData(ByteData.sublistView(vertices), 6) + ..bindIndexData(ByteData.sublistView(indices), IndexType.int16, 6) + ..draw(firstIndex: firstIndex, indexCount: indexCount) + ..submit(); + final pixels = await device.readPixels(target); + require(pixels != null, 'the target could not be read back'); + final bytes = pixels!.buffer.asUint8List(); + final at = ((size ~/ 2) * size + size ~/ 2) * 4; + return [bytes[at], bytes[at + 1], bytes[at + 2]]; + } + + bool near(List got, List want) => + got.indexed.every(((int, int) c) => (c.$2 - want[c.$1]).abs() <= 8); + + const red = [255, 0, 0]; + const green = [0, 255, 0]; + + // Mutation: drop `firstIndex +` from the `_indexAt` call in + // `CpuEncoder._drawOnce`. The second triangle is then read from the first + // three indices and this reads back red. + final second = await colourOf(firstIndex: 3, indexCount: 3); + require( + near(second, green), + 'a window of three indices from index 3 drew $second where the second ' + 'triangle\'s green was expected — the start of the window was ignored', + ); + + final first = await colourOf(firstIndex: 0, indexCount: 3); + require( + near(first, red), + 'a window of the first three indices drew $first where the first ' + 'triangle\'s red was expected — the count was ignored and both were drawn', + ); + + final both = await colourOf(firstIndex: 0); + require( + near(both, green), + 'a draw with no count drew $both where the second triangle, drawn last, ' + 'was expected — a missing count must read to the end of the binding', + ); + + // The refusal is asked in a pass of its own, which is submitted afterwards + // whatever happened, so a backend that does not refuse still leaves the + // device as the next check expects it. + final target = device.createTexture( + const RenderTargetSpec( + width: size, + height: size, + format: TextureFormat.r8g8b8a8UNormInt, + ), + ); + final pass = device.beginRenderPass( + RenderPassDescriptor( + colors: [ + ColorTarget( + texture: target, + loadAction: LoadAction.clear, + clearValue: Vector4.zero(), + ), + ], + ), + ); + pass + ..bindPipeline(pipeline) + ..setPrimitiveType(PrimitiveType.triangle) + ..setCullMode(CullMode.none) + ..bindUniformBlock(vertex, 'LineInfo', { + 'view_projection': Float32List.fromList(Matrix4.identity().storage), + }) + ..bindVertexData(ByteData.sublistView(vertices), 6) + ..bindIndexData(ByteData.sublistView(indices), IndexType.int16, 6); + final refused = () { + try { + pass.draw(firstIndex: 4, indexCount: 3); + return false; + } on RangeError { + return true; + } + }(); + pass.submit(); + require( + refused, + 'a window of three indices from index 4 of a binding of six was not ' + 'refused. It reads past the end, and every backend has to say so the same ' + 'way rather than leave it to a driver', + ); +} + /// A buffer uploaded for a use is bound for that use, and draws. /// /// **This check used to assert nothing.** It uploaded sixty-four zero bytes diff --git a/packages/flutter3d_cpu/lib/src/cpu_encoder.dart b/packages/flutter3d_cpu/lib/src/cpu_encoder.dart index 61b08598e..8d96d8f56 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_encoder.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_encoder.dart @@ -371,7 +371,16 @@ final class CpuEncoder implements CommandEncoder { } @override - void draw({int instanceCount = 1}) { + void draw({int instanceCount = 1, int firstIndex = 0, int? indexCount}) { + // Refused before the first instance rather than inside the loop, so a + // window past the binding draws nothing at all, as on the other three. + final window = _indices == null + ? null + : indexWindow( + _indexCount, + firstIndex: firstIndex, + indexCount: indexCount, + ); // Honestly, and to no advantage — which is the point. A scene the software // backend refuses to draw is a scene with no cross-backend check, and the // two most expensive bugs this repository has found were both found by @@ -383,11 +392,11 @@ final class CpuEncoder implements CommandEncoder { // attributes means on any backend. The instance *index* arrives with the // vertex layouts that give a stage something to read it for. for (var instance = 0; instance < instanceCount; instance++) { - _drawOnce(instance); + _drawOnce(instance, window?.first ?? 0, window?.count ?? 0); } } - void _drawOnce(int instance) { + void _drawOnce(int instance, int firstIndex, int indexCount) { final pipeline = _pipeline; final vertices = _vertices; if (pipeline == null || vertices == null) return; @@ -424,13 +433,13 @@ final class CpuEncoder implements CommandEncoder { final perPrimitive = _primitive == PrimitiveType.line ? 2 : 3; final count = _indices != null - ? _indexCount ~/ perPrimitive + ? indexCount ~/ perPrimitive : _vertexCount ~/ perPrimitive; for (var t = 0; t < count; t++) { for (var corner = 0; corner < perPrimitive; corner++) { final vertex = _indices != null - ? _indexAt(t * perPrimitive + corner) + ? _indexAt(firstIndex + t * perPrimitive + corner) : t * perPrimitive + corner; if (!fetch.into(attributes, vertex)) return; // A stage that wants its own index gets it — see diff --git a/packages/flutter3d_cpu/test/conformance_test.dart b/packages/flutter3d_cpu/test/conformance_test.dart index d301e34c7..81ab579cc 100644 --- a/packages/flutter3d_cpu/test/conformance_test.dart +++ b/packages/flutter3d_cpu/test/conformance_test.dart @@ -1,4 +1,4 @@ -/// The forty-one checks, against a backend with no GPU underneath it. +/// The forty-two checks, against a backend with no GPU underneath it. /// /// This is the only backend that can run them in a plain `dart test`: the /// other two need a device, so Impeller runs them from an application and diff --git a/packages/flutter3d_hardware/CHANGELOG.md b/packages/flutter3d_hardware/CHANGELOG.md index 36c03afec..6dbc30404 100644 --- a/packages/flutter3d_hardware/CHANGELOG.md +++ b/packages/flutter3d_hardware/CHANGELOG.md @@ -1,3 +1,14 @@ +## Unreleased + +- **A draw can read a window of the bound indices.** `PassEncoder.draw` takes + `firstIndex` and `indexCount`, so one buffer holding a model's every part is + bound once and drawn a part at a time. Leaving both out draws the whole + binding, as before. Every implementation of `PassEncoder` changes its + signature. A window past the end of the binding is refused with a + `RangeError` by `indexWindow`, which every backend calls, rather than left + to four drivers that answer it four different ways. Traces record the + window only when there is one, so older traces read as they were recorded. + ## 0.8.0+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_hardware/lib/src/command_encoder.dart b/packages/flutter3d_hardware/lib/src/command_encoder.dart index 2cdeeb314..08d9c43b2 100644 --- a/packages/flutter3d_hardware/lib/src/command_encoder.dart +++ b/packages/flutter3d_hardware/lib/src/command_encoder.dart @@ -381,7 +381,50 @@ abstract interface class PassEncoder { /// Zero draws nothing, which matters because the number usually comes from a /// live population: a particle system with nothing alive should not be a /// special case at every call site. - void draw({int instanceCount = 1}); + /// + /// [firstIndex] and [indexCount] draw a window of the bound indices rather + /// than all of them — `P7`. Null [indexCount] reads to the end of the + /// binding, so leaving both out is the whole buffer, as it always was. + /// + /// **On the draw, not on the binding, for the reason [instanceCount] is.** + /// One buffer holding a model's every part is bound once and drawn a part at + /// a time, each part with its own material; a window on the binding would + /// mean binding the same buffer again for every part to say something only + /// the draw knows. WebGPU and Metal take a first index on the draw for the + /// same reason, and WebGL2 takes a byte offset there. + /// + /// **A window past the end of the binding is refused with a [RangeError], + /// on every backend, before anything reaches the driver** — see + /// [indexWindow], which all four call. Left to the drivers it would be four + /// different answers: WebGL2 reports `INVALID_OPERATION` and draws nothing, + /// WebGPU invalidates the whole pass, Metal leaves it undefined, and the + /// software rasteriser would read past its bytes and throw from somewhere + /// that names none of them. Held by the conformance check + /// `a window of the index buffer draws that window`. + void draw({int instanceCount = 1, int firstIndex = 0, int? indexCount}); +} + +/// The window of [bound] indices a draw reads, from [firstIndex] for +/// [indexCount] of them, or to the end when [indexCount] is null. +/// +/// **For a backend's [PassEncoder.draw]**, so that all four refuse the same +/// windows in the same words rather than each reaching its driver's own +/// answer. Throws a [RangeError] naming the three numbers when the window +/// does not lie inside the binding. +({int first, int count}) indexWindow( + int bound, { + int firstIndex = 0, + int? indexCount, +}) { + final count = indexCount ?? bound - firstIndex; + if (firstIndex < 0 || count < 0 || firstIndex + count > bound) { + throw RangeError( + 'a draw of $count indices from index $firstIndex reads past the ' + '$bound the index binding holds. Draw a window inside it, or bind the ' + 'buffer whose indices the window means.', + ); + } + return (first: firstIndex, count: count); } /// A pass somebody opened, and must therefore close. diff --git a/packages/flutter3d_hardware/lib/src/testing_fake_pass.dart b/packages/flutter3d_hardware/lib/src/testing_fake_pass.dart index b4c341ffe..2223c647a 100644 --- a/packages/flutter3d_hardware/lib/src/testing_fake_pass.dart +++ b/packages/flutter3d_hardware/lib/src/testing_fake_pass.dart @@ -154,12 +154,22 @@ final class FakePass implements CommandEncoder { commands.add(RecordedVertices(vertexCount, transient: true, slot: slot)); @override - void bindIndexBuffer(GeometryBuffer buffer, IndexType type, int indexCount) => - commands.add(RecordedIndices(type, indexCount, transient: false)); + void bindIndexBuffer(GeometryBuffer buffer, IndexType type, int indexCount) { + commands.add(RecordedIndices(type, indexCount, transient: false)); + _boundIndices = indexCount; + } @override - void bindIndexData(ByteData bytes, IndexType type, int indexCount) => - commands.add(RecordedIndices(type, indexCount, transient: true)); + void bindIndexData(ByteData bytes, IndexType type, int indexCount) { + commands.add(RecordedIndices(type, indexCount, transient: true)); + _boundIndices = indexCount; + } + + /// How many indices the last binding carried, for holding a draw's window + /// to it as every real backend does. Null until something is bound, and + /// then a window is recorded as it was asked for: a test that draws with + /// nothing bound is asserting on the order of calls, not on geometry. + int? _boundIndices; @override bool bindUniformBlock( @@ -202,11 +212,26 @@ final class FakePass implements CommandEncoder { void clearBindings() { commands.add(const RecordedClearBindings()); _forget(); + _boundIndices = null; } @override - void draw({int instanceCount = 1}) { - commands.add(RecordedDraw(instanceCount: instanceCount)); + void draw({int instanceCount = 1, int firstIndex = 0, int? indexCount}) { + final window = switch (_boundIndices) { + final bound? => indexWindow( + bound, + firstIndex: firstIndex, + indexCount: indexCount, + ), + null => (first: firstIndex, count: indexCount), + }; + commands.add( + RecordedDraw( + instanceCount: instanceCount, + firstIndex: window.first, + indexCount: indexCount == null ? null : window.count, + ), + ); final bindings = stageBindings; final pipeline = _pipelineName; if (bindings == null || pipeline == null) return; diff --git a/packages/flutter3d_hardware/lib/src/testing_recorded.dart b/packages/flutter3d_hardware/lib/src/testing_recorded.dart index e8dc78c7c..98f07d1a1 100644 --- a/packages/flutter3d_hardware/lib/src/testing_recorded.dart +++ b/packages/flutter3d_hardware/lib/src/testing_recorded.dart @@ -17,12 +17,21 @@ sealed class Recorded { } final class RecordedDraw extends Recorded { - const RecordedDraw({this.instanceCount = 1}); + const RecordedDraw({ + this.instanceCount = 1, + this.firstIndex = 0, + this.indexCount, + }); /// How many instances the draw asked for. One for every draw this engine /// made before mesh particles, which is why the characterisation snapshots /// print `draw` unadorned unless it is anything else. final int instanceCount; + + /// The window of the bound indices the draw read — `P7`. Zero and null, the + /// whole binding, for every draw that did not ask for less. + final int firstIndex; + final int? indexCount; } final class RecordedPipeline extends Recorded { diff --git a/packages/flutter3d_hardware/lib/src/trace/recording_device.dart b/packages/flutter3d_hardware/lib/src/trace/recording_device.dart index 5e83fdbf5..e2a8c1be3 100644 --- a/packages/flutter3d_hardware/lib/src/trace/recording_device.dart +++ b/packages/flutter3d_hardware/lib/src/trace/recording_device.dart @@ -618,9 +618,13 @@ final class _RecordingEncoder implements CommandEncoder { } @override - void draw({int instanceCount = 1}) { - _events.add(TraceDraw(_pass, instanceCount)); - _inner.draw(instanceCount: instanceCount); + void draw({int instanceCount = 1, int firstIndex = 0, int? indexCount}) { + _events.add(TraceDraw(_pass, instanceCount, firstIndex, indexCount)); + _inner.draw( + instanceCount: instanceCount, + firstIndex: firstIndex, + indexCount: indexCount, + ); } @override diff --git a/packages/flutter3d_hardware/lib/src/trace/trace.dart b/packages/flutter3d_hardware/lib/src/trace/trace.dart index bfe1624be..3e3d5d0d6 100644 --- a/packages/flutter3d_hardware/lib/src/trace/trace.dart +++ b/packages/flutter3d_hardware/lib/src/trace/trace.dart @@ -335,8 +335,12 @@ void _replayPassEvent( ); case TraceClearBindings(): pass.clearBindings(); - case TraceDraw(:final instanceCount): - pass.draw(instanceCount: instanceCount); + case TraceDraw(:final instanceCount, :final firstIndex, :final indexCount): + pass.draw( + instanceCount: instanceCount, + firstIndex: firstIndex, + indexCount: indexCount, + ); case TraceSubmit(): pass.submit(); } diff --git a/packages/flutter3d_hardware/lib/src/trace/trace_event.dart b/packages/flutter3d_hardware/lib/src/trace/trace_event.dart index fad1ca03e..29449b391 100644 --- a/packages/flutter3d_hardware/lib/src/trace/trace_event.dart +++ b/packages/flutter3d_hardware/lib/src/trace/trace_event.dart @@ -251,7 +251,12 @@ sealed class TraceEvent { sampler: j['sampler'] == null ? null : samplerFromJson(j['sampler']), ), 'clearBindings' => TraceClearBindings(asInt(j['pass'])), - 'draw' => TraceDraw(asInt(j['pass']), asInt(j['instanceCount'])), + 'draw' => TraceDraw( + asInt(j['pass']), + asInt(j['instanceCount']), + j['firstIndex'] == null ? 0 : asInt(j['firstIndex']), + j['indexCount'] == null ? null : asInt(j['indexCount']), + ), 'submit' => TraceSubmit(asInt(j['pass'])), 'createStorageBuffer' => TraceCreateStorageBuffer( id: asInt(j['id']), @@ -924,14 +929,27 @@ final class TraceClearBindings extends TracePassEvent { } final class TraceDraw extends TracePassEvent { - const TraceDraw(super.pass, [this.instanceCount = 1]); + const TraceDraw( + super.pass, [ + this.instanceCount = 1, + this.firstIndex = 0, + this.indexCount, + ]); final int instanceCount; + + /// The window of the bound indices — `P7`. Written only when it is not the + /// whole binding, so a trace recorded before windows existed reads as it + /// was recorded and needs no new format version. + final int firstIndex; + final int? indexCount; @override String get kind => 'draw'; @override Map toJson(TraceBlobWriter blob) => { 'pass': pass, 'instanceCount': instanceCount, + if (firstIndex != 0) 'firstIndex': firstIndex, + if (indexCount != null) 'indexCount': indexCount, }; } diff --git a/packages/flutter3d_hardware/test/index_window_test.dart b/packages/flutter3d_hardware/test/index_window_test.dart new file mode 100644 index 000000000..5c027abbf --- /dev/null +++ b/packages/flutter3d_hardware/test/index_window_test.dart @@ -0,0 +1,51 @@ +/// What `indexWindow` lets a draw read, and what it refuses. +/// +/// flutter test test/index_window_test.dart +/// +/// The conformance check `a window of the index buffer draws that window` asks +/// the same of a live device; this asks it of the rule the four backends share, +/// which is where a refusal is added and so where a test of one belongs. +library; + +import 'package:flutter3d_hardware/flutter3d_hardware.dart'; +import 'package:test/test.dart'; + +void main() { + test('leaving both out is the whole binding', () { + expect(indexWindow(6), (first: 0, count: 6)); + }); + + test('a start with no count reads to the end', () { + expect(indexWindow(6, firstIndex: 3), (first: 3, count: 3)); + }); + + test('a window inside the binding is the window', () { + expect(indexWindow(6, firstIndex: 3, indexCount: 3), (first: 3, count: 3)); + expect(indexWindow(6, firstIndex: 0, indexCount: 0), (first: 0, count: 0)); + }); + + test('nothing bound is an empty whole, not a refusal', () { + // The encoders return early on a count of nought, which is how a draw + // with nothing bound has always drawn nothing. + expect(indexWindow(0), (first: 0, count: 0)); + }); + + test('a window past the end is refused, naming the three numbers', () { + expect( + () => indexWindow(6, firstIndex: 4, indexCount: 3), + throwsA( + isA().having( + (e) => e.message, + 'message', + allOf(contains('3 indices'), contains('index 4'), contains('6')), + ), + ), + ); + }); + + test('a negative start or count is refused', () { + expect(() => indexWindow(6, firstIndex: -1), throwsRangeError); + expect(() => indexWindow(6, indexCount: -1), throwsRangeError); + expect(() => indexWindow(6, firstIndex: 7), throwsRangeError); + }); +} diff --git a/packages/flutter3d_hardware/test/trace_codec_test.dart b/packages/flutter3d_hardware/test/trace_codec_test.dart index 09e41a953..4041270de 100644 --- a/packages/flutter3d_hardware/test/trace_codec_test.dart +++ b/packages/flutter3d_hardware/test/trace_codec_test.dart @@ -184,7 +184,7 @@ List _everyEvent() => [ sampler: SamplerOptions.nearestClamp, ), const TraceClearBindings(0), - const TraceDraw(0, 4), + const TraceDraw(0, 4, 3, 6), const TraceSubmit(0), const TraceReadPixels(1), const TraceReadback(texture: 1, region: _rect), @@ -236,6 +236,18 @@ void main() { expect(Trace(read.events, metadata: read.metadata).encode(), first); }); + test('a draw of the whole binding reads back as the whole binding', () { + // A trace recorded before windows existed has neither key, and has to + // read as the draw it was without a new format version. + final read = Trace.decode( + Trace(const [TraceDraw(0, 2)]).encode(), + ); + final draw = read.events.single as TraceDraw; + expect(draw.instanceCount, 2); + expect(draw.firstIndex, 0); + expect(draw.indexCount, isNull); + }); + test('a trace from another format version is refused by name', () { final bytes = Trace(const []).encode(); ByteData.sublistView(bytes).setUint32(8, 99, Endian.little); diff --git a/packages/flutter3d_impeller/lib/src/gpu_command_encoder.dart b/packages/flutter3d_impeller/lib/src/gpu_command_encoder.dart index 93f27d6d8..44a1d8478 100644 --- a/packages/flutter3d_impeller/lib/src/gpu_command_encoder.dart +++ b/packages/flutter3d_impeller/lib/src/gpu_command_encoder.dart @@ -197,6 +197,7 @@ final class GpuCommandEncoder implements CommandEncoder { // its draw needs after binding the pipeline, never before. _pass.clearBindings(); _indexCount = 0; + _indexView = null; _pass.bindPipeline(pipeline.backend as gpu.RenderPipeline); } @@ -222,17 +223,33 @@ final class GpuCommandEncoder implements CommandEncoder { _pass.bindVertexBuffer(_emplace(bytes), slot: slot); @override - void bindIndexBuffer(GeometryBuffer buffer, IndexType type, int indexCount) { - _pass.bindIndexBuffer(_view(buffer), type.toGpu()); - _indexCount = indexCount; - } + void bindIndexBuffer(GeometryBuffer buffer, IndexType type, int indexCount) => + _bindIndices(_view(buffer), type, indexCount); @override - void bindIndexData(ByteData bytes, IndexType type, int indexCount) { - _pass.bindIndexBuffer(_emplace(bytes), type.toGpu()); + void bindIndexData(ByteData bytes, IndexType type, int indexCount) => + _bindIndices(_emplace(bytes), type, indexCount); + + void _bindIndices(gpu.BufferView view, IndexType type, int indexCount) { + _pass.bindIndexBuffer(view, type.toGpu()); + _indexView = view; + _indexType = type; _indexCount = indexCount; + _boundFirstIndex = 0; } + /// The view the last index bind named, kept so a draw of a window can bind + /// a narrower one — `P7`. flutter_gpu's `drawIndexed` takes a count and no + /// first index, so where the window starts is said with the view's offset. + gpu.BufferView? _indexView; + IndexType _indexType = IndexType.int32; + + /// Where the view bound on the pass right now starts, in indices from the + /// start of [_indexView]. A window draw moves it, and the next draw that + /// wants a different start binds again rather than reading from wherever + /// the last window left it. + int _boundFirstIndex = 0; + @override bool bindUniformBlock( ShaderHandle shader, @@ -336,12 +353,31 @@ final class GpuCommandEncoder implements CommandEncoder { // draw after a `clearBindings` inherit the previous mesh's index count, // which is the kind of state leak that draws a plausible wrong picture. _indexCount = 0; + _indexView = null; } @override - void draw({int instanceCount = 1}) { - if (_indexCount == 0 || instanceCount <= 0) return; - _pass.drawIndexed(_indexCount, instanceCount: instanceCount); + void draw({int instanceCount = 1, int firstIndex = 0, int? indexCount}) { + final window = indexWindow( + _indexCount, + firstIndex: firstIndex, + indexCount: indexCount, + ); + final view = _indexView; + if (window.count == 0 || instanceCount <= 0 || view == null) return; + if (window.first != _boundFirstIndex) { + final skip = window.first * (_indexType == IndexType.int16 ? 2 : 4); + _pass.bindIndexBuffer( + gpu.BufferView( + view.buffer, + offsetInBytes: view.offsetInBytes + skip, + lengthInBytes: view.lengthInBytes - skip, + ), + _indexType.toGpu(), + ); + _boundFirstIndex = window.first; + } + _pass.drawIndexed(window.count, instanceCount: instanceCount); } @override diff --git a/packages/flutter3d_webgl/lib/src/webgl_encoder.dart b/packages/flutter3d_webgl/lib/src/webgl_encoder.dart index c6788114e..8d66bc133 100644 --- a/packages/flutter3d_webgl/lib/src/webgl_encoder.dart +++ b/packages/flutter3d_webgl/lib/src/webgl_encoder.dart @@ -890,8 +890,17 @@ final class WebGlEncoder implements CommandEncoder { } @override - void draw({int instanceCount = 1}) { + void draw({int instanceCount = 1, int firstIndex = 0, int? indexCount}) { + final window = indexWindow( + _indexCount, + firstIndex: firstIndex, + indexCount: indexCount, + ); if (instanceCount <= 0) return; + // WebGL2 says where a window starts as a byte offset into the bound + // buffer, so the start is added to the one the binding already carries. + final offset = + _indexOffset + window.first * (_indexType == IndexType.int16 ? 2 : 4); _clearWhatWasNotBound(); if (instanceCount == 1) { // Not `drawElementsInstanced` with a count of one. They are specified to @@ -901,16 +910,16 @@ final class WebGlEncoder implements CommandEncoder { // driver disagrees with the specification. _gl.drawElements( _primitive, - _indexCount, + window.count, indexTypeToGl(_indexType), - _indexOffset, + offset, ); } else { _gl.drawElementsInstanced( _primitive, - _indexCount, + window.count, indexTypeToGl(_indexType), - _indexOffset, + offset, instanceCount, ); } diff --git a/packages/flutter3d_webgpu/lib/src/webgpu_encoder.dart b/packages/flutter3d_webgpu/lib/src/webgpu_encoder.dart index fb194e814..a21847212 100644 --- a/packages/flutter3d_webgpu/lib/src/webgpu_encoder.dart +++ b/packages/flutter3d_webgpu/lib/src/webgpu_encoder.dart @@ -527,7 +527,12 @@ final class WebGpuEncoder implements CommandEncoder { // ------------------------------------------------------------- the draw @override - void draw({int instanceCount = 1}) { + void draw({int instanceCount = 1, int firstIndex = 0, int? indexCount}) { + final window = indexWindow( + _indexCount, + firstIndex: firstIndex, + indexCount: indexCount, + ); // Below one draws nothing, as it does on the other backends. A negative // count handed on would reach `drawIndexed` as an unsigned number in the // billions. @@ -579,7 +584,7 @@ final class WebGpuEncoder implements CommandEncoder { indices.offset, indices.length, ) - ..drawIndexed(_indexCount, instanceCount); + ..drawIndexed(window.count, instanceCount, window.first); } /// The signature this draw's state makes, which is what the cache is diff --git a/site/content/core/backends.md b/site/content/core/backends.md index 8d6e50f7f..311570c63 100644 --- a/site/content/core/backends.md +++ b/site/content/core/backends.md @@ -345,7 +345,7 @@ The Impeller backend's translation asserts that each enum value maps to the `flu ## Run the conformance suite first -`flutter3d_conformance` turns those semantics into executable checks, in **two tiers**. `coreChecks` works with clears, uploads and readback alone, so you can run it before you have a single shader compiled, which is when the answers are cheapest to act on. `shaderChecks` needs the bundle: thirty-one shader checks against eight that ask for none, and one more for a backend that can pack its own shaders as a loadable bundle: thirty-one of the forty-one link stages and draw. Among the thirty-one: that a pass starts covering its own attachment and nothing else, that its initial viewport covers the *level* rather than the base, that it inherits no clipping from the pass before it and starts with the stencil test off, that a binding made for one pipeline does not follow the next, that a block missing a member the caller named is refused, that a pass renders into a cube face and a mip, that a blend constant reaches the blend or is refused rather than drawn as zero, that a multisample resolve resolves, that an object id survives the draw and the readback, that a geometry overwrite draws what a fresh upload draws and leaves its neighbours untouched, and that wireframe and every primitive type are each drawn as themselves or refused, never substituted without a word. The lists are the authority (`coreChecks` and `shaderChecks` in `flutter3d_conformance.dart`), and the counts in this paragraph are held to them by `tool/structure.dart`, because the last time they were not, this page said fifteen. +`flutter3d_conformance` turns those semantics into executable checks, in **two tiers**. `coreChecks` works with clears, uploads and readback alone, so you can run it before you have a single shader compiled, which is when the answers are cheapest to act on. `shaderChecks` needs the bundle: thirty-two shader checks against eight that ask for none, and one more for a backend that can pack its own shaders as a loadable bundle: thirty-two of the forty-two link stages and draw. Among the thirty-two: that a pass starts covering its own attachment and nothing else, that its initial viewport covers the *level* rather than the base, that it inherits no clipping from the pass before it and starts with the stencil test off, that a binding made for one pipeline does not follow the next, that a draw of a window of the index buffer draws that window and refuses one past its end, that a block missing a member the caller named is refused, that a pass renders into a cube face and a mip, that a blend constant reaches the blend or is refused rather than drawn as zero, that a multisample resolve resolves, that an object id survives the draw and the readback, that a geometry overwrite draws what a fresh upload draws and leaves its neighbours untouched, and that wireframe and every primitive type are each drawn as themselves or refused, never substituted without a word. The lists are the authority (`coreChecks` and `shaderChecks` in `flutter3d_conformance.dart`), and the counts in this paragraph are held to them by `tool/structure.dart`, because the last time they were not, this page said fifteen. **A check a backend cannot be asked is reported as a decline, not as a pass.** Multisampling ends that way on the software rasteriser, which answers `supportsOffscreenMsaa` false and does not multisample at all; the blend constant ends that way on Impeller, where flutter_gpu exposes no setter, and on WebGPU, where the API has `"constant"` and `"one-minus-constant"` and no colour/alpha split to form `BlendFactor.blendAlpha` with; and the uniform-member rule is one only a backend that reflects its shaders can keep. WebGPU declines two in all, the blend constant and wireframe, which is how its run reads 41 of 41 without claiming anything it cannot do. It used to decline two more, rendering into a mip and the block-compressed formats, and the first of those is the caution this paragraph needs beside it: two checks read `supportsRenderToMip` and, finding it false, went on to ask a *smaller* question instead of skipping: one allocated a single mip level instead of two, the other returned before it drew. Neither showed up in the tally, because a check that answers half of itself still passes. The capability is true now and both ask the whole question, at exactly the same 41 of 41. The runner's tally line says `N passed, M failed, K declined` for exactly this reason: "the suite is green" and "the suite is green, and here is what it never asked" are different sentences, and a third party reading this page to decide what conformance buys them needs the second one. diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 1b5b797a9..a05e805c2 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10941 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 10948 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/packages.md b/site/content/reference/packages.md index de7be7587..bf7443725 100644 --- a/site/content/reference/packages.md +++ b/site/content/reference/packages.md @@ -65,7 +65,7 @@ It is how eighty-six golden scenes are checkable with no GPU in the room, and it ### `flutter3d_conformance` The suite any fourth backend would have to pass before it counted as one, plus the cross-backend comparison with per-scene budgets. -Two tiers, and the split is a correction. The library said it was shader-free as a whole, and that stopped being true the day a check needed a pipeline: thirty-one of the forty-one link stages and draw. `coreChecks` is what runs on clears, uploads and readback alone, so a backend can ask it before compiling a single shader; `shaderChecks` is the rest. A backend that believed the old promise would have met every one of those failures with nothing it could do about them yet. The phrasing here is the one `tool/structure.dart` holds to the lists themselves, so the sentence cannot go stale again without the scan saying so. +Two tiers, and the split is a correction. The library said it was shader-free as a whole, and that stopped being true the day a check needed a pipeline: thirty-two of the forty-two link stages and draw. `coreChecks` is what runs on clears, uploads and readback alone, so a backend can ask it before compiling a single shader; `shaderChecks` is the rest. A backend that believed the old promise would have met every one of those failures with nothing it could do about them yet. The phrasing here is the one `tool/structure.dart` holds to the lists themselves, so the sentence cannot go stale again without the scan saying so. → [Writing a HAL backend](/core/backends/) diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 0bd032c50..2b1450c6b 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10941 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10948 tests need a GPU. --- # Testing -10941 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10948 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -18,7 +18,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_shooter` | 340 | | `flutter3d_audio_core` | 55 | | | | | `flutter3d_audio` | 4 | | `flutter3d_game_racing` | 223 | | `flutter3d_webgl` | 62 | -| `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 63 | +| `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 70 | | `apps/flutter3d_demo_platformer` | 202 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 357 | | `apps/flutter3d_demo_strategy` | 50 | | `apps/flutter3d_editor` | 224 | | `apps/flutter3d_demo_arcade` | 20 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10915 rather than 10941: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10922 rather than 10948: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tool/webgpu_spike/lib/src/webgpu_interop.dart b/tool/webgpu_spike/lib/src/webgpu_interop.dart index bfe8df228..3883fccdd 100644 --- a/tool/webgpu_spike/lib/src/webgpu_interop.dart +++ b/tool/webgpu_spike/lib/src/webgpu_interop.dart @@ -105,7 +105,11 @@ extension type GPURenderPassEncoder._(JSObject _) implements JSObject { external void setScissorRect(int x, int y, int width, int height); external void setBlendConstant(GPUColorDict color); external void setStencilReference(int reference); - external void drawIndexed(int indexCount, int instanceCount); + external void drawIndexed( + int indexCount, + int instanceCount, [ + int firstIndex, + ]); external void end(); } diff --git a/tool/webgpu_spike/lib/src/webgpu_spike_encoder.dart b/tool/webgpu_spike/lib/src/webgpu_spike_encoder.dart index 444f9e879..dd151cf8f 100644 --- a/tool/webgpu_spike/lib/src/webgpu_spike_encoder.dart +++ b/tool/webgpu_spike/lib/src/webgpu_spike_encoder.dart @@ -291,7 +291,12 @@ final class WebGpuSpikeEncoder implements CommandEncoder { // ------------------------------------------------------------- the draw @override - void draw({int instanceCount = 1}) { + void draw({int instanceCount = 1, int firstIndex = 0, int? indexCount}) { + final window = indexWindow( + _indexCount, + firstIndex: firstIndex, + indexCount: indexCount, + ); if (instanceCount == 0) return; final pipeline = _pipeline; if (pipeline == null) { @@ -309,7 +314,7 @@ final class WebGpuSpikeEncoder implements CommandEncoder { } _pass ..setIndexBuffer(_indexBuffer!, gpuIndexFormat(_indexType)) - ..drawIndexed(_indexCount, instanceCount); + ..drawIndexed(window.count, instanceCount, window.first); } /// The key this draw's state makes, which is also what the pipeline cache is From d17a8aabd5bf2f46ec0b4e3814fb34646c27a86d Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:07:59 +0300 Subject: [PATCH 035/141] A material written in the material language now builds into a shader bundle The build hook finds assets_src/**/*.f3dmat and writes a .f3dshaders bundle for each at the same relative path under flutter3d_generated: an Impeller section from impellerc, a WebGL2 section from the GLSL ES translator and, when glslang and naga are on PATH, a WebGPU section. Any backend loads it through GraphicsDevice.loadShaders, and the software backend keeps evaluating the same source. A mistake fails the build with its file, line and column. The hook process cannot resolve a package that declares the Flutter SDK, so the translator, the WGSL preparation and compile, both section codecs and the varying numbering moved from flutter3d_webgl and flutter3d_webgpu into flutter3d_shaders, behind translate.dart and compile.dart. The changelogs of cpu, shaders, webgl and webgpu had this branch's entries under the released 0.8.2+1; they are back under Unreleased. --- ARCHITECTURE.md | 2 +- README.md | 3 +- doc/postprocessing-parity.md | 6 +- packages/flutter3d_build/CHANGELOG.md | 36 ++ packages/flutter3d_build/README.md | 12 + .../flutter3d_build/lib/flutter3d_build.dart | 1 + .../flutter3d_build/lib/src/build_assets.dart | 18 +- packages/flutter3d_build/lib/src/layout.dart | 48 +- .../flutter3d_build/lib/src/manifest.dart | 5 + .../lib/src/material_build.dart | 507 ++++++++++++++++++ packages/flutter3d_build/pubspec.yaml | 11 +- .../test/material_build_test.dart | 443 +++++++++++++++ packages/flutter3d_cpu/CHANGELOG.md | 15 +- packages/flutter3d_shaders/CHANGELOG.md | 28 +- packages/flutter3d_shaders/lib/compile.dart | 14 + .../lib/src/bundle_varyings.dart | 72 +++ .../lib/src/glsl_to_wgsl.dart | 26 +- .../lib/src/glsl_translate.dart | 5 + .../lib/src/source_package.dart | 41 +- .../lib/src/webgl_bundle_section.dart | 19 +- .../lib/src/wgsl_compiler.dart | 7 +- .../lib/src/wgsl_section.dart | 47 +- packages/flutter3d_shaders/lib/translate.dart | 18 + packages/flutter3d_webgl/CHANGELOG.md | 20 +- .../flutter3d_webgl/lib/flutter3d_webgl.dart | 11 +- .../lib/src/webgl_loaded_shaders.dart | 2 +- .../test/bundle_section_test.dart | 2 +- .../test/glsl_translate_test.dart | 2 +- .../tool/generate_shaders.dart | 2 +- .../flutter3d_webgl/tool/pack_shaders.dart | 3 +- packages/flutter3d_webgpu/CHANGELOG.md | 21 +- packages/flutter3d_webgpu/pubspec.yaml | 7 - .../test/glsl_to_wgsl_test.dart | 4 +- .../material_language_generators_test.dart | 29 +- .../test/wgsl_pipeline_test.dart | 6 +- .../test/wgsl_section_test.dart | 3 +- .../tool/generate_compute_shaders.dart | 4 +- .../tool/generate_shaders.dart | 24 +- .../tool/pack_wgsl_section.dart | 90 +--- site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- 41 files changed, 1373 insertions(+), 251 deletions(-) create mode 100644 packages/flutter3d_build/lib/src/material_build.dart create mode 100644 packages/flutter3d_build/test/material_build_test.dart create mode 100644 packages/flutter3d_shaders/lib/compile.dart create mode 100644 packages/flutter3d_shaders/lib/src/bundle_varyings.dart rename packages/{flutter3d_webgpu => flutter3d_shaders}/lib/src/glsl_to_wgsl.dart (96%) rename packages/{flutter3d_webgl => flutter3d_shaders}/lib/src/glsl_translate.dart (96%) rename packages/{flutter3d_webgpu => flutter3d_shaders}/lib/src/source_package.dart (73%) rename packages/{flutter3d_webgl => flutter3d_shaders}/lib/src/webgl_bundle_section.dart (72%) rename packages/{flutter3d_webgpu => flutter3d_shaders}/lib/src/wgsl_compiler.dart (96%) rename packages/{flutter3d_webgpu => flutter3d_shaders}/lib/src/wgsl_section.dart (78%) create mode 100644 packages/flutter3d_shaders/lib/translate.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 515a560c0..0617f67cf 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10948 tests** across 43 packages and 10 applications | +| Unit tests | **10957 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index bd6380685..7163cd84f 100644 --- a/README.md +++ b/README.md @@ -158,8 +158,9 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10948 tests across forty-three packages and nine applications. The +There are 10957 tests across forty-three packages and nine applications. The There are 10887 tests across forty-two packages and nine applications. The +There are 10889 tests across forty-two packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes There are 10895 tests across forty-two packages and nine applications. The diff --git a/doc/postprocessing-parity.md b/doc/postprocessing-parity.md index f81ff2e1e..d9ec7671f 100644 --- a/doc/postprocessing-parity.md +++ b/doc/postprocessing-parity.md @@ -73,9 +73,9 @@ transcription, and a golden scene". That was written from the survey and is wrong by three. **A post effect costs two hand-written implementations**: the GLSL source in `flutter3d_shaders`, and its Dart transcription for the CPU rasteriser, which has no shaders at all. WebGL and WebGPU are not transpiled -by hand — `flutter3d_webgl/lib/src/glsl_translate.dart` and -`flutter3d_webgpu/lib/src/glsl_to_wgsl.dart` are general translators driven by -`tool/generate_shaders.dart`, and a new uniform or a new branch goes through +by hand — `flutter3d_shaders/lib/src/glsl_translate.dart` and +`flutter3d_shaders/lib/src/glsl_to_wgsl.dart` are general translators driven by +each backend's `tool/generate_shaders.dart`, and a new uniform or a new branch goes through them untouched. Measured rather than assumed, while landing `gfx-26n`: editing diff --git a/packages/flutter3d_build/CHANGELOG.md b/packages/flutter3d_build/CHANGELOG.md index da3f2793e..8be1e8036 100644 --- a/packages/flutter3d_build/CHANGELOG.md +++ b/packages/flutter3d_build/CHANGELOG.md @@ -1,3 +1,39 @@ +## Unreleased + +**A material written in the material language compiles at build time into +a shader bundle every GPU backend loads.** The hook finds +`assets_src/**/*.f3dmat` and writes `flutter3d_generated/**/*.f3dshaders` at +the same relative path. It parses each source, emits GLSL through +`flutter3d_core`'s emitter, resolves the engine's headers from +`flutter3d_shaders`, and packs one `ShaderBundle` with three sections: +`impellerc` output for Impeller, GLSL ES 3.00 for WebGL2, and WGSL with its +reflection for WebGPU. `GraphicsDevice.loadShaders` takes the file on any +backend; the software backend evaluates the same source on the CPU and needs +no section. Before this, the language emitted GLSL that nothing built, so a +material could be evaluated on the CPU and could not be drawn by a GPU. + +A mistake in a material fails the build with the file, line and column +(`fx/rim.f3dmat:3:13: ...`). A compiler refusing the GLSL generated from a +valid source names the source and keeps the generated `.frag` under +`.dart_tool/flutter3d_build/materials/`, so the line in the compiler's +message can be opened. Nothing compiles at run time. + +The WebGPU section needs glslangValidator and naga. On a machine without +them the bundle gets the other two sections and the log says so, and the +WebGPU backend refuses that bundle by name. Failing the whole build over one +backend's tooling would make it a requirement for everyone. A missing +`impellerc` is an error, because it ships in the Flutter SDK that runs the +hook. + +Every material source and every engine shader header is declared as a hook +dependency, so editing `surface.glsl` rebuilds the bundles that include it. +A bundle whose inputs did not change is not compiled again, and a bundle +whose source is gone is deleted. A project without materials does not look +for a compiler or for the engine's sources at all. + +A manifest rule's `glob` and `exclude` apply to materials as they do to +models. + ## 0.8.0 **Every cached model converts again once.** `kAssetPipelineVersion` is 2, diff --git a/packages/flutter3d_build/README.md b/packages/flutter3d_build/README.md index b1337f88c..44ac3558b 100644 --- a/packages/flutter3d_build/README.md +++ b/packages/flutter3d_build/README.md @@ -23,6 +23,18 @@ name in its `hooks: user_defines:` overrides either choice. The value `universal` there is the one family every device can load; it is turned into BC, ASTC, ETC2 or RGBA8 when the texture is uploaded. +## Materials + +A material written in the material language goes under `assets_src/` with the +extension `.f3dmat`, next to the models that wear it. The hook compiles +`assets_src/fx/rim.f3dmat` into `flutter3d_generated/fx/rim.f3dshaders`: one +shader bundle with a fragment stage named after the material, carrying an +Impeller section, a WebGL2 section and, when `glslangValidator` and `naga` are +on `PATH`, a WebGPU section. Load it with `GraphicsDevice.loadShaders`. +Compiling happens only here, at build time. A mistake fails the build with the +material's file, line and column. A manifest rule's `glob` and `exclude` apply +to materials as well. + The same converter can be run by hand: ```sh diff --git a/packages/flutter3d_build/lib/flutter3d_build.dart b/packages/flutter3d_build/lib/flutter3d_build.dart index 2b062eb72..3632a4640 100644 --- a/packages/flutter3d_build/lib/flutter3d_build.dart +++ b/packages/flutter3d_build/lib/flutter3d_build.dart @@ -13,6 +13,7 @@ export 'src/init.dart'; export 'src/layout.dart'; export 'src/lod_generate.dart'; export 'src/manifest.dart'; +export 'src/material_build.dart'; export 'src/pipeline_version.dart'; export 'src/six_way_bake.dart'; export 'src/texture_encode.dart'; diff --git a/packages/flutter3d_build/lib/src/build_assets.dart b/packages/flutter3d_build/lib/src/build_assets.dart index 441d15f44..24c9927d4 100644 --- a/packages/flutter3d_build/lib/src/build_assets.dart +++ b/packages/flutter3d_build/lib/src/build_assets.dart @@ -20,6 +20,7 @@ import 'package:hooks/hooks.dart'; import 'convert.dart'; import 'device_classes.dart'; import 'layout.dart'; +import 'material_build.dart'; import 'pipeline_version.dart'; /// What one call to [runAssetBuild] did, for a hook's own log line and for @@ -384,7 +385,22 @@ Future buildAssets(BuildInput input, BuildOutputBuilder output) async { textures: textures, deviceClasses: deviceClasses, ); - for (final source in report.dependencies) { + + // **Materials — P8.** After the models, and in the same hook, because they + // land in the same directory the project already bundles. A material that + // does not compile fails the build here, naming its file and line, rather + // than a draw at run time: there is no compiler to fall back to there. + final MaterialBuildReport materials; + try { + materials = runMaterialBuild(projectRoot); + } on MaterialBuildException catch (error) { + throw BuildError(message: error.toString()); + } + + for (final source in [ + ...report.dependencies, + ...materials.dependencies, + ]) { output.dependencies.add(Uri.file(source)); } } diff --git a/packages/flutter3d_build/lib/src/layout.dart b/packages/flutter3d_build/lib/src/layout.dart index 4e2e57a6e..fb791ac19 100644 --- a/packages/flutter3d_build/lib/src/layout.dart +++ b/packages/flutter3d_build/lib/src/layout.dart @@ -1,5 +1,6 @@ /// `ap-04`'s other half: where `assets_src/` and `flutter3d_generated/` sit -/// in a project, and what to convert given an optional [AssetManifest]. +/// in a project, and what to convert given an optional [AssetManifest] — +/// models, and since P8 materials written in the material language. library; import 'dart:io'; @@ -63,8 +64,53 @@ final class AssetLayout { } return result; } + + /// Every material source under [sourcesDir] — P8 — paired with the shader + /// bundle it compiles into: `assets_src/fx/rim.f3dmat` becomes + /// `flutter3d_generated/fx/rim.f3dshaders`. + /// + /// **The same tree as the models, not a `shaders/` of its own.** A material + /// belongs to the asset that wears it, and an author who keeps `crate.glb` + /// and `crate_glow.f3dmat` side by side should not have to split them + /// because one is compiled by a different program. The output lands where + /// `flutter: assets:` already looks, so a project that bundles + /// `flutter3d_generated/` ships its materials with no new line in its + /// pubspec. A manifest rule's `glob` and `exclude` hold here as they do for + /// a model; nothing else in a rule means anything to a material. + /// + /// **`.f3dmat`, a name nothing else claims** — and kept out of + /// [recognisedExtensions], so the model plan never tries to decode one. + List materialPlan() { + if (!sourcesDir.existsSync()) return const []; + return [ + for (final entity in sourcesDir.listSync(recursive: true)) + if (entity is File && _extensionOf(entity.path) == materialExtension) + if (_relativeTo(entity) case final relative) + if (manifest.ruleFor(relative) case final rule + when !(rule?.exclude ?? false)) + AssetPlan( + source: entity.path, + destination: + '${generatedDir.path}/' + '${relative.substring(0, relative.length - materialExtension.length)}' + '$shaderBundleExtension', + rule: rule, + ), + ]; + } + + String _relativeTo(File file) => + file.path.substring(sourcesDir.path.length + 1); } +/// What a material source is called — P8. Matched case-insensitively, like +/// every other extension here. +const String materialExtension = '.f3dmat'; + +/// What a compiled material is called: a `ShaderBundle`, the same container +/// `GraphicsDevice.loadShaders` takes from any other packer. +const String shaderBundleExtension = '.f3dshaders'; + String _extensionOf(String path) { final dot = path.lastIndexOf('.'); final slash = path.lastIndexOf('/'); diff --git a/packages/flutter3d_build/lib/src/manifest.dart b/packages/flutter3d_build/lib/src/manifest.dart index 113d649b7..76f79291a 100644 --- a/packages/flutter3d_build/lib/src/manifest.dart +++ b/packages/flutter3d_build/lib/src/manifest.dart @@ -17,6 +17,11 @@ /// `chair.desktop.f3d` — each cut to its class's `DeviceClassBudget`. A /// manifest without it builds what it always built. /// +/// **Materials (`*.f3dmat`, P8) are matched by the same rules**, and only +/// `glob` and `exclude` mean anything to them. A material has no textures to +/// compress and no levels of detail, so `exclude: true` is the one thing a +/// rule can say about one. `AssetLayout.materialPlan` reads it. +/// /// **`"**/*.obj"` matches a root-level `a.obj` too.** That is /// `package:glob`'s rule since 2.2.0, where `**` at the start of a pattern /// may match no directory at all; before it, the same pattern skipped every diff --git a/packages/flutter3d_build/lib/src/material_build.dart b/packages/flutter3d_build/lib/src/material_build.dart new file mode 100644 index 000000000..e15834427 --- /dev/null +++ b/packages/flutter3d_build/lib/src/material_build.dart @@ -0,0 +1,507 @@ +/// P8: a project's materials, written in the material language, compiled +/// ahead of time into shader bundles every GPU backend loads. +/// +/// `assets_src/**/*.f3dmat` in, `flutter3d_generated/**/*.f3dshaders` out — +/// [AssetLayout.materialPlan] says which and where. Each source is parsed, +/// emitted as GLSL by `flutter3d_core`'s emitter, resolved against the +/// engine's own headers in `flutter3d_shaders`, and packed into one +/// [ShaderBundle] with three sections: `impellerc`'s output for Impeller, +/// GLSL ES 3.00 for WebGL2, and WGSL with its reflection for WebGPU. The +/// software backend needs no section; it evaluates the same source on the +/// CPU. `GraphicsDevice.loadShaders` takes the result on any of the four. +/// +/// **Ahead of time, and only ahead of time.** No backend gains a compiler at +/// run time (decision in §2 of the 0.9 roadmap): a material that does not +/// compile fails the build, on the author's machine, naming the file and the +/// line, rather than a draw on a player's. +/// +/// **The WebGPU section is the one allowed to be missing**, for the reason +/// `flutter3d_webgpu/tool/pack_wgsl_section.dart` gives: it needs +/// glslangValidator and naga, which a machine may not have, and a backend's +/// tooling must not become a requirement for everybody else's build. Without +/// them the bundle carries two sections, the log says so, and the WebGPU +/// backend refuses that bundle by name. `impellerc` is never missing in a +/// real build — it ships in the Flutter SDK that runs the hook — so its +/// absence is an error. +library; + +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:crypto/crypto.dart'; +import 'package:flutter3d_core/formats.dart'; +import 'package:flutter3d_hardware/shader_bundle.dart'; +import 'package:flutter3d_shaders/compile.dart'; + +import 'layout.dart'; +import 'pipeline_version.dart'; + +/// A material that did not build, and where. +/// +/// [line] and [column] are the material source's own when the failure is one +/// the parser can place — which is every mistake an author makes in the +/// language. A compiler refusing GLSL the language accepted names the source +/// too, and its own output names the line of the generated `.frag`, kept on +/// disk at the path the message gives. +final class MaterialBuildException implements Exception { + const MaterialBuildException( + this.message, { + required this.source, + this.line, + this.column, + }); + + final String message; + + /// The material source, as a path; empty for a failure no one source + /// caused, such as no `impellerc` to run. + final String source; + final int? line; + final int? column; + + @override + String toString() => switch ((source, line, column)) { + ('', _, _) => message, + (_, final int line, final int column) => '$source:$line:$column: $message', + (_, final int line, null) => '$source:$line: $message', + _ => '$source: $message', + }; +} + +/// Compiles one prepared stage to WGSL and reports the offsets glslang gave +/// each uniform block member — `compileStage`'s answer, behind a seam a test +/// can replace on a machine with neither compiler. +typedef WgslStageCompiler = + CompiledStage Function( + PreparedStage stage, { + required String name, + required bool fragment, + }); + +/// The programs a material build runs, and the SDK token it stamps. +/// +/// A value rather than a lookup inside the build, so a test hands it a stub +/// `impellerc` the way `flutter3d_impeller`'s own tests do, and so a build +/// that needs none of them — a project without materials — never looks. +final class MaterialCompilers { + const MaterialCompilers({ + required this.impellerc, + required this.sdk, + this.impellerIncludes = const [], + this.wgsl, + }); + + /// `impellerc`'s path. + final String impellerc; + + /// Extra `--include` roots for `impellerc`: the SDK's `shader_lib`. + final List impellerIncludes; + + /// What [ShaderBundle.sdk] says — the Dart that ran `impellerc`'s SDK, which + /// is the Dart running this build when the build runs as a hook. + final String sdk; + + /// Null when glslangValidator or naga is not on `PATH`: the bundle then + /// carries no WebGPU section. + final WgslStageCompiler? wgsl; + + /// The compilers of the Flutter SDK this process runs under, and glslang + /// and naga from `PATH` when both answer. + /// + /// Throws [MaterialBuildException] when there is no `impellerc` — this + /// process is not a Flutter SDK's Dart, or the SDK's artifacts were never + /// downloaded. + static MaterialCompilers locate() { + final sdkRoot = flutterSdkRootFrom(Platform.resolvedExecutable); + final name = Platform.isWindows ? 'impellerc.exe' : 'impellerc'; + final artifacts = '$sdkRoot/bin/cache/artifacts/engine'; + // macOS ships one universal `darwin-x64` directory even on Apple silicon, + // so the candidate that exists wins rather than a guess from the host. + final platform = switch (Platform.operatingSystem) { + 'macos' => const ['darwin-x64', 'darwin-arm64'], + 'linux' => const ['linux-x64', 'linux-arm64'], + 'windows' => const ['windows-x64', 'windows-arm64'], + _ => const ['darwin-x64', 'linux-x64', 'windows-x64'], + }.where((p) => File('$artifacts/$p/$name').existsSync()).firstOrNull; + if (platform == null) { + throw MaterialBuildException( + 'no $name under $artifacts — run "flutter precache" for this host', + source: '', + ); + } + return MaterialCompilers( + impellerc: '$artifacts/$platform/$name', + impellerIncludes: ['$artifacts/$platform/shader_lib'], + sdk: Platform.version.split(' ').first, + wgsl: _answers(kGlslang) && _answers(kNaga) ? _compileWgsl : null, + ); + } +} + +CompiledStage _compileWgsl( + PreparedStage stage, { + required String name, + required bool fragment, +}) => compileStage(stage.glsl, name: name, fragment: fragment); + +bool _answers(String program) { + try { + Process.runSync(program, const ['--version']); + return true; + } on ProcessException { + return false; + } +} + +/// The Flutter SDK root under [executable]: the SDK's own Dart, or +/// `flutter_tester` under its engine artifacts. +/// +/// The search `flutter3d_impeller`'s `shader_bundle_build.dart` makes, and +/// ported rather than shared: that package names Flutter, and this one is a +/// build hook that cannot resolve a package that does. Its doc comment holds +/// the measurements behind each shape — `$FLUTTER_ROOT` unset inside a hook, +/// `flutter_tester` under `flutter test`, backslashes and `.exe` on Windows. +String flutterSdkRootFrom(String executable) { + final normalized = executable.replaceAll(r'\', '/'); + const dart = '/bin/cache/dart-sdk/bin/dart'; + for (final suffix in [dart, '$dart.exe']) { + if (normalized.endsWith(suffix)) { + return executable.substring(0, executable.length - suffix.length); + } + } + const artifacts = '/bin/cache/artifacts/engine/'; + final at = normalized.indexOf(artifacts); + if (at >= 0 && + (normalized.endsWith('/flutter_tester') || + normalized.endsWith('/flutter_tester.exe'))) { + return executable.substring(0, at); + } + throw MaterialBuildException( + 'cannot find the Flutter SDK from $executable, so there is no impellerc ' + 'to compile materials with — build through `flutter`, whose Dart runs ' + 'this hook', + source: '', + ); +} + +/// What one call to [runMaterialBuild] did. +final class MaterialBuildReport { + const MaterialBuildReport({ + required this.built, + required this.skipped, + required this.dependencies, + }); + + /// Material sources this run compiled. + final List built; + + /// Material sources whose bundle was already current. + final List skipped; + + /// Every file the bundles were made from — each material source, and every + /// engine header they can include — for a hook to declare, so an edit to + /// any of them runs the hook again. + final List dependencies; +} + +/// Compiles every material [AssetLayout.materialPlan] finds under +/// [projectRoot]. +/// +/// [compilers] defaults to [MaterialCompilers.locate] and [engine] to the +/// `flutter3d_shaders` the project resolves; both are looked up only when +/// there is a material to build, so a project without one needs neither a +/// Flutter SDK nor the engine's sources. +/// +/// A bundle whose inputs have not changed — the source, every engine shader, +/// the SDK stamp and which sections the compilers can make — is left alone. +/// A bundle whose source is gone is deleted, because `flutter3d_generated/` +/// is bundled whole and would otherwise ship it. +/// +/// Throws [MaterialBuildException] on the first material that does not build, +/// having written nothing for it. +MaterialBuildReport runMaterialBuild( + Directory projectRoot, { + IOSink? log, + MaterialCompilers? compilers, + ShaderSet? engine, +}) { + final layout = AssetLayout(projectRoot: projectRoot); + final plan = layout.materialPlan(); + final cachePath = '${layout.generatedDir.path}/.flutter3d_materials.json'; + final previous = _readCache(cachePath); + final sink = log ?? stdout; + + // Bundles a previous build wrote for sources that are no longer planned. + for (final stale in previous.keys) { + if (plan.any((job) => job.destination == stale)) continue; + final file = File(stale); + if (file.existsSync()) file.deleteSync(); + } + if (plan.isEmpty) { + if (previous.isNotEmpty) File(cachePath).deleteSync(); + return const MaterialBuildReport( + built: [], + skipped: [], + dependencies: [], + ); + } + + final ShaderSet shaders; + try { + shaders = engine ?? loadShaders(from: projectRoot.path); + } on StateError catch (error) { + throw MaterialBuildException( + 'the engine\'s shaders are not reachable from ${projectRoot.path}: ' + '${error.message}', + source: plan.first.source, + ); + } + final tools = compilers ?? MaterialCompilers.locate(); + if (tools.wgsl == null) { + sink.writeln( + 'flutter3d_build: $kGlslang or $kNaga is not on PATH, so material ' + 'bundles get no "webgpu" section and the WebGPU backend will refuse ' + 'them by name. glslangValidator comes with the Vulkan SDK or `brew ' + 'install glslang`; naga from `cargo install naga-cli`.', + ); + } + + final engineHash = sha256 + .convert( + utf8.encode( + (shaders.sources.keys.toList()..sort()) + .map((key) => '$key\n${shaders.sources[key]}') + .join('\n'), + ), + ) + .toString(); + final stamp = + '$engineHash ${tools.sdk} $kAssetPipelineVersion ' + '${tools.wgsl == null ? 'no-webgpu' : 'webgpu'}'; + + final next = {}; + final built = []; + final skipped = []; + for (final job in plan) { + final text = File(job.source).readAsStringSync(); + final key = '${sha256.convert(utf8.encode(text))} $stamp'; + next[job.destination] = key; + if (previous[job.destination] == key && + File(job.destination).existsSync()) { + skipped.add(job.source); + continue; + } + + final bundle = compileMaterial( + text, + source: job.source, + engine: shaders, + compilers: tools, + scratch: Directory( + '${projectRoot.path}/.dart_tool/flutter3d_build/materials', + ), + ); + final bytes = bundle.encode(); + File(job.destination) + ..parent.createSync(recursive: true) + ..writeAsBytesSync( + bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes), + ); + sink.writeln( + 'flutter3d_build: ${job.destination} — "${bundle.name}", sections ' + '${bundle.sections.keys.join(', ')}', + ); + built.add(job.source); + } + + _writeCache(cachePath, next); + return MaterialBuildReport( + built: built, + skipped: skipped, + dependencies: [ + for (final job in plan) job.source, + for (final file in shaders.sources.keys) '${shaders.root}/$file', + ], + ); +} + +/// One material [text] as a bundle with a fragment stage named after the +/// material, compiled for every backend [compilers] can serve. +/// +/// [source] is the path every failure names. [scratch] is where the +/// generated `.frag` is written for `impellerc`, and where it stays when +/// a compiler refuses it, so the line in that compiler's message is a line +/// somebody can open. +ShaderBundle compileMaterial( + String text, { + required String source, + required ShaderSet engine, + required MaterialCompilers compilers, + required Directory scratch, +}) { + final MaterialProgram program; + try { + program = parseMaterial(text); + } on MaterialSyntaxError catch (error) { + throw MaterialBuildException( + error.message, + source: source, + line: error.line, + column: error.column, + ); + } + // One entry point per source, at its declared defaults. A variant is a + // second entry point with other constants folded in, and naming those is + // the manifest's to grow when somebody needs one. + final name = program.name; + final glsl = emitMaterialFragment( + specialiseMaterial(program, MaterialVariant(name)), + ); + + Never refused(String what, String message) => + throw MaterialBuildException('$what: $message', source: source); + + final generated = File('${scratch.path}/$name.frag') + ..parent.createSync(recursive: true) + ..writeAsStringSync(glsl); + + final impeller = _impeller(name, generated, engine, compilers, refused); + + final String webgl; + final String resolved; + try { + webgl = translateGlsl(glsl, engine.sources, from: source, fragment: true); + resolved = resolveIncludes(glsl, engine.sources, from: source); + } on GlslTranslateError catch (error) { + refused('the WebGL2 translation', error.message); + } + + final webgpu = switch (compilers.wgsl) { + null => null, + final compile => _webgpu( + name, + source, + resolved, + engine, + compile, + generated, + refused, + ), + }; + + // Nothing went wrong, so nothing needs opening. + generated.deleteSync(); + + return ShaderBundle( + name: name, + sdk: compilers.sdk, + stages: [ShaderBundleStage(name, fragment: true)], + sections: { + ShaderBundle.impellerSection: impeller, + ShaderBundle.webglSection: encodeWebGlSection( + vertex: const {}, + fragment: {name: webgl}, + ), + ShaderBundle.webgpuSection: ?webgpu, + }, + ); +} + +ByteData _impeller( + String name, + File generated, + ShaderSet engine, + MaterialCompilers compilers, + Never Function(String, String) refused, +) { + final out = File('${generated.parent.path}/$name.shaderbundle'); + final ProcessResult result; + try { + result = Process.runSync(compilers.impellerc, [ + '--shader-bundle=${jsonEncode({ + name: {'type': 'fragment', 'file': generated.path}, + })}', + '--sl=${out.path}', + // The tree the other two sections are translated against, read off + // the set rather than found again, so the three cannot disagree. + '--include=${engine.root}', + for (final include in compilers.impellerIncludes) '--include=$include', + ], workingDirectory: generated.parent.path); + } on ProcessException catch (error) { + refused( + 'impellerc', + 'could not run ${compilers.impellerc}: ${error.message}', + ); + } + if (result.exitCode != 0 || !out.existsSync()) { + refused( + 'impellerc', + 'refused the GLSL generated from it, kept at ${generated.path} ' + '(exit ${result.exitCode}):\n${result.stdout}${result.stderr}', + ); + } + final bytes = out.readAsBytesSync(); + out.deleteSync(); + return bytes.buffer.asByteData(); +} + +ByteData _webgpu( + String name, + String source, + String resolved, + ShaderSet engine, + WgslStageCompiler compile, + File generated, + Never Function(String, String) refused, +) { + try { + final prepared = prepareStage( + resolved, + from: source, + fragment: true, + varyingLocations: bundleVaryingLocations(engine, [ + (file: source, fragment: true, resolved: resolved), + ]), + ); + final compiled = compile(prepared, name: name, fragment: true); + checkStd140Offsets(name, prepared, compiled.offsets); + final document = wgslSectionDocument( + vertex: const {}, + fragment: { + name: (wgsl: compiled.wgsl, prepared: prepared), + }, + ); + return Uint8List.fromList(utf8.encode(document)).buffer.asByteData(); + } on WgslPrepareError catch (error) { + refused('the WebGPU preparation', error.message); + } on WgslCompileError catch (error) { + refused( + 'the WebGPU compile', + '${error.message}\n(the GLSL it was generated from is kept at ' + '${generated.path})', + ); + } on WgslSectionError catch (error) { + refused('the WebGPU section', error.message); + } +} + +Map _readCache(String path) { + final file = File(path); + if (!file.existsSync()) return const {}; + try { + return (jsonDecode(file.readAsStringSync()) as Map).map( + (key, value) => MapEntry(key, value! as String), + ); + } on FormatException { + return const {}; + } on TypeError { + return const {}; + } +} + +void _writeCache(String path, Map cache) { + File(path) + ..parent.createSync(recursive: true) + ..writeAsStringSync(jsonEncode(cache)); +} diff --git a/packages/flutter3d_build/pubspec.yaml b/packages/flutter3d_build/pubspec.yaml index 5fcd8ef9c..025cbee09 100644 --- a/packages/flutter3d_build/pubspec.yaml +++ b/packages/flutter3d_build/pubspec.yaml @@ -44,6 +44,14 @@ dependencies: flutter3d_editor_core: ^0.8.0 flutter3d_sim: ^0.8.0 + # P8's material step: `ShaderBundle`, the container it writes, and + # `flutter3d_shaders`' `compile.dart` — the engine's headers a material + # includes, the WebGL2 and WebGPU translators, and the WGSL compile. Both + # flat; the translators moved out of the two web backends for exactly this + # line, since a package declaring the Flutter SDK cannot be listed here. + flutter3d_hardware: ^0.8.0 + flutter3d_shaders: ^0.8.2 + # `hook/build.dart`'s own contract — `ap-00`'s spike proved this is the half # of the build-hook feature that is stable on Flutter 3.47/Dart 3.13; the # other half, `package:data_assets`, stays out (see `ap-00`'s own writeup @@ -79,9 +87,6 @@ dependencies: image: ^4.10.1 dev_dependencies: - # `RecordingDevice`, which a test wraps round the impostor bake's device to - # count what the bake uploads against what it releases. - flutter3d_hardware: ^0.8.0 test: ^1.25.0 flutter_lints: ^6.0.0 diff --git a/packages/flutter3d_build/test/material_build_test.dart b/packages/flutter3d_build/test/material_build_test.dart new file mode 100644 index 000000000..8e3f46148 --- /dev/null +++ b/packages/flutter3d_build/test/material_build_test.dart @@ -0,0 +1,443 @@ +/// P8: a material source compiles, at build time, into a bundle every GPU +/// backend loads. +/// +/// dart test test/material_build_test.dart +/// +/// **`impellerc` is a stub here**, a shell script that writes known bytes +/// where `--sl=` points — `flutter3d_impeller`'s `build_shaders_test.dart` +/// does the same, for the same reason: the real binary lives in a Flutter +/// SDK's artifact cache, and what this file checks is what the build does +/// with its answer. The WGSL compile has the same seam, and one test runs the +/// real glslang and naga where a machine has them. The WebGL2 translation has +/// no seam: it is pure Dart and always runs for real. +library; + +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter3d_build/flutter3d_build.dart'; +import 'package:flutter3d_hardware/shader_bundle.dart'; +import 'package:flutter3d_shaders/compile.dart'; +import 'package:test/test.dart'; + +const String _rim = ''' +material RimLight { + param float rimPower = 2.0; + texture base = base_color_texture; + + fragment { + let facing = clamp(nDotV, 0.0, 1.0); + let rim = pow(1.0 - facing, rimPower); + let texel = sample(base, uv); + return vec4(albedo * texel.rgb + vec3(rim), alpha * texel.a); + } +} +'''; + +/// What the stub writes, so a test can tell its bytes in the bundle. +const String _impellerBytes = 'stub impellerc output'; + +/// glslang's offsets read straight off the reflection, which is what a +/// compiler that agrees with the std140 arithmetic would report. +CompiledStage _fakeWgsl( + PreparedStage stage, { + required String name, + required bool fragment, +}) => ( + wgsl: '// WGSL for $name\n@fragment fn main() {}\n', + offsets: >{ + for (final block in stage.blocks) + block.name: { + for (final member in block.members) member.name: member.offsetInBytes, + }, + }, +); + +void main() { + // The engine's real tree, found from this package the way a hook finds it + // from a project. + final engine = loadShaders(); + + late Directory project; + late _Log log; + + setUp(() { + project = Directory.systemTemp.createTempSync('f3d_material_'); + log = _Log(); + }); + tearDown(() => project.deleteSync(recursive: true)); + + File source(String relative, String text) => + File('${project.path}/assets_src/$relative') + ..parent.createSync(recursive: true) + ..writeAsStringSync(text); + + /// A stub `impellerc` that writes [_impellerBytes] to `--sl=`, or exits + /// non-zero with [refusal] on stderr; it records its arguments beside it. + MaterialCompilers stub({String? refusal, WgslStageCompiler? wgsl}) { + final script = File('${project.path}/sdk/impellerc') + ..parent.createSync(recursive: true) + ..writeAsStringSync(''' +#!/usr/bin/env bash +printf '%s\\n' "\$@" > "\$(dirname "\$0")/arguments.txt" +${refusal == null ? '' : 'echo "$refusal" >&2; exit 1'} +for arg in "\$@"; do + case "\$arg" in + --sl=*) printf '%s' '$_impellerBytes' > "\${arg#--sl=}" ;; + esac +done +'''); + final chmod = Process.runSync('chmod', ['+x', script.path]); + expect(chmod.exitCode, 0, reason: '${chmod.stderr}'); + return MaterialCompilers( + impellerc: script.path, + sdk: '3.13.0', + wgsl: wgsl ?? _fakeWgsl, + ); + } + + ShaderBundle readBundle(String relative) { + final bytes = File( + '${project.path}/flutter3d_generated/$relative', + ).readAsBytesSync(); + return ShaderBundle.decode(bytes.buffer.asByteData()); + } + + String text(ByteData bytes) => utf8.decode( + bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes), + ); + + group('with a stub impellerc', () { + test('one source becomes one bundle carrying all three sections', () { + // Mutation: drop `ShaderBundle.webgpuSection` from the map + // `compileMaterial` returns — the bundle still decodes and still loads + // on Impeller and WebGL2, and this fails on the section list. + source('fx/rim.f3dmat', _rim); + + final report = runMaterialBuild( + project, + log: log, + compilers: stub(), + engine: engine, + ); + + expect(report.built, [ + '${project.path}/assets_src/fx/rim.f3dmat', + ]); + final bundle = readBundle('fx/rim.f3dshaders'); + expect(bundle.name, 'RimLight'); + expect(bundle.sdk, '3.13.0'); + expect(bundle.stages.single.name, 'RimLight'); + expect(bundle.stages.single.fragment, isTrue); + expect(bundle.sections.keys, [ + ShaderBundle.impellerSection, + ShaderBundle.webglSection, + ShaderBundle.webgpuSection, + ]); + + // Impeller's is impellerc's answer, as it is. + expect( + text(bundle.section(ShaderBundle.impellerSection)!), + _impellerBytes, + ); + + // WebGL2's is the translation, which is real here: GLSL ES 3.00 with + // the engine's headers inlined and the material's body in it. + final webgl = decodeWebGlSection( + bundle.section(ShaderBundle.webglSection)!, + ); + expect(webgl.vertex, isEmpty); + expect(webgl.fragment['RimLight'], startsWith('#version 300 es')); + expect(webgl.fragment['RimLight'], contains('struct Surface')); + expect( + webgl.fragment['RimLight'], + contains('texture(base_color_texture'), + ); + + // WebGPU's carries the WGSL and the reflection the browser cannot be + // asked for: the sampler the material reads has a group and binding. + final webgpu = + jsonDecode(text(bundle.section(ShaderBundle.webgpuSection)!)) + as Map; + expect(webgpu['version'], kSectionVersion); + final stage = + (webgpu['fragment']! as Map)['RimLight']! + as Map; + expect(stage['wgsl'], contains('@fragment')); + expect( + (stage['samplers']! as List).map( + (s) => (s! as Map)['name'], + ), + contains('base_color_texture'), + ); + }); + + test('impellerc is asked for one fragment stage, against the engine\'s ' + 'headers', () { + // Mutation: drop the `--include=${engine.root}` argument — the real + // impellerc then fails on `#include `, which the stub + // cannot, so the argument itself is what is held. + source('rim.f3dmat', _rim); + runMaterialBuild(project, log: log, compilers: stub(), engine: engine); + + final arguments = File( + '${project.path}/sdk/arguments.txt', + ).readAsLinesSync(); + final manifest = + jsonDecode( + arguments + .firstWhere((a) => a.startsWith('--shader-bundle=')) + .substring('--shader-bundle='.length), + ) + as Map; + expect(manifest.keys, ['RimLight']); + expect( + (manifest['RimLight']! as Map)['type'], + 'fragment', + ); + expect(arguments, contains('--include=${engine.root}')); + }); + + test('every source and every engine header is a dependency', () { + // Mutation: return only the material sources from + // `MaterialBuildReport.dependencies` — an edit to `surface.glsl`, which + // every material includes, then leaves every bundle stale until some + // other file changes. + source('rim.f3dmat', _rim); + final report = runMaterialBuild( + project, + log: log, + compilers: stub(), + engine: engine, + ); + expect( + report.dependencies, + containsAll([ + '${project.path}/assets_src/rim.f3dmat', + '${engine.root}/lib/surface.glsl', + ]), + ); + }); + + test('an unchanged source is not compiled again, and a removed one takes ' + 'its bundle with it', () { + // Mutation: skip the stale sweep at the top of `runMaterialBuild` — + // the deleted material's bundle stays in `flutter3d_generated/`, which + // is bundled whole, and ships. + final rim = source('rim.f3dmat', _rim); + runMaterialBuild(project, log: log, compilers: stub(), engine: engine); + + final again = runMaterialBuild( + project, + log: log, + compilers: stub(), + engine: engine, + ); + expect(again.built, isEmpty); + expect(again.skipped, [rim.path]); + + rim.deleteSync(); + runMaterialBuild(project, log: log, compilers: stub(), engine: engine); + expect( + File('${project.path}/flutter3d_generated/rim.f3dshaders').existsSync(), + isFalse, + ); + }); + + test('a source with a mistake in it names its file, line and column, ' + 'and writes nothing', () { + // Mutation: rethrow the parser's `MaterialSyntaxError` unwrapped — the + // message keeps its line but loses the file, and with two materials in + // a project the author is left to guess which. + final bad = source('fx/bad.f3dmat', ''' +material Bad { + fragment { + let x = nope(1.0); + return vec4(x); + } +} +'''); + + expect( + () => runMaterialBuild( + project, + log: log, + compilers: stub(), + engine: engine, + ), + throwsA( + isA() + .having((e) => e.source, 'source', bad.path) + .having((e) => e.line, 'line', 3) + .having( + (e) => e.toString(), + 'text', + startsWith('${bad.path}:3:'), + ), + ), + ); + expect( + File( + '${project.path}/flutter3d_generated/fx/bad.f3dshaders', + ).existsSync(), + isFalse, + ); + }); + + test('a compiler refusing the generated GLSL names the source and keeps ' + 'the GLSL where its line can be opened', () { + // Mutation: delete the generated `.frag` before calling impellerc's + // result an error — the message then points at a file that is gone, + // and the line impellerc named is a line nobody can read. + final rim = source('rim.f3dmat', _rim); + + expect( + () => runMaterialBuild( + project, + log: log, + compilers: stub(refusal: 'RimLight.frag:12: syntax error'), + engine: engine, + ), + throwsA( + isA() + .having((e) => e.source, 'source', rim.path) + .having( + (e) => e.message, + 'message', + allOf( + contains('impellerc'), + contains('RimLight.frag:12: syntax error'), + ), + ), + ), + ); + expect( + File( + '${project.path}/.dart_tool/flutter3d_build/materials/RimLight.frag', + ).existsSync(), + isTrue, + ); + }); + + test('without glslang and naga the bundle carries the other two sections, ' + 'and says so', () { + // Mutation: make a null `wgsl` an error — every machine without the + // Vulkan SDK, CI among them, then fails the whole build over one + // backend's tooling. + source('rim.f3dmat', _rim); + final compilers = stub(); + runMaterialBuild( + project, + log: log, + compilers: MaterialCompilers( + impellerc: compilers.impellerc, + sdk: compilers.sdk, + ), + engine: engine, + ); + expect(readBundle('rim.f3dshaders').sections.keys, [ + ShaderBundle.impellerSection, + ShaderBundle.webglSection, + ]); + expect(log.text, contains('no "webgpu" section')); + }); + }, skip: Platform.isWindows ? 'the stub impellerc is a bash script' : null); + + test('a project with no materials looks for no compiler and no engine', () { + // Mutation: call `MaterialCompilers.locate()` before checking the plan — + // this process is not always a Flutter SDK's Dart, and a project with + // only models in it would then fail to build for want of impellerc. + source('hero.obj', 'v 0 0 0\n'); + final report = runMaterialBuild(project, log: log); + expect(report.built, isEmpty); + expect(report.dependencies, isEmpty); + }); + + test('the real glslang and naga carry a material into WGSL', () { + // The fake above agrees with the reflection by construction; this is the + // one place the two compilers get to disagree with it. + source('rim.f3dmat', _rim); + final script = File('${project.path}/impellerc') + ..writeAsStringSync( + '#!/usr/bin/env bash\n' + 'for a in "\$@"; do case "\$a" in --sl=*) : > "\${a#--sl=}";; esac; ' + 'done\n', + ); + Process.runSync('chmod', ['+x', script.path]); + runMaterialBuild( + project, + log: log, + compilers: MaterialCompilers( + impellerc: script.path, + sdk: '3.13.0', + wgsl: (stage, {required name, required fragment}) => + compileStage(stage.glsl, name: name, fragment: fragment), + ), + engine: engine, + ); + final bundle = readBundle('rim.f3dshaders'); + final document = + jsonDecode(text(bundle.section(ShaderBundle.webgpuSection)!)) + as Map; + final wgsl = + ((document['fragment']! as Map)['RimLight']! + as Map)['wgsl']! + as String; + expect(wgsl, contains('@fragment')); + expect(wgsl, contains('base_color_texture')); + }, skip: _missing() ?? (Platform.isWindows ? 'bash stub' : null)); +} + +/// Why the real-compiler test cannot run here, or null when it can. +String? _missing() { + for (final program in [kGlslang, kNaga]) { + try { + Process.runSync(program, const ['--version']); + } on ProcessException { + return '$program is not on PATH'; + } + } + return null; +} + +/// An [IOSink] that keeps what it was told. +final class _Log implements IOSink { + final StringBuffer _buffer = StringBuffer(); + + String get text => _buffer.toString(); + + @override + Encoding encoding = utf8; + + @override + void write(Object? object) => _buffer.write(object); + + @override + void writeln([Object? object = '']) => _buffer.writeln(object); + + @override + void writeAll(Iterable objects, [String separator = '']) => + _buffer.writeAll(objects, separator); + + @override + void writeCharCode(int charCode) => _buffer.writeCharCode(charCode); + + @override + void add(List data) => _buffer.write(utf8.decode(data)); + + @override + void addError(Object error, [StackTrace? stackTrace]) {} + + @override + Future addStream(Stream> stream) async {} + + @override + Future flush() async {} + + @override + Future close() async {} + + @override + Future get done async {} +} diff --git a/packages/flutter3d_cpu/CHANGELOG.md b/packages/flutter3d_cpu/CHANGELOG.md index 718f2f86b..fcafaea3a 100644 --- a/packages/flutter3d_cpu/CHANGELOG.md +++ b/packages/flutter3d_cpu/CHANGELOG.md @@ -10,16 +10,11 @@ edges by the same amounts; its `smaa-teapot` reference matches Impeller's to the pixel. -## 0.8.2+1 - -**Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart -`^3.12.2` and `vector_math` 2.4.3, which were what this repository is built with rather than -what the package needs. A workspace that supports Flutter 3.44, Flame's among -them, could not depend on it. Nothing else changed. **The decal stage, as the GLSL has it.** `DecalShader` mirrors `post/decal.frag` line for line, the mip level of each picture chosen from the same footprint the GLSL computes by hand from neighbouring texels of the surface buffer. + **`P4`'s two stages.** `PlanarReflectionShader` mirrors `planar_reflection.frag` and `RenderTextureEncodeShader` mirrors `render_texture_encode.frag`; `planar_reflection_test.dart` holds ten @@ -35,10 +30,18 @@ mirror, and what was below the mirror came up through it here and on no other backend. A fragment with a window depth outside `[0, 1]` is dropped now, which is the same cut as clipping the triangle, because that depth is linear across the screen. No golden of this set moved. + **`SkyPhysicalShader`**, the software `SkyPhysical`, with the stars' hash rounded to single precision as the GPU computes it, and **height fog in `applyFog`**, as `color.glsl` has it. +## 0.8.2+1 + +**Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart +`^3.12.2` and `vector_math` 2.4.3, which were what this repository is built with rather than +what the package needs. A workspace that supports Flutter 3.44, Flame's among +them, could not depend on it. Nothing else changed. + ## 0.8.2 **The PCSS receiver-plane bias, as the GLSL has it.** The directional diff --git a/packages/flutter3d_shaders/CHANGELOG.md b/packages/flutter3d_shaders/CHANGELOG.md index 1c93fde92..58e2615c4 100644 --- a/packages/flutter3d_shaders/CHANGELOG.md +++ b/packages/flutter3d_shaders/CHANGELOG.md @@ -6,17 +6,12 @@ with the `SmaaInfo` block. A bundle must answer to them; the renderer falls back to FXAA when one does not. -## 0.8.2+1 - -**Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart -`^3.12.2`, which were what this repository is built with rather than -what the package needs. A workspace that supports Flutter 3.44, Flame's among -them, could not depend on it. Nothing else changed. **A decal stage.** `post/decal.frag` (`Decal`) paints up to sixteen projected boxes, reading four pictures, over the point the surface buffer names under each pixel. It writes a factor and a term for two blends, the albedo swapped under the light the albedo buffer lets it read back, and the colour an unlit surface and an emissive decal add. + **Two stages for `P4`.** `lighting/planar_reflection.frag` lays a mirrored picture over a reflector's surface, read by the fragment's place in its view and weighted by Schlick's Fresnel; it declares no surface buffer, as @@ -25,6 +20,7 @@ light into the sRGB bytes a material's map is read as, turning the rows over where the backend draws its first row at the bottom. Both are in the bundle, in `kRequiredShaders` and in `stageBindings`, `uniformBlocks` and `typed_blocks.dart`. + **`SkyPhysical` and `SkyPhysicalVertex`**, the physical sky: single scattering by molecules and haze marched per pixel, sixteen samples along the view and eight towards the sun from each, the disc and the stars dimmed by the @@ -35,6 +31,26 @@ gradient's preset does. and `FogInfo.fog.w` the density at the eye; a falloff of nought takes the old path unchanged. +**The WebGL2 and WebGPU translators live here now, behind +`translate.dart` and `compile.dart`.** `flutter3d_build`'s material step has +to translate a project's materials from a hook process that cannot resolve a +package declaring the Flutter SDK, and `flutter3d_webgl` and +`flutter3d_webgpu` both declare one. None of the moved files ever needed the +SDK. `translate.dart` holds the pure parts: `translateGlsl` and +`resolveIncludes`, `prepareStage`, and the writers for both sections. +`compile.dart` adds the parts that need a machine: `loadShaders` (which now +takes `from:`, and whose `ShaderSet` carries the `root` it read), +`compileStage` through glslang and naga, and `bundleVaryingLocations`, which +used to be private to `pack_wgsl_section.dart`. The barrel exports neither, +so an application carries no compiler. + +## 0.8.2+1 + +**Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart +`^3.12.2`, which were what this repository is built with rather than +what the package needs. A workspace that supports Flutter 3.44, Flame's among +them, could not depend on it. Nothing else changed. + ## 0.8.2 **A PCSS tap allows for the receiver's own slope.** `ShadowFactor` in diff --git a/packages/flutter3d_shaders/lib/compile.dart b/packages/flutter3d_shaders/lib/compile.dart new file mode 100644 index 000000000..ad0007ad2 --- /dev/null +++ b/packages/flutter3d_shaders/lib/compile.dart @@ -0,0 +1,14 @@ +/// Everything [translate.dart] has, and the half that needs a machine: the +/// engine's sources read off disk, glslang and naga run as processes, and a +/// bundle's varyings numbered against the engine's. +/// +/// For build scripts and build hooks only. An application never imports this: +/// it carries no shader compiler, and `dart:io` is no help in a browser. +/// +/// [translate.dart]: translate.dart +library; + +export 'src/bundle_varyings.dart'; +export 'src/source_package.dart'; +export 'src/wgsl_compiler.dart'; +export 'translate.dart'; diff --git a/packages/flutter3d_shaders/lib/src/bundle_varyings.dart b/packages/flutter3d_shaders/lib/src/bundle_varyings.dart new file mode 100644 index 000000000..24078121b --- /dev/null +++ b/packages/flutter3d_shaders/lib/src/bundle_varyings.dart @@ -0,0 +1,72 @@ +/// Varying locations for a loadable bundle, held against the engine's own. +/// +/// **A loaded stage is paired with a stage it was not compiled beside.** A +/// bundle's fragment stage runs against the engine's vertex stages, WebGPU +/// does not link, and the two modules are joined by `@location` alone — so a +/// varying's number has to be the number the engine already committed to +/// `flutter3d_webgpu`'s `lib/engine_shaders.dart`. Numbering a bundle on its +/// own would give `v_normal` whatever index its own family handed out, the +/// pipeline would still build, and the fragment stage would read normals out +/// of some other varying's slot. +/// +/// Written for `flutter3d_webgpu/tool/pack_wgsl_section.dart` and moved here +/// when `flutter3d_build` needed the same answer for a project's materials +/// (P8): two copies of this rule would be two rules, and the one that drifted +/// would draw wrongly rather than fail. +library; + +import 'glsl_to_wgsl.dart'; +import 'glsl_translate.dart'; +import 'source_package.dart'; + +/// One stage of a bundle, with its `#include`s already expanded. +typedef BundleStageSource = ({String file, bool fragment, String resolved}); + +/// A location per varying name, agreeing with [engine]'s. +/// +/// Computed over both manifests, then held against the engine's alone. Throws +/// [WgslPrepareError] when the bundle would move one of the engine's +/// varyings: the failure is a refusal to pack rather than a section that +/// would compile and read the wrong slot. +Map bundleVaryingLocations( + ShaderSet engine, + Iterable stages, +) { + final theirs = >[ + for (final entry in engine.stages.values) + scanVaryings( + _resolveEngine(engine, entry.file), + from: entry.file, + fragment: entry.fragment, + ), + ]; + final ours = >[ + for (final stage in stages) + scanVaryings(stage.resolved, from: stage.file, fragment: stage.fragment), + ]; + + final engineOnly = assignVaryingLocations(theirs); + final together = assignVaryingLocations(>[...theirs, ...ours]); + + for (final entry in engineOnly.entries) { + if (together[entry.key] == entry.value) continue; + throw WgslPrepareError( + 'this bundle moves the engine\'s varying "${entry.key}" from location ' + '${entry.value} to ${together[entry.key]}. A stage packed here is paired ' + 'with a stage the engine already compiled, and WebGPU joins the two by ' + 'location alone, so the numbering cannot be renegotiated by a bundle. ' + 'A varying declared beside the engine\'s in one stage joins their ' + 'family and renumbers it — give it a name of its own, or declare it in ' + 'a stage that shares nothing with the engine\'s.', + ); + } + return together; +} + +String _resolveEngine(ShaderSet engine, String file) { + try { + return resolveIncludes(engine.sources[file]!, engine.sources, from: file); + } on GlslTranslateError catch (error) { + throw WgslPrepareError('the engine\'s $file: ${error.message}'); + } +} diff --git a/packages/flutter3d_webgpu/lib/src/glsl_to_wgsl.dart b/packages/flutter3d_shaders/lib/src/glsl_to_wgsl.dart similarity index 96% rename from packages/flutter3d_webgpu/lib/src/glsl_to_wgsl.dart rename to packages/flutter3d_shaders/lib/src/glsl_to_wgsl.dart index 2674cb490..49b4e5205 100644 --- a/packages/flutter3d_webgpu/lib/src/glsl_to_wgsl.dart +++ b/packages/flutter3d_shaders/lib/src/glsl_to_wgsl.dart @@ -13,22 +13,20 @@ /// is the only part that needs a process, and by then the shape of the answer /// is already settled. /// -/// Under `lib/src/` and not under `tool/`, the way -/// `flutter3d_webgl/lib/src/glsl_translate.dart` is: nothing a consumer builds -/// reaches it, since `lib/engine_shaders.dart` is a table and not a -/// translation, and a test can import it. The browser test runner serves a -/// package from `test/` and cannot read a sibling directory, so a translator in -/// `tool/` is a translator no test in this package could name. +/// **Here, in `flutter3d_shaders`, rather than in `flutter3d_webgpu` where it +/// was written** — P8. A project's build hook compiles its own materials into +/// a WebGPU section, and that hook is a process with no Flutter SDK to +/// resolve; `flutter3d_webgpu` declares one, so nothing under its `lib/` is +/// reachable from there at all. The translator never needed the SDK, only the +/// package it lived in did. `package:flutter3d_shaders/translate.dart` is the +/// door; the WebGPU backend reads the section this feeds and keeps everything +/// that talks to a browser. /// /// **The `#include`s are already resolved by the time anything here runs, and -/// the caller resolves them.** `resolveIncludes` belongs to -/// `flutter3d_webgl/lib/src/glsl_translate.dart` and is reused rather than -/// rewritten — a second answer to "which headers does this shader really pull -/// in" is how two backends drift apart in the one place neither can see. It is -/// called from `tool/generate_shaders.dart` and from the tests rather than from -/// here because that package is a *dev* dependency: a backend that depended on -/// another backend at run time would not be two backends, and a file under -/// `lib/` may only import what ships. +/// the caller resolves them** — with `resolveIncludes` from +/// `glsl_translate.dart` beside this file, reused rather than rewritten: a +/// second answer to "which headers does this shader really pull in" is how two +/// backends drift apart in the one place neither can see. /// /// ## Why the source has to be edited at all /// diff --git a/packages/flutter3d_webgl/lib/src/glsl_translate.dart b/packages/flutter3d_shaders/lib/src/glsl_translate.dart similarity index 96% rename from packages/flutter3d_webgl/lib/src/glsl_translate.dart rename to packages/flutter3d_shaders/lib/src/glsl_translate.dart index 4d26eb3a3..554af2b66 100644 --- a/packages/flutter3d_webgl/lib/src/glsl_translate.dart +++ b/packages/flutter3d_shaders/lib/src/glsl_translate.dart @@ -14,6 +14,11 @@ /// Pure text in, pure text out, so every rule below is testable without a /// browser. What it cannot check is whether the result *compiles*; that is what /// the harness is for. +/// +/// **In `flutter3d_shaders` since P8**, moved out of `flutter3d_webgl` for the +/// reason `glsl_to_wgsl.dart` beside it gives: a project's build hook +/// translates its materials with it, and cannot resolve a package that +/// declares the Flutter SDK. library; /// A source file the translator can read. diff --git a/packages/flutter3d_webgpu/lib/src/source_package.dart b/packages/flutter3d_shaders/lib/src/source_package.dart similarity index 73% rename from packages/flutter3d_webgpu/lib/src/source_package.dart rename to packages/flutter3d_shaders/lib/src/source_package.dart index ddb25bd4d..0eb670797 100644 --- a/packages/flutter3d_webgpu/lib/src/source_package.dart +++ b/packages/flutter3d_shaders/lib/src/source_package.dart @@ -6,27 +6,25 @@ /// somebody added a `.frag` and forgot to name it in the manifest — which is /// the day the engine starts asking for a stage no backend has. /// -/// The twin of `flutter3d_webgl/tool/source_package.dart`, and copied rather -/// than shared: a `tool/` directory is a program and not a library, so nothing -/// under one package's `tool/` is importable from another's. Twenty lines of -/// pub plumbing is a cheaper duplicate than making one backend's build scripts -/// part of another backend's published surface. +/// Reached through `package:flutter3d_shaders/compile.dart`, never the barrel: +/// it reads files, and `dart:io` compiles for the web to stubs that throw, +/// which is why a browser test importing it still loads. /// -/// **`dart:io` under `lib/`, which reads oddly in a backend that only runs in a -/// browser.** It is here rather than in `tool/` because the browser test runner -/// serves a package from `test/` and cannot read a sibling directory, so a test -/// may only import what is under `lib/`. Nothing this package exports reaches -/// it, so nothing a consumer builds carries it; and `dart:io` compiles for the -/// web to stubs that throw, which is why the browser run gets through this file -/// rather than failing on it. +/// **`from` is P8's.** A build hook runs with the working directory wherever +/// the Flutter tool left it, and has to find `flutter3d_shaders` through the +/// project it is building rather than through itself. library; import 'dart:convert'; import 'dart:io'; /// Every shader file, keyed the way an `#include <…>` spells it, beside the -/// manifest's entry points in the order it lists them. +/// manifest's entry points in the order it lists them, and the `shaders/` +/// directory both were read from — which `impellerc` needs as an include +/// root, and which has to be this one rather than one found again, or two +/// sections of one bundle could be built against two trees. typedef ShaderSet = ({ + String root, Map sources, Map stages, }); @@ -35,8 +33,8 @@ typedef ShaderSet = ({ /// /// Throws [StateError] with a message worth showing a person when the package /// is not there or has no `shaders/` in it. -ShaderSet loadShaders() { - final root = packageRoot('flutter3d_shaders'); +ShaderSet loadShaders({String? from}) { + final root = packageRoot('flutter3d_shaders', from: from); final shaders = Directory('$root/shaders'); if (!shaders.existsSync()) throw StateError('no shaders/ in $root'); @@ -72,11 +70,11 @@ ShaderSet loadShaders() { stages[name] = (file: file, fragment: entry['type'] == 'fragment'); }); - return (sources: sources, stages: stages); + return (root: shaders.path, sources: sources, stages: stages); } /// The root directory of [name], through `.dart_tool/package_config.json` at or -/// above the working directory. +/// above [from], the working directory when it is null. /// /// The one mapping pub guarantees for path, git and hosted dependencies alike. /// A relative path would work here and break the day this package is consumed @@ -84,8 +82,9 @@ ShaderSet loadShaders() { /// /// `rootUri` is a URI and relative for a workspace member, resolved against /// `.dart_tool/` — which is why this is `Uri.resolve` and not a string join. -String packageRoot(String name) { - var dir = Directory.current; +String packageRoot(String name, {String? from}) { + final start = Directory(from ?? Directory.current.path).absolute; + var dir = start; while (true) { final config = File('${dir.path}/.dart_tool/package_config.json'); if (config.existsSync()) { @@ -107,9 +106,7 @@ String packageRoot(String name) { } final parent = dir.parent; if (parent.path == dir.path) { - throw StateError( - 'no .dart_tool/package_config.json above ${Directory.current.path}', - ); + throw StateError('no .dart_tool/package_config.json above ${start.path}'); } dir = parent; } diff --git a/packages/flutter3d_webgl/lib/src/webgl_bundle_section.dart b/packages/flutter3d_shaders/lib/src/webgl_bundle_section.dart similarity index 72% rename from packages/flutter3d_webgl/lib/src/webgl_bundle_section.dart rename to packages/flutter3d_shaders/lib/src/webgl_bundle_section.dart index 5dc4766c4..9417711f3 100644 --- a/packages/flutter3d_webgl/lib/src/webgl_bundle_section.dart +++ b/packages/flutter3d_shaders/lib/src/webgl_bundle_section.dart @@ -1,15 +1,18 @@ /// The WebGL section of a `ShaderBundle`: GLSL ES 3.00 sources by name. /// /// A JSON document, `{"vertex": {name: source}, "fragment": {name: source}}`, -/// as UTF-8. Text rather than anything compiled, because that is what this -/// backend compiles from: the browser is the compiler, and there is nothing -/// to run ahead of time except the translation from the engine's GLSL, which -/// `tool/pack_shaders.dart` does when it writes the section. +/// as UTF-8. Text rather than anything compiled, because that is what the +/// WebGL backend compiles from: the browser is the compiler, and there is +/// nothing to run ahead of time except the translation from the engine's GLSL, +/// which `flutter3d_webgl/tool/pack_shaders.dart` and `flutter3d_build`'s +/// material step do when they write the section. /// -/// **No `package:web` here, on purpose.** The packer runs on the Dart VM, -/// where a browser binding does not load, and it has to write exactly what -/// the device reads. One file both import is how the two stay the same -/// document. +/// **No `package:web` here, on purpose.** The packers run on the Dart VM, +/// where a browser binding does not load, and they have to write exactly what +/// the device reads. One file every side imports is how they stay the same +/// document — which is also why it lives in `flutter3d_shaders` rather than in +/// `flutter3d_webgl`: a build hook cannot resolve a package that declares the +/// Flutter SDK. library; import 'dart:convert'; diff --git a/packages/flutter3d_webgpu/lib/src/wgsl_compiler.dart b/packages/flutter3d_shaders/lib/src/wgsl_compiler.dart similarity index 96% rename from packages/flutter3d_webgpu/lib/src/wgsl_compiler.dart rename to packages/flutter3d_shaders/lib/src/wgsl_compiler.dart index 2f2dd7e6d..e78b413cb 100644 --- a/packages/flutter3d_webgpu/lib/src/wgsl_compiler.dart +++ b/packages/flutter3d_shaders/lib/src/wgsl_compiler.dart @@ -35,10 +35,9 @@ /// the packer already made. The packer numbers them, so the reflection is a /// by-product of the numbering rather than a reconstruction of it. /// -/// Under `lib/src/` for the reason `source_package.dart` gives: the browser -/// test runner cannot read a directory beside `test/`, so a build-time file no -/// test can import is a build-time file no test can measure. Nothing this -/// package exports reaches it. +/// Reached through `package:flutter3d_shaders/compile.dart` only, never the +/// package's barrel: it runs processes, and nothing a consumer's application +/// builds has a reason to carry that. library; import 'dart:io'; diff --git a/packages/flutter3d_webgpu/lib/src/wgsl_section.dart b/packages/flutter3d_shaders/lib/src/wgsl_section.dart similarity index 78% rename from packages/flutter3d_webgpu/lib/src/wgsl_section.dart rename to packages/flutter3d_shaders/lib/src/wgsl_section.dart index 159d73691..723ac051c 100644 --- a/packages/flutter3d_webgpu/lib/src/wgsl_section.dart +++ b/packages/flutter3d_shaders/lib/src/wgsl_section.dart @@ -1,36 +1,29 @@ /// What a packer writes into a bundle's `webgpu` section, as text. /// -/// The reading half of this document is `lib/src/webgpu_bundle_section.dart`, -/// and that file says its own most valuable line out loud: no `package:web` -/// here and no `dart:js_interop`, so that the packer on the VM and the device -/// in the browser read one document rather than two transcriptions of it. +/// The reading half of this document is `flutter3d_webgpu`'s +/// `lib/src/webgpu_bundle_section.dart`, and that file says its own most +/// valuable line out loud: no `package:web` here and no `dart:js_interop`, so +/// that the packer on the VM and the device in the browser read one document +/// rather than two transcriptions of it. /// /// **This is the writing half, and it could not be that same file.** The codec -/// there reaches `flutter3d_hardware`'s barrel for `VertexFormat`, the barrel -/// reaches `package:flutter`, and a bundle is packed by a `dart run` script -/// with no `dart:ui` under it — `flutter3d_webgl/tool/pack_shaders.dart` is one -/// and `tool/pack_wgsl_section.dart` beside this file is its sibling. Importing -/// the codec from either is a compile error naming `dart:ui`, which is the -/// measurement that put these twenty lines of `jsonEncode` here rather than in -/// the codec. +/// there reaches `flutter3d_hardware`'s barrel for `VertexFormat`, and a bundle +/// is packed by a process with no `dart:ui` under it — +/// `flutter3d_webgl/tool/pack_shaders.dart`, +/// `flutter3d_webgpu/tool/pack_wgsl_section.dart`, and since P8 a project's +/// own build hook through `flutter3d_build`. That last one cannot even resolve +/// `flutter3d_webgpu`, which declares the Flutter SDK, and is why this writer +/// lives in `flutter3d_shaders` now rather than beside its reader. /// -/// What holds the two halves together instead is `test/wgsl_section_test.dart`, -/// which writes a document with every field populated and reads it back with -/// `decodeWebGpuSection`, field for field. A writer nobody read back would be -/// the real duplication — the JSON would be right about the shape and wrong -/// about a spelling, and the first thing to notice would be a bundle refused on -/// somebody's machine. +/// What holds the two halves together instead is `flutter3d_webgpu`'s +/// `test/wgsl_section_test.dart`, which writes a document with every field +/// populated and reads it back with `decodeWebGpuSection`, field for field. A +/// writer nobody read back would be the real duplication — the JSON would be +/// right about the shape and wrong about a spelling, and the first thing to +/// notice would be a bundle refused on somebody's machine. /// -/// **Under `lib/src/` and not under `tool/`**, which reads oddly for a file only -/// a build script calls, and is the same decision `glsl_to_wgsl.dart`, -/// `wgsl_compiler.dart` and `source_package.dart` all record: the browser test -/// runner serves this package from `test/` and cannot read a sibling directory, -/// so a build-time file under `tool/` is a build-time file no test can measure. -/// It was written there first and the whole Chrome pass failed to compile on -/// the import, which is as clear a measurement of that rule as it gets. Nothing -/// this package exports reaches this file, so nothing a consumer builds carries -/// it; and it imports `dart:convert` and the translator alone, so the round trip -/// above runs in a browser as readily as on the VM. +/// It imports `dart:convert` and the translator alone, so the round trip above +/// runs in a browser as readily as on the VM. /// /// One spelling in particular is why that test earns its keep. /// [PreparedSampler.dimension] carries a `WebGpuTextureDimension`'s *Dart* name diff --git a/packages/flutter3d_shaders/lib/translate.dart b/packages/flutter3d_shaders/lib/translate.dart new file mode 100644 index 000000000..999ad864c --- /dev/null +++ b/packages/flutter3d_shaders/lib/translate.dart @@ -0,0 +1,18 @@ +/// The engine's GLSL in the dialects the web backends read: GLSL ES 3.00 for +/// WebGL2, and the prepared GLSL and reflection WebGPU's section is made of. +/// +/// **Pure text in, text out**, with no `dart:io` anywhere under it, so a +/// browser test and a backend's own library can import it as readily as a +/// build script. What runs a compiler is `compile.dart`. +/// +/// **Here rather than in the two backends that wrote it** — P8. A project's +/// build hook compiles its own materials for every backend, and the hook is a +/// process that cannot resolve a package declaring the Flutter SDK, which +/// both `flutter3d_webgl` and `flutter3d_webgpu` do. None of these files ever +/// needed the SDK; only the packages they lived in did. +library; + +export 'src/glsl_to_wgsl.dart'; +export 'src/glsl_translate.dart'; +export 'src/webgl_bundle_section.dart'; +export 'src/wgsl_section.dart'; diff --git a/packages/flutter3d_webgl/CHANGELOG.md b/packages/flutter3d_webgl/CHANGELOG.md index ca6e9e446..151543ddd 100644 --- a/packages/flutter3d_webgl/CHANGELOG.md +++ b/packages/flutter3d_webgl/CHANGELOG.md @@ -5,19 +5,27 @@ - **SMAA 1x's three stages translated**, from the same sources as every other stage; `smaa-teapot` is in the browser reference set. -## 0.8.2+1 - -**Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart -`^3.12.2` and `vector_math` 2.4.3, which were what this repository is built with rather than -what the package needs. A workspace that supports Flutter 3.44, Flame's among -them, could not depend on it. Nothing else changed. **The generated tables carry the decal stage** of `flutter3d_shaders`. + **The generated tables carry `PlanarReflection` and `RenderTextureEncode`**, and the `planar-mirror` and `render-texture` goldens are in this set. + **The generated tables carry `SkyPhysical`, `SkyPhysicalVertex` and the height fog in `ApplyFog`.** +**The GLSL ES translator and the section codec moved to +`flutter3d_shaders`**, so a build hook with no Flutter SDK can use them. +`encodeWebGlSection`, `decodeWebGlSection` and `WebGlSectionSources` are +still exported from this package's barrel under the same names. + +## 0.8.2+1 + +**Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart +`^3.12.2` and `vector_math` 2.4.3, which were what this repository is built with rather than +what the package needs. A workspace that supports Flutter 3.44, Flame's among +them, could not depend on it. Nothing else changed. + ## 0.8.2 **The generated tables carry the PCSS receiver-plane bias** of diff --git a/packages/flutter3d_webgl/lib/flutter3d_webgl.dart b/packages/flutter3d_webgl/lib/flutter3d_webgl.dart index 198833bc8..606fd4e2c 100644 --- a/packages/flutter3d_webgl/lib/flutter3d_webgl.dart +++ b/packages/flutter3d_webgl/lib/flutter3d_webgl.dart @@ -25,12 +25,15 @@ /// of date. library; -export 'src/open.dart'; - /// The `webgl` section of a loadable bundle, as the packer writes it and the -/// device reads it. No browser in it, so a harness on the VM can write one. +/// device reads it. No browser in it, so a harness on the VM can write one; +/// it lives in `flutter3d_shaders` since P8, where a build hook with no +/// Flutter SDK can write one too, and is still exported here by name. +export 'package:flutter3d_shaders/translate.dart' + show WebGlSectionSources, decodeWebGlSection, encodeWebGlSection; + +export 'src/open.dart'; export 'src/webgl_backend_registration.dart'; -export 'src/webgl_bundle_section.dart'; export 'src/webgl_device.dart'; export 'src/webgl_frame_presenter.dart'; export 'src/webgl_shaders.dart'; diff --git a/packages/flutter3d_webgl/lib/src/webgl_loaded_shaders.dart b/packages/flutter3d_webgl/lib/src/webgl_loaded_shaders.dart index 7baadd032..8a185baf7 100644 --- a/packages/flutter3d_webgl/lib/src/webgl_loaded_shaders.dart +++ b/packages/flutter3d_webgl/lib/src/webgl_loaded_shaders.dart @@ -12,9 +12,9 @@ library; import 'dart:typed_data'; import 'package:flutter3d_hardware/flutter3d_hardware.dart'; +import 'package:flutter3d_shaders/translate.dart' show decodeWebGlSection; import 'package:web/web.dart' as web; -import 'webgl_bundle_section.dart'; import 'webgl_shaders.dart'; /// A [LoadedShaderLibrary] over sources a bundle carried. diff --git a/packages/flutter3d_webgl/test/bundle_section_test.dart b/packages/flutter3d_webgl/test/bundle_section_test.dart index cb7042261..ea302e755 100644 --- a/packages/flutter3d_webgl/test/bundle_section_test.dart +++ b/packages/flutter3d_webgl/test/bundle_section_test.dart @@ -9,7 +9,7 @@ library; import 'dart:convert'; import 'dart:typed_data'; -import 'package:flutter3d_webgl/src/webgl_bundle_section.dart'; +import 'package:flutter3d_shaders/translate.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/flutter3d_webgl/test/glsl_translate_test.dart b/packages/flutter3d_webgl/test/glsl_translate_test.dart index e5f570331..ece366bf0 100644 --- a/packages/flutter3d_webgl/test/glsl_translate_test.dart +++ b/packages/flutter3d_webgl/test/glsl_translate_test.dart @@ -7,7 +7,7 @@ /// at offsets nobody wrote to. library; -import 'package:flutter3d_webgl/src/glsl_translate.dart'; +import 'package:flutter3d_shaders/translate.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/flutter3d_webgl/tool/generate_shaders.dart b/packages/flutter3d_webgl/tool/generate_shaders.dart index 9b07928d1..cbfae3b3d 100644 --- a/packages/flutter3d_webgl/tool/generate_shaders.dart +++ b/packages/flutter3d_webgl/tool/generate_shaders.dart @@ -16,7 +16,7 @@ library; import 'dart:convert'; import 'dart:io'; -import 'package:flutter3d_webgl/src/glsl_translate.dart'; +import 'package:flutter3d_shaders/translate.dart'; import 'source_package.dart'; diff --git a/packages/flutter3d_webgl/tool/pack_shaders.dart b/packages/flutter3d_webgl/tool/pack_shaders.dart index abff2e2a7..96bcb3336 100644 --- a/packages/flutter3d_webgl/tool/pack_shaders.dart +++ b/packages/flutter3d_webgl/tool/pack_shaders.dart @@ -60,8 +60,7 @@ import 'dart:typed_data'; // The container alone: the package's barrel reaches `package:flutter`, which // a `dart run` tool cannot load. `shader_bundle.dart` says so. import 'package:flutter3d_hardware/shader_bundle.dart'; -import 'package:flutter3d_webgl/src/glsl_translate.dart'; -import 'package:flutter3d_webgl/src/webgl_bundle_section.dart'; +import 'package:flutter3d_shaders/translate.dart'; import 'source_package.dart'; diff --git a/packages/flutter3d_webgpu/CHANGELOG.md b/packages/flutter3d_webgpu/CHANGELOG.md index e057b47f1..545e22f0e 100644 --- a/packages/flutter3d_webgpu/CHANGELOG.md +++ b/packages/flutter3d_webgpu/CHANGELOG.md @@ -6,21 +6,30 @@ other stage; `smaa-teapot` is in the WebGPU reference set and matches Impeller's to the pixel. -## 0.8.2+1 - -**Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart -`^3.12.2` and `vector_math` 2.4.3, which were what this repository is built with rather than -what the package needs. A workspace that supports Flutter 3.44, Flame's among -them, could not depend on it. Nothing else changed. **The generated tables carry the decal stage** of `flutter3d_shaders`, through glslang and naga like every other stage. + **The generated tables carry `PlanarReflection` and `RenderTextureEncode`**, through glslang and naga like every other stage, and the `planar-mirror` and `render-texture` goldens are in this set, both 0 of 172800 pixels from Impeller. + **The generated tables carry `SkyPhysical`, `SkyPhysicalVertex` and the height fog in `ApplyFog`**, through glslang and naga like every other stage. +**`prepareStage`, the WGSL compile and the section writer moved to +`flutter3d_shaders`** for the reason that package's changelog gives. The +tools and tests here import them from `package:flutter3d_shaders/compile.dart`, +and `flutter3d_webgl` is no longer a dev dependency: it was here only for +`resolveIncludes`. + +## 0.8.2+1 + +**Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart +`^3.12.2` and `vector_math` 2.4.3, which were what this repository is built with rather than +what the package needs. A workspace that supports Flutter 3.44, Flame's among +them, could not depend on it. Nothing else changed. + ## 0.8.2 **The generated tables carry the PCSS receiver-plane bias** of diff --git a/packages/flutter3d_webgpu/pubspec.yaml b/packages/flutter3d_webgpu/pubspec.yaml index 4dbf7bb76..fde3cd107 100644 --- a/packages/flutter3d_webgpu/pubspec.yaml +++ b/packages/flutter3d_webgpu/pubspec.yaml @@ -54,13 +54,6 @@ dev_dependencies: # source has to reach both generators, and this is the one package that has # both. Not a real dependency — nothing in `lib/` reaches it. flutter3d_core: ^0.8.2 - # For `resolveIncludes` in `glsl_translate.dart`, and for nothing else. The - # WebGL2 backend already answers "which headers does this shader really pull - # in", and a second answer to that question is how two backends drift apart in - # the one place neither of them can see. A dev dependency for the same reason - # `flutter3d_shaders` is: nothing in `lib/` reaches it, and two backends that - # depended on each other at run time would not be two backends. - flutter3d_webgl: ^0.8.2 # For `reflection_probe_test.dart` alone, which drives the real Renderer # through this backend so that a reflection probe is checked as a picture # rather than as a capability. The sibling WebGL2 package reaches for the diff --git a/packages/flutter3d_webgpu/test/glsl_to_wgsl_test.dart b/packages/flutter3d_webgpu/test/glsl_to_wgsl_test.dart index fa027556a..b7bd091c2 100644 --- a/packages/flutter3d_webgpu/test/glsl_to_wgsl_test.dart +++ b/packages/flutter3d_webgpu/test/glsl_to_wgsl_test.dart @@ -14,9 +14,7 @@ @TestOn('vm') library; -// ignore: implementation_imports -import 'package:flutter3d_webgl/src/glsl_translate.dart'; -import 'package:flutter3d_webgpu/src/glsl_to_wgsl.dart'; +import 'package:flutter3d_shaders/translate.dart'; import 'package:flutter_test/flutter_test.dart'; /// Includes resolved and then the stage prepared, the way diff --git a/packages/flutter3d_webgpu/test/material_language_generators_test.dart b/packages/flutter3d_webgpu/test/material_language_generators_test.dart index 56be9aa0e..0595708a0 100644 --- a/packages/flutter3d_webgpu/test/material_language_generators_test.dart +++ b/packages/flutter3d_webgpu/test/material_language_generators_test.dart @@ -13,18 +13,19 @@ /// language allowed and a generator cannot carry fails here rather than in a /// browser. /// -/// **Here rather than in `flutter3d_webgl`** because this is the one package -/// that already has both: its own generator, and the other backend's as a dev -/// dependency, for exactly the reason its `prepareStage` gives — two answers -/// to "which headers does this shader pull in" would drift where neither side -/// could see it. +/// Both generators live in `flutter3d_shaders` since P8; this file stays here +/// because it is the WebGPU half — `prepareStage` — that has the most to +/// refuse. `flutter3d_build`'s `material_build_test.dart` runs the same source +/// all the way into a bundle, through glslang and naga where a machine has +/// them. /// /// **Impeller is the one not exercised.** `impellerc` is a binary out of the -/// Flutter SDK's artifact cache, and `build_shaders_test.dart` next door stubs -/// it rather than depending on a machine having it. What this file can say is -/// that the emitted text is the same shape as every `.frag` that binary -/// already compiles — the same `#version`, the same one header, the same -/// prototypes — and that is stated rather than proved. +/// Flutter SDK's artifact cache, and `flutter3d_impeller`'s +/// `build_shaders_test.dart` stubs it rather than depending on a machine +/// having it. What this file can say is that the emitted text is the same +/// shape as every `.frag` that binary already compiles — the same `#version`, +/// the same one header, the same prototypes — and that is stated rather than +/// proved. /// /// **The VM only**, because resolving headers against the real tree is file /// access: `loadShaders` starts from `Directory.current`, which a browser @@ -34,13 +35,7 @@ library; import 'package:flutter3d_core/formats.dart'; -// The include resolver, and only the include resolver — the same -// `implementation_imports` `tool/generate_shaders.dart` takes, for the same -// reason. -// ignore: implementation_imports -import 'package:flutter3d_webgl/src/glsl_translate.dart'; -import 'package:flutter3d_webgpu/src/glsl_to_wgsl.dart'; -import 'package:flutter3d_webgpu/src/source_package.dart'; +import 'package:flutter3d_shaders/compile.dart'; import 'package:flutter_test/flutter_test.dart'; /// Everything the language has: a texture, a parameter of each shape, a diff --git a/packages/flutter3d_webgpu/test/wgsl_pipeline_test.dart b/packages/flutter3d_webgpu/test/wgsl_pipeline_test.dart index 26df4a78a..504b70963 100644 --- a/packages/flutter3d_webgpu/test/wgsl_pipeline_test.dart +++ b/packages/flutter3d_webgpu/test/wgsl_pipeline_test.dart @@ -19,11 +19,7 @@ library; import 'dart:io'; -// ignore: implementation_imports -import 'package:flutter3d_webgl/src/glsl_translate.dart'; -import 'package:flutter3d_webgpu/src/glsl_to_wgsl.dart'; -import 'package:flutter3d_webgpu/src/source_package.dart'; -import 'package:flutter3d_webgpu/src/wgsl_compiler.dart'; +import 'package:flutter3d_shaders/compile.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/flutter3d_webgpu/test/wgsl_section_test.dart b/packages/flutter3d_webgpu/test/wgsl_section_test.dart index 130fe809c..6eac6ea73 100644 --- a/packages/flutter3d_webgpu/test/wgsl_section_test.dart +++ b/packages/flutter3d_webgpu/test/wgsl_section_test.dart @@ -29,10 +29,9 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter3d_hardware/flutter3d_hardware.dart'; -import 'package:flutter3d_webgpu/src/glsl_to_wgsl.dart'; +import 'package:flutter3d_shaders/translate.dart'; import 'package:flutter3d_webgpu/src/webgpu_bundle_section.dart'; import 'package:flutter3d_webgpu/src/webgpu_loaded_shaders.dart'; -import 'package:flutter3d_webgpu/src/wgsl_section.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/packages/flutter3d_webgpu/tool/generate_compute_shaders.dart b/packages/flutter3d_webgpu/tool/generate_compute_shaders.dart index 27bbd9292..c1b5acf96 100644 --- a/packages/flutter3d_webgpu/tool/generate_compute_shaders.dart +++ b/packages/flutter3d_webgpu/tool/generate_compute_shaders.dart @@ -15,9 +15,7 @@ library; import 'dart:convert'; import 'dart:io'; -import 'package:flutter3d_webgl/src/glsl_translate.dart'; -import 'package:flutter3d_webgpu/src/source_package.dart'; -import 'package:flutter3d_webgpu/src/wgsl_compiler.dart'; +import 'package:flutter3d_shaders/compile.dart'; final RegExp _localSize = RegExp( r'layout\s*\(\s*local_size_x\s*=\s*(\d+)' diff --git a/packages/flutter3d_webgpu/tool/generate_shaders.dart b/packages/flutter3d_webgpu/tool/generate_shaders.dart index 815af8650..7beadf8b9 100644 --- a/packages/flutter3d_webgpu/tool/generate_shaders.dart +++ b/packages/flutter3d_webgpu/tool/generate_shaders.dart @@ -42,23 +42,13 @@ library; import 'dart:io'; -// The include resolver, and only the include resolver. A second one would be a -// second answer to "which headers does this shader really pull in", and the two -// backends would drift apart in the one place where drifting apart cannot be -// seen from either side. Called from here rather than from `prepareStage` -// because `flutter3d_webgl` is a dev dependency and a file under `lib/` may -// only import what ships. -// -// ignore: implementation_imports -import 'package:flutter3d_webgl/src/glsl_translate.dart'; -import 'package:flutter3d_webgpu/src/glsl_to_wgsl.dart'; -import 'package:flutter3d_webgpu/src/source_package.dart'; -import 'package:flutter3d_webgpu/src/wgsl_compiler.dart'; -// The std140 cross-check and the pair a finished stage is, shared with -// `tool/pack_wgsl_section.dart`. Two copies of the check would be two rules, -// and the loadable path — where the GLSL is somebody else's — is the one that -// needs it most. -import 'package:flutter3d_webgpu/src/wgsl_section.dart'; +// The translator, the include resolver WebGL2's generator also uses, the two +// compilers and the std140 cross-check — all in `flutter3d_shaders` since P8, +// shared with `tool/pack_wgsl_section.dart` and with `flutter3d_build`'s +// material step. A second include resolver would be a second answer to +// "which headers does this shader really pull in", and two copies of the +// check would be two rules. +import 'package:flutter3d_shaders/compile.dart'; void main(List args) { final ShaderSet shaders; diff --git a/packages/flutter3d_webgpu/tool/pack_wgsl_section.dart b/packages/flutter3d_webgpu/tool/pack_wgsl_section.dart index f5bca0360..aad030376 100644 --- a/packages/flutter3d_webgpu/tool/pack_wgsl_section.dart +++ b/packages/flutter3d_webgpu/tool/pack_wgsl_section.dart @@ -21,14 +21,12 @@ /// /// A bundle is one file with a section per backend, and `pack_shaders.dart` in /// `flutter3d_webgl` is what assembles it. It could have grown a third section -/// the way it grew the first two — except that everything the WGSL section is -/// made of lives here: the translator in `lib/src/glsl_to_wgsl.dart`, the two -/// external compilers in `lib/src/wgsl_compiler.dart`, and the shape of the -/// document in `lib/src/wgsl_section.dart`. Reaching for those from -/// the WebGL package's build script would make one backend's tooling depend on -/// another backend's library, which is the coupling four separate backends -/// exist to avoid — and `flutter3d_webgl` does not depend on this package, so -/// it would not even resolve. +/// the way it grew the first two — and since P8 it could, because the +/// translator, the two external compilers and the shape of the document all +/// live in `flutter3d_shaders` (`compile.dart`), where `flutter3d_build`'s +/// material step reaches them too. What stays here is the decision to run +/// glslang and naga at all: a machine without them still builds a bundle with +/// the other sections, and this program is the step it skips. /// /// So the section arrives at `pack_shaders.dart` already made, exactly the way /// impellerc's does: it is copied in under `--webgpu FILE` and that packer @@ -74,15 +72,7 @@ library; import 'dart:convert'; import 'dart:io'; -// The include resolver, and only the include resolver — the reason -// `tool/generate_shaders.dart` gives, and the same dev dependency. -// -// ignore: implementation_imports -import 'package:flutter3d_webgl/src/glsl_translate.dart'; -import 'package:flutter3d_webgpu/src/glsl_to_wgsl.dart'; -import 'package:flutter3d_webgpu/src/source_package.dart'; -import 'package:flutter3d_webgpu/src/wgsl_compiler.dart'; -import 'package:flutter3d_webgpu/src/wgsl_section.dart'; +import 'package:flutter3d_shaders/compile.dart'; void main(List args) { final options = _Options.parse(args); @@ -140,7 +130,7 @@ void main(List args) { } } - final entries = {}; + final entries = {}; manifest.forEach((name, spec) { final entry = spec as Map; final file = entry['file'] as String; @@ -165,7 +155,15 @@ void main(List args) { ); }); - final locations = _varyingLocations(engine, entries); + // Numbered against the engine's manifest as well as this one, and refused + // when that would move a number the engine already committed — see the + // header, and `bundleVaryingLocations`. + final Map locations; + try { + locations = bundleVaryingLocations(engine, entries.values); + } on WgslPrepareError catch (error) { + _fail(error.message); + } final vertex = {}; final fragment = {}; @@ -207,60 +205,6 @@ void main(List args) { ); } -/// One manifest entry, with its `#include`s already expanded. -typedef _Entry = ({String file, bool fragment, String resolved}); - -/// A location per varying name, agreeing with the engine's committed table. -/// -/// See the header: computed over both manifests, then held against the engine's -/// alone. The failure is a refusal to pack rather than a section that would -/// compile and read the wrong slot. -Map _varyingLocations(ShaderSet engine, Map mine) { - final theirs = >[]; - for (final entry in engine.stages.values) { - final String resolved; - try { - resolved = resolveIncludes( - engine.sources[entry.file]!, - engine.sources, - from: entry.file, - ); - } on GlslTranslateError catch (error) { - _fail('the engine\'s ${entry.file}: ${error.message}'); - } - theirs.add( - scanVaryings(resolved, from: entry.file, fragment: entry.fragment), - ); - } - final ours = >[ - for (final entry in mine.values) - scanVaryings(entry.resolved, from: entry.file, fragment: entry.fragment), - ]; - - final Map engineOnly; - final Map together; - try { - engineOnly = assignVaryingLocations(theirs); - together = assignVaryingLocations(>[...theirs, ...ours]); - } on WgslPrepareError catch (error) { - _fail(error.message); - } - - for (final entry in engineOnly.entries) { - if (together[entry.key] == entry.value) continue; - _fail( - 'this bundle moves the engine\'s varying "${entry.key}" from location ' - '${entry.value} to ${together[entry.key]}. A stage packed here is paired ' - 'with a stage the engine already compiled, and WebGPU joins the two by ' - 'location alone, so the numbering cannot be renegotiated by a bundle. ' - 'A varying declared beside the engine\'s in one stage joins their ' - 'family and renumbers it — give it a name of its own, or declare it in ' - 'a stage that shares nothing with the engine\'s.', - ); - } - return together; -} - /// `flutter3d_shaders`' root, or a failure naming what to run. String _shaderPackage(String from) { try { diff --git a/site/content/quickstart.md b/site/content/quickstart.md index a05e805c2..e6dc32f6f 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10948 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 10957 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 2b1450c6b..7886a89f5 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10948 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10957 tests need a GPU. --- # Testing -10948 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10957 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -34,13 +34,13 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_editor_play` | 12 | | `flutter3d_net` | 15 | | | | | `flutter3d_net_webrtc` | 2 | | | | | `flutter3d_sim_mcp` | 25 | -| `flutter3d_mcp_kit` | 2 | | `flutter3d_build` | 127 | +| `flutter3d_mcp_kit` | 2 | | `flutter3d_build` | 136 | | | | | `apps/flutter3d_lesson_viewer` | 48 | | | | | `apps/flutter3d_stereo_lesson_viewer` | 6 | | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10922 rather than 10948: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10931 rather than 10957: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. From 132202da2dcd614e5b555b87da93fda166b1d745 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:10:20 +0300 Subject: [PATCH 036/141] A sky panorama edited under a running game lights it again HotSwap.loadEnvironment builds the prefiltered cube a .hdr or an image asset makes, whose roughest level is also the irradiance, and a swap after the file changed builds it again and puts the new cube and its level count on every registered scene that was lit by the old one. EnvironmentMap .fromEncoded reads a panorama file's bytes for it. The README had stacked copies of its test count line from earlier merges; one is left. --- ARCHITECTURE.md | 2 +- README.md | 10 +- packages/flutter3d/README.md | 4 +- .../flutter3d/test/environment_map_test.dart | 65 ++++ packages/flutter3d_app/CHANGELOG.md | 11 + .../lib/src/hot_swap/hot_swap.dart | 344 ++++++++++++++---- .../flutter3d_app/test/hot_swap_test.dart | 168 +++++++++ packages/flutter3d_core/CHANGELOG.md | 7 + .../src/engine/render/environment_map.dart | 78 ++++ .../lib/src/panorama_sync.dart | 25 +- site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- tasks/0.9-engine-roadmap.md | 20 +- 13 files changed, 636 insertions(+), 110 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0617f67cf..d09a62372 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10957 tests** across 43 packages and 10 applications | +| Unit tests | **10965 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 7163cd84f..cf9512bba 100644 --- a/README.md +++ b/README.md @@ -158,15 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10957 tests across forty-three packages and nine applications. The -There are 10887 tests across forty-two packages and nine applications. The -There are 10889 tests across forty-two packages and nine applications. The -only ones that need a GPU are the Impeller half of the golden set. The other -half is rendered by the software backend, which is what makes 86 scenes -There are 10895 tests across forty-two packages and nine applications. The -only ones that need a GPU are the Impeller half of the golden set. The other -half is rendered by the software backend, which is what makes 86 scenes -There are 10892 tests across forty-two packages and nine applications. The +There are 10965 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/packages/flutter3d/README.md b/packages/flutter3d/README.md index 969903d5f..9441b009a 100644 --- a/packages/flutter3d/README.md +++ b/packages/flutter3d/README.md @@ -104,7 +104,7 @@ What works today: `CUBICSPLINE` with authored tangents), slerped rotations, an `AnimationPlayer` with play/pause/seek/speed and once/loop/ping-pong, and the decoded node hierarchy rebuilt on instantiation so an animated parent carries its subtree; -- 1634 tests covering projection, scene, sorting, debug draw, raycasting, +- 1637 tests covering projection, scene, sorting, debug draw, raycasting, animation, skinning, lighting, render targets, BVH, LOD, glTF, OBJ and `.f3d`, plus a real frame drawn through `flutter3d_cpu`'s software rasteriser for the ones that need one, all without a GPU. The geometry the engine is written in @@ -322,7 +322,7 @@ lib/src/engine/assets/ glTF, OBJ and .f3d decoders, isolate loading, ca example/lib/ the demo, and the frame capture hook skills/ the conventions, as agent skills — see below bin/skills.dart what copies them into a project that uses this -test/ 1634 tests, all runnable without a GPU +test/ 1637 tests, all runnable without a GPU ``` The GLSL is not here. Every shader this package draws with lives in diff --git a/packages/flutter3d/test/environment_map_test.dart b/packages/flutter3d/test/environment_map_test.dart index a2767ec8c..d4d7086d6 100644 --- a/packages/flutter3d/test/environment_map_test.dart +++ b/packages/flutter3d/test/environment_map_test.dart @@ -9,9 +9,11 @@ /// what most of this file is about. library; +import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; import 'package:flutter_test/flutter_test.dart'; const int _size = 8; @@ -302,4 +304,67 @@ void main() { ); }); }); + + group('an environment from a file', () { + final device = CpuDevice( + width: 4, + height: 4, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + + /// A two-by-one Radiance file, every pixel twice as bright as white. + final hdr = Uint8List.fromList([ + ...utf8.encode('#?RADIANCE\nFORMAT=32-bit_rle_rgbe\n\n-Y 1 +X 2\n'), + for (var i = 0; i < 2; i++) ...[128, 128, 128, 130], + ]); + + test('reads a .hdr itself and anything else through the decoder', () async { + // Mutation: hand every file to the decoder and a `.hdr`, which + // `dart:ui` does not read, never lights anything. + final decoded = []; + Future decode(Uint8List bytes) async { + decoded.add(bytes.length); + return Rgba8Image(width: 2, height: 1, pixels: Uint8List(8)); + } + + final fromHdr = await EnvironmentMap.fromEncoded( + device, + hdr, + decodeImage: decode, + size: 4, + levels: 2, + ); + expect(fromHdr?.levels, 2); + expect(decoded, isEmpty); + + final fromPng = await EnvironmentMap.fromEncoded( + device, + Uint8List.fromList([0x89, 0x50, 0x4e, 0x47]), + decodeImage: decode, + size: 4, + levels: 2, + ); + expect(fromPng, isNotNull); + expect(decoded, [4]); + }); + + test('is null for bytes neither reader takes', () async { + final none = await EnvironmentMap.fromEncoded( + device, + Uint8List.fromList([1, 2, 3]), + decodeImage: (Uint8List bytes) async => null, + ); + expect(none, isNull); + }); + + test('clamps a Radiance value above one to white', () { + // Mutation: scale by 255 without the clamp and the byte runs past + // white, so the brightest part of the sky lights least. + final pixels = EnvironmentMap.hdrToRgba8(readHdr(hdr)); + expect( + [for (var i = 0; i < 4; i++) pixels.getUint8(i)], + [255, 255, 255, 255], + ); + }); + }); } diff --git a/packages/flutter3d_app/CHANGELOG.md b/packages/flutter3d_app/CHANGELOG.md index b9cff280c..e28ef205b 100644 --- a/packages/flutter3d_app/CHANGELOG.md +++ b/packages/flutter3d_app/CHANGELOG.md @@ -9,6 +9,17 @@ `LevelLoader` watches every map a level names, and `LoadedLevel.dispose` stops. `ext.flutter3d.assets.put` accepts an image registered this way. +- **A sky panorama edited under a running game lights it again.** + `HotSwap.loadEnvironment` builds the prefiltered cube a `.hdr` or an image + asset makes, whose roughest level is also the irradiance, and + `SwappableEnvironment.applyTo` puts it on a scene. A swap after the file + changed prefilters it again and puts the new cube, with its level count, + on every registered scene that was lit by the old one; the old cube goes + back to the device after its frames, as a swapped texture does. + `registerEnvironment` takes an environment built some other way, the + report lists what changed under `environments`, and + `ext.flutter3d.assets.put` accepts a panorama registered either way. + **A hot reload shows the shaders it reloaded.** Flutter 3.47 reinitializes an asset-loaded shader bundle on hot reload, which the engine's own bundle is, but a pipeline linked from the old code went on drawing it: the library diff --git a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart index ae43d2d21..e370750e7 100644 --- a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart +++ b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart @@ -28,6 +28,14 @@ typedef TextureBytesSource = Future Function(); /// decode into one. typedef TextureBuild = Future Function(Uint8List bytes); +/// A prefiltered environment cube and how many levels it has below its base, +/// the two things `Scene.environment` and `Scene.environmentLevels` take. +typedef BuiltEnvironment = ({TextureHandle texture, int levels}); + +/// Builds an environment from a panorama file's bytes, or null when they do +/// not decode into one. +typedef EnvironmentBuild = Future Function(Uint8List bytes); + /// The renderers and shader libraries a running application has, and what to /// do with them when its code or its shaders change under it. /// @@ -76,6 +84,7 @@ final class HotSwap { final List<_Library> _libraries = <_Library>[]; final List _models = []; final List _textures = []; + final List _environments = []; final List> _scenes = >[]; final Map> _overrides = >{}; @@ -136,22 +145,14 @@ final class HotSwap { required GraphicsDevice device, }) async { final generated = generatedAssetPathFor(sourcePath); - Future load(String path) async { - try { - final data = await rootBundle.load(path); - return data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - } on FlutterError { - return null; - } - } // The file `loadModelAsset` would read: this device class's own first, // then the one every class shares. Future read() async => switch (assetDeviceClass) { final DeviceClass reading => - await load(deviceClassPath(generated, reading)) ?? - await load(generated), - null => await load(generated), + await _readAsset(deviceClassPath(generated, reading)) ?? + await _readAsset(generated), + null => await _readAsset(generated), }; Future build(Uint8List bytes) async => ModelAsset.fromDocument( @@ -257,14 +258,7 @@ final class HotSwap { required GraphicsDevice device, TextureSampling sampling = const TextureSampling(), }) async { - Future read() async { - try { - final data = await rootBundle.load(path); - return data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - } on FlutterError { - return null; - } - } + Future read() => _readAsset(path); Future build(Uint8List bytes) => uploadEncodedImage( device, @@ -355,16 +349,167 @@ final class HotSwap { final previous = texture.texture; texture._handle.value = next; _replaceTexture(previous, next); + _releaseAfterFrame(previous); + return null; + } + + /// Hands [texture] back to the device once no frame in flight can still + /// sample it. + void _releaseAfterFrame(TextureHandle texture) { _renderers.removeWhere((WeakReference r) => r.target == null); // One renderer releases it: a handle released twice is a mistake a // backend may refuse. With none alive nothing is drawing it either, and // it goes with the device. for (final renderer in _renderers) { if (renderer.target case final Renderer live) { - live.releaseTextureAfterFrame(previous); + live.releaseTextureAfterFrame(texture); break; } } + } + + /// Builds the environment a panorama asset at [path] makes — a Radiance + /// `.hdr` or any image Flutter decodes — and hands it back ready to be + /// built again when the file changes. Null when the file is not there, + /// does not decode, or the device has no cube textures. + /// + /// **The cube and its irradiance come back together.** The roughest level + /// of the chain is the diffuse term (`EnvironmentMap.diffuseLevel`), so one + /// prefilter makes both, and a swap after the file changed runs it again + /// and puts the new cube, with its level count, on every registered scene + /// whose environment was the old one; then the old one goes back to the + /// device as a swapped texture does. [size] and [levels] stay as given, so + /// the new file may be a panorama of another size. + /// + /// **A swap costs what the prefilter costs.** It runs where the swap runs, + /// and `EnvironmentMap` states the cost: at the default thirty-two a + /// fraction of a millisecond, at 512 not something to wait for on every + /// save. + /// + /// **For a game that lights its world from a panorama of its own**, the + /// call it makes in place of reading the file and calling + /// `EnvironmentMap.fromPanorama`. Put the result on a scene with + /// [SwappableEnvironment.applyTo]; a scene a `SceneSurface` draws is + /// registered already, and any other has to be passed to [registerScene] + /// for a swap to reach it. + Future loadEnvironment( + String path, { + required GraphicsDevice device, + int size = 32, + int levels = 4, + }) async { + Future read() => _readAsset(path); + + Future build(Uint8List bytes) => + EnvironmentMap.fromEncoded( + device, + bytes, + decodeImage: defaultImageDecoder, + size: size, + levels: levels, + ); + + final bytes = await read(); + if (bytes == null) return null; + final built = await build(bytes); + if (built == null) return null; + return registerEnvironment( + path, + built, + read: read, + build: build, + loadedFrom: bytes, + ); + } + + /// Wraps [environment], built from the file [read] reads, so that a [swap] + /// after the file changed builds it again with [build] and puts the result + /// where the old one was — see [loadEnvironment]. + /// + /// For an environment made some other way than [loadEnvironment] makes it: + /// a size chosen per device, a decoder of the game's own. [loadedFrom] are + /// the bytes [environment] was built from, when the caller has them; + /// without them the first swap takes the file as it finds it as the + /// starting point rather than as a change. + SwappableEnvironment registerEnvironment( + String path, + BuiltEnvironment environment, { + required TextureBytesSource read, + required EnvironmentBuild build, + Uint8List? loadedFrom, + }) { + final swappable = SwappableEnvironment._( + path, + environment, + read, + build, + loadedFrom == null + ? null + : _fingerprint(ByteData.sublistView(loadedFrom)), + ); + if (!enabled) return swappable; + _environments + ..removeWhere((SwappableEnvironment e) => e.path == path) + ..add(swappable); + _registerExtension(); + return swappable; + } + + /// Builds [bytes] in place of the environment registered at [path], as a + /// changed file would: what `ext.flutter3d.assets.put` does for a + /// panorama. Why it did not take when it did not, or null when it did or + /// nothing is registered there — [hasEnvironment] tells those two apart. + Future putEnvironment(String path, Uint8List bytes) async { + if (!enabled) return null; + for (final environment in _environments) { + if (environment.path == path) { + return _adoptEnvironment(environment, bytes); + } + } + return null; + } + + /// Stops watching [environment], for a game letting it go with the level it + /// lit: a swap after that would prefilter a sky nobody draws and release a + /// cube already released. + void forgetEnvironment(SwappableEnvironment environment) => _environments + .removeWhere((SwappableEnvironment e) => identical(e, environment)); + + /// Whether an environment is registered at [path]. + bool hasEnvironment(String path) => + _environments.any((SwappableEnvironment e) => e.path == path); + + /// Builds [bytes] for [environment] and puts the result on every + /// registered scene that was lit by the old one. Why not, when it did not. + Future _adoptEnvironment( + SwappableEnvironment environment, + Uint8List bytes, + ) async { + final BuiltEnvironment? next; + try { + next = await environment._build(bytes); + } on Object catch (error) { + return '$error'; + } + if (next == null) { + return 'the new bytes do not decode into a panorama this device can ' + 'hold as a cube'; + } + final previous = environment.built; + environment._built.value = next; + _scenes.removeWhere((WeakReference s) => s.target == null); + for (final reference in _scenes) { + if (reference.target case final Scene scene + when identical(scene.environment, previous.texture)) { + // The level count with the cube: the shader reads roughness against + // it, and a chain that changed length under the old count would + // read the wrong lobe. + scene + ..environment = next.texture + ..environmentLevels = next.levels; + } + } + _releaseAfterFrame(previous.texture); return null; } @@ -559,20 +704,8 @@ final class HotSwap { } final models = []; for (final model in List.of(_models)) { - final Uint8List? bytes; - try { - bytes = await model._read(); - } on Object catch (error) { - refused.add('${model.path}: could not be read ($error)'); - continue; - } + final bytes = await _changed(model, refused); if (bytes == null) continue; - final fingerprint = _fingerprint(ByteData.sublistView(bytes)); - if (model._fingerprint == null || fingerprint == model._fingerprint) { - model._fingerprint = fingerprint; - continue; - } - model._fingerprint = fingerprint; final report = await model._adopt(bytes); if (report.refused case final String reason) { refused.add('${model.path}: $reason'); @@ -583,26 +716,25 @@ final class HotSwap { if (models.isNotEmpty) _applyOverrides(); final textures = []; for (final texture in List.of(_textures)) { - final Uint8List? bytes; - try { - bytes = await texture._read(); - } on Object catch (error) { - refused.add('${texture.path}: could not be read ($error)'); - continue; - } + final bytes = await _changed(texture, refused); if (bytes == null) continue; - final fingerprint = _fingerprint(ByteData.sublistView(bytes)); - if (texture._fingerprint == null || fingerprint == texture._fingerprint) { - texture._fingerprint = fingerprint; - continue; - } - texture._fingerprint = fingerprint; if (await _adoptTexture(texture, bytes) case final String reason) { refused.add('${texture.path}: $reason'); } else { textures.add(texture.path); } } + final environments = []; + for (final environment in List.of(_environments)) { + final bytes = await _changed(environment, refused); + if (bytes == null) continue; + if (await _adoptEnvironment(environment, bytes) + case final String reason) { + refused.add('${environment.path}: $reason'); + } else { + environments.add(environment.path); + } + } _renderers.removeWhere((WeakReference r) => r.target == null); for (final renderer in _renderers) { renderer.target?.relinkShaders(); @@ -613,6 +745,7 @@ final class HotSwap { refused: List.unmodifiable(refused), models: List.unmodifiable(models), textures: List.unmodifiable(textures), + environments: List.unmodifiable(environments), ); for (final line in refused) { debugPrint('flutter3d: kept the last version that loaded, $line'); @@ -621,6 +754,35 @@ final class HotSwap { return report; } + /// [file]'s bytes when they differ from the last ones seen, or null when + /// they do not, cannot be read now, or are the first ones seen — which are + /// the starting point rather than a change. A read that throws goes into + /// [refused]. + Future _changed(_Watched file, List refused) async { + final Uint8List? bytes; + try { + bytes = await file._read(); + } on Object catch (error) { + refused.add('${file.path}: could not be read ($error)'); + return null; + } + if (bytes == null) return null; + final fingerprint = _fingerprint(ByteData.sublistView(bytes)); + final seen = file._fingerprint; + file._fingerprint = fingerprint; + return seen == null || seen == fingerprint ? null : bytes; + } + + /// An asset's bytes, or null when the bundle has no such file. + static Future _readAsset(String path) async { + try { + final data = await rootBundle.load(path); + return data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + } on FlutterError { + return null; + } + } + void _registerExtension() { if (_extensionRegistered || !identical(this, instance)) return; _extensionRegistered = true; @@ -674,8 +836,19 @@ final class HotSwap { } final decoded = base64Decode(bytes); final report = await instance.put(path, decoded); - if (report == null && instance.hasTexture(path)) { - final refused = await instance.putTexture(path, decoded); + final image = switch (report) { + null when instance.hasTexture(path) => ( + kind: 'texture', + put: instance.putTexture, + ), + null when instance.hasEnvironment(path) => ( + kind: 'environment', + put: instance.putEnvironment, + ), + _ => null, + }; + if (image != null) { + final refused = await image.put(path, decoded); if (refused != null) { return developer.ServiceExtensionResponse.error( developer.ServiceExtensionResponse.invalidParams, @@ -683,13 +856,13 @@ final class HotSwap { ); } return developer.ServiceExtensionResponse.result( - jsonEncode({'path': path, 'texture': true}), + jsonEncode({'path': path, image.kind: true}), ); } if (report == null) { return developer.ServiceExtensionResponse.error( developer.ServiceExtensionResponse.invalidParams, - 'no model or texture is registered at $path', + 'no model, texture or environment is registered at $path', ); } return developer.ServiceExtensionResponse.result( @@ -722,6 +895,7 @@ final class HotSwapReport { required this.refused, this.models = const [], this.textures = const [], + this.environments = const [], }); /// What a profile or release build reports: nothing was touched. @@ -748,12 +922,17 @@ final class HotSwapReport { /// one's place. final List textures; + /// The environments whose file changed and whose new cube took the old + /// one's place. + final List environments; + Map toJson() => { 'renderers': renderers, 'refreshed': refreshed, 'refused': refused, 'models': [for (final model in models) model.toJson()], 'textures': textures, + 'environments': environments, }; } @@ -771,27 +950,22 @@ final class _Library { /// What [HotSwap.loadModel] and [HotSwap.registerModel] hand back. Make /// instances through [instantiate], or hand ones made elsewhere to [track]; /// an instance the game drops is forgotten with it. -final class SwappableModel { +final class SwappableModel extends _Watched { SwappableModel._( - this.path, + super.path, this._asset, - this._read, + super._read, this._build, this._device, - this._fingerprint, + super._fingerprint, ); - /// The file this model was loaded from, as the game named it. - final String path; - /// The version drawn now; a new one after each swap. ModelAsset get asset => _asset; ModelAsset _asset; - final ModelBytesSource _read; final ModelBuild _build; final GraphicsDevice? _device; - int? _fingerprint; final List> _instances = >[]; @@ -856,18 +1030,15 @@ final class SwappableModel { /// in place of the old one rather than writing into it, because the new one /// may be another size and because writing a level over an existing one /// would leave its smaller mips showing the old picture. -final class SwappableTexture { +final class SwappableTexture extends _Watched { SwappableTexture._( - this.path, + super.path, TextureHandle texture, - this._read, + super._read, this._build, - this._fingerprint, + super._fingerprint, ) : _handle = ValueNotifier(texture); - /// The file it was loaded from, as the game named it. - final String path; - /// The texture drawn now; a new one after each swap. TextureHandle get texture => _handle.value; @@ -876,8 +1047,47 @@ final class SwappableTexture { ValueListenable get changes => _handle; final ValueNotifier _handle; - final TextureBytesSource _read; final TextureBuild _build; +} + +/// An environment a game lights with, kept so a changed panorama file is +/// prefiltered again and reaches every scene lit by it — see +/// [HotSwap.loadEnvironment]. +final class SwappableEnvironment extends _Watched { + SwappableEnvironment._( + super.path, + BuiltEnvironment built, + super._read, + this._build, + super._fingerprint, + ) : _built = ValueNotifier(built); + + /// The cube drawn now and its level count; new ones after each swap. + BuiltEnvironment get built => _built.value; + + /// [built], for something that holds the cube outside a scene — a + /// weapon's own view, say — and has to hear when it changes. + ValueListenable get changes => _built; + + /// Lights [scene] with it: the cube and its level count, two fields of a + /// scene that are only right together. + void applyTo(Scene scene) => scene + ..environment = built.texture + ..environmentLevels = built.levels; + + final ValueNotifier _built; + final EnvironmentBuild _build; +} + +/// A file something was built from, and what its bytes were the last time a +/// swap looked. +abstract base class _Watched { + _Watched(this.path, this._read, this._fingerprint); + + /// The file it was loaded from, as the game named it. + final String path; + + final Future Function() _read; int? _fingerprint; } diff --git a/packages/flutter3d_app/test/hot_swap_test.dart b/packages/flutter3d_app/test/hot_swap_test.dart index b500d2bb5..6473d5d86 100644 --- a/packages/flutter3d_app/test/hot_swap_test.dart +++ b/packages/flutter3d_app/test/hot_swap_test.dart @@ -540,4 +540,172 @@ void main() { }, ); }); + + group('an environment', () { + late CpuDevice device; + late HotSwap swap; + late int builds; + + /// A "panorama" whose first byte is the side of the cube it builds and + /// whose second is its level count; a zero side does not build. A flat + /// texture stands in for the cube: what a swap does with it is the same. + BuiltEnvironment? build(Uint8List bytes) { + builds++; + if (bytes.first == 0) return null; + final texture = device.createTextureFromPixels( + width: bytes.first, + height: bytes.first, + format: TextureFormat.r8g8b8a8UNormInt, + pixels: ByteData(bytes.first * bytes.first * 4), + ); + return texture == null ? null : (texture: texture, levels: bytes[1]); + } + + setUp(() { + device = CpuDevice( + width: 16, + height: 9, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + swap = HotSwap(enabled: true); + builds = 0; + }); + + ({ + SwappableEnvironment environment, + Scene lit, + Scene other, + void Function(int side, int levels) write, + }) + stage() { + var file = Uint8List.fromList([4, 2]); + final environment = swap.registerEnvironment( + 'assets/sky.hdr', + build(file)!, + read: () async => file, + build: (Uint8List bytes) async => build(bytes), + loadedFrom: file, + ); + final lit = Scene(); + environment.applyTo(lit); + final elsewhere = build(Uint8List.fromList([2, 1]))!; + final other = Scene() + ..environment = elsewhere.texture + ..environmentLevels = elsewhere.levels; + swap + ..registerScene(lit) + ..registerScene(other); + return ( + environment: environment, + lit: lit, + other: other, + write: (int side, int levels) => + file = Uint8List.fromList([side, levels]), + ); + } + + test('is prefiltered again when its file changes, and the scenes it lit ' + 'take the new cube with its level count', () async { + // Mutation: set only `Scene.environment` and the scene reads the new + // chain's roughness against the old count, so every surface takes the + // wrong lobe. + final it = stage(); + final otherCube = it.other.environment; + final heard = []; + it.environment.changes.addListener(() => heard.add(it.environment.built)); + + it.write(8, 3); + final report = await swap.swap(); + + expect(report.environments, ['assets/sky.hdr']); + final now = it.environment.built; + expect(now.texture.width, 8); + expect(it.lit.environment, same(now.texture)); + expect(it.lit.environmentLevels, 3); + expect( + it.other.environment, + same(otherCube), + reason: 'a scene lit by another environment keeps it', + ); + expect(it.other.environmentLevels, 1); + expect(heard, hasLength(1)); + }); + + test('an unchanged file is not prefiltered again', () async { + // Mutation: drop the fingerprint and every hot reload pays for a + // convolution of a sky nobody touched. + final it = stage(); + final before = it.lit.environment; + final built = builds; + + final report = await swap.swap(); + + expect(report.environments, isEmpty); + expect(builds, built); + expect(it.lit.environment, same(before)); + }); + + test('a file that does not build keeps the cube that did', () async { + final it = stage(); + final before = it.environment.built; + + it.write(0, 2); + final report = await swap.swap(); + + expect(report.refused.single, contains('assets/sky.hdr')); + expect(it.environment.built, same(before)); + expect(it.lit.environment, same(before.texture)); + expect(it.lit.environmentLevels, 2); + }); + + test('bytes put over the VM service take the file\'s place', () async { + final it = stage(); + + final refused = await swap.putEnvironment( + 'assets/sky.hdr', + Uint8List.fromList([16, 4]), + ); + + expect(refused, isNull); + expect(swap.hasEnvironment('assets/sky.hdr'), isTrue); + expect(it.lit.environment!.width, 16); + expect(it.lit.environmentLevels, 4); + }); + + test( + 'the old cube goes back through a renderer, after its frames', + () async { + // Mutation: never release it and every saved sky leaks a cube. + final it = stage(); + final recording = RecordingDevice(device); + final renderer = Renderer.create(device: recording); + final camera = CameraNode()..setPosition(0.0, 0.0, 4.0); + it.lit.add(camera); + final view = RenderView(camera: camera); + swap.registerRenderer(renderer); + void frame() => renderer.render( + width: 16, + height: 9, + scene: it.lit, + views: [view], + ); + for (var i = 0; i < 4; i++) { + frame(); + } + it.write(8, 2); + await swap.swap(); + final before = recording.events.length; + for (var i = 0; i < 4; i++) { + frame(); + } + final released = recording.events + .skip(before) + .whereType() + .length; + renderer.dispose(); + + expect(released, 1); + }, + ); + }); } diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index eb89ca26b..e34802847 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -84,6 +84,13 @@ reloaded shader reached the scene's own pipelines and not the splats, the debug lines or the particles. +- **`EnvironmentMap.fromEncoded` builds an environment from a panorama + file's bytes.** A Radiance `.hdr` is told apart by its header and read + here; anything else goes through the `ImageDecoder` given. Then it is + `fromPanorama` as before. `EnvironmentMap.hdrToRgba8` is the clamp to + eight bits that `flutter3d_model_core`'s `panoramaPixels` used to keep to + itself, so both read a `.hdr` the same way. + ## 0.8.3+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_core/lib/src/engine/render/environment_map.dart b/packages/flutter3d_core/lib/src/engine/render/environment_map.dart index 8ec86b130..80a1e3bca 100644 --- a/packages/flutter3d_core/lib/src/engine/render/environment_map.dart +++ b/packages/flutter3d_core/lib/src/engine/render/environment_map.dart @@ -1,9 +1,11 @@ import 'dart:math' as math; import 'dart:typed_data'; +import 'package:flutter3d_core/formats.dart'; import 'package:flutter3d_hardware/flutter3d_hardware.dart'; import 'package:vector_math/vector_math.dart'; +import '../assets/image_decoder.dart'; import 'sky_settings.dart'; /// Builds the cube a surface reflects: one environment, convolved by roughness. @@ -242,6 +244,82 @@ abstract final class EnvironmentMap { return texture == null ? null : (texture: texture, levels: levels); } + /// The same thing from a panorama file's bytes: a Radiance `.hdr`, or any + /// image [decodeImage] reads. + /// + /// **The file, not the pixels, because a file is what changes.** A game + /// that loads its sky from an asset and wants the edit drawn under a hot + /// swap has the bytes and nothing else; deciding which decoder they need + /// belongs beside the upload rather than at every caller that watches a + /// file. A `.hdr` is told apart by its header, so the name of the file + /// plays no part. + /// + /// Null for bytes neither reader takes, and for everything [fromPanorama] + /// answers with null. + static Future<({TextureHandle texture, int levels})?> fromEncoded( + GraphicsDevice device, + Uint8List bytes, { + required ImageDecoder decodeImage, + int size = 32, + int levels = 4, + }) async { + final ({ByteData pixels, int width, int height})? panorama = + hdrSizeOf(bytes) != null + ? _fromHdr(bytes) + : switch (await decodeImage(bytes)) { + final Rgba8Image image => ( + pixels: ByteData.sublistView(image.pixels), + width: image.width, + height: image.height, + ), + null => null, + }; + if (panorama == null) return null; + return fromPanorama( + device, + panorama.pixels, + width: panorama.width, + height: panorama.height, + size: size, + levels: levels, + ); + } + + static ({ByteData pixels, int width, int height})? _fromHdr(Uint8List bytes) { + try { + final HdrImage image = readHdr(bytes); + return ( + pixels: hdrToRgba8(image), + width: image.width, + height: image.height, + ); + } on HdrFormatException { + return null; + } + } + + /// A Radiance image as the RGBA8 [fromPanorama] reads. + /// + /// **Clamped rather than tone-mapped.** The cube is eight bits a channel — + /// [fromSky] says why, and that this is a real limitation — so a sun four + /// hundred times brighter than the sky around it becomes white either way. + /// Rolling the highlights off first would darken everything else to buy + /// detail in a region the cube cannot hold anyway, and would make the + /// indirect light a panorama gives differ from the indirect light the same + /// picture gives as a PNG. + static ByteData hdrToRgba8(HdrImage image) { + int byte(double value) => (value * 255.0).round().clamp(0, 255); + final out = ByteData(image.width * image.height * 4); + for (var i = 0; i < image.width * image.height; i++) { + out + ..setUint8(i * 4, byte(image.rgb[i * 3])) + ..setUint8(i * 4 + 1, byte(image.rgb[i * 3 + 1])) + ..setUint8(i * 4 + 2, byte(image.rgb[i * 3 + 2])) + ..setUint8(i * 4 + 3, 255); + } + return out; + } + /// Which level of the chain a shader should treat as the diffuse term. /// /// The roughest one. At roughness one the GGX lobe's reflected taps fall diff --git a/packages/flutter3d_model_core/lib/src/panorama_sync.dart b/packages/flutter3d_model_core/lib/src/panorama_sync.dart index f1235c2ce..642a7907c 100644 --- a/packages/flutter3d_model_core/lib/src/panorama_sync.dart +++ b/packages/flutter3d_model_core/lib/src/panorama_sync.dart @@ -92,31 +92,12 @@ final class PanoramaSync { } /// A Radiance file as the RGBA8 `EnvironmentMap.fromPanorama` reads, or null -/// when it is not one. -/// -/// **Clamped rather than tone-mapped.** The cube is eight bits a channel — -/// `EnvironmentMap`'s own doc comment says why, and that this is a real -/// limitation — so a sun four hundred times brighter than the sky around it -/// becomes white either way. Rolling the highlights off first would darken -/// everything else to buy detail in a region the cube cannot hold anyway, -/// and would make the indirect light a panorama gives differ from the -/// indirect light the same picture gives as a PNG. +/// when it is not one — clamped, for the reason `EnvironmentMap.hdrToRgba8` +/// gives. ByteData? panoramaPixels(Uint8List bytes) { - final HdrImage image; try { - image = readHdr(bytes); + return EnvironmentMap.hdrToRgba8(readHdr(bytes)); } on HdrFormatException { return null; } - final ByteData out = ByteData(image.width * image.height * 4); - for (var i = 0; i < image.width * image.height; i++) { - out - ..setUint8(i * 4, _byte(image.rgb[i * 3])) - ..setUint8(i * 4 + 1, _byte(image.rgb[i * 3 + 1])) - ..setUint8(i * 4 + 2, _byte(image.rgb[i * 3 + 2])) - ..setUint8(i * 4 + 3, 255); - } - return out; } - -int _byte(double value) => (value * 255.0).round().clamp(0, 255); diff --git a/site/content/quickstart.md b/site/content/quickstart.md index e6dc32f6f..5fb4d01df 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10957 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 10965 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 7886a89f5..eb47b75e8 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,14 +1,14 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10957 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10965 tests need a GPU. --- # Testing -10957 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10965 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| -| `flutter3d` | 1634 | | | | +| `flutter3d` | 1637 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | | `flutter3d_sim` | 591 | | `pad_input` | 67 | @@ -27,7 +27,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | -| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 135 | +| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 140 | | `flutter3d_game` | 279 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10931 rather than 10957: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10939 rather than 10965: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index dccf6bc39..1604a046b 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -146,9 +146,23 @@ picture, and mutable handles across four backends were not needed after all. `LevelLoader` registers every map a level names and keeps `materialTextures` in step; `LoadedLevel.dispose` stops watching them. `ext.flutter3d.assets.put` takes an image too. -*Left:* environments built from a file by prefiltering (a cube map and its -irradiance), which need their own loader to register; a model's embedded -images, which swap with the model. +*Environments done:* `HotSwap.loadEnvironment` reads a panorama asset (a +`.hdr` by its header, anything else through `defaultImageDecoder`, joined in +`EnvironmentMap.fromEncoded`), prefilters it, and registers the result. The +cube's roughest level is its irradiance, so one build makes both. A swap +after the file changed prefilters it again at the size and level count it +was loaded with, puts the new cube on every registered scene whose +environment was the old one and sets `environmentLevels` with it, since the +shader reads roughness against that count; the old cube goes through +`releaseTextureAfterFrame`. `registerEnvironment` takes one built some other +way, `SwappableEnvironment.changes` is for holders outside a scene, and +`assets.put` takes a panorama. The prefilter runs where the swap runs, which +at the default thirty-two is a fraction of a millisecond. +*Left:* a model's embedded images, which swap with the model; nothing loads +a level's or a project's panorama through `loadEnvironment` yet (`LevelLoader` +names no sky file, and the modeller's `PanoramaSync` rebuilds from the +document, not from a file); a reflection probe's cube is drawn on the device +and is not a file. **HR3. A level from the editor into a running game (L).** *Done:* saving in the editor while `PlayScreen` has the project running sends the document and From 7f1973b0e00c2405c09b6809a41f28f3ce101315 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:13:26 +0300 Subject: [PATCH 037/141] A save in the editor reaches the game as a patch, and a saved run replays it A level swapped under a running game is now written into the Demo, so a saved .f3drun replays the edit where it happened instead of the platformer starting a new demo from there. The editor sends level.apply a LevelPatch rather than the whole document after the first save: rows of brushes, lights and entities put in, taken out or replaced, materials by name, and other keys whole. A row is named by its place and its digest, so a patch made against one version is refused by any other and the editor falls back to the document. --- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_demo_platformer/lib/main.dart | 107 ++--- .../lib/src/run.dart | 63 ++- .../test/demo_level_swap_test.dart | 225 ++++++++++ apps/flutter3d_editor/lib/main.dart | 25 +- .../Flutter/GeneratedPluginRegistrant.swift | 2 - packages/flutter3d_editor_play/CHANGELOG.md | 6 + packages/flutter3d_editor_play/README.md | 2 +- .../lib/src/level_push.dart | 71 ++- .../test/level_push_test.dart | 50 +++ packages/flutter3d_game/CHANGELOG.md | 16 + .../flutter3d_game/lib/flutter3d_game.dart | 1 + .../lib/src/run/demo_recording.dart | 191 ++++++++ .../lib/src/run/demo_timeline.dart | 16 + .../lib/src/run/live_level.dart | 102 ++++- .../test/demo_recording_test.dart | 195 ++++++++ .../flutter3d_game/test/live_level_test.dart | 65 ++- .../test/run_timeline_extensions_test.dart | 35 +- packages/flutter3d_sim/CHANGELOG.md | 18 + packages/flutter3d_sim/bin/f3drun_info.dart | 3 + packages/flutter3d_sim/lib/flutter3d_sim.dart | 1 + .../lib/src/level/level_patch.dart | 416 ++++++++++++++++++ packages/flutter3d_sim/lib/src/save/demo.dart | 118 ++++- .../lib/src/save/state_digest.dart | 14 + packages/flutter3d_sim/test/demo_test.dart | 107 +++++ .../flutter3d_sim/test/f3drun_info_test.dart | 36 ++ .../flutter3d_sim/test/level_patch_test.dart | 246 +++++++++++ packages/flutter3d_sim_mcp/CHANGELOG.md | 7 + .../lib/src/sim_session.dart | 10 + .../flutter3d_sim_mcp/test/claims_test.dart | 33 ++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 16 +- tasks/0.9-engine-roadmap.md | 51 ++- 34 files changed, 2150 insertions(+), 104 deletions(-) create mode 100644 apps/flutter3d_demo_platformer/test/demo_level_swap_test.dart create mode 100644 packages/flutter3d_game/lib/src/run/demo_recording.dart create mode 100644 packages/flutter3d_game/test/demo_recording_test.dart create mode 100644 packages/flutter3d_sim/lib/src/level/level_patch.dart create mode 100644 packages/flutter3d_sim/test/level_patch_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index d09a62372..2374d5e33 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10965 tests** across 43 packages and 10 applications | +| Unit tests | **10994 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index cf9512bba..83a8d47d9 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10965 tests across forty-three packages and nine applications. The +There are 10994 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_demo_platformer/lib/main.dart b/apps/flutter3d_demo_platformer/lib/main.dart index 15928b365..7f922459e 100644 --- a/apps/flutter3d_demo_platformer/lib/main.dart +++ b/apps/flutter3d_demo_platformer/lib/main.dart @@ -340,41 +340,60 @@ class _GameScreenState extends State /// this repeats rather than reinvents. DemoFile? _demos; - /// Where the run being recorded started, and in which level. - Snapshot? _demoStart; - String? _demoLevel; - String? _demoLevelHash; - - /// A checkpoint every so many steps, taken live while the run is recorded. - DigestTrace? _demoCheckpoints; - - /// The demo's own recorder, beside no rewind buffer here — this game has - /// none of the dungeon's kill camera to share one with. - InputTapeRecorder? _demoRecorder; + /// The run being written down: its start, its own recorder — beside no + /// rewind buffer here, this game has none of the dungeon's kill camera to + /// share one with — its checkpoints, and `HR3`'s edits made under it. + DemoRecording? _demo; /// Starts writing the run down, from the state the level is in now. /// /// Now rather than at load: a level resumed from a save begins mid-run, and /// the demo has to begin where the player did. The tape's seed is the dice /// the snapshot carries, which is the one number a replay cannot do without. - void _beginDemo(String asset, LevelReady level) { - final start = level.sim.save(); - _demoStart = start; - _demoLevel = asset; - _demoLevelHash = level.loaded.level.digestHex; - _demoCheckpoints = DigestTrace(); + void _beginDemo(String asset, LevelReady level) => _record( + asset: asset, + levelHash: level.loaded.level.digestHex, + start: level.sim.save(), + ); + + void _record({ + required String asset, + required String levelHash, + required Snapshot start, + }) { _endRecording(); - final recorder = InputTapeRecorder(seed: start.data.integer('random')); - _demoRecorder = recorder; - _loop.recorders.add(recorder); + final demo = DemoRecording( + level: asset, + levelHash: levelHash, + start: start, + seed: start.data.integer('random'), + ); + _demo = demo; + _loop.recorders.add(demo.recorder); } /// Stops the demo's recorder. - InputTapeRecorder? _endRecording() { - final recorder = _demoRecorder; - if (recorder != null) _loop.recorders.remove(recorder); - _demoRecorder = null; - return recorder; + DemoRecording? _endRecording() { + final demo = _demo; + if (demo != null) _loop.recorders.remove(demo.recorder); + _demo = null; + return demo; + } + + /// `HR3`: writes an edit the timeline swapped in before [step] into the + /// demo, so the `.f3drun` replays it rather than parting from the run + /// there. + /// + /// An edit that took effect before this demo began — within a keyframe of + /// the level loading — cannot go into it, and the demo starts again from + /// now, in the level it was loaded as, with the edit at its first step. + void _recordSwap(Level next, int step) { + final demo = _demo; + final sim = _sim; + if (demo == null || sim == null) return; + if (demo.levelSwapped(next, stepsAgo: _rewind.step - step)) return; + _record(asset: demo.level, levelHash: demo.levelHash, start: sim.save()); + _demo?.levelSwapped(next, stepsAgo: 0); } /// Writes the run down when it ends, either way. @@ -383,28 +402,10 @@ class _GameScreenState extends State /// me off the edge" is a sentence, and the demo is the proof. Written once /// at the end rather than as it goes — the same reason the save is. void _endDemo() { - final recorder = _endRecording(); - final start = _demoStart; - final level = _demoLevel; - final levelHash = _demoLevelHash; - final checkpoints = _demoCheckpoints; - if (recorder == null || - start == null || - level == null || - levelHash == null || - checkpoints == null) { - return; - } + final demo = _endRecording(); + if (demo == null) return; _demos?.write( - Demo( - level: level, - levelHash: levelHash, - start: start, - tape: recorder.tape, - buildStamp: _buildStamp, - checkpoints: checkpoints, - platform: defaultTargetPlatform.name, - ), + demo.demo(buildStamp: _buildStamp, platform: defaultTargetPlatform.name), ); } @@ -427,8 +428,8 @@ class _GameScreenState extends State /// twice, so later levels move the one door along. Everything the edit /// takes is the run's: it builds the edited level ahead, swaps it in inside /// [_timeline]'s replay when the simulation has to be lived again, and only - /// then tells this widget, through [PlatformerRun.onLevelBuilt] like any - /// load. + /// then tells this widget, through [PlatformerRun.onLevelEdited]. The step + /// the edit took effect at goes into the demo on the way ([_recordSwap]). void _takeEdits(LevelReady level) { final live = _live; if (live != null) { @@ -443,6 +444,7 @@ class _GameScreenState extends State prepare: run.prepareEdit, rebuild: (Level next) => run.installEdit(), present: (Level next, LevelDiff diff) => run.announceEdit(), + swapped: _recordSwap, ), ); } @@ -612,6 +614,10 @@ class _GameScreenState extends State _beginDemo(asset, level); _takeEdits(level); }, + onLevelEdited: (String asset, LevelReady level, GraphicsDevice device) { + setState(() => _levelArrived(level, device)); + _takeEdits(level); + }, ), ); @@ -935,10 +941,7 @@ class _GameScreenState extends State // `rp-01`'s own checkpoint, taken here rather than replayed later from the // finished tape — see the dungeon's identical placement for why the step // number has to be the recorder's own. - final demoRecorder = _demoRecorder; - if (demoRecorder != null) { - _demoCheckpoints?.observe(demoRecorder.tape.steps, sim.save().toJson()); - } + _demo?.observe(sim.save); // Drained once, here, and handed to everything that wants it. Draining // empties the buffer, so two readers each draining would each get half of // what happened — and which half would depend on the order they ran in. diff --git a/apps/flutter3d_demo_platformer/lib/src/run.dart b/apps/flutter3d_demo_platformer/lib/src/run.dart index ef1221d6f..a1f9f739e 100644 --- a/apps/flutter3d_demo_platformer/lib/src/run.dart +++ b/apps/flutter3d_demo_platformer/lib/src/run.dart @@ -95,6 +95,7 @@ final class PlatformerRun extends RunSession { required this.input, required this.openDevice, required this.onLevelBuilt, + this.onLevelEdited, this.startingLives = 3, this.pauseBetweenLevels = const Duration(milliseconds: 1400), }); @@ -115,6 +116,16 @@ final class PlatformerRun extends RunSession { final void Function(String asset, LevelReady level, GraphicsDevice device) onLevelBuilt; + /// What the widget does with an edit of the level being played, once the + /// run is back at now. [onLevelBuilt] when null. + /// + /// **Not the same as a load**, because the run goes on: a load begins a + /// new demo, and an edit is written into the one being recorded + /// (`DemoRecording.levelSwapped`). A widget that began a new demo here + /// would lose everything played before the edit. + final void Function(String asset, LevelReady level, GraphicsDevice device)? + onLevelEdited; + final int startingLives; /// A beat on the results screen before the next level: arriving somewhere new @@ -208,8 +219,8 @@ final class PlatformerRun extends RunSession { } /// Installs the edit if [installEdit] has not, then hands the new build to - /// [onLevelBuilt] as a load would: the runner's node, the camera, a new - /// demo from here. + /// [onLevelEdited]: the runner's node, the camera, the edit written into + /// the demo. void announceEdit() { installEdit(); final playing = status; @@ -218,7 +229,53 @@ final class PlatformerRun extends RunSession { return; } _editUntold = false; - onLevelBuilt(playing.asset, playing.level, device); + (onLevelEdited ?? onLevelBuilt)(playing.asset, playing.level, device); + } + + /// Plays [demo] in the level being played, from its start, with the edits + /// it recorded swapped in where they were (`HR3`), and says whether it + /// kept to its checkpoints. The run is left where the demo ended. + /// + /// **Every edit is built before the first step**, for the reason + /// [prepareEdit] builds ahead: a build waits on the device, and a replay + /// that stopped to wait in the middle would be a replay with a hole in it. + /// The swap itself is [replaceLevel], as it was when the edit arrived live. + /// + /// Throws, and plays nothing, when the level up is not the one the demo + /// starts in, by path or by content. + Future replay(Demo demo) async { + final playing = status; + if (playing is! RunPlaying) { + throw StateError('no level is being played'); + } + if (playing.asset != demo.level) { + throw StateError( + 'the demo starts in ${demo.level}, and ${playing.asset} is up', + ); + } + final hash = playing.level.loaded.level.digestHex; + if (hash != demo.levelHash) { + throw StateError( + '${demo.level} digests to $hash, and the demo was recorded in ' + '${demo.levelHash}; the level has changed since', + ); + } + final device = await openDevice(); + final edits = [ + for (final swap in demo.levelSwaps) + await _build(playing.asset, device, document: swap.level), + ]; + final next = edits.iterator; + return replayDemo( + demo: demo, + input: input, + restore: (Snapshot snapshot) => level!.sim.restore(snapshot), + save: () => level!.sim.save(), + stepSim: (double dt) => level!.sim.step(dt), + swapLevel: (Level _) { + if (next.moveNext()) replaceLevel(next.current); + }, + ); } @override diff --git a/apps/flutter3d_demo_platformer/test/demo_level_swap_test.dart b/apps/flutter3d_demo_platformer/test/demo_level_swap_test.dart new file mode 100644 index 000000000..f718f3197 --- /dev/null +++ b/apps/flutter3d_demo_platformer/test/demo_level_swap_test.dart @@ -0,0 +1,225 @@ +/// `HR3`: a run with an editor's edit in it, written down and played back. +/// +/// flutter test test/demo_level_swap_test.dart +/// +/// The live half goes the way `main.dart` goes: a rewind buffer and the demo's +/// own recorder beside it, the edit through `LiveLevel` and the timeline, the +/// step it took effect at written into the demo by `LiveLevel.swapped`. The +/// replay is `PlatformerRun.replay` in a game of its own, from the file's +/// JSON. The claim is that the two arrive at the same bytes — and that +/// without the swap in the file they would not. +library; + +import 'dart:convert'; + +import 'package:flutter3d/flutter3d.dart' hide Material; +import 'package:flutter3d_app/flutter3d_app.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:flutter3d_demo_platformer/src/run.dart'; +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; + +const String _first = 'assets/levels/first_steps.json'; +const double _dt = 1.0 / 60.0; + +final class _Storage implements Storage { + final Map documents = {}; + + @override + String? read(String name) => documents[name]; + + @override + bool write(String name, String contents) { + documents[name] = contents; + return true; + } + + @override + void remove(String name) => documents.remove(name); +} + +PlatformerRun _game(InputState input) { + final device = CpuDevice( + width: 16, + height: 9, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + return PlatformerRun( + firstLevel: _first, + saves: SaveFile(appName: 'platformer', storage: _Storage()), + input: input, + openDevice: () async => device, + onLevelBuilt: (String asset, LevelReady level, GraphicsDevice device) {}, + pauseBetweenLevels: Duration.zero, + ); +} + +/// Forward most of the time, a jump a second: a run the floor matters to. +void _play(InputState input, int step) { + if (step % 30 < 22) { + input.press(GameAction.moveForward); + } else { + input.release(GameAction.moveForward); + } + if (step % 60 == 0) input.press(GameAction.jump); + if (step % 60 == 3) input.release(GameAction.jump); +} + +/// Steps [from] to [to] the way `GameLoop` and `main.dart`'s `_step` do: +/// both recorders, the keyframe before the step, the checkpoint after it. +void _live( + PlatformerRun run, + InputState input, + RewindBuffer rewind, + DemoRecording demo, { + required int from, + required int to, +}) { + for (var step = from; step < to; step++) { + _play(input, step); + rewind.recorder.record(input); + demo.recorder.record(input); + input.beginStep(); + if (rewind.keyframeDue) rewind.keyframe(run.level!.sim.save()); + run.level!.sim.step(_dt); + demo.observe(run.level!.sim.save); + input.endStep(); + } +} + +/// [level] with every brush half a metre lower: the runner, mid-run, is +/// suddenly over a floor that is not where it was. +Level _lowered(Level level) { + final document = level.toJson(); + return Level.fromJson({ + ...document, + 'brushes': [ + for (final brush in document['brushes']! as List) + switch (brush) { + {'at': final List at} => { + ...brush as Map, + 'at': [ + (at[0]! as num).toDouble(), + (at[1]! as num).toDouble() - 0.5, + (at[2]! as num).toDouble(), + ], + }, + _ => brush, + }, + ], + }); +} + +String _bytes(Snapshot snapshot) => jsonEncode(snapshot.toJson()); + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + test('a run with an edit at step K, saved and replayed, arrives where the ' + 'live run did', () async { + const edited = 150; + const end = 300; + final input = InputState(); + final run = _game(input); + await run.begin(); + final start = run.level!.sim.save(); + final demo = DemoRecording( + level: _first, + levelHash: run.level!.loaded.level.digestHex, + start: start, + seed: start.data.integer('random'), + ); + final rewind = RewindBuffer(stepsPerSecond: 60, history: 10.0); + final timeline = RunTimeline( + rewind: rewind, + input: input, + stepSim: (double dt) => run.level!.sim.step(dt), + restore: (Snapshot snapshot) => run.level!.sim.restore(snapshot), + ); + + _live(run, input, rewind, demo, from: 0, to: edited); + final applied = await LiveLevel( + level: run.level!.loaded.level, + timeline: timeline, + prepare: run.prepareEdit, + rebuild: (Level next) => run.installEdit(), + present: (Level next, LevelDiff diff) => run.announceEdit(), + swapped: (Level next, int step) => + demo.levelSwapped(next, stepsAgo: rewind.step - step), + ).applyWhenReady(_lowered(run.level!.loaded.level)); + _live(run, input, rewind, demo, from: edited, to: end); + final arrived = _bytes(run.level!.sim.save()); + + final sent = jsonEncode(demo.demo(buildStamp: 'test-build').toJson()); + final file = Demo.fromJson(jsonDecode(sent) as Map); + expect(file.steps, end); + expect( + file.levelSwaps.single.step, + applied.swappedAt, + reason: 'the demo started with the timeline, so the steps agree', + ); + expect(file.levelSwaps.single.step, lessThan(edited)); + expect(file.levelSwaps.single.levelHash, run.level!.loaded.level.digestHex); + + final replayInput = InputState(); + final replayRun = _game(replayInput); + await replayRun.begin(); + final replayed = await replayRun.replay(file); + + expect(replayed.steps, end); + expect( + replayed.divergence, + isNull, + reason: 'the replay keeps to the checkpoints written live', + ); + expect(_bytes(replayRun.level!.sim.save()), arrived); + expect( + replayRun.level!.loaded.level.digestHex, + file.levelSwaps.single.levelHash, + reason: 'the replay ends in the edited level, as the run did', + ); + + // Mutation: drop the swap from the file — the replay plays through the + // shipped floor to the end — and it has to arrive somewhere else, or the + // edit never reached the runner and the test above compared nothing. + final unswapped = Demo( + level: file.level, + levelHash: file.levelHash, + start: file.start, + tape: file.tape, + buildStamp: file.buildStamp, + checkpoints: file.checkpoints, + ); + final controlInput = InputState(); + final control = _game(controlInput); + await control.begin(); + final parted = await control.replay(unswapped); + expect(_bytes(control.level!.sim.save()), isNot(arrived)); + expect(parted.divergence, isNotNull); + expect(parted.divergence!.step, greaterThan(file.levelSwaps.single.step)); + }); + + test('a demo recorded in another level is refused before a step', () async { + final run = _game(InputState()); + await run.begin(); + final start = run.level!.sim.save(); + + // Mutation: drop the path check in `replay` — the tape plays into the + // level that happens to be up and answers with a divergence, which reads + // as a determinism bug rather than as the wrong file. + await expectLater( + run.replay( + Demo( + level: 'assets/levels/ascent.json', + levelHash: run.level!.loaded.level.digestHex, + start: start, + tape: InputTape(seed: 1, frames: const []), + buildStamp: 'test-build', + checkpoints: DigestTrace(), + ), + ), + throwsA(isA()), + ); + }); +} diff --git a/apps/flutter3d_editor/lib/main.dart b/apps/flutter3d_editor/lib/main.dart index 779a6a081..d87911b7e 100644 --- a/apps/flutter3d_editor/lib/main.dart +++ b/apps/flutter3d_editor/lib/main.dart @@ -211,6 +211,11 @@ class _EditorScreenState extends State /// The connection [_liveMaterials] sends through, to the game running now. GameMaterials? _gameMaterials; + /// `HR3`: the open document as it was last read or written, which is the + /// best guess at what a running game is playing. A save sends the game a + /// patch from this rather than the whole level; the game checks the guess. + String? _written; + /// `rp-04`: the macOS side of file association calls back through this — /// a double-click on a `.f3drun` in Finder, or a drop on the dock icon. OpenRunChannel? _openRunChannel; @@ -384,10 +389,8 @@ class _EditorScreenState extends State /// that turns out not to be a level is not offered back tomorrow. Future _openAt(String found) async { try { - final editing = Editing.parse( - await File(found).readAsString(), - path: found, - ); + final text = await File(found).readAsString(); + final editing = Editing.parse(text, path: found); _recent = _projects.remember(found, exists: _onDisk); // Where this document's own `assets/…` live. A game never has to work // this out; an editor always does, because the level it has open belongs @@ -399,6 +402,7 @@ class _EditorScreenState extends State final looks = await _readLooks(assetRoot); _standWhereThePlayerWould(editing.level); if (!mounted) return; + _written = text; _cubit.opened(editing, assetRoot: assetRoot, looks: looks); await _build(); } catch (error) { @@ -1135,7 +1139,11 @@ class _EditorScreenState extends State _cubit.say('written to $path'); // `HR3`: the game this level is playing in takes it too. Not a copy: // the running game plays the original, not the file beside it. - if (!copy) unawaited(_pushToRunningGame(document)); + if (!copy) { + final base = _written; + _written = document; + unawaited(_pushToRunningGame(document, base: base)); + } } catch (error) { _cubit.say('could not write $path: $error'); } @@ -1143,10 +1151,13 @@ class _EditorScreenState extends State /// Sends a saved level to the game [_run] started, when it is running, /// and says in the status strip what the game did with it. - Future _pushToRunningGame(String document) async { + /// + /// As a patch from [base], the document this save wrote over, when the + /// game is playing that; whole when it is not. + Future _pushToRunningGame(String document, {String? base}) async { if (_run?.state.value case PlayRunning(:final vmService)) { try { - final answer = await pushLevel(vmService, document); + final answer = await pushLevel(vmService, document, base: base); _cubit.say(describeLevelApplied(answer)); } catch (error) { _cubit.say('saved, but the running game did not take it: $error'); diff --git a/apps/flutter3d_editor/macos/Flutter/GeneratedPluginRegistrant.swift b/apps/flutter3d_editor/macos/Flutter/GeneratedPluginRegistrant.swift index 48a37e4cf..98ae825f6 100644 --- a/apps/flutter3d_editor/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/apps/flutter3d_editor/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,13 +6,11 @@ import FlutterMacOS import Foundation import file_selector_macos -import flutter_soloud import pad_input import pointer_lock func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) - FlutterSoloudPlugin.register(with: registry.registrar(forPlugin: "FlutterSoloudPlugin")) GamepadPlugin.register(with: registry.registrar(forPlugin: "GamepadPlugin")) PointerLockPlugin.register(with: registry.registrar(forPlugin: "PointerLockPlugin")) } diff --git a/packages/flutter3d_editor_play/CHANGELOG.md b/packages/flutter3d_editor_play/CHANGELOG.md index 42c4155e4..9f4f756f2 100644 --- a/packages/flutter3d_editor_play/CHANGELOG.md +++ b/packages/flutter3d_editor_play/CHANGELOG.md @@ -1,5 +1,11 @@ ## Unreleased +- **A save goes to the running game as a patch.** `pushLevel(base:)` sends + the patch from `base` to the saved document when the game has + `ext.flutter3d.level.patch` and the patch is shorter, and the whole + document when the game answers that the patch is stale; the answer then + carries `fellBack`, which `describeLevelApplied` puts at the end of its + line. `levelPatchArguments` builds the parameters. - **Play leaves the editor application.** `FlutterRun`, `projectRootFor` and `pushLevel` move here from `apps/flutter3d_editor/lib/src/play`, with `Watched` in place of Flutter's `ValueNotifier`, so the editor's MCP server diff --git a/packages/flutter3d_editor_play/README.md b/packages/flutter3d_editor_play/README.md index 97881f656..d98b841d1 100644 --- a/packages/flutter3d_editor_play/README.md +++ b/packages/flutter3d_editor_play/README.md @@ -29,7 +29,7 @@ if (run.state.value case PlayRunning(:final vmService)) { | `FlutterRun` | One `flutter run --machine`: its state, its console, reload, restart, stop | | `projectRootFor`, `projectRootOnDisk` | The project a level file belongs to: the nearest `pubspec.yaml` above it | | `flutterDevices`, `parseFlutterDevices` | What `-d` can be given, from `flutter devices --machine` | -| `pushLevel` | A saved level, sent to the running game's `ext.flutter3d.level.apply` | +| `pushLevel` | A saved level, sent to the running game's `ext.flutter3d.level.apply` — or, given the `base` it was saved over, as a patch to `ext.flutter3d.level.patch`, whole only when the game says the patch is stale | | `Watched` | A value and a stream of its changes, where Flutter's `ValueNotifier` cannot go | `package:flutter3d_editor_play/testing.dart` has `FakeFlutterTool`, a diff --git a/packages/flutter3d_editor_play/lib/src/level_push.dart b/packages/flutter3d_editor_play/lib/src/level_push.dart index 0f634386b..a748c8c48 100644 --- a/packages/flutter3d_editor_play/lib/src/level_push.dart +++ b/packages/flutter3d_editor_play/lib/src/level_push.dart @@ -20,8 +20,43 @@ Map levelApplyArguments(String document) => { ).digestHex, }; +/// The parameters `ext.flutter3d.level.patch` takes to turn [base], the level +/// the game is believed to have, into [document]; null when the patch would +/// be no shorter than the document, or [base] is not a level at all. +/// +/// **A guess, checked at the other end.** The editor cannot see which +/// version the game is playing — it may have been started from an older +/// file, or have refused the last save — so [base] is what the editor last +/// wrote, and the patch names its digest. A wrong guess costs one refusal and +/// the whole document after it. +Map? levelPatchArguments(String base, String document) { + final Level before; + try { + before = Level.fromJson(jsonDecode(base) as Map); + } on Object { + return null; + } + final after = Level.fromJson(jsonDecode(document) as Map); + final patch = jsonEncode(LevelPatch.between(before, after)); + return patch.length < document.length + ? {'patch': patch} + : null; +} + /// What the game said it did with a level, in a line for the status strip. +/// +/// A level that went whole after its patch was refused says why at the end: +/// it is the one sign that the editor and the game disagreed about which +/// version was running. String describeLevelApplied(Map answer) { + final line = _describeApplied(answer); + return switch (answer['fellBack']) { + final String reason => '$line (sent whole: $reason)', + _ => line, + }; +} + +String _describeApplied(Map answer) { final diff = answer['diff'] as Map? ?? const {}; final simulation = (diff['simulation'] as List? ?? const []) .cast(); @@ -47,28 +82,54 @@ bool _unchanged(Object? value) => switch (value) { /// Sends [document] to the game whose VM service is at [vmService], to the /// isolate that answers `ext.flutter3d.level.apply`. /// +/// With a [base] — the level the game is believed to be playing — what goes +/// first is the patch from it to [document] (`ext.flutter3d.level.patch`), +/// and the whole document only when the game answers that the patch is +/// stale, when it predates patches, or when the patch would be no shorter. +/// The answer then carries `fellBack`, the game's reason. +/// /// Throws when no isolate there has registered it: a game that never called /// `registerLevelExtension` cannot take a level, and saying so is better /// than a silence the person reads as "sent". Future> pushLevel( String vmService, - String document, -) async { + String document, { + String? base, +}) async { const method = 'ext.flutter3d.level.apply'; + const patchMethod = 'ext.flutter3d.level.patch'; final service = await vmServiceConnectUri(vmService); try { final vm = await service.getVM(); for (final ref in vm.isolates ?? const []) { final isolate = await service.getIsolate(ref.id!); - if (!(isolate.extensionRPCs ?? const []).contains(method)) { - continue; + final rpcs = isolate.extensionRPCs ?? const []; + if (!rpcs.contains(method)) continue; + final patch = base == null || !rpcs.contains(patchMethod) + ? null + : levelPatchArguments(base, document); + final String? fellBack; + if (patch != null) { + try { + final response = await service.callServiceExtension( + patchMethod, + isolateId: ref.id, + args: patch, + ); + return response.json ?? const {}; + } on RPCError catch (error) { + if (error.code != LevelPatch.staleCode) rethrow; + fellBack = error.details ?? error.message; + } + } else { + fellBack = null; } final response = await service.callServiceExtension( method, isolateId: ref.id, args: levelApplyArguments(document), ); - return response.json ?? const {}; + return {...?response.json, 'fellBack': ?fellBack}; } throw StateError( 'the running game does not take levels: nothing registered $method', diff --git a/packages/flutter3d_editor_play/test/level_push_test.dart b/packages/flutter3d_editor_play/test/level_push_test.dart index 2121ade2a..881844493 100644 --- a/packages/flutter3d_editor_play/test/level_push_test.dart +++ b/packages/flutter3d_editor_play/test/level_push_test.dart @@ -30,6 +30,56 @@ void main() { ); }); + // Mutation: build the patch from the text rather than from the level the + // game would parse, or send it when it is longer than the document, and + // one of these fails. + test('a save after a save goes as a patch, when that is shorter', () { + final base = jsonEncode({ + 'version': 1, + 'name': 'yard', + 'brushes': [ + for (var i = 0; i < 20; i++) + { + 'at': [i * 2.0, 0, 0], + 'size': [1, 1, 1], + }, + ], + }); + final moved = base.replaceFirst('[6.0,0.0,0.0]', '[6.5,0.0,0.0]'); + expect(moved, isNot(base)); + + final arguments = levelPatchArguments(base, moved)!; + final patch = LevelPatch.fromJson( + jsonDecode(arguments['patch']!) as Map, + ); + expect(patch.rows.single.at, 3); + expect( + (patch.applyTo(Level.fromJson(jsonDecode(base) as Map)) + as LevelPatched) + .level + .digestHex, + levelApplyArguments(moved)['hash'], + ); + + // A one-line level is shorter whole than as a patch. + expect( + levelPatchArguments(_document, _document.replaceFirst('0.02', '0.03')), + isNull, + ); + expect(levelPatchArguments('not json', _document), isNull); + }); + + test('a level sent whole after a stale patch says why', () { + expect( + describeLevelApplied({ + 'diff': {'fog': true}, + 'fellBack': 'the patch was made against level 1 and the game has 2', + }), + 'the game took the new look ' + '(sent whole: the patch was made against level 1 and the game has 2)', + ); + }); + test('the game\'s answer, in a line', () { Map answer({ List simulation = const [], diff --git a/packages/flutter3d_game/CHANGELOG.md b/packages/flutter3d_game/CHANGELOG.md index 0a9248b6f..4ae67f0c8 100644 --- a/packages/flutter3d_game/CHANGELOG.md +++ b/packages/flutter3d_game/CHANGELOG.md @@ -1,5 +1,21 @@ ## Unreleased +- **An edit made under a running game goes into its demo.** + `DemoRecording` is the run being written down: start, recorder, + checkpoints and the levels swapped in, with `levelSwapped` turning the + timeline's step into one on this tape and dropping what the swap lived + again. `LiveLevel(swapped:)` hears the step the timeline swapped at, before + `present`. `replayDemo` plays a `Demo` from its start, swaps each level in + where the tape reaches it and compares the file's checkpoints by step. + `rewindBufferFromDemo` refuses a demo with swaps, since a keyframe before a + swap belongs to the other level. +- **`ext.flutter3d.level.patch` takes an edit instead of a whole level.** + `registerLevelExtension` registers it beside `level.apply`; + `answerLevelPatch` applies a `LevelPatch` to `LiveLevel.level` and passes + the patch's own diff to `applyWhenReady(diff:)` and `apply(diff:)`. A + patch made against another version is answered with + `LevelPatch.staleCode`, which tells the sender to send the whole level; a + patched level that does not build is refused as an ordinary error. - **`replayAfterHotSwap` asks the question a hot reload leaves open on its own.** After every `HotSwap` it lives the last seconds again under the new code and says whether they came out the same or where they parted, in the diff --git a/packages/flutter3d_game/lib/flutter3d_game.dart b/packages/flutter3d_game/lib/flutter3d_game.dart index 914a45977..fef60945e 100644 --- a/packages/flutter3d_game/lib/flutter3d_game.dart +++ b/packages/flutter3d_game/lib/flutter3d_game.dart @@ -43,6 +43,7 @@ export 'src/input/pad_actions.dart'; export 'src/input/playing.dart'; export 'src/input/touch_controls.dart'; export 'src/run/bug_report.dart'; +export 'src/run/demo_recording.dart'; export 'src/run/demo_timeline.dart'; export 'src/run/live_level.dart'; export 'src/run/replay_after_swap.dart'; diff --git a/packages/flutter3d_game/lib/src/run/demo_recording.dart b/packages/flutter3d_game/lib/src/run/demo_recording.dart new file mode 100644 index 000000000..fb748bf27 --- /dev/null +++ b/packages/flutter3d_game/lib/src/run/demo_recording.dart @@ -0,0 +1,191 @@ +import 'package:flutter3d_sim/flutter3d_sim.dart'; + +/// A [Demo] being written while the run is played: the tape, the +/// checkpoints, and `HR3`'s levels swapped in under it. +/// +/// **What used to be five fields in every game's widget.** Each game kept a +/// start, a level, a hash, a trace and a recorder beside each other and +/// assembled the [Demo] at the end; a level swapped in mid-run had nowhere to +/// go, so the platformer threw its demo away at every edit and began another. +/// The swap has to touch the trace and the list of swaps together, at a step +/// counted in this tape rather than the timeline's, and that is one object's +/// job. +final class DemoRecording { + DemoRecording({ + required this.level, + required this.levelHash, + required this.start, + required int seed, + int checkpointEvery = 25, + }) : recorder = InputTapeRecorder(seed: seed), + checkpoints = DigestTrace(every: checkpointEvery); + + /// The asset path of the level the run started in. + final String level; + + /// [Level.digestHex] of that level as it was loaded. + final String levelHash; + + /// The state the tape starts from. + final Snapshot start; + + /// Where the loop writes each step's input. Add it to `GameLoop.recorders`. + final InputTapeRecorder recorder; + + /// A digest every so many steps, taken live. + final DigestTrace checkpoints; + + final List _swaps = []; + + /// How many steps have been recorded. + int get steps => recorder.tape.steps; + + /// Takes a checkpoint if the step just run is one. Call after every step; + /// [after] is asked for the state the step left only on a checkpoint step, + /// since a save of every body is what a checkpoint costs. + void observe(Snapshot Function() after) { + final step = recorder.tape.steps; + if (step % checkpoints.every == 0) { + checkpoints.observe(step, after().toJson()); + } + } + + /// Writes down that [next] was put under the run [stepsAgo] steps before + /// the present — `RunTimeline.swapLevel`'s step, counted back from now, + /// since the timeline and this tape did not start at the same step. + /// + /// The swap replaced the run from there on, so a swap already written at + /// or after that step is dropped with the checkpoints taken after it: both + /// describe a run that was lived again. + /// + /// False, and nothing written, when the swap took effect before this + /// recording began. The start this recording holds was then made by the + /// old level and the run since by the new one, and no file can say that; + /// the caller begins a new recording from the present instead and writes + /// the swap at its step zero. + bool levelSwapped(Level next, {required int stepsAgo}) { + final at = recorder.tape.steps - stepsAgo; + if (at < 0) return false; + _swaps + ..removeWhere((swap) => swap.step >= at) + ..add(DemoLevelSwap(step: at, level: next)); + checkpoints.forgetAfter(at); + return true; + } + + /// The run so far, as a file. + Demo demo({ + required String buildStamp, + String? platform, + String? recordedBy, + }) => Demo( + level: level, + levelHash: levelHash, + start: start, + tape: recorder.tape, + buildStamp: buildStamp, + checkpoints: checkpoints, + platform: platform, + recordedBy: recordedBy, + levelSwaps: List.unmodifiable(_swaps), + ); +} + +/// What [replayDemo] found. +final class DemoReplay { + const DemoReplay({required this.steps, this.divergence}); + + /// How many steps were played. + final int steps; + + /// The first checkpoint the replay did not match, or null when it matched + /// every one the file holds. + final Divergence? divergence; +} + +/// Plays [demo] through the simulation from its start, swapping in each of +/// [Demo.levelSwaps] as the tape reaches it, and checks the file's own +/// checkpoints on the way. +/// +/// The level [Demo.level] names must be up when this is called; [swapLevel] +/// puts a swapped document in its place and must carry the run over, as the +/// game did live — the state [save] answers before it is what [save] answers +/// after. It is required exactly when the demo has swaps in it: a replay that +/// played through them would part from the run at the first and report that +/// as a divergence of the simulation. +/// +/// A swap at step K goes in after the checkpoint at K is compared, which is +/// the order the live run met them in: the checkpoint was taken under the +/// old level, and the timeline swapped at the keyframe that state was. +/// +/// Checkpoints are compared by step rather than by position, since a swap +/// leaves a gap in them (`DigestTrace.forgetAfter`). +DemoReplay replayDemo({ + required Demo demo, + required InputState input, + required void Function(Snapshot snapshot) restore, + required Snapshot Function() save, + required void Function(double dt) stepSim, + void Function(Level level)? swapLevel, + double stepSeconds = 1.0 / 60.0, +}) { + if (demo.levelSwaps.isNotEmpty && swapLevel == null) { + throw ArgumentError.value( + demo, + 'demo', + 'replaces its level at step ${demo.levelSwaps.first.step}, and no ' + 'swapLevel was given to replace it with', + ); + } + final expected = { + for (var i = 0; i < demo.checkpoints.steps.length; i++) + demo.checkpoints.steps[i]: demo.checkpoints.digests[i], + }; + final swaps = demo.levelSwaps; + var nextSwap = 0; + void swapAt(int step) { + while (nextSwap < swaps.length && swaps[nextSwap].step == step) { + swapLevel!(swaps[nextSwap++].level); + } + } + + restore(demo.start); + swapAt(0); + final playback = InputTapePlayback(demo.tape); + final wasMuted = input.muted; + input.muted = true; + Divergence? divergence; + var step = 0; + try { + while (!playback.isFinished) { + playback.applyTo(input); + input.beginStep(); + stepSim(stepSeconds); + input.endStep(); + step++; + final digest = expected[step]; + if (digest != null && divergence == null) { + final found = StateDigest.of(save().toJson()); + if (found != digest) { + divergence = Divergence(step: step, expected: digest, found: found); + } + } + swapAt(step); + } + } finally { + input.muted = wasMuted; + } + final unreached = demo.checkpoints.steps.where((s) => s > step).firstOrNull; + return DemoReplay( + steps: step, + divergence: + divergence ?? + (unreached == null + ? null + : Divergence( + step: unreached, + expected: expected[unreached], + found: null, + )), + ); +} diff --git a/packages/flutter3d_game/lib/src/run/demo_timeline.dart b/packages/flutter3d_game/lib/src/run/demo_timeline.dart index dd8314b62..d38cb63ea 100644 --- a/packages/flutter3d_game/lib/src/run/demo_timeline.dart +++ b/packages/flutter3d_game/lib/src/run/demo_timeline.dart @@ -1,5 +1,7 @@ import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'demo_recording.dart'; + /// Rebuilds a [RewindBuffer] that reaches every step of [demo], not only the /// last few seconds a live [RunTimeline] keeps — `rp-02`'s "скраббер по всему /// прогону из `.f3drun`", built by replaying the whole tape once rather than @@ -21,6 +23,12 @@ import 'package:flutter3d_sim/flutter3d_sim.dart'; /// still meant to be reachable; a caller wanting to bound memory for a very /// long recording can pass a smaller one and accept that only its most /// recent stretch scrubs. +/// +/// A demo with `HR3` level swaps in it is refused: a keyframe before a swap +/// is a state of the old level, and a scrub to it would restore that state +/// under whichever level is up. Scrubbing across a swap needs the buffer to +/// know which level each keyframe belongs to; until it does, [replayDemo] +/// plays such a run and this does not pretend to scrub it. RewindBuffer rewindBufferFromDemo({ required Demo demo, required int stepsPerSecond, @@ -31,6 +39,14 @@ RewindBuffer rewindBufferFromDemo({ required Snapshot Function() save, required void Function(double dt) stepSim, }) { + if (demo.levelSwaps.isNotEmpty) { + throw ArgumentError.value( + demo, + 'demo', + 'replaces its level at step ${demo.levelSwaps.first.step}; a scrub ' + 'across a swap would restore one level\'s state under the other', + ); + } restore(demo.start); final wholeRun = demo.tape.frames.length / stepsPerSecond; final buffer = RewindBuffer( diff --git a/packages/flutter3d_game/lib/src/run/live_level.dart b/packages/flutter3d_game/lib/src/run/live_level.dart index 47b88c29f..dd482f5c5 100644 --- a/packages/flutter3d_game/lib/src/run/live_level.dart +++ b/packages/flutter3d_game/lib/src/run/live_level.dart @@ -3,6 +3,7 @@ import 'dart:developer' as developer; import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'demo_recording.dart'; import 'run_timeline.dart'; /// The level a running game is playing, and how to change it under the game. @@ -26,8 +27,15 @@ final class LiveLevel { required this.rebuild, this.timeline, this.prepare, + this.swapped, }); + /// Told the step [timeline] swapped [next] in before, right after the swap + /// and before [present]: what a [DemoRecording] needs to write the edit + /// into the run. The step is decided inside `RunTimeline.swapLevel` — the + /// last keyframe — so nothing outside the swap can know it otherwise. + final void Function(Level next, int step)? swapped; + /// Builds ahead what [rebuild] and [present] will swap in, for a game /// whose level cannot be made synchronously: textures to upload, meshes, /// a scene. Awaited by [applyWhenReady] before anything changes, so a @@ -55,37 +63,42 @@ final class LiveLevel { /// [prepare], then [apply]: what the extension does with a level that /// arrived. An edit that changes nothing prepares nothing. - Future applyWhenReady(Level next) async { - if (!diffLevel(level, next).isEmpty) await prepare?.call(next); - return apply(next); + Future applyWhenReady(Level next, {LevelDiff? diff}) async { + final change = diff ?? diffLevel(level, next); + if (!change.isEmpty) await prepare?.call(next); + return apply(next, diff: change); } /// Makes [next] the level being played, as little disturbed as the change /// allows. - LevelApplied apply(Level next) { - final diff = diffLevel(level, next); - if (diff.isEmpty) { + /// + /// [diff] is what a `LevelPatch` already said changed; without one the two + /// levels are compared by [diffLevel]. + LevelApplied apply(Level next, {LevelDiff? diff}) { + final change = diff ?? diffLevel(level, next); + if (change.isEmpty) { level = next; - return LevelApplied(diff: diff); + return LevelApplied(diff: change); } final int? swappedAt; - if (diff.presentationOnly) { + if (change.presentationOnly) { swappedAt = null; } else if (timeline case final RunTimeline run) { swappedAt = run.swapLevel( () => rebuild(next), levelDigest: next.digestHex, ); + swapped?.call(next, swappedAt); } else { rebuild(next); swappedAt = null; } - present(next, diff); + present(next, change); level = next; return LevelApplied( - diff: diff, + diff: change, swappedAt: swappedAt, - rebuiltInPlace: !diff.presentationOnly && swappedAt == null, + rebuiltInPlace: !change.presentationOnly && swappedAt == null, ); } } @@ -159,9 +172,55 @@ Future<({Map? result, String? error})> answerLevelApply( } } -/// Puts [live] on the VM service as `ext.flutter3d.level.apply`, beside the -/// timeline's own extensions: the editor that saved a level sends it here, -/// to a game on this machine or on a phone, and the running game takes it. +/// Answers `ext.flutter3d.level.patch` for [live]: [parameters] carry a +/// `LevelPatch` as JSON, made against the level the editor last saved. +/// +/// **Two kinds of no, because the sender does different things with them.** +/// A patch that does not apply to what the game has — another version, a +/// row that is not the one it names — is [stale]: the editor sends the whole +/// document, which the game can take whatever it had. A patch that applies +/// to a level that will not build is refused like a document that will not +/// build, and is not stale: sent whole, it would be refused again. +Future<({Map? result, String? error, bool stale})> +answerLevelPatch(LiveLevel live, Map parameters) async { + final text = parameters['patch']; + if (text == null) { + return (result: null, error: 'level.patch takes a patch', stale: false); + } + final LevelPatch patch; + try { + patch = LevelPatch.fromJson(jsonDecode(text) as Map); + } on Object catch (error) { + return ( + result: null, + error: 'the patch is not a level patch: $error', + stale: false, + ); + } + switch (patch.applyTo(live.level)) { + case LevelPatchRefused(:final reason): + return (result: null, error: reason, stale: true); + case LevelPatched(:final level, :final diff): + try { + final applied = await live.applyWhenReady(level, diff: diff); + return (result: applied.toJson(), error: null, stale: false); + } on Object catch (error) { + return ( + result: null, + error: 'the level did not build: $error', + stale: false, + ); + } + } +} + +/// Puts [live] on the VM service as `ext.flutter3d.level.apply` and +/// `ext.flutter3d.level.patch`, beside the timeline's own extensions: the +/// editor that saved a level sends it here, to a game on this machine or on +/// a phone, and the running game takes it. +/// +/// A stale patch is answered with `LevelPatch.staleCode` rather than +/// `invalidParams`, so the editor can tell "send it whole" from "no". void registerLevelExtension(LiveLevel live) { developer.registerExtension('ext.flutter3d.level.apply', ( method, @@ -176,4 +235,19 @@ void registerLevelExtension(LiveLevel live) { } return developer.ServiceExtensionResponse.result(jsonEncode(result)); }); + developer.registerExtension('ext.flutter3d.level.patch', ( + method, + parameters, + ) async { + final (:result, :error, :stale) = await answerLevelPatch(live, parameters); + if (error != null) { + return developer.ServiceExtensionResponse.error( + stale + ? LevelPatch.staleCode + : developer.ServiceExtensionResponse.invalidParams, + error, + ); + } + return developer.ServiceExtensionResponse.result(jsonEncode(result)); + }); } diff --git a/packages/flutter3d_game/test/demo_recording_test.dart b/packages/flutter3d_game/test/demo_recording_test.dart new file mode 100644 index 000000000..2dda76bec --- /dev/null +++ b/packages/flutter3d_game/test/demo_recording_test.dart @@ -0,0 +1,195 @@ +/// `HR3`'s tail: a level swapped under a run is written into the run's demo +/// and played back from it. +/// +/// flutter test test/demo_recording_test.dart +/// +/// On a toy whose one level property is its speed, so that a swap is a thing +/// the state visibly depends on. The shipped game's version of the claim, +/// through its own loader and its own swap, is the platformer's +/// `demo_level_swap_test.dart`. +library; + +import 'dart:convert'; + +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// The toy reads its speed off the level's fog density: any number a level +/// document carries will do, and this one needs no brushes. +Level _level(double speed) => Level(name: 'toy', fogDensity: speed); + +final class _Toy { + double speed = 1.0; + double x = 0.0; + + void step(InputState input) => x += input.moveAxis.x * speed; + + Snapshot save() => Snapshot({'x': x, 'random': 0}); + + void restore(Snapshot snapshot) => x = snapshot.data.number('x'); +} + +void _play(InputState input, int step) => + input.setStickAxis(step % 5 == 0 ? -0.25 : 1.0, 0.0); + +/// A run of [steps] steps on a live timeline with [edits] made at the steps +/// they are keyed by, recorded the way a game records it. +({DemoRecording demo, _Toy toy, List swappedAt}) _live({ + required int steps, + Map edits = const {}, +}) { + final toy = _Toy(); + final input = InputState(); + final rewind = RewindBuffer(stepsPerSecond: 60, keyframeEvery: 20); + final timeline = RunTimeline( + rewind: rewind, + input: input, + stepSim: (dt) => toy.step(input), + restore: toy.restore, + ); + final demo = DemoRecording( + level: 'assets/levels/toy.json', + levelHash: _level(1.0).digestHex, + start: toy.save(), + seed: 0, + checkpointEvery: 10, + ); + final swappedAt = []; + for (var step = 0; step < steps; step++) { + if (edits[step] case final double speed) { + final next = _level(speed); + final at = timeline.swapLevel( + () => toy.speed = speed, + levelDigest: next.digestHex, + ); + swappedAt.add(at); + expect(demo.levelSwapped(next, stepsAgo: rewind.step - at), isTrue); + } + _play(input, step); + rewind.recorder.record(input); + demo.recorder.record(input); + input.beginStep(); + if (rewind.keyframeDue) rewind.keyframe(toy.save()); + toy.step(input); + demo.observe(toy.save); + input.endStep(); + } + return (demo: demo, toy: toy, swappedAt: swappedAt); +} + +/// Plays [demo] on a fresh toy, [swaps] or not. +({DemoReplay replay, _Toy toy}) _replay(Demo demo, {bool swaps = true}) { + final toy = _Toy(); + final input = InputState(); + final replay = replayDemo( + demo: demo, + input: input, + restore: toy.restore, + save: toy.save, + stepSim: (dt) => toy.step(input), + swapLevel: swaps ? (level) => toy.speed = level.fogDensity : (level) {}, + ); + return (replay: replay, toy: toy); +} + +Demo _sent(DemoRecording demo) => Demo.fromJson( + jsonDecode(jsonEncode(demo.demo(buildStamp: 'test').toJson())) + as Map, +); + +void main() { + test('a run swapped at step K, saved and replayed, arrives where it did', () { + final live = _live(steps: 120, edits: const {50: 3.0}); + final file = _sent(live.demo); + + expect(file.levelSwaps.single.step, live.swappedAt.single); + expect(file.levelSwaps.single.step, 40, reason: 'the keyframe before 50'); + + final replayed = _replay(file); + expect(replayed.replay.divergence, isNull); + expect(replayed.replay.steps, 120); + expect(replayed.toy.x, live.toy.x); + + // Mutation: a replay that does not swap — the speed it plays at after + // step 40 is the old one, and the first checkpoint the file holds after + // it says so: 60, since the one at 50 was taken before the swap went + // back over it. + final unswapped = _replay(file, swaps: false); + expect(unswapped.toy.x, isNot(live.toy.x)); + expect(unswapped.replay.divergence?.step, 60); + }); + + test('a second edit within a keyframe replaces the first', () { + // Both swaps go back to the keyframe at 40: the second replays the + // steps the first had replayed, so the first never happened. + final live = _live(steps: 90, edits: const {45: 3.0, 55: 0.5}); + final file = _sent(live.demo); + + // Mutation: keep the earlier swap at the same step — the file is + // refused on reading, since two swaps at one step are out of order. + expect(live.swappedAt, [40, 40]); + expect(file.levelSwaps.single.level.fogDensity, 0.5); + expect(_replay(file).toy.x, live.toy.x); + }); + + test('the checkpoints after the swap step are forgotten, and the ones ' + 'before kept', () { + final live = _live(steps: 60, edits: const {50: 3.0}); + + // Mutation: keep the trace whole — the checkpoint at 50 was taken under + // the old speed, and every honest replay would diverge there. + expect(live.demo.checkpoints.steps, [10, 20, 30, 40, 60]); + }); + + test('a swap from before the recording began is not written into it', () { + final demo = DemoRecording( + level: 'assets/levels/toy.json', + levelHash: _level(1.0).digestHex, + start: _Toy().save(), + seed: 0, + ); + for (var step = 0; step < 5; step++) { + demo.recorder.record(InputState()); + } + + // Mutation: clamp the step to zero — the file would say the start was + // made by the new level, when the old one made it. + expect(demo.levelSwapped(_level(2.0), stepsAgo: 6), isFalse); + expect(demo.demo(buildStamp: 'test').levelSwaps, isEmpty); + expect(demo.levelSwapped(_level(2.0), stepsAgo: 5), isTrue); + expect(demo.demo(buildStamp: 'test').levelSwaps.single.step, 0); + }); + + test('a demo with swaps is refused where it cannot be played honestly', () { + final file = _sent( + _live(steps: 60, edits: const {30: 2.0}).demo, + ); + final toy = _Toy(); + final input = InputState(); + + // Mutation: drop either check — the replay plays through the swap and + // calls the result a divergence of the simulation. + expect( + () => replayDemo( + demo: file, + input: input, + restore: toy.restore, + save: toy.save, + stepSim: (dt) => toy.step(input), + ), + throwsArgumentError, + ); + expect( + () => rewindBufferFromDemo( + demo: file, + stepsPerSecond: 60, + input: input, + restore: toy.restore, + save: toy.save, + stepSim: (dt) => toy.step(input), + ), + throwsArgumentError, + ); + }); +} diff --git a/packages/flutter3d_game/test/live_level_test.dart b/packages/flutter3d_game/test/live_level_test.dart index ffa9e3f33..a31053f8d 100644 --- a/packages/flutter3d_game/test/live_level_test.dart +++ b/packages/flutter3d_game/test/live_level_test.dart @@ -44,6 +44,7 @@ Level _level({double wallAt = 5.0, double fog = 0.0}) => present: (next, diff) => calls.add('present fog=${diff.fog}'), rebuild: (next) => calls.add('rebuild ${next.brushes.single.centre.x}'), timeline: timeline, + swapped: (next, step) => calls.add('swapped $step'), prepare: prepares || prepareFails != null ? (next) async { await Future.delayed(Duration.zero); @@ -90,7 +91,13 @@ void main() { final applied = game.live.apply(_level(wallAt: 8.0)); - expect(game.calls, ['rebuild 8.0', 'present fog=false']); + // Mutation: drop the `swapped` call — the run goes on under the new + // level and the demo being recorded never hears of it. + expect(game.calls, [ + 'rebuild 8.0', + 'swapped 0', + 'present fog=false', + ]); expect(applied.swappedAt, 0); expect(restored, hasLength(1), reason: 'the keyframe the replay starts at'); expect( @@ -174,6 +181,62 @@ void main() { expect(game.calls, isEmpty); }); + // Mutation: answer a stale patch as an ordinary refusal and the editor + // never sends the whole level; mark a level that will not build as + // stale and it sends it whole only to be refused again. + test('a patch against the level the game has is applied', () async { + final game = _game(); + final patch = LevelPatch.between(_level(), _level(wallAt: 8.0)); + + final (:result, :error, :stale) = await answerLevelPatch( + game.live, + {'patch': jsonEncode(patch.toJson())}, + ); + + expect(error, isNull); + expect(stale, isFalse); + expect((result!['diff']! as Map)['simulation'], [ + 'brushes', + ]); + expect(game.calls, ['rebuild 8.0', 'present fog=false']); + expect(game.live.level.digestHex, _level(wallAt: 8.0).digestHex); + }); + + test( + 'a patch against another version is stale, and changes nothing', + () async { + final game = _game(); + final patch = LevelPatch.between( + _level(fog: 0.04), + _level(wallAt: 8.0), + ); + + final (:result, :error, :stale) = await answerLevelPatch( + game.live, + {'patch': jsonEncode(patch.toJson())}, + ); + + expect(result, isNull); + expect(stale, isTrue); + expect(error, contains(_level().digestHex)); + expect(game.calls, isEmpty); + }, + ); + + test('a patched level that does not build is refused, not stale', () async { + final game = _game(prepareFails: 'no texture stone.png'); + final patch = LevelPatch.between(_level(), _level(wallAt: 8.0)); + + final (:result, :error, :stale) = await answerLevelPatch( + game.live, + {'patch': jsonEncode(patch.toJson())}, + ); + + expect(stale, isFalse); + expect(error, contains('did not build')); + expect(game.live.level.brushes.single.centre.x, 5.0); + }); + test('refuses what is not a level, and what is missing', () async { final game = _game(); expect( diff --git a/packages/flutter3d_game/test/run_timeline_extensions_test.dart b/packages/flutter3d_game/test/run_timeline_extensions_test.dart index 9fb2059a8..67aab1fd9 100644 --- a/packages/flutter3d_game/test/run_timeline_extensions_test.dart +++ b/packages/flutter3d_game/test/run_timeline_extensions_test.dart @@ -241,7 +241,8 @@ void main() { }, timeout: const Timeout(Duration(seconds: 60))); test('a level sent from outside the process branches the run when it ' - 'moves a brush, and is refused when it changed on the way', () async { + 'moves a brush, is refused when it changed on the way, and the next ' + 'edit goes as a patch', () async { final target = await _startAndConnect(); addTearDown(() { target.service.dispose(); @@ -294,6 +295,38 @@ void main() { ), throwsA(isA()), ); + + // The next edit goes as a patch against the level the game now has; the + // same patch again is made against a level the game no longer has, and + // is answered with the code that tells the editor to send it whole. + final fogged = {...moved, 'fogDensity': 0.04}; + final patch = jsonEncode( + LevelPatch.between(Level.fromJson(moved), Level.fromJson(fogged)), + ); + final patched = _decode( + await target.service.callServiceExtension( + 'ext.flutter3d.level.patch', + isolateId: target.isolateId, + args: {'patch': patch}, + ), + ); + expect(patched['swappedAt'], isNull); + expect((patched['diff']! as Map)['fog'], isTrue); + + await expectLater( + target.service.callServiceExtension( + 'ext.flutter3d.level.patch', + isolateId: target.isolateId, + args: {'patch': patch}, + ), + throwsA( + isA().having( + (RPCError error) => error.code, + 'code', + LevelPatch.staleCode, + ), + ), + ); }, timeout: const Timeout(Duration(minutes: 3))); test( diff --git a/packages/flutter3d_sim/CHANGELOG.md b/packages/flutter3d_sim/CHANGELOG.md index 22a474160..fbfd0a3d2 100644 --- a/packages/flutter3d_sim/CHANGELOG.md +++ b/packages/flutter3d_sim/CHANGELOG.md @@ -1,5 +1,23 @@ ## Unreleased +- **A `.f3drun` carries the levels edited under the run.** + `Demo.levelSwaps` holds each one as a `DemoLevelSwap`: the step it took + effect before and the whole document, since the edited level exists in no + asset a replay could look up. On reading, the document is checked against + the hash written beside it, and swaps out of step order or past the end of + the tape are refused. A run with swaps is written as format 2, so an older + build refuses it instead of replaying it into a divergence; a run without + any is still written as 1. `f3drun_info` lists the swaps. +- **`DigestTrace.forgetAfter`** drops the checkpoints after a step, for a + run that was lived again from there. +- **`LevelPatch` carries an edit as the rows that changed.** + `LevelPatch.between` matches brushes, lights and entities by the digest + of each row (a deleted brush is one edit, not every row after it), takes + materials by name and every other key whole. `applyTo` refuses a level + other than the one the patch was made against, a row that is not the one + it names, and a result that is not the level it was meant to make; when + it applies, it says what changed without comparing the two documents. + `LevelPatch.staleCode` is the error a game answers a refused patch with. - **A number tuned while the game runs is on the tape.** `InputState.tune` sets a tunable for one step, `InputFrame.tunes` records it, playback applies it, and `Tunables` is the step's side: named values with defaults, diff --git a/packages/flutter3d_sim/bin/f3drun_info.dart b/packages/flutter3d_sim/bin/f3drun_info.dart index 52f5f4c81..1d895cc8a 100644 --- a/packages/flutter3d_sim/bin/f3drun_info.dart +++ b/packages/flutter3d_sim/bin/f3drun_info.dart @@ -66,4 +66,7 @@ void main(List args) { print('buildStamp: ${demo.buildStamp}'); print('platform: ${demo.platform ?? '(unknown)'}'); print('recordedBy: ${demo.recordedBy ?? '(anonymous)'}'); + for (final swap in demo.levelSwaps) { + print('levelSwap: step ${swap.step}, ${swap.levelHash}'); + } } diff --git a/packages/flutter3d_sim/lib/flutter3d_sim.dart b/packages/flutter3d_sim/lib/flutter3d_sim.dart index d73f3dfc7..c40acf84a 100644 --- a/packages/flutter3d_sim/lib/flutter3d_sim.dart +++ b/packages/flutter3d_sim/lib/flutter3d_sim.dart @@ -69,6 +69,7 @@ export 'src/level/level.dart'; export 'src/level/level_collision.dart'; export 'src/level/level_diff.dart'; export 'src/level/level_issue.dart'; +export 'src/level/level_patch.dart'; export 'src/level/level_validator.dart'; export 'src/level/level_visibility.dart'; export 'src/level/lightmap.dart'; diff --git a/packages/flutter3d_sim/lib/src/level/level_patch.dart b/packages/flutter3d_sim/lib/src/level/level_patch.dart new file mode 100644 index 000000000..e91a5a0f8 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/level/level_patch.dart @@ -0,0 +1,416 @@ +import '../save/state_digest.dart'; +import 'level.dart'; +import 'level_diff.dart'; + +/// The lists a level keeps its pieces in, which a [LevelPatch] edits row by +/// row rather than whole. +const List patchedLists = ['brushes', 'lights', 'entities']; + +/// One row of one of [patchedLists] put in, taken out or replaced. +/// +/// **A row is named by where it stands and what it was**, [at] and [was], +/// because the format gives a brush nothing else to be named by. Ids in the +/// document were the alternative and were refused: every level ever saved +/// would grow a key per row on its next save, recipes expand into brushes +/// that have none, and the editor's undo puts back whole documents, so ids +/// would have to be minted and kept stable across all of it. What a reference +/// has to guarantee is narrower than identity — that a patch never edits a +/// row other than the one it was made against — and the row's digest gives +/// exactly that: the receiving end finds the row at [at], digests it, and +/// refuses the patch when it is not the row [was] names. +final class RowEdit { + const RowEdit({required this.list, required this.at, this.was, this.row}); + + factory RowEdit.fromJson(Map json) => RowEdit( + list: json['list']! as String, + at: (json['at']! as num).toInt(), + was: json['was'] as String?, + row: json['row'] as Map?, + ); + + /// One of [patchedLists]. + final String list; + + /// Where the row stands in the list as it is when this edit is applied — + /// after the edits before it in the patch, not in the original. + final int at; + + /// The digest of the row taken out or replaced; null for a row put in. + final String? was; + + /// The row put in or replacing; null for a row taken out. + final Map? row; + + bool get inserts => was == null; + bool get removes => row == null; + + Map toJson() => { + 'list': list, + 'at': at, + 'was': ?was, + 'row': ?row, + }; +} + +/// What changed between two versions of one level, as edits a running game +/// can make to the version it has. +/// +/// **Made against a version, and applied only to that version.** [base] is +/// the digest of the level the patch was made from and [hash] the digest of +/// the level it makes; [applyTo] refuses a level that is not [base] and a +/// result that is not [hash]. A refusal costs one whole document — the sender +/// falls back to it — and a patch applied to the wrong level would cost a +/// level that nobody saved, so nothing between the two is guessed at. +/// +/// Three kinds of change, by how the document holds them: rows of +/// [patchedLists] ([rows]), materials by name ([materials]), and every other +/// top-level key whole ([fields]) — fog, music, the ground, recipes, `next`, +/// and keys this format does not know. A list or the material table that one +/// side has and the other does not goes whole as a field too: the patch then +/// says what the document says rather than inventing an empty one. +final class LevelPatch { + const LevelPatch({ + required this.base, + required this.hash, + this.rows = const [], + this.materials = const {}, + this.fields = const {}, + }); + + factory LevelPatch.fromJson(Map json) => LevelPatch( + base: json['base']! as String, + hash: json['hash']! as String, + rows: [ + for (final row in json['rows'] as List? ?? const []) + RowEdit.fromJson(row! as Map), + ], + materials: + json['materials'] as Map? ?? const {}, + fields: + json['fields'] as Map? ?? const {}, + ); + + /// The edits that make [after] out of [before]. + /// + /// Rows are matched by digest: a common head and tail are trimmed, and what + /// is left between them is aligned by its longest common run, so a brush + /// deleted from the middle of a thousand is one edit rather than the rest of + /// the list shifted by one. Unmatched rows facing each other are paired as + /// replacements, which is what moving or recolouring one thing looks like. + /// A middle too long to align (see [_alignLimit]) is replaced row for row — + /// a bigger patch, never a wrong one. + factory LevelPatch.between(Level before, Level after) { + final old = before.toJson(); + final now = after.toJson(); + final rows = []; + final fields = {}; + for (final key in {...old.keys, ...now.keys}) { + final (a, b) = (old[key], now[key]); + if (_digest(a) == _digest(b)) continue; + if (patchedLists.contains(key) && a is List && b is List) { + rows.addAll(_rowEdits(key, a, b)); + } else if (key == 'materials' && a is Map && b is Map) { + // Handled below, by name. + } else { + fields[key] = b; + } + } + final (oldMaterials, newMaterials) = (old['materials'], now['materials']); + return LevelPatch( + base: before.digestHex, + hash: after.digestHex, + rows: rows, + materials: oldMaterials is Map && newMaterials is Map + ? { + for (final name in { + ...oldMaterials.keys, + ...newMaterials.keys, + }) + if (_digest(oldMaterials[name]) != _digest(newMaterials[name])) + name! as String: newMaterials[name], + } + : const {}, + fields: fields, + ); + } + + /// The service extension error code a game answers a patch with when it + /// does not apply — made against another version, or making one nobody + /// saved. The sender's cue to send the whole document instead; any other + /// error means the level itself was refused, and sending it whole would be + /// refused the same way. + /// + /// Inside the range `dart:developer` leaves to extensions + /// (`ServiceExtensionResponse.extensionErrorMin` to `extensionErrorMax`), + /// spelled here because this package does not import the VM's. + static const int staleCode = -32001; + + /// How many row pairs [LevelPatch.between] will align by longest common + /// run, the cost of which grows as their product. A thousand by a thousand + /// is four megabytes and a few milliseconds; past it, rows are replaced. + static const int _alignLimit = 1000 * 1000; + + /// The digest of the level this patch was made from. + final String base; + + /// The digest of the level it makes. + final String hash; + + /// Row edits, in the order they are applied. + final List rows; + + /// Materials changed or added, by name; null for one taken out. + final Map materials; + + /// Other top-level keys, whole; null for one taken out. + final Map fields; + + /// Whether the patch changes nothing. + bool get isEmpty => rows.isEmpty && materials.isEmpty && fields.isEmpty; + + Map toJson() => { + 'base': base, + 'hash': hash, + if (rows.isNotEmpty) 'rows': [for (final r in rows) r.toJson()], + if (materials.isNotEmpty) 'materials': materials, + if (fields.isNotEmpty) 'fields': fields, + }; + + /// [level] with this patch made to it, and what that changed — or why not. + /// + /// **The diff comes from the patch, not from comparing the two levels.** + /// The edits already say which lamp changed and whether any brush did, so + /// the running game is told that without both documents being digested part + /// by part. The exception is a list or the material table sent whole, where + /// the patch knows that the part changed and not where: that is compared + /// the way [diffLevel] compares documents. + LevelPatchResult applyTo(Level level) { + final had = level.digestHex; + if (had != base) { + return LevelPatchRefused( + 'the patch was made against level $base and the game has $had', + ); + } + final document = level.toJson(); + for (final MapEntry(:key, :value) in fields.entries) { + if (value == null) { + document.remove(key); + } else { + document[key] = value; + } + } + if (materials.isNotEmpty) { + final table = { + ...document['materials'] as Map? ?? + const {}, + }; + for (final MapEntry(:key, :value) in materials.entries) { + if (value == null) { + table.remove(key); + } else { + table[key] = value; + } + } + document['materials'] = table; + } + // Copies, because a list the level never changed is handed back by + // `toJson` as the very list it was read from. + final lists = >{ + for (final list in {for (final edit in rows) edit.list}) + list: [...document[list] as List? ?? const []], + }; + for (final edit in rows) { + final target = lists[edit.list]!; + if (edit.inserts && edit.removes) { + return LevelPatchRefused( + '${edit.list}[${edit.at}] is neither put in nor taken out', + ); + } + final limit = edit.inserts ? target.length : target.length - 1; + if (edit.at < 0 || edit.at > limit) { + return LevelPatchRefused( + '${edit.list}[${edit.at}] is past the end of the game\'s ' + '${target.length}', + ); + } + if (edit.was case final String was) { + final found = _digest(target[edit.at]); + if (found != was) { + return LevelPatchRefused( + '${edit.list}[${edit.at}] is $found in the game, not $was', + ); + } + } + switch ((edit.was, edit.row)) { + case (null, final Map row): + target.insert(edit.at, row); + case (_, null): + target.removeAt(edit.at); + case (_, final Map row): + target[edit.at] = row; + } + } + document.addAll(lists); + + final Level next; + try { + next = Level.fromJson(document); + } on Object catch (error) { + return LevelPatchRefused('the patched document is not a level: $error'); + } + if (next.digestHex != hash) { + return LevelPatchRefused( + 'the patched level digests to ${next.digestHex}, not $hash', + ); + } + final whole = fields.keys.any( + (String key) => patchedLists.contains(key) || key == 'materials', + ); + return LevelPatched(next, whole ? diffLevel(level, next) : _diff()); + } + + /// What the edits change, read off the edits themselves. + LevelDiff _diff() { + Iterable of(String list) => + rows.where((RowEdit edit) => edit.list == list); + final lights = of('lights'); + final lightCountChanged = lights.any( + (RowEdit edit) => edit.inserts || edit.removes, + ); + return LevelDiff( + // With no row put in or taken out, a row's place in the patch is its + // place in both levels. + lights: lightCountChanged + ? const [] + : [for (final edit in lights) edit.at], + lightCountChanged: lightCountChanged, + materials: materials.keys.toList(), + fog: fields.containsKey('fogColor') || fields.containsKey('fogDensity'), + music: fields.containsKey('music'), + simulation: [ + if (of('brushes').isNotEmpty) 'brushes', + if (of('entities').isNotEmpty) 'entities', + for (final key in const ['heightfield', 'recipes', 'next']) + if (fields.containsKey(key)) key, + ], + ); + } + + static String _digest(Object? json) => + contentDigestHex({'v': json}); + + /// Edits that turn the rows [a] of [list] into [b], applied in order. + static List _rowEdits( + String list, + List a, + List b, + ) { + final oldDigests = [for (final row in a) _digest(row)]; + final newDigests = [for (final row in b) _digest(row)]; + final shorter = a.length < b.length ? a.length : b.length; + final head = Iterable.generate( + shorter, + ).takeWhile((int i) => oldDigests[i] == newDigests[i]).length; + final tail = Iterable.generate(shorter - head) + .takeWhile( + (int i) => + oldDigests[a.length - 1 - i] == newDigests[b.length - 1 - i], + ) + .length; + final from = oldDigests.sublist(head, a.length - tail); + final to = newDigests.sublist(head, b.length - tail); + + final edits = []; + var at = head; + var (i, j) = (0, 0); + // Each anchor is a row kept; the sentinel at the end flushes what is left. + for (final (ai, bj) in <(int, int)>[ + ..._anchors(from, to), + (from.length, to.length), + ]) { + final removed = ai - i; + final inserted = bj - j; + final paired = removed < inserted ? removed : inserted; + for (var k = 0; k < paired; k++) { + edits.add( + RowEdit( + list: list, + at: at++, + was: from[i + k], + row: b[head + j + k]! as Map, + ), + ); + } + for (var k = paired; k < removed; k++) { + edits.add(RowEdit(list: list, at: at, was: from[i + k])); + } + for (var k = paired; k < inserted; k++) { + edits.add( + RowEdit( + list: list, + at: at++, + row: b[head + j + k]! as Map, + ), + ); + } + at++; + (i, j) = (ai + 1, bj + 1); + } + return edits; + } + + /// The pairs of positions a longest common run of [a] and [b] keeps, in + /// order. Empty when the two are too long to align (see [_alignLimit]). + static List<(int, int)> _anchors(List a, List b) { + if (a.isEmpty || b.isEmpty || a.length * b.length > _alignLimit) { + return const <(int, int)>[]; + } + final width = b.length + 1; + // `runs[i * width + j]`: the longest common run of a[i..] and b[j..]. + final runs = List.filled((a.length + 1) * width, 0); + for (var i = a.length - 1; i >= 0; i--) { + for (var j = b.length - 1; j >= 0; j--) { + runs[i * width + j] = a[i] == b[j] + ? runs[(i + 1) * width + j + 1] + 1 + : _max(runs[(i + 1) * width + j], runs[i * width + j + 1]); + } + } + final anchors = <(int, int)>[]; + var (i, j) = (0, 0); + while (i < a.length && j < b.length) { + if (a[i] == b[j]) { + anchors.add((i++, j++)); + } else if (runs[(i + 1) * width + j] >= runs[i * width + j + 1]) { + i++; + } else { + j++; + } + } + return anchors; + } + + static int _max(int a, int b) => a > b ? a : b; +} + +/// What [LevelPatch.applyTo] made of a level. +sealed class LevelPatchResult { + const LevelPatchResult(); +} + +/// The patch applied: [level] is the version it makes, and [diff] what that +/// changed for the running game. +final class LevelPatched extends LevelPatchResult { + const LevelPatched(this.level, this.diff); + + final Level level; + final LevelDiff diff; +} + +/// The patch does not apply, and [reason] says where it stopped: a level +/// that is not the one it was made against, a row that is not the one it +/// names, or a result that is not the level it was meant to make. +final class LevelPatchRefused extends LevelPatchResult { + const LevelPatchRefused(this.reason); + + final String reason; +} diff --git a/packages/flutter3d_sim/lib/src/save/demo.dart b/packages/flutter3d_sim/lib/src/save/demo.dart index a2e5f5f55..bad54861a 100644 --- a/packages/flutter3d_sim/lib/src/save/demo.dart +++ b/packages/flutter3d_sim/lib/src/save/demo.dart @@ -1,4 +1,5 @@ import '../input/input_tape.dart'; +import '../level/level.dart'; import 'data_source_trace.dart'; import 'snapshot.dart'; import 'state_digest.dart'; @@ -50,6 +51,13 @@ import 'state_digest.dart'; /// silently degrades to a video. [platform] and [recordedBy] are the /// genuinely optional half: worth keeping when known, meaningless to invent /// when not. +/// +/// ## `HR3`: a level edited under the run +/// +/// [levelSwaps] are the edits the editor sent into the game while this run +/// was recorded, each with the whole document and the step it took effect +/// before. Without them a run with an edit in it parts from its own replay at +/// the edit, and the checkpoints say so without saying why. final class Demo { const Demo({ required this.level, @@ -61,10 +69,18 @@ final class Demo { this.platform, this.recordedBy, this.dataSources, + this.levelSwaps = const [], }); /// Bumped when an existing field changes meaning. - static const int formatVersion = 1; + /// + /// **2 is written only by a run with [levelSwaps] in it.** A build that + /// reads 1 would play such a file through the first level to the end and + /// report a divergence nobody can explain; refusing it with "a newer build" + /// is the honest answer. A run with no edit in it means exactly what it + /// meant before, so it is still written as 1 and older builds still open + /// it. + static const int formatVersion = 2; /// The extension a run is written under — `.f3drun`, wherever it becomes an /// actual file: attached to a bug report, downloaded from the cloud, or @@ -119,11 +135,19 @@ final class Demo { /// with no recorded bindings, not a run misread. final DataSourceTrace? dataSources; + /// The levels put under the run while it was recorded, in step order, no + /// two at the same step. Empty for a run played in one level throughout. + /// + /// [level] and [levelHash] still name the level the run started in: that + /// is the one a replay loads, and these are swapped in as the tape reaches + /// them. + final List levelSwaps; + /// How many fixed steps the run lasted. int get steps => tape.steps; Map toJson() => { - 'version': formatVersion, + 'version': levelSwaps.isEmpty ? 1 : formatVersion, 'level': level, 'levelHash': levelHash, 'run': start.toJson(), @@ -133,6 +157,10 @@ final class Demo { if (platform != null) 'platform': platform, if (recordedBy != null) 'recordedBy': recordedBy, if (dataSources != null) 'dataSources': dataSources!.toJson(), + if (levelSwaps.isNotEmpty) + 'levelSwaps': >[ + for (final swap in levelSwaps) swap.toJson(), + ], }; /// Reads a demo, or throws a [DemoFormatException] that says why not. @@ -206,18 +234,102 @@ final class Demo { throw DemoFormatException('the data sources: ${error.message}'); } } + final readTape = InputTape.fromJson(tape); return Demo( level: level, levelHash: levelHash, start: start, - tape: InputTape.fromJson(tape), + tape: readTape, buildStamp: buildStamp, checkpoints: trace, platform: platform is String ? platform : null, recordedBy: recordedBy is String ? recordedBy : null, dataSources: dataSources, + levelSwaps: _readSwaps(json['levelSwaps'], steps: readTape.steps), ); } + + static List _readSwaps(Object? raw, {required int steps}) { + if (raw == null) return const []; + if (raw is! List) { + throw const DemoFormatException('the level swaps are not a list'); + } + final swaps = [ + for (final entry in raw) DemoLevelSwap._fromJson(entry, steps: steps), + ]; + for (var i = 1; i < swaps.length; i++) { + if (swaps[i].step <= swaps[i - 1].step) { + throw DemoFormatException( + 'the level swap at step ${swaps[i].step} comes after the one at ' + 'step ${swaps[i - 1].step}; swaps are written in step order, one ' + 'per step', + ); + } + } + return List.unmodifiable(swaps); + } +} + +/// One level put under a recorded run: [level] took effect before step +/// [step] of the tape, that is, after [step] of its frames had been played. +/// +/// **The whole document, not only its digest.** The level a game was edited +/// into exists in the editor that sent it and nowhere a replay can look it +/// up — not in the game's assets, which still hold the level as shipped. A +/// digest alone would make the file unplayable everywhere but the machine +/// that recorded it, on the afternoon it was recorded. +final class DemoLevelSwap { + const DemoLevelSwap({required this.step, required this.level}); + + final int step; + final Level level; + + /// [Level.digestHex] of [level], what the editor and the timeline call it. + String get levelHash => level.digestHex; + + Map toJson() => { + 'step': step, + 'levelHash': levelHash, + 'document': level.toJson(), + }; + + /// **The digest is checked against the document**, as + /// `ext.flutter3d.level.apply` checks it: a document that no longer + /// digests to what was written beside it is not the level the run was + /// played in. + factory DemoLevelSwap._fromJson(Object? json, {required int steps}) { + if (json is! Map) { + throw const DemoFormatException('a level swap is not a document'); + } + final step = json['step']; + if (step is! int || step < 0 || step > steps) { + throw DemoFormatException( + 'a level swap names step $step, and the tape has steps 0 to $steps', + ); + } + final hash = json['levelHash']; + final document = json['document']; + if (hash is! String || document is! Map) { + throw DemoFormatException( + 'the level swap at step $step has no level hash or no document', + ); + } + final Level level; + try { + level = Level.fromJson(document); + } on LevelFormatException catch (error) { + throw DemoFormatException( + 'the level swapped in at step $step: ${error.message}', + ); + } + if (level.digestHex != hash) { + throw DemoFormatException( + 'the level swapped in at step $step digests to ${level.digestHex}, ' + 'not $hash; the document changed after it was written', + ); + } + return DemoLevelSwap(step: step, level: level); + } } /// Thrown when a demo cannot be read at all. diff --git a/packages/flutter3d_sim/lib/src/save/state_digest.dart b/packages/flutter3d_sim/lib/src/save/state_digest.dart index c911642a2..862407041 100644 --- a/packages/flutter3d_sim/lib/src/save/state_digest.dart +++ b/packages/flutter3d_sim/lib/src/save/state_digest.dart @@ -408,6 +408,20 @@ final class DigestTrace { _digests.add(StateDigest.of(state)); } + /// Drops the checkpoints taken after [step]. + /// + /// For a run that was lived again from [step] — a level swapped under it: + /// what was observed after [step] is a run that no longer happened, and a + /// replay checked against it would be checked against the wrong world. The + /// steps between [step] and the next [observe] stay unchecked, which is a + /// gap and not a false answer. + void forgetAfter(int step) { + final keep = _steps.indexWhere((s) => s > step); + if (keep < 0) return; + _steps.removeRange(keep, _steps.length); + _digests.removeRange(keep, _digests.length); + } + /// The first checkpoint at which this trace and [expected] disagree. /// /// Null when they agree the whole way, which is the answer the parity test diff --git a/packages/flutter3d_sim/test/demo_test.dart b/packages/flutter3d_sim/test/demo_test.dart index 2d15d2fe9..043415c7d 100644 --- a/packages/flutter3d_sim/test/demo_test.dart +++ b/packages/flutter3d_sim/test/demo_test.dart @@ -173,6 +173,113 @@ void main() { }); }); + group('HR3: a level swapped under the run', () { + Demo swapped(List swaps) { + final plain = _demo(steps: 6); + return Demo( + level: plain.level, + levelHash: plain.levelHash, + start: plain.start, + tape: plain.tape, + buildStamp: plain.buildStamp, + checkpoints: plain.checkpoints, + levelSwaps: swaps, + ); + } + + test('carries the document and its step through JSON', () { + final edit = Level(name: 'crypt', fogDensity: 0.02); + final json = + jsonDecode( + jsonEncode( + swapped([ + DemoLevelSwap(step: 4, level: edit), + ]).toJson(), + ), + ) + as Map; + + final read = Demo.fromJson(json); + + expect(read.levelSwaps.single.step, 4); + expect(read.levelSwaps.single.levelHash, edit.digestHex); + expect(read.levelSwaps.single.level.fogDensity, 0.02); + // Mutation: write 1 whatever the run holds — a build that cannot + // replay the swap opens the file and plays through it to a divergence. + expect(json['version'], 2); + }); + + test('a run with no swap is still written as format 1', () { + // Mutation: always write `formatVersion` — every demo this build + // records becomes unreadable to the build before it, for nothing. + expect(_demo().toJson()['version'], 1); + expect(_demo().toJson().containsKey('levelSwaps'), isFalse); + }); + + test('refuses a document that no longer digests to its hash', () { + final json = swapped([ + DemoLevelSwap(step: 2, level: Level(name: 'crypt')), + ]).toJson(); + final swap = (json['levelSwaps']! as List>).single; + (swap['document']! as Map)['fogDensity'] = 0.5; + + // Mutation: trust the hash — the replay swaps in a level nobody played. + expect( + () => + Demo.fromJson(jsonDecode(jsonEncode(json)) as Map), + throwsA( + isA().having( + (e) => e.message, + 'message', + contains('digests to'), + ), + ), + ); + }); + + test('refuses swaps out of order, or past the end of the tape', () { + final level = Level(name: 'crypt'); + for (final swaps in >[ + [ + DemoLevelSwap(step: 4, level: level), + DemoLevelSwap(step: 2, level: level), + ], + [ + DemoLevelSwap(step: 3, level: level), + DemoLevelSwap(step: 3, level: level), + ], + [DemoLevelSwap(step: 7, level: level)], + ]) { + // Mutation: drop the order check — a replay walking the list once + // would skip the swap that came second and play the wrong level. + expect( + () => Demo.fromJson( + jsonDecode(jsonEncode(swapped(swaps).toJson())) + as Map, + ), + throwsA(isA()), + reason: 'steps ${swaps.map((s) => s.step).toList()}', + ); + } + }); + + test('a trace forgets what was observed after a step, and keeps the ' + 'rest', () { + final trace = DigestTrace(every: 2); + for (var step = 1; step <= 8; step++) { + trace.observe(step, {'step': step}); + } + + trace.forgetAfter(5); + + // Mutation: forget from the step on — the checkpoint at the swap's own + // step, taken under the old level and still true, goes with it. + expect(trace.steps, [2, 4]); + trace.forgetAfter(4); + expect(trace.steps, [2, 4]); + }); + }); + group('the loop', () { test('records one entry per step, with the step\'s look in it', () { // The moment is the whole point: after the look for the step is added diff --git a/packages/flutter3d_sim/test/f3drun_info_test.dart b/packages/flutter3d_sim/test/f3drun_info_test.dart index bd27b09ee..ac6da372e 100644 --- a/packages/flutter3d_sim/test/f3drun_info_test.dart +++ b/packages/flutter3d_sim/test/f3drun_info_test.dart @@ -60,6 +60,42 @@ void main() { expect(out, contains('buildStamp: test-build-42')); expect(out, contains('platform: macos')); expect(out, contains('recordedBy: dmitrii')); + expect(out, isNot(contains('levelSwap'))); + }); + + test('a level swapped under the run is listed with its step', () { + final dir = Directory.systemTemp.createTempSync('f3drun_info_test'); + addTearDown(() => dir.deleteSync(recursive: true)); + final plain = _demo(); + final edit = Level(name: 'crypt', fogDensity: 0.02); + final file = File('${dir.path}/run${Demo.fileExtension}') + ..writeAsStringSync( + jsonEncode( + Demo( + level: plain.level, + levelHash: plain.levelHash, + start: plain.start, + tape: plain.tape, + buildStamp: plain.buildStamp, + checkpoints: plain.checkpoints, + levelSwaps: [DemoLevelSwap(step: 1, level: edit)], + ).toJson(), + ), + ); + + final result = Process.runSync('dart', [ + 'run', + 'bin/f3drun_info.dart', + file.path, + ], workingDirectory: Directory.current.path); + + // Mutation: leave the swaps out of the listing — a file that replays + // only in the game that recorded it looks like any other run. + expect(result.exitCode, 0, reason: 'stderr was: ${result.stderr}'); + expect( + result.stdout as String, + contains('levelSwap: step 1, ${edit.digestHex}'), + ); }); test('a demo from a newer format is refused with a suggestion, not a ' diff --git a/packages/flutter3d_sim/test/level_patch_test.dart b/packages/flutter3d_sim/test/level_patch_test.dart new file mode 100644 index 000000000..db7e02bdf --- /dev/null +++ b/packages/flutter3d_sim/test/level_patch_test.dart @@ -0,0 +1,246 @@ +/// `LevelPatch` carries an edit to a running game as the rows that changed, +/// and refuses to be applied to any level but the one it was made against. +/// +/// dart test test/level_patch_test.dart +/// +/// Mutation: drop the `was` check in `applyTo` and a patch made against one +/// arrangement of brushes edits whichever brush now stands at that index; +/// align by position instead of by digest and a brush deleted from the +/// middle becomes a replacement of every row after it. +library; + +import 'dart:convert'; +import 'dart:math' as math; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; + +Map _brush(double x, {String material = 'stone'}) => + { + 'at': [x, 0, 0], + 'size': [1, 1, 1], + 'material': material, + }; + +Map _document() => { + 'version': 1, + 'name': 'yard', + 'fogDensity': 0.02, + 'materials': { + 'stone': { + 'color': [0.5, 0.5, 0.5, 1.0], + }, + }, + 'brushes': [for (var i = 0; i < 10; i++) _brush(i * 2.0)], + 'lights': [ + { + 'type': 'point', + 'at': [0, 3, 0], + }, + { + 'type': 'point', + 'at': [6, 3, 0], + }, + ], + 'entities': [ + { + 'type': 'crate', + 'at': [2, 0, 2], + }, + ], +}; + +Level _edited(void Function(Map json) edit) { + final json = jsonDecode(jsonEncode(_document())) as Map; + edit(json); + return Level.fromJson(json); +} + +List _list(Map json, String key) => + json[key]! as List; + +/// The level [patch] makes of [level], or a failure naming the refusal. +Level _applied(LevelPatch patch, Level level) => switch (patch.applyTo(level)) { + LevelPatched(:final level) => level, + LevelPatchRefused(:final reason) => fail('refused: $reason'), +}; + +void main() { + final before = Level.fromJson(_document()); + + test('a moved brush is one row, and the run is told it was a brush', () { + final after = _edited((json) => _list(json, 'brushes')[4] = _brush(8.5)); + + final patch = LevelPatch.between(before, after); + + expect(patch.rows, hasLength(1)); + expect(patch.rows.single.at, 4); + expect(patch.materials, isEmpty); + expect(patch.fields, isEmpty); + final result = patch.applyTo(before) as LevelPatched; + expect(result.level.digestHex, after.digestHex); + expect(result.diff.simulation, ['brushes']); + }); + + test('a brush taken from the middle is one edit, not the rest shifted', () { + final after = _edited((json) => _list(json, 'brushes').removeAt(3)); + + final patch = LevelPatch.between(before, after); + + expect(patch.rows, hasLength(1)); + expect(patch.rows.single.removes, isTrue); + expect(_applied(patch, before).digestHex, after.digestHex); + }); + + test('a lamp recoloured is that lamp; one added is a count change', () { + final recoloured = _edited( + (json) => (_list(json, 'lights')[1]! as Map)['color'] = + [1, 0.5, 0.2], + ); + final one = LevelPatch.between(before, recoloured).applyTo(before); + expect((one as LevelPatched).diff.lights, [1]); + expect(one.diff.lightCountChanged, isFalse); + expect(one.diff.presentationOnly, isTrue); + + final added = _edited( + (json) => _list(json, 'lights').insert(1, { + 'type': 'point', + 'at': [3, 3, 0], + }), + ); + final two = LevelPatch.between(before, added).applyTo(before); + expect((two as LevelPatched).diff.lightCountChanged, isTrue); + expect(two.diff.presentationOnly, isTrue); + }); + + test('materials go by name and the fog as a field', () { + final after = _edited((json) { + (json['materials']! as Map)['moss'] = { + 'color': [0.1, 0.4, 0.1, 1.0], + }; + json['fogDensity'] = 0.05; + }); + + final patch = LevelPatch.between(before, after); + + expect(patch.rows, isEmpty); + expect(patch.materials.keys, ['moss']); + expect(patch.fields.keys, ['fogDensity']); + final result = patch.applyTo(before) as LevelPatched; + expect(result.diff.materials, ['moss']); + expect(result.diff.fog, isTrue); + expect(result.diff.presentationOnly, isTrue); + }); + + test('a list one side lacks goes whole, and is compared as a document', () { + final bare = Level.fromJson( + {..._document()}..remove('lights'), + ); + + final patch = LevelPatch.between(bare, before); + + expect(patch.fields.keys, ['lights']); + final result = patch.applyTo(bare) as LevelPatched; + expect(result.level.digestHex, before.digestHex); + expect(result.diff.lightCountChanged, isTrue); + }); + + test('refuses a level it was not made against, naming both', () { + final after = _edited((json) => json['fogDensity'] = 0.05); + final other = _edited((json) => _list(json, 'brushes').removeAt(0)); + + final result = LevelPatch.between(before, after).applyTo(other); + + expect(result, isA()); + expect( + (result as LevelPatchRefused).reason, + allOf(contains(before.digestHex), contains(other.digestHex)), + ); + }); + + test('refuses a row that is not the one it names', () { + final after = _edited((json) => _list(json, 'brushes')[4] = _brush(8.5)); + final patch = LevelPatch.between(before, after); + final pointedElsewhere = LevelPatch( + base: patch.base, + hash: patch.hash, + rows: [ + RowEdit( + list: 'brushes', + at: 5, + was: patch.rows.single.was, + row: patch.rows.single.row, + ), + ], + ); + + final result = pointedElsewhere.applyTo(before); + + expect((result as LevelPatchRefused).reason, contains('brushes[5]')); + }); + + test('crosses the wire as JSON and still applies', () { + final after = _edited((json) { + _list(json, 'brushes') + ..removeAt(7) + ..insert(2, _brush(-3, material: 'moss')); + _list(json, 'entities').add({'type': 'crate'}); + json['music'] = 'rain.ogg'; + }); + + final sent = LevelPatch.fromJson( + jsonDecode(jsonEncode(LevelPatch.between(before, after).toJson())) + as Map, + ); + + final result = sent.applyTo(before) as LevelPatched; + expect(result.level.digestHex, after.digestHex); + expect(result.diff.simulation, ['brushes', 'entities']); + expect(result.diff.music, isTrue); + }); + + test('any shuffle of rows patches to exactly the edited level', () { + // Removals, insertions, moves and duplicates at random, against the + // whole-document digest: the claim is that the alignment never produces + // a patch that makes a different level. + final random = math.Random(7); + for (var round = 0; round < 200; round++) { + final after = _edited((json) { + final brushes = _list(json, 'brushes'); + for (var n = random.nextInt(6); n > 0; n--) { + switch (random.nextInt(4)) { + case 0 when brushes.isNotEmpty: + brushes.removeAt(random.nextInt(brushes.length)); + case 1: + brushes.insert( + random.nextInt(brushes.length + 1), + _brush(random.nextInt(40) / 2), + ); + case 2 when brushes.isNotEmpty: + brushes[random.nextInt(brushes.length)] = _brush(-1.0 - round); + case _ when brushes.isNotEmpty: + brushes.add(brushes[random.nextInt(brushes.length)]); + } + } + }); + final patch = LevelPatch.between(before, after); + expect( + _applied(patch, before).digestHex, + after.digestHex, + reason: 'round $round', + ); + expect( + patch.rows.length, + lessThanOrEqualTo(10 + after.brushes.length), + reason: 'never more edits than rows on both sides', + ); + } + }); + + test('the same level is an empty patch', () { + expect( + LevelPatch.between(before, Level.fromJson(_document())).isEmpty, + isTrue, + ); + }); +} diff --git a/packages/flutter3d_sim_mcp/CHANGELOG.md b/packages/flutter3d_sim_mcp/CHANGELOG.md index 4764d0af7..e8096969a 100644 --- a/packages/flutter3d_sim_mcp/CHANGELOG.md +++ b/packages/flutter3d_sim_mcp/CHANGELOG.md @@ -1,3 +1,10 @@ +## Unreleased + +- **`verify` refuses a run with the level edited under it**, naming the + step. The swapped level is built by the game that recorded the run, and a + session over a bare simulation would answer with a divergence it did not + cause. + ## 0.8.0 **An agent's claim about a run comes with the run that proves it.** The diff --git a/packages/flutter3d_sim_mcp/lib/src/sim_session.dart b/packages/flutter3d_sim_mcp/lib/src/sim_session.dart index 73e6990ad..9d2a1520d 100644 --- a/packages/flutter3d_sim_mcp/lib/src/sim_session.dart +++ b/packages/flutter3d_sim_mcp/lib/src/sim_session.dart @@ -208,6 +208,16 @@ final class SimSession { } catch (error) { return _refuse('could not read a run from "$path": $error'); } + // A swapped level goes in through the game's own build, which a session + // over a bare simulation does not have; playing through the swap would + // answer with a divergence the simulation did not cause. + if (demo.levelSwaps case [final first, ...]) { + return _refuse( + '"$path" has the level edited under it at step ${first.step}, and ' + 'this replays a run in one level — play it in the game it was ' + 'recorded in', + ); + } final ReadingPredicate? claim; try { claim = predicate == null ? null : ReadingPredicate.fromJson(predicate); diff --git a/packages/flutter3d_sim_mcp/test/claims_test.dart b/packages/flutter3d_sim_mcp/test/claims_test.dart index dafda1606..65070fe82 100644 --- a/packages/flutter3d_sim_mcp/test/claims_test.dart +++ b/packages/flutter3d_sim_mcp/test/claims_test.dart @@ -284,6 +284,39 @@ void main() { expect(replayed.says, contains('has changed since the run was recorded')); }); + test('a run with the level edited under it is refused, naming the ' + 'step', () { + final session = _session()..open(_crypt); + final path = '${workspace.path}/edited.f3drun'; + session + ..step(steps: 10, moveY: 1.0) + ..writeRun(path); + final demo = Demo.fromJson( + jsonDecode(File(path).readAsStringSync()) as Map, + ); + File(path).writeAsStringSync( + jsonEncode( + Demo( + level: demo.level, + levelHash: demo.levelHash, + start: demo.start, + tape: demo.tape, + buildStamp: demo.buildStamp, + checkpoints: demo.checkpoints, + levelSwaps: [ + DemoLevelSwap(step: 4, level: Level(name: 'crypt')), + ], + ).toJson(), + ), + ); + + // Mutation: drop the refusal — the session plays through the edit in + // the shipped crypt and reports the run as diverging. + final replayed = session.verify(path); + expect(replayed.did, isFalse); + expect(replayed.says, contains('edited under it at step 4')); + }); + test('verify leaves the session\'s own run where it was', () { final session = _session()..open(_crypt); final path = '${workspace.path}/aside.f3drun'; diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 5fb4d01df..c1d3dc60f 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10965 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 10994 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index eb47b75e8..e2fc7187f 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,17 +1,17 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10965 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10994 tests need a GPU. --- # Testing -10965 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10994 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| | `flutter3d` | 1637 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | -| `flutter3d_sim` | 591 | | `pad_input` | 67 | +| `flutter3d_sim` | 607 | | `pad_input` | 67 | | `flutter3d_lab` | 15 | | `flutter3d_core` | 750 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | @@ -19,7 +19,7 @@ description: Four independent golden sets, mutation-checking every new test, det | | | | `flutter3d_audio` | 4 | | `flutter3d_game_racing` | 223 | | `flutter3d_webgl` | 62 | | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 70 | -| `apps/flutter3d_demo_platformer` | 202 | | `flutter3d_impeller` | 64 | +| `apps/flutter3d_demo_platformer` | 204 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 357 | | `apps/flutter3d_demo_strategy` | 50 | | `apps/flutter3d_editor` | 224 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | @@ -28,19 +28,19 @@ description: Four independent golden sets, mutation-checking every new test, det | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 140 | -| `flutter3d_game` | 279 | | `flutter3d_shaders` | 5 | +| `flutter3d_game` | 287 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | -| `flutter3d_editor_play` | 12 | | `flutter3d_net` | 15 | +| `flutter3d_editor_play` | 14 | | `flutter3d_net` | 15 | | | | | `flutter3d_net_webrtc` | 2 | -| | | | `flutter3d_sim_mcp` | 25 | +| | | | `flutter3d_sim_mcp` | 26 | | `flutter3d_mcp_kit` | 2 | | `flutter3d_build` | 136 | | | | | `apps/flutter3d_lesson_viewer` | 48 | | | | | `apps/flutter3d_stereo_lesson_viewer` | 6 | | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10939 rather than 10965: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10968 rather than 10994: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 1604a046b..24a63c811 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -186,10 +186,53 @@ edited document ahead (textures, meshes, a simulation of its own, through replay with the run carried over, and the widget hears of it once the run is back at now. A document for another level, or one that does not build, is refused and the level on screen stays. -*Left:* patches instead of whole documents; the swap written into a `Demo` -segment so a saved `.f3drun` replays it (the history has it, the file format -does not yet; the platformer starts a new demo at the swap instead); the -editor MCP driving the same path. +A saved `.f3drun` replays the swap. `Demo.levelSwaps` holds each edit as the +step it took effect before and the whole document, checked against its hash +on reading; a file with swaps is format 2, so an older build refuses it, and +one without is still 1. `DemoRecording` is the run being written down, and +`LiveLevel(swapped:)` hands it the timeline's step, which `levelSwapped` +turns into a step on its own tape, dropping a swap and the checkpoints the +new one lived again. `replayDemo` plays a file with the swaps in place and +compares checkpoints by step. The platformer writes edits into the demo it +is recording instead of starting another (`PlatformerRun.onLevelEdited`); +an edit that took effect before the demo began, within a keyframe of the +load, starts the demo again from now with the edit at step 0. +`PlatformerRun.replay` builds every edited level before the first step and +swaps them in through `replaceLevel`, as live. +*Tests:* in the platformer, a run edited at step 150 (swapped at the +keyframe at 120), saved through JSON and replayed in a game of its own, +arrives at the live run's bytes and keeps to every checkpoint, and the same +file with the swap removed ends elsewhere (`demo_level_swap_test.dart`); the +recording's step arithmetic, a second edit within a keyframe replacing the +first, and the refusals on a toy (`demo_recording_test.dart`); the format's +round trip and refusals (`flutter3d_sim/test/demo_test.dart`). +A save after a save goes as a patch. `LevelPatch.between` names a row of +brushes, lights or entities by its place and the digest of what it was +(ids in the format were refused: every document would grow a key per row, +recipes expand without them, and the editor's whole-document undo would +have to keep them stable); rows are aligned by digest, so a brush deleted +from the middle is one edit. Materials go by name, every other key whole. +`ext.flutter3d.level.patch` applies it only to the level it was made +against and only when the result digests to what the editor saved, and +hands `LiveLevel` the patch's own diff; a refusal comes back as +`LevelPatch.staleCode`, and `pushLevel(base:)` then sends the whole +document and says why in the status strip. The editor's base is the +document it last read or wrote; a game started from another version costs +one refusal. +*Tests:* a random shuffle of rows always patches to the edited level; a +row that is not the one named, and a level that is not the base, are +refused; a stale patch is told apart from a level that does not build; the +patch and the stale code over a real VM service. +*Left:* scrubbing across a swap in a loaded `.f3drun` (`rewindBufferFromDemo` +refuses one, since its keyframes do not know which level they belong to); a `.f3drun` with swaps verified by +`flutter3d_sim_mcp`, which refuses one because it has no game to build the +edited level with; a look-only edit is not written into the demo, so the +replay is right in what it simulates and shows the old lights and fog; a +branch through the timeline (`releaseAt`) still leaves the demo's tape on +the old future; the editor MCP driving the same path (its +`PlaySession.sendSaved` still sends whole documents, and has no base to +patch from); the platformer's `prepareEdit` still builds the whole edited +level, so a patch saves the wire and the comparison, not the build. **HR4. Live parameters and replay under new code (S–M).** *Done:* `ext.flutter3d.material.set {name, fields}` sets base colour, emissive, From 064e6a89295de607b69eb57a44ba22f60f7ab4f6 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:14:43 +0300 Subject: [PATCH 038/141] A shader parameter dragged in the editor changes the running game HotSwap.setMaterial takes parameters/ beside the built-in fields and writes into the list Material.parameters already holds, which the renderer binds every frame. A parameter the material was not loaded with, or a list of another length, is refused with what the material does have. A level material that defers to a .fmat is now called by the level's name for it, so the editor's material panel reaches it by the name it knows. --- ARCHITECTURE.md | 2 +- README.md | 2 +- .../lib/src/material_panel.dart | 62 ++++++++--- .../test/material_panel_test.dart | 59 ++++++++++ packages/flutter3d/CHANGELOG.md | 2 + .../src/engine/assets/material_loader.dart | 7 +- packages/flutter3d_app/CHANGELOG.md | 9 ++ .../lib/src/hot_swap/hot_swap.dart | 101 +++++++++++++++--- .../lib/src/level/level_loader.dart | 8 ++ .../flutter3d_app/test/hot_swap_test.dart | 88 +++++++++++++++ .../test/level_fmat_material_test.dart | 33 +++++- site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- tasks/0.9-engine-roadmap.md | 19 +++- 14 files changed, 365 insertions(+), 39 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 2374d5e33..b5703f173 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10994 tests** across 43 packages and 10 applications | +| Unit tests | **10999 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 83a8d47d9..5b99fad18 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10994 tests across forty-three packages and nine applications. The +There are 10999 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_editor/lib/src/material_panel.dart b/apps/flutter3d_editor/lib/src/material_panel.dart index fa8e50094..042e57762 100644 --- a/apps/flutter3d_editor/lib/src/material_panel.dart +++ b/apps/flutter3d_editor/lib/src/material_panel.dart @@ -178,6 +178,22 @@ Map? liveMaterialFields(String key, Object? value) => _ => null, }; +/// What a running game's `ext.flutter3d.material.set` takes for the `.fmat` +/// parameter [key] set to [value], or null for a value that is not numbers. +/// +/// **A list whatever the slider handed over**, for the reason the file is +/// written that way: a uniform is a list, and the game refuses one whose +/// length is not the one it loaded. +Map? liveParameterFields(String key, Object? value) => + switch (value) { + final num one => { + 'parameters/$key': [one], + }, + final List many when many.every((it) => it is num) => + {'parameters/$key': many}, + _ => null, + }; + /// The materials of a level, and the fields of the one that is open. final class MaterialPanel extends StatefulWidget { const MaterialPanel({ @@ -195,6 +211,11 @@ final class MaterialPanel extends StatefulWidget { /// Called with a material's name and [liveMaterialFields] for every value /// a slider passes through and every value written, for a running game to /// show before anything is saved. Keys a frame cannot show are not sent. + /// + /// For a material that defers to a `.fmat`, its parameters go out as + /// [liveParameterFields] and the level's own fields not at all: the game + /// draws that material from the file, so a level colour sent live would + /// show something the next load does not. final void Function(String material, Map fields)? onLive; /// Called after a field was actually written, naming it, so the screen can @@ -295,7 +316,12 @@ class _MaterialPanelState extends State { value: row[key], hint: levelMaterialHints[key], offers: widget.offers, - onPreview: (Object? value) => _live(name, key, value), + onPreview: (Object? value) => _live( + name, + document == null + ? liveMaterialFields(key, value) + : null, + ), onWrite: (Object? value) { if (setLevelMaterialField( widget.editing, @@ -303,7 +329,12 @@ class _MaterialPanelState extends State { key, value, )) { - _live(name, key, value); + _live( + name, + document == null + ? liveMaterialFields(key, value) + : null, + ); widget.onChanged('$name.$key'); } }, @@ -352,28 +383,34 @@ class _MaterialPanelState extends State { }, hint: document.hints[key], offers: widget.offers, + onPreview: (Object? value) => + _live(name, liveParameterFields(key, value)), // Back into a list on the way out, because that is what a uniform // is in the file whatever a slider hands over — a bare number under // `parameters` reads back as a list of one and would look to the // gate like a value the reader had misunderstood. - onWrite: (Object? value) => _writeFile( - name, - document, - 'parameters/$key', - value is num ? [value] : value, - ), + onWrite: (Object? value) { + if (_writeFile( + name, + document, + 'parameters/$key', + value is num ? [value] : value, + )) { + _live(name, liveParameterFields(key, value)); + } + }, ), ], ]; } - void _live(String material, String key, Object? value) { + void _live(String material, Map? fields) { final send = widget.onLive; - final fields = liveMaterialFields(key, value); if (send != null && fields != null) send(material, fields); } - void _writeFile( + /// Whether the file's reader took the change. + bool _writeFile( String name, MaterialDocument document, String key, @@ -385,9 +422,10 @@ class _MaterialPanelState extends State { value, hint: materialDocumentHint(document, key), ); - if (next == null) return; + if (next == null) return false; widget.onMaterialWritten?.call(name, next); widget.onChanged('$name:$key'); + return true; } Widget _heading(String says) => SectionLabel( diff --git a/apps/flutter3d_editor/test/material_panel_test.dart b/apps/flutter3d_editor/test/material_panel_test.dart index 2bf4f79b5..fe1af187b 100644 --- a/apps/flutter3d_editor/test/material_panel_test.dart +++ b/apps/flutter3d_editor/test/material_panel_test.dart @@ -266,6 +266,65 @@ void main() { expect(_in('parameter:windStrength', Slider), findsOneWidget); }); + testWidgets('and dragging it reaches a running game in the engine’s words', ( + WidgetTester tester, + ) async { + // `HR4`'s last half: the parameter row had no preview at all, so a game + // running beside the editor saw the wind only after a save and a reload. + // + // Mutation: drop the row's `onPreview`. Nothing is sent while the thumb + // is down and the first expectation fails. + final editing = openTestDocument(); + setLevelMaterialField(editing, 'stone', 'roughness', 0.4); + final live = <(String, Map)>[]; + await tester.pumpWidget( + _panel( + editing, + documents: {'stone': _document()}, + onLive: (String m, Map f) => live.add((m, f)), + ), + ); + + final gesture = await tester.startGesture( + tester.getCenter(_in('parameter:windStrength', Slider)), + ); + await gesture.moveBy(const Offset(20, 0)); + await tester.pump(); + expect(live, isNotEmpty, reason: 'sent while the thumb was still down'); + await gesture.up(); + await tester.pumpAndSettle(); + + expect(live.every((it) => it.$1 == 'stone'), isTrue); + expect( + live.every( + (it) => + it.$2.keys.single == 'parameters/windStrength' && + it.$2.values.single is List, + ), + isTrue, + reason: 'one key, and a list as the uniform is', + ); + + // And the level's own roughness is not sent for a material the game + // draws from its file: the next load would undo what it showed. + // + // Mutation: send `liveMaterialFields` whatever the material defers to. + live.clear(); + tester.widget(_in('field:roughness', Slider)).onChangeEnd!(0.75); + await tester.pump(); + expect(live, isEmpty); + }); + + test('a parameter in the engine’s words', () { + expect(liveParameterFields('wind', 0.5), { + 'parameters/wind': [0.5], + }); + expect(liveParameterFields('tint', [0.1, 0.2, 0.3]), { + 'parameters/tint': [0.1, 0.2, 0.3], + }); + expect(liveParameterFields('wind', 'strong'), isNull); + }); + testWidgets('and writing one hands back a document, never a file', ( WidgetTester tester, ) async { diff --git a/packages/flutter3d/CHANGELOG.md b/packages/flutter3d/CHANGELOG.md index 15fad22ad..752ede28f 100644 --- a/packages/flutter3d/CHANGELOG.md +++ b/packages/flutter3d/CHANGELOG.md @@ -6,6 +6,8 @@ mesh and material and the nodes stay, with their transforms, animation and children. `ModelSwap` reports what matched, what the new version dropped (left drawn as it was) and what it added (needs instantiating again). +- `bindMaterial` takes a `name` for the bound material, the file's own by + default; a level passes the name its surfaces use. ## 0.8.3+1 diff --git a/packages/flutter3d/lib/src/engine/assets/material_loader.dart b/packages/flutter3d/lib/src/engine/assets/material_loader.dart index 51d067f13..14346e4d8 100644 --- a/packages/flutter3d/lib/src/engine/assets/material_loader.dart +++ b/packages/flutter3d/lib/src/engine/assets/material_loader.dart @@ -80,10 +80,15 @@ Future loadMaterialDocument( /// field here to hang a sampler off, so the encoder binds it with the /// device's default. Only whether it carries a mip chain survives, because /// that is part of the texture rather than of the sampler. +/// +/// [name] is what the material is called once bound, the file's own name by +/// default. A level passes the name its surfaces use for it, because that is +/// the one a tool addresses it by (`ext.flutter3d.material.set`). Future bindMaterial( MaterialDocument document, { required GraphicsDevice device, required AssetUriResolver resolveUri, + String? name, LightingModel lighting = LightingModel.pbr, List? warnings, ImageDecoder decodeImage = defaultImageDecoder, @@ -192,7 +197,7 @@ Future bindMaterial( } return Material( - name: surface.name, + name: name ?? surface.name, lighting: document.lighting ?? (surface.lightingModel ?? diff --git a/packages/flutter3d_app/CHANGELOG.md b/packages/flutter3d_app/CHANGELOG.md index e28ef205b..f48079f18 100644 --- a/packages/flutter3d_app/CHANGELOG.md +++ b/packages/flutter3d_app/CHANGELOG.md @@ -9,6 +9,15 @@ `LevelLoader` watches every map a level names, and `LoadedLevel.dispose` stops. `ext.flutter3d.assets.put` accepts an image registered this way. +- **A shader parameter dragged in the editor changes the next frame.** + `HotSwap.setMaterial` takes `parameters/` beside the built-in + fields and writes the numbers into the list `Material.parameters` already + holds, which the renderer binds every frame. A parameter the material was + not loaded with, or a list of another length, is refused with what the + material does have. A level material that defers to a `.fmat` is now + called by the level's name for it rather than the file's, so the editor + reaches it by the name it knows. + - **A sky panorama edited under a running game lights it again.** `HotSwap.loadEnvironment` builds the prefiltered cube a `.hdr` or an image asset makes, whose roughest level is also the irradiance, and diff --git a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart index e370750e7..1e1c17f1c 100644 --- a/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart +++ b/packages/flutter3d_app/lib/src/hot_swap/hot_swap.dart @@ -553,6 +553,7 @@ final class HotSwap { } /// The fields [setMaterial] understands, and how many numbers each takes. + /// A shader's own parameters are named with [parameterField] in front. static const Map materialFields = { 'baseColor': 4, 'emissive': 3, @@ -563,6 +564,21 @@ final class HotSwap { 'alphaCutoff': 1, }; + /// What a field of [setMaterial] starts with when it sets one of + /// `Material.parameters` rather than a field every material has: + /// `parameters/windStrength` is the `windStrength` a `.fmat` lists under + /// `parameters` — the editor's own spelling of the same key. + /// + /// **Written into the list the material already has, and only that.** The + /// renderer binds `Material.parameters` afresh every frame, so a number + /// written in place is drawn on the next one with nothing rebuilt, the way + /// `Material.polylineViewport` is. A parameter the material was not loaded + /// with is refused rather than added: the map may be a constant, and a + /// member the compiled block does not have makes every frame of that + /// material throw in the encoder. A list of another length is refused for + /// the same reason — the block's layout is the shader's, not the panel's. + static const String parameterField = 'parameters/'; + /// Sets [fields] on every material called [name] in the registered /// scenes, from the next frame on, and keeps them as overrides. /// @@ -578,17 +594,49 @@ final class HotSwap { /// [clearMaterial] drops them. /// /// [fields] maps a name from [materialFields] to a number or a list of - /// that many numbers. Throws [ArgumentError] naming the field otherwise. + /// that many numbers, or a [parameterField] name to as many numbers as + /// that parameter of every such material has. Throws [ArgumentError] + /// naming the field otherwise, and keeps nothing of a call it refused. /// Returns how many materials took the change. int setMaterial(String name, Map fields) { if (!enabled) return 0; for (final MapEntry(key: field, :value) in fields.entries) { _numbers(field, value); } + for (final material in _materials(only: name)) { + for (final MapEntry(key: field, :value) in fields.entries) { + if (field.startsWith(parameterField)) { + _checkParameter(material, field, _numbers(field, value).length); + } + } + } (_overrides[name] ??= {}).addAll(fields); return _applyOverrides(only: name); } + static void _checkParameter(Material material, String field, int count) { + final parameter = field.substring(parameterField.length); + final held = material.parameters[parameter]; + if (held == null) { + final has = material.parameters.keys; + throw ArgumentError.value( + field, + 'field', + '${material.name} has no parameter "$parameter" ' + '(${has.isEmpty ? 'it has none' : 'it has ${has.join(', ')}'}); ' + 'one is added by writing it into the material file', + ); + } + if (held.length != count) { + throw ArgumentError.value( + count, + field, + 'takes ${held.length} number${held.length == 1 ? '' : 's'}, as ' + '${material.name} was loaded with', + ); + } + } + /// Forgets the overrides for [name], or for every material when null. The /// materials keep what they were set to until their file is loaded again. void clearMaterial([String? name]) { @@ -599,32 +647,45 @@ final class HotSwap { } } - int _applyOverrides({String? only}) { + /// Every named material in the registered scenes, or every one called + /// [only], once however many nodes wear it. + Set _materials({String? only}) { _scenes.removeWhere((WeakReference s) => s.target == null); final seen = Set.identity(); for (final scene in _scenes) { scene.target?.root.traverse((SceneNode node) { if (node is! MeshNode) return; - final material = node.material; - final name = material.name; + final name = node.material.name; if (name == null || (only != null && name != only)) return; - final fields = _overrides[name]; - if (fields == null || !seen.add(material)) return; - for (final MapEntry(key: field, :value) in fields.entries) { - _write(material, field, _numbers(field, value)); - } + seen.add(node.material); }); } - return seen.length; + return seen; + } + + int _applyOverrides({String? only}) { + final touched = [ + for (final material in _materials(only: only)) + if (_overrides.containsKey(material.name)) material, + ]; + for (final material in touched) { + for (final MapEntry(key: field, :value) + in _overrides[material.name]!.entries) { + _write(material, field, _numbers(field, value)); + } + } + return touched.length; } static List _numbers(String field, Object? value) { + final parameter = field.startsWith(parameterField); final count = materialFields[field]; - if (count == null) { + if (count == null && !parameter) { throw ArgumentError.value( field, 'field', - 'not a material field; there are ${materialFields.keys.join(', ')}', + 'not a material field; there are ${materialFields.keys.join(', ')}, ' + 'and $parameterField for a parameter of its shader', ); } final numbers = switch (value) { @@ -635,12 +696,15 @@ final class HotSwap { ], _ => const [], }; - if (numbers.length != count || - (value is List && value.length != count)) { + if (numbers.isEmpty || + (value is List && value.length != numbers.length) || + (count != null && numbers.length != count)) { throw ArgumentError.value( value, field, - 'takes $count number${count == 1 ? '' : 's'}', + count == null + ? 'takes a number or a list of numbers' + : 'takes $count number${count == 1 ? '' : 's'}', ); } return numbers; @@ -662,6 +726,13 @@ final class HotSwap { material.normalScale = v[0]; case 'alphaCutoff': material.alphaCutoff = v[0]; + default: + // A parameter. A swapped model whose material no longer carries it, + // or carries it at another length, is left as the file has it; the + // override waits for one that does. + final held = + material.parameters[field.substring(parameterField.length)]; + if (held != null && held.length == v.length) held.setAll(0, v); } } diff --git a/packages/flutter3d_app/lib/src/level/level_loader.dart b/packages/flutter3d_app/lib/src/level/level_loader.dart index 4deb69136..e320ecf8e 100644 --- a/packages/flutter3d_app/lib/src/level/level_loader.dart +++ b/packages/flutter3d_app/lib/src/level/level_loader.dart @@ -460,6 +460,7 @@ final class LevelLoader { if (entry.value.fmat case final String path) { if (await _fmatMaterial( device, + entry.key, path, readAsset ?? _bundleAsset, loadIssues, @@ -624,8 +625,14 @@ final class LevelLoader { /// The document's own findings come through too, because a `.fmat` with /// `roughnesss` in it is exactly the hand-edit that format exists to make /// survivable and the warning is the only place it shows. + /// + /// **Named [name], the level's word for it, not the file's.** The surfaces + /// that wear it are named that already, and it is the name the editor + /// sends a dragged parameter under (`HotSwap.setMaterial`); the file's own + /// name would leave a running level unreachable from the panel editing it. static Future _fmatMaterial( GraphicsDevice device, + String name, String path, AssetBytes read, List issues, @@ -640,6 +647,7 @@ final class LevelLoader { document, device: device, resolveUri: source.resolveUri, + name: name, warnings: warnings, ); } catch (error) { diff --git a/packages/flutter3d_app/test/hot_swap_test.dart b/packages/flutter3d_app/test/hot_swap_test.dart index 6473d5d86..ade84a1a3 100644 --- a/packages/flutter3d_app/test/hot_swap_test.dart +++ b/packages/flutter3d_app/test/hot_swap_test.dart @@ -380,6 +380,94 @@ void main() { await swap.swap(); expect(ship.meshes.single.material.roughness, isNot(0.1)); }); + + /// A scene with one quad in a material whose shader reads `wind` and + /// `tint`, as a `.fmat` with parameters is bound. + ({Scene scene, engine.Material material}) waving() { + final material = engine.Material( + name: 'grass', + parameters: { + 'wind': Float32List.fromList([0.3]), + 'tint': Float32List.fromList([0.1, 0.4, 0.5]), + }, + ); + final scene = Scene() + ..add( + MeshNode( + DeviceMesh.upload( + device, + CuboidShape(size: Vector3.all(1.0)).build(), + ), + material, + ), + ); + swap.registerScene(scene); + return (scene: scene, material: material); + } + + test('sets a parameter of its shader in the list the frame binds', () { + // `HR4`'s last half: a slider over a `.fmat` parameter reaches the + // game. Into the list the material already has, so the renderer, which + // binds `parameters` every frame, draws it next frame. + // + // Mutation: build a new list instead of writing into the old one. The + // values match but `held` is not the list the material was loaded with, + // and the second expectation fails — as a cached reference to it would. + final (scene: _, :material) = waving(); + final held = material.parameters['wind']!; + + final touched = swap.setMaterial('grass', { + 'parameters/wind': 1.5, + 'parameters/tint': [0.2, 0.2, 0.9], + }); + + expect(touched, 1); + expect(material.parameters['wind'], [1.5]); + expect(material.parameters['wind'], same(held)); + expect(material.parameters['tint']![2], closeTo(0.9, 1e-6)); + }); + + test('refuses a parameter the material was not loaded with, or one of ' + 'another length, and keeps nothing of the call', () { + // A member the compiled block does not have throws in the encoder on + // every frame of that material, so a typo in a panel must stop here. + // + // Mutation: drop `_checkParameter`. `gust` is stored as an override, the + // first expectation fails, and `tint` takes two numbers into three. + final (scene: _, :material) = waving(); + + expect( + () => swap.setMaterial('grass', { + 'parameters/gust': 1.0, + 'roughness': 0.2, + }), + throwsA( + isA().having( + (ArgumentError e) => e.message, + 'message', + contains('it has wind, tint'), + ), + ), + ); + expect( + () => swap.setMaterial('grass', { + 'parameters/tint': [1, 0], + }), + throwsArgumentError, + ); + expect( + () => swap.setMaterial('grass', { + 'parameters/wind': ['strong'], + }), + throwsArgumentError, + ); + expect(material.roughness, isNot(0.2), reason: 'nothing of it taken'); + expect(material.parameters['tint']![0], closeTo(0.1, 1e-6)); + + // And nothing kept to come back on a later call for another field. + swap.setMaterial('grass', {'metallic': 0.5}); + expect(material.roughness, isNot(0.2)); + }); }); group('a texture', () { diff --git a/packages/flutter3d_app/test/level_fmat_material_test.dart b/packages/flutter3d_app/test/level_fmat_material_test.dart index cd64507ed..73a766a59 100644 --- a/packages/flutter3d_app/test/level_fmat_material_test.dart +++ b/packages/flutter3d_app/test/level_fmat_material_test.dart @@ -113,7 +113,11 @@ void main() { expect(material.parameterBlock, 'WaterParams'); expect(material.parameters['speed'], [2.5]); expect(material.parameters['tint'], hasLength(3)); - expect(material.name, 'canal water'); + expect( + material.name, + 'wall', + reason: 'the level\'s name for it, which the editor addresses it by', + ); expect( material.roughness, closeTo(0.05, 1e-6), @@ -125,6 +129,33 @@ void main() { expect(material.albedo, isNotNull); }); + test( + 'and a parameter dragged in the editor reaches the running wall', + () async { + // `HR4`: the panel sends `parameters/speed` under the level material's + // name, and the game finds the wall by it. + // + // Mutation: bind the file under its own name (`canal water`) again. No + // material is called `wall`, nothing is touched, and the speed stays 2.5. + final loaded = await const LevelLoader().build( + Level.fromJson(_levelJson(fmat: 'materials/water.fmat')), + device: device, + registry: registry, + readAsset: readAsset, + ); + final swap = HotSwap(enabled: true)..registerScene(loaded.scene); + + final touched = swap.setMaterial('wall', { + 'parameters/speed': [3.5], + }); + + expect(touched, 1); + expect(loaded.brushNodes.single.material.parameters['speed'], [ + 3.5, + ]); + }, + ); + test('and its hints travel with it, describing without constraining', () async { // A hint is a description for an inspector, so it stops at the document — // a `Material` is what the renderer draws and has no use for a slider's diff --git a/site/content/quickstart.md b/site/content/quickstart.md index c1d3dc60f..8b7a0ee03 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10994 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 10999 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index e2fc7187f..4138d83bc 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10994 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10999 tests need a GPU. --- # Testing -10994 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10999 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -21,13 +21,13 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 70 | | `apps/flutter3d_demo_platformer` | 204 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 357 | | `apps/flutter3d_demo_strategy` | 50 | -| `apps/flutter3d_editor` | 224 | | `apps/flutter3d_demo_arcade` | 20 | +| `apps/flutter3d_editor` | 226 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | -| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 140 | +| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 143 | | `flutter3d_game` | 287 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10968 rather than 10994: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10973 rather than 10999: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 24a63c811..f770f15ff 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -256,8 +256,23 @@ written, to the game Play is running: in the engine's words most thirty times a second with the last value always sent. `replayAfterHotSwap` replays the last seconds after every `HotSwap` and reports where the new code parts from the old; the platformer calls it. -*Left:* the same live path for `.fmat` parameters, which the engine material -does not carry as fields. +`.fmat` parameters take the same path: `material.set` accepts +`parameters/` and writes into the list `Material.parameters` already +holds, which the renderer binds every frame; a parameter the material was +not loaded with, or one of another length, is refused naming what it has, +since a member the compiled block lacks would throw on every frame. The +panel's parameter rows send while the thumb moves (`liveParameterFields`), +and a material that defers to a file no longer sends the level's own +fields, which the next load would undo. `LevelLoader` binds a deferred +`.fmat` under the level's name for it (`bindMaterial(name:)`), the name the +editor sends; under the file's own it was unreachable. +*Left:* the `.fmat`'s own surface fields (its base colour, roughness and so +on, under the panel's file heading) are written to the file but not sent +live; a parameter the shader gained since the game loaded needs a reload, +since it is refused rather than added; and no test drives the editor and a +running game together — the panel, the throttle and the game's half are +each tested, the VM service connection between them (`GameMaterials`) is +not. **HR5. Editor Play (M), with §6.** *Done on desktop:* the toolbar's play button finds the project above the open level (the nearest `pubspec.yaml`) From 3e338cbe54170dbc691630cdfac244ffe8c0a9bf Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:16:34 +0300 Subject: [PATCH 039/141] The editor plays a running game it attaches to by address A browser has no process to start, so the web editor's Play takes the VM service address of a game already running and does with it what Play does on the desktop: the timeline, level pushes as patches, live materials. AttachedRun is that run; package:flutter3d_editor_play/attach.dart is the part of Play a browser has, and a test holds it free of dart:io. connectVmService replaces vmServiceConnectUri so the same code connects from either side. --- ARCHITECTURE.md | 4 +- README.md | 2 +- apps/flutter3d_editor/lib/main.dart | 76 ++++- .../lib/src/play/device_picker.dart | 69 +++++ .../lib/src/play/live_material.dart | 4 +- .../lib/src/play/play_screen.dart | 115 +++---- .../lib/src/timeline_client.dart | 17 +- apps/flutter3d_editor/pubspec.yaml | 7 +- .../test/play_screen_test.dart | 48 ++- packages/flutter3d_editor_play/CHANGELOG.md | 19 ++ packages/flutter3d_editor_play/README.md | 31 +- .../flutter3d_editor_play/lib/attach.dart | 16 + .../lib/flutter3d_editor_play.dart | 6 +- .../lib/src/attached_run.dart | 288 ++++++++++++++++++ .../lib/src/flutter_run.dart | 56 +--- .../lib/src/level_push.dart | 6 +- .../lib/src/play_state.dart | 83 +++++ .../lib/src/vm_connect.dart | 54 ++++ .../flutter3d_editor_play/lib/testing.dart | 166 +++++++++- packages/flutter3d_editor_play/pubspec.yaml | 9 +- .../test/attach_web_test.dart | 74 +++++ .../test/attached_run_test.dart | 211 +++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- tasks/0.9-engine-roadmap.md | 26 +- tool/structure/detectors.dart | 21 ++ 26 files changed, 1255 insertions(+), 165 deletions(-) create mode 100644 apps/flutter3d_editor/lib/src/play/device_picker.dart create mode 100644 packages/flutter3d_editor_play/lib/attach.dart create mode 100644 packages/flutter3d_editor_play/lib/src/attached_run.dart create mode 100644 packages/flutter3d_editor_play/lib/src/play_state.dart create mode 100644 packages/flutter3d_editor_play/lib/src/vm_connect.dart create mode 100644 packages/flutter3d_editor_play/test/attach_web_test.dart create mode 100644 packages/flutter3d_editor_play/test/attached_run_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b5703f173..b8a23ccda 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -239,7 +239,7 @@ point of §3.3. | `flutter3d_editor_core` | The headless half of a level editor: the document being changed and undone, the handles a pointer hits, the palette a level builds out of itself, the project a template becomes; `LevelScene`, which turns a level into brush meshes, materials, lights and probes with no Flutter; the seeded level generators; and `LightOptimizer`, which finds fewer lights that light a level the way it was lit. Plain Dart | | `flutter3d_editor_widgets` | Editor controls the modeller and the level editor share instead of each keeping its own copy — `ui-27`'s own package: `SectionLabel`, `NumberField`, `ColorField`, `RangeSliderField`, `EnumField`, `TextureSlotRow`, `TexturePathField`, `ColorSwatchField`, `HintTextBox`/`NumbersRow`, `FieldRow` and `EditorWidgetsTheme` so far | | `flutter3d_editor_mcp` | The same editor offered to an agent: `EditorCommand` as a table of MCP tools over stdio, one document per process, plus the two verbs a caller with no screen needs — a flat listing, and the validator. Plain Dart | -| `flutter3d_editor_play` | Play from a level editor: the game a level belongs to run with `flutter run --machine`, swapped, restarted and stopped, its devices listed, and a saved level sent to it. What the editor's Play button and `flutter3d_editor_mcp`'s `play` tools both run. Plain Dart with `dart:io`, which is why it is not in `flutter3d_editor_core` | +| `flutter3d_editor_play` | Play from a level editor: the game a level belongs to run with `flutter run --machine`, swapped, restarted and stopped, its devices listed, and a saved level sent to it. What the editor's Play button and `flutter3d_editor_mcp`'s `play` tools both run. Plain Dart with `dart:io`, which is why it is not in `flutter3d_editor_core`; `attach.dart` is the part without it, a game attached to by its VM service address, which is the web editor's Play | | `flutter3d_mcp_kit` | What every MCP server here shares: a tool paired with its handler, a server that is a list of them over one session, the two shapes of answer, and a loopback HTTP transport an open application offers its session over. Plain Dart | | `flutter3d_sim_mcp` | A level an agent plays blind, of whatever `HeadlessGame` a host hands it — step, read back, digest, hand over the run — and many seeded playtests in isolates; and a rendered frame of a level offered to an agent: drawn with no GPU in one of the renderer's debug views, one pixel read back unclamped, the passes the frame graph ran. Names no genre | | `flutter3d_build` | The converter behind `dart run flutter3d_build:convert` and the build hook that runs it on every build, and the bakes that need a renderer or a mesh editor ahead of time: levels of detail, impostors, cluster tables, a file per device class, paged splat captures, six-way smoke sheets, and the light optimizer's command. That is why it depends on `flutter3d_cpu`, `flutter3d_mesh`, `flutter3d_model_core`, `flutter3d_editor_core` and `flutter3d_sim`. Not a dependency of the engine: no game that draws a frame runs it. Plain Dart | @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10999 tests** across 43 packages and 10 applications | +| Unit tests | **11012 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 5b99fad18..673adf1c2 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10999 tests across forty-three packages and nine applications. The +There are 11012 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_editor/lib/main.dart b/apps/flutter3d_editor/lib/main.dart index d87911b7e..bb21e463d 100644 --- a/apps/flutter3d_editor/lib/main.dart +++ b/apps/flutter3d_editor/lib/main.dart @@ -51,6 +51,7 @@ import 'src/fly_camera.dart'; import 'src/light_plan_dialog.dart'; import 'src/material_panel.dart'; import 'src/open_run_channel.dart'; +import 'src/play/device_picker.dart'; import 'src/play/live_material.dart'; import 'src/play/play_screen.dart'; import 'src/playtest_report_screen.dart'; @@ -148,10 +149,12 @@ class _EditorScreenState extends State /// The documents this editor has had open, kept between launches. final RecentProjects _projects = RecentProjects(); - /// `HR5`: the project this level belongs to, run with `flutter run`. Kept - /// here rather than by [PlayScreen] so closing the panel leaves the game - /// running; replaced when a level from another project is played. - FlutterRun? _run; + /// `HR5`: the game being played — the project this level belongs to, run + /// with `flutter run`, or a game attached to by its VM service. Kept here + /// rather than by [PlayScreen] so closing the panel leaves the game + /// running; replaced when a level from another project is played, or + /// another game attached to. + PlayedGame? _run; /// What [_projects] said when the chooser last needed it. /// @@ -1149,8 +1152,9 @@ class _EditorScreenState extends State } } - /// Sends a saved level to the game [_run] started, when it is running, - /// and says in the status strip what the game did with it. + /// Sends a saved level to the game [_run] plays, when it is running, + /// whether this editor started it or attached to it, and says in the + /// status strip what the game did with it. /// /// As a patch from [base], the document this save wrote over, when the /// game is playing that; whole when it is not. @@ -1515,10 +1519,12 @@ class _EditorScreenState extends State bottom: 0, child: EditorLegend(state: state), ), - // `HR5`: runs the project this level belongs to. + // `HR5`: runs the project this level belongs to. Beside the + // material panel's button: at 92 it lay under the step + // panel's, which was drawn over it and took every press. Positioned( top: 4, - right: 92, + right: 212, child: IconButton( icon: const Icon( Icons.play_circle_outline, @@ -1528,9 +1534,9 @@ class _EditorScreenState extends State onPressed: () => _play(state), ), ), - // `rp-02`: attaches to a game already running elsewhere, - // over the same VM service channel DevTools uses — this - // editor still edits no simulation of its own. + // `rp-02`, and `HR5` without a process: attaches to a game + // already running elsewhere, over the same VM service + // channel DevTools uses, and plays it in the same panel. Positioned( top: 4, right: 12, @@ -1648,8 +1654,6 @@ class _EditorScreenState extends State await _openRunAt(file.path); } - /// Asks for a running game's VM service address, connects, and opens the - /// timeline panel on it — `rp-02`'s door, from the editor's side. /// Opens the play panel on the project [state]'s level belongs to, /// starting a run of it when none is going. Future _play(EditorReady state) async { @@ -1673,7 +1677,47 @@ class _EditorScreenState extends State } await Navigator.of(context).push( MaterialPageRoute( - builder: (_) => PlayScreen(run: run, onTimeline: _openTimeline), + builder: (_) => PlayScreen( + session: run, + onTimeline: _openTimeline, + picker: ({required bool enabled}) => + DevicePicker(run: run, enabled: enabled), + ), + ), + ); + } + + /// Attaches to the game at [uri] and opens the play panel on it: its + /// console, hot reload and restart, timeline, and every save sent to it. + /// + /// **Refused while a game this editor started is running.** The editor + /// plays one game at a time, and replacing that run would stop a game + /// somebody is in the middle of; they stop it first, in its own panel. + Future _attach(String uri) async { + final session = switch (_run) { + final AttachedRun same? when same.vmService == uri => same, + final PlayedGame owned? + when owned.ownsTheGame && owned.state.value is! PlayStopped => + null, + final other => () { + unawaited(other?.dispose()); + return _run = AttachedRun(uri); + }(), + }; + if (session == null) { + _changed( + 'a game started here is still running: stop it in the Play panel ' + 'before attaching to another', + ); + return; + } + if (session.state.value case PlayIdle() || PlayStopped()) { + unawaited(session.start()); + } + if (!mounted) return; + await Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => PlayScreen(session: session, onTimeline: _openTimeline), ), ); } @@ -1727,8 +1771,8 @@ class _EditorScreenState extends State ), ); controller.dispose(); - if (uri == null || uri.isEmpty || !mounted) return; - await _openTimeline(uri); + if (uri == null || uri.trim().isEmpty || !mounted) return; + await _attach(uri.trim()); } } diff --git a/apps/flutter3d_editor/lib/src/play/device_picker.dart b/apps/flutter3d_editor/lib/src/play/device_picker.dart new file mode 100644 index 000000000..a89857455 --- /dev/null +++ b/apps/flutter3d_editor/lib/src/play/device_picker.dart @@ -0,0 +1,69 @@ +/// Which device a [FlutterRun] starts on, for `PlayScreen`'s bar. +library; + +import 'package:flutter/material.dart'; +import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; + +/// Picks [run]'s device from [devices]. +/// +/// **Held still while a run is going** ([enabled] false): the device is +/// `flutter run`'s argument, and changing it means a new run. Its own file +/// because `flutterDevices` runs a process, and `PlayScreen` has to build +/// where there is none. +final class DevicePicker extends StatefulWidget { + const DevicePicker({ + super.key, + required this.run, + required this.enabled, + this.devices = flutterDevices, + }); + + final FlutterRun run; + final bool enabled; + + /// What the picker offers; `flutter devices --machine` by default. + final Future> Function() devices; + + @override + State createState() => _DevicePickerState(); +} + +class _DevicePickerState extends State { + static const Color _background = Color(0xFF14161A); + static const Color _text = Color(0xFFE6EAF0); + + /// Asked once per opening of the panel: the tool takes a second or two to + /// answer, and a phone plugged in afterwards is found by opening it again. + late final Future> _devices = widget.devices(); + + @override + Widget build(BuildContext context) => FutureBuilder>( + future: _devices, + builder: (BuildContext context, AsyncSnapshot> it) { + final devices = it.data ?? const []; + final chosen = widget.run.device; + return DropdownButton( + key: const ValueKey('play.device'), + value: devices.any((d) => d.id == chosen) ? chosen : null, + dropdownColor: _background, + style: const TextStyle(color: _text, fontSize: 13), + underline: const SizedBox.shrink(), + hint: Text( + it.hasError ? 'devices unavailable' : 'default device', + style: const TextStyle(color: _text, fontSize: 13), + ), + items: >[ + const DropdownMenuItem(child: Text('default device')), + for (final device in devices) + DropdownMenuItem( + value: device.id, + child: Text(device.name), + ), + ], + onChanged: widget.enabled + ? (String? id) => setState(() => widget.run.device = id) + : null, + ); + }, + ); +} diff --git a/apps/flutter3d_editor/lib/src/play/live_material.dart b/apps/flutter3d_editor/lib/src/play/live_material.dart index 43c56bce7..f27f06075 100644 --- a/apps/flutter3d_editor/lib/src/play/live_material.dart +++ b/apps/flutter3d_editor/lib/src/play/live_material.dart @@ -5,8 +5,8 @@ library; import 'dart:async'; import 'dart:convert'; +import 'package:flutter3d_editor_play/attach.dart'; import 'package:vm_service/vm_service.dart'; -import 'package:vm_service/vm_service_io.dart'; /// Passes on what the panel sends at most [interval] apart, merging what /// arrives in between, and always sends the last of it. @@ -100,7 +100,7 @@ final class GameMaterials { } Future<({VmService service, String isolate})> _connect() async { - final service = await vmServiceConnectUri(vmService); + final service = await connectVmService(vmService); final vm = await service.getVM(); for (final ref in vm.isolates ?? const []) { final isolate = await service.getIsolate(ref.id!); diff --git a/apps/flutter3d_editor/lib/src/play/play_screen.dart b/apps/flutter3d_editor/lib/src/play/play_screen.dart index d9e42624c..f5b419453 100644 --- a/apps/flutter3d_editor/lib/src/play/play_screen.dart +++ b/apps/flutter3d_editor/lib/src/play/play_screen.dart @@ -1,32 +1,38 @@ -/// `HR5`'s panel: the project running, its console, and the four things -/// done to it — start, hot reload, hot restart, stop — plus the timeline of -/// the game it started and the device it runs on. +/// `HR5`'s panel: the game being played, its console, and the four things +/// done to it — start, hot reload, hot restart, stop — plus its timeline and, +/// for a game the editor started, the device it runs on. library; import 'package:flutter/material.dart'; -import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; +import 'package:flutter3d_editor_play/attach.dart'; -/// Shows [run] and drives it. +/// Shows [session] and drives it. /// -/// **A view of the run, not its owner.** The editor keeps [run] for as long -/// as the project is open, so closing this screen leaves the game running and -/// opening it again finds the same console — the way a terminal tab with -/// `flutter run` in it would behave. +/// **A view of the session, not its owner.** The editor keeps [session] for +/// as long as the project is open, so closing this screen leaves the game +/// running and opening it again finds the same console — the way a terminal +/// tab with `flutter run` in it would behave. +/// +/// **No `dart:io` here.** The same panel shows a game the editor attached to +/// by its VM service address, which is the only Play a browser has; what +/// needs a process — the device picker — comes in as [picker] from a file +/// that is allowed one. final class PlayScreen extends StatefulWidget { const PlayScreen({ super.key, - required this.run, + required this.session, required this.onTimeline, - this.devices = flutterDevices, + this.picker, }); - final FlutterRun run; + final PlayedGame session; /// Opens the timeline panel on the running game's VM service. final void Function(String vmService) onTimeline; - /// What the device picker offers; `flutter devices --machine` by default. - final Future> Function() devices; + /// What chooses the device the next run starts on, enabled between runs; + /// none for a game somebody else started. + final Widget Function({required bool enabled})? picker; @override State createState() => _PlayScreenState(); @@ -36,43 +42,50 @@ class _PlayScreenState extends State { static const Color _background = Color(0xFF14161A); static const Color _text = Color(0xFFE6EAF0); - /// Asked once per opening of the panel: the tool takes a second or two to - /// answer, and a phone plugged in afterwards is found by opening it again. - late final Future> _devices = widget.devices(); - @override Widget build(BuildContext context) { - final run = widget.run; + final session = widget.session; + final owns = session.ownsTheGame; return StreamBuilder( - stream: run.state.changes, - initialData: run.state.value, + stream: session.state.changes, + initialData: session.state.value, builder: (BuildContext context, _) { - final state = run.state.value; + final state = session.state.value; final running = state is PlayRunning; final going = state is PlayStarting || running; return Scaffold( backgroundColor: _background, appBar: AppBar( - title: Text(run.projectRoot, overflow: TextOverflow.ellipsis), + title: Text(session.title, overflow: TextOverflow.ellipsis), actions: [ - _picker(enabled: !going), + ?widget.picker?.call(enabled: !going), IconButton( key: const ValueKey('play.start'), - icon: Icon(going ? Icons.stop : Icons.play_arrow), - tooltip: going ? 'Stop' : 'Run the project', - onPressed: going ? run.stop : run.start, + icon: Icon(switch ((going, owns)) { + (true, true) => Icons.stop, + (true, false) => Icons.link_off, + (false, true) => Icons.play_arrow, + (false, false) => Icons.link, + }), + tooltip: switch ((going, owns)) { + (true, true) => 'Stop', + (true, false) => 'Detach (the game keeps running)', + (false, true) => 'Run the project', + (false, false) => 'Attach again', + }, + onPressed: going ? session.stop : session.start, ), IconButton( key: const ValueKey('play.reload'), icon: const Icon(Icons.bolt), tooltip: 'Hot reload', - onPressed: running ? run.hotSwap : null, + onPressed: running ? session.hotSwap : null, ), IconButton( key: const ValueKey('play.restart'), icon: const Icon(Icons.restart_alt), tooltip: 'Hot restart', - onPressed: running ? run.hotRestart : null, + onPressed: running ? session.hotRestart : null, ), IconButton( key: const ValueKey('play.timeline'), @@ -94,9 +107,11 @@ class _PlayScreenState extends State { padding: const EdgeInsets.fromLTRB(12, 8, 12, 4), child: Text( switch (state) { - PlayIdle() => 'Not running', + PlayIdle() => owns ? 'Not running' : 'Not attached', PlayStarting(:final message) => message, - PlayRunning(:final vmService) => 'Running, $vmService', + PlayRunning(:final vmService) => + owns ? 'Running, $vmService' : 'Attached, $vmService', + PlayStopped(:final String reason) => reason, PlayStopped(:final exitCode) => 'Stopped ($exitCode)', }, key: const ValueKey('play.status'), @@ -105,12 +120,12 @@ class _PlayScreenState extends State { ), Expanded( child: StreamBuilder>( - stream: run.console.changes, - initialData: run.console.value, + stream: session.console.changes, + initialData: session.console.value, // Reversed, so the newest line sits at the bottom and the // list stays there as lines arrive, as a terminal does. builder: (BuildContext context, _) { - final lines = run.console.value; + final lines = session.console.value; return ListView.builder( reverse: true, padding: const EdgeInsets.symmetric(horizontal: 12), @@ -134,36 +149,4 @@ class _PlayScreenState extends State { }, ); } - - /// Which device the next run starts on. Held still while a run is going: - /// the device is `flutter run`'s argument, and changing it means a new run. - Widget _picker({required bool enabled}) => FutureBuilder>( - future: _devices, - builder: (BuildContext context, AsyncSnapshot> it) { - final devices = it.data ?? const []; - final chosen = widget.run.device; - return DropdownButton( - key: const ValueKey('play.device'), - value: devices.any((d) => d.id == chosen) ? chosen : null, - dropdownColor: _background, - style: const TextStyle(color: _text, fontSize: 13), - underline: const SizedBox.shrink(), - hint: Text( - it.hasError ? 'devices unavailable' : 'default device', - style: const TextStyle(color: _text, fontSize: 13), - ), - items: >[ - const DropdownMenuItem(child: Text('default device')), - for (final device in devices) - DropdownMenuItem( - value: device.id, - child: Text(device.name), - ), - ], - onChanged: enabled - ? (String? id) => setState(() => widget.run.device = id) - : null, - ); - }, - ); } diff --git a/apps/flutter3d_editor/lib/src/timeline_client.dart b/apps/flutter3d_editor/lib/src/timeline_client.dart index 513aef1f0..e40973677 100644 --- a/apps/flutter3d_editor/lib/src/timeline_client.dart +++ b/apps/flutter3d_editor/lib/src/timeline_client.dart @@ -1,5 +1,5 @@ +import 'package:flutter3d_editor_play/attach.dart'; import 'package:vm_service/vm_service.dart'; -import 'package:vm_service/vm_service_io.dart'; /// What `ext.flutter3d.timeline.preview` answers. typedef TimelinePreview = ({bool found, int? step}); @@ -60,8 +60,8 @@ abstract interface class TimelineClient { /// This does not start a game, does not know what a genre is, and does not /// hold a level document — it only speaks the seven /// `ext.flutter3d.timeline.*` extensions `registerTimelineExtensions` -/// registers on the other end, over a plain WebSocket that every desktop -/// platform this application ships to already has through `dart:io`. +/// registers on the other end, over `connectVmService`'s WebSocket, which is +/// `dart:io`'s on a desktop and the browser's in a web build. final class VmServiceTimelineClient implements TimelineClient { VmServiceTimelineClient._(this._service, this._isolateId); @@ -72,7 +72,7 @@ final class VmServiceTimelineClient implements TimelineClient { /// the same one a person pastes into DevTools, `http://` or `ws://`, /// with or without a trailing slash. static Future connect(String uri) async { - final service = await vmServiceConnectUri(_asWebSocket(uri)); + final service = await connectVmService(uri); final vm = await service.getVM(); final isolates = vm.isolates; if (isolates == null || isolates.isEmpty) { @@ -82,15 +82,6 @@ final class VmServiceTimelineClient implements TimelineClient { return VmServiceTimelineClient._(service, isolates.first.id!); } - static String _asWebSocket(String uri) { - final withScheme = uri - .replaceFirst('http://', 'ws://') - .replaceFirst('https://', 'wss://'); - return withScheme.endsWith('/ws') - ? withScheme - : '${withScheme.replaceFirst(RegExp(r'/$'), '')}/ws'; - } - Future> _call( String verb, [ Map? args, diff --git a/apps/flutter3d_editor/pubspec.yaml b/apps/flutter3d_editor/pubspec.yaml index d2b28fc1a..c883be2c3 100644 --- a/apps/flutter3d_editor/pubspec.yaml +++ b/apps/flutter3d_editor/pubspec.yaml @@ -48,7 +48,8 @@ dependencies: # Play: the project run with `flutter run --machine`, its devices, and a # saved level sent to it. Shared with `flutter3d_editor_mcp`, so an agent's - # Play is this toolbar's. + # Play is this toolbar's. Its `attach.dart` is the Play a web build has: a + # running game attached to by address, imported by `src/play/play_screen.dart`. flutter3d_editor_play: path: ../../packages/flutter3d_editor_play @@ -90,8 +91,8 @@ dependencies: # its `RunTimeline` — `rp-02`'s answer to "the editor runs the project", # over the same channel DevTools and `flutter attach` use, per # `doc/tooling-plan.md`. Not a plugin: this package speaks the VM service - # protocol over a plain WebSocket, which this application already has - # through `dart:io` on every platform it ships to. + # protocol over a plain WebSocket, opened by `flutter3d_editor_play`'s + # `connectVmService` so the same code reaches a game from a browser. vm_service: ^15.3.0 # State management — see the note in `packages/flutter3d_game/pubspec.yaml`. diff --git a/apps/flutter3d_editor/test/play_screen_test.dart b/apps/flutter3d_editor/test/play_screen_test.dart index 20e7ff893..b6e365f6b 100644 --- a/apps/flutter3d_editor/test/play_screen_test.dart +++ b/apps/flutter3d_editor/test/play_screen_test.dart @@ -1,9 +1,11 @@ -/// `HR5`'s panel over a run of the test's own making. +/// `HR5`'s panel over a run of the test's own making, and over a game the +/// editor attached to rather than started. /// /// flutter test test/play_screen_test.dart library; import 'package:flutter/material.dart'; +import 'package:flutter3d_editor/src/play/device_picker.dart'; import 'package:flutter3d_editor/src/play/play_screen.dart'; import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; import 'package:flutter3d_editor_play/testing.dart'; @@ -21,9 +23,13 @@ Future _pump( }) => tester.pumpWidget( MaterialApp( home: PlayScreen( - run: run, + session: run, onTimeline: timelines?.add ?? (String _) {}, - devices: () async => _devices, + picker: ({required bool enabled}) => DevicePicker( + run: run, + enabled: enabled, + devices: () async => _devices, + ), ), ), ); @@ -93,4 +99,40 @@ void main() { ); expect(picker.onChanged, isNull); }); + + testWidgets('an attached game is reloaded, timed and let go of', ( + WidgetTester tester, + ) async { + final game = FakeGame(registered: FakeGame.flutterTool()); + final run = fakeAttachedRun(game); + final timelines = []; + await tester.runAsync(run.start); + await tester.pumpWidget( + MaterialApp( + home: PlayScreen(session: run, onTimeline: timelines.add), + ), + ); + + // No process, so no device to pick. + expect(find.byKey(const ValueKey('play.device')), findsNothing); + expect(find.text('Attached, ws://game/ws'), findsOneWidget); + + await tester.tap(find.byKey(const ValueKey('play.reload'))); + await tester.runAsync(() => Future.delayed(Duration.zero)); + expect(game.calls.last['method'], 's0.reloadSources'); + + await tester.tap(find.byKey(const ValueKey('play.timeline'))); + expect(timelines, ['ws://game/ws']); + + // Mutation: the owner's tooltip here would promise a Stop that ends a + // game somebody else started, where the button only lets go of it. + expect( + _button(tester, 'play.start').tooltip, + 'Detach (the game keeps running)', + ); + await tester.tap(find.byKey(const ValueKey('play.start'))); + await tester.runAsync(() => Future.delayed(Duration.zero)); + await tester.pump(); + expect(find.text('Detached; the game keeps running'), findsOneWidget); + }); } diff --git a/packages/flutter3d_editor_play/CHANGELOG.md b/packages/flutter3d_editor_play/CHANGELOG.md index 9f4f756f2..00ffb574f 100644 --- a/packages/flutter3d_editor_play/CHANGELOG.md +++ b/packages/flutter3d_editor_play/CHANGELOG.md @@ -6,6 +6,25 @@ document when the game answers that the patch is stale; the answer then carries `fellBack`, which `describeLevelApplied` puts at the end of its line. `levelPatchArguments` builds the parameters. +- **`AttachedRun` plays a game somebody else started**, by its VM service + address: the console (with what the game printed before the attach, which + DDS replays), hot reload and restart through the services the flutter tool + registers on the game, and stop as a detach that leaves the game running. + It is the only Play a browser has, and the editor's attach button on a + desktop uses it too. `PlayedGame` is what it and `FlutterRun` both are, so + one panel shows either; `PlayState` moves to `src/play_state.dart`, and + `PlayStopped` takes a `reason` for an end with no exit code. +- **`package:flutter3d_editor_play/attach.dart`**: everything here that + starts no process — `AttachedRun`, `pushLevel`, `connectVmService`, + `PlayState`, `Watched` — with no `dart:io` anywhere under it, which a test + walks the imports to check. +- **`connectVmService` instead of `vmServiceConnectUri`**, over + `web_socket_channel`, so a level is sent from a browser as well; + `vmServiceWebSocket` reads the `http://` address a game prints as well as + the `ws://…/ws` one. `pushLevel` takes a `connect` for tests. +- **`FakeGame` and `fakeAttachedRun`** in `testing.dart`: a VM service with + a flutter tool on it, or without one, that a test controls. + - **Play leaves the editor application.** `FlutterRun`, `projectRootFor` and `pushLevel` move here from `apps/flutter3d_editor/lib/src/play`, with `Watched` in place of Flutter's `ValueNotifier`, so the editor's MCP server diff --git a/packages/flutter3d_editor_play/README.md b/packages/flutter3d_editor_play/README.md index d98b841d1..24b19429d 100644 --- a/packages/flutter3d_editor_play/README.md +++ b/packages/flutter3d_editor_play/README.md @@ -31,9 +31,34 @@ if (run.state.value case PlayRunning(:final vmService)) { | `flutterDevices`, `parseFlutterDevices` | What `-d` can be given, from `flutter devices --machine` | | `pushLevel` | A saved level, sent to the running game's `ext.flutter3d.level.apply` — or, given the `base` it was saved over, as a patch to `ext.flutter3d.level.patch`, whole only when the game says the patch is stale | | `Watched` | A value and a stream of its changes, where Flutter's `ValueNotifier` cannot go | +| `AttachedRun` | A game somebody else started, by its VM service address: console, hot reload and restart, detach | +| `PlayedGame` | What `FlutterRun` and `AttachedRun` both are, for one panel over either | +| `connectVmService` | A VM service connection over a socket a browser has too | `package:flutter3d_editor_play/testing.dart` has `FakeFlutterTool`, a -`flutter run --machine` a test controls, and `fakeFlutterRun` over it. +`flutter run --machine` a test controls, and `fakeFlutterRun` over it; +`FakeGame`, a VM service a test controls, and `fakeAttachedRun` over it. + +## In a browser + +A web build has no process to start, so it imports +`package:flutter3d_editor_play/attach.dart`, which has no `dart:io` under it, +and attaches to a game that is already running: + +```dart +final game = AttachedRun('http://127.0.0.1:8181/abc=/'); // what the game printed +await game.start(); +print(await game.hotSwap()); // "Swapped in the new code" +print(describeLevelApplied(await pushLevel(game.vmService, savedDocument))); +await game.stop(); // lets go; the game keeps running +``` + +The hot reload is not the VM's: a Flutter game cannot compile its own new +code. The `flutter run` or `flutter attach` connected to it registers +`reloadSources` and `hotRestart` on its VM service, and `AttachedRun` calls +those, as DevTools does. With no tool connected it says so and what to run. +Checked against a real macOS game from the Dart VM and from Chrome, on a +page served from `localhost`. ## Why a package of its own @@ -41,8 +66,8 @@ Play starts a process and talks to it. `flutter3d_editor_core` is written never to do either, so a validator or a web build can open a level with it, and the editor's MCP server could not reach code that lived inside the editor application. Here both depend on it, and neither on the other. It is plain -Dart with `dart:io`: it runs under `dart run` and in a desktop app, and not in -a browser, where there is no process to start. +Dart with `dart:io`: it runs under `dart run` and in a desktop app, and only +`attach.dart` of it in a browser, where there is no process to start. The game side of it, the extension a level is sent to, is `LiveLevel` in `flutter3d_game`. diff --git a/packages/flutter3d_editor_play/lib/attach.dart b/packages/flutter3d_editor_play/lib/attach.dart new file mode 100644 index 000000000..00de0f33c --- /dev/null +++ b/packages/flutter3d_editor_play/lib/attach.dart @@ -0,0 +1,16 @@ +/// The half of Play a browser can run: a game somebody else started, +/// attached to by its VM service address, with its console, its hot reload +/// and restart, and a saved level sent to it. +/// +/// **No `dart:io` anywhere under it**, which `test/attach_web_test.dart` +/// checks, since a web build compiles with `dart:io` in it and fails only +/// when it is called: the web editor imports this and not +/// `flutter3d_editor_play.dart`, whose `FlutterRun` starts a process a +/// browser does not have. +library; + +export 'src/attached_run.dart'; +export 'src/level_push.dart'; +export 'src/play_state.dart'; +export 'src/vm_connect.dart'; +export 'src/watched.dart'; diff --git a/packages/flutter3d_editor_play/lib/flutter3d_editor_play.dart b/packages/flutter3d_editor_play/lib/flutter3d_editor_play.dart index c1f3bd17b..9321787e7 100644 --- a/packages/flutter3d_editor_play/lib/flutter3d_editor_play.dart +++ b/packages/flutter3d_editor_play/lib/flutter3d_editor_play.dart @@ -3,10 +3,10 @@ /// level as soon as it is saved. /// /// Used by the editor application's toolbar and by `flutter3d_editor_mcp`, -/// so an agent's Play is a person's Play. +/// so an agent's Play is a person's Play. A browser imports `attach.dart` +/// instead, which is everything here that starts no process. library; +export 'attach.dart'; export 'src/devices.dart'; export 'src/flutter_run.dart'; -export 'src/level_push.dart'; -export 'src/watched.dart'; diff --git a/packages/flutter3d_editor_play/lib/src/attached_run.dart b/packages/flutter3d_editor_play/lib/src/attached_run.dart new file mode 100644 index 000000000..78bdc2caf --- /dev/null +++ b/packages/flutter3d_editor_play/lib/src/attached_run.dart @@ -0,0 +1,288 @@ +/// `HR5` in a browser: a game somebody else started, attached to by its VM +/// service address. +/// +/// The web editor has no process to start, so it cannot run the project the +/// way the desktop one does. What it can do is open the same WebSocket +/// DevTools opens, and everything the desktop Play panel does once the game +/// is running goes over that socket anyway, except the hot reload itself, +/// which the flutter tool that started the game offers on it. +library; + +import 'dart:async'; +import 'dart:convert'; + +import 'package:vm_service/vm_service.dart'; + +import 'play_state.dart'; +import 'vm_connect.dart'; +import 'watched.dart'; + +/// A running game, attached to at [vmService] rather than started. +/// +/// **The reload is the flutter tool's, found on the socket.** A Flutter game +/// cannot recompile itself: the kernel for a hot reload is built by the +/// `flutter run` (or `flutter attach`) that is connected to it, and that tool +/// registers `reloadSources` and `hotRestart` on the game's VM service for +/// exactly this, which is how DevTools' own reload button works. So the +/// buttons call what the tool registered, and when no tool is connected they +/// say so rather than calling the VM's bare `reloadSources`, which would +/// answer that nothing changed. +/// +/// **Its console starts with what the game printed before.** The service a +/// flutter tool puts in front of the VM (DDS) keeps the game's stdout and its +/// `log` calls and replays them to whoever listens, so an editor attaching +/// late still sees the start-up — seen on a real macOS run, ticks from before +/// the attach arriving first. The replay comes a stream at a time, so prints +/// and logs from before the attach are not interleaved as they happened. A +/// VM with no DDS in front gives only what follows. +final class AttachedRun implements PlayedGame { + AttachedRun(this.vmService, {this._connect = connectVmService}); + + /// The address a person gave, in any spelling `vmServiceWebSocket` reads. + final String vmService; + + final ConnectVmService _connect; + + @override + String get title => vmService; + + @override + bool get ownsTheGame => false; + + @override + final Watched state = Watched(const PlayIdle()); + + @override + final Watched> console = Watched>(const []); + + static const int consoleLimit = 2000; + + /// The names the flutter tool registered its services under, by service: + /// `reloadSources` → `s0.reloadSources`. + final Map _services = {}; + + VmService? _service; + bool _lettingGo = false; + + /// What each output stream has written since its last newline. + final Map _partial = {}; + final List> _listening = + >[]; + + /// The services a flutter tool registers on the game it runs, by the + /// protocol's names. + static const String swapService = 'reloadSources'; + static const String restartService = 'hotRestart'; + + @override + Future start() async { + if (_service != null) return; + state.value = PlayStarting('Attaching to $vmService…'); + final VmService service; + try { + service = await _connect(vmService); + } on Object catch (error) { + _print('could not attach to $vmService: $error'); + state.value = const PlayStopped( + -1, + reason: + 'Could not attach: is the game running, and is this the ' + 'address it printed?', + ); + return; + } + final VM vm; + try { + vm = await service.getVM(); + } on Object catch (error) { + await service.dispose(); + _print('$vmService answered, but not as a VM service: $error'); + state.value = const PlayStopped( + -1, + reason: + 'Could not attach: is the game running, and is this the ' + 'address it printed?', + ); + return; + } + _service = service; + _lettingGo = false; + _listening.addAll(>[ + service.onStdoutEvent.listen((Event it) => _printed('stdout', it)), + service.onStderrEvent.listen((Event it) => _printed('stderr', it)), + service.onLoggingEvent.listen(_logged), + service.onServiceEvent.listen(_registered), + ]); + // Subscribing to `Service` replays what is already registered, so a tool + // that connected long before the editor is still found. + for (final stream in const [ + EventStreams.kStdout, + EventStreams.kStderr, + EventStreams.kLogging, + EventStreams.kService, + ]) { + try { + await service.streamListen(stream); + } on RPCError { + // Already listened to on this connection, or a VM without the + // stream: the console is then a stream shorter, nothing more. + } + } + unawaited(service.onDone.then((_) => _gone())); + state.value = PlayRunning(appId: vm.name ?? 'vm', vmService: vmService); + } + + @override + Future hotSwap() => _tool( + swapService, + isolate: true, + doing: 'swap new code into', + done: 'Swapped in the new code', + ); + + @override + Future hotRestart() => _tool( + restartService, + isolate: false, + doing: 'restart', + done: 'Restarted with the new code', + ); + + /// Lets go of the game, which keeps running. + @override + Future stop() async { + final service = _service; + if (service == null) return; + _lettingGo = true; + await service.dispose(); + await _gone(); + } + + @override + Future dispose() async { + await stop(); + await state.close(); + await console.close(); + } + + /// Calls the flutter tool's service [name]; [doing] and [done] are what + /// the console says of it. + Future _tool( + String name, { + required bool isolate, + required String doing, + required String done, + }) async { + final service = _service; + if (service == null) return null; + final method = _services[name]; + if (method == null) { + return _say( + 'nothing attached to this game can $doing it: the new code is ' + 'compiled by the flutter tool that runs the game, and none has ' + 'registered $name here. Start the game with `flutter run`, or ' + '`flutter attach` to it, and attach to the address that prints', + ); + } + try { + final response = await service.callMethod( + method, + isolateId: isolate ? await _mainIsolate(service) : null, + args: {'pause': false, 'force': false}, + ); + return _say(switch (response.json) { + {'type': 'Success'} || null => done, + final Map other => '${other['type'] ?? other}', + }); + } on RPCError catch (error) { + // A compile error is printed by the tool in its own terminal; what + // comes back here is only that the new code did not go in. + final details = error.details ?? ''; + return _say( + 'the tool refused to $doing the game: ${error.message}' + '${details.isEmpty ? '' : ': $details'}', + ); + } on Object catch (error) { + return _say('could not ask for $name: $error'); + } + } + + /// The isolate the game runs in: the one called `main` when there is one, + /// since a game may have spawned others, and the first otherwise. + Future _mainIsolate(VmService service) async { + final isolates = (await service.getVM()).isolates ?? const []; + if (isolates.isEmpty) { + throw StateError('the VM at $vmService reports no isolates'); + } + return (isolates.where((it) => it.name == 'main').firstOrNull ?? + isolates.first) + .id!; + } + + void _registered(Event event) { + switch (event) { + case Event( + kind: EventKind.kServiceRegistered, + :final String service, + :final String method, + ): + _services[service] = method; + case Event(kind: EventKind.kServiceUnregistered, :final String service): + _services.remove(service); + default: + break; + } + } + + /// **A line is printed when its newline arrives, not with each write.** The + /// engine sends a `print` as two writes, the text and then `\n` — seen on a + /// real macOS run — so a write is not a line, and taking it for one put an + /// empty line after everything the game printed. + void _printed(String stream, Event event) { + if (event.bytes case final String bytes) { + final lines = + ((_partial[stream] ?? '') + + utf8.decode(base64.decode(bytes), allowMalformed: true)) + .split('\n'); + _partial[stream] = lines.last; + lines + .take(lines.length - 1) + .map((line) => line.replaceFirst(RegExp(r'\r$'), '')) + .forEach(_print); + } + } + + void _logged(Event event) { + if (event.logRecord?.message?.valueAsString case final String message) { + _print(message); + } + } + + Future _gone() async { + if (_service == null) return; + _service = null; + _services.clear(); + // A last line the game never ended is still a line it printed. + _partial.values.where((it) => it.isNotEmpty).forEach(_print); + _partial.clear(); + for (final listening in _listening) { + await listening.cancel(); + } + _listening.clear(); + state.value = PlayStopped( + 0, + reason: _lettingGo + ? 'Detached; the game keeps running' + : 'The game closed the connection', + ); + } + + String _say(String line) { + _print(line); + return line; + } + + void _print(String line) { + console.value = appendLine(console.value, line, consoleLimit); + } +} diff --git a/packages/flutter3d_editor_play/lib/src/flutter_run.dart b/packages/flutter3d_editor_play/lib/src/flutter_run.dart index 53f84deab..9092c2110 100644 --- a/packages/flutter3d_editor_play/lib/src/flutter_run.dart +++ b/packages/flutter3d_editor_play/lib/src/flutter_run.dart @@ -13,40 +13,9 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'play_state.dart'; import 'watched.dart'; -/// Where a run is: not yet started, starting, running with a VM service to -/// attach to, or over. -sealed class PlayState { - const PlayState(); -} - -final class PlayIdle extends PlayState { - const PlayIdle(); -} - -final class PlayStarting extends PlayState { - const PlayStarting(this.message); - - /// What the tool says it is doing: "Launching lib/main.dart…". - final String message; -} - -final class PlayRunning extends PlayState { - const PlayRunning({required this.appId, required this.vmService}); - - final String appId; - - /// The game's VM service, for the timeline panel and `ext.flutter3d.*`. - final String vmService; -} - -final class PlayStopped extends PlayState { - const PlayStopped(this.exitCode); - - final int exitCode; -} - /// Starts `flutter` with [arguments] in [workingDirectory]. A parameter of /// [FlutterRun] so a test hands it a process of its own making. typedef StartFlutter = @@ -70,7 +39,7 @@ Future _startFlutter( /// go to [console] in the order they arrived, because the moment somebody /// needs this panel is the moment something went wrong, and a filtered /// console hides exactly that. -final class FlutterRun { +final class FlutterRun implements PlayedGame { FlutterRun({ required this.projectRoot, this.device, @@ -86,12 +55,20 @@ final class FlutterRun { final StartFlutter _start; + @override + String get title => projectRoot; + + @override + bool get ownsTheGame => true; + /// Where the run is; a panel rebuilds from it. + @override final Watched state = Watched(const PlayIdle()); /// Every line the run has printed, oldest first. Capped at [consoleLimit], /// dropping the oldest, so a game that logs every frame costs a bounded /// list and not the editor's memory. + @override final Watched> console = Watched>(const []); static const int consoleLimit = 2000; @@ -102,6 +79,7 @@ final class FlutterRun { final Map> _pending = >{}; /// Starts the run. Does nothing while one is already going. + @override Future start() async { if (_process != null) return; state.value = const PlayStarting('Starting flutter run…'); @@ -143,12 +121,15 @@ final class FlutterRun { /// and swaps changed models from `reassemble`. What the tool said about it, /// "Reloaded 3 of 812 libraries" or why it refused, or null when nothing is /// running. + @override Future hotSwap() => _restart(fullRestart: false); /// A hot restart: the game starts over from `main`, with the new code. + @override Future hotRestart() => _restart(fullRestart: true); /// Asks the game to stop, and the tool with it. + @override Future stop() async { final appId = _appId; if (appId == null) { @@ -163,6 +144,7 @@ final class FlutterRun { } /// Stops the run if there is one and lets go of the process. + @override Future dispose() async { await stop(); await state.close(); @@ -258,13 +240,7 @@ final class FlutterRun { } void _print(String line) { - final lines = console.value; - console.value = [ - ...lines.length >= consoleLimit - ? lines.skip(lines.length - consoleLimit + 1) - : lines, - line, - ]; + console.value = appendLine(console.value, line, consoleLimit); } } diff --git a/packages/flutter3d_editor_play/lib/src/level_push.dart b/packages/flutter3d_editor_play/lib/src/level_push.dart index a748c8c48..ef2eb668b 100644 --- a/packages/flutter3d_editor_play/lib/src/level_push.dart +++ b/packages/flutter3d_editor_play/lib/src/level_push.dart @@ -5,7 +5,8 @@ import 'dart:convert'; import 'package:flutter3d_sim/flutter3d_sim.dart'; import 'package:vm_service/vm_service.dart'; -import 'package:vm_service/vm_service_io.dart'; + +import 'vm_connect.dart'; /// The parameters `ext.flutter3d.level.apply` takes for [document]: the text /// as saved, and the digest of the level it parses to. @@ -95,10 +96,11 @@ Future> pushLevel( String vmService, String document, { String? base, + ConnectVmService connect = connectVmService, }) async { const method = 'ext.flutter3d.level.apply'; const patchMethod = 'ext.flutter3d.level.patch'; - final service = await vmServiceConnectUri(vmService); + final service = await connect(vmService); try { final vm = await service.getVM(); for (final ref in vm.isolates ?? const []) { diff --git a/packages/flutter3d_editor_play/lib/src/play_state.dart b/packages/flutter3d_editor_play/lib/src/play_state.dart new file mode 100644 index 000000000..886327503 --- /dev/null +++ b/packages/flutter3d_editor_play/lib/src/play_state.dart @@ -0,0 +1,83 @@ +/// Where a game the editor plays is, and what the editor can do to it, +/// whether the editor started it or found it already running. +library; + +import 'watched.dart'; + +/// Where a run is: not yet started, starting, running with a VM service to +/// attach to, or over. +sealed class PlayState { + const PlayState(); +} + +final class PlayIdle extends PlayState { + const PlayIdle(); +} + +final class PlayStarting extends PlayState { + const PlayStarting(this.message); + + /// What the tool says it is doing: "Launching lib/main.dart…". + final String message; +} + +final class PlayRunning extends PlayState { + const PlayRunning({required this.appId, required this.vmService}); + + final String appId; + + /// The game's VM service, for the timeline panel and `ext.flutter3d.*`. + final String vmService; +} + +final class PlayStopped extends PlayState { + const PlayStopped(this.exitCode, {this.reason}); + + final int exitCode; + + /// What ended it, when an exit code says nothing: an attached game has + /// none to give, since the editor let go of it or it closed the socket. + final String? reason; +} + +/// A game the editor plays: one it started with `flutter run --machine` +/// (`FlutterRun`) or one it attached to by its VM service (`AttachedRun`). +/// +/// **One panel for both.** The browser has no process to start, so there the +/// game is always one somebody else ran; what a person does to it is the +/// same — read its console, swap in new code, open its timeline, send it the +/// saved level — and a second panel would be a second place for that to +/// drift. +abstract interface class PlayedGame { + /// What the panel's title says: the project, or the address attached to. + String get title; + + /// Whether [stop] ends the game, or only lets go of it. + bool get ownsTheGame; + + Watched get state; + + /// Every line the game has printed, oldest first, capped. + Watched> get console; + + /// Runs the game, or attaches to it again. Does nothing while it goes. + Future start(); + + /// A hot reload: what was said about it, or null when nothing is running. + Future hotSwap(); + + /// A hot restart: what was said about it, or null when nothing is running. + Future hotRestart(); + + /// Ends the game when [ownsTheGame], and lets go of it otherwise. + Future stop(); + + Future dispose(); +} + +/// The lines of the console a session keeps, with [line] added and the +/// oldest dropped past [limit]. +List appendLine(List lines, String line, int limit) => [ + ...lines.length >= limit ? lines.skip(lines.length - limit + 1) : lines, + line, +]; diff --git a/packages/flutter3d_editor_play/lib/src/vm_connect.dart b/packages/flutter3d_editor_play/lib/src/vm_connect.dart new file mode 100644 index 000000000..bcac19f97 --- /dev/null +++ b/packages/flutter3d_editor_play/lib/src/vm_connect.dart @@ -0,0 +1,54 @@ +/// A running game's VM service, reached from a desktop and from a browser. +library; + +import 'dart:async'; + +import 'package:vm_service/vm_service.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +/// Connects to a VM service. A parameter of what attaches to a game, so a +/// test hands it a service of its own making. +typedef ConnectVmService = Future Function(String uri); + +/// The WebSocket address of a VM service, from whatever a person pasted: the +/// `http://` address a game prints on startup, the `ws://…/ws` one +/// `flutter run --machine` reports, with or without a trailing slash. +String vmServiceWebSocket(String uri) { + final trimmed = uri.trim(); + final withScheme = trimmed + .replaceFirst(RegExp('^http://'), 'ws://') + .replaceFirst(RegExp('^https://'), 'wss://'); + return withScheme.endsWith('/ws') + ? withScheme + : '${withScheme.replaceFirst(RegExp(r'/$'), '')}/ws'; +} + +/// Connects to the VM service at [uri], in any of [vmServiceWebSocket]'s +/// spellings. +/// +/// **Not `vmServiceConnectUri`.** That one opens a `dart:io` `WebSocket`, +/// which a browser does not have, and the web editor reaches a game through +/// nothing else; `WebSocketChannel` is the browser's socket there and +/// `dart:io`'s on a desktop, so both editors connect through the same lines. +Future connectVmService(String uri) async { + final address = vmServiceWebSocket(uri); + final channel = WebSocketChannel.connect(Uri.parse(address)); + await channel.ready; + final closed = Completer(); + final incoming = StreamController(); + channel.stream.listen( + incoming.add, + onError: incoming.addError, + onDone: () { + if (!closed.isCompleted) closed.complete(); + unawaited(incoming.close()); + }, + ); + return VmService( + incoming.stream, + channel.sink.add, + disposeHandler: channel.sink.close, + streamClosed: closed.future, + wsUri: address, + ); +} diff --git a/packages/flutter3d_editor_play/lib/testing.dart b/packages/flutter3d_editor_play/lib/testing.dart index d2d24f86d..31220e16c 100644 --- a/packages/flutter3d_editor_play/lib/testing.dart +++ b/packages/flutter3d_editor_play/lib/testing.dart @@ -1,13 +1,177 @@ /// A `flutter run --machine` a test controls, for anything that drives a -/// [FlutterRun] and wants to say what the tool answers. +/// [FlutterRun] and wants to say what the tool answers, and a game's VM +/// service a test controls, for an [AttachedRun]. library; import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:vm_service/vm_service.dart'; + import 'flutter3d_editor_play.dart'; +/// A running game's VM service, answering over [service] as the real one +/// answers over a socket, and recording every call it is sent. +/// +/// The game has one isolate, `main`, whose extensions are [extensions]. A +/// flutter tool attached to it is [registered]: its services by name, and +/// what each answers in [toolAnswers], `null` for success or an +/// [RPCError] to refuse. +final class FakeGame { + FakeGame({ + Map? registered, + this.extensions = const [], + }) : registered = registered ?? {}; + + /// The flutter tool `flutter run` attaches, as DevTools finds it. + static Map flutterTool() => { + 'reloadSources': 's0.reloadSources', + 'hotRestart': 's0.hotRestart', + }; + + final Map registered; + final List extensions; + + /// What a registered method answers, by its registered name. + final Map toolAnswers = {}; + + /// What an extension answers, by its name. + final Map> extensionAnswers = + >{}; + + /// Every call, as `{'method': …, 'params': …}`, in order. + final List> calls = >[]; + + final StreamController _out = StreamController(); + late final VmService service = VmService(_out.stream, _receive); + + static const String isolateId = 'isolates/1'; + + /// The game printing [text] to stdout. + void prints(String text) => _event('Stdout', { + 'kind': 'WriteEvent', + 'bytes': base64.encode(utf8.encode(text)), + }); + + /// The game calling `dart:developer`'s `log` with [message]. + void logs(String message) => _event('Logging', { + 'kind': 'Logging', + 'logRecord': { + 'type': 'LogRecord', + 'message': { + 'type': '@Instance', + 'id': 'objects/1', + 'kind': 'String', + 'valueAsString': message, + }, + 'time': 0, + 'level': 800, + 'sequenceNumber': 0, + 'loggerName': { + 'type': '@Instance', + 'id': 'objects/2', + 'kind': 'String', + 'valueAsString': '', + }, + 'zone': {'type': '@Instance', 'kind': 'Null'}, + 'error': {'type': '@Instance', 'kind': 'Null'}, + 'stackTrace': {'type': '@Instance', 'kind': 'Null'}, + }, + }); + + /// The game exiting, which closes the socket. + Future exits() => _out.close(); + + void _receive(String message) { + final request = jsonDecode(message) as Map; + final method = request['method']! as String; + final params = request['params'] as Map? ?? const {}; + calls.add({'method': method, 'params': params}); + final id = request['id']; + void reply(Map result) => + _send({'jsonrpc': '2.0', 'id': id, 'result': result}); + switch (method) { + case 'getVM': + reply({ + 'type': 'VM', + 'name': 'game', + 'isolates': [ + { + 'type': '@Isolate', + 'id': isolateId, + 'name': 'main', + 'number': '1', + 'isSystemIsolate': false, + }, + ], + }); + case 'getIsolate': + reply({ + 'type': 'Isolate', + 'id': isolateId, + 'name': 'main', + 'number': '1', + 'isSystemIsolate': false, + 'extensionRPCs': extensions, + }); + case 'streamListen': + reply(const {'type': 'Success'}); + if (params['streamId'] == 'Service') { + for (final MapEntry(key: name, value: registeredAs) + in registered.entries) { + _event('Service', { + 'kind': 'ServiceRegistered', + 'service': name, + 'method': registeredAs, + 'alias': 'Flutter Tools', + }); + } + } + case final String name when name.startsWith('ext.'): + reply(extensionAnswers[name] ?? const {}); + case final String name when registered.containsValue(name): + switch (toolAnswers[name]) { + case final RPCError error: + _send({ + 'jsonrpc': '2.0', + 'id': id, + 'error': error.toMap(), + }); + case null: + reply(const {'type': 'Success'}); + } + default: + _send({ + 'jsonrpc': '2.0', + 'id': id, + 'error': { + 'code': -32601, + 'message': 'Method not found', + }, + }); + } + } + + void _event(String stream, Map event) => + _send({ + 'jsonrpc': '2.0', + 'method': 'streamNotify', + 'params': { + 'streamId': stream, + 'event': {'type': 'Event', 'timestamp': 0, ...event}, + }, + }); + + void _send(Map message) { + if (!_out.isClosed) _out.add(jsonEncode(message)); + } +} + +/// An [AttachedRun] to [game]. +AttachedRun fakeAttachedRun(FakeGame game, {String uri = 'ws://game/ws'}) => + AttachedRun(uri, connect: (String _) async => game.service); + /// A `flutter run --machine` that says what [say] is given and records what /// it is sent. final class FakeFlutterTool implements Process { diff --git a/packages/flutter3d_editor_play/pubspec.yaml b/packages/flutter3d_editor_play/pubspec.yaml index 7de881921..96ed5d23f 100644 --- a/packages/flutter3d_editor_play/pubspec.yaml +++ b/packages/flutter3d_editor_play/pubspec.yaml @@ -21,6 +21,11 @@ dependencies: # The running game's VM service, where `ext.flutter3d.level.apply` is. vm_service: ^15.3.0 + # The socket to that service, in a browser as well as on a desktop: + # `vmServiceConnectUri` opens a `dart:io` `WebSocket`, and the web editor + # attaches to a game through nothing else. See `src/vm_connect.dart`. + web_socket_channel: ^3.0.0 + dev_dependencies: test: ^1.25.0 flutter_lints: ^6.0.0 @@ -33,4 +38,6 @@ dev_dependencies: # editor application had this code under `lib/src/play`, where the editor's # MCP server, which wants the same Play for an agent, could not reach it # without depending on an application. Here both depend on it, and neither -# depends on the other. +# depends on the other. The `dart:io` stays out of `lib/attach.dart` and what +# it exports, which is the part of Play a web build has: attaching to a game +# that is already running. diff --git a/packages/flutter3d_editor_play/test/attach_web_test.dart b/packages/flutter3d_editor_play/test/attach_web_test.dart new file mode 100644 index 000000000..5ebd4edb8 --- /dev/null +++ b/packages/flutter3d_editor_play/test/attach_web_test.dart @@ -0,0 +1,74 @@ +/// `attach.dart` is the part of Play a browser has, so nothing under it may +/// import `dart:io`. +/// +/// dart test test/attach_web_test.dart +library; + +import 'dart:io'; + +import 'package:test/test.dart'; + +final RegExp _directive = RegExp( + r'''^(?:import|export)\s+['"]([^'"]+)['"]''', + multiLine: true, +); + +void main() { + test('nothing attach.dart reaches in this repository imports dart:io', () { + // Workspace packages are followed; a hosted one is taken at its word, + // since `web_socket_channel` itself picks `dart:io` by a conditional + // import that a browser build never takes. + final packages = _workspacePackages(); + final seen = {}; + final reachesIo = []; + + void visit(File file) { + if (!seen.add(file.absolute.path)) return; + for (final match in _directive.allMatches(file.readAsStringSync())) { + final target = match.group(1)!; + if (target == 'dart:io') reachesIo.add(file.path); + final next = switch (target) { + _ when target.startsWith('dart:') => null, + _ when target.startsWith('package:') => () { + final [name, ...rest] = target.substring(8).split('/'); + final root = packages[name]; + return root == null ? null : File('$root/lib/${rest.join('/')}'); + }(), + _ => File.fromUri(file.uri.resolve(target)), + }; + if (next != null) visit(next); + } + } + + visit(File('lib/attach.dart')); + + // Mutation: `level_push.dart` back on `vmServiceConnectUri`, or + // `attach.dart` exporting `flutter_run.dart`, puts `dart:io` under it. + // A web build still compiles then — dart2js takes `dart:io` and throws + // `UnsupportedError` from it at run time — so this test is the only + // thing that notices before somebody presses Send in a browser. + expect(reachesIo, isEmpty); + expect(seen.any((it) => it.endsWith('attached_run.dart')), isTrue); + expect(seen.any((it) => it.contains('flutter3d_sim')), isTrue); + }); +} + +/// Every package of this workspace, by name, to its directory. +Map _workspacePackages() { + final root = Directory('../..').absolute; + return { + for (final pubspec in [ + ...Directory('${root.path}/packages') + .listSync() + .whereType() + .map((d) => File('${d.path}/pubspec.yaml')), + ]) + if (pubspec.existsSync()) + if (RegExp( + r'^name:\s*(\S+)', + multiLine: true, + ).firstMatch(pubspec.readAsStringSync()) + case final match?) + match.group(1)!: pubspec.parent.path, + }; +} diff --git a/packages/flutter3d_editor_play/test/attached_run_test.dart b/packages/flutter3d_editor_play/test/attached_run_test.dart new file mode 100644 index 000000000..68ab22d04 --- /dev/null +++ b/packages/flutter3d_editor_play/test/attached_run_test.dart @@ -0,0 +1,211 @@ +/// `HR5` in a browser: a game attached to by its VM service, with no +/// process of the editor's own, read, reloaded, let go of and sent a level. +/// +/// dart test test/attached_run_test.dart +library; + +import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; +import 'package:flutter3d_editor_play/testing.dart'; +import 'package:test/test.dart'; +import 'package:vm_service/vm_service.dart'; + +const String _document = '{"version": 1, "name": "yard", "fogDensity": 0.02}'; + +/// Lets the fake's replies and events cross the stream to the run. +Future _settle() => Future.delayed(Duration.zero); + +Future<(FakeGame, AttachedRun)> _attached({ + Map? registered, +}) async { + final game = FakeGame(registered: registered ?? FakeGame.flutterTool()); + final run = fakeAttachedRun(game); + await run.start(); + await _settle(); + return (game, run); +} + +void main() { + test('attaching is running, at the address given', () async { + final (_, run) = await _attached(); + + expect( + run.state.value, + isA().having( + (it) => it.vmService, + 'vmService', + 'ws://game/ws', + ), + ); + expect(run.ownsTheGame, isFalse); + }); + + test('a line is what ends in a newline, not each write', () async { + final (game, run) = await _attached(); + + // What a real macOS run sends for `print('tick 1')`: the text, then the + // newline on its own. + game + ..prints('flutter: tick 1') + ..prints('\n') + ..prints('flutter: half'); + await _settle(); + + // Mutation: taking each write for a line puts an empty line after every + // `print` and shows `half` before it is finished. + expect(run.console.value, ['flutter: tick 1']); + + await game.exits(); + await _settle(); + expect(run.console.value.last, 'flutter: half'); + }); + + test('the console is what the game prints and logs', () async { + final (game, run) = await _attached(); + + game + ..prints('frame 1\nframe 2\n') + ..logs('level loaded'); + await _settle(); + + // Mutation: listening to `Stdout` alone drops what `log()` writes, and + // a game that logs through `dart:developer` shows an empty console. + expect(run.console.value, ['frame 1', 'frame 2', 'level loaded']); + }); + + test( + 'a hot reload is the flutter tool\'s, called in the main isolate', + () async { + final (game, run) = await _attached(); + + expect(await run.hotSwap(), 'Swapped in the new code'); + + // Mutation: calling the VM's own `reloadSources` instead of the name the + // tool registered reaches a VM that cannot compile, which answers that + // nothing changed while the game keeps its old code. + final call = game.calls.last; + expect(call['method'], 's0.reloadSources'); + expect(call['params'], containsPair('isolateId', FakeGame.isolateId)); + + expect(await run.hotRestart(), 'Restarted with the new code'); + expect(game.calls.last['method'], 's0.hotRestart'); + }, + ); + + test( + 'with no tool attached to the game, the reload says what to do', + () async { + final (game, run) = await _attached(registered: {}); + final before = game.calls.length; + + final said = await run.hotSwap(); + + // Mutation: falling back to the bare `reloadSources` would send a call + // here and report success for a reload that did not happen. + expect(game.calls, hasLength(before)); + expect(said, contains('flutter run')); + expect(said, contains('flutter attach')); + expect(run.console.value.last, said); + }, + ); + + test('a reload the tool refuses is said as refused', () async { + final (game, run) = await _attached(); + game.toolAnswers['s0.reloadSources'] = RPCError( + 's0.reloadSources', + RPCErrorKind.kInternalError.code, + 'Unable to reload sources', + ); + + final said = await run.hotSwap(); + + expect(said, startsWith('the tool refused to swap new code into the game')); + expect(said, contains('Unable to reload sources')); + }); + + test('stopping lets go of the game and does not end it', () async { + final (game, run) = await _attached(); + + await run.stop(); + + // Mutation: an `exit` or `kill` on stop would end a game somebody else + // started; the only calls are the attach's own. + expect(game.calls.map((it) => it['method']).toSet(), { + 'getVM', + 'streamListen', + }); + expect( + run.state.value, + isA().having( + (it) => it.reason, + 'reason', + contains('keeps running'), + ), + ); + expect(await run.hotSwap(), isNull); + }); + + test('a game that exits is a run that stopped', () async { + final (game, run) = await _attached(); + + await game.exits(); + await _settle(); + + expect( + run.state.value, + isA().having( + (it) => it.reason, + 'reason', + contains('closed'), + ), + ); + }); + + test('an address with nothing behind it is a stop with a reason', () async { + final run = AttachedRun( + 'http://127.0.0.1:1/', + connect: (String _) async => throw StateError('connection refused'), + ); + + await run.start(); + + expect(run.state.value, isA()); + expect(run.console.value.single, contains('connection refused')); + }); + + test('a saved level reaches the isolate that takes levels', () async { + final game = FakeGame(extensions: ['ext.flutter3d.level.apply']) + ..extensionAnswers['ext.flutter3d.level.apply'] = { + 'type': '_extensionType', + 'swappedAt': 12, + 'diff': { + 'simulation': ['brushes'], + }, + }; + + final answer = await pushLevel( + 'ws://game/ws', + _document, + connect: (String _) async => game.service, + ); + + expect( + describeLevelApplied(answer), + 'the game replayed from step 12 with the new brushes', + ); + final call = game.calls.last; + expect(call['method'], 'ext.flutter3d.level.apply'); + expect(call['params'], containsPair('document', _document)); + }); + + test('every spelling of the address is the same socket', () { + for (final spelling in [ + 'http://127.0.0.1:8181/abc=/', + 'http://127.0.0.1:8181/abc=', + 'ws://127.0.0.1:8181/abc=/ws', + ' ws://127.0.0.1:8181/abc=/ ', + ]) { + expect(vmServiceWebSocket(spelling), 'ws://127.0.0.1:8181/abc=/ws'); + } + expect(vmServiceWebSocket('https://host/x/'), 'wss://host/x/ws'); + }); +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 8b7a0ee03..8e791b525 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10999 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11012 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 4138d83bc..b4401df88 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10999 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11012 tests need a GPU. --- # Testing -10999 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11012 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -21,7 +21,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 70 | | `apps/flutter3d_demo_platformer` | 204 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 357 | | `apps/flutter3d_demo_strategy` | 50 | -| `apps/flutter3d_editor` | 226 | | `apps/flutter3d_demo_arcade` | 20 | +| `apps/flutter3d_editor` | 227 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | @@ -31,7 +31,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game` | 287 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | -| `flutter3d_editor_play` | 14 | | `flutter3d_net` | 15 | +| `flutter3d_editor_play` | 26 | | `flutter3d_net` | 15 | | | | | `flutter3d_net_webrtc` | 2 | | | | | `flutter3d_sim_mcp` | 26 | | `flutter3d_mcp_kit` | 2 | | `flutter3d_build` | 136 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10973 rather than 10999: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10986 rather than 11012: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index f770f15ff..5fccea4aa 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -288,8 +288,27 @@ with `dart:io`, `Watched` in place of `ValueNotifier`), so the editor's MCP server runs the same code: `play`, `play_status`, `play_swap`, `play_stop` and `play_devices`, and a `save` while the game runs sends it the level. The panel picks the device between runs from `flutter devices --machine`. -*Left:* the web editor, which has no process to start and will attach -instead. +*Done without a process:* `AttachedRun` plays a game somebody else started, +by its VM service address, and `package:flutter3d_editor_play/attach.dart` +is that half of Play with no `dart:io` under it (a test walks the imports). +The console comes off the `Stdout`, `Stderr` and `Logging` streams, with what +the game printed before the attach, which DDS replays; hot reload and restart +call the `reloadSources` and `hotRestart` services the flutter tool +registers on the game, as DevTools does, and say what to run when no tool is +connected; stop is a detach. `connectVmService` (over `web_socket_channel`) +replaces `vmServiceConnectUri` in `pushLevel`, the timeline client and the +live materials, so a save, the timeline and a dragged material reach the +game from a browser too. `PlayScreen` shows any `PlayedGame` and imports +only `attach.dart`, the device picker moved to a file of its own, and the +editor's attach button now opens it rather than only the timeline. Checked +against a real macOS game from the Dart VM and from Chrome on a `localhost` +page: attach, console with history, swap (new code after restart), restart, +the level call answered, detach with the game still running. +*Left:* the web build of the editor itself (P11: a web backend, documents +without the disk, `main.dart`'s `dart:io` behind the Play wiring); and from +an `https` page, a `ws://127.0.0.1` socket is mixed content, which browsers +treat differently for loopback; only a `localhost` page has been checked, +and the published web editor needs it checked from its own address. ## 4. Parity with flutter_scene 0.24 @@ -508,7 +527,8 @@ component inspector, console, command palette, render-graph inspector; published like the modeler: a web build, which cannot Play another project, and desktop downloads for macOS, Windows and Linux with full Play (decision 24). flutter_scene's editor is not published, which is the chance to be -first. +first. The web build's Play is ready in `flutter3d_editor_play/attach.dart` +(HR5); the build itself is not. **P12. Agent tooling (M).** In a running game, not only the 320×200 CPU render: `list_draws`, `get_draw`, `list_passes`, `pass_output`, `read_pixel`, diff --git a/tool/structure/detectors.dart b/tool/structure/detectors.dart index 244431aa5..e7222ad76 100644 --- a/tool/structure/detectors.dart +++ b/tool/structure/detectors.dart @@ -73,6 +73,15 @@ const List kGenreWords = [ 'lap', 'nitro', 'chicane', 'pit stop', ]; +/// Names another program chose, which this repository calls by them and +/// cannot rename, each exempt from [kGenreWords] whole and only whole. +/// +/// * `reloadSources` is the service the flutter tool registers on a game's +/// VM service for a hot reload (`flutter3d_editor_play`'s `AttachedRun` +/// calls it, as DevTools does). Spelling it any other way is a call that +/// finds nothing; `reloadAll` of our own still fires. +const Set kProtocolNames = {'reloadSources'}; + final RegExp _camel = RegExp(r'[A-Z]+(?![a-z])|[A-Z][a-z0-9]*|[a-z0-9]+'); final RegExp _identifier = RegExp(r'[A-Za-z_][A-Za-z0-9_]*'); @@ -286,6 +295,7 @@ bool _sameWords(List a, List b) { List genreWordsIn(String source, {List words = kGenreWords}) { final found = []; for (final match in _identifier.allMatches(codeOf(source))) { + if (kProtocolNames.contains(match.group(0))) continue; final said = _words(match.group(0)!); for (final forbidden in words) { final wanted = forbidden.split(' '); @@ -479,6 +489,17 @@ List proveDetectorsWork() { 'a line comment', 'prose explaining the rule must not break it', ); + quiet( + 'genre words', + genreWordsIn("const swapService = 'reloadSources';").isEmpty, + 'a protocol method name', + 'a name another program chose is called by its name', + ); + fires( + 'genre words', + genreWordsIn('Future reloadAll() async {}').isNotEmpty, + 'reloadAll (one of ours)', + ); quiet( 'genre words', genreWordsIn('/* a boss with a gun */ int x = 0;').isEmpty, From f7dbaf42d638734877473be7498b6bcf65f3d825 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:17:50 +0300 Subject: [PATCH 040/141] An actor decides by a tree that is written down Behaviour trees and utility choices are data, read by BehaviourTree.read and shared by every actor that runs them; leaves are registered by kind. What each actor remembers between decisions lives in a Blackboard component on its entity, so a snapshot, a rewind or a save brings a decision back half made, and a board restored under a changed tree starts over. Values are JSON-shaped only. BehaviourOverlay draws the path each tree last took. --- ARCHITECTURE.md | 2 +- README.md | 2 +- packages/flutter3d_game/CHANGELOG.md | 5 + .../flutter3d_game/lib/flutter3d_game.dart | 1 + .../lib/src/visuals/behaviour_overlay.dart | 68 ++ .../test/behaviour_overlay_test.dart | 74 +++ packages/flutter3d_sim/CHANGELOG.md | 10 + packages/flutter3d_sim/lib/flutter3d_sim.dart | 2 + .../lib/src/actors/actor_components.dart | 4 + .../lib/src/actors/behaviour_brain.dart | 100 +++ .../lib/src/actors/behaviour_kinds.dart | 391 +++++++++++ .../lib/src/actors/behaviour_tree.dart | 562 ++++++++++++++++ .../lib/src/actors/blackboard.dart | 221 +++++++ .../test/behaviour_tree_test.dart | 619 ++++++++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- tasks/0.9-engine-roadmap.md | 21 + tool/structure/repository.dart | 7 + 18 files changed, 2093 insertions(+), 8 deletions(-) create mode 100644 packages/flutter3d_game/lib/src/visuals/behaviour_overlay.dart create mode 100644 packages/flutter3d_game/test/behaviour_overlay_test.dart create mode 100644 packages/flutter3d_sim/lib/src/actors/behaviour_brain.dart create mode 100644 packages/flutter3d_sim/lib/src/actors/behaviour_kinds.dart create mode 100644 packages/flutter3d_sim/lib/src/actors/behaviour_tree.dart create mode 100644 packages/flutter3d_sim/lib/src/actors/blackboard.dart create mode 100644 packages/flutter3d_sim/test/behaviour_tree_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b8a23ccda..8abafe0f1 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11012 tests** across 43 packages and 10 applications | +| Unit tests | **11027 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 673adf1c2..2d59cbba5 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11012 tests across forty-three packages and nine applications. The +There are 11027 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/packages/flutter3d_game/CHANGELOG.md b/packages/flutter3d_game/CHANGELOG.md index 4ae67f0c8..2731cbe51 100644 --- a/packages/flutter3d_game/CHANGELOG.md +++ b/packages/flutter3d_game/CHANGELOG.md @@ -16,6 +16,11 @@ patch made against another version is answered with `LevelPatch.staleCode`, which tells the sender to send the whole level; a patched level that does not build is refused as an ordinary error. +- **`BehaviourOverlay` draws what every tree last decided**: a stroke per + node on the running path above each actor, coloured by how it came out, and + a line to where its leaf is taking it; `describe()` gives the same path by + name. It reads boards and never makes one, so switching it on cannot change + a snapshot. - **`replayAfterHotSwap` asks the question a hot reload leaves open on its own.** After every `HotSwap` it lives the last seconds again under the new code and says whether they came out the same or where they parted, in the diff --git a/packages/flutter3d_game/lib/flutter3d_game.dart b/packages/flutter3d_game/lib/flutter3d_game.dart index fef60945e..05f5fb17f 100644 --- a/packages/flutter3d_game/lib/flutter3d_game.dart +++ b/packages/flutter3d_game/lib/flutter3d_game.dart @@ -68,6 +68,7 @@ export 'src/screens/tap_to_restart.dart'; export 'src/screens/touch_platform.dart'; export 'src/screens/volumes.dart'; export 'src/visuals/actor_visuals.dart'; +export 'src/visuals/behaviour_overlay.dart'; export 'src/visuals/fixture_visuals.dart'; export 'src/visuals/sound_occlusion.dart'; export 'src/walk/level_walk.dart'; diff --git a/packages/flutter3d_game/lib/src/visuals/behaviour_overlay.dart b/packages/flutter3d_game/lib/src/visuals/behaviour_overlay.dart new file mode 100644 index 000000000..505524372 --- /dev/null +++ b/packages/flutter3d_game/lib/src/visuals/behaviour_overlay.dart @@ -0,0 +1,68 @@ +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:vector_math/vector_math.dart'; + +/// What every actor running a behaviour tree last decided, drawn where it +/// stands. +/// +/// **Read-only, and that is the condition for having it at all.** It reads +/// the boards through `BehaviourBrain.pathOf` and `goalOf`, which never make +/// one, so switching the overlay on cannot change a snapshot — an overlay that +/// did would make a run under the debugger a different run. +/// +/// Above each actor, one short upright stroke per node on the path, root +/// lowest, coloured by how the node came out: amber running, green success, +/// red failure. A line from the actor to where its running leaf is taking it. +/// Lines cannot carry words, so the names are [describe]'s, for whatever text +/// the game shows beside the picture. +/// +/// Hand [draw] to `Renderer.debugLines`. +final class BehaviourOverlay { + BehaviourOverlay(this.actors); + + final ActorSystem actors; + + /// How far apart the strokes are, and how tall, in metres. + static const double _pitch = 0.16; + static const double _stroke = 0.12; + + /// A new vector each call: a shared one is a colour anybody can scale. + static Vector4 colourOf(BehaviourStatus status) => switch (status) { + BehaviourStatus.running => Vector4(1.0, 0.75, 0.1, 1.0), + BehaviourStatus.success => Vector4(0.3, 0.9, 0.35, 1.0), + BehaviourStatus.failure => Vector4(0.95, 0.25, 0.2, 1.0), + }; + + void draw(DebugDraw lines) { + for (final actor in actors.actors) { + final body = actor.body; + if (body == null || !actor.isAlive) continue; + final path = BehaviourBrain.pathOf(actor); + if (path.isEmpty) continue; + final at = body.position; + final top = at.y + body.halfExtents.y + 0.2; + for (final (i, step) in path.indexed) { + final y = top + i * _pitch; + lines.addLine( + Vector3(at.x, y, at.z), + Vector3(at.x, y + _stroke, at.z), + colourOf(step.status), + ); + } + final goal = BehaviourBrain.goalOf(actor, actors); + if (goal != null) lines.addLine(at, goal, colourOf(path.last.status)); + } + } + + /// One line per actor running a tree: who, and the path by name — + /// `a1: utility › patrol › goTo (running)`. + List describe() => [ + for (final actor in actors.actors) + if (BehaviourBrain.pathOf(actor) case final path when path.isNotEmpty) + [ + '${actor.name ?? '#${actor.ordinal}'}:', + path.map((step) => step.label).join(' › '), + '(${path.last.status.name})', + ].join(' '), + ]; +} diff --git a/packages/flutter3d_game/test/behaviour_overlay_test.dart b/packages/flutter3d_game/test/behaviour_overlay_test.dart new file mode 100644 index 000000000..0df8fdf5c --- /dev/null +++ b/packages/flutter3d_game/test/behaviour_overlay_test.dart @@ -0,0 +1,74 @@ +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +void main() { + ActorSystem patrolling() { + final world = CollisionWorld() + ..addBox(Vector3(0.0, -0.5, 0.0), Vector3(40.0, 1.0, 40.0)) + ..update(); + final system = ActorSystem(world: world, random: GameRandom(1)); + final tree = BehaviourTree.read({ + 'kind': 'sequence', + 'name': 'patrol', + 'children': [ + {'kind': 'goTo', 'key': 'post'}, + {'kind': 'wait', 'seconds': 1}, + ], + }, BehaviourKinds()).tree!; + final actor = system.spawn( + body: CharacterController(world: world, position: Vector3(0, 0.9, 0)), + brain: BehaviourBrain(tree), + name: 'guard', + ); + system.entities.set( + actor.entity, + Blackboard( + values: { + 'post': [5.0, 0.9, 0.0], + }, + ), + ); + return system; + } + + test('draws a stroke per node on the path and a line to the goal', () { + final system = patrolling(); + system + ..beginStep() + // Inside the close range, so the actor thinks on the first step. + ..step(1 / 60, focus: Vector3(0, 0.9, 10)); + final lines = DebugDraw(); + BehaviourOverlay(system).draw(lines); + // Mutation: drawing only the leaf leaves the sequence out, and the + // picture no longer says which branch the leaf belongs to. + expect(lines.lineCount, 3); + expect(BehaviourOverlay(system).describe(), [ + 'guard: patrol › goTo (running)', + ]); + }); + + test('an actor that has not decided yet is not drawn, and gets no board', () { + final system = patrolling(); + final fresh = system.spawn( + body: CharacterController( + world: system.world, + position: Vector3(3, 0.9, 3), + ), + brain: BehaviourBrain( + BehaviourTree.read({ + 'kind': 'wait', + 'seconds': 1, + }, BehaviourKinds()).tree!, + ), + ); + final lines = DebugDraw(); + BehaviourOverlay(system).draw(lines); + expect(lines.lineCount, 0); + // Mutation: an overlay that made a board to read it would put a + // component in the next snapshot just by being switched on. + expect(system.entities.has(fresh.entity), isFalse); + }); +} diff --git a/packages/flutter3d_sim/CHANGELOG.md b/packages/flutter3d_sim/CHANGELOG.md index fbfd0a3d2..1a5c2baf4 100644 --- a/packages/flutter3d_sim/CHANGELOG.md +++ b/packages/flutter3d_sim/CHANGELOG.md @@ -18,6 +18,16 @@ it names, and a result that is not the level it was meant to make; when it applies, it says what changed without comparing the two documents. `LevelPatch.staleCode` is the error a game answers a refused patch with. +- **Behaviour trees and utility choices as data.** `BehaviourTree.read` + takes a JSON document of `sequence`, `selector`, `utility`, `invert`, + `alwaysSucceed`, `cooldown` and leaves, and answers a tree or every + problem with where it is. Leaves and utility considerations are registered + by kind in `BehaviourKinds`, which comes with the ones the engine's `Mind` + can already do (`goToFocus`, `goTo`, `wait`, `seesFocus`, `check`, `set`, + `markFocus`, …). `BehaviourBrain` runs a tree; everything it knows is a + `Blackboard` component, so a snapshot or a rewind brings a decision back + half made. A board ticked by another tree starts again rather than resuming + at node numbers that now name something else. - **A number tuned while the game runs is on the tape.** `InputState.tune` sets a tunable for one step, `InputFrame.tunes` records it, playback applies it, and `Tunables` is the step's side: named values with defaults, diff --git a/packages/flutter3d_sim/lib/flutter3d_sim.dart b/packages/flutter3d_sim/lib/flutter3d_sim.dart index c40acf84a..196d543cb 100644 --- a/packages/flutter3d_sim/lib/flutter3d_sim.dart +++ b/packages/flutter3d_sim/lib/flutter3d_sim.dart @@ -45,6 +45,8 @@ export 'src/actors/actor.dart'; export 'src/actors/actor_components.dart'; export 'src/actors/actor_hurt.dart'; export 'src/actors/actor_system.dart'; +export 'src/actors/behaviour_brain.dart'; +export 'src/actors/behaviour_tree.dart'; export 'src/actors/brain.dart'; export 'src/actors/damageable.dart'; export 'src/actors/health.dart'; diff --git a/packages/flutter3d_sim/lib/src/actors/actor_components.dart b/packages/flutter3d_sim/lib/src/actors/actor_components.dart index 80b27584b..96a2a8e0e 100644 --- a/packages/flutter3d_sim/lib/src/actors/actor_components.dart +++ b/packages/flutter3d_sim/lib/src/actors/actor_components.dart @@ -27,6 +27,7 @@ library; import 'package:flutter3d_physics/flutter3d_physics.dart'; import '../ecs/ecs_world.dart'; +import 'blackboard.dart'; import 'brain.dart'; import 'health.dart'; @@ -130,4 +131,7 @@ void registerActorComponents(EcsWorld entities) { if (data is Map) value.brain.restore(data.cast()); }, ); + // With the rest, so that a behaviour tree's state is in the save of any + // world an actor system stands on, whoever made the world. + registerBlackboard(entities); } diff --git a/packages/flutter3d_sim/lib/src/actors/behaviour_brain.dart b/packages/flutter3d_sim/lib/src/actors/behaviour_brain.dart new file mode 100644 index 000000000..a8c372c54 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/actors/behaviour_brain.dart @@ -0,0 +1,100 @@ +/// A [Brain] that runs a behaviour tree, keeping everything it knows on the +/// actor's [Blackboard]. +/// +/// ## Nothing on the brain +/// +/// [save] is the default empty map, and that is the design rather than an +/// omission. Where the actor is in its tree, what it heard and when, how long +/// it has waited: all of it is a component, so the ECS save writes it and a +/// rewind restores it without this class being asked. A brain that kept even +/// one field would put that field outside every snapshot that forgot to call +/// `save` on it. +/// +/// ## What it notices for the tree +/// +/// The engine tells a brain about noise and pain, and a tree cannot override +/// a method. So this writes them down, where a leaf or a consideration can +/// read them: [heard] (where), [heardAt] and [hurtAt] (the board's clock when +/// it happened). `since` is the consideration that turns "heard at" into +/// "heard recently". +library; + +import 'package:vector_math/vector_math.dart'; + +import 'actor.dart'; +import 'actor_system.dart'; +import 'behaviour_tree.dart'; +import 'brain.dart'; + +final class BehaviourBrain extends Brain { + BehaviourBrain(this.tree); + + /// Shared by every actor that runs it; see [BehaviourTree]. + final BehaviourTree tree; + + /// Where the last noise this actor heard came from, as a point. + static const String heard = 'heard'; + + /// The board's clock when it was heard. + static const String heardAt = 'heardAt'; + + /// The board's clock when this actor was last hurt and lived. + static const String hurtAt = 'hurtAt'; + + /// [actor]'s board, made on the spot when it has none. + /// + /// Made here rather than at spawn so that `ActorSystem.spawn` stays the one + /// way an actor is built; a game that wants values on the board before the + /// first decision sets a [Blackboard] on the entity itself. + static Blackboard boardOf(Actor actor) { + final present = actor.entities.get(actor.entity); + if (present != null) return present; + final made = Blackboard(); + actor.entities.set(actor.entity, made); + return made; + } + + @override + void think(Mind it) => tree.tick(it, boardOf(it.actor)); + + @override + void act(Mind it) { + final board = boardOf(it.actor); + board.clock += it.dt; + tree.act(it, board); + } + + @override + void onNoise(Mind it, Vector3 at) { + final board = boardOf(it.actor)..setPoint(heard, at); + board.set(heardAt, board.clock); + } + + @override + void onHurt(Mind it, double amount) { + final board = boardOf(it.actor); + board.set(hurtAt, board.clock); + } + + /// The path [actor] took last time it decided, root first, or empty when it + /// does not run a tree or has not decided yet. + /// + /// **Reads the board and never makes one**, unlike [boardOf]: this is what + /// an overlay calls between frames, and an overlay that put a component on + /// an entity would change the next snapshot by being switched on. + static List pathOf(Actor actor) => + switch ((actor.brain, actor.entities.get(actor.entity))) { + (final BehaviourBrain brain, final Blackboard board) => + brain.tree.pathOf(board), + _ => const [], + }; + + /// Where [actor]'s running leaf is taking it, or null. Reads, like + /// [pathOf]. + static Vector3? goalOf(Actor actor, ActorSystem system) => + switch ((actor.brain, actor.entities.get(actor.entity))) { + (final BehaviourBrain brain, final Blackboard board) => + brain.tree.goalOf((actor: actor, system: system, board: board)), + _ => null, + }; +} diff --git a/packages/flutter3d_sim/lib/src/actors/behaviour_kinds.dart b/packages/flutter3d_sim/lib/src/actors/behaviour_kinds.dart new file mode 100644 index 000000000..cd00be305 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/actors/behaviour_kinds.dart @@ -0,0 +1,391 @@ +/// What a leaf kind in a behaviour tree document means, and the ones every +/// game gets. +/// +/// ## Registered by kind, and why not by type +/// +/// A document says `{"kind": "goTo", "key": "post"}`, and something has to +/// turn that string into code. A table from name to builder is that something, +/// and it is the same table whoever wrote the document — a level designer, a +/// mod, an agent over MCP. A game adds its own kinds to it next to the +/// standard ones, and a document naming a kind nobody registered is refused +/// with the list of kinds that are, rather than failing at the first tick. +/// +/// ## What is standard +/// +/// Only what the engine's [Mind] can already do: walk to the focus or to a +/// point, wait, look, jump, and read and write the board. What any of it is +/// *for* — an attack, a pickup, a conversation — is the game's, registered +/// beside these. +/// +/// Walking to a point is straight, sliding off walls, because that is what +/// [Mind.steerTowards] does; walking to the focus goes round corners on the +/// level's flow field. A navigation mesh that routes to any point is a +/// separate piece of work, and when it lands it replaces `goTo` here. +library; + +import 'package:vector_math/vector_math.dart'; + +import 'behaviour_tree.dart'; +import 'brain.dart'; + +/// One leaf kind's code. +/// +/// **Shared by every actor running the tree, so it keeps no state of its +/// own.** What a leaf needs to remember while it runs goes in +/// [BehaviourContext.memory], which is on the actor's board and in its +/// snapshot; a field here would be one value for every actor at once and +/// gone on a restore. +abstract base class BehaviourLeaf { + const BehaviourLeaf(); + + /// Decides, on the steps the actor thinks on. + BehaviourStatus tick(BehaviourContext context); + + /// Moves, every step this leaf is the one running. Thinking is throttled + /// for an actor far from the focus and moving is not; a leaf that only + /// steered in [tick] would stutter out there. + void act(BehaviourContext context) {} + + /// Where this leaf is taking the actor, for the debug overlay. + Vector3? goal(BehaviourView view) => null; +} + +/// How much one consideration likes an option, from nought to one. +typedef Consideration = double Function(BehaviourContext context); + +typedef LeafBuilder = BehaviourLeaf Function(BehaviourParams params); +typedef ConsiderationBuilder = Consideration Function(BehaviourParams params); + +/// A node's own fields, read with the problems written down rather than +/// thrown, so that a document is checked in one pass. +final class BehaviourParams { + BehaviourParams(this._row, this._where, this._problems); + + final Map _row; + final String _where; + final List _problems; + + /// The number [name], or [fallback] when it is absent. Absent with no + /// fallback, or not a number, is a problem, and the answer is nought. + double number(String name, [double? fallback]) { + final value = _row[name]; + if (value is num) return value.toDouble(); + if (value == null && fallback != null) return fallback; + _problems.add( + value == null + ? '$_where: ${_row['kind']} needs a number "$name"' + : '$_where: "$name" is a number, not $value', + ); + return 0.0; + } + + /// The number [name], or null when it is absent. + double? optionalNumber(String name) { + final value = _row[name]; + if (value == null) return null; + return number(name); + } + + /// The string [name]. Absent, or not a string, is a problem. + String text(String name) { + final value = _row[name]; + if (value is String) return value; + _problems.add('$_where: ${_row['kind']} needs a string "$name"'); + return ''; + } + + /// Whatever [name] holds, unread. + Object? value(String name) => _row[name]; +} + +final class BehaviourKinds { + /// The standard kinds, which a game adds its own to. + BehaviourKinds() { + _standardLeaves(); + _standardConsiderations(); + } + + /// No kinds at all, for a game that wants none of the standard ones. + BehaviourKinds.none(); + + /// The kinds the tree reader handles itself, which no leaf may take. + static const Set composites = { + 'sequence', + 'selector', + 'utility', + 'invert', + 'alwaysSucceed', + 'cooldown', + }; + + final Map _leaves = {}; + final Map _considerations = + {}; + + /// Says what the leaf kind [kind] is. + /// + /// Throws for a composite's name, and for a kind already registered unless + /// [replace] says that is meant — a game swapping the standard `goTo` for + /// one that routes is the case for it. Thrown, because this is a game's + /// own setup and a clash there is a mistake in the code, not in a document. + void leaf(String kind, LeafBuilder build, {bool replace = false}) { + if (composites.contains(kind)) { + throw ArgumentError.value(kind, 'kind', 'is a composite the reader owns'); + } + if (!replace && _leaves.containsKey(kind)) { + throw ArgumentError.value(kind, 'kind', 'is already a leaf kind'); + } + _leaves[kind] = build; + } + + /// Says what the consideration kind [kind] is. See [leaf] for [replace]. + void consideration( + String kind, + ConsiderationBuilder build, { + bool replace = false, + }) { + if (!replace && _considerations.containsKey(kind)) { + throw ArgumentError.value(kind, 'kind', 'is already a consideration'); + } + _considerations[kind] = build; + } + + LeafBuilder? leafBuilder(String kind) => _leaves[kind]; + + ConsiderationBuilder? considerationBuilder(String kind) => + _considerations[kind]; + + /// The leaf kinds and composites, sorted, for a refusal to list. + String describeKnown() => + ([...composites, ..._leaves.keys]..sort()).join(', '); + + String describeKnownConsiderations() => + (_considerations.keys.toList()..sort()).join(', '); + + void _standardLeaves() { + leaf('wait', (p) => _Wait(p.number('seconds'))); + leaf('goToFocus', (p) => _GoToFocus(p.number('within', 1.0))); + leaf('goTo', (p) => _GoTo(p.text('key'), p.number('within', 0.5))); + leaf('seesFocus', (p) => const _SeesFocus()); + leaf('focusWithin', (p) => _FocusWithin(p.number('distance'))); + leaf( + 'check', + (p) => _Check( + p.text('key'), + p.optionalNumber('above'), + p.optionalNumber('below'), + ), + ); + leaf('set', (p) => _Set(p.text('key'), p.value('value'))); + leaf('markFocus', (p) => _MarkFocus(p.text('key'))); + leaf('jump', (p) => const _Jump()); + } + + void _standardConsiderations() { + consideration('constant', (p) { + final value = p.number('value'); + return (_) => value; + }); + consideration('focusDistance', (p) { + final from = p.number('from'); + final to = p.number('to'); + return (c) => ramp(c.mind.distance, from, to); + }); + consideration( + 'seesFocus', + (p) => + (c) => c.mind.canSee() ? 1.0 : 0.0, + ); + consideration('health', (p) { + final from = p.number('from', 0.0); + final to = p.number('to', 1.0); + return (c) { + final health = c.mind.actor.health; + if (health == null || health.maximum <= 0.0) return 1.0; + return ramp(health.current / health.maximum, from, to); + }; + }); + consideration('blackboard', (p) { + final key = p.text('key'); + final from = p.number('from'); + final to = p.number('to'); + return (c) => switch (c.board.number(key)) { + final double value => ramp(value, from, to), + null => 0.0, + }; + }); + consideration('since', (p) { + final key = p.text('key'); + final from = p.number('from'); + final to = p.number('to'); + return (c) => switch (c.board.number(key)) { + final double at => ramp(c.clock - at, from, to), + null => 0.0, + }; + }); + } +} + +/// [value] mapped onto nought to one: nought at [from], one at [to], clamped +/// outside. [from] above [to] turns it round, so "near is good" is +/// `from: 10, to: 2`. Equal ends are a step at that value. +double ramp(double value, double from, double to) { + if (from == to) return value >= to ? 1.0 : 0.0; + final t = (value - from) / (to - from); + return t <= 0.0 ? 0.0 : (t >= 1.0 ? 1.0 : t); +} + +void _faceHeading(Mind it) { + final heading = it.heading; + it.turnTowards(heading.x, heading.z); +} + +final class _Wait extends BehaviourLeaf { + const _Wait(this.seconds); + final double seconds; + + @override + BehaviourStatus tick(BehaviourContext context) { + final start = switch (context.memory) { + final num at => at.toDouble(), + _ => context.clock, + }; + context.memory = start; + return context.clock - start >= seconds + ? BehaviourStatus.success + : BehaviourStatus.running; + } +} + +final class _GoToFocus extends BehaviourLeaf { + const _GoToFocus(this.within); + final double within; + + @override + BehaviourStatus tick(BehaviourContext context) => + context.mind.distance <= within + ? BehaviourStatus.success + : BehaviourStatus.running; + + @override + void act(BehaviourContext context) { + context.mind.steerTowardsFocus(); + _faceHeading(context.mind); + } + + @override + Vector3? goal(BehaviourView view) => view.system.focus.clone(); +} + +final class _GoTo extends BehaviourLeaf { + _GoTo(this.key, this.within); + final String key; + final double within; + + /// Scratch, which is safe to share: the step is single-threaded and the + /// point is read and used inside one call. + final Vector3 _at = Vector3.zero(); + + @override + BehaviourStatus tick(BehaviourContext context) { + final position = context.mind.actor.position; + if (position == null || !context.board.point(key, _at)) { + return BehaviourStatus.failure; + } + final dx = _at.x - position.x; + final dz = _at.z - position.z; + return dx * dx + dz * dz <= within * within + ? BehaviourStatus.success + : BehaviourStatus.running; + } + + @override + void act(BehaviourContext context) { + if (!context.board.point(key, _at)) return; + context.mind.steerTowards(_at); + _faceHeading(context.mind); + } + + @override + Vector3? goal(BehaviourView view) { + final at = Vector3.zero(); + return view.board.point(key, at) ? at : null; + } +} + +final class _SeesFocus extends BehaviourLeaf { + const _SeesFocus(); + + @override + BehaviourStatus tick(BehaviourContext context) => + context.mind.canSee() ? BehaviourStatus.success : BehaviourStatus.failure; +} + +final class _FocusWithin extends BehaviourLeaf { + const _FocusWithin(this.distance); + final double distance; + + @override + BehaviourStatus tick(BehaviourContext context) => + context.mind.distance <= distance + ? BehaviourStatus.success + : BehaviourStatus.failure; +} + +/// Succeeds when [key] holds something other than `false` — or, given +/// [above] or [below], a number past them. +final class _Check extends BehaviourLeaf { + const _Check(this.key, this.above, this.below); + final String key; + final double? above; + final double? below; + + @override + BehaviourStatus tick(BehaviourContext context) { + final board = context.board; + final holds = switch ((above, below)) { + (null, null) => board.flag(key), + _ => switch (board.number(key)) { + null => false, + final double value => + (above == null || value > above!) && + (below == null || value < below!), + }, + }; + return holds ? BehaviourStatus.success : BehaviourStatus.failure; + } +} + +final class _Set extends BehaviourLeaf { + const _Set(this.key, this.value); + final String key; + final Object? value; + + @override + BehaviourStatus tick(BehaviourContext context) { + context.board.set(key, value); + return BehaviourStatus.success; + } +} + +/// Writes where the focus is now, for a search of where it was last seen. +final class _MarkFocus extends BehaviourLeaf { + const _MarkFocus(this.key); + final String key; + + @override + BehaviourStatus tick(BehaviourContext context) { + context.board.setPoint(key, context.mind.focus); + return BehaviourStatus.success; + } +} + +final class _Jump extends BehaviourLeaf { + const _Jump(); + + @override + BehaviourStatus tick(BehaviourContext context) { + context.mind.jump(); + return BehaviourStatus.success; + } +} diff --git a/packages/flutter3d_sim/lib/src/actors/behaviour_tree.dart b/packages/flutter3d_sim/lib/src/actors/behaviour_tree.dart new file mode 100644 index 000000000..3a02a31ef --- /dev/null +++ b/packages/flutter3d_sim/lib/src/actors/behaviour_tree.dart @@ -0,0 +1,562 @@ +/// A decision written down as data: a behaviour tree, with utility choices +/// among its nodes. +/// +/// ## Data, read once, shared +/// +/// A tree is a JSON document — `{"kind": "selector", "children": [...]}` — +/// read by [BehaviourTree.read] against a [BehaviourKinds] that says what each +/// leaf kind is. A level, a mod or an agent can write one without compiling +/// anything, and two actors running the same tree share it: everything that +/// differs between them is on their [Blackboard], which is a component and is +/// therefore in every snapshot. +/// +/// Nodes are numbered in the order the document lists them, and the number is +/// what a blackboard remembers. That is why a board carries the digest of the +/// tree it was ticked by ([Blackboard.tree]): under an edited tree the numbers +/// name other nodes, and resuming there would run something nobody chose. +/// +/// ## The composites, and the one that is missing +/// +/// * `sequence` — children in order until one does not succeed. **It +/// remembers** which child was running and resumes there, so a sequence +/// that walked somewhere and is now waiting does not walk there again. +/// * `selector` — the first child that does not fail. **It does not +/// remember**: every tick asks again from the first child, so a higher +/// priority that becomes true takes over from a lower one that is running. +/// Remembering here is the classic way to write a guard that never fires +/// until the patrol it interrupts happens to finish. +/// * `utility` — scores every option and runs the best (see [_Utility]). +/// * `invert`, `alwaysSucceed` and `cooldown` — one child each. +/// +/// No `parallel`. An actor has one body and it walks one way: two running +/// leaves would both steer and the last one would win, decided by the order of +/// a list. A condition that must hold while something runs is a `selector` +/// with the condition's branch first, which is checked every tick anyway. +library; + +import 'package:vector_math/vector_math.dart'; + +import '../save/state_digest.dart'; +import 'actor.dart'; +import 'actor_system.dart'; +import 'behaviour_kinds.dart'; +import 'blackboard.dart'; +import 'brain.dart'; + +export 'behaviour_kinds.dart'; +export 'blackboard.dart'; + +/// What a leaf may look at while it ticks or acts. +/// +/// One per tree and reused, because a tick happens for every thinking actor +/// on every thinking step and allocating one each time is garbage in the step. +final class BehaviourContext { + BehaviourContext._(); + + /// The actor's mind: its body, its focus, and what it may ask for. + late Mind mind; + + late Blackboard board; + + /// Which node is ticking, by its number in the tree. + int node = 0; + + /// Seconds the actor has been acting. See [Blackboard.clock]. + double get clock => board.clock; + + /// This node's own memory, kept while it is on the running path and + /// forgotten when it leaves it. JSON-shaped, like everything on the board. + Object? get memory => board.memory[node]; + set memory(Object? value) { + if (value == null) { + board.memory.remove(node); + } else { + board.memory[node] = value; + } + } +} + +/// What the overlay may look at outside a step, when there is no mind. +typedef BehaviourView = ({Actor actor, ActorSystem system, Blackboard board}); + +/// One node on the running path, for whoever draws or prints it. +typedef BehaviourPathStep = ({ + int node, + String kind, + String label, + BehaviourStatus status, +}); + +/// What [BehaviourTree.read] made of a document: a tree, or why not. +final class BehaviourTreeRead { + const BehaviourTreeRead._(this.tree, this.problems); + + /// The tree, when the document had no problems. + final BehaviourTree? tree; + + /// Every problem, each naming where in the document it is — all of them, + /// rather than the first, so that a document is fixed in one pass. + final List problems; +} + +final class BehaviourTree { + BehaviourTree._(this._root, this._nodes, this.digestHex, this.json) + : _statusOf = List.filled(_nodes.length, null); + + /// Reads [json] against [kinds]. + /// + /// A refusal is an answer here rather than an exception: a tree arrives + /// from a file, an editor or an agent, and each of them wants every problem + /// listed with where it is — `root.children[2]: no leaf kind "fly"; known: + /// …` — rather than a stack trace about the first. + static BehaviourTreeRead read(Object? json, BehaviourKinds kinds) { + final reader = _Reader(kinds); + final root = reader.node(json, 'root'); + final digest = reader.digestOf(json); + if (root == null || reader.problems.isNotEmpty || digest == null) { + return BehaviourTreeRead._( + null, + List.unmodifiable(reader.problems), + ); + } + return BehaviourTreeRead._( + BehaviourTree._( + root, + reader.nodes, + digest, + json! as Map, + ), + const [], + ); + } + + final _Node _root; + final List<_Node> _nodes; + + /// The document this was read from. + final Map json; + + /// The document's [StateDigest], which is how a board knows whether it was + /// ticked by this tree. + final String digestHex; + + /// How many nodes there are. + int get length => _nodes.length; + + final BehaviourContext _context = BehaviourContext._(); + final List _stack = []; + final List _lastPath = []; + final List _statusOf; + + /// Makes [board] belong to this tree, starting its decision again if it + /// was ticked by another one. + void prepare(Blackboard board) { + if (board.tree == digestHex) return; + board + ..restart() + ..tree = digestHex; + } + + /// Decides: runs the tree from the root, records the path it took on + /// [board], and forgets the memory of every node that is no longer running. + BehaviourStatus tick(Mind mind, Blackboard board) { + prepare(board); + final context = _context + ..mind = mind + ..board = board; + _stack.clear(); + _lastPath.clear(); + _statusOf.fillRange(0, _statusOf.length, null); + + final status = _tick(_root, context); + + board.path + ..clear() + ..addAll(_lastPath); + board.statuses + ..clear() + ..addAll([ + for (final node in _lastPath) _statusOf[node]!, + ]); + if (status == BehaviourStatus.running) { + board.memory.removeWhere((node, _) => !_lastPath.contains(node)); + } else { + board.memory.clear(); + } + return status; + } + + /// Moves: the running leaf's [BehaviourLeaf.act], every step, between the + /// decisions [tick] makes on the steps the system thinks on. + void act(Mind mind, Blackboard board) { + final leaf = _runningLeaf(board); + if (leaf == null) return; + leaf.leaf.act( + _context + ..mind = mind + ..board = board + ..node = leaf.index, + ); + } + + /// The path [board] last took through this tree, root first; empty when it + /// was ticked by another tree or not yet at all. + List pathOf(Blackboard board) => board.tree != digestHex + ? const [] + : [ + for (var i = 0; i < board.path.length; i++) + if (board.path[i] case final int node when node < _nodes.length) + ( + node: node, + kind: _nodes[node].kind, + label: _nodes[node].label, + status: board.statuses[i], + ), + ]; + + /// Where the running leaf is taking the actor, for the overlay; null when + /// nothing is running or the leaf is not going anywhere. + Vector3? goalOf(BehaviourView view) => + _runningLeaf(view.board)?.leaf.goal(view); + + _Leaf? _runningLeaf(Blackboard board) { + if (board.tree != digestHex || !board.running) return null; + return switch (_nodes.elementAtOrNull(board.path.last)) { + final _Leaf leaf => leaf, + _ => null, + }; + } + + BehaviourStatus _tick(_Node node, BehaviourContext context) { + _stack.add(node.index); + final status = switch (node) { + _Leaf() => _leaf(node, context), + _Sequence() => _sequence(node, context), + _Selector() => _selector(node, context), + _Utility() => _utility(node, context), + _Invert() => switch (_tick(node.child, context)) { + BehaviourStatus.success => BehaviourStatus.failure, + BehaviourStatus.failure => BehaviourStatus.success, + BehaviourStatus.running => BehaviourStatus.running, + }, + _AlwaysSucceed() => switch (_tick(node.child, context)) { + BehaviourStatus.running => BehaviourStatus.running, + _ => BehaviourStatus.success, + }, + _Cooldown() => _cooldown(node, context), + }; + _stack.removeLast(); + _statusOf[node.index] = status; + return status; + } + + BehaviourStatus _leaf(_Leaf node, BehaviourContext context) { + // The path is the stack at the last leaf ticked: whatever comes out + // running returns at once, so no leaf is ticked after the running one. + _lastPath + ..clear() + ..addAll(_stack); + context.node = node.index; + return node.leaf.tick(context); + } + + BehaviourStatus _sequence(_Sequence node, BehaviourContext context) { + context.node = node.index; + final from = switch (context.memory) { + final num at => at.toInt(), + _ => 0, + }; + for (var i = from; i < node.children.length; i++) { + final status = _tick(node.children[i], context); + if (status == BehaviourStatus.success) continue; + context.node = node.index; + context.memory = status == BehaviourStatus.running ? i : null; + return status; + } + context.node = node.index; + context.memory = null; + return BehaviourStatus.success; + } + + BehaviourStatus _selector(_Selector node, BehaviourContext context) { + for (final child in node.children) { + final status = _tick(child, context); + if (status != BehaviourStatus.failure) return status; + } + return BehaviourStatus.failure; + } + + BehaviourStatus _utility(_Utility node, BehaviourContext context) { + context.node = node.index; + final last = switch (context.memory) { + final num at => at.toInt(), + _ => -1, + }; + final best = node.choose(context, last); + if (best < 0) { + context.memory = null; + return BehaviourStatus.failure; + } + final status = _tick(node.options[best].child, context); + context.node = node.index; + context.memory = status == BehaviourStatus.running ? best : null; + return status; + } + + BehaviourStatus _cooldown(_Cooldown node, BehaviourContext context) { + final board = context.board; + final readyAt = board.readyAt[node.index]; + if (readyAt != null && board.clock < readyAt) { + return BehaviourStatus.failure; + } + final status = _tick(node.child, context); + if (status != BehaviourStatus.running) { + board.readyAt[node.index] = board.clock + node.seconds; + } + return status; + } +} + +sealed class _Node { + _Node(this.index, this.kind, this.label); + final int index; + final String kind; + final String label; +} + +final class _Leaf extends _Node { + _Leaf(super.index, super.kind, super.label, this.leaf); + final BehaviourLeaf leaf; +} + +final class _Sequence extends _Node { + _Sequence(super.index, super.kind, super.label); + final List<_Node> children = <_Node>[]; +} + +final class _Selector extends _Node { + _Selector(super.index, super.kind, super.label); + final List<_Node> children = <_Node>[]; +} + +final class _Invert extends _Node { + _Invert(super.index, super.kind, super.label); + late final _Node child; +} + +final class _AlwaysSucceed extends _Node { + _AlwaysSucceed(super.index, super.kind, super.label); + late final _Node child; +} + +final class _Cooldown extends _Node { + _Cooldown(super.index, super.kind, super.label, this.seconds); + final double seconds; + late final _Node child; +} + +typedef _Option = ({ + double weight, + List considerations, + _Node child, +}); + +/// Scores every option and runs the best one. +/// +/// **A score is the product of its considerations, times the option's +/// weight**, so any consideration at zero vetoes the option — "only when the +/// focus is in sight" is a consideration that answers nought otherwise — and +/// an option scoring nought is never chosen even when it is the only one. +/// +/// **The option already running gets [inertia] added**, and that is what +/// stops an actor dithering on the line between two options whose scores +/// cross back and forth with every step. Ties go to the earlier option, so +/// the document's order is the tie-break and two runs agree on it. +final class _Utility extends _Node { + _Utility(super.index, super.kind, super.label, this.inertia); + final double inertia; + final List<_Option> options = <_Option>[]; + + int choose(BehaviourContext context, int last) { + var best = -1; + var bestScore = 0.0; + for (var i = 0; i < options.length; i++) { + final option = options[i]; + final score = + option.considerations.fold( + option.weight, + (double product, consider) => product * consider(context), + ) + + (i == last ? inertia : 0.0); + if (score > bestScore) { + best = i; + bestScore = score; + } + } + return best; + } +} + +/// Builds nodes from a document, numbering them as it goes. +final class _Reader { + _Reader(this.kinds); + + final BehaviourKinds kinds; + final List<_Node> nodes = <_Node>[]; + final List problems = []; + + String? digestOf(Object? json) { + try { + return contentDigestHex((json! as Map).cast()); + } on Object { + // Already reported by [node]: a root that is not a JSON object. + return null; + } + } + + _Node? node(Object? json, String where) { + if (json is! Map) { + problems.add('$where: a node is an object with a "kind"'); + return null; + } + final row = json.cast(); + final kind = row['kind']; + if (kind is! String) { + problems.add('$where: a node is an object with a "kind"'); + return null; + } + final label = switch (row['name']) { + final String name => name, + _ => kind, + }; + final index = nodes.length; + switch (kind) { + case 'sequence': + final made = _Sequence(index, kind, label); + nodes.add(made); + made.children.addAll(_children(row, where)); + return made; + case 'selector': + final made = _Selector(index, kind, label); + nodes.add(made); + made.children.addAll(_children(row, where)); + return made; + case 'invert': + final made = _Invert(index, kind, label); + nodes.add(made); + final child = node(row['child'], '$where.child'); + if (child == null) return null; + return made..child = child; + case 'alwaysSucceed': + final made = _AlwaysSucceed(index, kind, label); + nodes.add(made); + final child = node(row['child'], '$where.child'); + if (child == null) return null; + return made..child = child; + case 'cooldown': + final params = BehaviourParams(row, where, problems); + final made = _Cooldown(index, kind, label, params.number('seconds')); + nodes.add(made); + final child = node(row['child'], '$where.child'); + if (child == null) return null; + return made..child = child; + case 'utility': + final params = BehaviourParams(row, where, problems); + final made = _Utility( + index, + kind, + label, + params.number('inertia', 0.0), + ); + nodes.add(made); + made.options.addAll(_options(row, where)); + return made; + } + final build = kinds.leafBuilder(kind); + if (build == null) { + problems.add( + '$where: no leaf kind "$kind"; known: ${kinds.describeKnown()}', + ); + return null; + } + final made = _Leaf( + index, + kind, + label, + build(BehaviourParams(row, where, problems)), + ); + nodes.add(made); + return made; + } + + List<_Node> _children(Map row, String where) { + final children = row['children']; + if (children is! List) { + problems.add('$where: a ${row['kind']} lists its "children"'); + return const <_Node>[]; + } + return <_Node>[ + for (var i = 0; i < children.length; i++) + ?node(children[i], '$where.children[$i]'), + ]; + } + + List<_Option> _options(Map row, String where) { + final options = row['options']; + if (options is! List || options.isEmpty) { + problems.add('$where: a utility lists its "options"'); + return const <_Option>[]; + } + final read = <_Option>[]; + for (var i = 0; i < options.length; i++) { + final at = '$where.options[$i]'; + final option = options[i]; + if (option is! Map) { + problems.add('$at: an option is an object with "do"'); + continue; + } + final params = BehaviourParams(option, at, problems); + final weight = params.number('weight', 1.0); + final considerations = _considerations(option['considerations'], at); + // Named by the option when the child is not, so the overlay says + // "flee" rather than the leaf kind the option happens to run. + final body = option['do']; + final named = + body is Map && body['name'] == null && option['name'] is String + ? {...body, 'name': option['name']} + : body; + final child = node(named, '$at.do'); + if (child == null) continue; + read.add((weight: weight, considerations: considerations, child: child)); + } + return read; + } + + List _considerations(Object? json, String where) { + if (json == null) return const []; + if (json is! List) { + problems.add('$where: "considerations" is a list'); + return const []; + } + final read = []; + for (var i = 0; i < json.length; i++) { + final at = '$where.considerations[$i]'; + final row = json[i]; + final kind = row is Map ? row['kind'] : null; + if (row is! Map || kind is! String) { + problems.add('$at: a consideration is an object with a "kind"'); + continue; + } + final build = kinds.considerationBuilder(kind); + if (build == null) { + problems.add( + '$at: no consideration kind "$kind"; known: ' + '${kinds.describeKnownConsiderations()}', + ); + continue; + } + read.add(build(BehaviourParams(row, at, problems))); + } + return read; + } +} diff --git a/packages/flutter3d_sim/lib/src/actors/blackboard.dart b/packages/flutter3d_sim/lib/src/actors/blackboard.dart new file mode 100644 index 000000000..f2954d291 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/actors/blackboard.dart @@ -0,0 +1,221 @@ +/// What a behaviour tree remembers between one decision and the next. +/// +/// ## A component, and that is the point of it +/// +/// A tree is data and is shared by every actor that runs it; what differs +/// between two of them is where each is in it, what each has noticed and how +/// long each has been waiting. All of that is here, on the actor's entity, so +/// `EcsWorld.save()` writes it with everything else and a snapshot, a rewind +/// or a save file brings a decision back half made. The alternative was state +/// on the brain, written out through `Brain.save` — which works until the +/// first leaf somebody adds keeps a field and forgets to. +/// +/// ## Only what a snapshot may hold +/// +/// Every value is JSON-shaped: `null`, `bool`, `num`, `String`, lists and +/// string-keyed maps of those. [set] refuses anything else, because a +/// `Vector3` put here would be saved as nothing and the restored run would +/// part from the original without anybody being told why. A point is a list +/// of three numbers, and [setPoint] and [point] write and read one. +library; + +import 'package:flutter3d_physics/flutter3d_physics.dart' show readVector; +import 'package:vector_math/vector_math.dart'; + +import '../ecs/ecs_world.dart'; + +/// How a node came out of its last tick. +enum BehaviourStatus { success, failure, running } + +final class Blackboard { + Blackboard({Map? values}) : _values = {} { + values?.forEach(set); + } + + final Map _values; + + /// Seconds this actor has been acting, advanced every step it is alive. + /// + /// **Its own clock rather than the system's step count**, because thinking + /// is throttled: an actor far from the focus decides every fourth step, and + /// a wait measured in decisions would last four times as long out there. + double clock = 0.0; + + /// The digest of the tree this was last ticked by, as eight hex digits. + /// + /// Node indices mean nothing under another tree, so a board restored under a + /// tree that changed starts its decision again rather than resuming at an + /// index that now names something else. See `BehaviourTree.prepare`. + String tree = ''; + + /// Per node, what it needs while it is running: a sequence's cursor, the + /// moment a wait began. Dropped for every node that is not on the running + /// path after a tick, so an abandoned branch starts afresh when it is next + /// chosen. + final Map memory = {}; + + /// Per cooldown node, the clock at which it may run again. Kept off the + /// running path, which is what a cooldown is for. + final Map readyAt = {}; + + /// The nodes ticked on the way to the last leaf, root first. + final List path = []; + + /// How each node on [path] came out, in the same order. + final List statuses = []; + + /// Whether the last tick left something running, which is what `act` asks. + bool get running => + statuses.isNotEmpty && statuses.first == BehaviourStatus.running; + + /// Every key with a value. + Iterable get keys => _values.keys; + + bool has(String key) => _values.containsKey(key); + + Object? operator [](String key) => _values[key]; + + /// Puts [value] under [key], or removes the key for `null`. + /// + /// Throws for a value a snapshot cannot hold — see the note at the top of + /// this file. Thrown rather than skipped: a skipped value is a run that + /// restores differently, found much later. + void set(String key, Object? value) { + if (value == null) { + _values.remove(key); + return; + } + if (!_jsonShaped(value)) { + throw ArgumentError.value( + value, + key, + 'a blackboard holds what a snapshot may hold: null, bool, num, ' + 'String, and lists and string-keyed maps of those', + ); + } + _values[key] = value; + } + + void remove(String key) => _values.remove(key); + + /// The number under [key], or null when there is none. + double? number(String key) => switch (_values[key]) { + final num value => value.toDouble(), + _ => null, + }; + + /// Whether [key] holds anything other than `false`. + bool flag(String key) => switch (_values[key]) { + null || false => false, + _ => true, + }; + + /// Writes [at] as a point, three numbers. + void setPoint(String key, Vector3 at) => + _values[key] = [at.x, at.y, at.z]; + + /// Reads the point under [key] into [into], and says whether there was one. + bool point(String key, Vector3 into) => readVector(_values[key], into); + + /// Forgets where the tree was, keeping what it noticed and the clock. + void restart() { + memory.clear(); + readyAt.clear(); + path.clear(); + statuses.clear(); + } + + Map toJson() => { + 'values': Map.of(_values), + 'clock': clock, + 'tree': tree, + 'memory': { + for (final entry in memory.entries) '${entry.key}': entry.value, + }, + 'readyAt': { + for (final entry in readyAt.entries) '${entry.key}': entry.value, + }, + 'path': List.of(path), + 'statuses': [for (final status in statuses) status.name], + }; + + /// Reads a board back, or null when [data] is not one. + /// + /// Lenient about the parts: a row with the values and nothing else is a + /// board that has not decided anything yet, which is what it reads as. + static Blackboard? fromJson(Object? data) { + if (data is! Map) return null; + final values = data['values']; + final board = Blackboard(); + if (values is Map) { + for (final entry in values.entries) { + final key = entry.key; + if (key is String && _jsonShaped(entry.value)) { + board._values[key] = entry.value; + } + } + } + board + ..clock = switch (data['clock']) { + final num value => value.toDouble(), + _ => 0.0, + } + ..tree = switch (data['tree']) { + final String value => value, + _ => '', + }; + if (data['memory'] case final Map memory) { + for (final entry in memory.entries) { + final index = int.tryParse('${entry.key}'); + if (index != null) board.memory[index] = entry.value; + } + } + if (data['readyAt'] case final Map readyAt) { + for (final entry in readyAt.entries) { + final index = int.tryParse('${entry.key}'); + if (index != null && entry.value is num) { + board.readyAt[index] = (entry.value as num).toDouble(); + } + } + } + final path = data['path']; + final statuses = data['statuses']; + if (path is List && statuses is List && path.length == statuses.length) { + final names = { + for (final status in BehaviourStatus.values) status.name: status, + }; + final read = <(int, BehaviourStatus)>[ + for (var i = 0; i < path.length; i++) + if (path[i] case final num index) + if (names[statuses[i]] case final BehaviourStatus status) + (index.toInt(), status), + ]; + // All or nothing: half a path would act on a leaf the tree never chose. + if (read.length == path.length) { + board.path.addAll(read.map((entry) => entry.$1)); + board.statuses.addAll(read.map((entry) => entry.$2)); + } + } + return board; + } + + static bool _jsonShaped(Object? value) => switch (value) { + null || bool() || num() || String() => true, + final List list => list.every(_jsonShaped), + final Map map => map.entries.every( + (entry) => entry.key is String && _jsonShaped(entry.value), + ), + _ => false, + }; +} + +/// Teaches [entities] to write a [Blackboard] down. +/// +/// A value component, decoded rather than restored in place: a board owns +/// nothing live, so a restore can build a new one, and an entity whose board +/// the save did not have gets a fresh one the next time its brain thinks. +void registerBlackboard(EcsWorld entities) => entities.register( + 'blackboard', + encode: (Blackboard value) => value.toJson(), + decode: Blackboard.fromJson, +); diff --git a/packages/flutter3d_sim/test/behaviour_tree_test.dart b/packages/flutter3d_sim/test/behaviour_tree_test.dart new file mode 100644 index 000000000..d7c56d06a --- /dev/null +++ b/packages/flutter3d_sim/test/behaviour_tree_test.dart @@ -0,0 +1,619 @@ +/// Behaviour trees as data: what a document is refused for, what each node +/// does, and that a decision half made comes back from a snapshot. +library; + +import 'dart:convert'; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +const double _dt = 1.0 / 60.0; + +/// Adds one to [key] every tick and succeeds — a leaf a game would register, +/// here to count how often a node was really ticked. +final class _Count extends BehaviourLeaf { + const _Count(this.key); + final String key; + + @override + BehaviourStatus tick(BehaviourContext context) { + context.board.set(key, (context.board.number(key) ?? 0.0) + 1.0); + return BehaviourStatus.success; + } +} + +/// Runs for ever. +final class _Hold extends BehaviourLeaf { + const _Hold(); + + @override + BehaviourStatus tick(BehaviourContext context) => BehaviourStatus.running; +} + +BehaviourKinds _kinds() => BehaviourKinds() + ..leaf('count', (p) => _Count(p.text('key'))) + ..leaf('hold', (p) => const _Hold()); + +BehaviourTree _tree(Map json) { + final read = BehaviourTree.read(json, _kinds()); + expect(read.problems, isEmpty); + return read.tree!; +} + +/// One actor with no body, thinking every step: the focus is at the origin +/// and so is it, which keeps it inside the close range. +final class _Rig { + _Rig(BehaviourTree tree, {Map? board}) + : system = ActorSystem(world: CollisionWorld(), random: GameRandom(1)) { + actor = system.spawn(brain: BehaviourBrain(tree)); + if (board != null) { + system.entities.set(actor.entity, Blackboard(values: board)); + } + } + + final ActorSystem system; + late final Actor actor; + + Blackboard get board => system.entities.get(actor.entity)!; + + List get path => [ + for (final step in BehaviourBrain.pathOf(actor)) + '${step.label}:${step.status.name}', + ]; + + void step([int times = 1]) { + for (var i = 0; i < times; i++) { + system + ..beginStep() + ..step(_dt, focus: Vector3.zero()); + } + } +} + +void main() { + group('reading a document', () { + test('lists every problem with where it is, and the kinds it knows', () { + final read = BehaviourTree.read({ + 'kind': 'selector', + 'children': [ + {'kind': 'fly'}, + {'kind': 'wait'}, + {'kind': 'utility'}, + {'kind': 'sequence'}, + ], + }, BehaviourKinds()); + + // Mutation: stopping at the first problem leaves three of these out. + expect(read.tree, isNull); + expect(read.problems, hasLength(4)); + expect( + read.problems[0], + startsWith('root.children[0]: no leaf kind "fly"'), + ); + expect(read.problems[0], contains('goToFocus')); + expect( + read.problems[1], + 'root.children[1]: wait needs a number "seconds"', + ); + expect( + read.problems[2], + 'root.children[2]: a utility lists its "options"', + ); + expect( + read.problems[3], + 'root.children[3]: a sequence lists its "children"', + ); + }); + + test('an unknown consideration is refused with the ones that exist', () { + final read = BehaviourTree.read({ + 'kind': 'utility', + 'options': [ + { + 'considerations': [ + {'kind': 'mood'}, + ], + 'do': {'kind': 'hold'}, + }, + ], + }, _kinds()); + expect(read.problems, hasLength(1)); + expect( + read.problems.single, + startsWith( + 'root.options[0].considerations[0]: no consideration kind "mood"', + ), + ); + expect(read.problems.single, contains('focusDistance')); + }); + + test('a leaf may not take a composite\'s name', () { + expect( + () => BehaviourKinds().leaf('sequence', (p) => const _Hold()), + throwsArgumentError, + ); + expect( + () => BehaviourKinds().leaf('wait', (p) => const _Hold()), + throwsArgumentError, + ); + // Replacing on purpose is the way a game swaps a standard kind. + BehaviourKinds().leaf('wait', (p) => const _Hold(), replace: true); + }); + }); + + group('nodes', () { + test('a sequence resumes at the child that was running', () { + final rig = _Rig( + _tree({ + 'kind': 'sequence', + 'children': [ + {'kind': 'count', 'key': 'n'}, + {'kind': 'wait', 'seconds': 0.5}, + ], + }), + ); + rig.step(10); + // Mutation: a sequence that starts again from its first child every + // tick counts ten here instead of one. + expect(rig.board.number('n'), 1.0); + expect(rig.path, ['sequence:running', 'wait:running']); + + // Half a second on, the wait ends, the sequence succeeds, and the next + // tick starts it again from the top. + rig.step(30); + expect(rig.board.number('n'), 2.0); + }); + + test('a selector asks its first child again every tick', () { + final rig = _Rig( + _tree({ + 'kind': 'selector', + 'children': [ + { + 'kind': 'sequence', + 'name': 'alarm', + 'children': [ + {'kind': 'check', 'key': 'alarm'}, + {'kind': 'hold'}, + ], + }, + {'kind': 'wait', 'name': 'idle', 'seconds': 100}, + ], + }), + ); + rig.step(5); + expect(rig.path.last, 'idle:running'); + final waitNode = BehaviourBrain.pathOf(rig.actor).last.node; + expect(rig.board.memory, contains(waitNode)); + + rig.board.set('alarm', true); + rig.step(); + // Mutation: a selector that remembers its running child stays idle + // until the hundred-second wait ends. + expect(rig.path, [ + 'selector:running', + 'alarm:running', + 'hold:running', + ]); + // The abandoned wait's start time is forgotten, so it starts afresh. + expect(rig.board.memory, isNot(contains(waitNode))); + }); + + test('a utility runs the best option, and holds on to it', () { + final rig = _Rig( + _tree({ + 'kind': 'utility', + 'inertia': 0.2, + 'options': [ + { + 'name': 'rest', + 'considerations': [ + { + 'kind': 'blackboard', + 'key': 'tired', + 'from': 0, + 'to': 1, + }, + ], + 'do': {'kind': 'hold'}, + }, + { + 'name': 'work', + 'considerations': [ + {'kind': 'constant', 'value': 0.5}, + ], + 'do': {'kind': 'hold'}, + }, + { + 'name': 'never', + 'considerations': [ + {'kind': 'constant', 'value': 0}, + ], + 'weight': 100, + 'do': {'kind': 'hold'}, + }, + ], + }), + board: {'tired': 0.2}, + ); + rig.step(); + // Mutation: a sum rather than a product lets the zero-scored option + // win on its weight. + expect(rig.path.last, 'work:running'); + + // Past work's half, but not by its inertia: the running option stays. + rig.board.set('tired', 0.6); + rig.step(); + // Mutation: without inertia this flips to rest. + expect(rig.path.last, 'work:running'); + + rig.board.set('tired', 0.8); + rig.step(); + expect(rig.path.last, 'rest:running'); + }); + + test('a cooldown refuses its child until the time is up', () { + final rig = _Rig( + _tree({ + 'kind': 'selector', + 'children': [ + { + 'kind': 'cooldown', + 'seconds': 1, + 'child': {'kind': 'count', 'key': 'n'}, + }, + {'kind': 'hold'}, + ], + }), + ); + rig.step(30); + expect(rig.board.number('n'), 1.0); + rig.step(31); + // Mutation: a cooldown kept in memory that is forgotten off the + // running path would count every tick. + expect(rig.board.number('n'), 2.0); + }); + + test('invert and alwaysSucceed turn their child\'s answer', () { + final rig = _Rig( + _tree({ + 'kind': 'sequence', + 'children': [ + { + 'kind': 'invert', + 'child': {'kind': 'check', 'key': 'x'}, + }, + { + 'kind': 'alwaysSucceed', + 'child': {'kind': 'check', 'key': 'x'}, + }, + ], + }), + ); + // Nothing runs, so every decision starts from the top and both + // answers are seen. + rig.step(); + // Mutation: an alwaysSucceed that passed its child's failure through + // fails the sequence here. + expect(rig.path, [ + 'sequence:success', + 'alwaysSucceed:success', + 'check:failure', + ]); + rig.board.set('x', true); + rig.step(); + // Mutation: an invert that passed its child's answer through lets the + // sequence carry on to the second child. + expect(rig.path, [ + 'sequence:failure', + 'invert:failure', + 'check:success', + ]); + }); + }); + + group('the board', () { + test('refuses what a snapshot cannot hold', () { + final board = Blackboard(); + expect(() => board.set('at', Vector3.zero()), throwsArgumentError); + board + ..setPoint('at', Vector3(1.0, 2.0, 3.0)) + ..set('seen', true) + ..set('names', ['a', 1, null]); + final read = Blackboard.fromJson(jsonDecode(jsonEncode(board.toJson())))!; + final at = Vector3.zero(); + expect(read.point('at', at), isTrue); + expect(at, Vector3(1.0, 2.0, 3.0)); + expect(read.flag('seen'), isTrue); + expect(read['names'], ['a', 1, null]); + }); + + test('a tree that changed starts its decision again', () { + final first = _tree({ + 'kind': 'sequence', + 'children': [ + {'kind': 'count', 'key': 'n'}, + {'kind': 'hold'}, + ], + }); + final rig = _Rig(first)..step(3); + expect(rig.board.number('n'), 1.0); + + // Same shape, one more child: node numbers are the same, the digest + // is not. + final second = _tree({ + 'kind': 'sequence', + 'children': [ + {'kind': 'count', 'key': 'n'}, + {'kind': 'hold'}, + {'kind': 'hold'}, + ], + }); + rig.system.entities.get(rig.actor.entity)!.brain = + BehaviourBrain(second); + rig.step(); + // Mutation: a board that trusted its node numbers would resume at the + // hold and never count again. + expect(rig.board.number('n'), 2.0); + expect(rig.board.tree, second.digestHex); + }); + + test('noise and pain are written where the tree can read them', () { + // A body, because hearing is by distance and a bodiless actor is + // nowhere to hear from. + final world = CollisionWorld() + ..addBox(Vector3(0.0, -0.5, 0.0), Vector3(20.0, 1.0, 20.0)) + ..update(); + final system = ActorSystem(world: world, random: GameRandom(1)); + final actor = system.spawn( + body: CharacterController(world: world, position: Vector3(0, 0.9, 0)), + health: Health(10.0), + brain: BehaviourBrain(_tree({'kind': 'hold'})), + ); + for (var i = 0; i < 6; i++) { + system + ..beginStep() + ..step(_dt, focus: Vector3(0.0, 0.9, 5.0)); + } + final board = BehaviourBrain.boardOf(actor); + + system.hear(Vector3(1.0, 0.0, 0.0), radius: 1000.0); + // Mutation: an onNoise left at Brain's default writes nothing here. + expect(board.number(BehaviourBrain.heardAt), closeTo(0.1, 1e-12)); + final at = Vector3.zero(); + expect(board.point(BehaviourBrain.heard, at), isTrue); + expect(at.x, 1.0); + + system.beginStep(); + actor.applyDamage(1.0); + expect(board.number(BehaviourBrain.hurtAt), closeTo(0.1, 1e-12)); + }); + }); + + group('determinism', () { + test('a run of trees has the digests it had', () { + final run = _Arena(); + final trace = DigestTrace(every: 60); + final chosen = {}; + for (var step = 1; step <= 600; step++) { + run.step(step); + trace.observe(step, run.state()); + for (final actor in run.system.actors) { + final path = BehaviourBrain.pathOf(actor); + if (path.length > 1) chosen.add(path[1].label); + } + } + // A trace of a run that never left one option would hold its digests + // while the other two were broken. + expect( + chosen, + containsAll(['close in', 'look into it', 'patrol']), + ); + // Recorded once on the VM. Every number here is `+`, `-`, `*`, `/`, + // `sqrt` and `Portable`, so these are the same on every platform; a + // change of any of them is a change of behaviour, and says at which + // second it started. + expect(trace.hexDigests, _arenaDigests); + }); + + test('a decision half made comes back from a snapshot', () { + // The snapshot is taken on the first step past four seconds at which + // somebody is part-way through a wait: mid-decision, with a sequence's + // cursor and a wait's start on a board, which is the case worth + // checking. Found the same way on every run, so the test is too. + final original = _Arena(); + var at = 0; + while (at < 240 || !original.waiting) { + at++; + original.step(at); + } + expect(at, lessThan(500)); + final saved = jsonDecode(jsonEncode(original.state())) as Map; + + final restored = _Arena()..restore(saved); + final blind = _Arena() + ..restore({ + ...saved.cast(), + 'ecs': _withoutBoards(saved['ecs'] as Map), + }); + final agree = []; + final blindAgrees = []; + for (var step = at + 1; step <= at + 300; step++) { + original.step(step); + restored.step(step); + blind.step(step); + final want = StateDigest.of(original.state()); + agree.add(StateDigest.of(restored.state()) == want); + blindAgrees.add(StateDigest.of(blind.state()) == want); + } + expect(agree, everyElement(isTrue)); + // Mutation: the boards are what carry the decision; a restore without + // them is a different run, and this says the test can tell. + expect(blindAgrees, contains(isFalse)); + }); + }); +} + +Map _withoutBoards(Map ecs) => { + ...ecs.cast(), + 'components': { + ...(ecs['components'] as Map).cast(), + }..remove('blackboard'), +}; + +/// Three actors on a floor with a wall, one tree, a focus that walks a +/// square, and a noise every few seconds. +/// +/// The tree uses every composite and most standard leaves: go for the focus +/// when it is seen and near, look into a noise heard recently, otherwise +/// patrol between two posts with a pause at each. +final class _Arena { + _Arena() { + world + ..addBox(Vector3(0.0, -0.5, 0.0), Vector3(80.0, 1.0, 80.0)) + ..addBox(Vector3(0.0, 1.5, 4.0), Vector3(8.0, 3.0, 0.5)) + ..update(); + for (final (i, x) in <(int, double)>[(0, -6.0), (1, 3.0), (2, 22.0)]) { + final actor = system.spawn( + body: CharacterController( + world: world, + position: Vector3(x, 0.9, -3.0 - i), + ), + health: Health(30.0), + brain: BehaviourBrain(_arenaTree), + facing: Facing(), + name: 'a$i', + ); + system.entities.set( + actor.entity, + Blackboard( + values: { + 'postA': [x - 3.0, 0.9, -8.0], + 'postB': [x + 3.0, 0.9, -8.0], + }, + ), + ); + } + } + + final CollisionWorld world = CollisionWorld(); + late final ActorSystem system = ActorSystem( + world: world, + random: GameRandom(7), + ); + + static final BehaviourTree _arenaTree = BehaviourTree.read( + _arenaJson, + BehaviourKinds(), + ).tree!; + + /// The focus walks a square of side twelve, once every twenty seconds. + Vector3 focusAt(int step) { + final t = (step % 1200) / 1200.0 * 4.0; + final side = t.floor(); + final along = (t - side) * 12.0 - 6.0; + return switch (side) { + 0 => Vector3(along, 0.9, 10.0), + 1 => Vector3(6.0, 0.9, 10.0 - (along + 6.0)), + 2 => Vector3(-along, 0.9, -2.0), + _ => Vector3(-6.0, 0.9, -2.0 + (along + 6.0)), + }; + } + + void step(int step) { + system + ..beginStep() + ..step(_dt, focus: focusAt(step)); + if (step % 170 == 0) { + system.hear( + Vector3(step % 340 == 0 ? 10.0 : -10.0, 0.9, 0.0), + radius: 40, + ); + } + } + + /// Whether any actor is in a wait right now. + bool get waiting => system.actors.any( + (actor) => BehaviourBrain.pathOf(actor).any((s) => s.kind == 'wait'), + ); + + Map state() => { + 'ecs': system.entities.save(), + 'system': system.save(), + }; + + void restore(Map state) { + system.entities.restore((state['ecs']! as Map).cast()); + system + ..restore(state['system']) + ..syncCorpses(); + } +} + +const Map _arenaJson = { + 'kind': 'utility', + 'inertia': 0.1, + 'options': [ + { + 'name': 'close in', + 'considerations': [ + {'kind': 'seesFocus'}, + {'kind': 'focusDistance', 'from': 12, 'to': 4}, + ], + 'do': { + 'kind': 'sequence', + 'children': [ + {'kind': 'markFocus', 'key': 'lastSeen'}, + {'kind': 'goToFocus', 'within': 1.5}, + {'kind': 'wait', 'seconds': 0.5}, + ], + }, + }, + { + 'name': 'look into it', + 'weight': 0.8, + 'considerations': [ + { + 'kind': 'since', + 'key': 'heardAt', + 'from': 6, + 'to': 1, + }, + ], + 'do': { + 'kind': 'cooldown', + 'seconds': 2, + 'child': {'kind': 'goTo', 'key': 'heard', 'within': 1}, + }, + }, + { + 'name': 'patrol', + 'considerations': [ + {'kind': 'constant', 'value': 0.2}, + ], + 'do': { + 'kind': 'sequence', + 'children': [ + {'kind': 'goTo', 'key': 'postA'}, + {'kind': 'wait', 'seconds': 1}, + {'kind': 'goTo', 'key': 'postB'}, + {'kind': 'wait', 'seconds': 1}, + ], + }, + }, + ], +}; + +const List _arenaDigests = [ + '8e79608e', + '147317ff', + '86db2f1d', + '0e366189', + '4bab99a5', + '35708b46', + '79ff0948', + '893cf555', + '257ecce7', + '6bee2463', +]; diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 8e791b525..271c7a6fc 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11012 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11027 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index b4401df88..779387660 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,17 +1,17 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11012 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11027 tests need a GPU. --- # Testing -11012 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11027 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| | `flutter3d` | 1637 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | -| `flutter3d_sim` | 607 | | `pad_input` | 67 | +| `flutter3d_sim` | 620 | | `pad_input` | 67 | | `flutter3d_lab` | 15 | | `flutter3d_core` | 750 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | @@ -28,7 +28,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 143 | -| `flutter3d_game` | 287 | | `flutter3d_shaders` | 5 | +| `flutter3d_game` | 289 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | | `flutter3d_editor_play` | 26 | | `flutter3d_net` | 15 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10986 rather than 11012: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11001 rather than 11027: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 5fccea4aa..ede67d0cd 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -568,6 +568,27 @@ Lifted exclusions (decision 12), each its own item: someone else's replication (decision 22). - Behaviour on objects and level generators are N3 and N11. +**N3 progress.** *Done:* `BehaviourTree.read` takes a JSON tree +(`sequence` that resumes, `selector` that asks again from the top, `utility` +scoring options as weight times the product of considerations with inertia +for the running one, `invert`, `alwaysSucceed`, `cooldown`) and refuses a +document with every problem and where it is. Leaves and considerations are +registered by kind in `BehaviourKinds`; the standard ones are what `Mind` +already does. `BehaviourBrain` keeps nothing itself: path, per-node memory, +cooldowns, a clock and what it heard or felt are a `Blackboard` component in +the ECS save, and a board under a changed tree (by digest) starts again. +`BehaviourOverlay` in `flutter3d_game` draws the running path and the goal +through `Renderer.debugLines` without touching the boards. +*Tests:* `flutter3d_sim/test/behaviour_tree_test.dart` (reading, every node, +the board, a 600-step digest trace and a restore at step 300 that agrees to +the end while one without boards does not); +`flutter3d_game/test/behaviour_overlay_test.dart`. +*Left:* walking to a point goes straight (`Mind.steerTowards`) until N2's +navmesh can route there, and then `goTo` is replaced in `BehaviourKinds`; no +`parallel` node (one body steers one way); trees authored in the editor and +over MCP; the overlay's names shown in a demo rather than only returned by +`describe()`; the digest trace measured on a second operating system. + ## 6. Platforms (decision 16) - **Windows and Linux:** runners for the demos and the editor; native sides of diff --git a/tool/structure/repository.dart b/tool/structure/repository.dart index d645fc714..36ca91178 100644 --- a/tool/structure/repository.dart +++ b/tool/structure/repository.dart @@ -635,6 +635,13 @@ String relative(File file, Directory dir) => file.path /// the next one has to be argued for rather than typed. const Map> boundaryEnumExempt = >{ + 'flutter3d_sim/lib/src/actors/blackboard.dart': { + 'BehaviourStatus': + 'success, failure and running are the whole algebra a behaviour ' + "tree's composites are defined over. A fourth is not an outcome a " + "leaf is missing; it is a different kind of tree, and every " + 'composite would have to be rewritten for it anyway', + }, 'flame_flutter3d/lib/src/world/grid_mover.dart': { 'GridHeading': 'the four ways out of a square cell and standing still. A fifth ' From 3d5f1dcadf5aaf08291f993af7d46459d2d40c82 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:19:15 +0300 Subject: [PATCH 041/141] A paused run can be scrubbed, branched from, and bisected against another RunTimeline.scrubTo moves the live state to any held step while paused; returnToPresent puts the present back exactly and branchHere cuts the future at the scrubbed step. EntityTracks keeps one lane per component of each entity, holding only the steps it changed. bisectTapes replays two tapes and finds the first step after which they differ, naming the entity and component and whether that step's input differed, in about a dozen probes for a thousand steps. The editor's timeline panel has the scrubber, the branch and the lanes, over the new timeline extensions. --- ARCHITECTURE.md | 2 +- README.md | 2 +- .../lib/src/timeline_attach_screen.dart | 195 +++++++++++ .../lib/src/timeline_client.dart | 86 ++++- .../test/timeline_attach_screen_test.dart | 126 +++++++ packages/flutter3d_game/CHANGELOG.md | 11 + .../lib/src/run/run_timeline.dart | 244 +++++++++++++ .../lib/src/run/run_timeline_extensions.dart | 90 ++++- .../test/fixtures/timeline_target.dart | 6 + .../test/run_timeline_extensions_test.dart | 71 +++- .../test/run_timeline_scrub_test.dart | 195 +++++++++++ packages/flutter3d_sim/CHANGELOG.md | 14 + packages/flutter3d_sim/lib/flutter3d_sim.dart | 2 + .../lib/src/save/entity_tracks.dart | 245 +++++++++++++ .../flutter3d_sim/lib/src/save/rewind.dart | 4 + .../lib/src/save/tape_bisect.dart | 329 ++++++++++++++++++ .../test/entity_tracks_test.dart | 134 +++++++ .../flutter3d_sim/test/tape_bisect_test.dart | 247 +++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 12 +- tasks/0.9-engine-roadmap.md | 33 ++ 21 files changed, 2033 insertions(+), 17 deletions(-) create mode 100644 packages/flutter3d_game/test/run_timeline_scrub_test.dart create mode 100644 packages/flutter3d_sim/lib/src/save/entity_tracks.dart create mode 100644 packages/flutter3d_sim/lib/src/save/tape_bisect.dart create mode 100644 packages/flutter3d_sim/test/entity_tracks_test.dart create mode 100644 packages/flutter3d_sim/test/tape_bisect_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 8abafe0f1..77ce9c16a 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11027 tests** across 43 packages and 10 applications | +| Unit tests | **11050 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 2d59cbba5..547650552 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11027 tests across forty-three packages and nine applications. The +There are 11050 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_editor/lib/src/timeline_attach_screen.dart b/apps/flutter3d_editor/lib/src/timeline_attach_screen.dart index c77e8d39c..c0a8f7f6e 100644 --- a/apps/flutter3d_editor/lib/src/timeline_attach_screen.dart +++ b/apps/flutter3d_editor/lib/src/timeline_attach_screen.dart @@ -43,6 +43,17 @@ final class _TimelineAttachScreenState extends State { /// disable itself and nobody double-taps a file picker. bool _savingBugReport = false; + /// `N4`'s scrubber range; null until the first status arrives. + TimelineWindow? _window; + + /// Where the slider is while it is being dragged, before the drag ends and + /// the game is asked to move — one scrub per release, not per pixel. + double? _dragging; + + /// Lanes loaded on request; they cost the game a replay of its buffer, so + /// they are not polled. + List? _lanes; + @override void initState() { super.initState(); @@ -58,6 +69,7 @@ final class _TimelineAttachScreenState extends State { Future _refresh() async { final paused = await widget.client.status(); final history = await widget.client.history(); + final window = await widget.client.window(); // `frameTimes` is optional on the other end — a game that registered no // `StepTimeTrace` throws here, and that is silence, not a failure to // report through `_said`. @@ -71,10 +83,26 @@ final class _TimelineAttachScreenState extends State { setState(() { _paused = paused; _history = history; + _window = window; _frameTimes = frameTimes; }); } + /// A scrub or branch the game refused: its reason goes where any other + /// failure here goes, and one it carried out clears the last. Set before + /// [_run]'s refresh, whose `setState` draws it. + Future _answer(Future Function() action) => + _run(() async => _said = await action()); + + Future _loadTracks() async { + try { + final lanes = await widget.client.tracks(); + if (mounted) setState(() => _lanes = lanes); + } catch (error) { + if (mounted) setState(() => _said = '$error'); + } + } + Future _run(Future Function() action) async { try { await action(); @@ -106,6 +134,71 @@ final class _TimelineAttachScreenState extends State { } } + /// `N4`: a slider over the steps the game holds, the two ways out of a + /// scrub, and the lanes. Shown only while paused, since the game refuses a + /// scrub on a live run, and only once there is a range to scrub. + List _scrubber() { + final window = _window; + final oldest = window?.oldest; + if (!_paused || window == null || oldest == null) return const []; + if (window.present <= oldest) return const []; + final at = window.scrubbedAt ?? window.present; + final shown = (_dragging ?? at.toDouble()).round(); + final scrubbed = window.scrubbedAt != null; + return [ + const SizedBox(height: 16.0), + Text( + scrubbed || _dragging != null + ? 'Scrub: step $shown of ${window.present}' + : 'Scrub: at the present, step ${window.present}', + ), + Slider( + min: oldest.toDouble(), + max: window.present.toDouble(), + divisions: window.present - oldest, + value: (_dragging ?? at.toDouble()).clamp( + oldest.toDouble(), + window.present.toDouble(), + ), + onChanged: (value) => setState(() => _dragging = value), + onChangeEnd: (value) { + setState(() => _dragging = null); + _answer(() => widget.client.scrubTo(value.round())); + }, + ), + Row( + children: [ + OutlinedButton( + onPressed: scrubbed + ? () => _run(widget.client.returnToPresent) + : null, + child: const Text('Back to present'), + ), + const SizedBox(width: 8.0), + FilledButton.tonal( + onPressed: scrubbed + ? () => _answer(widget.client.branchHere) + : null, + child: const Text('Branch here'), + ), + const SizedBox(width: 8.0), + OutlinedButton( + onPressed: _loadTracks, + child: const Text('Load tracks'), + ), + ], + ), + if (_lanes case final lanes?) + _TrackLanes( + lanes: lanes, + oldest: oldest, + present: window.present, + cursor: at, + onTapStep: (step) => _answer(() => widget.client.scrubTo(step)), + ), + ]; + } + @override Widget build(BuildContext context) { return Scaffold( @@ -174,6 +267,7 @@ final class _TimelineAttachScreenState extends State { }), child: const Text('Release here'), ), + ..._scrubber(), if (_said != null) ...[ const SizedBox(height: 8.0), Text( @@ -271,3 +365,104 @@ final class _FrameTimeStrip extends StatelessWidget { ); } } + +/// `N4`'s lanes: one row per component of each entity, a mark at every step +/// it changed, placed along the same range as the scrubber above, and a +/// line where the live state is. Tapping a mark scrubs there — the change +/// that looks wrong is one tap from the state it happened in. +final class _TrackLanes extends StatelessWidget { + const _TrackLanes({ + required this.lanes, + required this.oldest, + required this.present, + required this.cursor, + required this.onTapStep, + }); + + final List lanes; + final int oldest; + final int present; + final int cursor; + final ValueChanged onTapStep; + + static const double _laneHeight = 18.0; + static const double _labelWidth = 160.0; + + @override + Widget build(BuildContext context) { + if (lanes.isEmpty) { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Text('no entities in what the game holds'), + ); + } + final colours = Theme.of(context).colorScheme; + final span = (present - oldest).clamp(1, 1 << 30); + return SizedBox( + height: (lanes.length * _laneHeight).clamp(_laneHeight, 120.0), + child: ListView( + children: [ + for (final lane in lanes) + SizedBox( + height: _laneHeight, + child: Row( + children: [ + SizedBox( + width: _labelWidth, + child: Text( + '${lane.entity} · ${lane.component}', + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 12), + ), + ), + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + double x(int step) => + constraints.maxWidth * (step - oldest) / span; + return Stack( + children: [ + Positioned( + left: x(cursor), + top: 0, + bottom: 0, + child: Container( + width: 1.0, + color: colours.error, + ), + ), + for (final (i, step) in lane.steps.indexed) + Positioned( + left: (x(step) - 2.0).clamp( + 0.0, + constraints.maxWidth - 4.0, + ), + top: 3.0, + child: Tooltip( + message: 'step $step: ${lane.values[i]}', + child: GestureDetector( + key: ValueKey( + '${lane.entity}.${lane.component}@$step', + ), + onTap: () => onTapStep(step), + child: Container( + width: 4.0, + height: _laneHeight - 6.0, + color: colours.primary, + ), + ), + ), + ), + ], + ); + }, + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/apps/flutter3d_editor/lib/src/timeline_client.dart b/apps/flutter3d_editor/lib/src/timeline_client.dart index e40973677..d040528cc 100644 --- a/apps/flutter3d_editor/lib/src/timeline_client.dart +++ b/apps/flutter3d_editor/lib/src/timeline_client.dart @@ -9,6 +9,21 @@ typedef TimelinePreview = ({bool found, int? step}); /// for the one shape it reads out of it. typedef StepCosts = ({List steps, List millis}); +/// What `ext.flutter3d.timeline.status` answers: the two ends of the +/// scrubber — the oldest step a rewind reaches and the present — and where +/// the live state is scrubbed to, null at the present. +typedef TimelineWindow = ({int present, int? oldest, int? scrubbedAt}); + +/// One lane of `ext.flutter3d.timeline.tracks`: the steps one component of +/// one entity changed at, and what it changed to, as text — the panel draws +/// marks and tooltips and never reads the value back. +typedef TrackLane = ({ + String entity, + String component, + List steps, + List values, +}); + /// What a running game's `RunTimeline` looks like from outside it. /// /// An interface rather than [VmServiceTimelineClient] directly, so @@ -49,6 +64,24 @@ abstract interface class TimelineClient { /// if that callback itself threw (nothing recorded yet, most likely). Future> bugReport(); + /// `N4`: the scrubber's range and position. + Future window(); + + /// Moves the live state to before [step] without cutting anything. Null + /// when it moved; otherwise the running game's reason it did not. + Future scrubTo(int step); + + /// Puts the present back after a scrub; false when there was none. + Future returnToPresent(); + + /// Makes the scrubbed step the present. Null when it did; otherwise the + /// reason it did not. + Future branchHere(); + + /// Every lane over the steps the game holds, entity by entity. Throws if + /// the running game registered no entity layout to read them through. + Future> tracks(); + /// Closes the connection. Safe to call more than once. Future dispose(); } @@ -58,7 +91,7 @@ abstract interface class TimelineClient { /// /// **The same channel DevTools and `flutter attach` use, and nothing else.** /// This does not start a game, does not know what a genre is, and does not -/// hold a level document — it only speaks the seven +/// hold a level document — it only speaks the /// `ext.flutter3d.timeline.*` extensions `registerTimelineExtensions` /// registers on the other end, over `connectVmService`'s WebSocket, which is /// `dart:io`'s on a desktop and the browser's in a web build. @@ -144,6 +177,57 @@ final class VmServiceTimelineClient implements TimelineClient { @override Future> bugReport() => _call('bugReport'); + @override + Future window() async { + final json = await _call('status'); + // A game built before `N4` answers `paused` alone; it reads as a window + // with nothing in it, which hides the scrubber rather than failing. + return ( + present: json['step'] as int? ?? 0, + oldest: json['oldest'] as int?, + scrubbedAt: json['scrubbedAt'] as int?, + ); + } + + @override + Future scrubTo(int step) async => + _refusal(await _call('scrubTo', {'step': '$step'})); + + @override + Future returnToPresent() async => + (await _call('returnToPresent'))['returned']! as bool; + + @override + Future branchHere() async => _refusal(await _call('branchHere')); + + static String? _refusal(Map json) => + json['moved'] == true ? null : '${json['refusal']}'; + + @override + Future> tracks() async { + final json = await _call('tracks'); + final entities = json['entities']; + if (entities is! Map) return const []; + return [ + for (final MapEntry(key: entity, value: lanes) in entities.entries) + if (lanes is Map) + for (final MapEntry(key: component, value: samples) in lanes.entries) + if (samples is List) + ( + entity: entity, + component: component, + steps: [ + for (final sample in samples.cast>()) + sample['step']! as int, + ], + values: [ + for (final sample in samples.cast>()) + sample['absent'] == true ? 'absent' : '${sample['value']}', + ], + ), + ]; + } + @override Future dispose() => _service.dispose(); } diff --git a/apps/flutter3d_editor/test/timeline_attach_screen_test.dart b/apps/flutter3d_editor/test/timeline_attach_screen_test.dart index 36884dc89..6da4efdd1 100644 --- a/apps/flutter3d_editor/test/timeline_attach_screen_test.dart +++ b/apps/flutter3d_editor/test/timeline_attach_screen_test.dart @@ -83,6 +83,46 @@ final class _FakeTimelineClient implements TimelineClient { @override Future> bugReport() async => nextBugReport; + /// `N4`: a buffer from [oldest] to [present], scrubbed to [scrubbedAt]. + int present = 600; + int? oldest = 0; + int? scrubbedAt; + String? scrubRefusal; + List nextLanes = const []; + + @override + Future window() async => + (present: present, oldest: oldest, scrubbedAt: scrubbedAt); + + @override + Future scrubTo(int step) async { + if (scrubRefusal != null) return scrubRefusal; + scrubbedAt = step == present ? null : step; + commands.add('scrubbed:$step'); + return null; + } + + @override + Future returnToPresent() async { + final was = scrubbedAt != null; + scrubbedAt = null; + if (was) commands.add('returned'); + return was; + } + + @override + Future branchHere() async { + final at = scrubbedAt; + if (at == null) return 'scrub to a step first'; + present = at; + scrubbedAt = null; + commands.add('branched:$at'); + return null; + } + + @override + Future> tracks() async => nextLanes; + @override Future dispose() async {} } @@ -271,4 +311,90 @@ void main() { expect(button.onPressed, isNotNull, reason: 'usable again once saved'); }, ); + + group('N4 scrubber', () { + testWidgets('is hidden on a live run, which would refuse a scrub', ( + tester, + ) async { + // Mutation: drop the `_paused` check in `_scrubber`. The slider is + // then offered on a run that refuses every scrub it sends. + await _pump(tester, _FakeTimelineClient()); + expect(find.byType(Slider), findsNothing); + }); + + testWidgets('a released drag scrubs once, and the way back and the branch ' + 'come alive', (tester) async { + // Mutation: scrub from `onChanged`. Every pixel of a drag is then a + // replay in the game, and `commands` holds dozens of scrubs. + final client = _FakeTimelineClient()..paused = true; + await _pump(tester, client); + expect(find.text('Scrub: at the present, step 600'), findsOneWidget); + final branch = find.widgetWithText(FilledButton, 'Branch here'); + expect(tester.widget(branch).onPressed, isNull); + + final slider = tester.widget(find.byType(Slider)); + slider.onChanged!(120.0); + slider.onChanged!(150.0); + slider.onChangeEnd!(150.0); + await tester.pumpAndSettle(); + + expect(client.commands, ['scrubbed:150']); + expect(find.text('Scrub: step 150 of 600'), findsOneWidget); + expect(tester.widget(branch).onPressed, isNotNull); + + await tester.tap(find.text('Back to present')); + await tester.pumpAndSettle(); + expect(client.commands.last, 'returned'); + expect(find.text('Scrub: at the present, step 600'), findsOneWidget); + + tester.widget(find.byType(Slider)).onChangeEnd!(90.0); + await tester.pumpAndSettle(); + await tester.tap(branch); + await tester.pumpAndSettle(); + expect(client.commands.last, 'branched:90'); + expect(find.text('Scrub: at the present, step 90'), findsOneWidget); + }); + + testWidgets('a refused scrub says why', (tester) async { + // Mutation: ignore the answer of `scrubTo`. The panel then shows the + // scrubbed step while the game is still at the present. + final client = _FakeTimelineClient() + ..paused = true + ..scrubRefusal = 'step 5 is not held; the buffer reaches from 0'; + await _pump(tester, client); + + tester.widget(find.byType(Slider)).onChangeEnd!(5.0); + await tester.pumpAndSettle(); + + expect(find.textContaining('is not held'), findsOneWidget); + expect(client.commands, isEmpty); + }); + + testWidgets('lanes load on request, and a mark scrubs to its step', ( + tester, + ) async { + // Mutation: hand the mark's tap to `releaseAtStep` like the frame + // strip. Tapping a change would then cut the future it sits in. + final client = _FakeTimelineClient() + ..paused = true + ..nextLanes = [ + ( + entity: '7', + component: 'Health', + steps: [0, 412], + values: ['100', '93'], + ), + ]; + await _pump(tester, client); + expect(find.text('7 · Health'), findsNothing); + + await tester.tap(find.text('Load tracks')); + await tester.pumpAndSettle(); + expect(find.text('7 · Health'), findsOneWidget); + + await tester.tap(find.byKey(const ValueKey('7.Health@412'))); + await tester.pumpAndSettle(); + expect(client.commands, ['scrubbed:412']); + }); + }); } diff --git a/packages/flutter3d_game/CHANGELOG.md b/packages/flutter3d_game/CHANGELOG.md index 2731cbe51..f489a9181 100644 --- a/packages/flutter3d_game/CHANGELOG.md +++ b/packages/flutter3d_game/CHANGELOG.md @@ -21,6 +21,17 @@ a line to where its leaf is taking it; `describe()` gives the same path by name. It reads boards and never makes one, so switching it on cannot change a snapshot. +- **`RunTimeline` scrubs without cutting.** `scrubTo` moves the live state + to a held step while paused and keeps the tape and the present; + `returnToPresent` puts the present back exactly, `branchHere` cuts the + future at the scrubbed step and stays paused, and `stepOnce` from a scrub + walks the tape. `resume` from a scrub resumes at the present. `tracks` + replays the buffer into `EntityTracks` and puts the live state back. + Refusals come back as `ScrubRefused` with a reason. +- **`registerTimelineExtensions`** adds `scrubTo`, `returnToPresent`, + `branchHere` and, with an `entityLayout`, `tracks`; `status` answers the + present step, the oldest held and the scrub. + - **`replayAfterHotSwap` asks the question a hot reload leaves open on its own.** After every `HotSwap` it lives the last seconds again under the new code and says whether they came out the same or where they parted, in the diff --git a/packages/flutter3d_game/lib/src/run/run_timeline.dart b/packages/flutter3d_game/lib/src/run/run_timeline.dart index 41c359317..67da57de8 100644 --- a/packages/flutter3d_game/lib/src/run/run_timeline.dart +++ b/packages/flutter3d_game/lib/src/run/run_timeline.dart @@ -27,6 +27,32 @@ final class TimelineStepped extends TimelineCommand { final class TimelineBranched extends TimelineCommand { const TimelineBranched(this.step); final int step; + + @override + bool operator ==(Object other) => + other is TimelineBranched && other.step == step; + + @override + int get hashCode => step.hashCode; +} + +/// The live state was moved to the state before [step] to be looked at; the +/// tape and the present are kept, and nothing has branched yet. +final class TimelineScrubbed extends TimelineCommand { + const TimelineScrubbed(this.step); + final int step; + + @override + bool operator ==(Object other) => + other is TimelineScrubbed && other.step == step; + + @override + int get hashCode => step.hashCode; +} + +/// A scrub was left, and the present put back as it was. +final class TimelineReturned extends TimelineCommand { + const TimelineReturned(); } /// The level under the run was replaced, taking effect before [step]: the @@ -132,6 +158,40 @@ final class CodeReplay { }; } +/// What [RunTimeline.scrubTo] and [RunTimeline.branchHere] answered. +sealed class ScrubAnswer { + const ScrubAnswer(); + + Map toJson(); +} + +/// The live state is now the state before [step]. +final class ScrubMoved extends ScrubAnswer { + const ScrubMoved(this.step); + final int step; + + @override + Map toJson() => { + 'moved': true, + 'step': step, + }; +} + +/// Nothing moved, and [reason] says why and what to do instead. +final class ScrubRefused extends ScrubAnswer { + const ScrubRefused(this.reason); + final String reason; + + @override + Map toJson() => { + 'moved': false, + 'refusal': reason, + }; + + @override + String toString() => reason; +} + /// Pause, step, rewind and branch, built on a live [RewindBuffer]. /// /// **What `rp-02`'s editor panel is a face for, not the panel itself.** The @@ -205,8 +265,12 @@ final class RunTimeline { _history.add(const TimelinePaused()); } + /// Lets the run go on. From a scrub, the present is put back first: the + /// run resumes where it was paused, and going on from the scrubbed moment + /// instead is [branchHere], asked for by name. void resume() { if (!_paused) return; + returnToPresent(); _paused = false; _history.add(const TimelineResumed()); } @@ -214,10 +278,20 @@ final class RunTimeline { /// Runs one fixed step. Only while [isPaused] — a step taken on a running /// timeline would be a second step nobody asked for, on top of whatever is /// driving the loop already. + /// + /// From a scrub this moves the scrub one step along the tape rather than + /// stepping the simulation off it, so "step" in a debugger walks the + /// recorded run; at the present the scrub ends. void stepOnce() { if (!_paused) { throw StateError('stepOnce is only valid while the timeline is paused'); } + final at = _scrubbedAt; + if (at != null) { + _scrubForward(at + 1); + _history.add(const TimelineStepped()); + return; + } input.beginStep(); stepSim(stepSeconds); input.endStep(); @@ -239,6 +313,7 @@ final class RunTimeline { /// release is asking to keep playing from here, not to pause on arrival — /// call [pause] afterwards for that. void releaseAt(RewindPoint point) { + _forgetScrub(); restore(point.snapshot); final toPoint = InputTapePlayback(point.tapeToPoint); final wasMuted = input.muted; @@ -279,6 +354,7 @@ final class RunTimeline { /// Returns the step the new level took effect before, which is what /// [TimelineLevelSwapped] records. int swapLevel(void Function() swap, {required String levelDigest}) { + returnToPresent(); final point = rewind.rewindTo(rewind.step); if (point == null) { swap(); @@ -327,6 +403,7 @@ final class RunTimeline { required double seconds, required Snapshot Function() capture, }) { + returnToPresent(); final now = rewind.step; final point = rewind.rewindBy(seconds); if (point == null) return null; @@ -389,4 +466,171 @@ final class RunTimeline { releaseAt(point); return true; } + + /// The present, written down when a scrub began; null while not scrubbed. + Snapshot? _present; + + int? _scrubbedAt; + + /// The step the live state is scrubbed to, or null at the present. + int? get scrubbedAt => _scrubbedAt; + + /// Puts the live state where the run was before [step], to be looked at, + /// keeping the tape and the present: the scrubber of a time-travel + /// debugger. + /// + /// **Not [releaseAt].** A release cuts the buffer, so dragging back and + /// forth through it would forget the future on the first drag. A scrub + /// keeps everything: [capture] writes the present down once, on the first + /// scrub, and [returnToPresent] restores it exactly; going on from the + /// scrubbed moment is [branchHere]. Scrubbing forward from a scrubbed step + /// plays on from there instead of from the keyframe, so a drag to the + /// right costs the distance dragged. + /// + /// Only while paused, since the loop would step the scrubbed state as if it + /// were the present. A scrub to the present is [returnToPresent]. + ScrubAnswer scrubTo(int step, {required Snapshot Function() capture}) { + if (!_paused) { + return const ScrubRefused( + 'the run is live, so a scrub would be stepped on by the loop; pause ' + 'it first', + ); + } + if (step == rewind.step) { + returnToPresent(); + return ScrubMoved(step); + } + final point = rewind.rewindTo(step); + if (point == null) { + return ScrubRefused(switch (rewind.oldestStep) { + null => + 'nothing is held yet to scrub through; let the run play a ' + 'keyframe interval first', + final oldest => + 'step $step is not held; the buffer reaches from step ' + '$oldest to ${rewind.step}', + }); + } + _present ??= capture(); + _scrubTo(point); + _history.add(TimelineScrubbed(step)); + return ScrubMoved(step); + } + + /// Puts the present back after a scrub. Does nothing at the present, and + /// answers whether there was a scrub to leave. + bool returnToPresent() { + final present = _present; + if (present == null) return false; + restore(present); + _forgetScrub(); + _history.add(const TimelineReturned()); + return true; + } + + /// Makes the scrubbed moment the present: the tape after it is cut, as + /// [releaseAt] cuts it, and the run goes on from here when resumed. + /// + /// **Stays paused**, unlike [releaseAt]: the person branching is looking + /// at the moment they chose, and the first step of the new branch is + /// theirs to take. + ScrubAnswer branchHere() { + final at = _scrubbedAt; + final point = at == null ? null : rewind.rewindTo(at); + if (at == null || point == null) { + return const ScrubRefused( + 'the run is at the present, which is already where it goes on ' + 'from; scrub to a step first', + ); + } + rewind.cut(point); + _forgetScrub(); + _history.add(TimelineBranched(at)); + return ScrubMoved(at); + } + + /// What every entity did over the steps the buffer holds, read through + /// [layout] from a [capture] of each step. + /// + /// The steps are lived again from the oldest keyframe with the devices + /// muted and the live state is put back afterwards — at the present or at + /// the scrub, wherever it was — so asking costs a replay of the buffer and + /// changes nothing. [every] reads one step in so many, for a buffer whose + /// snapshots are too large to take sixty times a second of history. Null + /// before the first keyframe. + EntityTracks? tracks({ + required Snapshot Function() capture, + required EntityLayout layout, + int every = 1, + }) { + final oldest = rewind.oldestStep; + final point = oldest == null ? null : rewind.rewindTo(oldest); + if (oldest == null || point == null) return null; + final here = capture(); + final tracks = EntityTracks(layout); + restore(point.snapshot); + tracks.observe(oldest, capture()); + final playback = InputTapePlayback( + InputTape(seed: point.seed, frames: point.frames), + ); + final wasMuted = input.muted; + input.muted = true; + try { + while (!playback.isFinished) { + playback.applyTo(input); + input.beginStep(); + stepSim(stepSeconds); + input.endStep(); + final step = oldest + playback.step; + if (step % every == 0 || step == rewind.step) { + tracks.observe(step, capture()); + } + } + } finally { + input.muted = wasMuted; + } + restore(here); + return tracks; + } + + void _scrubTo(RewindPoint point) { + final base = point.step - point.replayed; + final at = _scrubbedAt; + final from = at != null && at >= base && at <= point.step ? at : base; + if (from != at) restore(point.snapshot); + _playMuted(point.frames.sublist(from - base, point.replayed), point.seed); + _scrubbedAt = point.step; + } + + /// [stepOnce] from a scrub: one step further along the tape, which ends the + /// scrub when it reaches the present. + void _scrubForward(int step) { + final point = rewind.rewindTo(step); + if (step >= rewind.step || point == null) { + returnToPresent(); + return; + } + _scrubTo(point); + } + + void _forgetScrub() { + _present = null; + _scrubbedAt = null; + } + + void _playMuted(List frames, int seed) { + final playback = InputTapePlayback(InputTape(seed: seed, frames: frames)); + final wasMuted = input.muted; + input.muted = true; + try { + while (!playback.isFinished) { + playback.applyTo(input); + input.beginStep(); + stepSim(stepSeconds); + input.endStep(); + } + } finally { + input.muted = wasMuted; + } + } } diff --git a/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart b/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart index 001248cbc..c17e9f67b 100644 --- a/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart +++ b/packages/flutter3d_game/lib/src/run/run_timeline_extensions.dart @@ -28,7 +28,7 @@ import 'run_timeline.dart'; /// /// ## What a caller on the other end sees /// -/// Seven extensions always, and up to two more depending on what the caller +/// Nine extensions always, and up to five more depending on what the caller /// hands over — each named `ext.flutter3d.timeline.`, callable the way /// any `package:vm_service` client calls one — /// `service.callServiceExtension(isolateId, method: name, args: params)` — @@ -44,9 +44,20 @@ import 'run_timeline.dart'; /// * `history` — no parameters; returns `{"commands": [...]}`, one short /// string per [TimelineCommand] — `"paused"`, `"resumed"`, `"stepped"`, or /// `"branched:118"` naming the step a branch landed at. -/// * `status` — no parameters; returns `{"paused": true}` — what a panel -/// polls to draw its own pause/play button correctly on first connecting, -/// rather than assuming the timeline was already running. +/// * `status` — no parameters; returns `{"paused": true, "step": 600, +/// "oldest": 0, "scrubbedAt": null}` — what a panel polls to draw its own +/// pause/play button correctly on first connecting, rather than assuming +/// the timeline was already running, and the two ends of its scrubber. +/// * `returnToPresent` — no parameters; returns `{"returned": true}`, false +/// when there was no scrub to leave. +/// * `branchHere` — no parameters; returns `{"moved": true, "step": 118}`, +/// or `{"moved": false, "refusal": "..."}` at the present. +/// * `scrubTo` — registered only when [capture] is given; `step`, an integer +/// as a string; the same answer as `branchHere`. `N4`'s scrubber. +/// * `tracks` — registered only when [capture] and [entityLayout] are given; +/// `every`, steps between reads (one when absent); returns +/// [EntityTracks.toJson] over the steps the buffer holds, or +/// `{"reached": false}` before the first keyframe. /// * `frameTimes` — registered only when [frameTimes] is given; no /// parameters, returns that [StepTimeTrace]'s own [StepTimeTrace.toJson] — /// `rp-06`'s strip, read from wherever the caller is already timing its @@ -72,6 +83,7 @@ void registerTimelineExtensions( StepTimeTrace? frameTimes, Map Function()? bugReport, Snapshot Function()? capture, + EntityLayout? entityLayout, }) { developer.registerExtension('ext.flutter3d.timeline.pause', ( method, @@ -159,7 +171,30 @@ void registerTimelineExtensions( parameters, ) async { return developer.ServiceExtensionResponse.result( - jsonEncode({'paused': timeline.isPaused}), + jsonEncode({ + 'paused': timeline.isPaused, + 'step': timeline.rewind.step, + 'oldest': timeline.rewind.oldestStep, + 'scrubbedAt': timeline.scrubbedAt, + }), + ); + }); + + developer.registerExtension('ext.flutter3d.timeline.returnToPresent', ( + method, + parameters, + ) async { + return developer.ServiceExtensionResponse.result( + jsonEncode({'returned': timeline.returnToPresent()}), + ); + }); + + developer.registerExtension('ext.flutter3d.timeline.branchHere', ( + method, + parameters, + ) async { + return developer.ServiceExtensionResponse.result( + jsonEncode(timeline.branchHere().toJson()), ); }); @@ -174,6 +209,49 @@ void registerTimelineExtensions( }); } + if (capture != null) { + developer.registerExtension('ext.flutter3d.timeline.scrubTo', ( + method, + parameters, + ) async { + final step = int.tryParse(parameters['step'] ?? ''); + if (step == null) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + 'step must be an integer', + ); + } + return developer.ServiceExtensionResponse.result( + jsonEncode(timeline.scrubTo(step, capture: capture).toJson()), + ); + }); + } + + if (capture != null && entityLayout != null) { + developer.registerExtension('ext.flutter3d.timeline.tracks', ( + method, + parameters, + ) async { + final every = int.tryParse(parameters['every'] ?? '') ?? 1; + if (every < 1) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + 'every must be a whole number of steps, one or more', + ); + } + final tracks = timeline.tracks( + capture: capture, + layout: entityLayout, + every: every, + ); + return developer.ServiceExtensionResponse.result( + jsonEncode( + tracks?.toJson() ?? const {'reached': false}, + ), + ); + }); + } + if (capture != null) { developer.registerExtension('ext.flutter3d.timeline.replayUnderNewCode', ( method, @@ -219,6 +297,8 @@ String _describe(TimelineCommand command) => switch (command) { TimelineLevelSwapped(:final step, :final levelDigest) => 'level:$step:$levelDigest', TimelineReplayed(:final fromStep) => 'replayed:$fromStep', + TimelineScrubbed(:final step) => 'scrubbed:$step', + TimelineReturned() => 'returned', }; /// Puts [tunables] on the VM service, so an inspector can change them while diff --git a/packages/flutter3d_game/test/fixtures/timeline_target.dart b/packages/flutter3d_game/test/fixtures/timeline_target.dart index 025812034..5c737457c 100644 --- a/packages/flutter3d_game/test/fixtures/timeline_target.dart +++ b/packages/flutter3d_game/test/fixtures/timeline_target.dart @@ -32,6 +32,11 @@ final class _Toy { Snapshot save() => Snapshot({ 'x': x, + // `N4`'s tracks read the toy as one entity; restore ignores it, since + // it repeats `x`. + 'entities': { + 'toy': {'x': x}, + }, 'random': dice.state, 'tunables': tunables.toJson(), }); @@ -80,6 +85,7 @@ void main() { registerTimelineExtensions( timeline, capture: toy.save, + entityLayout: EntityLayout.rows('entities'), frameTimes: frameTimes, bugReport: () => {'x': toy.x, 'step': stepNumber}, ); diff --git a/packages/flutter3d_game/test/run_timeline_extensions_test.dart b/packages/flutter3d_game/test/run_timeline_extensions_test.dart index 67aab1fd9..b026a32ea 100644 --- a/packages/flutter3d_game/test/run_timeline_extensions_test.dart +++ b/packages/flutter3d_game/test/run_timeline_extensions_test.dart @@ -149,7 +149,7 @@ void main() { 'ext.flutter3d.timeline.status', isolateId: target.isolateId, ); - expect(_decode(statusBefore), {'paused': false}); + expect(_decode(statusBefore), containsPair('paused', false)); final paused = await target.service.callServiceExtension( 'ext.flutter3d.timeline.pause', @@ -161,7 +161,7 @@ void main() { 'ext.flutter3d.timeline.status', isolateId: target.isolateId, ); - expect(_decode(statusAfter), {'paused': true}); + expect(_decode(statusAfter), containsPair('paused', true)); // stepOnce refuses when the timeline is not paused, from the outside // exactly as it does from a direct call — proved by asking for a @@ -385,4 +385,71 @@ void main() { }, timeout: const Timeout(Duration(minutes: 3)), ); + + test('N4: scrub, read the tracks, go back, and branch, from outside the ' + 'process', () async { + final target = await _startAndConnect(); + addTearDown(() { + target.service.dispose(); + target.process.kill(); + }); + await Future.delayed(const Duration(seconds: 2)); + + Future> call( + String verb, [ + Map? args, + ]) async => _decode( + await target.service.callServiceExtension( + 'ext.flutter3d.timeline.$verb', + isolateId: target.isolateId, + args: args, + ), + ); + + // Mutation: scrub without the pause check. The fixture's loop then + // steps the scrubbed state and the window below moves under it. + final refused = await call('scrubTo', {'step': '10'}); + expect(refused['moved'], isFalse); + expect(refused['refusal'], contains('pause')); + + await call('pause'); + final window = await call('status'); + final present = window['step']! as int; + final oldest = window['oldest']! as int; + expect(present, greaterThan(oldest)); + + final scrub = oldest + (present - oldest) ~/ 2; + expect(await call('scrubTo', {'step': '$scrub'}), { + 'moved': true, + 'step': scrub, + }); + expect((await call('status'))['scrubbedAt'], scrub); + + final tracks = await call('tracks'); + expect(tracks['first'], oldest); + expect(tracks['last'], present); + final lane = + ((tracks['entities']! as Map)['toy']! + as Map)['x']! + as List; + expect(lane, isNotEmpty, reason: 'the toy moves every step'); + + expect(await call('returnToPresent'), {'returned': true}); + expect((await call('status'))['scrubbedAt'], isNull); + + await call('scrubTo', {'step': '$scrub'}); + expect(await call('branchHere'), {'moved': true, 'step': scrub}); + final after = await call('status'); + expect(after['step'], scrub); + expect(after['paused'], isTrue); + + final history = (await call('history'))['commands']! as List; + expect(history, [ + 'paused', + 'scrubbed:$scrub', + 'returned', + 'scrubbed:$scrub', + 'branched:$scrub', + ]); + }, timeout: const Timeout(Duration(minutes: 3))); } diff --git a/packages/flutter3d_game/test/run_timeline_scrub_test.dart b/packages/flutter3d_game/test/run_timeline_scrub_test.dart new file mode 100644 index 000000000..0d40a5a75 --- /dev/null +++ b/packages/flutter3d_game/test/run_timeline_scrub_test.dart @@ -0,0 +1,195 @@ +/// `N4`'s time-travel debugger on a live run: scrub through the buffer +/// without losing the future, go back to the present exactly, branch from a +/// scrubbed moment, and read every entity's lanes over what is held. +/// +/// flutter test test/run_timeline_scrub_test.dart +library; + +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; + +const GameAction _fire = GameAction('fire'); + +/// Two entities: a runner that follows the stick and a target that loses +/// health, by the dice, whenever fire is pressed. +final class _Toy { + _Toy(int seed) : dice = GameRandom(seed); + + final GameRandom dice; + double x = 0.0; + int hp = 1000; + + void step(InputState input) { + x += input.moveAxis.x; + if (input.pressed(_fire)) hp -= 1 + dice.nextInt(9); + } + + Snapshot save() => Snapshot({ + 'entities': { + 'runner': {'x': x}, + 'target': {'hp': hp}, + }, + 'random': dice.state, + }); + + void restore(Snapshot snapshot) { + final entities = snapshot.data.object('entities')!; + x = entities.object('runner')!.number('x'); + hp = entities.object('target')!.integer('hp'); + dice.state = snapshot.data.integer('random'); + } + + String get state => '$x/$hp/${dice.state}'; +} + +void _play(InputState input, int step) { + input.setStickAxis(step % 3 == 0 ? 1.0 : -0.5, 0.0); + if (step % 11 == 0) input.press(_fire); + if (step % 11 == 4) input.release(_fire); +} + +/// A run of [steps] steps under a game loop's own order, with the state +/// before every step written down to compare a scrub against. +({_Toy toy, RewindBuffer rewind, RunTimeline timeline, Map seen}) +_run(int steps) { + final toy = _Toy(3); + final input = InputState(); + final rewind = RewindBuffer(stepsPerSecond: 60, history: 10.0); + final timeline = RunTimeline( + rewind: rewind, + input: input, + stepSim: (dt) => toy.step(input), + restore: toy.restore, + ); + final seen = {}; + for (var step = 0; step < steps; step++) { + seen[step] = toy.state; + _play(input, step); + rewind.recorder.record(input); + input.beginStep(); + if (rewind.keyframeDue) rewind.keyframe(toy.save()); + toy.step(input); + input.endStep(); + } + seen[steps] = toy.state; + return (toy: toy, rewind: rewind, timeline: timeline, seen: seen); +} + +void main() { + test('a scrub lands on the state the run had before that step, back and ' + 'forth, and the present comes back exactly', () { + // Mutation: play forward from the scrubbed step without checking it is + // at or after the keyframe — a drag to the left then plays on from the + // right and lands in the future. + final run = _run(300); + run.timeline.pause(); + + for (final step in [200, 230, 130, 131, 299, 60]) { + expect( + run.timeline.scrubTo(step, capture: run.toy.save), + isA(), + ); + expect(run.toy.state, run.seen[step], reason: 'scrubbed to $step'); + } + expect(run.timeline.scrubbedAt, 60); + expect(run.rewind.step, 300, reason: 'a scrub keeps the tape whole'); + + expect(run.timeline.returnToPresent(), isTrue); + expect(run.toy.state, run.seen[300]); + expect(run.timeline.scrubbedAt, isNull); + expect(run.timeline.returnToPresent(), isFalse); + }); + + test('stepping from a scrub walks the tape, and ends at the present', () { + // Mutation: let `stepOnce` step the simulation while scrubbed. The state + // then leaves the tape, and the next scrub restores from a keyframe as + // if nothing happened. + final run = _run(300); + run.timeline + ..pause() + ..scrubTo(297, capture: run.toy.save) + ..stepOnce(); + expect(run.toy.state, run.seen[298]); + run.timeline + ..stepOnce() + ..stepOnce(); + expect(run.timeline.scrubbedAt, isNull); + expect(run.toy.state, run.seen[300]); + }); + + test('a scrub on a live run, or outside the buffer, is refused with a ' + 'reason, and nothing moves', () { + // Mutation: scrub a live run. The loop would step the scrubbed state as + // if it were the present, and the run would jump. + final run = _run(300); + final live = run.timeline.scrubTo(100, capture: run.toy.save); + expect(live, isA()); + expect('$live', contains('pause')); + + run.timeline.pause(); + final far = run.timeline.scrubTo(400, capture: run.toy.save); + expect('$far', contains('from step 0 to 300')); + expect(run.toy.state, run.seen[300]); + expect(run.timeline.branchHere(), isA()); + }); + + test('branching from a scrub cuts the future there and stays paused', () { + // Mutation: branch without the cut. The buffer then still holds the + // future the branch replaced, and a later rewind lands in it. + final run = _run(300); + run.timeline + ..pause() + ..scrubTo(150, capture: run.toy.save); + + expect(run.timeline.branchHere(), isA()); + expect(run.rewind.step, 150); + expect(run.toy.state, run.seen[150]); + expect(run.timeline.isPaused, isTrue); + expect(run.timeline.history, [ + const TimelinePaused(), + const TimelineScrubbed(150), + const TimelineBranched(150), + ]); + }); + + test('resuming from a scrub goes on from the present, not the scrub', () { + // Mutation: drop the `returnToPresent` in `resume`. The loop then goes on + // from step 120 with a tape that says 300. + final run = _run(300); + run.timeline + ..pause() + ..scrubTo(120, capture: run.toy.save) + ..resume(); + expect(run.toy.state, run.seen[300]); + expect(run.rewind.step, 300); + }); + + test('tracks hold every change each entity made over the buffer, and ' + 'reading them changes nothing', () { + // Mutation: drop the final restore in `tracks`. The live state is then + // the end of a replay that took one more step's capture, and a scrub + // left open is lost. + final run = _run(300); + run.timeline + ..pause() + ..scrubTo(140, capture: run.toy.save); + final tracks = run.timeline.tracks( + capture: run.toy.save, + layout: EntityLayout.rows('entities'), + )!; + + expect(run.toy.state, run.seen[140]); + expect(tracks.entities, ['runner', 'target']); + expect(tracks.span, (first: 0, last: 300)); + // Fire is pressed on every eleventh step from 0, so the target's health + // changes on the step after each press, 28 times in 300 steps. + final hits = tracks.samples('target', 'hp'); + expect(hits.length, 1 + 28); + expect(hits[1].step, 1); + expect( + tracks.at('runner', 'x', 140)!.value, + double.parse(run.seen[140]!.split('/').first), + ); + }); +} diff --git a/packages/flutter3d_sim/CHANGELOG.md b/packages/flutter3d_sim/CHANGELOG.md index 1a5c2baf4..c2766ecb8 100644 --- a/packages/flutter3d_sim/CHANGELOG.md +++ b/packages/flutter3d_sim/CHANGELOG.md @@ -28,6 +28,20 @@ `Blackboard` component, so a snapshot or a rewind brings a decision back half made. A board ticked by another tree starts again rather than resuming at node numbers that now name something else. +- **`bisectTapes` finds where two runs part.** Each side is a + `ReplaySide` — a start, a tape and the simulation's step, restore and + capture — that keeps the states it has been asked for and plays on from + the nearest. The search compares digests and reads the full snapshots + once, at the step it names; it says whether that step's input differed, + and through an `EntityLayout` which entity and component moved. + `bracketFromTraces` narrows the search to one checkpoint interval of two + `DigestTrace`s. +- **`EntityTracks`** reads a run as one lane per component of each entity, + holding only the steps a value changed and closing a lane when the + component goes. `EntityLayout.ecs` reads an `EcsWorld.save()`, + `EntityLayout.rows` one row per entity. `RewindBuffer.oldestStep` is the + left end of a scrubber. + - **A number tuned while the game runs is on the tape.** `InputState.tune` sets a tunable for one step, `InputFrame.tunes` records it, playback applies it, and `Tunables` is the step's side: named values with defaults, diff --git a/packages/flutter3d_sim/lib/flutter3d_sim.dart b/packages/flutter3d_sim/lib/flutter3d_sim.dart index 196d543cb..07497c069 100644 --- a/packages/flutter3d_sim/lib/flutter3d_sim.dart +++ b/packages/flutter3d_sim/lib/flutter3d_sim.dart @@ -101,6 +101,7 @@ export 'src/nav/navigation.dart'; export 'src/physics/layers.dart'; export 'src/save/data_source_trace.dart'; export 'src/save/demo.dart'; +export 'src/save/entity_tracks.dart'; export 'src/save/first_differing_path.dart'; export 'src/save/game_random.dart'; export 'src/save/replay.dart'; @@ -111,6 +112,7 @@ export 'src/save/snapshot.dart'; export 'src/save/state_digest.dart'; export 'src/save/step_time_trace.dart'; export 'src/save/tally.dart'; +export 'src/save/tape_bisect.dart'; export 'src/world/exit.dart'; export 'src/world/key_ring.dart'; export 'src/world/light_fixture.dart'; diff --git a/packages/flutter3d_sim/lib/src/save/entity_tracks.dart b/packages/flutter3d_sim/lib/src/save/entity_tracks.dart new file mode 100644 index 000000000..d19c080b0 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/save/entity_tracks.dart @@ -0,0 +1,245 @@ +import 'first_differing_path.dart'; +import 'snapshot.dart'; + +/// Where in a snapshot the entities are, and what their components are +/// called. +/// +/// **A reading of the save, not a second save.** A snapshot is whatever a +/// genre's `save()` wrote, and the engine does not know an entity from a +/// field; what it can be told is where to look. Two shapes cover what the +/// repository writes: [EntityLayout.ecs] for an `EcsWorld.save()`, which is +/// component-major (`components.Health.7`), and [EntityLayout.rows] for a +/// genre that writes one row per entity (`monsters[3].hp`). Both answer the +/// same thing — entity, then component, then value — so a track and a +/// divergence name an entity the same way whichever wrote it. +final class EntityLayout { + const EntityLayout._(this._read); + + /// An `EcsWorld.save()` found under [at] in the snapshot — the empty list + /// when the snapshot is the world itself. Entities are named by their + /// index, components by the name they were registered under. + factory EntityLayout.ecs([List at = const []]) => + EntityLayout._((data) { + final world = _descend(data, at); + final components = world is Map ? world['components'] : null; + if (components is! Map) return const >{}; + final entities = >{}; + for (final MapEntry(key: component, value: rows) + in components.entries) { + if (rows is! Map) continue; + for (final MapEntry(key: entity, :value) in rows.entries) { + (entities['$entity'] ??= {})['$component'] = value; + } + } + return entities; + }); + + /// One row per entity under [key]: a list, whose entities are named by + /// index, or a map, named by key. Each row's own fields are its + /// components; a row that is not a map is one component called `value`. + factory EntityLayout.rows(String key) => EntityLayout._((data) { + final rows = data[key]; + final named = switch (rows) { + final List list => { + for (var i = 0; i < list.length; i++) '$i': list[i], + }, + final Map map => { + for (final MapEntry(:key, :value) in map.entries) '$key': value, + }, + _ => const {}, + }; + return >{ + for (final MapEntry(key: entity, :value) in named.entries) + entity: value is Map + ? { + for (final MapEntry(:key, value: field) in value.entries) + '$key': field, + } + : {'value': value}, + }; + }); + + final Map> Function(Map data) + _read; + + /// Entity name to component name to value, for one snapshot. + Map> entitiesOf(Snapshot snapshot) => + _read(snapshot.data); + + /// Every (entity, component) that differs between [a] and [b], in entity + /// and then component order — present on one side only counts. + List differences(Snapshot a, Snapshot b) { + final left = entitiesOf(a); + final right = entitiesOf(b); + return [ + for (final entity in _sorted({...left.keys, ...right.keys})) + for (final component in _sorted({ + ...?left[entity]?.keys, + ...?right[entity]?.keys, + })) + if (_differs(left[entity], right[entity], component)) + EntityComponent(entity, component), + ]; + } + + static bool _differs( + Map? a, + Map? b, + String component, + ) => + (a?.containsKey(component) ?? false) != + (b?.containsKey(component) ?? false) || + firstDifferingPath(a?[component], b?[component]) != null; + + static Object? _descend(Object? value, List path) => + path.fold(value, (node, key) => node is Map ? node[key] : null); +} + +/// One component of one entity, named as an [EntityLayout] names them. +final class EntityComponent { + const EntityComponent(this.entity, this.component); + + final String entity; + final String component; + + Map toJson() => { + 'entity': entity, + 'component': component, + }; + + @override + bool operator ==(Object other) => + other is EntityComponent && + other.entity == entity && + other.component == component; + + @override + int get hashCode => Object.hash(entity, component); + + @override + String toString() => '$entity.$component'; +} + +/// A component's value from [step] on, until the next sample says otherwise. +final class TrackSample { + const TrackSample(this.step, this.value, {this.present = true}); + + final int step; + + /// What the component held; meaningless when not [present]. + final Object? value; + + /// False from the step the entity lost the component, or was despawned. + final bool present; + + Map toJson() => { + 'step': step, + if (present) 'value': value else 'absent': true, + }; +} + +/// What every entity's components did over a run, one lane per component. +/// +/// **Changes, not samples.** A lane holds a sample only at the steps its +/// value changed, so a thousand steps of a door that never moved is one +/// entry, and the steps at which something did happen are exactly the marks +/// a scrubber wants to draw. Observing every step costs a snapshot read per +/// step and nothing per entity that stood still. +final class EntityTracks { + EntityTracks(this.layout); + + final EntityLayout layout; + + final Map>> _lanes = + >>{}; + + int? _first; + int? _last; + + /// The first and last step observed, or null before the first. + ({int first, int last})? get span => switch ((_first, _last)) { + (final int first, final int last) => (first: first, last: last), + _ => null, + }; + + /// Reads [snapshot] as the state at [step]. Steps must arrive in order; a + /// step at or before the last one observed is ignored, since a lane holds + /// one history and a second pass over it would write the same marks twice. + void observe(int step, Snapshot snapshot) { + final last = _last; + if (last != null && step <= last) return; + _first ??= step; + _last = step; + final entities = layout.entitiesOf(snapshot); + for (final MapEntry(key: entity, value: components) in entities.entries) { + final lanes = _lanes[entity] ??= >{}; + for (final MapEntry(key: component, :value) in components.entries) { + final lane = lanes[component] ??= []; + final previous = lane.isEmpty ? null : lane.last; + if (previous == null || + !previous.present || + firstDifferingPath(previous.value, value) != null) { + lane.add(TrackSample(step, value)); + } + } + } + // A lane whose component is gone this step closes with an absent sample, + // so a despawn reads as the step it happened rather than as the value + // the component last had, held forever. + for (final MapEntry(key: entity, value: lanes) in _lanes.entries) { + for (final MapEntry(key: component, value: lane) in lanes.entries) { + if (lane.last.present && + !(entities[entity]?.containsKey(component) ?? false)) { + lane.add(TrackSample(step, null, present: false)); + } + } + } + } + + /// Every entity seen, in name order. + List get entities => _sorted(_lanes.keys); + + /// Every component [entity] was seen with, in name order. + List componentsOf(String entity) => + _sorted(_lanes[entity]?.keys ?? const []); + + /// The changes on one lane, oldest first; empty for a lane never seen. + List samples(String entity, String component) => + List.unmodifiable( + _lanes[entity]?[component] ?? const [], + ); + + /// The sample in force at [step]: the last change at or before it, or + /// null when the lane had not started yet. + TrackSample? at(String entity, String component, int step) => samples( + entity, + component, + ).lastWhereOrNull((sample) => sample.step <= step); + + /// Every lane, for a panel across a wire: `{first, last, entities: {name: + /// {component: [{step, value} | {step, absent}]}}}`. + Map toJson() => { + 'first': _first, + 'last': _last, + 'entities': { + for (final entity in entities) + entity: { + for (final component in componentsOf(entity)) + component: >[ + for (final sample in samples(entity, component)) sample.toJson(), + ], + }, + }, + }; +} + +List _sorted(Iterable names) => names.toList()..sort(); + +extension on List { + T? lastWhereOrNull(bool Function(T) test) { + for (var i = length - 1; i >= 0; i--) { + if (test(this[i])) return this[i]; + } + return null; + } +} diff --git a/packages/flutter3d_sim/lib/src/save/rewind.dart b/packages/flutter3d_sim/lib/src/save/rewind.dart index 2e1322add..9487ecbe0 100644 --- a/packages/flutter3d_sim/lib/src/save/rewind.dart +++ b/packages/flutter3d_sim/lib/src/save/rewind.dart @@ -124,6 +124,10 @@ final class RewindBuffer { _forget(); } + /// The earliest step a rewind can reach, or null before the first + /// keyframe — the left end of a scrubber, whose right end is [step]. + int? get oldestStep => _keyframes.isEmpty ? null : _keyframes.first.step; + /// How many seconds back a rewind can currently reach. /// /// Less than [history] at the start of a run and until the first keyframe is diff --git a/packages/flutter3d_sim/lib/src/save/tape_bisect.dart b/packages/flutter3d_sim/lib/src/save/tape_bisect.dart new file mode 100644 index 000000000..62c527f41 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/save/tape_bisect.dart @@ -0,0 +1,329 @@ +import '../input/input_state.dart'; +import '../input/input_tape.dart'; +import 'entity_tracks.dart'; +import 'first_differing_path.dart'; +import 'snapshot.dart'; +import 'state_digest.dart'; + +/// One side of a comparison: a simulation, the state it starts from and the +/// tape it plays, able to answer what the state was after any number of +/// steps. +/// +/// **Forward only, with the states it has been asked for kept.** A +/// simulation cannot step backwards, so reaching step 40 after step 90 +/// restores the nearest kept state at or before 40 and plays on from there. +/// Bisection asks for a handful of states, each between two it already has, +/// so the work is a couple of passes over the bracket rather than one per +/// probe. +/// +/// **Each side owns its live objects.** A side remembers where it left them +/// so it can play on rather than restore; two sides stepping one simulation +/// would each find it where the other left it. Comparing two tapes on one +/// build means two instances of the simulation, which is also what keeps +/// the two runs from sharing anything by accident. +final class ReplaySide { + ReplaySide({ + required this.start, + required this.tape, + required this.input, + required this.step, + required this.restore, + required this.capture, + }) : _kept = {0: start}; + + /// The state before the first step. + final Snapshot start; + + /// One entry per step, played into [input] before each. + final InputTape tape; + + final InputState input; + + /// Runs one fixed step of this side's simulation. + final void Function() step; + + /// Puts a snapshot back into this side's live objects. + final void Function(Snapshot snapshot) restore; + + /// Writes this side's live objects down. + final Snapshot Function() capture; + + final Map _kept; + + /// How many steps the live objects have taken since [start], or null when + /// they have not been restored by this side yet. + int? _at; + + /// How many steps this side has run, over every question asked of it — + /// what a test reads to see that bisection is cheaper than walking. + int stepsRun = 0; + + /// The state after [steps] steps of [tape]; `stateAfter(0)` is [start]. + Snapshot stateAfter(int steps) { + assert(steps >= 0 && steps <= tape.steps, 'the tape has ${tape.steps}'); + final kept = _kept[steps]; + if (kept != null) return kept; + final from = _kept.keys.where((k) => k <= steps).reduce(_max); + final at = _at; + final resume = at != null && at >= from && at <= steps ? at : from; + if (resume != at) restore(_kept[resume]!); + final playback = InputTapePlayback( + InputTape(seed: tape.seed, frames: tape.frames.sublist(resume, steps)), + ); + final wasMuted = input.muted; + input.muted = true; + try { + while (!playback.isFinished) { + playback.applyTo(input); + input.beginStep(); + step(); + input.endStep(); + stepsRun++; + } + } finally { + input.muted = wasMuted; + } + _at = steps; + return _kept[steps] = capture(); + } + + static int _max(int a, int b) => a > b ? a : b; +} + +/// What [bisectTapes] found. +sealed class TapeBisection { + const TapeBisection(); + + Map toJson(); +} + +/// The two runs were the same state after every step both tapes have. +final class TapesAgree extends TapeBisection { + const TapesAgree({ + required this.steps, + required this.stepsA, + required this.stepsB, + }); + + /// How many steps were compared: the shorter tape's length. + final int steps; + + final int stepsA; + final int stepsB; + + @override + Map toJson() => { + 'agree': true, + 'steps': steps, + 'stepsA': stepsA, + 'stepsB': stepsB, + }; + + @override + String toString() => stepsA == stepsB + ? 'the runs agree for all $steps steps' + : 'the runs agree for the $steps steps both have; one tape has ' + '${stepsA > stepsB ? stepsA - stepsB : stepsB - stepsA} more'; +} + +/// The first step after which the two runs were different states. +final class TapesDiverge extends TapeBisection { + const TapesDiverge({ + required this.step, + required this.inputsDiffer, + required this.path, + required this.expected, + required this.found, + required this.components, + required this.probes, + }); + + /// After this many steps the states differ, and after one fewer they were + /// the same. Zero when the two runs started from different states; else + /// the tape entry that made the difference is `step - 1`. + final int step; + + /// Whether the two tapes hold different input for that entry. True points + /// at the input — a different recording; false at the code or the + /// platform, since the same state and the same input led somewhere else. + final bool inputsDiffer; + + /// The first differing value, as `firstDifferingPath` spells it: under the + /// entity and component when an [EntityLayout] was given and found one + /// (`7.Health.hp`), into the raw snapshot otherwise (`random`). + final String path; + + final Object? expected; + final Object? found; + + /// Every entity component that differs at [step], in order; empty when no + /// layout was given or the difference is outside the entities. + final List components; + + /// How many steps were compared on the way: the cost of the answer. + final int probes; + + /// The first differing component, or null when [components] is empty. + EntityComponent? get component => + components.isEmpty ? null : components.first; + + @override + Map toJson() => { + 'agree': false, + 'step': step, + 'inputsDiffer': inputsDiffer, + 'path': path, + 'expected': expected, + 'found': found, + 'components': >[ + for (final component in components) component.toJson(), + ], + 'probes': probes, + }; + + @override + String toString() { + final cause = step == 0 + ? 'the runs start from different states' + : 'step ${step - 1} ${inputsDiffer ? 'had different input' : 'had the same input'}'; + return '$cause; first at `$path`: $expected against $found'; + } +} + +/// The first step after which [a] and [b] are different states, found by +/// bisection, and what differs there. +/// +/// **Digests on the way, full snapshots once.** Each probe compares two +/// [StateDigest]s, and only the final step is read field by field. Comparing +/// the states is the part a run pays for per entity, so bisecting a +/// thousand-step bracket costs ten comparisons instead of a thousand, and +/// the stepping stays within twice the bracket (see [ReplaySide]). +/// +/// [agreedAt] and [differsAt] bracket the search when something already +/// narrowed it — two [DigestTrace]s through [bracketFromTraces], say. Both +/// are checked rather than trusted: a bracket that does not hold is widened +/// to the start and to the end of the shorter tape. +/// +/// **What "first" means.** The answer is a step after which the runs differ +/// with the step before it agreeing: a real point where the same state went +/// two ways. Runs that part, meet again and part later are found at one of +/// the partings, the first unless the meeting falls between probes — the +/// search can only be sure of the step it names. +/// +/// [layout] names the entity and component that differ; without one the path +/// is into the raw snapshot. +TapeBisection bisectTapes({ + required ReplaySide a, + required ReplaySide b, + EntityLayout? layout, + int agreedAt = 0, + int? differsAt, +}) { + final limit = a.tape.steps < b.tape.steps ? a.tape.steps : b.tape.steps; + var probes = 0; + bool same(int steps) { + probes++; + return StateDigest.of(a.stateAfter(steps).data) == + StateDigest.of(b.stateAfter(steps).data); + } + + var lo = agreedAt.clamp(0, limit); + if (!same(lo)) { + if (lo == 0 || !same(0)) return _diverge(a, b, 0, layout, probes); + lo = 0; + } + var hi = (differsAt ?? limit).clamp(lo, limit); + if (hi == lo || same(hi)) { + lo = hi; + hi = limit; + if (hi == lo || same(hi)) { + return TapesAgree( + steps: limit, + stepsA: a.tape.steps, + stepsB: b.tape.steps, + ); + } + } + while (hi - lo > 1) { + final mid = lo + (hi - lo) ~/ 2; + if (same(mid)) { + lo = mid; + } else { + hi = mid; + } + } + return _diverge(a, b, hi, layout, probes); +} + +TapesDiverge _diverge( + ReplaySide a, + ReplaySide b, + int step, + EntityLayout? layout, + int probes, +) { + final left = a.stateAfter(step); + final right = b.stateAfter(step); + final components = + layout?.differences(left, right) ?? const []; + final first = components.isEmpty ? null : components.first; + final differs = first == null + ? firstDifferingPath(left.data, right.data) + : _withPrefix( + '${first.entity}.${first.component}', + firstDifferingPath( + layout!.entitiesOf(left)[first.entity]?[first.component], + layout.entitiesOf(right)[first.entity]?[first.component], + ), + ); + final inputsDiffer = + step > 0 && + StateDigest.of(a.tape.frames[step - 1].toJson()) != + StateDigest.of(b.tape.frames[step - 1].toJson()); + return TapesDiverge( + step: step, + inputsDiffer: inputsDiffer, + path: differs?.path ?? '', + expected: differs?.a, + found: differs?.b, + components: components, + probes: probes, + ); +} + +/// A component present on one side only reads as a whole-value difference +/// at the component itself, which `firstDifferingPath` reports with an empty +/// path under it. +({String path, Object? a, Object? b})? _withPrefix( + String prefix, + ({String path, Object? a, Object? b})? inner, +) => inner == null + ? (path: prefix, a: null, b: null) + : ( + path: inner.path.isEmpty + ? prefix + : inner.path.startsWith('[') + ? '$prefix${inner.path}' + : '$prefix.${inner.path}', + a: inner.a, + b: inner.b, + ); + +/// The bracket two digest traces give [bisectTapes], in its own counting. +/// +/// A trace's checkpoint at step `s` is taken after the step numbered `s` has +/// run (`DigestTrace.observe`), so it is the state after `s + 1` steps. The +/// bracket is the last checkpoint both traces agree on and the first they do +/// not; null when they never disagree. +({int agreedAt, int differsAt})? bracketFromTraces( + DigestTrace a, + DigestTrace b, +) { + final divergence = a.divergenceFrom(b.digests); + if (divergence == null) return null; + final before = divergence.step - a.every; + return ( + agreedAt: before < 0 ? 0 : before + 1, + differsAt: divergence.step + 1, + ); +} diff --git a/packages/flutter3d_sim/test/entity_tracks_test.dart b/packages/flutter3d_sim/test/entity_tracks_test.dart new file mode 100644 index 000000000..a01d97126 --- /dev/null +++ b/packages/flutter3d_sim/test/entity_tracks_test.dart @@ -0,0 +1,134 @@ +/// `N4`'s per-entity tracks: a run read back as one lane per component of +/// each entity, holding only the steps at which something changed. +/// +/// flutter test test/entity_tracks_test.dart +library; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; + +final class _Health { + _Health(this.hp); + int hp; +} + +EcsWorld _world() => EcsWorld() + ..register<_Health>( + 'Health', + encode: (h) => h.hp, + decode: (d) => d is num ? _Health(d.toInt()) : null, + ); + +void main() { + test('reads an EcsWorld save as entity, component, value', () { + // Mutation: key the pivot by component first — the entity is then + // `Health` and the component `0`. + final world = _world(); + final a = world.spawn(); + world.set(a, _Health(5)); + final entities = EntityLayout.ecs(const [ + 'world', + ]).entitiesOf(Snapshot({'world': world.save()})); + + expect(entities, >{ + '${a.index}': {'Health': 5}, + }); + }); + + test('reads rows as entities, by index or by key', () { + // Mutation: name list rows by their contents' `id` — a row with none + // then disappears. + final listed = EntityLayout.rows('monsters').entitiesOf( + const Snapshot({ + 'monsters': [ + {'hp': 3, 'x': 1.0}, + 7, + ], + }), + ); + expect(listed, >{ + '0': {'hp': 3, 'x': 1.0}, + '1': {'value': 7}, + }); + + final keyed = EntityLayout.rows('doors').entitiesOf( + const Snapshot({ + 'doors': { + 'crypt_door': {'open': false}, + }, + }), + ); + expect(keyed.keys, ['crypt_door']); + }); + + test('a lane holds the changes, and a removal closes it', () { + // Mutation: append a sample on every observe. The lane for a value that + // held still is then a hundred entries long. + final world = _world(); + final hero = world.spawn(); + final crate = world.spawn(); + world + ..set(hero, _Health(10)) + ..set(crate, _Health(1)); + final tracks = EntityTracks(EntityLayout.ecs()); + for (var step = 0; step < 100; step++) { + if (step == 40) world.get<_Health>(hero)!.hp = 7; + if (step == 60) world.remove<_Health>(crate); + tracks.observe(step, Snapshot(world.save())); + } + + final heroLane = tracks.samples('${hero.index}', 'Health'); + expect([for (final s in heroLane) (s.step, s.value)], [(0, 10), (40, 7)]); + + final crateLane = tracks.samples('${crate.index}', 'Health'); + expect( + [for (final s in crateLane) (s.step, s.present)], + [(0, true), (60, false)], + ); + expect(tracks.at('${hero.index}', 'Health', 39)!.value, 10); + expect(tracks.at('${hero.index}', 'Health', 40)!.value, 7); + expect(tracks.at('${crate.index}', 'Health', 80)!.present, isFalse); + expect(tracks.span, (first: 0, last: 99)); + }); + + test('a step observed twice is not a second history', () { + // Mutation: drop the guard on `step <= last`. A scrubber that asks for + // the same window again would then double every mark. + final tracks = EntityTracks(EntityLayout.rows('e')); + Snapshot at(int v) => Snapshot({ + 'e': [ + {'v': v}, + ], + }); + tracks + ..observe(0, at(1)) + ..observe(1, at(2)) + ..observe(1, at(3)) + ..observe(0, at(4)); + + expect([for (final s in tracks.samples('0', 'v')) s.value], [1, 2]); + }); + + test('differences names every component that moved, in order', () { + // Mutation: compare presence only. A changed value on both sides is + // then no difference. + final layout = EntityLayout.rows('e'); + final a = const Snapshot({ + 'e': [ + {'hp': 1, 'x': 0}, + {'hp': 1}, + ], + }); + final b = const Snapshot({ + 'e': [ + {'hp': 1, 'x': 2}, + {'hp': 1, 'shield': true}, + ], + }); + + expect(layout.differences(a, b), [ + const EntityComponent('0', 'x'), + const EntityComponent('1', 'shield'), + ]); + }); +} diff --git a/packages/flutter3d_sim/test/tape_bisect_test.dart b/packages/flutter3d_sim/test/tape_bisect_test.dart new file mode 100644 index 000000000..cb3cb9e36 --- /dev/null +++ b/packages/flutter3d_sim/test/tape_bisect_test.dart @@ -0,0 +1,247 @@ +/// `N4`'s divergence bisect: two runs of a tape, and the first step and +/// component at which they stop being the same run. +/// +/// flutter test test/tape_bisect_test.dart +/// +/// The toy is an `EcsWorld` of three entities with a position and a health +/// each, so the answer is checked in the words a person debugging a desync +/// wants — which entity, which component — and a "build" with a defect +/// planted at a known step stands in for a code change or another platform. +library; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; + +const GameAction _fire = GameAction('fire'); + +final class _Position { + _Position(this.x); + double x; +} + +final class _Health { + _Health(this.hp); + int hp; +} + +/// One build of the game. [defectAt] makes entity 1 lose a point of health +/// on that step and only that step — a defect that does nothing to the +/// position, so the component named has to be the right one. +final class _Toy { + _Toy({this.defectAt}) { + world + ..register<_Position>( + 'Position', + encode: (p) => p.x, + decode: (d) => d is num ? _Position(d.toDouble()) : null, + ) + ..register<_Health>( + 'Health', + encode: (h) => h.hp, + decode: (d) => d is num ? _Health(d.toInt()) : null, + ); + entities = [for (var i = 0; i < 3; i++) world.spawn()]; + for (final (i, entity) in entities.indexed) { + world + ..set(entity, _Position(i * 10.0)) + ..set(entity, _Health(100)); + } + } + + final int? defectAt; + final EcsWorld world = EcsWorld(); + final GameRandom dice = GameRandom(7); + late final List entities; + int steps = 0; + + void step(InputState input) { + for (final entity in entities) { + world.get<_Position>(entity)!.x += input.moveAxis.x; + } + if (input.pressed(_fire)) { + world.get<_Health>(entities[2])!.hp -= 1 + dice.nextInt(3); + } + if (steps == defectAt) world.get<_Health>(entities[1])!.hp -= 1; + steps++; + } + + Snapshot save() => Snapshot({ + 'world': world.save(), + 'random': dice.state, + 'steps': steps, + }); + + void restore(Snapshot snapshot) { + world.restore(snapshot.data.object('world')!); + dice.state = snapshot.data.integer('random'); + steps = snapshot.data.integer('steps'); + } +} + +/// A tape of [length] steps: the stick swings and fire is pressed now and +/// then. [fireAt] adds one press, for a recording that differs. +InputTape _tape(int length, {int? fireAt}) => InputTape( + seed: 7, + frames: [ + for (var i = 0; i < length; i++) + InputFrame( + pressed: [if (i % 37 == 5 || i == fireAt) _fire.name], + released: [ + if (i % 37 == 6 || (fireAt != null && i == fireAt + 1)) _fire.name, + ], + stickX: i % 4 == 0 ? 1.0 : -0.25, + stickY: 0.0, + ), + ], +); + +ReplaySide _side(_Toy toy, InputTape tape) { + final input = InputState(); + return ReplaySide( + start: toy.save(), + tape: tape, + input: input, + step: () => toy.step(input), + restore: toy.restore, + capture: toy.save, + ); +} + +final _layout = EntityLayout.ecs(const ['world']); + +void main() { + test('names the step a defect was planted on, and the component', () { + // Mutation: return `lo` instead of `hi` from the search — the step named + // is then the last agreement, and `step - 1` misses the defect by one. + final result = bisectTapes( + a: _side(_Toy(), _tape(1000)), + b: _side(_Toy(defectAt: 613), _tape(1000)), + layout: _layout, + ); + + expect(result, isA()); + final diverge = result as TapesDiverge; + expect(diverge.step - 1, 613, reason: 'the tape entry that differed'); + expect(diverge.inputsDiffer, isFalse); + expect(diverge.component, EntityComponent('1', 'Health')); + expect(diverge.components, [ + EntityComponent('1', 'Health'), + ], reason: 'the position and the other healths did not move'); + expect(diverge.path, '1.Health'); + expect((diverge.expected, diverge.found), (100, 99)); + }); + + test('compares a logarithm of the states, not all of them', () { + // Mutation: walk `lo` up one step at a time instead of halving. The + // answer is the same; the probes are six hundred. + final a = _side(_Toy(), _tape(1000)); + final b = _side(_Toy(defectAt: 613), _tape(1000)); + final diverge = bisectTapes(a: a, b: b) as TapesDiverge; + + expect(diverge.probes, lessThanOrEqualTo(12)); + expect( + a.stepsRun, + lessThanOrEqualTo(2000), + reason: + 'the first pass reaches the end, and each probe after it plays on ' + 'from the nearest state already reached: half the bracket, then a ' + 'quarter, and so on', + ); + }); + + test('says when the input is what differs', () { + // Mutation: compare frames at `step` instead of `step - 1`, the entry + // after the one that made the difference. + final diverge = + bisectTapes( + a: _side(_Toy(), _tape(400)), + b: _side(_Toy(), _tape(400, fireAt: 250)), + layout: _layout, + ) + as TapesDiverge; + + expect(diverge.step - 1, 250); + expect(diverge.inputsDiffer, isTrue); + expect(diverge.component, EntityComponent('2', 'Health')); + }); + + test('two identical runs agree, and a longer tape is reported', () { + // Mutation: compare to the longer tape's end; `stateAfter` asserts past + // the shorter one. + final result = bisectTapes( + a: _side(_Toy(), _tape(300)), + b: _side(_Toy(), _tape(320)), + ); + + expect(result, isA()); + expect((result as TapesAgree).steps, 300); + expect(result.toString(), contains('20 more')); + }); + + test('runs that start apart diverge at step zero', () { + // Mutation: start the search without checking the start — bisection + // between two differing ends then names step one. + final shifted = _Toy()..dice.nextInt(5); + final diverge = + bisectTapes( + a: _side(_Toy(), _tape(100)), + b: _side(shifted, _tape(100)), + layout: _layout, + ) + as TapesDiverge; + + expect(diverge.step, 0); + expect(diverge.components, isEmpty, reason: 'only the dice differ'); + expect(diverge.path, 'random'); + }); + + test('a bracket that does not hold is widened rather than trusted', () { + // Mutation: trust `differsAt` — the search between two agreeing ends + // then names a step that never differed. + final diverge = + bisectTapes( + a: _side(_Toy(), _tape(1000)), + b: _side(_Toy(defectAt: 613), _tape(1000)), + agreedAt: 700, + differsAt: 200, + ) + as TapesDiverge; + + expect(diverge.step - 1, 613); + }); + + test('two digest traces bracket the search to one checkpoint interval', () { + // Mutation: drop the `+ 1` that turns a checkpoint step into a count of + // steps — the bracket's top then agrees and is widened to the end. + DigestTrace trace(_Toy toy) { + final input = InputState(); + final playback = InputTapePlayback(_tape(1000)); + final trace = DigestTrace(); + for (var step = 0; !playback.isFinished; step++) { + playback.applyTo(input); + input.beginStep(); + toy.step(input); + input.endStep(); + trace.observe(step, toy.save().data); + } + return trace; + } + + final bracket = bracketFromTraces( + trace(_Toy()), + trace(_Toy(defectAt: 613)), + )!; + expect(bracket, (agreedAt: 601, differsAt: 626)); + + final diverge = + bisectTapes( + a: _side(_Toy(), _tape(1000)), + b: _side(_Toy(defectAt: 613), _tape(1000)), + agreedAt: bracket.agreedAt, + differsAt: bracket.differsAt, + ) + as TapesDiverge; + expect(diverge.step - 1, 613); + expect(diverge.probes, lessThanOrEqualTo(7)); + }); +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 271c7a6fc..d7a95b901 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11027 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11050 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 779387660..9bcacad92 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,17 +1,17 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11027 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11050 tests need a GPU. --- # Testing -11027 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11050 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| | `flutter3d` | 1637 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | -| `flutter3d_sim` | 620 | | `pad_input` | 67 | +| `flutter3d_sim` | 632 | | `pad_input` | 67 | | `flutter3d_lab` | 15 | | `flutter3d_core` | 750 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | @@ -21,14 +21,14 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 70 | | `apps/flutter3d_demo_platformer` | 204 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 357 | | `apps/flutter3d_demo_strategy` | 50 | -| `apps/flutter3d_editor` | 227 | | `apps/flutter3d_demo_arcade` | 20 | +| `apps/flutter3d_editor` | 231 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 27 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 143 | -| `flutter3d_game` | 289 | | `flutter3d_shaders` | 5 | +| `flutter3d_game` | 296 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | | `flutter3d_editor_play` | 26 | | `flutter3d_net` | 15 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11001 rather than 11027: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11024 rather than 11050: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index ede67d0cd..bfa8d0795 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -556,6 +556,39 @@ Checked absent in a clone of flutter_scene by grep, and in its open issues. | N11 | **Procedural generation:** WFC over brush-kit tiles, hydraulic and thermal erosion on `Heightfield`, `(seed, rules) → Level` in an isolate, a reachability rule, an MCP `generate_level` | `sim/level/{heightfield,level_recipes}.dart`, `editor_core/tool/levels/` | M | | N12 | **Cinematic sequencer:** camera on a spline, clips and graph parameters, audio, signals, subtitles, fades; in the fixed step, so it replays and rewinds; skippable to the same state | `game/run/demo_timeline.dart`, `model_core/key_table.dart`, `sim/math/spline.dart` | M–L | +**N4. Time-travel debugger.** *Done:* `RunTimeline.scrubTo` moves the +live state to any held step while paused, keeping the tape and the present +(written down on the first scrub); a drag to the right plays on from the +scrubbed step instead of the keyframe. `returnToPresent` restores the +present exactly, `branchHere` cuts the future at the scrubbed step and stays +paused, `stepOnce` from a scrub walks the tape, and `resume` goes on from +the present. `EntityTracks` is one lane per component of each entity, +holding only the steps it changed, read through an `EntityLayout` +(`ecs` for an `EcsWorld.save()`, `rows` for one row per entity); +`RunTimeline.tracks` fills it by replaying the buffer and puts the live +state back. `bisectTapes` takes two `ReplaySide`s (start, tape, step, +restore, capture) and finds the step after which the two runs first differ +with the step before agreeing: digests at each probe, the full snapshots +once, so a thousand-step bracket is about a dozen comparisons and at most +twice its length in steps. It names the entity and component, and says +whether that step's input differed (another recording) or not (the code or +the platform). `bracketFromTraces` narrows it to one checkpoint interval of +two `DigestTrace`s. Over the wire: `ext.flutter3d.timeline.scrubTo`, +`returnToPresent`, `branchHere`, `tracks`, and `status` with the window. The +editor's timeline panel has the scrubber, "Back to present", "Branch here" +and the lanes, whose marks scrub to their step. +*Tests:* a scrub back and forth lands on the state recorded before each +step; bisection finds a planted defect at its step and component in at most +twelve probes; the extensions end to end over a real VM service; the panel +against a fake client. +*Left:* bisection is a library call; the two `.f3drun` files side by side in +the editor and as MCP tools are P12. A scrub through a *loaded* `.f3drun`'s +whole length, beyond what the rewind buffer holds. `stepOnce` at the present +still steps without recording an entry or a keyframe (older than N4), so +the tape is short of the state by the steps taken that way, and a rewind or +branch afterwards replays without them. The cross-OS digest test §7 asks of the step does not apply yet: N4 +adds nothing that runs in the step. + Lifted exclusions (decision 12), each its own item: - **N13. `flutter3d_voxel` plus a demo (M–L):** chunks, greedy meshing, From 6d3ed03fca6d4978d6329258bd81d6c263ab087c Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:20:59 +0300 Subject: [PATCH 042/141] A recorded run is a game's regression test, with no GPU testReplay(path, digestAt:, goldensAt:) plays a .f3drun on the software backend inside a plain flutter test, compares the state digest at every checkpoint the recording kept and draws goldens at the steps asked for, so a game's CI needs no GPU to know a change kept its levels playing the same. The platformer carries one such test over a recorded ascent. --- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_demo_platformer/pubspec.yaml | 5 + .../test/goldens/ascent-120.png | Bin 0 -> 230663 bytes .../test/goldens/ascent-600.png | Bin 0 -> 230663 bytes .../test/replay_test.dart | 111 +++++++ .../test/tapes/ascent.f3drun | 1 + .../tool/record_sample.dart | 21 +- packages/flutter3d_testing/CHANGELOG.md | 17 + packages/flutter3d_testing/README.md | 22 ++ .../lib/flutter3d_testing.dart | 1 + packages/flutter3d_testing/lib/src/draw.dart | 48 +++ .../lib/src/render_frame.dart | 29 +- .../lib/src/test_replay.dart | 313 ++++++++++++++++++ .../test/test_replay_test.dart | 284 ++++++++++++++++ site/assets/samples/platformer.f3drun | 2 +- site/content/quickstart.md | 2 +- site/content/reference/testing.md | 24 +- tasks/0.9-engine-roadmap.md | 24 ++ 19 files changed, 873 insertions(+), 35 deletions(-) create mode 100644 apps/flutter3d_demo_platformer/test/goldens/ascent-120.png create mode 100644 apps/flutter3d_demo_platformer/test/goldens/ascent-600.png create mode 100644 apps/flutter3d_demo_platformer/test/replay_test.dart create mode 100644 apps/flutter3d_demo_platformer/test/tapes/ascent.f3drun create mode 100644 packages/flutter3d_testing/lib/src/draw.dart create mode 100644 packages/flutter3d_testing/lib/src/test_replay.dart create mode 100644 packages/flutter3d_testing/test/test_replay_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 77ce9c16a..fd9056651 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11050 tests** across 43 packages and 10 applications | +| Unit tests | **11061 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 547650552..dfdf119a8 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11050 tests across forty-three packages and nine applications. The +There are 11061 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_demo_platformer/pubspec.yaml b/apps/flutter3d_demo_platformer/pubspec.yaml index df89ff2f4..e06ee5846 100644 --- a/apps/flutter3d_demo_platformer/pubspec.yaml +++ b/apps/flutter3d_demo_platformer/pubspec.yaml @@ -73,6 +73,11 @@ dev_dependencies: flutter3d_cpu: path: ../../packages/flutter3d_cpu + # `N5`'s `testReplay`: `test/replay_test.dart` replays a recorded run on the + # software backend and checks it by digest and by golden. + flutter3d_testing: + path: ../../packages/flutter3d_testing + # `ap-12`: the build hook (`hook/build.dart`) that converts # `assets_src/models/*.glb` into `flutter3d_generated/*.f3d` on every # build — `path`, the same convention every other sibling package in this diff --git a/apps/flutter3d_demo_platformer/test/goldens/ascent-120.png b/apps/flutter3d_demo_platformer/test/goldens/ascent-120.png new file mode 100644 index 0000000000000000000000000000000000000000..0827efb0fd90df6af65f8209ffb44236f1f5359f GIT binary patch literal 230663 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

    Uv-2qpt&JJJtUKv8mStu3r?>`_^?T)}o$S12 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�Nf3m6X*mz0nxeuQ3j?QE+bou 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 z zrtSsmvTZ&~(%ocD=`#%A8Zu<3Ig1KdQfgOK1poy6Je2K-&mRG%Jr03d@ELObF%A`r zA()R_=~NNuJPZ@kX#-$etI)JLGfgVKXC{b65Oy{`02*K&!vZVvq%9a>(Tfodm9zx~ z$j5wlfCI$B3j@KB-|PX9%FqK0d& zlJJl%2W2Iclo{dy_qgE!>gfn(DqF1t7yvmtJAZ3mjEuZ*zY%5AJ8TS`huIo)*ocYk zel{&ARn4&pdNioyrF~$1&HdY7ST+*|2#_dSg^kMvqgFJ*JGe?DMyZce7And@K@y@6xLPyw^Yw z<|hDR^RgO>YVf6izz08iMQRu-mkZjueMc^2SaY9y+XGZh3Z?g!D$cGTv70RYM!eg z*s#G^d1zmes05@(XcMV04Y=7G#~O%-u$w~wp~u(_*nm@>j18~iyB0mLO3bb|4D*Qh zwKFF65{8{PLhY-tMjbAykJ|A1kF`dlx&(&xGZ!b^F5na_B?$z99)0N}5ke=8-w zKG5CpxoeP*%?yC+!#3?jatHt%W+4MItdaDZMr|zwK$)A4009mrWbMJEutpRpFs_LFBO#jWqyme%|A-mB1mNIe=9ja$iTtC|f); z!roMHSoyfd&SQ^<9I`+rP*TeG&p-lyR{e3ozR7{Jay^CNg#~Q$4c|UBHiX>f588yLdGL<;3D4pVdL4z6;F!ewe578Pb7KUwIk;Q6>|Hz(P-dVF1%+?y|oy zKNd$yRB;{yG$X*#^6;;zn8;S~otO_`TK1^W3#emc{dLC9oLus0Wq!#)HBgz*B1`i- z-rCai{N8-ehj)v?MFH!(4ExrlALc9r^l#N)jsAE%F9jUlR=qQec+6s!1&(O$3<@7fNTV&ad zVC$X037_J3>h%$<$EwrhkY>(9alVO2h0Lpx9@lS+kpXS#3?pDa%CK@#!Fc7pD!2T9%Z?^ZN~|AIMLAo!)pL|!KM5H|3^)hWzH}=Sfak-*}TR8zb5$Y57goR$G`d9>How2 z0Kc=Uz8`s4*x&i5G1gAZx@2WimdbWv` z%?L79RJW;EA5e;&5S3Gq3L`Qs#BdduoNU+z94o^LCvJ-ir=~ir=-&l}-?=EcdvEOwel8p8EjKQO?K-$XUQu`=Ed-Xz6q@ zbfp4-YOZ05-!9FLvBpDd;PM#z+?Wgz>jI8Zb;AO&Rmk*yEV_F$1mni3p0Y=$Y0nlm=Q#+{Rer>Z)87@HMp>-Ee4<&4*wDx3C1&)V()?>HF9$JHxmw7#;+0Ijo^o+1G447=0< zucpAa(zs97gKF^<`&a7jF$T(*9>>Zj3uR%+ItxLU-%%dfsthVDN@mC+5hS%U38E8Z zniF!PoHl!pIRFq!_5c9Wut=tWUyJH%{|ddOb3#SzNj(1&VKF9dMW-mN`Yxn;3 zd%Aa&@7RIhwSXaP@C_F(@7}k0Pj~(?jKeUv0&ub~h9Fk~$Wgz2&E~T!V#;eaA-&X% zj-JPO4ydd%@}$!TSasM91m@QyWsY${{cRmnogml%4QH9*Oqv=Ch-ZEE4T7g%CYV=` zcN|y%j;gPWn$4}{{SdNMp~l+?jEN7GO1V#2&!~VKWt;;;|`g+T3KR9Hp!gLSg|{?ZVBAi~}~q z)bxW{>U}Pe$I8^+n*~Q@clO)x7|K2YA3-t);HE0t2W(`3!vGmTiwauPM5;ust(8|b z92O}E7I=b&V6$vv2Pdi#DYGAV3>0H2;$Y3f>k&9RPmX z`ueEman2}{&J*R~d?v*^jkjY_OsN)Z)!+rjZN0P>?6lfRDu}RvXJ&xN6qr~u)XfFe zN;HiF_n#B zQaT1ysRilwAsCz>s5-_q)8UPs+r)OWKyoOf_ABI*-Dt;Jkgc#}QCKFbQAK8f%Mg?= zOa~wn46PC^@D_ls;2eNvECk_H5=M4q(_x|=Gpa7kNXL%`cjwv@S8Sn&#Z%fPxzdJFj#>@~hMkX-9 zDe}#z<};^k*j%Rwm3)-BVZ0~YgN@`H*j_Cgbi)HsYiz>yQc*7Zz61dGjPF-CpS?ye zuTGG+cbDD%j_!^pZcZ3ai;OK9HVw8)Wu5}wQ)Km-&fVM?Pv5U$W(}K-P7h^DFB5=B zO(Xp~P#Xg*$Y=vBH$6k0(%O>eT`|T5f4e~!Fu*uk99!otT1bQ4+~=OYH_Z1EZ?M3Z z#_UvxsmU0zfqVlaY}lX;J*uB)?y^D15-Whxrc1KLBfXhu7)A~lLs{^-5#SV$ueLN8 zq%1T}1yxd1!9v!@f>$=x^1KJ@Q$;KQ7DHPEK}-Ex)YUTmc9yf(acSlVM`2!XY=ivm z*|JgmwhhE|n&`zfv}S}&PDjfKzzr)D=u{~I5kpN3K^ed#mCwI*QphUD``5fLKqXdB z+4*0ghxc=^d6=j54K40EB0MI*EKn0~IVQWszRGsB6SPZ;O%!~_Y#mz;`2P6R+pKqY zj7)L3AeZ*a0qJgiRPH|PF){+eL)2=)ctl3ZnK|)$dTS>D--K8_8CGm%>}*zru?+h@ zAF_4Axp+~NT+F7PtzWIX)el)5rlGkg0xUdqR1BGpi(iAgtpda3WFx35iNt3jMU}8@ zl?#%|B(s~dKrP;xs?zM}VJZN$;Ac3E@|yP=JC*?E;zhQ`_&Wfm5o7>E!Nydt)en&I zM|CQq?D9I9kgA{nU;`jJ@nlZ1NqXH=(9kRpwMunIlK{VK+Mu-t=Tw_n03c>v$S`Mu zi!w5fEi&r$4hRX-cphDdEo0UoS3@>U<059vCZ?nTIO`v`-Qvgf4tQjXjvZksjo^g( ztXDOjau%tMAl12eUo~3@$f$lT89fUsKHIjy4Q);^^MWcbvLYGW45qFZw?W1!EPu*I z7mqsEk_9%hQ-II$Ye?g(m63kJ6Xcu!gfaXHGXAvbCV+^fX@H-DeK3IMzg0KDOC-JK8I$fm#;IyPh?HONqtrBV4B zIVYs`O_5m{P4&w?z-ZI$j``8BnWW<#8@j6R81^SC)WqT@XUt^6ta#QjW~5d>j>#*i8hsp-h9mx}A!^{@u)D!?xX*-2vpmMem(Yg6es=8h?IprY!G zeW?wvDPUU_8q63#6l7$1vMcALVIB4y9s_J+z;KKMM22VY8!I!{R%@0#$lhFM0AfIp z_sa+PETz0m`O{8jNSnV`5kojuB?bh?q|}XkO^|o8TsTjWv*H}aC=iknG6Vz#%OQc* zBfv!lCA%6U-_Y<9RNAPcfQ?QI8Q#8ls#T?}U=yH&Y0BdC?8*km_fx}9Sp(>Rqs|i8 zX~FLX00lx)>xP0M0pBHA9j3IkBp6g=zl>4q$FRqdp+GRnF(5e<6jjSn&jmnBiKi?O<6WW#nPn;?U;dHoHzU9~RRj zz_J5t4d%)yBWexG2GEjO0h3!kO?U`kVa+^`H6XJh_*)fsj*6X-85U7(4-jOWaX$tP zC&K_%B$F!v=dng;WGs+p=vX3K!MP0}`WYT*GFt#97LuSep>RP3lSxA)?WSMgx`w1+ zdz+y0CG-SVw|E8_m6Hw4K;|VfZYXpilQ)z_o2&O{knrqLo&o=#Fy1Y3k*zi{m~zb~ zlCe{+AJNE$JktW%3Ugo0i4|~M*o?Q`<02O_&Ip^SWH&gSY8YUpS;cWg#c#=K*)hO- z|2-!7|Ni&cYXHA%-`3qlPz^(@s!}Mf(I#d9skzgqcd;nFL?-+c%=b7uVo zR4*3~=}t3jJyTT~Z6qwZg23W}g=qJ*G=jlXHs--3778F4qkl{3wdtji5uj(01Xj$JlQ2pry&uWp@6^spnexWM8le=uF}NC_u`q!)P;GbGL6@ZeEjZ?QN2jRVVQ+xn7nBzu*?dEIucxvFETQ2!^p{45{R7n$_6rGES1xFs-s5jU;^us71n&;KFr#5W14Zk zYwtouRt45f;D(X#C7!=oFg|Ai(6Vo)WNOcIV@?n_Aem?a=1gKASuZ0th_n{PBRt^f z!MFyfmVNVTnTsO`!;s)*S(Oco%&^jiSX=kcv`IEXRcBRTz0PUKglep~sZU{51kqVkgO4Q8to%dk$EpczARepYpI?A1G9ajV7JhKj+(j)v<3VC8v|^OIWtIk{UYgC z92P`4Aq~q3cZ`uexuq=HB~^vBjcLEr_NZDIKxRSq0k`ziMNa#&8m^$MPHrFibo$>qjzfk6>AkF=(92-E!^TtpX0=}(wa{L07k#Y8H zzJ>aF>d3Gul0n3CoC9HeE(8u2>k;GGV?y`Yd6iSIod$Sg<;K|Vv_q+a8=q%VzRsCc zlU4;a8J}xtr<(z)Q`nl>DKmge2gHW|3^OL3IKj>&-geCfWO4wb7Y)g6RmMwoE*aZ4gr=UJi>>7-yt4by|sN2XeR=Tk*s8wgorqLs8 zEk>Kb2aJ*L5C(bypgZs&*!hr9nj?;6dnhjj76wnk=;DBZeFB2%{lPP+D8?`%=XXTq z7iIH*B{HOe6Zdl_IiY}E46v}iKD%chL5{eY9y>^KB{2|(euL;GRJ%qkF!n0-=U{lL zC8rUZD#V5Y==Jmg`2LFiANI;H#VQ#POz8byoS*Yf-5IlRt@dJBmV3z?E`DO%9;&_>&$`s+Y&9LL=M2$_4BIeIXoL!SMiUPhck zWZbAL z_X#vqa9+ndrLds0jZ4;`_)b_|`F@cBP*`cD^b`R{3d;mL0tMYw4o-y~u?yQZ-4r5) zH7&=$)&yp96MCW|%&Qm&(e2fd-Bp}eGX-Ygvg7c2#$e9Tmz^9lC}#>Of{eDmvq+`` zY#DG7Dc|um1LyThc99_uJnTUfL{~fnZ7zTS7tp_qxiPNFxUW8o%X~X?C<{FlFbw0i z48C-SuBdmVQ7*O{5TWHa3~o&YSMI$^{6k7L9mTcIZPv`=Cu96g&vRobS%_*$W!YSWtZsdcfHnH@zDp4RpwC;Gk(H)(f+g zc8PIroGJ|51GLcq!-n6fxwggl8suuA!FC}x9;fm%psfv4T2f0UoUQd`U(S4o?_1km z&&P;epdZ=?^@FA!@cIIV$n$*m0&?|qK|?Q1M($zgOENG9J_8}`k!kLob<6BbU!Xsa zsDVEUVuO&Nb|czCdC%C~)Gc(tLp|c=4%w=Xcp&DYxnj*fGbe3CkYIsTSc|)XYvz^B zjXMD0^nf@K;xgv-ib=i#ZPvI9S4^-{8?gX}7)ZugsY?uv32;Qt)UJ|RvD%GO z*hl*=ka2|@an0he)bvVJVI}9aa_Xhveo_e*IXzof%7DmFHts#Bg6w)@w1AJGQI4fi zwMWQ&z;1Gg_1W}{0Um-O3)XQR5Ey2z+L*(%1=A_iZKS`t+vC;Yw?#l3e5{ahtw#rR zTrsXTX<6od{>Fo%n|`VMAa(8o6pz~*omGlB0Y%mM)u$WovA~e#VjZ!-MZW_U>H*_> z00Q=r(VV(5JKR7wDHiuJ#xA`YR#?zxpkA%pTJKnmoO+ZZ12V3E*mO(Bl(Bv7P;TU$ z>s4`wVJs6J?rX+g3N%A!j-0xfk+WuAlz5H?Eam|_PM^hoz@$E4=eqzk={cUrIYc)# zk#~R&IAlBvJrj;82p*dYW75(SAZ2i*s(?(2T2kav51zdh7Rcb)z7NZIrvO6erahk%^)zwhB|+_imz0!M^H zjARzf+luv;mVjKRP}kIO4cIpr+-Cs)jOR76Za?o$F>+4$jPFlgh3)|s?Mr|?tU%zi zYO3W=bc=>M0zQStM&ASUH2xjpPVF<67XSf40fSU7 zu>4B&Z^6`ZH0Y?2`T)JG2ap_MR5)S+4czgOKUwThswCzjr^&+tps4{G z<&1-Iw~w2eg(hIHScpqphZ`2<23PiqMJEs+3*G?4h6Ox#G4S3383jM-pk;DwW^4x{ zlbJo&(KDb_8qUy7j7IJOdWX2=j+o>Eqy&$;s`@^% zSiaeKn1w)sp1J-hRaKF7E4Z+cywF^IEW8^O-GaCT=vFA2r+$n;)_YfKqI!>`qod3VDtD(F3Hc+B{0G zJ%rIHXAeE<5TH}eBjgQG`#$Vy0M$2vcgTD-g~;4ay)aYk8#BgKITMS5t}0M3en!!~ zpCAi|fH#N5E!n`<*;tT`_gv6!fT{zQ`Ic>|;s|~v&$(ukt9a&?{i%4r5BqWR>m#Qd z^aYsH0iKH^z^%{RJwh+-ovyWkd8nr#Xe7M`^UQq!*La%5r2sM(;GE#ynZsVEfZbFO z1RNJ^R+omds51q6LvvG^<0I*n?^!PvE2L_$~k( z9!HRho|`sCx+6fO2l(~y7z6~3S<%hRcw0qwADd#(UX9K#7&qvhEuNJr?%D=JqK!eO z0HF;_CKwN^mjGozu7M3UfSmQalK-Y$fU^4Y7^XKe(wR*}UD#YUqreYvJzoIqE|6Pz z2p}iz1f$-eaw2;Vck|P%SbVPbYfvfB?V& zKqCX}12kg*x@5r$ex@&2XUl*_4M8okriC1f&(hX&+=fdkgf5j8pxe4o0q70q4U5zg z*n(i>L#~ud;XZAd2)0NqS>a|f_-9Fa>IBPV~? z92NIcvLTHaS7kk7@f|UyeO&K7Hj}=ait;+H%!?5q9W@x4jm|cr+vla*KS0d}EC5b1 zvd)op{xeH=a}Rh(HpshTe4VEY#~sg!m4BzqLG3Xna9cpJ2J{5Jq|7ikE10xiH)Cz| zb`4QGWr9g8X0x`!g0FipY&XW9AQQ5|++aRijwx)F&A;Y4iKdomYN*ET-6EeLIKe>J z5Cbv=Y!;}XbB+aa&v1iJ@J=)szEmaCoLCS96pd!KcD{#YimYlN2o7O|1ODFwWCDVy zjfVi-A%J}V%R7X*_SCWj7XWa;`vd;3E84iW8ju%Y!~S5G76O#5S`NDXAsa0~SYv>j zpuW!d-vkzB+qxE4bBmXv0yNXC7qC-ZwoFW+Z2axH03fY$Fyt6$6a*;EyF0+}cL9cX zh)jau0qXD}ASf92j@X3yqwayv-rs%p)1U6j@u++7fd{*XX~O(@&I04-A9&!k<00NV zBj82F0W?z9M0JwIn>tFycO+3()lAT0aWVO0(9d))tDeNV=~RzQPXGy0JV~W>`(8AUI=s9b~X(r0@o-7KR!QXC(h0a`x_$DK-B@G zpK_*@hfGj|nuaQ38j`+%VR6pn56h5kF!AkvW4(Gi-dH-?nnr1MqO2@5nV$<_j=JQ6 zm~p5A+1Wj%j``e#*MhZiLaIP6f`A{d3qXH}TB66piU|W{RV`Ci3~NISoaV17dj;rK zxR9retKJN~95Ji`Tp9rvfZCk%uA`AECSZ;%wrVdZfoe% zwgoX?J@;D>8A%|f^zlaGz_a*j5ztX&ESURa8~0V_ySsWPt+UfcrmyksZPY z4*}v)FHdbad&t3#gp!C zD$iPe`;6iG$@{_zUqHim?2jAK*M1swW^S1*ZH>`dRY2R`l8GXqHPC zI7V(5$_QCw{ATyJHkF2e{DOeVuG0yJrxrCU4F}I4z@kBi~EX+ z=WE?jH7Y=ZtPQf*ms1vL$qxYziaV<%ofa%5_M!LmJSyg#qpO%c#ogOzJW24+|7P~rR+?(LR2GKAH8+PNQ z?Z~3pNOWhXY9;_?{E_a^!f}N`DQf`;jJei&@IA&?k4O#U z_pro^Omg|%UFC!&_P!+zgk{v}9K`kg-MC5_%WRYY+DZU|DYdGM$eI)$R9Byq?%1T` z@V#AWW;Q*>I-WVMC!-du`!FHhz}nZ`T!zen0JU>~G3@hw)%TJ)P+>>5jF0O6l;77| zq;tVd#dM)vvt8x@->H}jj-gFmGrhm)lVu-Y4SG&H9GTo9CJBJbF02Bf$Y z#Hch}R}gFgxCUEZNjtdFv$$?a)fgeOiM=H%uI|}XLBPgW!1RXve+awl19*L|(Fgo` zFi9OEhg|CrmDpNWIz;NK?aH$5n!4#ea@YCpUBB^xFuq3~dI**G;qK9gAMPGU_4T!9 zkB2G2ea4Xe!b#6!NtVCCP8i}X8fUo;7gJOf8Im>|*o0Lewc}a%%wCP@(WrY6aIitd zLcnBlq?aQTGEIlsty^}%)S_px8R%y;@tV>d2Gm)*U3-lHW_cKc5Qj{5Rf_(LOm=`W zl?RYdIY&$!V>O&SH2~#zlv(y;(y|T!0WfXbU^z}|fy)XH$@OEgTl0P?yA$NNmvn^U zer&dzbQ73miuy4=1}9p3%YL8Buy5!X^BPotoNnUDP7=Em#nds1|59H+(B~=ccrZP z4&%FIY?s9B%w(A`Ok-0VrxE}Ztc}z0(6&~X#=$Wb`~VGpF28 zjAw})JYP59^nB%eE#qNfzij9{KRX-Qv!Uhrr4|;?wq>q6XZIz;wX>#f8X!1e9`s>s zZr+Eeq&+u7uCEPS`(R>t*g!|fpsWT6y6Go)78z~(HkF-XKbrvhdQbJfN-~Ug-jj_M zAl$WH17IW>I0afhW6DLUngUiy_Qjzw(QXIe*2rMoO$p!*xTb-rJ=oTf2fueH*Z`KP z!YTmrnIk~e3JgPjBZpvYj0*s0bp_ki=~S?kO9MkqS0X^udREruvcz-?;93c_0@Umo zngal3}a+cL=ZxvLuUX$stz9kghzO<4`GIfey5~`Ufp(W z({`ibu)F*n@9A#3nc7!}{Zsqj&fcfZ@5#p>&+!!Jr=NThCkVi2evoB?Yj#Z^gg|1V zY}8UFM7ommep<6J38oCB^`25MOHVL|ukDX0n@eL@0g**%&W^b{Cdj4*9p6i*BNmF4 zRwpckBnvaCR0Ry)E0Lu@jT{4jGCwEJnBO!oEd|>6zmv^LUa&J8%`A(EdI}h6o1E~z z-I3BI+khxo;=Z|sl^oJNh{Jq-&Cq&rbL(78V6yYPpeQ z&od?k?8X?F0St1)-;vofpUdBXN(HFx7pKv;u^$4=u`MY#iy;*?`HtWmFFQ^gqnypT zOX>--X0reglcR-$YMuSWo2{G7OL0YiWvf& zl0WBMK#i7t8WWUKM$8?%Or_YRk5jy;t#>8cw>(Pg9M-9VnIM44vq?!I ze%}It=D|5+;WWrrF2LB=0<-{O?JElbrm_G|f*QMHgWa(rHKRstt#Q1hGz?M3sVo3U zrm!D2KFhkKwv~1^RBOG%Jp$%>09ao;Aitdh&>O-wd;H&W)MI2zBsbt(0!RfB$|Wq+ z)@*hrDk$Y*+J~KrjC%I91qyBHCfJn#UkN~tAH9{-5k8AOuS9nHc3O^l2Y#?gK+;-Q z$!+62;O_zQ?E?iz!=fw3B_K#ZYFTw3{NPpHhd+E>cZ5A~Fd(IAINpIC;I{+7$GWGU zcp~pT%^3#BU~~8YO_xYI1gtM5gJDDNbk_E^EBF+9mQ8)_&b=l5z@WIb-asZ$Dikn5 z439}dDw8|OvsN!K13$T9d>fG7Xt=fC!c)yTwA#;grVN~2lD~r=P!MsSCjAP6*6AWb2 zyB2Xcl3v0o^KG0l+6e^~2b zaMKy4yoGZEc}+krklhW&`d>QA9A~Q^2-c3Z^TP_YMg`ktXg`;&1ZyO|k>c7RErFh5 znA}tCoOd&1o~MVVrC)HcnO9~lHalrKA9np1xIW*tuS4u(Y>E;H=R%?a0JI@&$qjM@ z>v99MmX+FzYO`Tzva!r}KBrn0im5wzq>psXAmVNnHVMhR+nl=%vjY zuRSLh&5|%A*?O3WtM+9DRL7u#k!o%l0yblqmai>&J<>VC(MP?Nm8vF>06IOe1HjP2 ze*?X4AE39G)o2!V+YXLp$-D?svJg4TnhR~P@oNnv*|}Axl+TRd>e0vM`o+u@Olstjb1Fiw6TD8UVTq@VoVv+q(CE;7Y)c z%^iE-cw7OFRn9WM0N^PQ?m_U3^_^r0rXyI#N-u0Yy!>tRS1S{&34Pd-Y`P`~Y~yUE+6AMB+0I*x3y&AFPQ8y8yEb(6tckMg0hI9 z=@4m3;_k{}ZKW_%z$5lSe$OOM6OEI!%mOeRC0=CiBMobjW5w@m$W2SPbrxfKT(Wqy zsZCYWkU>n@B5TK;C$P0choPBB%Ta zufw!h7^zjoXU!p-qN2rnj4G@As{c#sVVN5kdC6_F(aHi4FiP8KZPQtzRyw|XPIeV= z5xmLPUp~oa8Rykw+&_uFc@6?5k(-Ww$0Rr;e5~v-u=v3_!HP#1!yb zJdUg+Ca<30%spGVA@aJ3IUfOQYjA0faeR}bsjO}`f*o<7gDxtM%j^Nyh{>H5Xs%Y7V=Mrxb9AdJeeBR;|=(T`Nn&lmN$&(WxRgqz0+Aytf zDu9;ep8-&&nG9i^W57k=8v<|^`tv;t{S5)Z5sWZ-dV)GEHFCfTZPFo7+jg!s`4{KU z47pmqW01C?E+f?6T~iMkA>ZM%f?$F3#U{TsWuHf%y0II7lH>lHC;)gf@yC>nf%zRh zb~9C0ZzkUFrc^li$ivrT_p8JFic!%WoWIb${hjaaZoeIXhV|(T*@It!gT&X5KCBJ! z7l&anXJVJpR>Q(H5Ywiz+LpGkr42Tu6@Vs~EMY=|-!TQ(>;R+MxY8p`j+i-bmOgeu z$OKK6UBIN~l*?GtrDpp$3&m`GD;=o*7@wz_<{}Y@e68I_0OYzhU637RQn`Bzb}j_| zCm3irx3HfcGu3qEUb3iVqZ^o$36g0_jB4mTn7d}rMx8G!q{anFAoP!@;Ty1=9AG+&Sb*^;hH-&{8~*1<~?4i&uuo@0!168b>Om z3MwpKa}hVPJlM1yn_QeL5>l1d+&gVl#xN#l(KC!?$>*nln7vU{?ISA|KD+lo6V2J7-L1PjFuxD`kF$&meO;FS#KJ zmZ!{BndgoJ^V9LnfeeO6y(Tk%3)VF<%fyv=hOOF|&waR(!0epXwyPP$Ik>h>Amgb$ zRok9L`V2#IY!=KBWAbEpZsw^VD*(EoGuA5-*ll>-q7|t<2(V%D%vA%gGgo1Hsi7au z#Z3ja2k0f4UUi*~FkssfT`hnIv;KJQK26DTAQdzvgV4p8JNp9 zMAaOj3fpibjYj}8Ywst|O?BPy8bH#T_6?u6OO$zT4eYMLInfZVGUM7)ZH_oIY*f-R z4hB72sIcWXQ{5B*tnofq1Tr7qdrkL|lPkOP7eCZ}aP;=>n)z>b=Rb8#cN5*eZn*mc z-5nHByzRaZb$8u$zPse?oAKmMyU}3Sz4v`rbk|;cV|R4e4*=>F7W6!Hj+8|cPq9uB zk4GPRIOj8I?8+n(`0RGHVWDnO3ru`HG4YkjEf5HVb0)+Ruv&2}m~&Ka}MpYD`H@2!{VXg#MWrk5Kl-bG6y(Zj*&!;-7?2yk|6=MAq zuVE(vYbUv?Bn!ai$&oqC5BA~$4Z8x2l{w(XVnTK+otm^Ouq5Li&<;={YXWNdeecl} zuq~k^E5PCS>tuNV+$$R@Glq6>(pg|8eikm{VSZV1%mC2^j)GKb;Pb1eIgoq(3}+7O zbP3khI-4N7Y*%&7EPsJenapKd0KN;ZX*^$&i~%cO$BWKA%y^yxk8yeiQzzk=d9itg zpk`9nHUQfir^S+Lvn$5i&CHF^E=ndx&y^W(@j~x5V?F2k3z)8zC6>s!ROZua9Nx>! z4Tk3snE^f-nD^-%2-9XB%#nEpAgkWm;c4&KQB!EVFJzW2n$ngK8#YMik{Krkv73PI zvr|iPoMXE2`7re$MYtVR|#ZqeD>@*8ZD1 z2U>tjXGgj(`FU)krpMWgM9;4+-x`2U(3}u#J(a-{I;Kg;wr`cOe1x^g9&N&F$8{Ym zscHb=C8~gH32=I!^@JPt5|1+fH}O}nOnMt3E9q#2>a16~#U|MDogENTw~OCvbcCQ1 zr&Bq=SM%ML&+0`s`y&lX1VL2-^IJ|%7uYh4!vDF*I0Qds zVdAd&jsU*oI+lM{6&Apj$h2nCZcG((th}bJ1)yL^yTD$V4~rd{1UVP)QkD#`2zb8drm^B!11f8P!Kw;-;7}G1 zS?3xiBtzNIu*3Y>cv=M`?o)f&ic&9v_U4ORhY~MqRM7yaYzlc(nucelb0q9Ftd;RZ z9-R$IEH>s4Kv^gQ;F5}>f&|Z4TdDT3Y;ef$CbMe{qg|5s{Tu+5tRObYCC3V9P3&rX zcl~NZj2eK2$2bhq&BxCPn2haOxsf@4TaHLMc@7>nk;z%MCJO{u1E9!Z>|t_tWqPnu zfmWc_HoJt;n@JF8>?!&o1)sx1Qj(Mw*bR{BKG0ECL@zHmQ+ABr#C^>0H6+;FgJibDWIA< z_&r&jp<`(}jH7CXqof6J1gREvyalu@Pcz44q=V1+)Ru52yR}y@p-vOuC)=99_N+xU z!>On3UR!toQI-DGNs=-wfMEkj1vCLy)7%mwCSVIvU*|BU5CE)C*_#d*d(f)dnp4}r z0MmYs&rJD!L9jtBwz+Vr4O2i?Jx1-F0Du#&GsV*#@WcC@6gX7u3h(b-_saY(?>;zw zTX)I(&UKf(mt%kC^&0`iqeCJAjtDgZ1aH6XPC)SW-4rG`2Kb7Rw!fYIPv&QMOpbbz zb5%8{$8}WU0HC0^Wx;MZubiYBS{+<=8v)RV9#z=I2~cI=q3y{63HULo^ZlYbfyv0g zwj8QRs&d-41kpG@1VI><1@^+OR4qB|tC`M00-rlI6MN0Xm5FXxOk32x0GMD0@G0|} zNiR^Z+3}aE?yBWXY!6xG3V98&k^Q;DTee1fS^}E|K_-r+Ubd)n$KN1Z;YEH!qqyoP z@@tnxluKjs%R_*RVW=fYuKB>mRw;#&~hOr_HVyK)&ti*MqS+S8Sf>ar@Sm! zD=Qcy>Bb}Y#{>YVxhPjMLDhI!82^ikZ|D(+_O%(`U31;-OLce5v1QCRUk7j^JF);K zs>U-n8`yUW1#&L|K^E&0hNgT=K@5O0P+UjLhWEDLA&t#zzf9%^<1RR_klkk!@Z;>? zGHKbm@&&vFKD(-0m&?sca|Yv^x?X}dhjE$fZ*uK#a&5+Yjm)dlW5IKp%K`yG<+fhh zLJ;Q}>M2(C73FN!u*rS;oS{wvw5fkNtC}OB;*=7s06~M?Q`v+2a5L7_v(nsidVg+yGV6dJ_HGMTCep!;9kp0c(aY?3rIQ#c zt|3__0VEZa-wQvmE8_PYz`54&4$rMTbOH+J*c zVrLUPRecSQ5%6;6bsg|SEiO0!O6`GXPOx_z!w5V!Oa#?i7kU_!suRp%d3reT?v`VyfNga02hxsSQ_iQvL{%k__$lwKn$u5w3(WRj`!BDd!`YKap!wUJIi zFbkh+Z@)**D)NWzxY$|vVK;_y$N*i80TU*q;U>r~plknJ10EY#r8&JgH zyJNB=PEh|&Sg_&9VAsA@`mC^0PUXmLQ5c;?E#gt{jb2YQW zn)Rx}YsPwm+}?xgyv%c@mz&SV?&o+TcM3rwcL3431FK!LQCS3HO;GcSLpe>aJNeNB zwuzkFi}}%8o=2E9+b{E3yRNFR8_|SKQhTMTh&CP(2+bf_ z8u-s|ngozdh|MdFWdN>;>Z#zz+=zFWbHZkL&t}@&5aRKlxVO}di5-^~4VV(6L95kq*=d0qP;Ufl+JpbkmF&#I+@p+WyM zt~(>6#N1%(+-onKaNPz5rgznP^nzal3v2_3+U;o9Cd1TuM10?j>r4phHurc=OybgP zA7cLIULSCr1CCQz;0yqq1Bf$OAT~aaM!O&3E)AY(n-Qx9+w|AY?2R67)oo-k?afN2 zC2@#%JaTRKfz>;^ORjoDcgcr-jpOy*B_HH`fuAn9K<8 zaMm^7iCmn?jf6wtjgkufPRem$ZuhUIfp4_ zW;HDNy?Nx{WoD?tMtxO1h6Sz!6P;1Y&FeMZ+c;eWe!$}*;BlEGf`}H%5APN@-EC8LEZ&L5$66) zCW-fxMl)aI{D#*9BAbE@ub`S7*Hh@Z141!x9_$+S2fW`xbCF3T=cNCWUR$9PF5iiJW}q}nY#2=4aSn1;z` zR&3BquDgJ3t&m|wl*0rTXh5-AS;{`62J6T$wpGW*zJr;bWD6gds%NU7jf!hi{YdF+ z09$i)ZDD7l53^hnIIXP^@Uwigp(r{|W-!{Bsy{$H0aO=!XHH6<$AXl%IeEuZ(%NQZ zfGhw>j~O7i14C?;r(oC;8(0w_wHh*7gV+oZtml#U7(Fn_GPQlh=3e z-M|2^d1H<tgLwXQiUj-snvE<9F!DHNg5@=nL;$%60B)ER zTdsEzrXcuj`RxtYzIe*0wy3N+J@m>&-DY7Lw-?}LF{-M}0X@SZ^t`6tTjXVD=6BLS z1la`=b5yml37C-`<`It{fW3W^*Ra@)peXx;k!}I)H49P)e|hPr;%6tCch^`-ypYaPv&$&BMr}6E6o5%f}aH;WV0u) z0N$#`$_}7iB*yRL+k8*k#V_NNRaV^f}Lffr_2rWhV5!( zW04uJr;NK98?r#lZ)@AM#=oBJ%~Os)z|DJMfQ-Qw`BJC?09-R3R>E0vy<=ooV^}c< z*RVF(w{y$QRr7;@xZ=4kQF)bHbAyaKrPADd0Q1-y8AGeQ=xLVyO)2V710G4CBOlrR z5H0hk^)uKv-uAvRLduZ}J2g%aovk@c~-C zF3HnvEIn2*^=UZ$wQbLAj}rX`G}H6UACAfQ*_Ax^jqpxzSmC=0X*!Y%h- z1p~aUd(X+cUIV~Ou6+3u2xT|~5dp^)zdF6+@ z)p8Y;_b-EBJi^%jj1SaN;&Eoh!&~z3bZ`iGa}2tIU=Am^lN%7xc&u?>qiZ@Kq5fJU z&7zU!+ge-Cd`peJ0*=OhckRUG0f1Y)m2rybVBngj%q0zthif5%t$DDh3MOD1>H)*?6HjO;0iWsbatnT_c&hlOO)XKC*2uk)J*p}xyYF#CX4vZn6;Ku4 zYO1nT0npcusj9Byo6ro{>Y7^h0TqD&HX_^Bvph#07h}u#S}IGKCvG;#o#cMNM5Db9 z0PT4z07_fn3NP#SOT33;XZ>a5)J_Np>kwE9&HzRL99CrxtVI^gH9mpyrQ8;PqdFWL zCeKSXSci_xUKLoN)_WVm)!r7`#M(D<~?gqYM#)(wn7$} zU}9HMa}&-4=xL)f%g5&sl^(mR1wV|H4C`j@IpzU6=8e@^oF}qC8_w7VQ?_MuYUljK zNCwuACKDYcMnuG%k>|+o32La47TlNuLaLEdK?9X_2IE_+D&x&va6fbHZ2*)i@B(1d z7AROu1immpRbs$UhYPzQHlC$AqDI8)%@K6xE6pIQx`9VN)5e1 zb(LYpX=2t3-ea97Gq0<&1F*Do)fBLsA$Lm=gO+^U9HhTZQDIewr>L+qRN@H$*x=Mq z9i9MeHR*M+1?mmPDI)ueV}zjQsP;HsEZ%zi16OyqJ$QBZK2=~DplUGeuNX7l~SBA)h(C%ilU{x5I z&V~iPHQ!Uf1pwCkUpD71w`KCKnXJbEl|Zo;ESR){o=%P#JKz$V&002!0p4Kj6^l?X zRo1Nw{uIPl?(R(N1dd|=OPmu1WbGd*zr^BLv-kzRFjyw}TtB5%r zY;ca~2wFyNd_PE{KDx3X`U zHu7s~(Bqx7H}28g(}Yt1xPqV_VcYRph`>UrGYfhOCfw7mnlgU_&~Y#z^9n|`ejWy= zTB-~%`NT^K@_?OY20iH8m-t(7=DV^bRYt*XMjV@JoL*^}uXf!P>{Ny!J<|Bhdm&-1Rc2~Ae^IcJ}JI_Fw*zVjVpykiW<$$gFAm;=Uoley>oOl5#< zRS=hTX3%A~(YdS<2`D!X+Tr60BGl@de;ofW`yV07wHU zl}bqnezZtltRHVp`39h9*@HCzs3ffg{L~0GzMl96Eihu%gzeR$*RxmwS0i`>euAI{ z7Br3Zxmi`(Xm7N9>@kBR`W0oE>U~Q=7a2?q7+cA?1q@q0S5w7BcBkoEVZO>%Yb7uQ zGG-rRN)rSr34qk-3M;^{VmxMsRDfOs8}uqAf4{sk0 zr_4Iovia6*z6~sAgM)vm3<2TfLRQ7%XpDM7?_E3s;L;P@6PJ@Lm2?5XMkg*n5cvTM zP$zL3dbk-+B52u2nyf5}4I89qGi_jTy?_BQ9P1ln0SR;+8-Jlw+5m>K6E4kW8EQwA zZ4LOv{B`UyRX}?yLj%BDbNOak|ozolq9 z0L~mXFxNGgz9jvZ$vx=hBP*(d;xK_%9RQGO0G4+UAEnIM{} z(eufCcpj!Wn!48ljHdFLMPfk$L3@GeDg(4Um4!;3gW}~#TsSy1_hp39Dl&G(&?|#R zxfs77$oS&h9l$o)w03|9ZO`vl)zjoZX400=;iASe)W;m1ox6=PBdsMtChR9-#ImoT-J`v~w` zbFhWs0W<px^_^sj0cb^=-^}wU~{ibdM zc$&cG2Ad-+o7Aj6wGDfsD-kBr7C%vsrmW{{%{?i6#LH58Of*l>eFbN0ry1P}{nv4; zSq1Qodz{hHT>qGj2qOpb+X{&7rbePRGx zvLqOzb}sPY>6~{XF4>UL#mS!Bj4}$wzh`w;Rf5N!A=ey4VDNSMKnQRY!mONz+2K#1&>1p<9O3JD{bojYU+#t?U zh6O`xVdn~v_X6se#sFT%FB1fmw*cGr`@H!*;K>|x2RJT4FYJ$> z3E;M%3k!7pe2R|_MyUC%KG5+;-ompoKX@-PP5=ta{m znd~;TzyBV>u^@P!sumo-)2qbG`lgCAq5_; zvcY6}C_qN|ER)m6R%;StrdIg#tQj~5{Hz*ngxG2>_874YS4E)1T6|WL*1Ruy)kKy7 zR%UYG&#MV+bVyrTSY5+ad<5VLW}4Cb+#(11`vmYpGZzU=SRZq$dA|et8hW5as22DtFw9gZL<8x#!v*(^bh z$?)vCixfq`oPO<%;q?6v4_(_00l^dIX90y2Q8ylJSy30v>E!qroy)MYV3k$RAvU zj@bo?DdNKP>qM;CaCUi@ooG^e8KpGxYPIEnAOINP(p|uKHqHT%HAQg4|Czp-+)wYF z(i44RF@*pY*->OD+*|Lp0f}Dl;B|+I*ON-7rcVu`K?MOs_UG7TQC3qKeS*Blp<;1V znMuG~)+zwr1w80tu5YcAA!2Gehn25_xB!UhEfC9=W&6FL%$U@A_JExxzrmRF`eba8 zr!Y6OI1EtuzGK}mhp|N->jqt)*({pb`X9{!?)ZIM64;N^NU<3*nzu&dBj(16$oRDu zSQ|!)SNG^_PB5l%{8W#24n~h-GBZczNrJO!gSu>1{QaK)%QX>K0{2r>HpMvj9V6o2 zH+z?Qhy(=reSM)a!01#3QJE#IF!CNO6N@1DnL-gU>&S?4+6!V1*Iulhn7nB9YV>ll zKKr3ZBU!F8RFT<`N+DN&_OmFlI08 z(2Nejucni`9nt4jmnK+JSd0~uSx~f*P%=DZrXLb4fw?uXwuQirPfsxGV0a~fHzx&7 z>5JyGxxZ=v1CR=#{e9S^K-vQ2jXK3GGIvD!j&XGNfW^)Y$s%5{=mfcBUw8_0s(Auy63R@nN!gT| zk9~ZLvM)BJ3=lSv13C%R8L&#{?O6WCZL0hPV-&1D6sM0dK#NEdG$lEDS@Dxc7QX znxX7apQzs}K-SdOT(t9hD$}5Wv)DoMdij6NwN1Z9%vN0t`kNxs#qYI7#1cRYh*f;-1Vi=9tY0Ni5_zvLM6S~KpN^&kMR7`trHacfF% z6<_CB@N+@JvlXiGZ#N4$6-8Q&VQYrp$hQ4`1P3 zft1azhBVBz5J5bzW{(%d#0GFzPtud>-hT}e}8F?GGMrX$u+V*BfXlnmgC}UE5Mj; zXV62I+rSFLg2`^F$t|wYBQ|D-FplfLygXb)1bx1EET*q4ub9mUY`i`^bN0gU+SO~r za~Gc4Nspvpdcz0Y;G!%F<5eQ1i|~xP!0MgFu`sN zr~s@NE?lBJI-4T?6MQPEcp_k+9rNnQ+Atj~qGEkRoTkZB#k})pOTb2xc!^`Ur58x^ zCDBQmx757)wLgnA#)a#N%c5MfA$CX`?g)M)#P>>Kykt`>YSYqO#MZboy0~X^k+ryp z0iiW!k06+nJL3oV^zRWkvD%2BQ8WIOTHBG^ocx_4(Sr3cf4Ypxp_kuS7=sbFKYFmA$2zMiq2~%P${GTxaVI+H*NX#w9F5B2( zO4|fk@GyfR<~d+sbsj;Z2D}@_-e&xOHyd97G|M7#4aTl)Wa<0?HxHbaUL4tgG{NJ=H6ru{D0OoOj$4H?;I@C@rQk}?W;JEoede)Ne53A|OvUMML&nphp+ULL=%!%F$%suE0 z&LNg9okLqy76_S3fKLDni1XZK!)e}(C1k#@mnm;Xj^j&)0hWJ5{&R0S z%Epb1`-=_&%W43lM%q1Y$QohQsTe3)6e2-1cZ~TIo zyc2#;MrzRo>-?D)Tq)`n^aMeDj0v)k1@ivo;l<-;WA+~2VjWTKWpa2pJo3G#F=y#l zLe%&A8*dH|o_RFUV1pX6K3Glp&b65p`6c$ zmtcy|o_~&NFw$-ws1=1f7pj{M;A<5vyZJOFJwaWpfQJjv2io4xgRRdrxgQ+HY998<}}@yiB#*hqYjC*zlK7z8j?Dhv>|r2ol#kxj=Y zbRI^O)se|6tSdKGdSSEkcWdW>@H%EN_aLCb{6_%fv)qqV#eu=rm|HgJEdcB}3wlLZNARw4R5R64Yb`EB#1aP@; zIdlN7l*ID>1Foq61EZV4QuG0qSPrt-5>j9t!!SyGj~=u>vlKSF!(q1qYz4t3){BKlDooep6~p{2$z@?y zS)7qsO>+ZCWTg}9^$9q9W)4G}$pQgEP5MGsiTRwC4*-zGR4tYo z@Wf14Iy1H3FV_)TzbPHw1jNY~{9Uwuw!2iAt?AkTcmiKT9lsSBC4Sv~2tyPUnV(VK zJ@`#wg;R%ngI6MT5flZ% zYk;9(w&=QHdPGp;op<8*dzor5nz)6TAAn;tg8<+@SaO9b(fR}pteiW4DW$VcX&&hC z79a$Q3e(q9TN5loQNT`9(I7(2qKJuXfWngx@K8KlqNlSjusS+5Q|V@j4x~Cyzy2v3M=Nl){|+o>f~?FxC085&ewc>r2x|V^7}#mSGB1;X+3ID(kS-q}n-w zHxYB##*R(5a}I(y-yaje*utFjF$o@~S*7|h_i!`;84Gg?=mNs}ni5gwRUe`3&VCpc z1Fd<#X4AU)0wDf9=34VNm6lkK&nnMhBhD9QY6P`~h1>BwJ$eDW0^d{V#k`CV2Rvk) znvXE29&z;XN3c*|e}~AK07jnx2EWhqkTuneEgkDTL}Y`E!Lb?<_ewr5{_gOdxbbj;n#o&ERV2d0m z<55mzW{Wi{9sky6A&4^v8LU>r=r`p)^8XYZ?j_)2RM+QX4zun9S)j!syt2Mo4@!5e zm}6BOJHgv!Pyj7G%ouAJI-e~tLFdXv*asz_Dfzx(y?YQ%5gt#7xX#%NGeyW;Qw77U zry7tk&CD`a9gJ@Y`&(nNEXf>MV{Ih>D02ls^aINNtdc%8n*uN^Fpu}CaLRlHK7ES; zK!Dp4S2F-+4k%iVOCX#8N>e_Y+J=Cq?^_x_#Z2+(ee*Lmf71VVgvonEdHV@E!U+QE zBYOY?fQ7*m}-CqX?Ii1W_$HcXFclKL&QHfAwY5E)hojR04R`Y4twq8FRxMj^z?lX!V+O~FJ2xF4k(0r z1Mmj;s?zi~D!>r+l?D2lut8p}8gu&oM-nj6e|SnZ;if9|dGN3+H!Xl+T{9Psl7P96 zRH%8$LI?=5kW;cszf44(EFgj)1x{hr=oTDEb8h@pN)~qbi|(*E^*PBr?!Y$Q{gg}t zeNSvQO&Z0c73PM8RAYYp8hT9nSxJxIl%xuP$lS&&4r>-3 zeEh!M%?jpR@=Q{_+vg-Z@vssI0R-hFW>hqQV9l6~pxREL;#llJyG-xGb_!u~7*VOc5z7(Va9XTlsb$vqrLX2{=#Makjfc`r0DbIH zG9D_gklnDZEEAG*fac*$^jDBR^hbOxI}u?D}v-rba=g#tfj5AzB*W zpobM)ERQcBra>9wPLWoCae|3?NDyMe-bh8{%f0tE(w2j3%T1Yc%PRwFq`C>2wRmx?+7A)X~h3)(_@4gDG01b%I%b z_^YeKVA|x~>2_dC5kTxsCwAUB6A#_lYN;i3zJ@#fr~Hd&BB(j3(`sD_3EH z55W+B7^#f!34mkvhb)k4(14sj&xPMlzu?)k2LR@!Xbb;y>t7%I@1Otu>=ggna$Wp; I_THcW57k)4(*OVf literal 0 HcmV?d00001 diff --git a/apps/flutter3d_demo_platformer/test/goldens/ascent-600.png b/apps/flutter3d_demo_platformer/test/goldens/ascent-600.png new file mode 100644 index 0000000000000000000000000000000000000000..b875f6adcffeb2419e62445596616afeb1d1b6de GIT binary patch literal 230663 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 C;RioynEU-@F=3HOdoLnex;=_6n9tZ&W*E9oK5J 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 open(ReplayStart start) async { + final (:kinds, :loaded, :fixtures) = await openLevel( + start.demo.level, + device: start.device, + ); + final staged = stage( + loaded.level, + loaded.collision, + input: start.input, + registry: kinds, + onFixture: fixtures.add, + ); + loaded.collision.update(); + final runnerNode = RunnerVisuals( + model: '', + ).box(start.device, loaded.scene, staged.runner); + return _Ascent._(loaded, staged, fixtures, runnerNode); + } + + final LoadedLevel _loaded; + final Staged _staged; + final FixtureVisuals _fixtures; + final SceneNode _runnerNode; + final FollowCamera _follow; + final CameraNode _camera = CameraNode(projection: Lens.base); + double _elapsed = 0.0; + + @override + String? get levelHash => _loaded.level.digestHex; + + @override + void restore(Snapshot start) { + _staged.sim.restore(start); + _follow.cut(); + } + + @override + void step(double dt) { + _staged.sim.step(dt); + _staged.sim.events.drain(); + _elapsed += dt; + // Followed every step, as the game follows it every frame: a camera + // placed only when a golden is drawn would start from where it was built. + final runner = _staged.runner; + _follow.follow(runner.position, dt, travelling: runner.body.velocity); + } + + @override + Snapshot save() => _staged.sim.save(); + + @override + Future frame(int step) async { + // The coins and the gates are models read in the background; drawn + // before they arrive, the golden depends on how busy the machine was. + await _fixtures.settled; + _fixtures.sync(_elapsed); + final p = _staged.runner.position; + _runnerNode.setPosition(p.x, p.y, p.z); + _camera + ..setPositionFrom(_follow.eye) + ..lookAt(_follow.target); + if (_camera.parent == null) _loaded.scene.add(_camera); + return (scene: _loaded.scene, camera: _camera); + } +} + +void main() { + // Mutation: change a number in the runner's tuning — `jumpSpeed`, say — and + // the first checkpoint after the first jump disagrees and names its step. + // Move a brush in `ascent.json` and the test fails on the level's hash + // before a step is taken. + testReplay( + 'test/tapes/ascent.f3drun', + start: _Ascent.open, + goldensAt: [120, 600], + ); +} diff --git a/apps/flutter3d_demo_platformer/test/tapes/ascent.f3drun b/apps/flutter3d_demo_platformer/test/tapes/ascent.f3drun new file mode 100644 index 000000000..4728fa1a7 --- /dev/null +++ b/apps/flutter3d_demo_platformer/test/tapes/ascent.f3drun @@ -0,0 +1 @@ +{"version":1,"level":"assets/levels/ascent.json","levelHash":"1e491529","run":{"version":1,"runner":{"body":{"at":[0.0,0.8999999761581421,-22.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0},"health":{"current":100.0,"armour":0.0,"mourned":false},"purse":{},"keys":[],"yaw":0.0,"coyote":0.12,"buffer":0.0,"airJumps":1,"dashCooldown":0.0,"wallCoyote":0.0,"wallAway":[0.0,0.0,0.0]},"random":1,"state":"running","respawn":[0.0,0.0,-22.0],"deaths":0,"lives":-1,"elapsed":0.0,"mechanisms":{"coin one":{"taken":false},"coin two":{"taken":false},"coin three":{"taken":false},"yard coin one":{"taken":false},"yard coin two":{"taken":false},"yard coin three":{"taken":false},"vault coin 1":{"taken":false},"vault coin 2":{"taken":false},"vault coin 3":{"taken":false},"vault coin 4":{"taken":false},"the green key":{"taken":false},"plateau coin one":{"taken":false},"plateau coin two":{"taken":false},"plateau coin three":{"taken":false},"the high pad":{},"plateau coin four":{"taken":false},"the first pad":{},"high coin":{"taken":false},"the brink":{"reached":false},"arc coin one":{"taken":false},"arc coin two":{"taken":false},"arc coin three":{"taken":false},"the first drop":{},"past the drop":{"reached":false},"the ruin spikes":{},"the ruin pad":{},"the shaft coin":{"taken":false},"the second shaft coin":{"taken":false},"the third shaft coin":{"taken":false},"the blue key":{"taken":false},"the canyon rim":{"reached":false},"the gulf":{},"the first barge":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the second barge":{"progress":0.5468750000000017,"goal":1.0,"held":0.0,"wasMoving":false},"the third barge":{"progress":1.0,"goal":1.0,"held":0.4166666666666794,"wasMoving":false},"canyon coin one":{"taken":false},"canyon coin two":{"taken":false},"canyon coin three":{"taken":false},"the west ferry":{"progress":0.5624999999999991,"goal":1.0,"held":0.0,"wasMoving":false},"the west skiff":{"progress":1.0,"goal":1.0,"held":0.3166666666666723,"wasMoving":false},"the east ferry":{"progress":0.5624999999999991,"goal":1.0,"held":0.0,"wasMoving":false},"the east skiff":{"progress":1.0,"goal":1.0,"held":0.3166666666666723,"wasMoving":false},"the quarry pad":{},"the scaffold hoist":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the scaffold shuttle":{"progress":0.25,"goal":1.0,"held":0.0,"wasMoving":false},"the forecourt":{"reached":false},"the tower lift":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the lift plate":{"spent":false},"tower coin one":{"taken":false},"tower coin two":{"taken":false},"tower coin three":{"taken":false},"tower coin four":{"taken":false},"the blue gate":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the blue gate's plate":{"spent":false},"the maze lamp 1":{"enabled":true},"the maze lamp 2":{"enabled":true},"the maze lamp 3":{"enabled":true},"the maze lamp 4":{"enabled":true},"the maze lamp 5":{"enabled":true},"the maze lamp 6":{"enabled":true},"the maze lamp 7":{"enabled":true},"the maze lamp 8":{"enabled":true},"the maze lamp 9":{"enabled":true},"maze coin 1":{"taken":false},"maze coin 2":{"taken":false},"maze coin 3":{"taken":false},"maze coin 4":{"taken":false},"maze coin 5":{"taken":false},"maze coin 6":{"taken":false},"maze coin 7":{"taken":false},"maze coin 8":{"taken":false},"maze coin 9":{"taken":false},"maze coin 10":{"taken":false},"maze coin 11":{"taken":false},"maze coin 12":{"taken":false},"maze coin 13":{"taken":false},"the green gate":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the green gate's plate":{"spent":false},"the east lift":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the east plate":{"spent":false},"the saw arm":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the saw":{},"the gallery ladder":{"time":0.0},"the rope":{"time":0.0},"the cold":{"reached":false},"the crevasse":{},"the ferry":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the west floe":{"progress":0.3124999999999995,"goal":1.0,"held":0.0,"wasMoving":false},"the east floe":{"progress":0.9375000000000041,"goal":1.0,"held":0.0,"wasMoving":false},"floe coin one":{"taken":false},"floe coin two":{"taken":false},"floe coin three":{"taken":false},"floe coin four":{"taken":false},"the far west floe":{"progress":0.49999999999999994,"goal":1.0,"held":0.0,"wasMoving":false},"the far east floe":{"progress":0.49999999999999994,"goal":1.0,"held":0.0,"wasMoving":false},"the shelf 1":{"since":null,"fallen":false,"away":0.0},"the shelf 2":{"since":null,"fallen":false,"away":0.0},"the shelf 3":{"since":null,"fallen":false,"away":0.0},"the shelf 4":{"since":null,"fallen":false,"away":0.0},"the shelf 5":{"since":null,"fallen":false,"away":0.0},"the spikes":{},"more spikes":{},"the far spikes":{},"the eastern spikes":{},"the cold pad":{},"the cold coin":{"taken":false},"ice coin one":{"taken":false},"ice coin two":{"taken":false},"the floe pad west":{},"the floe pad east":{},"past the ice":{"reached":false},"the chimney coin one":{"taken":false},"the chimney coin two":{"taken":false},"the dash coin":{"taken":false},"the coin over the gap":{"taken":false},"the foot of the stair":{"reached":false},"stair coin one":{"taken":false},"the last pad":{},"stair coin two":{"taken":false},"stair coin three":{"taken":false},"stair coin four":{"taken":false},"the summit lift":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the summit plate":{"spent":false},"ring coin 1":{"taken":false},"ring coin 2":{"taken":false},"ring coin 3":{"taken":false},"ring coin 4":{"taken":false},"ring coin 5":{"taken":false},"ring coin 6":{"taken":false},"ring coin 7":{"taken":false},"ring coin 8":{"taken":false},"the summit":{"reached":false},"the cap 1":{"broken":false},"the cap 2":{"broken":false},"the cap 3":{"broken":false},"the west hoist":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the west hoist plate":{"spent":false},"the east hoist":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the east hoist plate":{"spent":false},"coin 019":{"taken":false},"coin 020":{"taken":false},"coin 021":{"taken":false},"coin 022":{"taken":false},"coin 023":{"taken":false},"coin 024":{"taken":false},"coin 025":{"taken":false},"coin 026":{"taken":false},"coin 027":{"taken":false},"coin 028":{"taken":false},"coin 029":{"taken":false},"coin 030":{"taken":false},"coin 031":{"taken":false},"coin 032":{"taken":false},"coin 033":{"taken":false},"coin 034":{"taken":false},"coin 035":{"taken":false},"coin 036":{"taken":false},"coin 037":{"taken":false},"coin 038":{"taken":false},"coin 039":{"taken":false},"coin 040":{"taken":false},"coin 041":{"taken":false},"coin 042":{"taken":false},"coin 043":{"taken":false},"coin 044":{"taken":false},"coin 045":{"taken":false},"coin 046":{"taken":false},"coin 047":{"taken":false},"coin 048":{"taken":false},"coin 049":{"taken":false},"coin 050":{"taken":false},"coin 051":{"taken":false},"coin 052":{"taken":false},"coin 053":{"taken":false},"coin 054":{"taken":false},"coin 055":{"taken":false},"coin 056":{"taken":false},"coin 057":{"taken":false},"coin 058":{"taken":false},"coin 059":{"taken":false},"coin 060":{"taken":false},"coin 061":{"taken":false},"coin 062":{"taken":false},"coin 063":{"taken":false},"coin 064":{"taken":false},"coin 065":{"taken":false},"coin 066":{"taken":false},"coin 067":{"taken":false},"coin 068":{"taken":false},"coin 069":{"taken":false},"coin 070":{"taken":false},"coin 077":{"taken":false},"coin 078":{"taken":false},"coin 079":{"taken":false},"coin 080":{"taken":false},"coin 081":{"taken":false},"coin 082":{"taken":false},"coin 083":{"taken":false},"coin 084":{"taken":false},"coin 085":{"taken":false},"coin 086":{"taken":false},"coin 087":{"taken":false},"coin 088":{"taken":false},"coin 089":{"taken":false},"coin 090":{"taken":false},"coin 091":{"taken":false},"coin 092":{"taken":false},"coin 093":{"taken":false},"coin 094":{"taken":false},"coin 095":{"taken":false},"coin 096":{"taken":false},"coin 097":{"taken":false},"coin 098":{"taken":false},"coin 099":{"taken":false},"coin 100":{"taken":false},"coin 101":{"taken":false},"coin 102":{"taken":false},"coin 103":{"taken":false},"coin 104":{"taken":false},"coin 105":{"taken":false},"coin 106":{"taken":false},"coin 107":{"taken":false},"coin 108":{"taken":false},"coin 109":{"taken":false},"coin 127":{"taken":false},"coin 128":{"taken":false},"coin 129":{"taken":false},"coin 130":{"taken":false},"coin 131":{"taken":false},"coin 132":{"taken":false},"coin 133":{"taken":false},"coin 134":{"taken":false},"coin 135":{"taken":false},"coin 136":{"taken":false},"coin 137":{"taken":false},"coin 138":{"taken":false},"coin 139":{"taken":false},"coin 140":{"taken":false},"coin 141":{"taken":false},"coin 142":{"taken":false},"coin 143":{"taken":false},"coin 144":{"taken":false},"coin 145":{"taken":false},"coin 146":{"taken":false},"coin 147":{"taken":false},"coin 148":{"taken":false},"coin 149":{"taken":false},"coin 150":{"taken":false},"coin 151":{"taken":false},"coin 152":{"taken":false},"coin 153":{"taken":false},"coin 154":{"taken":false},"coin 155":{"taken":false},"coin 156":{"taken":false},"coin 157":{"taken":false},"coin 158":{"taken":false},"coin 159":{"taken":false},"coin 160":{"taken":false},"coin 161":{"taken":false},"coin 162":{"taken":false},"coin 167":{"taken":false},"coin 168":{"taken":false},"coin 169":{"taken":false},"coin 170":{"taken":false},"coin 171":{"taken":false},"coin 175":{"taken":false},"coin 176":{"taken":false},"coin 177":{"taken":false},"coin 178":{"taken":false},"coin 179":{"taken":false},"coin 180":{"taken":false},"coin 181":{"taken":false},"coin 182":{"taken":false},"coin 183":{"taken":false},"coin 184":{"taken":false},"coin 185":{"taken":false},"coin 186":{"taken":false},"coin 187":{"taken":false},"coin 188":{"taken":false},"coin 189":{"taken":false},"coin 190":{"taken":false},"coin 207":{"taken":false},"coin 208":{"taken":false},"coin 209":{"taken":false},"coin 210":{"taken":false},"coin 211":{"taken":false},"coin 212":{"taken":false},"coin 213":{"taken":false},"coin 214":{"taken":false},"coin 215":{"taken":false},"coin 216":{"taken":false},"coin 217":{"taken":false},"coin 218":{"taken":false},"coin 219":{"taken":false},"coin 220":{"taken":false},"coin 221":{"taken":false},"coin 222":{"taken":false},"coin 223":{"taken":false},"coin 224":{"taken":false},"coin 225":{"taken":false},"coin 226":{"taken":false},"coin 227":{"taken":false},"coin 228":{"taken":false},"coin 229":{"taken":false},"coin 230":{"taken":false},"coin 231":{"taken":false},"coin 232":{"taken":false},"coin 233":{"taken":false},"coin 234":{"taken":false},"coin 235":{"taken":false},"coin 236":{"taken":false},"coin 237":{"taken":false},"coin 238":{"taken":false},"coin 239":{"taken":false},"coin 240":{"taken":false},"coin 241":{"taken":false},"coin 242":{"taken":false},"coin 243":{"taken":false},"coin 244":{"taken":false},"coin 245":{"taken":false},"coin 246":{"taken":false},"coin 247":{"taken":false},"coin 248":{"taken":false},"coin 249":{"taken":false},"coin 250":{"taken":false},"coin 251":{"taken":false},"coin 252":{"taken":false},"coin 253":{"taken":false},"coin 254":{"taken":false},"coin 255":{"taken":false},"coin 256":{"taken":false},"coin 257":{"taken":false},"coin 258":{"taken":false},"coin 259":{"taken":false}},"entities":{"generations":[0,0,0],"free":[],"components":{"body":{"0":{"at":[-10.0,0.3499999940395355,-4.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0},"1":{"at":[30.0,0.3499999940395355,-6.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0},"2":{"at":[-16.0,0.3499999940395355,74.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0}},"vitality":{"0":{"current":20.0,"armour":0.0,"mourned":false},"1":{"current":20.0,"armour":0.0,"mourned":false},"2":{"current":20.0,"armour":0.0,"mourned":false}},"facing":{"0":{"yaw":0.0,"turnRate":6.0,"eyeFraction":0.32},"1":{"yaw":0.0,"turnRate":6.0,"eyeFraction":0.32},"2":{"yaw":0.0,"turnRate":6.0,"eyeFraction":0.32}},"thinking":{"0":{"target":1,"waiting":0.0},"1":{"target":1,"waiting":0.0},"2":{"target":1,"waiting":0.0}}}},"actors":{"tick":0}},"tape":{"seed":1,"frames":[{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0}]},"buildStamp":"tpl-02-sample","checkpoints":{"every":25,"steps":[25,50,75,100,125,150,175,200,225,250,275,300,325,350,375,400,425,450,475,500,525,550,575,600],"digests":["972e8158","91ca445f","a61c6ae9","0fc234f2","8c514970","bbd009ae","64b68c7f","ce6e0665","11a863e7","bcc7df84","ea4a432d","3b1c63f1","dfa5ead2","e4736057","11966675","606b1465","87f8329d","04b2974c","3ef2ff17","86ac5737","8b08843a","309013ce","b06fd6e8","f47d6837"]}} \ No newline at end of file diff --git a/apps/flutter3d_demo_platformer/tool/record_sample.dart b/apps/flutter3d_demo_platformer/tool/record_sample.dart index 8dbc58cf2..09c638b7a 100644 --- a/apps/flutter3d_demo_platformer/tool/record_sample.dart +++ b/apps/flutter3d_demo_platformer/tool/record_sample.dart @@ -2,6 +2,11 @@ /// built from the exact staging and route `test/demo_test.dart` already /// proves round-trips — this script only adds the write. /// +/// `N5`: the same bytes go to `test/tapes/ascent.f3drun`, which +/// `test/replay_test.dart` replays. One recording in two places rather than +/// the test reading the site's, so the game's tests do not reach outside the +/// game; and one script, so the two cannot be recorded from different runs. +/// /// A `flutter test`-shaped generator rather than `dart run`, for the reason /// `rp-05`'s own row now documents at length: `flutter3d_game_platformer` /// names `flutter: sdk: flutter`, and plain `dart run` fails compiling the @@ -56,7 +61,8 @@ void _play(InputState input, int step) { } void main() { - test('records site/assets/samples/platformer.f3drun', () { + test('records site/assets/samples/platformer.f3drun and the replay ' + "test's tape", () { final levelHash = _shipped().digestHex; final live = _stage(); final start = live.sim.save(); @@ -79,9 +85,14 @@ void main() { checkpoints: checkpoints, ); - final outFile = File('../../site/assets/samples/platformer.f3drun'); - outFile.writeAsStringSync(jsonEncode(demo.toJson())); - // ignore: avoid_print - print('wrote ${outFile.path} (${outFile.lengthSync()} bytes)'); + final json = jsonEncode(demo.toJson()); + for (final path in [ + '../../site/assets/samples/platformer.f3drun', + 'test/tapes/ascent.f3drun', + ]) { + final outFile = File(path)..writeAsStringSync(json); + // ignore: avoid_print + print('wrote ${outFile.path} (${outFile.lengthSync()} bytes)'); + } }); } diff --git a/packages/flutter3d_testing/CHANGELOG.md b/packages/flutter3d_testing/CHANGELOG.md index 67cc11fae..a489fd20d 100644 --- a/packages/flutter3d_testing/CHANGELOG.md +++ b/packages/flutter3d_testing/CHANGELOG.md @@ -1,3 +1,20 @@ +## Unreleased + +* **`testReplay` turns a recorded run into a test that needs no GPU.** + `testReplay('test/tapes/level.f3drun', start: …, digestAt: …, goldensAt: …)` + registers a test that plays the tape into the game on the software + backend. At each step in `digestAt` (every checkpoint the tape holds, by + default) the digest of the game's snapshot must equal the recorded one; a + divergence names its step and the last checkpoint that agreed. At each + step in `goldensAt` the frame is compared with + `test/goldens/-.png`. The game implements `ReplaySubject` + (`levelHash`, `restore`, `step`, `save`, `frame`) and gets a `ReplayStart` + with the tape, the input to read and the device to upload to. A step past + the end of the tape, a digest where the tape has no checkpoint, a test + that checks nothing and a level changed since the recording all fail + before the first step. `expectReplayMatches` does the same for a `Demo` + in memory, and `readTape` reads a `.f3drun` or says why it cannot. + ## 0.8.0 * **Two pictures can be compared the way an eye would.** `flip` takes two diff --git a/packages/flutter3d_testing/README.md b/packages/flutter3d_testing/README.md index 38ae72f47..cf45b5c50 100644 --- a/packages/flutter3d_testing/README.md +++ b/packages/flutter3d_testing/README.md @@ -22,6 +22,28 @@ test('the crypt still looks like the crypt', () async { The first run records the reference and says so. Later runs fail when the picture changes, and the message says by how much and how to re-record. +## Replaying a recorded run + +A `.f3drun` your game recorded is a test too: + +```dart +void main() { + testReplay( + 'test/tapes/ascent.f3drun', + start: Ascent.open, // builds a ReplaySubject on the device it is given + goldensAt: [120, 600], + ); +} +``` + +The replay runs on the software backend. At every checkpoint the tape holds +(or only those you name in `digestAt`) the simulation's digest has to match the +recorded one, and the frames at `goldensAt` are compared with +`test/goldens/ascent-120.png` and `ascent-600.png`. If the level changed since +the tape was recorded, the test says that before stepping, because a +divergence at step 25 would send you looking in the wrong place. +`apps/flutter3d_demo_platformer/test/replay_test.dart` is a complete example. + ## Why this can exist here and nowhere else `flutter3d_cpu` is a full backend: a software rasteriser that passes the same diff --git a/packages/flutter3d_testing/lib/flutter3d_testing.dart b/packages/flutter3d_testing/lib/flutter3d_testing.dart index 04bfbde9e..75a172439 100644 --- a/packages/flutter3d_testing/lib/flutter3d_testing.dart +++ b/packages/flutter3d_testing/lib/flutter3d_testing.dart @@ -45,3 +45,4 @@ export 'src/quality_table_builder.dart'; export 'src/render_frame.dart'; export 'src/replay_golden.dart'; export 'src/replay_pacing.dart'; +export 'src/test_replay.dart'; diff --git a/packages/flutter3d_testing/lib/src/draw.dart b/packages/flutter3d_testing/lib/src/draw.dart new file mode 100644 index 000000000..221dcc966 --- /dev/null +++ b/packages/flutter3d_testing/lib/src/draw.dart @@ -0,0 +1,48 @@ +import 'package:flutter3d/flutter3d.dart'; +import 'package:vector_math/vector_math.dart'; + +import 'render_frame.dart'; + +/// Draws [subject] once through [renderer] and reads the frame back. +/// +/// **Not exported: the two callers are [renderFrame], which builds a device per +/// frame, and `testReplay`, which keeps one for a whole run.** A replay cannot +/// take a fresh device per golden — the level it stepped was uploaded to the +/// first one — so the half that is the same in both lives here rather than +/// twice. +Future drawOnce({ + required GraphicsDevice device, + required Renderer renderer, + required FrameSubject subject, + required int width, + required int height, + RenderSettings settings = const RenderSettings(), + Vector4? clearColor, +}) async { + final result = renderer.render( + width: width, + height: height, + scene: subject.scene, + views: [ + RenderView( + camera: subject.camera, + clearColor: clearColor ?? Vector4(0.0, 0.0, 0.0, 1.0), + ), + ], + settings: settings, + ); + + final pixels = await device.readPixels(result.frame); + if (pixels == null) { + throw StateError( + 'the frame could not be read back from the software device, which has ' + 'nothing to be busy with and no driver to blame', + ); + } + return ( + pixels: pixels.buffer.asUint8List(), + width: width, + height: height, + drawCalls: result.drawCalls, + ); +} diff --git a/packages/flutter3d_testing/lib/src/render_frame.dart b/packages/flutter3d_testing/lib/src/render_frame.dart index 961b0bfd8..423e35ad6 100644 --- a/packages/flutter3d_testing/lib/src/render_frame.dart +++ b/packages/flutter3d_testing/lib/src/render_frame.dart @@ -4,6 +4,8 @@ import 'package:flutter3d/flutter3d.dart'; import 'package:flutter3d_cpu/testing.dart'; import 'package:vector_math/vector_math.dart'; +import 'draw.dart'; + /// What a test hands back: the scene to draw and the camera to draw it from. typedef FrameSubject = ({Scene scene, CameraNode camera}); @@ -76,30 +78,13 @@ Future renderFrame({ fallbackNormal: kit.normal, ); - final result = renderer.render( + return drawOnce( + device: kit.device, + renderer: renderer, + subject: subject, width: width, height: height, - scene: subject.scene, - views: [ - RenderView( - camera: subject.camera, - clearColor: clearColor ?? Vector4(0.0, 0.0, 0.0, 1.0), - ), - ], settings: settings, - ); - - final pixels = await kit.device.readPixels(result.frame); - if (pixels == null) { - throw StateError( - 'the frame could not be read back from the software device, which has ' - 'nothing to be busy with and no driver to blame', - ); - } - return ( - pixels: pixels.buffer.asUint8List(), - width: width, - height: height, - drawCalls: result.drawCalls, + clearColor: clearColor, ); } diff --git a/packages/flutter3d_testing/lib/src/test_replay.dart b/packages/flutter3d_testing/lib/src/test_replay.dart new file mode 100644 index 000000000..0c057d19c --- /dev/null +++ b/packages/flutter3d_testing/lib/src/test_replay.dart @@ -0,0 +1,313 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_cpu/testing.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'draw.dart'; +import 'golden.dart'; +import 'render_frame.dart'; + +/// What a game is handed to build the run a tape is replayed into. +final class ReplayStart { + const ReplayStart({ + required this.demo, + required this.input, + required this.device, + }); + + /// The tape being replayed, for its level path and anything else the game + /// wants to know before it loads. + final Demo demo; + + /// The input the tape is played into, a step at a time. + /// + /// **The game has to read this one.** A simulation built with an + /// [InputState] of its own steps against nobody's hands, stands still for + /// the whole tape, and diverges at the first checkpoint for a reason that + /// is about the test and not the game. + final InputState input; + + /// Where the level's meshes and textures go: the software device every + /// golden of this replay is drawn on. + final GraphicsDevice device; +} + +/// One run of a game, as a replay test drives it. +/// +/// **An interface the game implements in its test, not one this package +/// implements for every genre.** A platformer, a racer and a strategy game +/// step, save and draw in their own ways, and the four calls below are the +/// whole of what a replay needs from any of them. +abstract interface class ReplaySubject { + /// The `levelHash` of the level the run was built from — [Level.digestHex], + /// or [contentDigestHex] of a genre's own document — or null when the game + /// cannot say. + /// + /// Asked so that a level edited since the tape was recorded fails as that, + /// rather than as a divergence at the first checkpoint, which reads as a + /// bug in the simulation and sends whoever reads it looking there. + String? get levelHash; + + /// Puts the run in the state the tape starts from. + void restore(Snapshot start); + + /// Advances the run one fixed step of [dt] seconds, reading the input it + /// was handed in [ReplayStart.input]. + void step(double dt); + + /// The whole state, the same snapshot the tape's checkpoints were taken of. + Snapshot save(); + + /// The scene and camera to draw at [step], once the run has reached it. + /// + /// May be asynchronous so that a game can wait for what it loads in the + /// background — models a level names, which arrive a few frames after the + /// level does — before the golden is drawn. A frame drawn before they + /// arrived differs from one drawn after by how busy the machine was. + FutureOr frame(int step); +} + +/// Builds the run a tape is replayed into. +typedef ReplayStarter = FutureOr Function(ReplayStart start); + +/// Registers a test that replays the tape at [tapePath] and checks it. +/// +/// void main() { +/// testReplay( +/// 'test/tapes/ascent.f3drun', +/// start: Ascent.open, +/// goldensAt: [120, 600], +/// ); +/// } +/// +/// A tape is a `.f3drun` — a [Demo] written with `jsonEncode(demo.toJson())` +/// — and the game's own recorder writes one whenever a level is played. The +/// replay runs on the software backend, so a game's CI needs no GPU, no +/// display and no driver to keep two promises: +/// +/// * **[digestAt] says the simulation still does what it did.** At each step +/// named, the digest of [ReplaySubject.save] must equal the one the tape +/// recorded there. Null, the default, means every checkpoint the tape holds; +/// an empty list means none. +/// * **[goldensAt] says the picture at those steps still looks the same.** +/// Each is compared against `goldenDirectory/-.png`, recorded on +/// the first local run the way [expectMatchesGolden] records any golden. +/// +/// The two answer different questions, which is why one test asks both: a +/// step can be numerically identical and drawn by a renderer that changed, +/// and a step can look the same by eye while the state underneath diverged. +/// +/// Everything else is [expectReplayMatches], which does the work. +void testReplay( + String tapePath, { + required ReplayStarter start, + Iterable? digestAt, + Iterable goldensAt = const [], + String goldenDirectory = 'test/goldens', + int width = 320, + int height = 180, + double dt = 1.0 / 60.0, + RenderSettings settings = const RenderSettings(), + double tolerance = 0.0, + bool? recordMissing, + String? description, + Object? skip, +}) { + // A level is read through `rootBundle`, which needs the binding up; a game + // developer should not have to know that to write one line. + TestWidgetsFlutterBinding.ensureInitialized(); + test(description ?? 'the replay of $tapePath still checks out', () async { + await expectReplayMatches( + readTape(tapePath), + start: start, + digestAt: digestAt, + goldensAt: goldensAt, + goldenDirectory: goldenDirectory, + goldenName: _stem(tapePath), + width: width, + height: height, + dt: dt, + settings: settings, + tolerance: tolerance, + recordMissing: recordMissing, + label: tapePath, + ); + }, skip: skip); +} + +/// The tape at [path], or a test failure that says why it could not be read. +/// +/// A failure rather than a [DemoFormatException], because it is read in a +/// test report: the path and the reason in one sentence, and what to do. +Demo readTape(String path) { + final file = File(path); + if (!file.existsSync()) { + fail( + 'no tape at $path. A tape is a .f3drun, the JSON of a Demo; record one ' + 'by playing the level, or write one with jsonEncode(demo.toJson()).', + ); + } + final Object? json; + try { + json = jsonDecode(file.readAsStringSync()); + } on FormatException catch (error) { + fail('$path is not JSON: ${error.message}'); + } + if (json is! Map) { + fail('$path is not a tape: a .f3drun is a JSON object'); + } + try { + return Demo.fromJson(json); + } on DemoFormatException catch (error) { + fail('$path is not a tape: ${error.message}'); + } +} + +/// Replays [demo] into the run [start] builds and checks it at the steps +/// asked for; see [testReplay] for what [digestAt] and [goldensAt] mean. +/// +/// [tolerance] and [recordMissing] are [expectMatchesGolden]'s, for every +/// golden of the replay. +/// +/// For a game that has its [Demo] in memory rather than in a file — one it +/// has just recorded, say. The golden for step `s` is +/// `goldenDirectory/goldenName-s.png`, and [label] names the tape in every +/// failure. +/// +/// **Refuses before it steps** a request it cannot answer: a step past the +/// end of the tape, a digest at a step the tape has no checkpoint for, a +/// test that asks for nothing, and a level that changed since the recording. +/// Each is a mistake in the test or in the tape, and failing on it after a +/// thousand steps of replay would only make the report longer. +Future expectReplayMatches( + Demo demo, { + required ReplayStarter start, + Iterable? digestAt, + Iterable goldensAt = const [], + String goldenDirectory = 'test/goldens', + String goldenName = 'replay', + int width = 320, + int height = 180, + double dt = 1.0 / 60.0, + RenderSettings settings = const RenderSettings(), + double tolerance = 0.0, + bool? recordMissing, + String label = 'the tape', +}) async { + final recorded = { + for (final (i, step) in demo.checkpoints.steps.indexed) + step: demo.checkpoints.digests[i], + }; + final digests = digestAt?.toSet() ?? recorded.keys.toSet(); + final goldens = goldensAt.toSet(); + + final unrecorded = digests.where((step) => !recorded.containsKey(step)); + if (unrecorded.isNotEmpty) { + fail( + '$label has no checkpoint at step ${unrecorded.join(', ')}: it took ' + 'one every ${demo.checkpoints.every} steps' + '${recorded.isEmpty ? ' and holds none' : ', ${_some(recorded.keys)}'}. ' + 'Ask for one of those, or leave digestAt out to check them all.', + ); + } + final outside = goldens.where((step) => step < 1 || step > demo.steps); + if (outside.isNotEmpty) { + fail( + '$label lasts ${demo.steps} steps, so there is no step ' + '${outside.join(', ')} to draw. Ask for a step from 1 to ${demo.steps}, ' + 'or record a longer run.', + ); + } + if (digests.isEmpty && goldens.isEmpty) { + fail( + 'this replay of $label checks nothing: ' + '${recorded.isEmpty ? 'the tape holds no checkpoints' : 'digestAt is empty'} ' + 'and goldensAt names no step. A replay that checks nothing passes ' + 'whatever the game does.', + ); + } + + final kit = cpuTestDevice(width: width, height: height); + final renderer = Renderer.create( + device: kit.device, + fallbackAlbedo: kit.albedo, + fallbackNormal: kit.normal, + ); + final input = InputState(); + final run = await start( + ReplayStart(demo: demo, input: input, device: kit.device), + ); + + final levelHash = run.levelHash; + if (levelHash != null && levelHash != demo.levelHash) { + fail( + 'the level ${demo.level} has changed since $label was recorded: the ' + 'tape was played on ${demo.levelHash}, the level is $levelHash now. A ' + 'tape played into ' + 'edited geometry diverges for a reason that is not a bug; record the ' + 'tape again.', + ); + } + + run.restore(demo.start); + final playback = InputTapePlayback(demo.tape); + final last = {...digests, ...goldens}.reduce((a, b) => a > b ? a : b); + int? agreed; + for (var step = 1; step <= last; step++) { + playback.applyTo(input); + run.step(dt); + // Before `endStep`, because that is where the recorder took it: the + // checkpoint is the state the step left, with the step's input still on. + if (digests.contains(step)) { + final found = StateDigest.of(run.save().toJson()); + final expected = recorded[step]!; + if (found != expected) { + fail( + '$label diverged at step $step: the tape recorded ${_hex(expected)} ' + 'and this build reached ${_hex(found)}. ' + '${agreed == null ? 'No earlier checkpoint was checked' : 'Step $agreed still agreed'}, ' + 'so the change is in the steps between. If it is meant, record the ' + 'tape again.', + ); + } + agreed = step; + } + input.endStep(); + + if (goldens.contains(step)) { + final frame = await drawOnce( + device: kit.device, + renderer: renderer, + subject: await run.frame(step), + width: width, + height: height, + settings: settings, + ); + await expectMatchesGolden( + frame, + '$goldenDirectory/$goldenName-$step.png', + tolerance: tolerance, + recordMissing: recordMissing, + reason: 'step $step of $label', + ); + } + } +} + +String _hex(int digest) => digest.toRadixString(16).padLeft(8, '0'); + +/// The first few of [steps], for a sentence rather than a column. +String _some(Iterable steps) => steps.length <= 6 + ? 'at ${steps.join(', ')}' + : 'at ${steps.take(5).join(', ')} and ${steps.length - 5} more'; + +/// `test/tapes/ascent.f3drun` → `ascent`. +String _stem(String path) { + final name = path.split(RegExp(r'[/\\]')).last; + final dot = name.lastIndexOf('.'); + return dot > 0 ? name.substring(0, dot) : name; +} diff --git a/packages/flutter3d_testing/test/test_replay_test.dart b/packages/flutter3d_testing/test/test_replay_test.dart new file mode 100644 index 000000000..59430e654 --- /dev/null +++ b/packages/flutter3d_testing/test/test_replay_test.dart @@ -0,0 +1,284 @@ +/// `N5`: a tape replayed on the software backend, checked by digest and by +/// golden. +/// +/// flutter test test/test_replay_test.dart +/// +/// A walker on a line rather than a shipped level, for the reason +/// `replay_golden_test.dart` gives: what is under test is the replay — which +/// steps are checked, what a divergence says, what is refused before a step +/// is taken — and a counter makes every one of those visible. A real game's +/// replay test is `apps/flutter3d_demo_platformer/test/replay_test.dart`. +library; + +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter3d_testing/flutter3d_testing.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +const double _dt = 1.0 / 60.0; +const String _levelHash = 'c0ffee00'; + +/// Walks forward while the button is held, and counts every step. +final class _Walker implements ReplaySubject { + _Walker(this.input, {this.strayFrom, this.hash}); + + final InputState input; + + /// The step from which this walker goes twice as far — a change to the + /// simulation that only shows after it. + final int? strayFrom; + + final String? hash; + + double x = 0.0; + int steps = 0; + final List framesAt = []; + + @override + String? get levelHash => hash; + + @override + void restore(Snapshot start) { + x = start.data.number('x'); + steps = start.data.integer('steps'); + } + + @override + void step(double dt) { + steps++; + final stray = strayFrom != null && steps >= strayFrom!; + if (input.held(GameAction.moveForward)) x += stray ? 2.0 : 1.0; + } + + @override + Snapshot save() => Snapshot({'x': x, 'steps': steps}); + + @override + FrameSubject frame(int step) { + framesAt.add(step); + final scene = Scene(); + final camera = CameraNode( + projection: const PerspectiveProjection( + fovYRadians: 1.2, + near: 0.05, + far: 200.0, + ), + )..setPositionFrom(Vector3(0.0, 0.0, 5.0)); + camera.lookAt(Vector3.zero()); + scene.add(camera); + return (scene: scene, camera: camera); + } +} + +/// Twenty steps of the walker, held forward on two steps out of three, with +/// a checkpoint every five — the way a game's own recorder writes a run. +Demo _record({int steps = 20}) { + final input = InputState(); + final walker = _Walker(input); + final start = walker.save(); + final recorder = InputTapeRecorder(seed: 1); + final checkpoints = DigestTrace(every: 5); + for (var i = 0; i < steps; i++) { + i % 3 == 2 + ? input.release(GameAction.moveForward) + : input.press(GameAction.moveForward); + recorder.record(input); + walker.step(_dt); + checkpoints.observe(recorder.tape.steps, walker.save().toJson()); + input.endStep(); + } + return Demo( + level: 'line.json', + levelHash: _levelHash, + start: start, + tape: recorder.tape, + buildStamp: 'test-build', + checkpoints: checkpoints, + ); +} + +Matcher _failsWith(String text) => throwsA( + isA().having((e) => e.message, 'message', contains(text)), +); + +void main() { + late Directory dir; + setUp(() => dir = Directory.systemTemp.createTempSync('test_replay_test')); + tearDown(() => dir.deleteSync(recursive: true)); + + test('the same simulation replays its own tape and every checkpoint ' + 'agrees', () async { + // Mutation: drop `playback.applyTo(input)` from `expectReplayMatches`, + // and the walker stands still through the replay and diverges at step 5. + late _Walker walker; + await expectReplayMatches( + _record(), + start: (start) => walker = _Walker(start.input), + width: 16, + height: 16, + ); + // Mutation: stop at the first checkpoint rather than the last, and only + // five steps are taken. + expect(walker.steps, 20, reason: 'the last checkpoint is at step 20'); + }); + + test('a changed simulation fails at the first checkpoint it changed ' + 'before, and names the one that still agreed', () async { + // Mutation: compare the digest after `endStep` against a recorder that + // took it before, or compare nothing, and a walker that goes twice as + // far from step 8 passes. + await expectLater( + expectReplayMatches( + _record(), + start: (start) => _Walker(start.input, strayFrom: 8), + width: 16, + height: 16, + label: 'line.f3drun', + ), + _failsWith('line.f3drun diverged at step 10'), + ); + await expectLater( + expectReplayMatches( + _record(), + start: (start) => _Walker(start.input, strayFrom: 8), + width: 16, + height: 16, + ), + _failsWith('Step 5 still agreed'), + ); + }); + + test('digestAt checks only the steps it names', () async { + // Mutation: ignore `digestAt` and check every checkpoint, and the stray + // from step 8 is caught at step 10, which this test did not ask about. + late _Walker walker; + await expectReplayMatches( + _record(), + start: (start) => walker = _Walker(start.input, strayFrom: 8), + digestAt: [5], + width: 16, + height: 16, + ); + expect(walker.steps, 5, reason: 'nothing past the last step asked for'); + }); + + test('goldensAt draws at the steps it names, into goldenDirectory, and a ' + 'second replay matches what the first recorded', () async { + // Mutation: draw only at the last step asked for, and `framesAt` is + // [10]; name the file after its place in the list rather than its step, + // and `line-3.png` is not where it is looked for. + late _Walker walker; + Future once() => expectReplayMatches( + _record(), + start: (start) => walker = _Walker(start.input), + digestAt: const [], + goldensAt: [3, 10], + goldenDirectory: dir.path, + goldenName: 'line', + width: 16, + height: 16, + // Recording is what the first call is for, even on a CI runner. + recordMissing: true, + ); + await once(); + expect(walker.framesAt, [3, 10]); + expect(File('${dir.path}/line-3.png').existsSync(), isTrue); + expect(File('${dir.path}/line-10.png').existsSync(), isTrue); + await once(); + }); + + group('refuses before it steps', () { + Future<_Walker> refused( + Matcher failure, { + Iterable? digestAt, + Iterable goldensAt = const [], + Demo? demo, + String? hash, + }) async { + _Walker? walker; + await expectLater( + expectReplayMatches( + demo ?? _record(), + start: (start) => walker = _Walker(start.input, hash: hash), + digestAt: digestAt, + goldensAt: goldensAt, + goldenDirectory: dir.path, + width: 16, + height: 16, + ), + failure, + ); + return walker ?? _Walker(InputState()); + } + + test('a digest at a step the tape holds no checkpoint for', () async { + // Mutation: skip the check, and `recorded[7]!` throws a null check + // error that names neither the step nor the checkpoints there are. + await refused( + _failsWith('no checkpoint at step 7: it took one every 5 steps'), + digestAt: [7], + ); + }); + + test('a golden past the end of the tape', () async { + // Mutation: skip the check, and the replay steps an empty tape past + // its end, draws a frame of a walker who stopped, and records it. + await refused( + _failsWith('lasts 20 steps, so there is no step 21'), + goldensAt: [21], + ); + }); + + test('a replay that checks nothing', () async { + // Mutation: skip the check, and `reduce` throws on an empty set — or, + // written with a default, the test passes whatever the game does. + await refused(_failsWith('checks nothing'), digestAt: const []); + }); + + test('a level that changed since the recording', () async { + // Mutation: skip the check, and the failure is a divergence at step 5, + // which sends the reader into the simulation. + final walker = await refused( + _failsWith('the level line.json has changed'), + hash: 'baadf00d', + ); + expect(walker.steps, 0, reason: 'refused before the first step'); + }); + }); + + group('readTape', () { + test('names the path when there is no file there', () { + // Mutation: let `File.readAsStringSync` throw, and the report is a + // PathNotFoundException rather than what to do about it. + expect( + () => readTape('${dir.path}/missing.f3drun'), + _failsWith('no tape at ${dir.path}/missing.f3drun'), + ); + }); + + test("says why a file is not a tape, in the format's own words", () { + // Mutation: let the DemoFormatException through, and the path is lost. + final file = File('${dir.path}/half.f3drun') + ..writeAsStringSync(jsonEncode({'version': 1})); + expect( + () => readTape(file.path), + _failsWith('${file.path} is not a tape: the demo names no level'), + ); + }); + + test('reads back what a recorder wrote', () { + // Mutation: read the tape but drop its checkpoints, and a replay of it + // has nothing to compare against. + final demo = _record(); + final file = File('${dir.path}/line.f3drun') + ..writeAsStringSync(jsonEncode(demo.toJson())); + final read = readTape(file.path); + expect(read.steps, 20); + expect(read.checkpoints.hexDigests, demo.checkpoints.hexDigests); + }); + }); +} diff --git a/site/assets/samples/platformer.f3drun b/site/assets/samples/platformer.f3drun index e8db3490f..4728fa1a7 100644 --- a/site/assets/samples/platformer.f3drun +++ b/site/assets/samples/platformer.f3drun @@ -1 +1 @@ -{"version":1,"level":"assets/levels/ascent.json","levelHash":"99ef1386","run":{"version":1,"runner":{"body":{"at":[0.0,0.8999999761581421,-22.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0},"health":{"current":100.0,"armour":0.0,"mourned":false},"purse":{},"keys":[],"yaw":0.0,"coyote":0.12,"buffer":0.0,"airJumps":1,"dashCooldown":0.0,"wallCoyote":0.0,"wallAway":[0.0,0.0,0.0]},"random":1,"state":"running","respawn":[0.0,0.0,-22.0],"deaths":0,"lives":-1,"elapsed":0.0,"mechanisms":{"coin one":{"taken":false},"coin two":{"taken":false},"coin three":{"taken":false},"yard coin one":{"taken":false},"yard coin two":{"taken":false},"yard coin three":{"taken":false},"vault coin 1":{"taken":false},"vault coin 2":{"taken":false},"vault coin 3":{"taken":false},"vault coin 4":{"taken":false},"the green key":{"taken":false},"plateau coin one":{"taken":false},"plateau coin two":{"taken":false},"plateau coin three":{"taken":false},"the high pad":{},"plateau coin four":{"taken":false},"the first pad":{},"high coin":{"taken":false},"the brink":{"reached":false},"arc coin one":{"taken":false},"arc coin two":{"taken":false},"arc coin three":{"taken":false},"the first drop":{},"past the drop":{"reached":false},"the ruin spikes":{},"the ruin pad":{},"the shaft coin":{"taken":false},"the second shaft coin":{"taken":false},"the third shaft coin":{"taken":false},"the blue key":{"taken":false},"the canyon rim":{"reached":false},"the gulf":{},"the first barge":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the second barge":{"progress":0.5468750000000017,"goal":1.0,"held":0.0,"wasMoving":false},"the third barge":{"progress":1.0,"goal":1.0,"held":0.4166666666666794,"wasMoving":false},"canyon coin one":{"taken":false},"canyon coin two":{"taken":false},"canyon coin three":{"taken":false},"the west ferry":{"progress":0.5624999999999991,"goal":1.0,"held":0.0,"wasMoving":false},"the west skiff":{"progress":1.0,"goal":1.0,"held":0.3166666666666723,"wasMoving":false},"the east ferry":{"progress":0.5624999999999991,"goal":1.0,"held":0.0,"wasMoving":false},"the east skiff":{"progress":1.0,"goal":1.0,"held":0.3166666666666723,"wasMoving":false},"the quarry pad":{},"the scaffold hoist":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the scaffold shuttle":{"progress":0.25,"goal":1.0,"held":0.0,"wasMoving":false},"the forecourt":{"reached":false},"the tower lift":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the lift plate":{"spent":false},"tower coin one":{"taken":false},"tower coin two":{"taken":false},"tower coin three":{"taken":false},"tower coin four":{"taken":false},"the blue gate":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the blue gate's plate":{"spent":false},"the maze lamp 1":{"enabled":true},"the maze lamp 2":{"enabled":true},"the maze lamp 3":{"enabled":true},"the maze lamp 4":{"enabled":true},"the maze lamp 5":{"enabled":true},"the maze lamp 6":{"enabled":true},"the maze lamp 7":{"enabled":true},"the maze lamp 8":{"enabled":true},"the maze lamp 9":{"enabled":true},"maze coin 1":{"taken":false},"maze coin 2":{"taken":false},"maze coin 3":{"taken":false},"maze coin 4":{"taken":false},"maze coin 5":{"taken":false},"maze coin 6":{"taken":false},"maze coin 7":{"taken":false},"maze coin 8":{"taken":false},"maze coin 9":{"taken":false},"maze coin 10":{"taken":false},"maze coin 11":{"taken":false},"maze coin 12":{"taken":false},"maze coin 13":{"taken":false},"the green gate":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the green gate's plate":{"spent":false},"the east lift":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the east plate":{"spent":false},"the saw arm":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the saw":{},"the gallery ladder":{"time":0.0},"the rope":{"time":0.0},"the cold":{"reached":false},"the crevasse":{},"the ferry":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the west floe":{"progress":0.3124999999999995,"goal":1.0,"held":0.0,"wasMoving":false},"the east floe":{"progress":0.9375000000000041,"goal":1.0,"held":0.0,"wasMoving":false},"floe coin one":{"taken":false},"floe coin two":{"taken":false},"floe coin three":{"taken":false},"floe coin four":{"taken":false},"the far west floe":{"progress":0.49999999999999994,"goal":1.0,"held":0.0,"wasMoving":false},"the far east floe":{"progress":0.49999999999999994,"goal":1.0,"held":0.0,"wasMoving":false},"the shelf 1":{"since":null,"fallen":false,"away":0.0},"the shelf 2":{"since":null,"fallen":false,"away":0.0},"the shelf 3":{"since":null,"fallen":false,"away":0.0},"the shelf 4":{"since":null,"fallen":false,"away":0.0},"the shelf 5":{"since":null,"fallen":false,"away":0.0},"the spikes":{},"more spikes":{},"the far spikes":{},"the eastern spikes":{},"the cold pad":{},"the cold coin":{"taken":false},"ice coin one":{"taken":false},"ice coin two":{"taken":false},"the floe pad west":{},"the floe pad east":{},"past the ice":{"reached":false},"the chimney coin one":{"taken":false},"the chimney coin two":{"taken":false},"the dash coin":{"taken":false},"the coin over the gap":{"taken":false},"the foot of the stair":{"reached":false},"stair coin one":{"taken":false},"the last pad":{},"stair coin two":{"taken":false},"stair coin three":{"taken":false},"stair coin four":{"taken":false},"the summit lift":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the summit plate":{"spent":false},"ring coin 1":{"taken":false},"ring coin 2":{"taken":false},"ring coin 3":{"taken":false},"ring coin 4":{"taken":false},"ring coin 5":{"taken":false},"ring coin 6":{"taken":false},"ring coin 7":{"taken":false},"ring coin 8":{"taken":false},"the summit":{"reached":false},"the cap 1":{"broken":false},"the cap 2":{"broken":false},"the cap 3":{"broken":false},"the west hoist":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the west hoist plate":{"spent":false},"the east hoist":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the east hoist plate":{"spent":false},"coin 019":{"taken":false},"coin 020":{"taken":false},"coin 021":{"taken":false},"coin 022":{"taken":false},"coin 023":{"taken":false},"coin 024":{"taken":false},"coin 025":{"taken":false},"coin 026":{"taken":false},"coin 027":{"taken":false},"coin 028":{"taken":false},"coin 029":{"taken":false},"coin 030":{"taken":false},"coin 031":{"taken":false},"coin 032":{"taken":false},"coin 033":{"taken":false},"coin 034":{"taken":false},"coin 035":{"taken":false},"coin 036":{"taken":false},"coin 037":{"taken":false},"coin 038":{"taken":false},"coin 039":{"taken":false},"coin 040":{"taken":false},"coin 041":{"taken":false},"coin 042":{"taken":false},"coin 043":{"taken":false},"coin 044":{"taken":false},"coin 045":{"taken":false},"coin 046":{"taken":false},"coin 047":{"taken":false},"coin 048":{"taken":false},"coin 049":{"taken":false},"coin 050":{"taken":false},"coin 051":{"taken":false},"coin 052":{"taken":false},"coin 053":{"taken":false},"coin 054":{"taken":false},"coin 055":{"taken":false},"coin 056":{"taken":false},"coin 057":{"taken":false},"coin 058":{"taken":false},"coin 059":{"taken":false},"coin 060":{"taken":false},"coin 061":{"taken":false},"coin 062":{"taken":false},"coin 063":{"taken":false},"coin 064":{"taken":false},"coin 065":{"taken":false},"coin 066":{"taken":false},"coin 067":{"taken":false},"coin 068":{"taken":false},"coin 069":{"taken":false},"coin 070":{"taken":false},"coin 077":{"taken":false},"coin 078":{"taken":false},"coin 079":{"taken":false},"coin 080":{"taken":false},"coin 081":{"taken":false},"coin 082":{"taken":false},"coin 083":{"taken":false},"coin 084":{"taken":false},"coin 085":{"taken":false},"coin 086":{"taken":false},"coin 087":{"taken":false},"coin 088":{"taken":false},"coin 089":{"taken":false},"coin 090":{"taken":false},"coin 091":{"taken":false},"coin 092":{"taken":false},"coin 093":{"taken":false},"coin 094":{"taken":false},"coin 095":{"taken":false},"coin 096":{"taken":false},"coin 097":{"taken":false},"coin 098":{"taken":false},"coin 099":{"taken":false},"coin 100":{"taken":false},"coin 101":{"taken":false},"coin 102":{"taken":false},"coin 103":{"taken":false},"coin 104":{"taken":false},"coin 105":{"taken":false},"coin 106":{"taken":false},"coin 107":{"taken":false},"coin 108":{"taken":false},"coin 109":{"taken":false},"coin 127":{"taken":false},"coin 128":{"taken":false},"coin 129":{"taken":false},"coin 130":{"taken":false},"coin 131":{"taken":false},"coin 132":{"taken":false},"coin 133":{"taken":false},"coin 134":{"taken":false},"coin 135":{"taken":false},"coin 136":{"taken":false},"coin 137":{"taken":false},"coin 138":{"taken":false},"coin 139":{"taken":false},"coin 140":{"taken":false},"coin 141":{"taken":false},"coin 142":{"taken":false},"coin 143":{"taken":false},"coin 144":{"taken":false},"coin 145":{"taken":false},"coin 146":{"taken":false},"coin 147":{"taken":false},"coin 148":{"taken":false},"coin 149":{"taken":false},"coin 150":{"taken":false},"coin 151":{"taken":false},"coin 152":{"taken":false},"coin 153":{"taken":false},"coin 154":{"taken":false},"coin 155":{"taken":false},"coin 156":{"taken":false},"coin 157":{"taken":false},"coin 158":{"taken":false},"coin 159":{"taken":false},"coin 160":{"taken":false},"coin 161":{"taken":false},"coin 162":{"taken":false},"coin 167":{"taken":false},"coin 168":{"taken":false},"coin 169":{"taken":false},"coin 170":{"taken":false},"coin 171":{"taken":false},"coin 175":{"taken":false},"coin 176":{"taken":false},"coin 177":{"taken":false},"coin 178":{"taken":false},"coin 179":{"taken":false},"coin 180":{"taken":false},"coin 181":{"taken":false},"coin 182":{"taken":false},"coin 183":{"taken":false},"coin 184":{"taken":false},"coin 185":{"taken":false},"coin 186":{"taken":false},"coin 187":{"taken":false},"coin 188":{"taken":false},"coin 189":{"taken":false},"coin 190":{"taken":false},"coin 207":{"taken":false},"coin 208":{"taken":false},"coin 209":{"taken":false},"coin 210":{"taken":false},"coin 211":{"taken":false},"coin 212":{"taken":false},"coin 213":{"taken":false},"coin 214":{"taken":false},"coin 215":{"taken":false},"coin 216":{"taken":false},"coin 217":{"taken":false},"coin 218":{"taken":false},"coin 219":{"taken":false},"coin 220":{"taken":false},"coin 221":{"taken":false},"coin 222":{"taken":false},"coin 223":{"taken":false},"coin 224":{"taken":false},"coin 225":{"taken":false},"coin 226":{"taken":false},"coin 227":{"taken":false},"coin 228":{"taken":false},"coin 229":{"taken":false},"coin 230":{"taken":false},"coin 231":{"taken":false},"coin 232":{"taken":false},"coin 233":{"taken":false},"coin 234":{"taken":false},"coin 235":{"taken":false},"coin 236":{"taken":false},"coin 237":{"taken":false},"coin 238":{"taken":false},"coin 239":{"taken":false},"coin 240":{"taken":false},"coin 241":{"taken":false},"coin 242":{"taken":false},"coin 243":{"taken":false},"coin 244":{"taken":false},"coin 245":{"taken":false},"coin 246":{"taken":false},"coin 247":{"taken":false},"coin 248":{"taken":false},"coin 249":{"taken":false},"coin 250":{"taken":false},"coin 251":{"taken":false},"coin 252":{"taken":false},"coin 253":{"taken":false},"coin 254":{"taken":false},"coin 255":{"taken":false},"coin 256":{"taken":false},"coin 257":{"taken":false},"coin 258":{"taken":false},"coin 259":{"taken":false}},"entities":{"generations":[0,0,0],"free":[],"components":{"body":{"0":{"at":[-10.0,0.3499999940395355,-4.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0},"1":{"at":[30.0,0.3499999940395355,-6.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0},"2":{"at":[-16.0,0.3499999940395355,74.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0}},"vitality":{"0":{"current":20.0,"armour":0.0,"mourned":false},"1":{"current":20.0,"armour":0.0,"mourned":false},"2":{"current":20.0,"armour":0.0,"mourned":false}},"facing":{"0":{"yaw":0.0,"turnRate":6.0,"eyeFraction":0.32},"1":{"yaw":0.0,"turnRate":6.0,"eyeFraction":0.32},"2":{"yaw":0.0,"turnRate":6.0,"eyeFraction":0.32}},"thinking":{"0":{"target":1,"waiting":0.0},"1":{"target":1,"waiting":0.0},"2":{"target":1,"waiting":0.0}}}},"actors":{"tick":0}},"tape":{"seed":1,"frames":[{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0}]},"buildStamp":"tpl-02-sample","checkpoints":{"every":25,"steps":[25,50,75,100,125,150,175,200,225,250,275,300,325,350,375,400,425,450,475,500,525,550,575,600],"digests":["972e8158","91ca445f","a61c6ae9","0fc234f2","8c514970","bbd009ae","64b68c7f","ce6e0665","11a863e7","bcc7df84","ea4a432d","3b1c63f1","dfa5ead2","e4736057","11966675","606b1465","87f8329d","04b2974c","3ef2ff17","86ac5737","8b08843a","309013ce","b06fd6e8","f47d6837"]}} \ No newline at end of file +{"version":1,"level":"assets/levels/ascent.json","levelHash":"1e491529","run":{"version":1,"runner":{"body":{"at":[0.0,0.8999999761581421,-22.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0},"health":{"current":100.0,"armour":0.0,"mourned":false},"purse":{},"keys":[],"yaw":0.0,"coyote":0.12,"buffer":0.0,"airJumps":1,"dashCooldown":0.0,"wallCoyote":0.0,"wallAway":[0.0,0.0,0.0]},"random":1,"state":"running","respawn":[0.0,0.0,-22.0],"deaths":0,"lives":-1,"elapsed":0.0,"mechanisms":{"coin one":{"taken":false},"coin two":{"taken":false},"coin three":{"taken":false},"yard coin one":{"taken":false},"yard coin two":{"taken":false},"yard coin three":{"taken":false},"vault coin 1":{"taken":false},"vault coin 2":{"taken":false},"vault coin 3":{"taken":false},"vault coin 4":{"taken":false},"the green key":{"taken":false},"plateau coin one":{"taken":false},"plateau coin two":{"taken":false},"plateau coin three":{"taken":false},"the high pad":{},"plateau coin four":{"taken":false},"the first pad":{},"high coin":{"taken":false},"the brink":{"reached":false},"arc coin one":{"taken":false},"arc coin two":{"taken":false},"arc coin three":{"taken":false},"the first drop":{},"past the drop":{"reached":false},"the ruin spikes":{},"the ruin pad":{},"the shaft coin":{"taken":false},"the second shaft coin":{"taken":false},"the third shaft coin":{"taken":false},"the blue key":{"taken":false},"the canyon rim":{"reached":false},"the gulf":{},"the first barge":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the second barge":{"progress":0.5468750000000017,"goal":1.0,"held":0.0,"wasMoving":false},"the third barge":{"progress":1.0,"goal":1.0,"held":0.4166666666666794,"wasMoving":false},"canyon coin one":{"taken":false},"canyon coin two":{"taken":false},"canyon coin three":{"taken":false},"the west ferry":{"progress":0.5624999999999991,"goal":1.0,"held":0.0,"wasMoving":false},"the west skiff":{"progress":1.0,"goal":1.0,"held":0.3166666666666723,"wasMoving":false},"the east ferry":{"progress":0.5624999999999991,"goal":1.0,"held":0.0,"wasMoving":false},"the east skiff":{"progress":1.0,"goal":1.0,"held":0.3166666666666723,"wasMoving":false},"the quarry pad":{},"the scaffold hoist":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the scaffold shuttle":{"progress":0.25,"goal":1.0,"held":0.0,"wasMoving":false},"the forecourt":{"reached":false},"the tower lift":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the lift plate":{"spent":false},"tower coin one":{"taken":false},"tower coin two":{"taken":false},"tower coin three":{"taken":false},"tower coin four":{"taken":false},"the blue gate":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the blue gate's plate":{"spent":false},"the maze lamp 1":{"enabled":true},"the maze lamp 2":{"enabled":true},"the maze lamp 3":{"enabled":true},"the maze lamp 4":{"enabled":true},"the maze lamp 5":{"enabled":true},"the maze lamp 6":{"enabled":true},"the maze lamp 7":{"enabled":true},"the maze lamp 8":{"enabled":true},"the maze lamp 9":{"enabled":true},"maze coin 1":{"taken":false},"maze coin 2":{"taken":false},"maze coin 3":{"taken":false},"maze coin 4":{"taken":false},"maze coin 5":{"taken":false},"maze coin 6":{"taken":false},"maze coin 7":{"taken":false},"maze coin 8":{"taken":false},"maze coin 9":{"taken":false},"maze coin 10":{"taken":false},"maze coin 11":{"taken":false},"maze coin 12":{"taken":false},"maze coin 13":{"taken":false},"the green gate":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the green gate's plate":{"spent":false},"the east lift":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the east plate":{"spent":false},"the saw arm":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the saw":{},"the gallery ladder":{"time":0.0},"the rope":{"time":0.0},"the cold":{"reached":false},"the crevasse":{},"the ferry":{"progress":0.0,"goal":1.0,"held":0.0,"wasMoving":false},"the west floe":{"progress":0.3124999999999995,"goal":1.0,"held":0.0,"wasMoving":false},"the east floe":{"progress":0.9375000000000041,"goal":1.0,"held":0.0,"wasMoving":false},"floe coin one":{"taken":false},"floe coin two":{"taken":false},"floe coin three":{"taken":false},"floe coin four":{"taken":false},"the far west floe":{"progress":0.49999999999999994,"goal":1.0,"held":0.0,"wasMoving":false},"the far east floe":{"progress":0.49999999999999994,"goal":1.0,"held":0.0,"wasMoving":false},"the shelf 1":{"since":null,"fallen":false,"away":0.0},"the shelf 2":{"since":null,"fallen":false,"away":0.0},"the shelf 3":{"since":null,"fallen":false,"away":0.0},"the shelf 4":{"since":null,"fallen":false,"away":0.0},"the shelf 5":{"since":null,"fallen":false,"away":0.0},"the spikes":{},"more spikes":{},"the far spikes":{},"the eastern spikes":{},"the cold pad":{},"the cold coin":{"taken":false},"ice coin one":{"taken":false},"ice coin two":{"taken":false},"the floe pad west":{},"the floe pad east":{},"past the ice":{"reached":false},"the chimney coin one":{"taken":false},"the chimney coin two":{"taken":false},"the dash coin":{"taken":false},"the coin over the gap":{"taken":false},"the foot of the stair":{"reached":false},"stair coin one":{"taken":false},"the last pad":{},"stair coin two":{"taken":false},"stair coin three":{"taken":false},"stair coin four":{"taken":false},"the summit lift":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the summit plate":{"spent":false},"ring coin 1":{"taken":false},"ring coin 2":{"taken":false},"ring coin 3":{"taken":false},"ring coin 4":{"taken":false},"ring coin 5":{"taken":false},"ring coin 6":{"taken":false},"ring coin 7":{"taken":false},"ring coin 8":{"taken":false},"the summit":{"reached":false},"the cap 1":{"broken":false},"the cap 2":{"broken":false},"the cap 3":{"broken":false},"the west hoist":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the west hoist plate":{"spent":false},"the east hoist":{"progress":0.0,"goal":0.0,"held":0.0,"wasMoving":false},"the east hoist plate":{"spent":false},"coin 019":{"taken":false},"coin 020":{"taken":false},"coin 021":{"taken":false},"coin 022":{"taken":false},"coin 023":{"taken":false},"coin 024":{"taken":false},"coin 025":{"taken":false},"coin 026":{"taken":false},"coin 027":{"taken":false},"coin 028":{"taken":false},"coin 029":{"taken":false},"coin 030":{"taken":false},"coin 031":{"taken":false},"coin 032":{"taken":false},"coin 033":{"taken":false},"coin 034":{"taken":false},"coin 035":{"taken":false},"coin 036":{"taken":false},"coin 037":{"taken":false},"coin 038":{"taken":false},"coin 039":{"taken":false},"coin 040":{"taken":false},"coin 041":{"taken":false},"coin 042":{"taken":false},"coin 043":{"taken":false},"coin 044":{"taken":false},"coin 045":{"taken":false},"coin 046":{"taken":false},"coin 047":{"taken":false},"coin 048":{"taken":false},"coin 049":{"taken":false},"coin 050":{"taken":false},"coin 051":{"taken":false},"coin 052":{"taken":false},"coin 053":{"taken":false},"coin 054":{"taken":false},"coin 055":{"taken":false},"coin 056":{"taken":false},"coin 057":{"taken":false},"coin 058":{"taken":false},"coin 059":{"taken":false},"coin 060":{"taken":false},"coin 061":{"taken":false},"coin 062":{"taken":false},"coin 063":{"taken":false},"coin 064":{"taken":false},"coin 065":{"taken":false},"coin 066":{"taken":false},"coin 067":{"taken":false},"coin 068":{"taken":false},"coin 069":{"taken":false},"coin 070":{"taken":false},"coin 077":{"taken":false},"coin 078":{"taken":false},"coin 079":{"taken":false},"coin 080":{"taken":false},"coin 081":{"taken":false},"coin 082":{"taken":false},"coin 083":{"taken":false},"coin 084":{"taken":false},"coin 085":{"taken":false},"coin 086":{"taken":false},"coin 087":{"taken":false},"coin 088":{"taken":false},"coin 089":{"taken":false},"coin 090":{"taken":false},"coin 091":{"taken":false},"coin 092":{"taken":false},"coin 093":{"taken":false},"coin 094":{"taken":false},"coin 095":{"taken":false},"coin 096":{"taken":false},"coin 097":{"taken":false},"coin 098":{"taken":false},"coin 099":{"taken":false},"coin 100":{"taken":false},"coin 101":{"taken":false},"coin 102":{"taken":false},"coin 103":{"taken":false},"coin 104":{"taken":false},"coin 105":{"taken":false},"coin 106":{"taken":false},"coin 107":{"taken":false},"coin 108":{"taken":false},"coin 109":{"taken":false},"coin 127":{"taken":false},"coin 128":{"taken":false},"coin 129":{"taken":false},"coin 130":{"taken":false},"coin 131":{"taken":false},"coin 132":{"taken":false},"coin 133":{"taken":false},"coin 134":{"taken":false},"coin 135":{"taken":false},"coin 136":{"taken":false},"coin 137":{"taken":false},"coin 138":{"taken":false},"coin 139":{"taken":false},"coin 140":{"taken":false},"coin 141":{"taken":false},"coin 142":{"taken":false},"coin 143":{"taken":false},"coin 144":{"taken":false},"coin 145":{"taken":false},"coin 146":{"taken":false},"coin 147":{"taken":false},"coin 148":{"taken":false},"coin 149":{"taken":false},"coin 150":{"taken":false},"coin 151":{"taken":false},"coin 152":{"taken":false},"coin 153":{"taken":false},"coin 154":{"taken":false},"coin 155":{"taken":false},"coin 156":{"taken":false},"coin 157":{"taken":false},"coin 158":{"taken":false},"coin 159":{"taken":false},"coin 160":{"taken":false},"coin 161":{"taken":false},"coin 162":{"taken":false},"coin 167":{"taken":false},"coin 168":{"taken":false},"coin 169":{"taken":false},"coin 170":{"taken":false},"coin 171":{"taken":false},"coin 175":{"taken":false},"coin 176":{"taken":false},"coin 177":{"taken":false},"coin 178":{"taken":false},"coin 179":{"taken":false},"coin 180":{"taken":false},"coin 181":{"taken":false},"coin 182":{"taken":false},"coin 183":{"taken":false},"coin 184":{"taken":false},"coin 185":{"taken":false},"coin 186":{"taken":false},"coin 187":{"taken":false},"coin 188":{"taken":false},"coin 189":{"taken":false},"coin 190":{"taken":false},"coin 207":{"taken":false},"coin 208":{"taken":false},"coin 209":{"taken":false},"coin 210":{"taken":false},"coin 211":{"taken":false},"coin 212":{"taken":false},"coin 213":{"taken":false},"coin 214":{"taken":false},"coin 215":{"taken":false},"coin 216":{"taken":false},"coin 217":{"taken":false},"coin 218":{"taken":false},"coin 219":{"taken":false},"coin 220":{"taken":false},"coin 221":{"taken":false},"coin 222":{"taken":false},"coin 223":{"taken":false},"coin 224":{"taken":false},"coin 225":{"taken":false},"coin 226":{"taken":false},"coin 227":{"taken":false},"coin 228":{"taken":false},"coin 229":{"taken":false},"coin 230":{"taken":false},"coin 231":{"taken":false},"coin 232":{"taken":false},"coin 233":{"taken":false},"coin 234":{"taken":false},"coin 235":{"taken":false},"coin 236":{"taken":false},"coin 237":{"taken":false},"coin 238":{"taken":false},"coin 239":{"taken":false},"coin 240":{"taken":false},"coin 241":{"taken":false},"coin 242":{"taken":false},"coin 243":{"taken":false},"coin 244":{"taken":false},"coin 245":{"taken":false},"coin 246":{"taken":false},"coin 247":{"taken":false},"coin 248":{"taken":false},"coin 249":{"taken":false},"coin 250":{"taken":false},"coin 251":{"taken":false},"coin 252":{"taken":false},"coin 253":{"taken":false},"coin 254":{"taken":false},"coin 255":{"taken":false},"coin 256":{"taken":false},"coin 257":{"taken":false},"coin 258":{"taken":false},"coin 259":{"taken":false}},"entities":{"generations":[0,0,0],"free":[],"components":{"body":{"0":{"at":[-10.0,0.3499999940395355,-4.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0},"1":{"at":[30.0,0.3499999940395355,-6.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0},"2":{"at":[-16.0,0.3499999940395355,74.0],"velocity":[0.0,0.0,0.0],"grounded":false,"coyote":0.0,"jumpBuffer":0.0}},"vitality":{"0":{"current":20.0,"armour":0.0,"mourned":false},"1":{"current":20.0,"armour":0.0,"mourned":false},"2":{"current":20.0,"armour":0.0,"mourned":false}},"facing":{"0":{"yaw":0.0,"turnRate":6.0,"eyeFraction":0.32},"1":{"yaw":0.0,"turnRate":6.0,"eyeFraction":0.32},"2":{"yaw":0.0,"turnRate":6.0,"eyeFraction":0.32}},"thinking":{"0":{"target":1,"waiting":0.0},"1":{"target":1,"waiting":0.0},"2":{"target":1,"waiting":0.0}}}},"actors":{"tick":0}},"tape":{"seed":1,"frames":[{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward","jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"released":["jump"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward","dropThrough"],"sy":1.0},{"pressed":["moveForward"],"released":["dropThrough"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"pressed":["moveForward"],"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0},{"sy":1.0}]},"buildStamp":"tpl-02-sample","checkpoints":{"every":25,"steps":[25,50,75,100,125,150,175,200,225,250,275,300,325,350,375,400,425,450,475,500,525,550,575,600],"digests":["972e8158","91ca445f","a61c6ae9","0fc234f2","8c514970","bbd009ae","64b68c7f","ce6e0665","11a863e7","bcc7df84","ea4a432d","3b1c63f1","dfa5ead2","e4736057","11966675","606b1465","87f8329d","04b2974c","3ef2ff17","86ac5737","8b08843a","309013ce","b06fd6e8","f47d6837"]}} \ No newline at end of file diff --git a/site/content/quickstart.md b/site/content/quickstart.md index d7a95b901..956e0be6c 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11050 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11061 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 9bcacad92..9e604db10 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11050 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11061 tests need a GPU. --- # Testing -11050 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11061 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -25,7 +25,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | -| `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 27 | +| `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 38 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 143 | | `flutter3d_game` | 296 | | `flutter3d_shaders` | 5 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11024 rather than 11050: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11035 rather than 11061: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. @@ -153,6 +153,22 @@ What this settles: a run submitted to a server can be replayed bit for bit on a

    why: a digest and not a comparison. Two machines cannot compare their worlds by sending each other their worlds: a snapshot is tens of kilobytes and a run is thousands of steps. StateDigest is 32-bit FNV-1a taken over bits rather than text, with the multiply done in halves so that no intermediate passes 2^53 and a browser gets the same number. DigestTrace takes a checkpoint every so many steps and names the first one two runs disagree at, which turns "the replay diverged" into an interval to bisect.

    +## A replay test for your game + +The same tape is also the cheapest regression test a game can have. Play a level once with the recorder on, commit the `.f3drun`, and `testReplay` in `flutter3d_testing` plays it back on the software backend in CI: + +```dart +void main() { + testReplay( + 'test/tapes/ascent.f3drun', + start: Ascent.open, + goldensAt: [120, 600], + ); +} +``` + +`start` builds the game against the device and the input it is handed, and returns a `ReplaySubject`: four methods, `restore`, `step`, `save` and `frame`, plus the level's hash. At every checkpoint the tape holds (or only those named in `digestAt`) the digest of `save()` has to equal the recorded one, and a mismatch names the step and the last one that still agreed. At each step in `goldensAt` the frame is compared with `test/goldens/-.png`, recorded on the first local run. A level edited since the recording fails as exactly that, before a step is taken, rather than as a divergence at step 25 that sends somebody into the physics. That check found its first stale tape the day it was written: the platformer's sample on this site had been recorded before the level was last changed. `apps/flutter3d_demo_platformer/test/replay_test.dart` is the whole example, about a hundred lines with the comments. + ## What can be tested without a device Everything except the Impeller goldens. In practice that means: diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index bfa8d0795..3024f0e94 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -588,6 +588,30 @@ still steps without recording an entry or a keyframe (older than N4), so the tape is short of the state by the steps taken that way, and a rewind or branch afterwards replays without them. The cross-OS digest test §7 asks of the step does not apply yet: N4 adds nothing that runs in the step. +**N5.** *Done:* `testReplay(path, start:, digestAt:, goldensAt:)` in +`flutter3d_testing` registers a test that reads a `.f3drun`, builds the game +on a software device and replays the tape into it. At each step of +`digestAt` (by default every checkpoint the tape holds) the digest of the +game's snapshot must equal the recorded one, and a divergence names its step +and the last checkpoint that agreed; at each step of `goldensAt` the frame is +compared with `test/goldens/-.png`. A game implements +`ReplaySubject` (`levelHash`, `restore`, `step`, `save`, `frame`), which is +four calls and the hash. Refused before the first step: a step past the end +of the tape, a digest where no checkpoint was taken, a test checking nothing, +and a level whose hash changed since the recording. `expectReplayMatches` +takes a `Demo` already in memory, `readTape` reads one with a sentence when +it cannot. The example is `apps/flutter3d_demo_platformer/test/replay_test.dart`: +600 steps of the Ascent, 24 checkpoints, goldens at 120 and 600, about five +seconds. Its hash check found the site's `platformer.f3drun` recorded before +the level's last edit; `tool/record_sample.dart` now writes that sample and +the test's tape from one run. +*Left:* a digest-only replay under plain `dart test` through +`HeadlessGame`/`HeadlessRun`, for a game whose simulation has no Flutter in +it and a server that verifies runs (`HeadlessRun` would need `restore`); the +dungeon's `replay_golden_test.dart` and the racing demo moved onto +`testReplay`; the strategy demo's `MatchDemo`, which is not a `Demo`; and a +way to drop a tape the running game recorded into `test/tapes/` from the +editor or the MCP server instead of a script per game. Lifted exclusions (decision 12), each its own item: From 6d5547d86a4ba8266a8819726886a677b80eb4f0 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:23:02 +0300 Subject: [PATCH 043/141] A save is migrated, written on pause, and kept in the cloud only with consent A save carries its schema version, and SaveSchema migrates an older one step by step on read. Autosave writes the run on pause and at checkpoints. SaveSync compares this device's run with the cloud's by step and digest and uploads, downloads or asks; it sends nothing until the player agrees. The stores are an HTTP one and a platform channel for Play Games and iCloud, whose native sides are not written yet. --- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_demo_platformer/lib/main.dart | 11 +- .../lib/src/run.dart | 6 + packages/flutter3d_game/CHANGELOG.md | 17 ++ .../flutter3d_game/lib/flutter3d_game.dart | 5 + .../lib/src/cloud/cloud_save_store.dart | 71 +++++ .../lib/src/cloud/http_cloud_saves.dart | 123 ++++++++ .../lib/src/cloud/platform_cloud_saves.dart | 111 +++++++ .../lib/src/cloud/save_sync.dart | 271 +++++++++++++++++ .../flutter3d_game/lib/src/run/autosave.dart | 74 +++++ .../lib/src/run/run_session.dart | 21 +- .../lib/src/screens/save_file.dart | 137 +++++---- packages/flutter3d_game/pubspec.yaml | 6 + .../flutter3d_game/test/autosave_test.dart | 150 ++++++++++ .../test/cloud_save_stores_test.dart | 184 ++++++++++++ .../flutter3d_game/test/save_file_test.dart | 47 ++- .../flutter3d_game/test/save_sync_test.dart | 279 ++++++++++++++++++ packages/flutter3d_sim/CHANGELOG.md | 10 + packages/flutter3d_sim/lib/flutter3d_sim.dart | 2 + .../lib/src/save/save_record.dart | 202 +++++++++++++ .../lib/src/save/save_schema.dart | 90 ++++++ .../flutter3d_sim/test/save_record_test.dart | 219 ++++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- tasks/0.9-engine-roadmap.md | 25 ++ tool/structure/repository.dart | 6 + 27 files changed, 2012 insertions(+), 71 deletions(-) create mode 100644 packages/flutter3d_game/lib/src/cloud/cloud_save_store.dart create mode 100644 packages/flutter3d_game/lib/src/cloud/http_cloud_saves.dart create mode 100644 packages/flutter3d_game/lib/src/cloud/platform_cloud_saves.dart create mode 100644 packages/flutter3d_game/lib/src/cloud/save_sync.dart create mode 100644 packages/flutter3d_game/lib/src/run/autosave.dart create mode 100644 packages/flutter3d_game/test/autosave_test.dart create mode 100644 packages/flutter3d_game/test/cloud_save_stores_test.dart create mode 100644 packages/flutter3d_game/test/save_sync_test.dart create mode 100644 packages/flutter3d_sim/lib/src/save/save_record.dart create mode 100644 packages/flutter3d_sim/lib/src/save/save_schema.dart create mode 100644 packages/flutter3d_sim/test/save_record_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index fd9056651..3cddff6d2 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11061 tests** across 43 packages and 10 applications | +| Unit tests | **11103 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index dfdf119a8..594596677 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11061 tests across forty-three packages and nine applications. The +There are 11103 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_demo_platformer/lib/main.dart b/apps/flutter3d_demo_platformer/lib/main.dart index 7f922459e..77174cc27 100644 --- a/apps/flutter3d_demo_platformer/lib/main.dart +++ b/apps/flutter3d_demo_platformer/lib/main.dart @@ -316,6 +316,10 @@ class _GameScreenState extends State /// reads [_runOrNull]. RunCubit get _run => _runOrNull!; + /// Writes the run at a checkpoint, on the way into a pause and when the + /// application goes to the background. Built beside [_runOrNull]. + Autosave? _autosave; + /// What a step sounds like. See `soundtrack.dart` for why this is a class /// and not a method: a decision can be tested, an effect inside a widget /// cannot. @@ -620,6 +624,7 @@ class _GameScreenState extends State }, ), ); + _autosave = Autosave(_run.run)..watchLifecycle(); _ticker = createTicker(_onTick)..start(); @@ -785,7 +790,7 @@ class _GameScreenState extends State final sim = _sim; if (sim == null) return; if (!_screen.shouldSave(sim.respawnPoint)) return; - _run.save(); + _autosave?.checkpoint(); } /// What the pad means to a screen rather than to the runner. @@ -891,6 +896,9 @@ class _GameScreenState extends State pointerHeld: _devices.isCaptured, padConnected: _pad.isConnected, ); + // A pause is where most sessions end — the menu opened to quit, the pad + // put down — so the run is written on the way in. + _autosave?.paused(_loop.paused); _loop.advance(dt); // The loop has always counted the simulated time it could not run. Nobody // read it, so a machine that could not keep up ran the game slowly and said @@ -1104,6 +1112,7 @@ class _GameScreenState extends State // Null if the device never opened: nothing ran, so there is nothing to // keep — and the cubit to close was never built either. _runOrNull?.save(); + _autosave?.dispose(); // Closed like the three below, and the cubit unhooks itself from the // session first — see `RunCubit.close` for why the order matters. unawaited(_runOrNull?.close()); diff --git a/apps/flutter3d_demo_platformer/lib/src/run.dart b/apps/flutter3d_demo_platformer/lib/src/run.dart index a1f9f739e..bb9592948 100644 --- a/apps/flutter3d_demo_platformer/lib/src/run.dart +++ b/apps/flutter3d_demo_platformer/lib/src/run.dart @@ -291,6 +291,12 @@ final class PlatformerRun extends RunSession { void restoreInto(LevelReady level, Snapshot snapshot) => level.sim.restore(snapshot); + /// The elapsed time in steps. It is carried from level to level and adds up + /// the loop's fixed sixtieths, so it is the run's step count across levels + /// without a counter of its own. + @override + int stepOf(LevelReady level) => (level.sim.elapsed * 60.0).round(); + /// The tally of the run so far. Only this side knows what the level after /// this one is, so only this side can carry anything into it. @override diff --git a/packages/flutter3d_game/CHANGELOG.md b/packages/flutter3d_game/CHANGELOG.md index f489a9181..f868e46d1 100644 --- a/packages/flutter3d_game/CHANGELOG.md +++ b/packages/flutter3d_game/CHANGELOG.md @@ -31,6 +31,23 @@ - **`registerTimelineExtensions`** adds `scrubTo`, `returnToPresent`, `branchHere` and, with an `entityLayout`, `tracks`; `status` answers the present step, the oldest held and the scrub. +- **`Autosave` writes the run on the way into a pause, at a checkpoint and + when the application goes to the background**, which on a phone is the + only warning before the process is ended. `SaveFile` skips a write of the + run it last wrote, so a menu opened and closed costs nothing. +- **`SaveFile` takes a `SaveSchema`** and migrates older saves on the way in; + a save now carries its schema, its step and its digest. `readRecord`, + `parse`, `writeRecord` and `encode` are for copies kept elsewhere. + `RunSession.stepOf` says how far a run has got, and `save` returns whether + it wrote. +- **`SaveSync` keeps a save in the cloud, and sends nothing until the player + agrees.** Consent is off on a fresh install and is kept in a document of + its own; without it no store is called. Conflicts are settled by + `resolveSaves`; two equal runs come back as `SyncOutcome.ask` for the + player, and a cloud save from a newer build is left alone. Stores: + `HttpCloudSaves` (`GET`/`PUT` with `ETag` preconditions) and + `PlatformCloudSaves` for Play Games and iCloud over the + `flutter3d/cloud_saves` channel. Every failure is an answer, not a throw. - **`replayAfterHotSwap` asks the question a hot reload leaves open on its own.** After every `HotSwap` it lives the last seconds again under the new diff --git a/packages/flutter3d_game/lib/flutter3d_game.dart b/packages/flutter3d_game/lib/flutter3d_game.dart index 05f5fb17f..d37f0d7da 100644 --- a/packages/flutter3d_game/lib/flutter3d_game.dart +++ b/packages/flutter3d_game/lib/flutter3d_game.dart @@ -35,6 +35,10 @@ /// the run — [RunSession] is an ordinary class. library; +export 'src/cloud/cloud_save_store.dart'; +export 'src/cloud/http_cloud_saves.dart'; +export 'src/cloud/platform_cloud_saves.dart'; +export 'src/cloud/save_sync.dart'; export 'src/config/accommodations.dart'; export 'src/config/game_config.dart'; export 'src/input/bindings.dart'; @@ -42,6 +46,7 @@ export 'src/input/desktop_input.dart'; export 'src/input/pad_actions.dart'; export 'src/input/playing.dart'; export 'src/input/touch_controls.dart'; +export 'src/run/autosave.dart'; export 'src/run/bug_report.dart'; export 'src/run/demo_recording.dart'; export 'src/run/demo_timeline.dart'; diff --git a/packages/flutter3d_game/lib/src/cloud/cloud_save_store.dart b/packages/flutter3d_game/lib/src/cloud/cloud_save_store.dart new file mode 100644 index 000000000..1238950b1 --- /dev/null +++ b/packages/flutter3d_game/lib/src/cloud/cloud_save_store.dart @@ -0,0 +1,71 @@ +/// Somewhere other than this device that a save can be kept. +/// +/// ## Answers, not exceptions +/// +/// A network that is down, a player signed out of Play Games, an iCloud +/// account with no space: all ordinary, all reached on a launch the player +/// expects to work. So every call answers, and an answer that is not the +/// document says why in a sentence [SaveSync] can show. Nothing here throws +/// for a reason the network gave. +/// +/// ## Versions +/// +/// Every copy comes back with a version the store chose — an HTTP `ETag`, +/// Play Games' snapshot revision, an iCloud change token — and a write names +/// the version it means to replace. A store that has moved on since answers +/// [CloudMoved] rather than writing, so two devices syncing at once cannot +/// both win and silently lose one of the runs. +library; + +abstract interface class CloudSaveStore { + /// The store's name as a player knows it — "Play Games", "iCloud" — for + /// the sentences a sync answers with. + String get name; + + /// The copy kept under [slot], if there is one. + Future fetch(String slot); + + /// Keeps [document] under [slot] in place of the copy at [replacing], or + /// only if there is no copy at all when [replacing] is null. + Future put(String slot, String document, {String? replacing}); +} + +/// What [CloudSaveStore.fetch] found. +sealed class CloudFetch { + const CloudFetch(); +} + +/// What [CloudSaveStore.put] did. +sealed class CloudPut { + const CloudPut(); +} + +final class CloudDocument extends CloudFetch { + const CloudDocument(this.document, {required this.version}); + + final String document; + final String version; +} + +/// Nothing is kept there yet. +final class CloudEmpty extends CloudFetch { + const CloudEmpty(); +} + +final class CloudStored extends CloudPut { + const CloudStored({required this.version}); + + final String version; +} + +/// Another device wrote since the version a write meant to replace. +final class CloudMoved extends CloudPut { + const CloudMoved(); +} + +/// The store could not be reached, or would not do it, and why. +final class CloudUnavailable implements CloudFetch, CloudPut { + const CloudUnavailable(this.reason); + + final String reason; +} diff --git a/packages/flutter3d_game/lib/src/cloud/http_cloud_saves.dart b/packages/flutter3d_game/lib/src/cloud/http_cloud_saves.dart new file mode 100644 index 000000000..65ff95427 --- /dev/null +++ b/packages/flutter3d_game/lib/src/cloud/http_cloud_saves.dart @@ -0,0 +1,123 @@ +import 'package:http/http.dart' as http; + +import 'cloud_save_store.dart'; + +/// Saves kept on a server the game's makers run, over plain HTTP. +/// +/// ## The protocol +/// +/// One resource per slot, at `/saves//`: +/// +/// | | | +/// |---|---| +/// | `GET` | `200` with the document and an `ETag`, or `404` for none | +/// | `PUT` | the document, with `If-Match: ` to replace a copy or `If-None-Match: *` to create one; `200`, `201` or `204` with the new `ETag`, or `412` when another device wrote first | +/// +/// That is all of it, on purpose: conditional requests are what HTTP already +/// has for "write this unless somebody else did", and any server — a function +/// behind a CDN, a few lines of shelf — can answer them. +/// +/// **An `ETag` is required, not hoped for.** Without one a write cannot say +/// what it replaces, and the one thing this store is for — two devices not +/// overwriting each other — would rest on luck. A server that sends none is +/// refused, saying so. +/// +/// Who the player is goes in [headers], asked for on every request so a token +/// that expired between two syncs is fetched again by whoever owns it. +final class HttpCloudSaves implements CloudSaveStore { + HttpCloudSaves({ + required this.base, + required this.game, + this.headers, + http.Client? client, + }) : _client = client ?? http.Client(), + _ownsClient = client == null; + + /// Where the server is. Slots are below `saves//` under it. + final Uri base; + + /// Which game's saves, so one server can keep several. + final String game; + + /// The headers that say who the player is, usually an `Authorization`. + final Future> Function()? headers; + + final http.Client _client; + final bool _ownsClient; + + @override + String get name => base.host.isEmpty ? 'the save server' : base.host; + + Uri _slot(String slot) => base.replace( + pathSegments: [ + ...base.pathSegments.where((segment) => segment.isNotEmpty), + 'saves', + game, + slot, + ], + ); + + @override + Future fetch(String slot) async { + final uri = _slot(slot); + try { + final response = await _client.get(uri, headers: await _headers()); + return switch (response.statusCode) { + 404 => const CloudEmpty(), + 200 => switch (response.headers['etag']) { + final etag? => CloudDocument(response.body, version: etag), + null => CloudUnavailable( + '$name sent the save without an ETag, so a write could not tell ' + 'whether another device wrote in between', + ), + }, + final status => CloudUnavailable('GET $uri answered $status'), + }; + } on Exception catch (error) { + return CloudUnavailable('$name could not be reached ($error)'); + } + } + + @override + Future put( + String slot, + String document, { + String? replacing, + }) async { + final uri = _slot(slot); + try { + final response = await _client.put( + uri, + headers: { + ...await _headers(), + 'Content-Type': 'application/json', + if (replacing != null) + 'If-Match': replacing + else + 'If-None-Match': '*', + }, + body: document, + ); + return switch (response.statusCode) { + 412 => const CloudMoved(), + 200 || 201 || 204 => switch (response.headers['etag']) { + final etag? => CloudStored(version: etag), + null => CloudUnavailable( + '$name kept the save but sent no ETag for it', + ), + }, + final status => CloudUnavailable('PUT $uri answered $status'), + }; + } on Exception catch (error) { + return CloudUnavailable('$name could not be reached ($error)'); + } + } + + Future> _headers() async => + await headers?.call() ?? const {}; + + /// Frees the HTTP client, when this made it. + void close() { + if (_ownsClient) _client.close(); + } +} diff --git a/packages/flutter3d_game/lib/src/cloud/platform_cloud_saves.dart b/packages/flutter3d_game/lib/src/cloud/platform_cloud_saves.dart new file mode 100644 index 000000000..07d30ba1a --- /dev/null +++ b/packages/flutter3d_game/lib/src/cloud/platform_cloud_saves.dart @@ -0,0 +1,111 @@ +import 'package:flutter/services.dart'; + +import 'cloud_save_store.dart'; + +/// A platform service a player is already signed in to. +/// +/// **A class a game can make more of**, not an enum: a store this package does +/// not name — Steam Cloud, a console's own — is one more constant and a native +/// side that answers to its [name], with nothing here to change. +final class CloudProvider { + const CloudProvider(this.name, this.title); + + /// What the platform side is told, in the channel's `provider` argument. + final String name; + + /// As a player knows it. + final String title; + + /// Google Play Games Services' saved games, on Android. + static const CloudProvider playGames = CloudProvider( + 'playGames', + 'Play Games', + ); + + /// iCloud, on iOS and macOS. + static const CloudProvider iCloud = CloudProvider('iCloud', 'iCloud'); +} + +/// Saves kept by Play Games or iCloud, through the platform's own side. +/// +/// ## The channel +/// +/// `flutter3d/cloud_saves`, two methods, each with `provider` (the +/// [CloudProvider]'s name) and `slot`: +/// +/// | | | +/// |---|---| +/// | `fetch` | null for no copy, or `{document, version}` | +/// | `put` | also `document` and `replacing` (null to create); `{version}` when written, `{moved: true}` when another device wrote first | +/// +/// The versions are the service's own: a Play Games snapshot's revision, an +/// iCloud record's change tag. The Dart side never reads them, it only hands +/// them back, so each platform keeps what its service already compares. +/// +/// **A build without the platform side answers, it does not crash.** The +/// method channel throws `MissingPluginException` there, and a game that +/// offered iCloud on a build that cannot reach it should say so on the +/// settings screen rather than in a crash report. +final class PlatformCloudSaves implements CloudSaveStore { + PlatformCloudSaves(this.provider, {MethodChannel? channel}) + : _channel = channel ?? const MethodChannel('flutter3d/cloud_saves'); + + final CloudProvider provider; + final MethodChannel _channel; + + @override + String get name => provider.title; + + @override + Future fetch(String slot) async => switch (await _ask( + 'fetch', + {'slot': slot}, + )) { + (_, final CloudUnavailable failed) => failed, + (null, _) => const CloudEmpty(), + ({'document': final String document, 'version': final String version}, _) => + CloudDocument(document, version: version), + (final answer, _) => CloudUnavailable( + '$name answered a fetch with $answer', + ), + }; + + @override + Future put( + String slot, + String document, { + String? replacing, + }) async => switch (await _ask('put', { + 'slot': slot, + 'document': document, + 'replacing': replacing, + })) { + (_, final CloudUnavailable failed) => failed, + ({'moved': true}, _) => const CloudMoved(), + ({'version': final String version}, _) => CloudStored(version: version), + (final answer, _) => CloudUnavailable( + '$name answered a write with $answer', + ), + }; + + /// The platform's answer, or why there was none. + Future<(Object?, CloudUnavailable?)> _ask( + String method, + Map arguments, + ) async { + try { + final answer = await _channel.invokeMethod( + method, + {'provider': provider.name, ...arguments}, + ); + return (answer, null); + } on MissingPluginException { + return (null, CloudUnavailable('$name is not available in this build')); + } on PlatformException catch (error) { + return ( + null, + CloudUnavailable('$name refused: ${error.message ?? error.code}'), + ); + } + } +} diff --git a/packages/flutter3d_game/lib/src/cloud/save_sync.dart b/packages/flutter3d_game/lib/src/cloud/save_sync.dart new file mode 100644 index 000000000..5d4fd16da --- /dev/null +++ b/packages/flutter3d_game/lib/src/cloud/save_sync.dart @@ -0,0 +1,271 @@ +import 'dart:convert'; + +import 'package:flutter3d_app/flutter3d_app.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart' + show + SaveFound, + SaveNotRead, + SaveRead, + SaveRecord, + SaveResolution, + resolveSaves; + +import '../screens/save_file.dart'; +import 'cloud_save_store.dart'; + +/// How a sync ended. +/// +/// **Constants rather than an enum**, so a later outcome — a player signed +/// out, a quota reached — is an addition and not a break in every game's +/// `switch`. Compare with `==`; [SyncReport.message] is the sentence. +final class SyncOutcome { + const SyncOutcome._(this.name); + + final String name; + + /// The player has not agreed to cloud saves. Nothing was sent or asked. + static const SyncOutcome notConsented = SyncOutcome._('notConsented'); + + /// Both copies are the same run, or there is none. + static const SyncOutcome inSync = SyncOutcome._('inSync'); + + /// This device's run was sent. + static const SyncOutcome uploaded = SyncOutcome._('uploaded'); + + /// The cloud's run was written here. A run already loaded is stale now. + static const SyncOutcome downloaded = SyncOutcome._('downloaded'); + + /// Two different runs equally far along. Settle with [SaveSync.settle]. + static const SyncOutcome ask = SyncOutcome._('ask'); + + /// The cloud copy is not one this build can use, and was left alone. + static const SyncOutcome refused = SyncOutcome._('refused'); + + /// The store could not be reached, or would not do it. + static const SyncOutcome unavailable = SyncOutcome._('unavailable'); + + @override + String toString() => 'SyncOutcome.$name'; +} + +/// What [SaveSync.sync] did, in a sentence, and for [SyncOutcome.ask] the two +/// runs to choose between. +final class SyncReport { + const SyncReport( + this.outcome, + this.message, { + this.local, + this.remote, + this.remoteVersion, + }); + + final SyncOutcome outcome; + final String message; + + final SaveRecord? local; + final SaveRecord? remote; + + /// The version of [remote] in the store, which a settling write replaces. + final String? remoteVersion; +} + +/// The save on this device and a copy of it in a [CloudSaveStore], made one. +/// +/// ## Consent first +/// +/// **Nothing leaves the device until the player says it may.** [consented] is +/// false on a fresh install, kept in a document of its own beside the save, +/// and every call below that would touch the store checks it first and +/// answers [SyncOutcome.notConsented] without a request. Withdrawing forgets +/// what the two sides last agreed on as well, so turning it back on later is +/// a first sync again rather than a resolution against a stale base. +/// +/// ## When to call it +/// +/// Before `RunSession.begin`, and after `Autosave` wrote at a pause or in the +/// background. A run that is already loaded keeps playing from memory, so a +/// download under it is overwritten by its next autosave — which is why +/// [SyncOutcome.downloaded] says the run is stale and the game should begin +/// again from the save. +/// +/// ## Which copy wins +/// +/// `resolveSaves`, by step and digest against the digest the two last agreed +/// on. Recorded after every sync that ended with both sides holding one run. +final class SaveSync { + SaveSync({required this.saves, required this.store, Storage? storage}) + : storage = storage ?? saves.storage; + + final SaveFile saves; + final CloudSaveStore store; + + /// Where consent and the last agreed digest are kept. + final Storage storage; + + /// The document consent is kept in. Separate from the save so clearing a + /// finished run does not quietly turn cloud saves off, and from the + /// settings so a reset of the controls does not quietly turn them on. + static const String stateName = 'cloud_saves.json'; + + Map get _state { + final text = storage.read(stateName); + if (text == null) return const {}; + try { + final json = jsonDecode(text); + return json is Map ? json : const {}; + } on FormatException { + return const {}; + } + } + + bool _keep(Map state) => + storage.write(stateName, jsonEncode(state)); + + /// Whether the player has agreed to keep saves in [store]. + /// + /// Anything but an explicit `true` on disk is no — a document that will not + /// read is not consent. + bool get consented => _state['consented'] == true; + + /// The player agreed. Returns whether that was kept. + bool consent() => _keep({..._state, 'consented': true}); + + /// The player took it back. + bool withdraw() => _keep(const {'consented': false}); + + /// The digest both sides last held, if they have met. + String? get base => switch (_state['base']) { + final String digest => digest, + _ => null, + }; + + void _agreed(String? digest) => + _keep({..._state, 'base': digest}); + + static const SyncReport _notConsented = SyncReport( + SyncOutcome.notConsented, + 'cloud saves are off, so nothing was sent', + ); + + /// Compares the two copies and moves whichever one should move. + Future sync() => _sync(retried: false); + + Future _sync({required bool retried}) async { + if (!consented) return _notConsented; + final SaveRecord? remote; + final String? version; + switch (await store.fetch(SaveFile.name)) { + case CloudUnavailable(:final reason): + return SyncReport(SyncOutcome.unavailable, reason); + case CloudEmpty(): + (remote, version) = (null, null); + case CloudDocument(:final document, version: final stored): + switch (_read(document)) { + case SaveFound(:final record): + (remote, version) = (record, stored); + case SaveNotRead(:final reason, :final newer): + return SyncReport( + SyncOutcome.refused, + newer + ? 'the save in ${store.name} is from a newer build of the ' + 'game ($reason); update to use it — it was left alone' + : 'the save in ${store.name} could not be read ($reason); ' + 'it was left alone', + ); + } + } + final local = saves.readRecord(); + switch (resolveSaves(local: local, remote: remote, base: base)) { + case SaveResolution.inSync: + _agreed(local?.digest); + return SyncReport( + SyncOutcome.inSync, + 'the save here and in ${store.name} are the same', + ); + case SaveResolution.keepLocal: + return switch (await _send(local!, replacing: version)) { + // Another device wrote between the fetch and the write. Once more + // from the top, against what it wrote; twice in a row is a device + // that is syncing in a loop, and that is said rather than chased. + null when !retried => _sync(retried: true), + null => _movedReport, + final sent => sent, + }; + case SaveResolution.takeRemote: + return _take(remote!); + case SaveResolution.ask: + return SyncReport( + SyncOutcome.ask, + 'this device and ${store.name} hold two different runs, equally ' + 'far along', + local: local, + remote: remote, + remoteVersion: version, + ); + } + } + + /// Finishes an [SyncOutcome.ask] with the player's choice: [keepLocal] + /// sends this device's run, otherwise the cloud's is written here. + Future settle(SyncReport asked, {required bool keepLocal}) async { + if (!consented) return _notConsented; + final local = asked.local; + final remote = asked.remote; + if (asked.outcome != SyncOutcome.ask || local == null || remote == null) { + return const SyncReport( + SyncOutcome.refused, + 'there was no choice to settle; sync again', + ); + } + return keepLocal + ? await _send(local, replacing: asked.remoteVersion) ?? _movedReport + : _take(remote); + } + + SyncReport get _movedReport => SyncReport( + SyncOutcome.unavailable, + '${store.name} changed while this was syncing; try again', + ); + + /// Sends [local]; null when another device wrote first. + Future _send(SaveRecord local, {String? replacing}) async { + switch (await store.put( + SaveFile.name, + SaveFile.encode(local), + replacing: replacing, + )) { + case CloudStored(): + _agreed(local.digest); + return SyncReport( + SyncOutcome.uploaded, + 'this run was saved to ${store.name}', + ); + case CloudMoved(): + return null; + case CloudUnavailable(:final reason): + return SyncReport(SyncOutcome.unavailable, reason); + } + } + + SyncReport _take(SaveRecord remote) { + if (!saves.writeRecord(remote)) { + return SyncReport( + SyncOutcome.unavailable, + 'the run from ${store.name} could not be written on this device', + ); + } + _agreed(remote.digest); + return SyncReport( + SyncOutcome.downloaded, + 'the run from ${store.name} was kept on this device', + ); + } + + SaveRead _read(String document) { + try { + return SaveRecord.read(jsonDecode(document), saves.schema); + } on FormatException catch (error) { + return SaveNotRead(error.message); + } + } +} diff --git a/packages/flutter3d_game/lib/src/run/autosave.dart b/packages/flutter3d_game/lib/src/run/autosave.dart new file mode 100644 index 000000000..e99c56401 --- /dev/null +++ b/packages/flutter3d_game/lib/src/run/autosave.dart @@ -0,0 +1,74 @@ +import 'package:flutter/widgets.dart'; + +import 'run_session.dart'; + +/// When a run is written down without the player asking. +/// +/// ## The moments +/// +/// * **A checkpoint**, because that is what a checkpoint promises: the place +/// you come back to has moved. The game says when — only it knows what its +/// checkpoints are — by calling [checkpoint]. +/// * **A pause**, because a pause is the moment before most of the ways a +/// session ends: the menu opened to quit, the pad put down, the window left. +/// Written on the way in and not on every paused frame. +/// * **The application going to the background**, which on a phone is the +/// only warning before the system ends the process. There is no pause +/// screen between a home-button press and a kill, so a game that saved only +/// on pause lost whatever was played since the last one. +/// +/// Not every frame, and not on a timer: a write in the frame budget is a +/// stutter, and a save from the middle of a jump restores into the air. +/// +/// Repeated writes of the same run cost nothing — `SaveFile` keeps the digest +/// of the last one — so a player toggling the menu does not rewrite the disk. +final class Autosave with WidgetsBindingObserver { + Autosave(this.session); + + final RunSession session; + + bool _paused = false; + bool _watching = false; + + /// Tells this whether the game is paused now. Call once per frame, or on + /// every change; only the step into a pause writes. + /// + /// Returns whether it wrote. + bool paused(bool now) { + final entering = now && !_paused; + _paused = now; + return entering && session.save(); + } + + /// The game passed a checkpoint. Returns whether it wrote. + bool checkpoint() => session.save(); + + /// Starts hearing about the application going to the background. + void watchLifecycle() { + if (_watching) return; + _watching = true; + WidgetsBinding.instance.addObserver(this); + } + + /// Stops it. Call from the game's `dispose`. + void dispose() { + if (!_watching) return; + _watching = false; + WidgetsBinding.instance.removeObserver(this); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + switch (state) { + // `inactive` first, because it is the one every platform sends before + // the others, and on iOS the last one a process is sure to see. + case AppLifecycleState.inactive || + AppLifecycleState.hidden || + AppLifecycleState.paused || + AppLifecycleState.detached: + session.save(); + case AppLifecycleState.resumed: + break; + } + } +} diff --git a/packages/flutter3d_game/lib/src/run/run_session.dart b/packages/flutter3d_game/lib/src/run/run_session.dart index 244111a0e..c838d4a80 100644 --- a/packages/flutter3d_game/lib/src/run/run_session.dart +++ b/packages/flutter3d_game/lib/src/run/run_session.dart @@ -103,6 +103,14 @@ abstract base class RunSession { /// changed. void restoreInto(L level, Snapshot snapshot); + /// How far the run has got, in simulation steps across all its levels. + /// Zero by default. + /// + /// Written beside the save so two copies of it can be told apart by more + /// than a clock — see `resolveSaves`. A game that leaves it at zero still + /// syncs; two different runs of it are simply always the player's call. + int stepOf(L level) => 0; + /// What the player takes with them into [next]. Empty by default. /// /// Called on the level being left, before the next one is read — which is the @@ -304,11 +312,16 @@ abstract base class RunSession { save(); } - /// Writes where the run has got to, if there is one worth writing. - void save() { + /// Writes where the run has got to, if there is one worth writing, and says + /// whether it did. + bool save() { final playing = _status; - if (playing is! RunPlaying || playing.outcome.isOver) return; - saves.write(playing.asset, snapshotOf(playing.level)); + if (playing is! RunPlaying || playing.outcome.isOver) return false; + return saves.write( + playing.asset, + snapshotOf(playing.level), + step: stepOf(playing.level), + ); } /// Puts [next], another build of the level being played, in its place, and diff --git a/packages/flutter3d_game/lib/src/screens/save_file.dart b/packages/flutter3d_game/lib/src/screens/save_file.dart index 7177c3c88..3bed2317a 100644 --- a/packages/flutter3d_game/lib/src/screens/save_file.dart +++ b/packages/flutter3d_game/lib/src/screens/save_file.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:flutter3d_app/flutter3d_app.dart'; import 'package:flutter3d_sim/flutter3d_sim.dart' - show Snapshot, SnapshotFormatException; + show SaveFound, SaveNotRead, SaveRecord, SaveSchema, Snapshot; import 'settings_file.dart'; @@ -24,10 +24,18 @@ import 'settings_file.dart'; /// level's path and a `Snapshot` are what any game that can be quit halfway /// through has to keep, and the only thing that differed was the name of the /// directory — which is what [appName] is. +/// +/// The document is a [SaveRecord]: the step and the digest ride along so a +/// copy kept elsewhere — see `SaveSync` — can be compared with this one, and +/// the game's [schema] version so a later build can migrate it. final class SaveFile { - SaveFile({required this.appName, Storage? storage, IssueSink? onIssue}) - : onIssue = onIssue ?? printIssue, - storage = storage ?? defaultStorage(appName, onIssue: onIssue); + SaveFile({ + required this.appName, + Storage? storage, + IssueSink? onIssue, + this.schema = const SaveSchema(), + }) : onIssue = onIssue ?? printIssue, + storage = storage ?? defaultStorage(appName, onIssue: onIssue); /// Which game's save this is. Two games served from one browser origin would /// otherwise resume into each other's levels — see [SettingsFile.appName], @@ -51,76 +59,95 @@ final class SaveFile { /// nobody can tell the two apart, least of all the person it happened to. final IssueSink onIssue; - /// A separate document from the settings on purpose: settings are what a - /// player chose and a save is where they got to, and one of those must survive - /// a delete of the other. Wiping a corrupt save should not cost the bindings. - static const String _name = 'save.json'; + /// The game's own save fields, by version, and the migrations between them. + /// + /// Empty by default, which is version 0 — what every save written before + /// games had a schema is read as. + final SaveSchema schema; + + /// The name the document is kept under — and the slot a cloud copy of it is + /// kept in, so the two are the same thing in both places. + static const String name = 'save.json'; + + /// The digest of what was last written, so a second write of the same run + /// costs nothing. Autosave writes on every pause, and most pauses are the + /// same run as the last one. + String? _written; /// The run that was saved, or null if there is nothing to resume. /// /// **Never throws**, on the same argument as [SettingsFile.read]: a save that /// cannot be read is a run that has to start again, and that is a much /// smaller loss than a game that will not launch. - ({String level, Snapshot run})? read() { - final text = storage.read(_name); - if (text == null) return null; + ({String level, Snapshot run})? read() => switch (readRecord()) { + final record? => (level: record.level, run: record.run), + null => null, + }; + + /// [read], with the step, the digest and the schema it was written at. + SaveRecord? readRecord() { + final text = storage.read(name); + return text == null ? null : parse(text); + } + + /// Reads a save document from wherever it came from — this device's storage + /// or a cloud copy — migrating it to [schema] on the way. + /// + /// Null, with an issue said, when it is not a save this build can use. + SaveRecord? parse(String text) { + final Object? json; try { - final json = jsonDecode(text); - // **These three said nothing at all**, which is worse than the `catch` - // below: a document that parses as JSON but is not a save — an empty - // object, a half-written file, a save from a build that named its keys - // differently — went back as "no save" without a word anywhere. - if (json is! Map) { - onIssue(Issue('save: the document is not an object, starting fresh')); - return null; - } - final level = json['level']; - final run = json['run']; - if (level is! String || run is! Map) { - onIssue(Issue('save: no level and run in it, starting fresh')); - return null; - } - // A save with no version in it was written by a build that had the - // version and did not send it — `write` used to hand over `run.data`, - // which is the payload without the header `toJson` adds, so the key - // never reached the disk and `Snapshot.fromJson` was never called on the - // way back. That is every save this build can find. The shape did not - // change, so it is a version 1 document that failed to say so, and - // refusing it would cost a player a run to fix a bug that was never - // theirs. Deletable once no save predates the fix. - final versioned = run.containsKey('version') - ? run - : {'version': Snapshot.formatVersion, ...run}; - return (level: level, run: Snapshot.fromJson(versioned)); - } on SnapshotFormatException catch (error) { - // The case the version exists for, and it is worth its own arm: this is - // a save from a *newer* build, and "starting fresh" would silently - // discard a run the player can still open by going back to the build - // that wrote it. Saying which is which is the whole point of refusing - // rather than misreading. - onIssue(Issue('save: ${error.message}, starting fresh')); - return null; + json = jsonDecode(text); } catch (error) { onIssue(Issue('save: could not be read, starting fresh ($error)')); return null; } + switch (SaveRecord.read(json, schema)) { + case SaveFound(:final record): + return record; + case SaveNotRead(:final reason): + // A save from a *newer* build is the case the versions exist for: + // "starting fresh" without a word would silently discard a run the + // player can still open by going back to the build that wrote it. + onIssue(Issue('save: $reason, starting fresh')); + return null; + } } /// Writes the run, and says whether it managed to. /// - /// Through [Snapshot.toJson] rather than around it: the version is added - /// there, and a document written from `run.data` carries the payload with no - /// header, which is what made the refusal above unreachable. - bool write(String level, Snapshot run) => storage.write( - _name, - const JsonEncoder.withIndent( - ' ', - ).convert({'level': level, 'run': run.toJson()}), + /// [step] is how far the run has got across its levels — see + /// `SaveRecord.step` for what it settles. + bool write(String level, Snapshot run, {int step = 0}) => writeRecord( + SaveRecord(level: level, run: run, step: step, schema: schema.version), ); + /// Writes [record] as it is: a run from a cloud copy, kept here. + /// + /// The same run written twice in a row is written once. True either way — + /// the save on disk is that run. + bool writeRecord(SaveRecord record) { + if (record.digest == _written) return true; + final wrote = storage.write(name, encode(record)); + if (wrote) _written = record.digest; + return wrote; + } + + /// The document [writeRecord] keeps, for a caller that keeps it elsewhere. + /// + /// Through `Snapshot.toJson` rather than around it: the version is added + /// there, and a document written from `run.data` carries the payload with no + /// header — which is what once made every refusal of a newer save + /// unreachable. + static String encode(SaveRecord record) => + const JsonEncoder.withIndent(' ').convert(record.toJson()); + /// Forgets the run. Called when it ends, either way. /// /// A finished run left behind is resumed on the next launch, and the player is /// put back on the last checkpoint of a level they already beat. - void clear() => storage.remove(_name); + void clear() { + _written = null; + storage.remove(name); + } } diff --git a/packages/flutter3d_game/pubspec.yaml b/packages/flutter3d_game/pubspec.yaml index bf8be4a67..c3bfa1371 100644 --- a/packages/flutter3d_game/pubspec.yaml +++ b/packages/flutter3d_game/pubspec.yaml @@ -64,6 +64,12 @@ dependencies: vector_math: ^2.2.0 + # `HttpCloudSaves`, a save kept on a server. Not `dart:io`'s client: that + # one does not exist in a browser, and a web build is where a player is most + # likely to come back on another device. Already in the workspace for + # `flutter3d_lti`. + http: ^1.2.0 + dev_dependencies: flutter_test: sdk: flutter diff --git a/packages/flutter3d_game/test/autosave_test.dart b/packages/flutter3d_game/test/autosave_test.dart new file mode 100644 index 000000000..69a12a132 --- /dev/null +++ b/packages/flutter3d_game/test/autosave_test.dart @@ -0,0 +1,150 @@ +/// When a run is written without the player asking: on the way into a pause, +/// at a checkpoint, and when the application goes to the background. +/// +/// flutter test test/autosave_test.dart +library; + +import 'package:flutter/widgets.dart'; +import 'package:flutter3d_app/flutter3d_app.dart'; +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// Counts writes, which is what these tests are about. +final class _Storage implements Storage { + final Map documents = {}; + int writes = 0; + + @override + String? read(String name) => documents[name]; + + @override + bool write(String name, String contents) { + writes++; + documents[name] = contents; + return true; + } + + @override + void remove(String name) => documents.remove(name); +} + +final class _Level { + int coins = 0; + int steps = 0; +} + +final class _Game extends RunSession<_Level> { + _Game({required super.saves}) : super(firstLevel: 'one'); + + @override + Future<_Level> open(String asset) async => _Level(); + + @override + RunOutcome outcomeOf(_Level level) => RunOutcome.playing; + + @override + String? nextOf(_Level level) => null; + + @override + Snapshot snapshotOf(_Level level) => + Snapshot({'coins': level.coins}); + + @override + void restoreInto(_Level level, Snapshot snapshot) => + level.coins = snapshot.data.integer('coins'); + + @override + int stepOf(_Level level) => level.steps; +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late _Storage storage; + late SaveFile saves; + late _Game game; + late Autosave autosave; + + setUp(() async { + storage = _Storage(); + saves = SaveFile(appName: 't', storage: storage); + game = _Game(saves: saves); + await game.begin(); + autosave = Autosave(game); + }); + + test('a pause writes on the way in, and not on every paused frame', () { + // Mutation: write whenever `paused` is true. Every frame behind the menu + // is then a file write — sixty a second for as long as the menu is open. + expect(autosave.paused(false), isFalse); + expect(autosave.paused(true), isTrue); + expect(storage.writes, 1); + + game.level!.coins = 3; + autosave.paused(true); + autosave.paused(true); + expect(storage.writes, 1); + + autosave.paused(false); + expect(storage.writes, 1, reason: 'leaving a pause writes nothing'); + expect(autosave.paused(true), isTrue); + expect(storage.writes, 2); + expect(saves.read()!.run.data['coins'], 3); + }); + + test('the same run is not written twice', () { + // Mutation: drop the digest check in `SaveFile.writeRecord`. A player who + // opens and closes the menu rewrites the same save each time, which on a + // phone is flash wear for nothing. + autosave.checkpoint(); + autosave.paused(true); + autosave.paused(false); + autosave.paused(true); + + expect(storage.writes, 1); + }); + + test('the step is written beside the run', () { + // Mutation: drop `step: stepOf(...)` from `RunSession.save`. Every save is + // then step 0, and two different runs are always the player's call. + game.level!.steps = 4200; + autosave.checkpoint(); + + expect(saves.readRecord()!.step, 4200); + }); + + test('going to the background writes the run', () { + // On a phone there is no pause screen between the home button and the + // system ending the process. Mutation: write only on `paused`, which iOS + // does not always deliver before it kills a backgrounded game. + game.level!.coins = 7; + autosave.didChangeAppLifecycleState(AppLifecycleState.inactive); + expect(saves.read()!.run.data['coins'], 7); + + game.level!.coins = 8; + autosave.didChangeAppLifecycleState(AppLifecycleState.resumed); + expect(saves.read()!.run.data['coins'], 7, reason: 'coming back is not'); + }); + + test('watching the lifecycle hears the binding, and stops', () { + // Mutation: forget `removeObserver` in `dispose`. A game torn down keeps + // writing a run that is no longer there every time the window loses focus. + autosave.watchLifecycle(); + game.level!.coins = 5; + WidgetsBinding.instance.handleAppLifecycleStateChanged( + AppLifecycleState.inactive, + ); + expect(saves.read()!.run.data['coins'], 5); + + autosave.dispose(); + WidgetsBinding.instance.handleAppLifecycleStateChanged( + AppLifecycleState.resumed, + ); + game.level!.coins = 6; + WidgetsBinding.instance.handleAppLifecycleStateChanged( + AppLifecycleState.inactive, + ); + expect(saves.read()!.run.data['coins'], 5); + }); +} diff --git a/packages/flutter3d_game/test/cloud_save_stores_test.dart b/packages/flutter3d_game/test/cloud_save_stores_test.dart new file mode 100644 index 000000000..db1b01bb1 --- /dev/null +++ b/packages/flutter3d_game/test/cloud_save_stores_test.dart @@ -0,0 +1,184 @@ +/// The two ways a save leaves the device: a server over HTTP, and Play Games +/// or iCloud through the platform's side. Both answer; neither throws for a +/// reason the network gave. +/// +/// flutter test test/cloud_save_stores_test.dart +library; + +import 'package:flutter/services.dart'; +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; +import 'package:http/testing.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('HttpCloudSaves', () { + test('a slot is a resource under saves//', () async { + final asked = []; + final store = HttpCloudSaves( + base: Uri.parse('https://example.test/api/'), + game: 'platformer', + headers: () async => {'Authorization': 'Bearer t'}, + client: MockClient((request) async { + asked.add(request); + return http.Response('{}', 200, headers: {'etag': '"v1"'}); + }), + ); + + final fetched = await store.fetch('save.json'); + + expect( + asked.single.url.toString(), + 'https://example.test/api/saves/platformer/save.json', + ); + expect(asked.single.headers['Authorization'], 'Bearer t'); + expect(fetched, isA()); + expect((fetched as CloudDocument).version, '"v1"'); + }); + + test('nothing there is 404, and empty', () async { + final store = HttpCloudSaves( + base: Uri.parse('https://example.test'), + game: 'g', + client: MockClient((_) async => http.Response('', 404)), + ); + + expect(await store.fetch('save.json'), isA()); + }); + + test('a write is conditional on the version it replaces', () async { + // Mutation: send no precondition. Two devices syncing at once both + // write, and whichever lands second erases the other's run unseen. + final asked = []; + final store = HttpCloudSaves( + base: Uri.parse('https://example.test'), + game: 'g', + client: MockClient((request) async { + asked.add(request); + return request.headers['If-Match'] == '"v1"' || + request.headers['If-None-Match'] == '*' + ? http.Response('', 204, headers: {'etag': '"v2"'}) + : http.Response('', 412); + }), + ); + + final replaced = await store.put('s', '{}', replacing: '"v1"'); + final created = await store.put('s', '{}'); + final stale = await store.put('s', '{}', replacing: '"v0"'); + + expect((replaced as CloudStored).version, '"v2"'); + expect(created, isA()); + expect(asked[1].headers['If-None-Match'], '*'); + expect(asked[1].headers.containsKey('If-Match'), isFalse); + expect(stale, isA()); + }); + + test('a server that sends no ETag is refused, saying why', () async { + // Mutation: accept the document with an empty version. The next write + // then replaces whatever is there, which is the race the versions stop. + final store = HttpCloudSaves( + base: Uri.parse('https://example.test'), + game: 'g', + client: MockClient((_) async => http.Response('{}', 200)), + ); + + final fetched = await store.fetch('s'); + + expect(fetched, isA()); + expect((fetched as CloudUnavailable).reason, contains('ETag')); + }); + + test('a network that is down is an answer', () async { + // Mutation: let the client's exception out. A launch with no network + // then fails at the title card instead of saying the cloud is away. + final store = HttpCloudSaves( + base: Uri.parse('https://example.test'), + game: 'g', + client: MockClient((_) async => throw http.ClientException('down')), + ); + + expect(await store.fetch('s'), isA()); + expect(await store.put('s', '{}'), isA()); + }); + }); + + group('PlatformCloudSaves', () { + const channel = MethodChannel('flutter3d/cloud_saves'); + final messenger = + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; + + tearDown(() => messenger.setMockMethodCallHandler(channel, null)); + + test( + 'asks the platform with the provider, and reads its answers', + () async { + final calls = []; + messenger.setMockMethodCallHandler(channel, (call) async { + calls.add(call); + return switch (call.method) { + 'fetch' => {'document': '{}', 'version': 'r7'}, + _ => {'version': 'r8'}, + }; + }); + final store = PlatformCloudSaves(CloudProvider.playGames); + + final fetched = await store.fetch('save.json'); + final put = await store.put('save.json', '{}', replacing: 'r7'); + + expect((fetched as CloudDocument).version, 'r7'); + expect((put as CloudStored).version, 'r8'); + expect(calls.first.arguments, { + 'provider': 'playGames', + 'slot': 'save.json', + }); + expect((calls.last.arguments as Map)['replacing'], 'r7'); + expect(store.name, 'Play Games'); + }, + ); + + test('nothing kept, and a lost race, are their own answers', () async { + messenger.setMockMethodCallHandler( + channel, + (call) async => switch (call.method) { + 'fetch' => null, + _ => {'moved': true}, + }, + ); + final store = PlatformCloudSaves(CloudProvider.iCloud); + + expect(await store.fetch('s'), isA()); + expect(await store.put('s', '{}'), isA()); + }); + + test('a build without the platform side says so', () async { + // Mutation: let `MissingPluginException` out. A build that offers + // iCloud where it cannot reach it then crashes on the settings screen. + final store = PlatformCloudSaves(CloudProvider.iCloud); + + final fetched = await store.fetch('s'); + + expect(fetched, isA()); + expect( + (fetched as CloudUnavailable).reason, + 'iCloud is not available in this build', + ); + }); + + test('a refusal from the service names it', () async { + messenger.setMockMethodCallHandler( + channel, + (_) async => throw PlatformException( + code: 'SIGN_IN_REQUIRED', + message: 'not signed in to Play Games', + ), + ); + final store = PlatformCloudSaves(CloudProvider.playGames); + + final put = await store.put('s', '{}'); + + expect((put as CloudUnavailable).reason, contains('not signed in')); + }); + }); +} diff --git a/packages/flutter3d_game/test/save_file_test.dart b/packages/flutter3d_game/test/save_file_test.dart index b6efcbc6e..c98ed9a92 100644 --- a/packages/flutter3d_game/test/save_file_test.dart +++ b/packages/flutter3d_game/test/save_file_test.dart @@ -7,7 +7,8 @@ import 'dart:io'; import 'package:flutter3d_app/flutter3d_app.dart'; import 'package:flutter3d_game/flutter3d_game.dart'; -import 'package:flutter3d_sim/flutter3d_sim.dart' show Snapshot; +import 'package:flutter3d_sim/flutter3d_sim.dart' + show SaveMigration, SaveSchema, Snapshot; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -112,8 +113,8 @@ void main() { // shape did not change, so refusing it would cost a player a run to fix a // bug that was never theirs. // - // Mutation: drop the `containsKey('version')` arm in `read`. This fails, - // and every existing save becomes a fresh start. + // Mutation: drop the arm in `SaveRecord.read` that supplies a missing + // version. This fails, and every existing save becomes a fresh start. storage.write('save.json', '{"level": "a.json", "run": {"deaths": 3}}'); final read = saves.read(); @@ -122,6 +123,46 @@ void main() { expect(read!.run.data['deaths'], 3); }); + test('a save from an older schema is migrated on the way in', () { + // A save written before the game split health into hearts. Mutation: + // construct the reader without passing `schema` through to + // `SaveRecord.read`, and the hearts come back as nothing while the old + // field rides along unread. + storage.write( + 'save.json', + '{"level": "a.json", "run": {"version": 1, "health": 30}}', + ); + final reader = SaveFile( + appName: 'test', + storage: storage, + schema: SaveSchema([ + (run) => run..['hearts'] = (run.remove('health')! as num) ~/ 10, + ]), + ); + + final read = reader.readRecord(); + + expect(read!.run.data, {'hearts': 3}); + expect(read.schema, 1); + }); + + test('a save says which schema it was written at', () { + // Mutation: write without the schema. Every save then reads as version + // 0, and the first migration runs again on a run it already changed. + final writer = SaveFile( + appName: 'test', + storage: storage, + schema: SaveSchema([(run) => run]), + ); + writer.write('a.json', Snapshot({}), step: 12); + + final written = + jsonDecode(storage.read('save.json')!) as Map; + expect(written['schema'], 1); + expect(written['step'], 12); + expect(written['digest'], isA()); + }); + test('a finished run is cleared, and clearing twice is fine', () { // Mutation: never clear. The player beats the level, quits, comes back, and // is put on the last checkpoint of the level they already finished. diff --git a/packages/flutter3d_game/test/save_sync_test.dart b/packages/flutter3d_game/test/save_sync_test.dart new file mode 100644 index 000000000..c38409f14 --- /dev/null +++ b/packages/flutter3d_game/test/save_sync_test.dart @@ -0,0 +1,279 @@ +/// A save on this device and a copy in the cloud, made one — and nothing sent +/// until the player says it may be. +/// +/// flutter test test/save_sync_test.dart +library; + +import 'package:flutter3d_app/flutter3d_app.dart'; +import 'package:flutter3d_game/flutter3d_game.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; + +final class _Storage implements Storage { + final Map documents = {}; + + @override + String? read(String name) => documents[name]; + + @override + bool write(String name, String contents) { + documents[name] = contents; + return true; + } + + @override + void remove(String name) => documents.remove(name); +} + +/// A store that keeps one slot, versions it, and records every call. +final class _Store implements CloudSaveStore { + String? document; + int revision = 0; + final List calls = []; + + /// Another device's write, landed between this one's fetch and its put. + String? writtenMeanwhile; + + @override + String get name => 'the test cloud'; + + @override + Future fetch(String slot) async { + calls.add('fetch $slot'); + final kept = document; + return kept == null + ? const CloudEmpty() + : CloudDocument(kept, version: '$revision'); + } + + @override + Future put( + String slot, + String document, { + String? replacing, + }) async { + calls.add('put $slot'); + final meanwhile = writtenMeanwhile; + if (meanwhile != null) { + writtenMeanwhile = null; + this.document = meanwhile; + revision++; + } + final current = this.document == null ? null : '$revision'; + if (replacing != current) return const CloudMoved(); + this.document = document; + revision++; + return CloudStored(version: '$revision'); + } +} + +SaveRecord _run(int coins, {int step = 0}) => SaveRecord( + level: 'one', + run: Snapshot({'coins': coins}), + step: step, +); + +void main() { + late _Storage storage; + late SaveFile saves; + late _Store store; + late SaveSync sync; + + setUp(() { + storage = _Storage(); + saves = SaveFile(appName: 't', storage: storage, onIssue: (_) {}); + store = _Store(); + sync = SaveSync(saves: saves, store: store); + }); + + group('consent', () { + test('nothing is sent or asked for before the player agrees', () async { + // Mutation: check consent after the fetch, or not at all. The first + // tells a server who is playing before they said it may know; the + // second uploads their run. + saves.writeRecord(_run(5, step: 100)); + + final report = await sync.sync(); + + expect(report.outcome, SyncOutcome.notConsented); + expect(store.calls, isEmpty); + expect(store.document, isNull); + }); + + test('consent is off on a fresh install and off when unreadable', () { + // Mutation: treat a missing or broken consent document as yes. + expect(sync.consented, isFalse); + storage.write(SaveSync.stateName, '{"consented": "yes"'); + expect(sync.consented, isFalse); + storage.write(SaveSync.stateName, '{"consented": "yes"}'); + expect(sync.consented, isFalse, reason: 'only true is consent'); + }); + + test('consent is kept, and withdrawing forgets the base', () async { + expect(sync.consent(), isTrue); + expect(SaveSync(saves: saves, store: store).consented, isTrue); + + saves.writeRecord(_run(5)); + await sync.sync(); + expect(sync.base, isNotNull); + + // Mutation: keep the base on withdrawal. Turning cloud saves back on + // months later resolves against a run both sides have long left. + sync.withdraw(); + expect(sync.consented, isFalse); + expect(sync.base, isNull); + store.calls.clear(); + expect((await sync.sync()).outcome, SyncOutcome.notConsented); + expect(store.calls, isEmpty); + }); + + test('settling a choice also needs consent', () async { + sync.consent(); + saves.writeRecord(_run(1, step: 50)); + store.document = SaveFile.encode(_run(2, step: 50)); + final asked = await sync.sync(); + expect(asked.outcome, SyncOutcome.ask); + + sync.withdraw(); + store.calls.clear(); + final settled = await sync.settle(asked, keepLocal: true); + + expect(settled.outcome, SyncOutcome.notConsented); + expect(store.calls, isEmpty); + }); + }); + + group('with consent', () { + setUp(() => sync.consent()); + + test('a first sync sends the run there is', () async { + saves.writeRecord(_run(5, step: 100)); + + final report = await sync.sync(); + + expect(report.outcome, SyncOutcome.uploaded); + expect(saves.parse(store.document!)!.run.data['coins'], 5); + expect(sync.base, saves.readRecord()!.digest); + }); + + test('a new device takes the run from the cloud', () async { + store.document = SaveFile.encode(_run(9, step: 300)); + + final report = await sync.sync(); + + expect(report.outcome, SyncOutcome.downloaded); + expect(saves.read()!.run.data['coins'], 9); + expect(saves.readRecord()!.step, 300); + }); + + test('the further run wins when both moved', () async { + saves.writeRecord(_run(1, step: 100)); + store.document = SaveFile.encode(_run(2, step: 900)); + + expect((await sync.sync()).outcome, SyncOutcome.downloaded); + expect(saves.read()!.run.data['coins'], 2); + }); + + test('equal runs say so and send nothing', () async { + saves.writeRecord(_run(4, step: 10)); + store.document = SaveFile.encode(_run(4, step: 10)); + store.calls.clear(); + + expect((await sync.sync()).outcome, SyncOutcome.inSync); + expect(store.calls, ['fetch save.json']); + }); + + test('a step back taken on this device is sent, not undone', () async { + // Both sides agreed on the long run; then the player went back to an + // earlier checkpoint here. Mutation: drop the base from `resolveSaves`, + // and the further run in the cloud comes back over the player's choice. + saves.writeRecord(_run(8, step: 900)); + await sync.sync(); + saves.writeRecord(_run(3, step: 200)); + + expect((await sync.sync()).outcome, SyncOutcome.uploaded); + expect(saves.parse(store.document!)!.run.data['coins'], 3); + }); + + test('two runs equally far along wait for the player', () async { + saves.writeRecord(_run(1, step: 500)); + store.document = SaveFile.encode(_run(2, step: 500)); + + final asked = await sync.sync(); + expect(asked.outcome, SyncOutcome.ask); + expect(saves.read()!.run.data['coins'], 1, reason: 'nothing moved yet'); + + final kept = await sync.settle(asked, keepLocal: true); + expect(kept.outcome, SyncOutcome.uploaded); + expect(saves.parse(store.document!)!.run.data['coins'], 1); + }); + + test( + 'a write that lost a race fetches again rather than overwriting', + () async { + // Another device wrote between this one's fetch and its put. Mutation: + // put without `replacing`, and the other device's run is overwritten + // without ever being compared. + saves.writeRecord(_run(1, step: 100)); + store.writtenMeanwhile = SaveFile.encode(_run(7, step: 800)); + + final report = await sync.sync(); + + expect(report.outcome, SyncOutcome.downloaded); + expect(saves.read()!.run.data['coins'], 7); + expect(store.calls, [ + 'fetch save.json', + 'put save.json', + 'fetch save.json', + ]); + }, + ); + + test('a cloud save from a newer build is left alone', () async { + // Mutation: treat an unreadable remote as no remote. The local run is + // then uploaded over progress made on a newer build of the game. + saves.writeRecord(_run(1, step: 100)); + final newer = SaveFile( + appName: 't', + storage: _Storage(), + schema: SaveSchema([(run) => run]), + ); + store.document = SaveFile.encode( + SaveRecord( + level: 'one', + run: Snapshot({'coins': 2}), + schema: newer.schema.version, + ), + ); + final before = store.document; + + final report = await sync.sync(); + + expect(report.outcome, SyncOutcome.refused); + expect(report.message, contains('newer build')); + expect(store.document, before); + }); + + test('a store that is down is an answer, not a throw', () async { + final down = SaveSync(saves: saves, store: _Down()); + + final report = await down.sync(); + + expect(report.outcome, SyncOutcome.unavailable); + expect(report.message, 'no network'); + }); + }); +} + +final class _Down implements CloudSaveStore { + @override + String get name => 'down'; + + @override + Future fetch(String slot) async => + const CloudUnavailable('no network'); + + @override + Future put(String slot, String document, {String? replacing}) => + throw UnimplementedError(); +} diff --git a/packages/flutter3d_sim/CHANGELOG.md b/packages/flutter3d_sim/CHANGELOG.md index c2766ecb8..9fb626dcf 100644 --- a/packages/flutter3d_sim/CHANGELOG.md +++ b/packages/flutter3d_sim/CHANGELOG.md @@ -41,6 +41,16 @@ component goes. `EntityLayout.ecs` reads an `EcsWorld.save()`, `EntityLayout.rows` one row per entity. `RewindBuffer.oldestStep` is the left end of a scrubber. +- **A save says what version of the game wrote it, and an old one is + migrated.** `SaveSchema` is a game's list of migrations, and its version is + their count, so the version cannot move without one. `upgrade` brings an + older run up and refuses a newer one, saying it is newer, rather than + misreading it. `SaveRecord` is the save document: level, snapshot, schema, + step and a digest of the run. `SaveRecord.read` never throws. +- **`resolveSaves` decides between two copies of a save** by digest and step, + against the digest both last agreed on: the same run is in sync, a side + still at the base lost to the one that moved, otherwise the further run + wins, and two different runs equally far along go to the player. - **A number tuned while the game runs is on the tape.** `InputState.tune` sets a tunable for one step, `InputFrame.tunes` records it, playback diff --git a/packages/flutter3d_sim/lib/flutter3d_sim.dart b/packages/flutter3d_sim/lib/flutter3d_sim.dart index 07497c069..e64ce3313 100644 --- a/packages/flutter3d_sim/lib/flutter3d_sim.dart +++ b/packages/flutter3d_sim/lib/flutter3d_sim.dart @@ -107,6 +107,8 @@ export 'src/save/game_random.dart'; export 'src/save/replay.dart'; export 'src/save/rewind.dart'; export 'src/save/run_stats.dart'; +export 'src/save/save_record.dart'; +export 'src/save/save_schema.dart'; export 'src/save/scoring.dart'; export 'src/save/snapshot.dart'; export 'src/save/state_digest.dart'; diff --git a/packages/flutter3d_sim/lib/src/save/save_record.dart b/packages/flutter3d_sim/lib/src/save/save_record.dart new file mode 100644 index 000000000..df0e62eef --- /dev/null +++ b/packages/flutter3d_sim/lib/src/save/save_record.dart @@ -0,0 +1,202 @@ +/// A saved run as a document: which level, the snapshot, and the two numbers +/// that let two copies of it be compared. +/// +/// ## Why the step and the digest travel with it +/// +/// A save that lives in two places — a phone and a tablet, a machine and a +/// server — sooner or later differs between them, and the question is which +/// one the player wants. A wall clock cannot answer it: two devices disagree +/// about the time, and the newer file is not the further run when somebody +/// replayed an old checkpoint. Two things can. The **step** says how far the +/// run has got, in simulation steps across its levels; the **digest** says +/// whether two copies are the same run at all, without comparing them field +/// by field. Equal digests are one save in two places. Different digests are +/// two runs, and the further one is what a player who kept playing wants. +/// +/// The digest is [contentDigestHex] — the same bits-not-text hash a replay +/// checks itself with, so a phone and a browser agree on it. +library; + +import 'save_schema.dart'; +import 'snapshot.dart'; +import 'state_digest.dart' show contentDigestHex; + +final class SaveRecord { + SaveRecord({ + required this.level, + required this.run, + this.step = 0, + this.schema = 0, + }); + + /// The level the snapshot belongs to — see `SaveFile` for why the two are + /// never kept apart. + final String level; + + final Snapshot run; + + /// How far the run has got, in simulation steps across all its levels. + /// + /// Zero for a game that does not count them, which leaves a conflict + /// between two different runs to the player — see [resolveSaves]. + final int step; + + /// The game's [SaveSchema.version] the run's fields are at. + final int schema; + + /// Whether two records are the same run, as eight hexadecimal digits. + /// + /// Of the level and the run and nothing else: the step counts how the run + /// got here, and two copies of one save that disagree about it are still + /// one save. + late final String digest = contentDigestHex({ + 'level': level, + 'run': run.toJson(), + }); + + /// The document. The digest is written down for whoever stores it — a + /// server can show and compare it without understanding a snapshot — and + /// recomputed on the way back in rather than trusted. + Map toJson() => { + 'level': level, + 'schema': schema, + 'step': step, + 'digest': digest, + 'run': run.toJson(), + }; + + /// Reads a document written by [toJson], or by a build from before it + /// carried a schema, and brings the run up to [schema]. + /// + /// **Never throws.** Every way a document can fail to be a save is a + /// [SaveNotRead] that says which. + static SaveRead read(Object? json, SaveSchema schema) { + if (json is! Map) { + return const SaveNotRead('the document is not an object'); + } + final level = json['level']; + final run = json['run']; + if (level is! String || run is! Map) { + return const SaveNotRead('no level and run in it'); + } + // A run with no version in it was written by a build that had the version + // and did not send it — `SaveFile.write` once handed over `run.data`, + // which is the payload without the header `toJson` adds. The shape did not + // change, so it is a version 1 document that failed to say so, and + // refusing it would cost a player a run to fix a bug that was never + // theirs. + final envelope = run[Snapshot.versionKey]; + if (envelope is num && envelope > Snapshot.formatVersion) { + return SaveNotRead( + 'snapshot format version $envelope is newer than this build ' + 'understands (${Snapshot.formatVersion})', + newer: true, + ); + } + final Snapshot snapshot; + try { + snapshot = Snapshot.fromJson( + envelope == null + ? { + Snapshot.versionKey: Snapshot.formatVersion, + ...run, + } + : run, + ); + } on SnapshotFormatException catch (error) { + return SaveNotRead(error.message); + } + final written = switch (json['schema']) { + final num value => value.toInt(), + // A save from before games had a schema: the first one. + _ => 0, + }; + return switch (schema.upgrade(snapshot.data, written)) { + SaveUpgraded(:final run, :final from) => SaveFound( + SaveRecord( + level: level, + run: Snapshot(run), + step: switch (json['step']) { + final num value => value.toInt(), + _ => 0, + }, + schema: schema.version, + ), + upgradedFrom: from, + ), + SaveRefused(:final reason, :final newer) => SaveNotRead( + reason, + newer: newer, + ), + }; + } +} + +/// What [SaveRecord.read] made of a document. +sealed class SaveRead { + const SaveRead(); +} + +final class SaveFound extends SaveRead { + const SaveFound(this.record, {required this.upgradedFrom}); + + final SaveRecord record; + + /// The schema the document was written at. Less than `record.schema` when + /// it was migrated on the way in. + final int upgradedFrom; +} + +final class SaveNotRead extends SaveRead { + const SaveNotRead(this.reason, {this.newer = false}); + + final String reason; + + /// From a newer build: somebody's real progress, to be left alone rather + /// than overwritten. See [SaveRefused.newer]. + final bool newer; +} + +/// Which of two copies of a save to keep. +enum SaveResolution { + /// They are the same run, or there is neither. + inSync, + + /// The local copy is the one to keep, and to send. + keepLocal, + + /// The other copy is the one to keep, and to write here. + takeRemote, + + /// Two different runs equally far along. Only the player can say. + ask, +} + +/// Decides between [local] and [remote], given the digest both sides last +/// agreed on, [base], when there was one. +/// +/// **The base is what makes this more than "the further one wins".** When one +/// copy is still the base, only the other has moved since the two last met, +/// and it wins whatever its step: a player who went back to an earlier +/// checkpoint on the tablet meant to. Only when both moved — two devices +/// played apart — is the step asked, and a tie between two different runs +/// goes to the player, because either answer picked here throws away +/// somebody's evening. +SaveResolution resolveSaves({ + required SaveRecord? local, + required SaveRecord? remote, + String? base, +}) => switch ((local, remote)) { + (null, null) => SaveResolution.inSync, + (_?, null) => SaveResolution.keepLocal, + (null, _?) => SaveResolution.takeRemote, + (final here?, final there?) when here.digest == there.digest => + SaveResolution.inSync, + (final here?, _) when here.digest == base => SaveResolution.takeRemote, + (_, final there?) when there.digest == base => SaveResolution.keepLocal, + (final here?, final there?) when here.step > there.step => + SaveResolution.keepLocal, + (final here?, final there?) when here.step < there.step => + SaveResolution.takeRemote, + _ => SaveResolution.ask, +}; diff --git a/packages/flutter3d_sim/lib/src/save/save_schema.dart b/packages/flutter3d_sim/lib/src/save/save_schema.dart new file mode 100644 index 000000000..bd4cd84b6 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/save/save_schema.dart @@ -0,0 +1,90 @@ +/// What a game's own save data looks like, by version, and how an old one +/// becomes a new one. +/// +/// ## Two versions, and why +/// +/// [Snapshot.formatVersion] is the engine's: it changes when the envelope does, +/// and no game can move it. A game's fields change far more often — a health +/// bar becomes a list of hearts, a key ring gains a colour — and until this +/// existed the only answer was the one `Snapshot` gives a newer document: +/// refuse it. An *older* document was read as if it were current, so a renamed +/// field came back as its default and the player lost what it held without a +/// word. +/// +/// ## The version is the number of migrations +/// +/// **There is no separate number to bump.** A schema with three migrations is +/// version 3, and the only way to make it 4 is to write the fourth. A number +/// kept beside the list could be raised without a migration, and the save from +/// the version in between would then be read as current — the failure this +/// exists to end. A change that needs no rewriting is a migration that returns +/// its argument, written down so the history is complete. +library; + +import 'snapshot.dart' show Snapshot; + +/// Turns a game's save data at one version into the next. +/// +/// Given the run's fields — a copy, so a migration may change it in place and +/// return it — and returns the fields at the next version. +typedef SaveMigration = Map Function(Map run); + +/// A game's save schema: the migrations from version 0 to now. +final class SaveSchema { + const SaveSchema([this.migrations = const []]); + + /// `migrations[n]` takes a version-`n` run to version `n + 1`. + final List migrations; + + /// The version this build writes. + int get version => migrations.length; + + /// Brings [run], written at version [from], up to [version]. + /// + /// A refusal rather than a throw, in both cases that can go wrong: a save + /// from a newer build cannot be read here and must not be overwritten + /// either, and a migration that throws on somebody's real save is a bug + /// whose report should name the step it failed at. + SaveUpgrade upgrade(Map run, int from) { + if (from > version) { + return SaveRefused( + 'save schema $from is newer than this build understands ($version)', + newer: true, + ); + } + if (from < 0) return SaveRefused('save schema $from is not a version'); + try { + final upgraded = migrations.skip(from).fold({ + ...run, + }, (fields, step) => step({...fields})); + return SaveUpgraded(upgraded, from: from); + } catch (error) { + return SaveRefused('a migration from save schema $from failed: $error'); + } + } +} + +/// What [SaveSchema.upgrade] made of a run. +sealed class SaveUpgrade { + const SaveUpgrade(); +} + +/// The run at the current version, and the version it was written at. +final class SaveUpgraded extends SaveUpgrade { + const SaveUpgraded(this.run, {required this.from}); + + final Map run; + final int from; +} + +/// Why the run cannot be read by this build. +final class SaveRefused extends SaveUpgrade { + const SaveRefused(this.reason, {this.newer = false}); + + /// A sentence, in the words a log line or a bug report wants. + final String reason; + + /// Whether the document is from a newer build — and so is somebody's real + /// progress that this build must leave alone rather than replace. + final bool newer; +} diff --git a/packages/flutter3d_sim/test/save_record_test.dart b/packages/flutter3d_sim/test/save_record_test.dart new file mode 100644 index 000000000..4d718d281 --- /dev/null +++ b/packages/flutter3d_sim/test/save_record_test.dart @@ -0,0 +1,219 @@ +/// A save read by a later build of the game, and two copies of one save that +/// have to become one. Migrations, schema refusals and the conflict rules are +/// what these are about. +library; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; + +SaveRecord _record(Map run, {int step = 0, String? level}) => + SaveRecord(level: level ?? 'a.json', run: Snapshot(run), step: step); + +void main() { + group('SaveSchema', () { + // Version 0 kept health as a number; version 1 split it into hearts; + // version 2 renamed `keys` to `keyRing`. + final schema = SaveSchema([ + (run) => run..['hearts'] = ((run.remove('health') as num?) ?? 0) ~/ 10, + (run) => run..['keyRing'] = run.remove('keys'), + ]); + + test('the version is the number of migrations', () { + // Mutation: a version kept beside the list. It can be raised without a + // migration, and the save from the version between is read as current. + expect(schema.version, 2); + expect(const SaveSchema().version, 0); + }); + + test('an old run is brought up through every migration in order', () { + // Mutation: apply only the last migration, or start from 0 whatever the + // run says. The first loses the hearts, the second divides them again. + final upgraded = schema.upgrade({ + 'health': 30, + 'keys': ['red'], + }, 0); + + expect(upgraded, isA()); + final run = (upgraded as SaveUpgraded).run; + expect(run, { + 'hearts': 3, + 'keyRing': ['red'], + }); + + final fromOne = schema.upgrade({ + 'hearts': 2, + 'keys': [], + }, 1); + expect((fromOne as SaveUpgraded).run['hearts'], 2); + }); + + test('a run from a newer build is refused, and said to be newer', () { + // Mutation: read it as current. Its fields mean something this build + // has never heard of, and the next save overwrites the player's + // progress with a misreading of it. + final refused = schema.upgrade({}, 3); + + expect(refused, isA()); + expect((refused as SaveRefused).newer, isTrue); + expect(refused.reason, contains('newer')); + }); + + test('a migration that throws is a refusal naming where it failed', () { + // Mutation: let the throw out. `SaveFile.read` promises never to throw, + // and a launch that crashes on somebody's save is the worst version of + // "the save could not be read". + final broken = SaveSchema([ + (run) => throw StateError('no such field'), + ]); + + final refused = broken.upgrade({}, 0); + expect(refused, isA()); + expect((refused as SaveRefused).newer, isFalse); + expect(refused.reason, contains('from save schema 0')); + }); + + test('the run handed in is left as it was', () { + // Mutation: hand the caller's map to the first migration. A failed + // upgrade then leaves the save it was reading half rewritten. + final run = {'health': 30}; + schema.upgrade(run, 0); + + expect(run, {'health': 30}); + }); + }); + + group('SaveRecord', () { + test('what is written is read back, schema, step and all', () { + final written = SaveRecord( + level: 'b.json', + run: Snapshot({'deaths': 2}), + step: 1200, + schema: 1, + ); + + final read = SaveRecord.read( + written.toJson(), + SaveSchema([(run) => run]), + ); + + expect(read, isA()); + final record = (read as SaveFound).record; + expect(record.level, 'b.json'); + expect(record.step, 1200); + expect(record.run.data, {'deaths': 2}); + expect(record.digest, written.digest); + expect(read.upgradedFrom, 1); + }); + + test('a save from before schemas is version 0 and migrated', () { + // The shape every save on disk had before this: a level and a run, no + // schema, no step. Mutation: treat a missing schema as current, and the + // migration a game wrote for its first change never runs on the saves + // it was written for. + final read = SaveRecord.read( + { + 'level': 'a.json', + 'run': {'version': 1, 'health': 20}, + }, + SaveSchema([ + (run) => run..['hearts'] = (run.remove('health')! as num) ~/ 10, + ]), + ); + + final found = read as SaveFound; + expect(found.upgradedFrom, 0); + expect(found.record.schema, 1); + expect(found.record.run.data, {'hearts': 2}); + }); + + test('a snapshot from a newer engine is refused as newer', () { + final read = SaveRecord.read({ + 'level': 'a.json', + 'run': {'version': Snapshot.formatVersion + 1}, + }, const SaveSchema()); + + expect(read, isA()); + expect((read as SaveNotRead).newer, isTrue); + }); + + test('the digest is of the run, not of how far it got', () { + // Mutation: hash the step too. Two copies of one save that counted + // their steps differently — an older build counted none — would then be + // a conflict for the player to settle, about nothing. + final a = _record({'x': 1}, step: 10); + final b = _record({'x': 1}, step: 99); + final c = _record({'x': 2}, step: 10); + + expect(a.digest, b.digest); + expect(a.digest, isNot(c.digest)); + expect( + _record({'x': 1}, level: 'b.json').digest, + isNot(a.digest), + ); + }); + }); + + group('resolveSaves', () { + final early = _record({'at': 1}, step: 100); + final late = _record({'at': 2}, step: 900); + final sameStep = _record({'at': 3}, step: 900); + + test('one copy, or the same copy twice, is no conflict', () { + expect(resolveSaves(local: null, remote: null), SaveResolution.inSync); + expect( + resolveSaves(local: early, remote: null), + SaveResolution.keepLocal, + ); + expect( + resolveSaves(local: null, remote: early), + SaveResolution.takeRemote, + ); + expect( + resolveSaves( + local: early, + remote: _record({'at': 1}, step: 5), + ), + SaveResolution.inSync, + ); + }); + + test('with no base, the further run wins', () { + // Mutation: compare in the wrong direction, or by digest order. The + // player who played on the tablet comes back to the phone's older run. + expect( + resolveSaves(local: late, remote: early), + SaveResolution.keepLocal, + ); + expect( + resolveSaves(local: early, remote: late), + SaveResolution.takeRemote, + ); + }); + + test('when one side is still the base, the other moved and wins', () { + // A player went back to an earlier checkpoint on the tablet. The phone + // still holds the run both last agreed on, so the tablet's choice is + // the newer one even though it is fewer steps in. + // + // Mutation: ask the step first. The player's deliberate step back is + // undone the next time the phone syncs. + expect( + resolveSaves(local: late, remote: early, base: late.digest), + SaveResolution.takeRemote, + ); + expect( + resolveSaves(local: early, remote: late, base: late.digest), + SaveResolution.keepLocal, + ); + }); + + test('two different runs equally far along go to the player', () { + // Mutation: pick one. Either answer throws away a run somebody played. + expect(resolveSaves(local: late, remote: sameStep), SaveResolution.ask); + expect( + resolveSaves(local: late, remote: sameStep, base: early.digest), + SaveResolution.ask, + ); + }); + }); +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 956e0be6c..e155e057b 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11061 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11103 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 9e604db10..2fa68ddfb 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,17 +1,17 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11061 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11103 tests need a GPU. --- # Testing -11061 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11103 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| | `flutter3d` | 1637 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | -| `flutter3d_sim` | 632 | | `pad_input` | 67 | +| `flutter3d_sim` | 645 | | `pad_input` | 67 | | `flutter3d_lab` | 15 | | `flutter3d_core` | 750 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | @@ -28,7 +28,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 38 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | | `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 143 | -| `flutter3d_game` | 296 | | `flutter3d_shaders` | 5 | +| `flutter3d_game` | 325 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | | `flutter3d_editor_play` | 26 | | `flutter3d_net` | 15 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11035 rather than 11061: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11077 rather than 11103: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 3024f0e94..f4c38b92c 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -645,6 +645,31 @@ navmesh can route there, and then `goTo` is replaced in `BehaviourKinds`; no `parallel` node (one body steers one way); trees authored in the editor and over MCP; the overlay's names shown in a demo rather than only returned by `describe()`; the digest trace measured on a second operating system. +**N6, saves.** *Done:* `SaveSchema` in `flutter3d_sim` is a game's list of +migrations, and its version is their count, so the version cannot move +without one; an older run is brought up on the way in and a newer one is +refused as newer rather than misread. The document is a `SaveRecord`: +level, snapshot, schema, step (`RunSession.stepOf`, the run's steps across +its levels) and a digest of the level and run. `resolveSaves` settles two +copies by digest and step against the digest both last agreed on, and a tie +between two different runs goes to the player. `Autosave` in +`flutter3d_game` writes on the way into a pause, at a checkpoint and when +the application goes to the background; `SaveFile` skips writing the run it +last wrote. `SaveSync` sends nothing until the player has agreed (kept in +`cloud_saves.json`, off on a fresh install, and checked before any store is +called), settles by `resolveSaves`, retries once when another device wrote +in between, and leaves a cloud save from a newer build alone. Stores: +`HttpCloudSaves` (`GET`/`PUT` on `saves//` with `ETag` +preconditions) and `PlatformCloudSaves` for Play Games and iCloud over the +`flutter3d/cloud_saves` channel. The platformer autosaves on pause, at +checkpoints and in the background, and counts its steps from its elapsed +time. +*Left:* the native sides of `flutter3d/cloud_saves` (Play Games Services +snapshots in Kotlin, CloudKit or the iCloud key-value store in Swift) and a +plugin package to carry them; the `saves/` routes in the reference server +in `cloud/`, with accounts (decision 23); a consent switch and a "which run +do you want" dialog in the settings panel, and the platformer calling +`SaveSync` before `begin`; the dungeon and racing on `Autosave`. ## 6. Platforms (decision 16) diff --git a/tool/structure/repository.dart b/tool/structure/repository.dart index 36ca91178..c8f91fc11 100644 --- a/tool/structure/repository.dart +++ b/tool/structure/repository.dart @@ -988,6 +988,12 @@ boundaryEnumExempt = >{ 'playing, won, lost. The vocabulary all three games and every ' 'screen are built on, and the same set RunStatus is sealed around', }, + 'flutter3d_sim/lib/src/save/save_record.dart': { + 'SaveResolution': + 'what can come of comparing two copies of one thing: they are the ' + 'same, keep this one, keep that one, or nobody but the player can ' + 'say. A fifth would not be an answer this comparison is missing', + }, 'flutter3d_editor_core/lib/src/gizmos.dart': { 'Piece': 'the three things a level document is made of, seen from an editor: ' From 180ed44107099c0442098f4190644a5b54f5a534 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:24:57 +0300 Subject: [PATCH 044/141] A player's run can be sent with consent and drawn as a heatmap TelemetryUpload sends a run's tape only after TelemetryConsent says yes. The reference server in cloud/ plays the tape again through the same simulation, refuses a tape that parts from the run it claims, keeps the trail and bins it; a run's key erases it. Heatmap turns trails into cells, and the editor's playtest report fetches and draws them. --- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_editor/lib/main.dart | 8 +- .../lib/src/playtest_report_screen.dart | 130 +++++- .../lib/src/telemetry_fetch.dart | 71 +++ .../test/playtest_report_screen_test.dart | 88 ++++ cloud/README.md | 19 + cloud/server/lib/main.server.dart | 16 + cloud/server/lib/src/config.dart | 7 + cloud/server/lib/src/db/migrations.g.dart | 25 + .../src/db/migrations/004_telemetry_runs.sql | 23 + .../lib/src/db/telemetry_repository.dart | 86 ++++ cloud/server/lib/src/http/app.dart | 4 + .../server/lib/src/http/telemetry_routes.dart | 55 +++ cloud/server/lib/src/services.dart | 19 + .../lib/src/telemetry/telemetry_service.dart | 266 +++++++++++ .../lib/src/telemetry/telemetry_store.dart | 96 ++++ cloud/server/pubspec.lock | 19 +- cloud/server/pubspec.yaml | 12 + cloud/server/test/telemetry_db_test.dart | 74 +++ cloud/server/test/telemetry_routes_test.dart | 344 ++++++++++++++ packages/flutter3d_sim/CHANGELOG.md | 17 + packages/flutter3d_sim/lib/flutter3d_sim.dart | 4 + .../lib/src/telemetry/heatmap.dart | 223 +++++++++ .../lib/src/telemetry/resimulation.dart | 138 ++++++ .../lib/src/telemetry/telemetry_consent.dart | 71 +++ .../lib/src/telemetry/telemetry_upload.dart | 269 +++++++++++ .../flutter3d_sim/test/telemetry_test.dart | 429 ++++++++++++++++++ packages/flutter3d_sim_mcp/CHANGELOG.md | 4 + .../flutter3d_sim_mcp/lib/src/playtest.dart | 56 +-- .../lib/src/sim_session.dart | 72 ++- site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- tasks/0.9-engine-roadmap.md | 32 ++ 34 files changed, 2582 insertions(+), 111 deletions(-) create mode 100644 apps/flutter3d_editor/lib/src/telemetry_fetch.dart create mode 100644 cloud/server/lib/src/db/migrations/004_telemetry_runs.sql create mode 100644 cloud/server/lib/src/db/telemetry_repository.dart create mode 100644 cloud/server/lib/src/http/telemetry_routes.dart create mode 100644 cloud/server/lib/src/telemetry/telemetry_service.dart create mode 100644 cloud/server/lib/src/telemetry/telemetry_store.dart create mode 100644 cloud/server/test/telemetry_db_test.dart create mode 100644 cloud/server/test/telemetry_routes_test.dart create mode 100644 packages/flutter3d_sim/lib/src/telemetry/heatmap.dart create mode 100644 packages/flutter3d_sim/lib/src/telemetry/resimulation.dart create mode 100644 packages/flutter3d_sim/lib/src/telemetry/telemetry_consent.dart create mode 100644 packages/flutter3d_sim/lib/src/telemetry/telemetry_upload.dart create mode 100644 packages/flutter3d_sim/test/telemetry_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 3cddff6d2..cc22533db 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11103 tests** across 43 packages and 10 applications | +| Unit tests | **11118 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 594596677..946bb6e01 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11103 tests across forty-three packages and nine applications. The +There are 11118 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_editor/lib/main.dart b/apps/flutter3d_editor/lib/main.dart index bb21e463d..deebecb0d 100644 --- a/apps/flutter3d_editor/lib/main.dart +++ b/apps/flutter3d_editor/lib/main.dart @@ -1610,10 +1610,14 @@ class _EditorScreenState extends State /// Opens `ai-02`'s own screen — the report itself is opened from inside /// it, since that is where the file picker and the "no report open" state - /// already live. + /// already live. The open level's digest goes with it, for N7's heatmap of + /// players' runs of exactly this document. Future _openPlaytestReport() async { + final levelHash = _editing?.level.digestHex; await Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const PlaytestReportScreen()), + MaterialPageRoute( + builder: (_) => PlaytestReportScreen(levelHash: levelHash), + ), ); } diff --git a/apps/flutter3d_editor/lib/src/playtest_report_screen.dart b/apps/flutter3d_editor/lib/src/playtest_report_screen.dart index 8eb27cff1..63a842ee4 100644 --- a/apps/flutter3d_editor/lib/src/playtest_report_screen.dart +++ b/apps/flutter3d_editor/lib/src/playtest_report_screen.dart @@ -5,18 +5,33 @@ import 'package:flutter/material.dart'; import 'playtest_heatmap_view.dart'; import 'playtest_report.dart'; +import 'telemetry_fetch.dart'; /// `ai-02`'s screen: open a heatmap `ai-01` wrote, look at where a level /// swallows a policy that never learns it, and tap a death for the one /// number worth reading off it — which step, in which run. +/// +/// N7 draws real players here too: the same report, fetched from a telemetry +/// server that played their runs again. final class PlaytestReportScreen extends StatefulWidget { - const PlaytestReportScreen({super.key, this.initialReport}); + const PlaytestReportScreen({ + super.key, + this.initialReport, + this.levelHash, + this.get = getText, + }); /// A report already loaded, for a caller that opened the file itself — /// what every test here hands over, since a real open panel is not /// something a widget test can drive. final PlaytestReport? initialReport; + /// The open level's digest, offered when asking a telemetry server for its + /// heatmap. + final String? levelHash; + + final TextGet get; + @override State createState() => _PlaytestReportScreenState(); } @@ -25,12 +40,30 @@ final class _PlaytestReportScreenState extends State { PlaytestReport? _report; String? _said; + /// Whether [_report] is players' runs rather than a playtest's: a marker + /// is then a run id on the server, not a seed to play again. + bool _fromTelemetry = false; + + final TextEditingController _server = TextEditingController( + text: 'http://localhost:8794', + ); + late final TextEditingController _level = TextEditingController( + text: widget.levelHash ?? '', + ); + @override void initState() { super.initState(); _report = widget.initialReport; } + @override + void dispose() { + _server.dispose(); + _level.dispose(); + super.dispose(); + } + Future _open() async { const jsonFiles = XTypeGroup( label: 'playtest reports', @@ -45,6 +78,7 @@ final class _PlaytestReportScreenState extends State { jsonDecode(await file.readAsString()) as Map; setState(() { _report = PlaytestReport.fromJson(json); + _fromTelemetry = false; _said = null; }); } catch (error) { @@ -52,17 +86,77 @@ final class _PlaytestReportScreenState extends State { } } + Future _fetch() async { + final asked = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Telemetry heatmap'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + key: const ValueKey('telemetry-server'), + controller: _server, + decoration: const InputDecoration(labelText: 'Server'), + ), + TextField( + key: const ValueKey('telemetry-level'), + controller: _level, + decoration: const InputDecoration(labelText: 'Level digest'), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: const Text('Fetch'), + ), + ], + ), + ); + if (asked != true || !mounted) return; + final server = Uri.tryParse(_server.text.trim()); + if (server == null || !server.hasScheme) { + setState(() => _said = '"${_server.text}" is not a server address'); + return; + } + final fetched = await fetchTelemetryHeatmap( + server: server, + levelHash: _level.text.trim(), + get: widget.get, + ); + if (!mounted) return; + setState(() { + _said = fetched.says; + if (fetched.report case final report?) { + _report = report; + _fromTelemetry = true; + } + }); + } + void _onDeathTap(DeathPoint death) { + final where = + '(${death.x.toStringAsFixed(1)}, ${death.z.toStringAsFixed(1)})'; showDialog( context: context, builder: (context) => AlertDialog( - title: const Text('A death, from ai-01'), + title: Text( + _fromTelemetry ? 'A run lost, from telemetry' : 'A death, from ai-01', + ), content: Text( - 'run seed ${death.seed}, step ${death.step}, at ' - '(${death.x.toStringAsFixed(1)}, ${death.z.toStringAsFixed(1)}).\n\n' - 'Scrubbing to this step needs a running instance of the level\'s ' - 'own genre — the editor draws where it happened, not what led to ' - 'it.', + _fromTelemetry + ? 'run ${death.seed} on the server, step ${death.step}, at ' + '$where.\n\nThe server keeps where the run went, not ' + 'what was pressed, so it cannot be played here.' + : 'run seed ${death.seed}, step ${death.step}, at $where.\n\n' + 'Scrubbing to this step needs a running instance of the ' + 'level\'s own genre — the editor draws where it happened, ' + 'not what led to it.', ), actions: [ TextButton( @@ -77,10 +171,17 @@ final class _PlaytestReportScreenState extends State { @override Widget build(BuildContext context) { final report = _report; + final said = _said; + final error = TextStyle(color: Theme.of(context).colorScheme.error); return Scaffold( appBar: AppBar( title: const Text('Playtest report'), actions: [ + IconButton( + onPressed: _fetch, + icon: const Icon(Icons.cloud_download), + tooltip: 'Fetch players\' heatmap from a telemetry server', + ), IconButton( onPressed: _open, icon: const Icon(Icons.folder_open), @@ -94,14 +195,9 @@ final class _PlaytestReportScreenState extends State { mainAxisSize: MainAxisSize.min, children: [ const Text('No report open.'), - if (_said != null) ...[ + if (said != null) ...[ const SizedBox(height: 8.0), - Text( - _said!, - style: TextStyle( - color: Theme.of(context).colorScheme.error, - ), - ), + Text(said, style: error), ], ], ), @@ -111,9 +207,15 @@ final class _PlaytestReportScreenState extends State { Padding( padding: const EdgeInsets.all(8.0), child: Text( + '${_fromTelemetry ? 'Players: ' : ''}' '${report.totalRuns} runs — ${report.outcomes.entries.map((e) => '${e.key}: ${e.value}').join(', ')}', ), ), + if (said != null) + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text(said), + ), Expanded( child: PlaytestHeatmapView( report: report, diff --git a/apps/flutter3d_editor/lib/src/telemetry_fetch.dart b/apps/flutter3d_editor/lib/src/telemetry_fetch.dart new file mode 100644 index 000000000..04b553af4 --- /dev/null +++ b/apps/flutter3d_editor/lib/src/telemetry_fetch.dart @@ -0,0 +1,71 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'playtest_report.dart'; + +/// One GET: a status and a body. Handed in so a test needs no server. +typedef TextGet = Future<({int status, String body})> Function(Uri url); + +/// N7: the heatmap a telemetry server binned from players' runs of one level, +/// as the same [PlaytestReport] `ai-01`'s playtests are read into. +/// +/// **The level goes by its digest, not its path.** Runs are kept against the +/// document they were played in, so a level edited since has a heatmap of its +/// own — empty until somebody plays it — rather than one drawn from geometry +/// that is no longer there. +Future<({PlaytestReport? report, String says})> fetchTelemetryHeatmap({ + required Uri server, + required String levelHash, + double cellSize = 1.0, + TextGet get = getText, +}) async { + final url = server.resolve( + '/api/telemetry/heatmap?level=${Uri.encodeQueryComponent(levelHash)}' + '&cell=$cellSize', + ); + final ({int status, String body}) answer; + try { + answer = await get(url); + } catch (error) { + return (report: null, says: 'could not reach $server: $error'); + } + final Object? json; + try { + json = jsonDecode(answer.body); + } on FormatException { + return (report: null, says: '$url answered ${answer.status}, not JSON'); + } + if (json is! Map) { + return (report: null, says: '$url answered ${answer.status}, not a map'); + } + final says = json['says'] is String + ? json['says']! as String + : 'answered ${answer.status}'; + if (answer.status != 200) return (report: null, says: says); + try { + final report = PlaytestReport.fromJson(json); + return ( + report: report, + says: report.totalRuns == 0 + ? 'nobody has sent a run of this level yet' + : says, + ); + } catch (error) { + return (report: null, says: '$url sent a heatmap this cannot read: $error'); + } +} + +/// [TextGet] over `dart:io`. +Future<({int status, String body})> getText(Uri url) async { + final client = HttpClient()..connectionTimeout = const Duration(seconds: 10); + try { + final request = await client.getUrl(url); + final response = await request.close(); + return ( + status: response.statusCode, + body: await response.transform(utf8.decoder).join(), + ); + } finally { + client.close(); + } +} diff --git a/apps/flutter3d_editor/test/playtest_report_screen_test.dart b/apps/flutter3d_editor/test/playtest_report_screen_test.dart index 5587532e4..5b5e5f780 100644 --- a/apps/flutter3d_editor/test/playtest_report_screen_test.dart +++ b/apps/flutter3d_editor/test/playtest_report_screen_test.dart @@ -48,4 +48,92 @@ void main() { expect(find.textContaining('seed 5'), findsOneWidget); expect(find.textContaining('step 120'), findsOneWidget); }); + + group('N7: players\' runs from a telemetry server', () { + // What `cloud/server`'s `/api/telemetry/heatmap` answers: the playtest + // report's keys, with a run id under `seed`. + const String heatmap = + '{"cellSize":1.0,"cells":[{"x":0,"z":0,"samples":6,"runs":3}],' + '"deaths":[{"seed":41,"step":300,"x":0.5,"z":0.5}],' + '"outcomes":{"won":2,"lost":1,"unfinished":0},' + '"level":"cafe0001","says":"3 runs of cafe0001"}'; + + Future fetch(WidgetTester tester) async { + await tester.tap(find.byIcon(Icons.cloud_download)); + await tester.pumpAndSettle(); + await tester.tap(find.text('Fetch')); + await tester.pumpAndSettle(); + } + + testWidgets('asks for the open level by its digest, and draws what the ' + 'server binned', (tester) async { + // Mutation: asking for the level by path, or not at all, draws + // somebody else's level. + Uri? asked; + await tester.pumpWidget( + MaterialApp( + home: PlaytestReportScreen( + levelHash: 'cafe0001', + get: (url) async { + asked = url; + return (status: 200, body: heatmap); + }, + ), + ), + ); + await fetch(tester); + + expect(asked!.path, '/api/telemetry/heatmap'); + expect(asked!.queryParameters['level'], 'cafe0001'); + expect(find.textContaining('Players: 3 runs'), findsOneWidget); + expect(find.byType(PlaytestHeatmapView), findsOneWidget); + }); + + testWidgets('a lost run is named as a run on the server, not a seed to ' + 'play again', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: PlaytestReportScreen( + levelHash: 'cafe0001', + get: (url) async => (status: 200, body: heatmap), + ), + ), + ); + await fetch(tester); + + final view = tester.widget( + find.byType(PlaytestHeatmapView), + ); + final origin = tester.getTopLeft(find.byType(PlaytestHeatmapView)); + final size = tester.getSize(find.byType(PlaytestHeatmapView)); + final layout = HeatmapLayout(report: view.report, size: size); + await tester.tapAt(origin + layout.toScreen(0.5, 0.5)); + await tester.pumpAndSettle(); + + expect(find.text('A run lost, from telemetry'), findsOneWidget); + expect(find.textContaining('run 41 on the server'), findsOneWidget); + }); + + testWidgets('a server that refuses is quoted, and nothing is drawn', ( + tester, + ) async { + // Mutation: drawing an empty report on a refusal looks like a level + // nobody struggled with. + await tester.pumpWidget( + MaterialApp( + home: PlaytestReportScreen( + levelHash: 'cafe0001', + get: (url) async => ( + status: 400, + body: '{"says":"name the level: ?level="}', + ), + ), + ), + ); + await fetch(tester); + + expect(find.text('No report open.'), findsOneWidget); + expect(find.text('name the level: ?level='), findsOneWidget); + }); + }); } diff --git a/cloud/README.md b/cloud/README.md index 06299be48..b357e8859 100644 --- a/cloud/README.md +++ b/cloud/README.md @@ -60,6 +60,7 @@ names all of them. | `MODELS_ASSETS_DIR` | Default `web/assets` | | `MODELS_VIEWER_DIR` | Optional; serves the viewer at `/app/` when set | | `MODELS_LEARN_DIR` | Default `content/learn/modeler`, which is right in a checkout and nowhere else. A directory with no case in it gives an empty tutorial and one line in the log; the start does not fail | +| `MODELS_TELEMETRY_LEVELS_DIR` | Optional; the levels telemetry runs are played again in. See [Telemetry](#telemetry) | ## Tests @@ -75,6 +76,24 @@ checks that the first one was signed out, and deletes the model and its file. After editing a migration, run `dart run tool/embed_migrations.dart`. With `--check` it fails when the generated file is stale. +## Telemetry + +The reference server for runs that players agreed to send (N7 in +`tasks/0.9-engine-roadmap.md`). Under `/api/telemetry/`: + +| | | +|---|---| +| `POST runs` | A `TelemetryUpload` from `flutter3d_sim`. Refused without the consent it was sent under. The run is played again through `resimulate`; one that diverges, or names a game or a level this server lacks, is refused and nothing is kept. An accepted run keeps its trail, outcome, length and the consent record, not its input, and answers `201 {run, eraseKey}` | +| `DELETE runs/?key=` | Deletes the run. Only the key's hash is stored | +| `GET heatmap?level=&cell=` | The newest 2000 runs of that level, binned. The JSON is the playtest report's, which the editor's report screen draws | + +`MODELS_TELEMETRY_LEVELS_DIR` names a directory of level documents, keyed by +their digest; a file that does not parse is named in the log and skipped. +The games are `HeadlessGame`s handed to `Services(telemetryGames:)` in code. +`main.server.dart` hands none, because every genre in the repository needs +Flutter and this process runs under `dart run`, so as shipped the endpoint +answers 503 and says why. + ## Deploying ```bash diff --git a/cloud/server/lib/main.server.dart b/cloud/server/lib/main.server.dart index 6bc8645fd..ff4b33840 100644 --- a/cloud/server/lib/main.server.dart +++ b/cloud/server/lib/main.server.dart @@ -9,6 +9,7 @@ library; import 'dart:async'; import 'dart:io'; +import 'package:flutter3d_sim/flutter3d_sim.dart' show Level; import 'package:jaspr/server.dart'; import 'package:shelf/shelf_io.dart' as shelf_io; @@ -20,6 +21,7 @@ import 'src/http/app.dart'; import 'src/mail/mailer.dart'; import 'src/services.dart'; import 'src/storage/blob_store.dart'; +import 'src/telemetry/telemetry_service.dart'; Future main(List arguments) async { Jaspr.initializeApp(options: defaultServerOptions); @@ -38,6 +40,16 @@ Future main(List arguments) async { stderr.writeln(problem); } + // N7. Not a reason to stop either: a broken level costs its own runs. + final ( + telemetryLevels, + levelProblems, + ) = switch (config.telemetryLevelsDirectory) { + final directory? => readTelemetryLevels(directory), + null => ({}, []), + }; + levelProblems.forEach(stderr.writeln); + final db = await Database.open(config.databaseUrl); final services = Services( config: config, @@ -47,6 +59,10 @@ Future main(List arguments) async { null => ConsoleMailer(), }, blobs: FileBlobStore(config.blobDirectory), + // No games: every genre in the repository needs Flutter, which a server + // started with `dart run` does not have. A deploy that plays one builds + // its own entry point and names it here. + telemetryLevels: telemetryLevels, ); Services.instance = services; diff --git a/cloud/server/lib/src/config.dart b/cloud/server/lib/src/config.dart index 34c0e9714..cd74e37a9 100644 --- a/cloud/server/lib/src/config.dart +++ b/cloud/server/lib/src/config.dart @@ -23,6 +23,7 @@ class Config { this.assetsDirectory = 'web/assets', this.viewerDirectory, this.learnDirectory = 'content/learn/modeler', + this.telemetryLevelsDirectory, }); /// Reads the configuration from the process environment. @@ -63,6 +64,7 @@ class Config { assetsDirectory: env['MODELS_ASSETS_DIR'] ?? 'web/assets', viewerDirectory: env['MODELS_VIEWER_DIR'], learnDirectory: env['MODELS_LEARN_DIR'] ?? 'content/learn/modeler', + telemetryLevelsDirectory: env['MODELS_TELEMETRY_LEVELS_DIR'], ); if (missing.isNotEmpty) throw ConfigError(missing); @@ -120,6 +122,11 @@ class Config { /// nothing. final String learnDirectory; + /// N7: the level documents telemetry runs are played again in, or null for + /// a server that takes no telemetry. Optional, because most deploys of the + /// models service have no game to play them with. + final String? telemetryLevelsDirectory; + /// Whether letters are actually sent. bool get sendsMail => resendApiKey != null; diff --git a/cloud/server/lib/src/db/migrations.g.dart b/cloud/server/lib/src/db/migrations.g.dart index 017b16d76..cf6ce224a 100644 --- a/cloud/server/lib/src/db/migrations.g.dart +++ b/cloud/server/lib/src/db/migrations.g.dart @@ -214,5 +214,30 @@ alter table models add column search tsvector ) stored; create index models_search on models using gin (search); +'''), + Migration(4, '004_telemetry_runs.sql', r''' +-- N7: runs players agreed to send, as what they did rather than what they +-- pressed. The input is replayed and dropped; see `telemetry_store.dart`. + +create table telemetry_runs ( + id bigserial primary key, + game text not null, + level_hash text not null, + level text not null, + outcome text not null + check (outcome in ('won', 'lost', 'unfinished')), + steps integer not null check (steps > 0), + -- `[[x, z], ...]`, sampled every few steps of the replay. + trail jsonb not null, + -- The hash, never the key: a leaked table erases nothing. + erase_key_sha256 text not null, + -- The record of consent the run was taken under. + policy text not null, + consented_at timestamptz not null, + received_at timestamptz not null default now() +); + +-- A heatmap reads one level's newest runs. +create index telemetry_runs_by_level on telemetry_runs (level_hash, id desc); '''), ]; diff --git a/cloud/server/lib/src/db/migrations/004_telemetry_runs.sql b/cloud/server/lib/src/db/migrations/004_telemetry_runs.sql new file mode 100644 index 000000000..cff6b4858 --- /dev/null +++ b/cloud/server/lib/src/db/migrations/004_telemetry_runs.sql @@ -0,0 +1,23 @@ +-- N7: runs players agreed to send, as what they did rather than what they +-- pressed. The input is replayed and dropped; see `telemetry_store.dart`. + +create table telemetry_runs ( + id bigserial primary key, + game text not null, + level_hash text not null, + level text not null, + outcome text not null + check (outcome in ('won', 'lost', 'unfinished')), + steps integer not null check (steps > 0), + -- `[[x, z], ...]`, sampled every few steps of the replay. + trail jsonb not null, + -- The hash, never the key: a leaked table erases nothing. + erase_key_sha256 text not null, + -- The record of consent the run was taken under. + policy text not null, + consented_at timestamptz not null, + received_at timestamptz not null default now() +); + +-- A heatmap reads one level's newest runs. +create index telemetry_runs_by_level on telemetry_runs (level_hash, id desc); diff --git a/cloud/server/lib/src/db/telemetry_repository.dart b/cloud/server/lib/src/db/telemetry_repository.dart new file mode 100644 index 000000000..147ded664 --- /dev/null +++ b/cloud/server/lib/src/db/telemetry_repository.dart @@ -0,0 +1,86 @@ +/// Telemetry runs, as rows — `TelemetryStore` over Postgres. +library; + +import 'dart:convert'; + +import 'package:flutter3d_sim/flutter3d_sim.dart' show HeatmapTrail; +import 'package:postgres/postgres.dart'; + +import '../telemetry/telemetry_store.dart'; +import 'database.dart'; + +class TelemetryRepository implements TelemetryStore { + const TelemetryRepository(this._db); + + final Database _db; + + @override + Future add(TelemetryRunRow row) => _db.run((session) async { + final inserted = await session.execute( + Sql.named(''' + insert into telemetry_runs + (game, level_hash, level, outcome, steps, trail, erase_key_sha256, + policy, consented_at) + values + (@game, @levelHash, @level, @outcome, @steps, @trail::jsonb, + @eraseKey, @policy, @consentedAt) + returning id + '''), + parameters: { + 'game': row.game, + 'levelHash': row.levelHash, + 'level': row.level, + 'outcome': row.outcome, + 'steps': row.steps, + 'trail': jsonEncode(>[ + for (final (x, z) in row.trail) [x, z], + ]), + 'eraseKey': row.eraseKeySha256, + 'policy': row.policy, + 'consentedAt': row.consentedAt.toUtc(), + }, + ); + return inserted.first[0]! as int; + }); + + @override + Future erase(int id, String eraseKeySha256) => _db.run((session) async { + final deleted = await session.execute( + Sql.named( + 'delete from telemetry_runs ' + 'where id = @id and erase_key_sha256 = @key', + ), + parameters: {'id': id, 'key': eraseKeySha256}, + ); + return deleted.affectedRows == 1; + }); + + @override + Future> trails( + String levelHash, { + required int limit, + }) => _db.run((session) async { + final rows = await session.execute( + Sql.named(''' + select id, steps, outcome, trail from telemetry_runs + where level_hash = @level + order by id desc + limit @limit + '''), + parameters: {'level': levelHash, 'limit': limit}, + ); + return [ + for (final row in rows) + HeatmapTrail( + run: row[0]! as int, + steps: row[1]! as int, + outcome: row[2]! as String, + positions: <(double, double)>[ + for (final List point in (row[3]! as List).cast()) + ((point[0]! as num).toDouble(), (point[1]! as num).toDouble()), + ], + endedBadly: row[2] == 'lost', + ), + ]; + }); +} diff --git a/cloud/server/lib/src/http/app.dart b/cloud/server/lib/src/http/app.dart index 57b30bfc2..ae763b5ec 100644 --- a/cloud/server/lib/src/http/app.dart +++ b/cloud/server/lib/src/http/app.dart @@ -38,6 +38,7 @@ import 'metrics.dart'; import 'render.dart'; import 'request.dart'; import 'static_files.dart'; +import 'telemetry_routes.dart'; /// The largest preview picture accepted, in bytes. /// @@ -60,6 +61,9 @@ Handler buildHandler(Services services) { // `gal-07`: the modeller's own gallery, behind one endpoint of ours, // so the keys the outside catalogues want stay on a machine we own. ..mount('/gallery/', galleryRoutes(services.gallery).call) + // N7: runs a player agreed to send, played again here; no session and no + // cookie, so nothing for a forged form to ride on. + ..mount('/api/telemetry/', telemetryRoutes(services.telemetry).call) // Nothing here checks who is asking. The endpoint is not linked from any // page and nginx keeps it off the public vhost (see // `cloud/monitoring/deploy/nginx-grafana.pleion.dev.conf` and diff --git a/cloud/server/lib/src/http/telemetry_routes.dart b/cloud/server/lib/src/http/telemetry_routes.dart new file mode 100644 index 000000000..05c8d35de --- /dev/null +++ b/cloud/server/lib/src/http/telemetry_routes.dart @@ -0,0 +1,55 @@ +/// N7's three endpoints, mounted under `/api/telemetry/`. +/// +/// **No account, on purpose.** A run arrives with the consent it was sent +/// under and leaves with the key that deletes it; tying it to a person would +/// make anonymous telemetry a thing this server promises and could break. +library; + +import 'dart:convert'; + +import 'package:shelf/shelf.dart'; +import 'package:shelf_router/shelf_router.dart'; + +import '../telemetry/telemetry_service.dart'; + +Response _json(TelemetryAnswer answer) => Response( + answer.status, + body: jsonEncode(answer.body), + headers: { + 'content-type': 'application/json; charset=utf-8', + 'cache-control': 'no-store', + }, +); + +Router telemetryRoutes(TelemetryService service) => Router() + ..post('/runs', (Request request) async { + // Read no further than the limit allows: a body that announces itself as + // too large is refused before it is held in memory. + final length = request.contentLength; + if (length != null && length > service.maxBodyBytes) { + return _json(( + status: 413, + body: { + 'says': 'a run is at most ${service.maxBodyBytes} bytes here', + }, + )); + } + return _json(await service.accept(await request.readAsString())); + }) + ..delete('/runs/', (Request request, String id) async { + final run = int.tryParse(id); + final key = request.url.queryParameters['key']; + if (run == null || key == null || key.isEmpty) { + return _json(( + status: 400, + body: { + 'says': 'erase with /api/telemetry/runs/?key=', + }, + )); + } + return _json(await service.erase(run, key)); + }) + ..get('/heatmap', (Request request) async { + final query = request.url.queryParameters; + return _json(await service.heatmap(query['level'], query['cell'])); + }); diff --git a/cloud/server/lib/src/services.dart b/cloud/server/lib/src/services.dart index e7cd78cc0..76b7732f6 100644 --- a/cloud/server/lib/src/services.dart +++ b/cloud/server/lib/src/services.dart @@ -1,6 +1,8 @@ /// Everything a request handler or a page reaches for, built once at start. library; +import 'package:flutter3d_sim/flutter3d_sim.dart' show HeadlessGame, Level; + import 'auth/accounts.dart'; import 'config.dart'; import 'db/database.dart'; @@ -10,11 +12,13 @@ import 'db/models_repository.dart'; import 'db/projects_repository.dart'; import 'db/rate_limit.dart'; import 'db/sessions_repository.dart'; +import 'db/telemetry_repository.dart'; import 'db/users_repository.dart'; import 'http/cookies.dart'; import 'http/gallery_catalogue.dart'; import 'mail/mailer.dart'; import 'storage/blob_store.dart'; +import 'telemetry/telemetry_service.dart'; class Services { Services({ @@ -23,7 +27,14 @@ class Services { required this.mailer, required this.blobs, GalleryCatalogue? gallery, + Map telemetryGames = const {}, + Map telemetryLevels = const {}, }) : gallery = gallery ?? const FixedCatalogue(), + telemetry = TelemetryService( + games: telemetryGames, + levels: telemetryLevels, + store: TelemetryRepository(db), + ), users = UsersRepository(db), sessions = SessionsRepository(db), tokens = EmailTokensRepository(db), @@ -65,6 +76,14 @@ class Services { final Mailer mailer; final BlobStore blobs; + /// N7: runs sent with a player's consent, played again and binned. + /// + /// **Empty unless a deploy hands it games and levels**, and an empty one + /// answers every run with why it took none. The games are [HeadlessGame]s + /// this process can step, so they are chosen in code where the server is + /// built; the levels come from `MODELS_TELEMETRY_LEVELS_DIR`. + final TelemetryService telemetry; + final UsersRepository users; final SessionsRepository sessions; final EmailTokensRepository tokens; diff --git a/cloud/server/lib/src/telemetry/telemetry_service.dart b/cloud/server/lib/src/telemetry/telemetry_service.dart new file mode 100644 index 000000000..097f4dbfd --- /dev/null +++ b/cloud/server/lib/src/telemetry/telemetry_service.dart @@ -0,0 +1,266 @@ +/// N7: runs players agreed to send, played again here, and the heatmap of +/// where they went. +library; + +import 'dart:convert'; +import 'dart:io'; +import 'dart:isolate'; +import 'dart:math'; + +import 'package:crypto/crypto.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; + +import 'telemetry_store.dart'; + +/// Every outcome a stored run can have, counted even at zero. +const List kTelemetryOutcomes = ['won', 'lost', 'unfinished']; + +/// An answer: the status, and a JSON body whose `says` is a sentence. +typedef TelemetryAnswer = ({int status, Map body}); + +TelemetryAnswer _say(int status, String says, [Map? more]) => + (status: status, body: {'says': says, ...?more}); + +/// What a replay in another isolate hands back: plain data, since a +/// [HeadlessRun] does not cross. +typedef _Replayed = ({ + String? refused, + String outcome, + int steps, + List<(double, double)> trail, +}); + +/// The levels this server can play runs in, by [Level.digestHex], read from +/// every `*.json` in [directory]. +/// +/// A file that is not a level is skipped and named in the second half of the +/// answer, so a deploy with one broken file still takes runs for the rest and +/// the log says which one. +(Map, List) readTelemetryLevels(String directory) { + final folder = Directory(directory); + if (!folder.existsSync()) { + return ({}, ['no directory at $directory']); + } + final files = + folder + .listSync() + .whereType() + .where((file) => file.path.endsWith('.json')) + .toList() + ..sort((a, b) => a.path.compareTo(b.path)); + final read = <(Level?, String)>[for (final file in files) _readLevel(file)]; + return ( + { + for (final level in read.map((entry) => entry.$1).nonNulls) + level.digestHex: level, + }, + [ + for (final (level, problem) in read) + if (level == null) problem, + ], + ); +} + +(Level?, String) _readLevel(File file) { + try { + final json = jsonDecode(file.readAsStringSync()); + return (Level.fromJson((json as Map).cast()), file.path); + } catch (error) { + return (null, '${file.path} is not a level: $error'); + } +} + +/// Takes runs, plays them again, keeps what they did, and bins it. +/// +/// **The server proves nothing about a game it does not have.** [games] are +/// the [HeadlessGame]s this process can step; a run of any other game is +/// refused by name rather than stored unverified. A genre whose package needs +/// Flutter cannot be one of them under `dart run`, so which games a deploy +/// plays is decided where it is built. +final class TelemetryService { + TelemetryService({ + required this.games, + required this.levels, + required this.store, + this.maxSteps = 60 * 60 * 60, + this.maxBodyBytes = 16 * 1024 * 1024, + this.sampleEvery = 10, + this.heatmapRuns = 2000, + this.isolated = true, + Random? random, + }) : _random = random ?? Random.secure(); + + final Map games; + final Map levels; + final TelemetryStore store; + + /// The longest run replayed: an hour at sixty steps a second. A replay is + /// this server's CPU, and a tape is cheap to make long. + final int maxSteps; + final int maxBodyBytes; + final int sampleEvery; + + /// How many of a level's newest runs a heatmap bins. + final int heatmapRuns; + + /// Whether a replay runs in an isolate of its own, so one long run does not + /// hold every other request. Off in tests that hand in a game no isolate + /// can be sent. + final bool isolated; + + final Random _random; + + /// Whether this server can take any run at all. + bool get takesRuns => games.isNotEmpty && levels.isNotEmpty; + + /// `POST /api/telemetry/runs`. + Future accept(String body) async { + if (!takesRuns) { + return _say( + 503, + 'this server takes no telemetry: it has ' + '${games.isEmpty ? 'no game' : 'no level'} to play runs in', + ); + } + if (body.length > maxBodyBytes) { + return _say(413, 'a run is at most $maxBodyBytes bytes here'); + } + final TelemetryUpload upload; + try { + final json = jsonDecode(body); + if (json is! Map) { + return _say(400, 'an upload is a JSON object'); + } + upload = TelemetryUpload.fromJson(json); + } on FormatException catch (error) { + return _say(400, 'not JSON: ${error.message}'); + } on TelemetryUploadFormatException catch (error) { + return _say(400, error.message); + } + + final demo = upload.demo; + final game = games[upload.game]; + if (game == null) { + return _say( + 422, + 'this server plays no ${upload.game}; it plays ' + '${games.keys.join(', ')}', + ); + } + final level = levels[demo.levelHash]; + if (level == null) { + return _say( + 422, + 'this server has no level with hash ${demo.levelHash} ' + '(${demo.level}), so the run cannot be played again here', + ); + } + if (demo.steps > maxSteps) { + return _say( + 413, + 'the run is ${demo.steps} steps and this server replays at most ' + '$maxSteps', + ); + } + + final args = (game: game, level: level, demo: demo, every: sampleEvery); + final replayed = isolated + ? await Isolate.run(() => _replay(args)) + : _replay(args); + if (replayed.refused case final refused?) { + return _say(422, '$refused — nothing was kept'); + } + + final key = _eraseKey(); + final id = await store.add( + TelemetryRunRow( + game: upload.game, + levelHash: demo.levelHash, + level: demo.level, + outcome: replayed.outcome, + steps: replayed.steps, + trail: replayed.trail, + eraseKeySha256: _sha256(key), + policy: upload.policy, + consentedAt: upload.consentedAt, + ), + ); + return _say( + 201, + 'played ${replayed.steps} steps again and kept run $id ' + '(${replayed.outcome}); the input itself was not kept', + {'run': id, 'eraseKey': key}, + ); + } + + /// `DELETE /api/telemetry/runs/` with the receipt's key. + Future erase(int id, String key) async => + await store.erase(id, _sha256(key)) + ? _say(200, 'run $id is deleted') + : _say(404, 'no run $id with that key'); + + /// `GET /api/telemetry/heatmap?level=&cell=`. + Future heatmap(String? levelHash, String? cell) async { + if (levelHash == null || levelHash.isEmpty) { + return _say(400, 'name the level: ?level='); + } + final cellSize = double.tryParse(cell ?? '1') ?? 0.0; + if (cellSize <= 0.0 || cellSize > 1000.0) { + return _say(400, 'a cell is between 0 and 1000 metres, not "$cell"'); + } + final trails = await store.trails(levelHash, limit: heatmapRuns); + final map = Heatmap.bin( + trails, + cellSize: cellSize, + outcomeNames: kTelemetryOutcomes, + ); + return ( + status: 200, + body: { + ...map.toJson(), + 'level': levelHash, + 'says': '${map.runs} runs of $levelHash', + }, + ); + } + + String _eraseKey() => [ + for (var i = 0; i < 24; i++) + _random.nextInt(256).toRadixString(16).padLeft(2, '0'), + ].join(); +} + +String _sha256(String key) => sha256.convert(utf8.encode(key)).toString(); + +_Replayed _replay( + ({HeadlessGame game, Level level, Demo demo, int every}) args, +) => switch (resimulate( + game: args.game, + level: args.level, + demo: args.demo, + sampleEvery: args.every, +)) { + ResimulationLevelChanged(:final found, :final recorded) => _refused( + 'the level is $found here and the run says $recorded', + ), + ResimulationStartDiffers() => _refused( + 'a fresh run of the level does not start where this one started', + ), + ResimulationDiverged(:final divergence, :final agreedUntil) => _refused( + 'the replay parts from the run at step ${divergence.step} (agreed until ' + '$agreedUntil), so it is not the run that was played', + ), + ResimulationRetraced(:final outcome, :final steps, :final trail) => ( + refused: null, + outcome: switch (outcome) { + RunOutcome.won => 'won', + RunOutcome.lost => 'lost', + RunOutcome.playing => 'unfinished', + }, + steps: steps, + trail: trail, + ), +}; + +_Replayed _refused(String why) => + (refused: why, outcome: '', steps: 0, trail: const <(double, double)>[]); diff --git a/cloud/server/lib/src/telemetry/telemetry_store.dart b/cloud/server/lib/src/telemetry/telemetry_store.dart new file mode 100644 index 000000000..850e3370e --- /dev/null +++ b/cloud/server/lib/src/telemetry/telemetry_store.dart @@ -0,0 +1,96 @@ +/// Where accepted telemetry runs are kept, as the service needs them. +/// +/// **Metrics, not input.** What a run did — its trail, how it ended, how long +/// it took — is kept; the tape it was worked out from is not. The tape is the +/// player's every keypress, and nothing the heatmap answers needs it once the +/// replay has run. A run the server re-simulates under new code later is a +/// run the player sends again, under consent that is still current. +library; + +import 'package:flutter3d_sim/flutter3d_sim.dart' show HeatmapTrail; + +/// One accepted run, as it is written. +final class TelemetryRunRow { + const TelemetryRunRow({ + required this.game, + required this.levelHash, + required this.level, + required this.outcome, + required this.steps, + required this.trail, + required this.eraseKeySha256, + required this.policy, + required this.consentedAt, + }); + + final String game; + final String levelHash; + + /// The level's path, as the run named it — for an operator reading rows, + /// not for finding the level, which goes by [levelHash]. + final String level; + + /// `won`, `lost` or `unfinished`. + final String outcome; + final int steps; + final List<(double, double)> trail; + + /// The erase key's SHA-256, never the key: a leaked table erases nothing. + final String eraseKeySha256; + + /// The wording the player agreed to, and when — the record of consent the + /// run was taken under. + final String policy; + final DateTime consentedAt; +} + +abstract interface class TelemetryStore { + /// Keeps [row] and answers its id. + Future add(TelemetryRunRow row); + + /// Deletes run [id] if [eraseKeySha256] is its key's hash. Whether it was + /// there and the key wrong, or not there at all, is one answer: false. + Future erase(int id, String eraseKeySha256); + + /// The newest [limit] runs of the level [levelHash], as trails. + Future> trails(String levelHash, {required int limit}); +} + +/// A store in memory: the tests', and a server's that keeps nothing past a +/// restart. +final class MemoryTelemetryStore implements TelemetryStore { + final Map rows = {}; + int _next = 1; + + @override + Future add(TelemetryRunRow row) async { + final id = _next++; + rows[id] = row; + return id; + } + + @override + Future erase(int id, String eraseKeySha256) async { + if (rows[id]?.eraseKeySha256 != eraseKeySha256) return false; + rows.remove(id); + return true; + } + + @override + Future> trails( + String levelHash, { + required int limit, + }) async => [ + for (final MapEntry(key: id, value: row) in rows.entries.toList().reversed) + if (row.levelHash == levelHash) trailOf(id, row), + ].take(limit).toList(); +} + +/// [row] as the heatmap bins it: a lost run marks where it was lost. +HeatmapTrail trailOf(int id, TelemetryRunRow row) => HeatmapTrail( + run: id, + steps: row.steps, + outcome: row.outcome, + positions: row.trail, + endedBadly: row.outcome == 'lost', +); diff --git a/cloud/server/pubspec.lock b/cloud/server/pubspec.lock index 6eddbab51..0497949d7 100644 --- a/cloud/server/pubspec.lock +++ b/cloud/server/pubspec.lock @@ -223,14 +223,14 @@ packages: path: "../../packages/flutter3d_core" relative: true source: path - version: "0.8.2" + version: "0.8.3+1" flutter3d_hardware: dependency: "direct overridden" description: path: "../../packages/flutter3d_hardware" relative: true source: path - version: "0.8.0" + version: "0.8.0+1" flutter3d_mesh: dependency: "direct overridden" description: @@ -251,21 +251,28 @@ packages: path: "../../packages/flutter3d_particles" relative: true source: path - version: "0.8.0" + version: "0.8.1+1" flutter3d_physics: dependency: "direct main" description: path: "../../packages/flutter3d_physics" relative: true source: path - version: "0.8.2" + version: "0.8.2+1" flutter3d_shaders: dependency: "direct overridden" description: path: "../../packages/flutter3d_shaders" relative: true source: path - version: "0.8.2" + version: "0.8.2+1" + flutter3d_sim: + dependency: "direct main" + description: + path: "../../packages/flutter3d_sim" + relative: true + source: path + version: "0.8.1+1" frontend_server_client: dependency: transitive description: @@ -683,7 +690,7 @@ packages: source: hosted version: "4.6.0" vector_math: - dependency: transitive + dependency: "direct dev" description: name: vector_math sha256: "92b9910f66ed1057fd4da7b040ae7c74cafacf885bdc81be496928d5049b032d" diff --git a/cloud/server/pubspec.yaml b/cloud/server/pubspec.yaml index 471063609..df49e2e26 100644 --- a/cloud/server/pubspec.yaml +++ b/cloud/server/pubspec.yaml @@ -52,6 +52,12 @@ dependencies: flutter3d_physics: path: ../../packages/flutter3d_physics + # N7: a run sent as telemetry is played again here, through the simulation + # the player ran — `resimulate`, `TelemetryUpload`, `Heatmap`. Plain Dart, + # which is why a server can hold it at all. + flutter3d_sim: + path: ../../packages/flutter3d_sim + # Argon2id for passwords, in Dart. The FFI binding to libargon2 stopped at # Dart 2, and a password hash is the one place where a dependency that has # stopped is not a trade worth making. @@ -72,6 +78,10 @@ dev_dependencies: lints: ^6.0.0 test: ^1.25.0 + # The positions of the walking body `telemetry_routes_test.dart` re-simulates + # — the simulation's own vector type, already in the lock through it. + vector_math: ^2.4.3 + # `tool/embed_migrations.dart` formats its own output before writing it, so # `--check`'s byte comparison and the repository's own `dart format` step # never disagree about the same file. @@ -111,6 +121,8 @@ dependency_overrides: path: ../../packages/flutter3d_physics flutter3d_shaders: path: ../../packages/flutter3d_shaders + flutter3d_sim: + path: ../../packages/flutter3d_sim jaspr: mode: server diff --git a/cloud/server/test/telemetry_db_test.dart b/cloud/server/test/telemetry_db_test.dart new file mode 100644 index 000000000..dc75c0f31 --- /dev/null +++ b/cloud/server/test/telemetry_db_test.dart @@ -0,0 +1,74 @@ +/// [TelemetryRepository] against a real database: the trail through `jsonb` +/// and back, and an erase that needs the key. +@Tags(['db']) +library; + +import 'dart:io'; + +import 'package:flutter3d_models/src/db/database.dart'; +import 'package:flutter3d_models/src/db/telemetry_repository.dart'; +import 'package:flutter3d_models/src/telemetry/telemetry_store.dart'; +import 'package:test/test.dart'; + +TelemetryRunRow _row({String level = 'abcd1234', String outcome = 'lost'}) => + TelemetryRunRow( + game: 'walk', + levelHash: level, + level: 'levels/strip.json', + outcome: outcome, + steps: 120, + trail: const <(double, double)>[(0.5, 0.0), (-2.5, 1.25)], + eraseKeySha256: 'hash-of-key', + policy: '2026-10', + consentedAt: DateTime.utc(2026, 10, 1), + ); + +void main() { + late Database db; + late TelemetryRepository runs; + + setUpAll(() async { + final url = + Platform.environment['MODELS_TEST_DATABASE_URL'] ?? + 'postgres://models:models@localhost:55432/models'; + db = await Database.open(url); + runs = TelemetryRepository(db); + }); + + tearDownAll(() => db.close()); + + setUp( + () => db.run( + (s) => s.execute('truncate telemetry_runs restart identity cascade'), + ), + ); + + test('a kept run comes back as the trail it was, newest first, and only ' + 'for its own level', () async { + // Mutation: writing the trail as `[x, z]` objects keyed differently, or + // reading it back in another order, moves every cell of the heatmap. + final first = await runs.add(_row()); + final second = await runs.add(_row(outcome: 'won')); + await runs.add(_row(level: 'other')); + + final trails = await runs.trails('abcd1234', limit: 10); + + expect(trails.map((t) => t.run), [second, first]); + expect(trails.last.positions, const <(double, double)>[ + (0.5, 0.0), + (-2.5, 1.25), + ]); + expect(trails.last.endedBadly, isTrue); + expect(trails.first.endedBadly, isFalse); + expect(await runs.trails('abcd1234', limit: 1), hasLength(1)); + }); + + test('a run is erased with its key and with nothing else', () async { + final id = await runs.add(_row()); + + expect(await runs.erase(id, 'wrong'), isFalse); + expect(await runs.trails('abcd1234', limit: 10), hasLength(1)); + expect(await runs.erase(id, 'hash-of-key'), isTrue); + expect(await runs.trails('abcd1234', limit: 10), isEmpty); + }); +} diff --git a/cloud/server/test/telemetry_routes_test.dart b/cloud/server/test/telemetry_routes_test.dart new file mode 100644 index 000000000..368491cba --- /dev/null +++ b/cloud/server/test/telemetry_routes_test.dart @@ -0,0 +1,344 @@ +/// N7: a run sent with consent is played again here, kept as what it did, and +/// binned into the heatmap the editor draws. +/// +/// dart test test/telemetry_routes_test.dart +library; + +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter3d_models/src/http/telemetry_routes.dart'; +import 'package:flutter3d_models/src/telemetry/telemetry_service.dart'; +import 'package:flutter3d_models/src/telemetry/telemetry_store.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:shelf/shelf.dart'; +import 'package:shelf_router/shelf_router.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +/// A body that walks as the stick says: past x = 4 it has won, below x = -2 +/// it has lost. +final class _Walk implements HeadlessRun { + _Walk(this.input); + + final InputState input; + final Vector3 _at = Vector3.zero(); + + @override + RunOutcome outcome = RunOutcome.playing; + + @override + void step(double dt) { + if (outcome.isOver) return; + _at + ..x += input.moveAxis.x * 3.0 * dt + ..z += input.moveAxis.y * 3.0 * dt; + if (_at.x > 4.0) outcome = RunOutcome.won; + if (_at.x < -2.0) outcome = RunOutcome.lost; + } + + @override + Snapshot save() => Snapshot({ + 'x': _at.x, + 'z': _at.z, + 'outcome': outcome.name, + }); + + @override + Vector3 get position => _at; + + @override + void eye(Vector3 out) => out.setFrom(_at); + + @override + void aim(Vector3 out) => out.setValues(1.0, 0.0, 0.0); + + @override + String get summary => 'at ${_at.x}'; + + @override + Map get reading => {'x': _at.x}; +} + +final class _WalkGame implements HeadlessGame { + const _WalkGame(); + + @override + String get name => 'walk'; + + @override + Map get buttons => const {}; + + @override + EntityRegistry registry() => EntityRegistry(const []); + + @override + HeadlessRun start(Level level, CollisionWorld world, InputState input) => + _Walk(input); +} + +Level _level() => Level( + name: 'strip', + materials: {'stone': LevelMaterial()}, + brushes: [ + Brush( + centre: Vector3(0.0, -0.5, 0.0), + size: Vector3(20.0, 1.0, 4.0), + material: 'stone', + ), + ], + entities: const [], +); + +Demo _record({required double stick, int steps = 120}) { + final level = _level(); + final input = InputState(); + final world = CollisionWorld(); + level.addTo(world); + final run = const _WalkGame().start(level, world, input); + final start = run.save(); + final recorder = InputTapeRecorder(seed: 1); + final trace = DigestTrace(every: 10); + for (var step = 1; step <= steps; step++) { + input.setStickAxis(stick, 0.0); + recorder.record(input); + input.beginStep(); + run.step(1.0 / 60.0); + trace.observe(step, run.save().toJson()); + input.endStep(); + } + return Demo( + level: 'levels/strip.json', + levelHash: level.digestHex, + start: start, + tape: recorder.tape, + buildStamp: 'test', + checkpoints: trace, + ); +} + +String _upload(Demo demo, {String game = 'walk'}) => jsonEncode( + TelemetryUpload.prepare( + game: game, + demo: demo, + consent: TelemetryConsent.granted( + policy: '2026-10', + at: DateTime.utc(2026, 10, 1), + ), + policy: '2026-10', + ).upload!.toJson(), +); + +typedef _Server = ({MemoryTelemetryStore store, Handler handler}); + +_Server _server({Map? games}) { + final store = MemoryTelemetryStore(); + final service = TelemetryService( + games: games ?? const {'walk': _WalkGame()}, + levels: {_level().digestHex: _level()}, + store: store, + sampleEvery: 20, + ); + return ( + store: store, + // Through the mount `app.dart` uses, so the prefix is tested too. + handler: (Router()..mount('/api/telemetry/', telemetryRoutes(service).call)) + .call, + ); +} + +Future<(int, Map)> _call( + _Server server, + String method, + String path, [ + String? body, +]) async { + final response = await server.handler( + Request( + method, + Uri.parse('http://localhost/api/telemetry$path'), + body: body, + ), + ); + return ( + response.statusCode, + jsonDecode(await response.readAsString()) as Map, + ); +} + +void main() { + test('a run with consent is played again, kept as its trail, binned, and ' + 'erased with its key', () async { + // Mutation: storing the client's claim instead of the replay's outcome + // would keep a run nobody re-simulated; erasing without the key would + // let anybody delete anybody's run. + final server = _server(); + + final (status, receipt) = await _call( + server, + 'POST', + '/runs', + _upload(_record(stick: -1.0)), + ); + expect(status, 201, reason: '${receipt['says']}'); + expect(receipt['says'], contains('the input itself was not kept')); + final run = receipt['run']! as int; + final key = receipt['eraseKey']! as String; + final kept = server.store.rows[run]!; + expect(kept.outcome, 'lost'); + expect(kept.trail.last.$1, lessThan(-2.0)); + expect(kept.eraseKeySha256, isNot(key)); + expect(kept.policy, '2026-10'); + + await _call(server, 'POST', '/runs', _upload(_record(stick: 1.0))); + final (_, map) = await _call( + server, + 'GET', + '/heatmap?level=${_level().digestHex}&cell=2', + ); + final heatmap = Heatmap.fromJson(map); + expect(heatmap.outcomes, { + 'won': 1, + 'lost': 1, + 'unfinished': 0, + }); + expect(heatmap.ends.single.run, run); + expect(heatmap.cellSize, 2.0); + + final (wrong, _) = await _call(server, 'DELETE', '/runs/$run?key=nope'); + expect(wrong, 404); + expect(server.store.rows, contains(run)); + final (erased, _) = await _call(server, 'DELETE', '/runs/$run?key=$key'); + expect(erased, 200); + expect(server.store.rows, isNot(contains(run))); + }); + + test('a run without consent is refused and nothing is kept', () async { + // Mutation: a server that reads uploads whether or not they were agreed + // to. + final server = _server(); + final json = + jsonDecode(_upload(_record(stick: 1.0))) as Map + ..remove('consent'); + + final (status, answer) = await _call( + server, + 'POST', + '/runs', + jsonEncode(json), + ); + + expect(status, 400); + expect(answer['says'], contains('no consent')); + expect(server.store.rows, isEmpty); + }); + + test( + 'a run of a game or a level this server lacks is refused by name', + () async { + final server = _server(); + + final (game, gameAnswer) = await _call( + server, + 'POST', + '/runs', + _upload(_record(stick: 1.0), game: 'kart'), + ); + expect(game, 422); + expect(gameAnswer['says'], 'this server plays no kart; it plays walk'); + + final demo = _record(stick: 1.0); + final elsewhere = Demo( + level: 'levels/other.json', + levelHash: '0badc0de', + start: demo.start, + tape: demo.tape, + buildStamp: demo.buildStamp, + checkpoints: demo.checkpoints, + ); + final (level, levelAnswer) = await _call( + server, + 'POST', + '/runs', + _upload(elsewhere), + ); + expect(level, 422); + expect(levelAnswer['says'], contains('no level with hash 0badc0de')); + expect(server.store.rows, isEmpty); + }, + ); + + test('a tape edited after it was recorded is refused at the step it parts ' + 'from the run', () async { + // Mutation: keeping a run whose replay diverged would put a trail + // nobody played into the heatmap. + final server = _server(); + final honest = _record(stick: 1.0); + final forged = Demo( + level: honest.level, + levelHash: honest.levelHash, + start: honest.start, + tape: InputTape( + seed: 1, + frames: [ + ...honest.tape.frames.take(30), + const InputFrame(stickX: -1.0), + ...honest.tape.frames.skip(31), + ], + ), + buildStamp: honest.buildStamp, + checkpoints: honest.checkpoints, + ); + + final (status, answer) = await _call( + server, + 'POST', + '/runs', + _upload(forged), + ); + + expect(status, 422); + expect(answer['says'], contains('parts from the run at step 40')); + expect(answer['says'], contains('nothing was kept')); + expect(server.store.rows, isEmpty); + }); + + test('a server with no game says so rather than taking runs', () async { + final server = _server(games: const {}); + final (status, answer) = await _call( + server, + 'POST', + '/runs', + _upload(_record(stick: 1.0)), + ); + expect(status, 503); + expect(answer['says'], contains('no game')); + }); + + test('a heatmap names its level and a cell it can draw', () async { + final server = _server(); + final (none, _) = await _call(server, 'GET', '/heatmap'); + expect(none, 400); + final (bad, answer) = await _call(server, 'GET', '/heatmap?level=a&cell=0'); + expect(bad, 400); + expect(answer['says'], contains('"0"')); + final (empty, map) = await _call(server, 'GET', '/heatmap?level=a'); + expect(empty, 200); + expect(Heatmap.fromJson(map).runs, 0); + }); + + test('levels are read from a directory by digest, and a broken file is ' + 'named rather than stopping the rest', () { + final folder = Directory.systemTemp.createTempSync('telemetry_levels'); + addTearDown(() => folder.deleteSync(recursive: true)); + File( + '${folder.path}/strip.json', + ).writeAsStringSync(jsonEncode(_level().toJson())); + File('${folder.path}/broken.json').writeAsStringSync('{'); + + final (levels, problems) = readTelemetryLevels(folder.path); + + expect(levels.keys, [_level().digestHex]); + expect(problems.single, contains('broken.json')); + }); +} diff --git a/packages/flutter3d_sim/CHANGELOG.md b/packages/flutter3d_sim/CHANGELOG.md index 9fb626dcf..e07f340fd 100644 --- a/packages/flutter3d_sim/CHANGELOG.md +++ b/packages/flutter3d_sim/CHANGELOG.md @@ -51,6 +51,23 @@ against the digest both last agreed on: the same run is in sync, a side still at the base lost to the one that moved, otherwise the further run wins, and two different runs equally far along go to the player. +- **A run leaves the machine only with the player's yes.** + `TelemetryConsent` keeps the answer with the wording it was given to and + when; a grant to an older wording does not count, and a damaged settings + file reads as not asked. `TelemetryUpload.prepare` is the only way to build + an upload and refuses without consent; it drops `Demo.recordedBy`, and the + consent travels with the run so a server can refuse one that has none. + `TelemetryUploader` checks consent on every send; `HttpTelemetrySink` + posts through a `JsonPost` the caller hands in, so this package still + imports no network. +- **`resimulate` plays a demo again and says what it did.** It checks the + level hash, the starting state and every checkpoint, and returns a sealed + `Resimulation`: the level changed, the start differs, the replay diverged + (with the step), or it retraced, with the run, its outcome and a trail of + positions. +- **`Heatmap`** bins trails into cells, counting samples and distinct runs, + and marks where runs were lost. Its JSON is the playtest report's, so the + editor reads both. - **A number tuned while the game runs is on the tape.** `InputState.tune` sets a tunable for one step, `InputFrame.tunes` records it, playback diff --git a/packages/flutter3d_sim/lib/flutter3d_sim.dart b/packages/flutter3d_sim/lib/flutter3d_sim.dart index e64ce3313..a83d66b72 100644 --- a/packages/flutter3d_sim/lib/flutter3d_sim.dart +++ b/packages/flutter3d_sim/lib/flutter3d_sim.dart @@ -115,6 +115,10 @@ export 'src/save/state_digest.dart'; export 'src/save/step_time_trace.dart'; export 'src/save/tally.dart'; export 'src/save/tape_bisect.dart'; +export 'src/telemetry/heatmap.dart'; +export 'src/telemetry/resimulation.dart'; +export 'src/telemetry/telemetry_consent.dart'; +export 'src/telemetry/telemetry_upload.dart'; export 'src/world/exit.dart'; export 'src/world/key_ring.dart'; export 'src/world/light_fixture.dart'; diff --git a/packages/flutter3d_sim/lib/src/telemetry/heatmap.dart b/packages/flutter3d_sim/lib/src/telemetry/heatmap.dart new file mode 100644 index 000000000..0b06be0f9 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/telemetry/heatmap.dart @@ -0,0 +1,223 @@ +/// Where many runs of one level went, binned into cells. +/// +/// **One shape for two sources.** `ai-01`'s playtest bins the trails of a +/// random policy, and N7's telemetry bins the trails of real players +/// re-simulated on a server; the editor draws both through the same view. The +/// shape was `Playtest.heatmap`'s map before it was a class here, and the +/// JSON is that map unchanged, so a report written by either reads in the +/// other's reader. +library; + +/// The path of one run across a level, as [Heatmap.bin] takes it. +final class HeatmapTrail { + const HeatmapTrail({ + required this.run, + required this.steps, + required this.outcome, + required this.positions, + this.endedBadly = false, + }); + + /// Which run this was, by the number its source gives runs: a playtest's + /// seed, a telemetry server's run id. + final int run; + + /// The step the run stopped at. + final int steps; + + /// How it ended, in the source's own word — counted under [Heatmap.outcomes]. + final String outcome; + + /// `(x, z)` samples, in order. + final List<(double, double)> positions; + + /// Whether the last of [positions] is a place worth marking: where the run + /// was lost. + final bool endedBadly; +} + +/// One cell of a [Heatmap]. +final class HeatmapCell { + const HeatmapCell({ + required this.x, + required this.z, + required this.samples, + required this.runs, + }); + + /// In cells, not metres: multiply by [Heatmap.cellSize]. + final int x; + final int z; + + /// How many positions landed here. + final int samples; + + /// How many distinct runs those positions came from. A run that stood in + /// one corner for a minute is many samples and one run, and the two numbers + /// answer different questions: where players linger, and where they pass. + final int runs; +} + +/// Where a run was lost. +final class HeatmapEnd { + const HeatmapEnd({ + required this.run, + required this.step, + required this.x, + required this.z, + }); + + final int run; + final int step; + final double x; + final double z; +} + +/// Thrown when a heatmap document cannot be read. +final class HeatmapFormatException implements Exception { + const HeatmapFormatException(this.message); + + final String message; + + @override + String toString() => 'HeatmapFormatException: $message'; +} + +final class Heatmap { + const Heatmap({ + required this.cellSize, + required this.cells, + required this.ends, + required this.outcomes, + }); + + /// Bins every position of every trail into squares [cellSize] metres on a + /// side. + /// + /// [outcomeNames] are counted even when no trail ended that way, so a + /// reader sees `stuck: 0` rather than wondering whether stuck was measured. + factory Heatmap.bin( + Iterable trails, { + double cellSize = 1.0, + Iterable outcomeNames = const [], + }) { + final listed = trails.toList(); + final samples = <(int, int), int>{}; + final runsThrough = <(int, int), Set>{}; + for (final trail in listed) { + for (final (x, z) in trail.positions) { + final cell = ((x / cellSize).floor(), (z / cellSize).floor()); + samples[cell] = (samples[cell] ?? 0) + 1; + (runsThrough[cell] ??= {}).add(trail.run); + } + } + return Heatmap( + cellSize: cellSize, + cells: [ + for (final MapEntry(key: (x, z), value: count) in samples.entries) + HeatmapCell( + x: x, + z: z, + samples: count, + runs: runsThrough[(x, z)]!.length, + ), + ], + ends: [ + for (final trail in listed) + if (trail.endedBadly && trail.positions.isNotEmpty) + HeatmapEnd( + run: trail.run, + step: trail.steps, + x: trail.positions.last.$1, + z: trail.positions.last.$2, + ), + ], + outcomes: { + for (final name in outcomeNames) name: 0, + for (final name in listed.map((trail) => trail.outcome).toSet()) + name: listed.where((trail) => trail.outcome == name).length, + }, + ); + } + + /// Reads what [toJson] wrote, or throws a [HeatmapFormatException] naming + /// the field that is wrong. + factory Heatmap.fromJson(Map json) { + final cellSize = json['cellSize']; + final cells = json['cells']; + final ends = json['deaths']; + final outcomes = json['outcomes']; + if (cellSize is! num || cellSize <= 0) { + throw const HeatmapFormatException('the heatmap has no cell size'); + } + if (cells is! List || ends is! List || outcomes is! Map) { + throw const HeatmapFormatException( + 'a heatmap has cells, deaths and outcomes, and this lacks one', + ); + } + try { + return Heatmap( + cellSize: cellSize.toDouble(), + cells: [ + for (final Map row in cells.cast()) + HeatmapCell( + x: (row['x']! as num).toInt(), + z: (row['z']! as num).toInt(), + samples: (row['samples']! as num).toInt(), + runs: (row['runs']! as num).toInt(), + ), + ], + ends: [ + for (final Map row in ends.cast()) + HeatmapEnd( + run: (row['seed']! as num).toInt(), + step: (row['step']! as num).toInt(), + x: (row['x']! as num).toDouble(), + z: (row['z']! as num).toDouble(), + ), + ], + outcomes: { + for (final MapEntry(:key, :value) in outcomes.entries) + key! as String: (value! as num).toInt(), + }, + ); + } on TypeError catch (error) { + throw HeatmapFormatException('a cell or a death is malformed: $error'); + } + } + + final double cellSize; + final List cells; + final List ends; + final Map outcomes; + + /// How many runs went in. + int get runs => outcomes.values.fold(0, (a, b) => a + b); + + /// **`deaths` and `seed` are the keys, and a run id goes under `seed`.** + /// They are `ai-01`'s, already in reports on disk and in the editor's + /// reader; renaming them for telemetry would split one format into two for + /// the sake of a word. + Map toJson() => { + 'cellSize': cellSize, + 'cells': >[ + for (final cell in cells) + { + 'x': cell.x, + 'z': cell.z, + 'samples': cell.samples, + 'runs': cell.runs, + }, + ], + 'deaths': >[ + for (final end in ends) + { + 'seed': end.run, + 'step': end.step, + 'x': end.x, + 'z': end.z, + }, + ], + 'outcomes': outcomes, + }; +} diff --git a/packages/flutter3d_sim/lib/src/telemetry/resimulation.dart b/packages/flutter3d_sim/lib/src/telemetry/resimulation.dart new file mode 100644 index 000000000..b6e970a88 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/telemetry/resimulation.dart @@ -0,0 +1,138 @@ +/// A recorded run played again, through the same simulation, to read numbers +/// off it the player never sent. +library; + +import 'dart:math' as math; + +import 'package:flutter3d_physics/flutter3d_physics.dart' show CollisionWorld; + +import '../input/input_state.dart'; +import '../input/input_tape.dart'; +import '../level/level.dart'; +import '../level/level_collision.dart'; +import '../loop/headless_run.dart'; +import '../loop/run_outcome.dart'; +import '../save/demo.dart'; +import '../save/state_digest.dart'; + +/// What [resimulate] found. +sealed class Resimulation { + const Resimulation(); +} + +/// The level handed in is not the one the run was recorded in. +final class ResimulationLevelChanged extends Resimulation { + const ResimulationLevelChanged({required this.found, required this.recorded}); + + final String found; + final String recorded; +} + +/// A fresh run of the level does not start where the recording started: the +/// demo began mid-run, or the game's spawning has changed since. +final class ResimulationStartDiffers extends Resimulation { + const ResimulationStartDiffers(); +} + +/// The replay parted from the checkpoints the run was recorded with. +final class ResimulationDiverged extends Resimulation { + const ResimulationDiverged(this.divergence, {required this.every}); + + final Divergence divergence; + + /// How far apart checkpoints were, so the answer can bracket the defect. + final int every; + + /// The last step at which the two runs are known to have agreed. + int get agreedUntil => math.max(0, divergence.step - every); +} + +/// The replay retraced every checkpoint, and here is what it saw on the way. +final class ResimulationRetraced extends Resimulation { + const ResimulationRetraced({ + required this.run, + required this.steps, + required this.checkpoints, + required this.trail, + required this.finalDigest, + }); + + /// The run as the last step left it — [HeadlessRun.reading] and + /// [HeadlessRun.outcome] are the metrics a caller asks of it. + final HeadlessRun run; + + final int steps; + final int checkpoints; + + /// `(x, z)` of [HeadlessRun.position] every so many steps, and once more at + /// the last step, so a run's end is where its trail ends. + final List<(double, double)> trail; + + final int finalDigest; + + RunOutcome get outcome => run.outcome; +} + +/// Plays [demo] into a fresh run of [game] in [level], checks it against the +/// checkpoints it was recorded with, and samples where the player went every +/// [sampleEvery] steps. +/// +/// **Numbers off a replay rather than numbers off the client**, and that is +/// what makes them telemetry worth keeping. A client that reported its own +/// metrics could report anything; one that sends its input can only send +/// input, and the server works out what that input did with the same +/// simulation the player ran — or finds that it did something else and says +/// at which checkpoint. +/// +/// A run that diverges answers no metrics at all: a trail that parted from the +/// player's at step 400 is a trail of somebody who never played. +Resimulation resimulate({ + required HeadlessGame game, + required Level level, + required Demo demo, + int sampleEvery = 10, + double dt = 1.0 / 60.0, +}) { + assert(sampleEvery > 0, 'a sample every no steps is no trail'); + final found = level.digestHex; + if (found != demo.levelHash) { + return ResimulationLevelChanged(found: found, recorded: demo.levelHash); + } + + final world = CollisionWorld(); + level.addTo(world); + final input = InputState(); + final run = game.start(level, world, input); + world.update(); + if (StateDigest.of(run.save().toJson()) != + StateDigest.of(demo.start.toJson())) { + return const ResimulationStartDiffers(); + } + + final playback = InputTapePlayback(demo.tape); + final trace = DigestTrace(every: demo.checkpoints.every); + final trail = <(double, double)>[]; + for (var step = 1; step <= demo.steps; step++) { + playback.applyTo(input); + input.beginStep(); + run.step(dt); + if (step % trace.every == 0) trace.observe(step, run.save().toJson()); + input.endStep(); + if (step % sampleEvery == 0 || step == demo.steps) { + final at = run.position; + trail.add((at.x, at.z)); + } + } + + final divergence = trace.divergenceFrom(demo.checkpoints.digests); + if (divergence != null) { + return ResimulationDiverged(divergence, every: trace.every); + } + return ResimulationRetraced( + run: run, + steps: demo.steps, + checkpoints: trace.steps.length, + trail: trail, + finalDigest: StateDigest.of(run.save().toJson()), + ); +} diff --git a/packages/flutter3d_sim/lib/src/telemetry/telemetry_consent.dart b/packages/flutter3d_sim/lib/src/telemetry/telemetry_consent.dart new file mode 100644 index 000000000..279b341ec --- /dev/null +++ b/packages/flutter3d_sim/lib/src/telemetry/telemetry_consent.dart @@ -0,0 +1,71 @@ +/// Whether a player has said their runs may leave the machine. +library; + +/// A player's answer to "may we send what you did in a level, to see where +/// levels are hard?", kept with when they gave it and to which wording. +/// +/// **Not asked is not yes.** A game that never showed the question sends +/// nothing, and so does one whose question changed since the player answered: +/// [allows] wants a grant to the [policy] the game asks under now. A grant to +/// last year's wording is consent to last year's wording. +/// +/// A game keeps this in its own settings document beside volumes and +/// bindings; [toJson] is the part of that document it owns. +final class TelemetryConsent { + const TelemetryConsent.notAsked() : _yes = null, policy = null, at = null; + + TelemetryConsent.granted({required String this.policy, required this.at}) + : _yes = true; + + TelemetryConsent.declined({required String this.policy, required this.at}) + : _yes = false; + + /// Reads what [toJson] wrote. Anything it cannot read is not asked, never a + /// grant: a settings file damaged in the one field that says yes must not + /// say yes. + factory TelemetryConsent.fromJson(Object? json) { + if (json is! Map) return const TelemetryConsent.notAsked(); + final answer = json['answer']; + final policy = json['policy']; + final at = DateTime.tryParse(json['at'] as String? ?? ''); + if (policy is! String || policy.isEmpty || at == null) { + return const TelemetryConsent.notAsked(); + } + return switch (answer) { + 'granted' => TelemetryConsent.granted(policy: policy, at: at), + 'declined' => TelemetryConsent.declined(policy: policy, at: at), + _ => const TelemetryConsent.notAsked(), + }; + } + + /// Yes, no, or null for never asked. A bool rather than an enum of three: + /// there is no fourth answer to add, and a switch over three names would be + /// a promise there never will be. + final bool? _yes; + + /// Whether the player was asked at all. + bool get asked => _yes != null; + + /// Whether they said yes — to [policy], which may not be today's. + bool get granted => _yes ?? false; + + /// The wording the player answered, by the name the game gives it — + /// `'2026-10'`, `'v2'`. + final String? policy; + + /// When they answered, in UTC. + final DateTime? at; + + /// Whether a run may be sent under [currentPolicy]. + bool allows(String currentPolicy) => granted && policy == currentPolicy; + + Map toJson() => { + 'answer': switch (_yes) { + true => 'granted', + false => 'declined', + null => 'notAsked', + }, + if (policy != null) 'policy': policy, + if (at != null) 'at': at!.toUtc().toIso8601String(), + }; +} diff --git a/packages/flutter3d_sim/lib/src/telemetry/telemetry_upload.dart b/packages/flutter3d_sim/lib/src/telemetry/telemetry_upload.dart new file mode 100644 index 000000000..8185f7deb --- /dev/null +++ b/packages/flutter3d_sim/lib/src/telemetry/telemetry_upload.dart @@ -0,0 +1,269 @@ +/// One run on its way to a telemetry server, and what sends it. +library; + +import 'dart:convert'; + +import '../save/demo.dart'; +import 'telemetry_consent.dart'; + +/// Thrown when an upload cannot be read. +final class TelemetryUploadFormatException implements Exception { + const TelemetryUploadFormatException(this.message); + + final String message; + + @override + String toString() => 'TelemetryUploadFormatException: $message'; +} + +/// What goes over the wire: which game, the run as a [Demo], and the consent +/// it was sent under. +/// +/// **The consent travels with the run**, so the server can refuse a run that +/// arrives without one rather than trusting that every client checked. A +/// client that lies about it can lie; one that forgot is caught. +/// +/// **Anonymous by construction.** [Demo.recordedBy] is dropped on the way in: +/// where a level is hard needs no name, and a field that is never sent is a +/// field nobody has to promise to delete. +final class TelemetryUpload { + TelemetryUpload._({ + required this.game, + required this.demo, + required this.policy, + required this.consentedAt, + }); + + /// Bumped when an existing field changes meaning. + static const int formatVersion = 1; + + /// An upload of [demo], or a sentence saying why there is none. + /// + /// The only way to build one, and it asks [consent] first: an upload that + /// exists is an upload somebody agreed to. + static ({TelemetryUpload? upload, String says}) prepare({ + required String game, + required Demo demo, + required TelemetryConsent consent, + required String policy, + }) { + if (!consent.allows(policy)) { + return ( + upload: null, + says: switch ((consent.asked, consent.granted)) { + (false, _) => + 'not sent: the player has not been asked whether runs may be ' + 'sent — ask, and send once they say yes', + (true, false) => 'not sent: the player said no to sending runs', + (true, true) => + 'not sent: the player agreed to policy "${consent.policy}", ' + 'and runs go out under "$policy" now — ask again', + }, + ); + } + if (demo.steps == 0) { + return (upload: null, says: 'not sent: the run has no steps'); + } + return ( + upload: TelemetryUpload._( + game: game, + demo: Demo( + level: demo.level, + levelHash: demo.levelHash, + start: demo.start, + tape: demo.tape, + buildStamp: demo.buildStamp, + checkpoints: demo.checkpoints, + platform: demo.platform, + dataSources: demo.dataSources, + ), + policy: policy, + consentedAt: consent.at!, + ), + says: 'ready: ${demo.steps} steps of ${demo.level}', + ); + } + + /// Reads an upload, or throws a [TelemetryUploadFormatException] that says + /// why not — a server's side of [toJson]. + factory TelemetryUpload.fromJson(Map json) { + final version = json['version']; + if (version is! num) { + throw const TelemetryUploadFormatException('the upload has no version'); + } + if (version > formatVersion) { + throw TelemetryUploadFormatException( + 'the upload is format $version and this reads $formatVersion', + ); + } + final game = json['game']; + if (game is! String || game.isEmpty) { + throw const TelemetryUploadFormatException('the upload names no game'); + } + final consent = json['consent']; + final policy = consent is Map ? consent['policy'] : null; + final at = consent is Map + ? DateTime.tryParse(consent['at'] as String? ?? '') + : null; + if (policy is! String || policy.isEmpty || at == null) { + throw const TelemetryUploadFormatException( + 'the upload carries no consent, so it was not agreed to', + ); + } + final run = json['run']; + if (run is! Map) { + throw const TelemetryUploadFormatException('the upload has no run'); + } + final Demo demo; + try { + demo = Demo.fromJson(run); + } on DemoFormatException catch (error) { + throw TelemetryUploadFormatException('the run: ${error.message}'); + } + return TelemetryUpload._( + game: game, + demo: demo, + policy: policy, + consentedAt: at, + ); + } + + final String game; + final Demo demo; + final String policy; + final DateTime consentedAt; + + Map toJson() => { + 'version': formatVersion, + 'game': game, + 'consent': { + 'policy': policy, + 'at': consentedAt.toUtc().toIso8601String(), + }, + 'run': demo.toJson(), + }; +} + +/// What a server gave back for an accepted run: its number there, and the key +/// that takes it back out. +final class TelemetryReceipt { + const TelemetryReceipt({required this.run, required this.eraseKey}); + + final int run; + + /// Shown to nobody but the player who sent the run. With it, and only with + /// it, the run is deleted — consent withdrawn is a run that is gone, not a + /// flag on one that stays. + final String eraseKey; +} + +/// The answer to sending one run. +typedef TelemetrySent = ({bool did, String says, TelemetryReceipt? receipt}); + +/// Where uploads go. The HTTP one is [HttpTelemetrySink]; a test hands in its +/// own. +abstract interface class TelemetrySink { + Future deliver(TelemetryUpload upload); +} + +/// One POST, as this package needs it: a body out, a status and a body back. +/// +/// **Handed in rather than imported.** This package runs in a browser and on +/// a server and imports neither `dart:io` nor a client library; whoever has a +/// network wraps theirs in this. +typedef JsonPost = + Future<({int status, String body})> Function(Uri url, String json); + +/// Sends uploads to a server's `POST /api/telemetry/runs`, which the +/// reference server in `cloud/` answers. +final class HttpTelemetrySink implements TelemetrySink { + const HttpTelemetrySink({required this.endpoint, required this.post}); + + /// The server's runs endpoint, in full. + final Uri endpoint; + final JsonPost post; + + @override + Future deliver(TelemetryUpload upload) async { + final ({int status, String body}) answer; + try { + answer = await post(endpoint, jsonEncode(upload.toJson())); + } catch (error) { + return ( + did: false, + says: 'could not reach $endpoint: $error', + receipt: null, + ); + } + final Object? body; + try { + body = jsonDecode(answer.body); + } on FormatException { + return ( + did: false, + says: '$endpoint answered ${answer.status} with no JSON', + receipt: null, + ); + } + final says = body is Map && body['says'] is String + ? body['says'] as String + : '$endpoint answered ${answer.status}'; + if (answer.status != 201 || body is! Map) { + return (did: false, says: says, receipt: null); + } + final run = body['run']; + final key = body['eraseKey']; + if (run is! num || key is! String) { + return ( + did: false, + says: '$endpoint took the run and gave no receipt for it', + receipt: null, + ); + } + return ( + did: true, + says: says, + receipt: TelemetryReceipt(run: run.toInt(), eraseKey: key), + ); + } +} + +/// Sends a finished run, if and only if the player agreed. +/// +/// **The check is here and not in every game**, so a game cannot forget it: +/// [send] reads [consent] at the moment of sending, and the sink is never +/// called without a grant to [policy]. +final class TelemetryUploader { + TelemetryUploader({ + required this.game, + required this.policy, + required this.consent, + required this.sink, + }); + + /// The game's name, as the server knows the games it can re-simulate. + final String game; + + /// The wording consent is asked under now. + final String policy; + + /// The player's answer as it stands, read on every send — a player who + /// withdraws mid-session stops the next run, not the next launch. + final TelemetryConsent Function() consent; + + final TelemetrySink sink; + + Future send(Demo demo) async { + final prepared = TelemetryUpload.prepare( + game: game, + demo: demo, + consent: consent(), + policy: policy, + ); + final upload = prepared.upload; + if (upload == null) { + return (did: false, says: prepared.says, receipt: null); + } + return sink.deliver(upload); + } +} diff --git a/packages/flutter3d_sim/test/telemetry_test.dart b/packages/flutter3d_sim/test/telemetry_test.dart new file mode 100644 index 000000000..5d94447e9 --- /dev/null +++ b/packages/flutter3d_sim/test/telemetry_test.dart @@ -0,0 +1,429 @@ +/// N7: a run leaves the machine only with the player's yes, and what the +/// server reads off it comes from playing it again. +/// +/// dart test test/telemetry_test.dart +library; + +import 'dart:convert'; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +const String _policy = '2026-10'; + +/// A body that walks along x as the stick says: past x = 4 the run is won, +/// below x = -2 it is lost. +final class _Walk implements HeadlessRun { + _Walk(this.input, {required this.start}); + + final InputState input; + final double start; + late final Vector3 _at = Vector3(start, 0.0, 0.0); + + @override + RunOutcome outcome = RunOutcome.playing; + + @override + void step(double dt) { + if (outcome.isOver) return; + _at + ..x += input.moveAxis.x * 3.0 * dt + ..z += input.moveAxis.y * 3.0 * dt; + if (_at.x > 4.0) outcome = RunOutcome.won; + if (_at.x < -2.0) outcome = RunOutcome.lost; + } + + @override + Snapshot save() => Snapshot({ + 'x': _at.x, + 'z': _at.z, + 'outcome': outcome.name, + }); + + @override + Vector3 get position => _at; + + @override + void eye(Vector3 out) => out.setFrom(_at); + + @override + void aim(Vector3 out) => out.setValues(1.0, 0.0, 0.0); + + @override + String get summary => 'at ${_at.x}'; + + @override + Map get reading => {'x': _at.x}; +} + +final class _WalkGame implements HeadlessGame { + const _WalkGame({this.from = 0.0}); + + final double from; + + @override + String get name => 'walk'; + + @override + Map get buttons => const {}; + + @override + EntityRegistry registry() => EntityRegistry(const []); + + @override + HeadlessRun start(Level level, CollisionWorld world, InputState input) => + _Walk(input, start: from); +} + +Level _level({double width = 20.0}) => Level( + name: 'strip', + materials: {'stone': LevelMaterial()}, + brushes: [ + Brush( + centre: Vector3(0.0, -0.5, 0.0), + size: Vector3(width, 1.0, 4.0), + material: 'stone', + ), + ], + entities: const [], +); + +/// Plays [level] with the stick at [stick] for [steps] steps, recording as a +/// game does: the tape before the step, the checkpoint after it. +Demo _record(Level level, {required double stick, int steps = 120}) { + final input = InputState(); + final world = CollisionWorld(); + level.addTo(world); + final run = const _WalkGame().start(level, world, input); + final start = run.save(); + final recorder = InputTapeRecorder(seed: 1); + final trace = DigestTrace(every: 10); + for (var step = 1; step <= steps; step++) { + input.setStickAxis(stick, 0.2); + recorder.record(input); + input.beginStep(); + run.step(1.0 / 60.0); + trace.observe(step, run.save().toJson()); + input.endStep(); + } + return Demo( + level: 'levels/strip.json', + levelHash: level.digestHex, + start: start, + tape: recorder.tape, + buildStamp: 'test', + checkpoints: trace, + recordedBy: 'somebody', + ); +} + +TelemetryConsent _granted([String policy = _policy]) => + TelemetryConsent.granted(policy: policy, at: DateTime.utc(2026, 10, 1)); + +final class _Sink implements TelemetrySink { + final List delivered = []; + + @override + Future deliver(TelemetryUpload upload) async { + delivered.add(upload); + return ( + did: true, + says: 'taken', + receipt: const TelemetryReceipt(run: 1, eraseKey: 'k'), + ); + } +} + +void main() { + group('consent', () { + test('nothing is sent until the player is asked and says yes', () async { + // Mutation: `allows` answering true for notAsked sends every run of a + // player who never saw the question. + final sink = _Sink(); + var consent = const TelemetryConsent.notAsked(); + final uploader = TelemetryUploader( + game: 'walk', + policy: _policy, + consent: () => consent, + sink: sink, + ); + final demo = _record(_level(), stick: 1.0); + + final unasked = await uploader.send(demo); + expect(unasked.did, isFalse); + expect(unasked.says, contains('has not been asked')); + + consent = TelemetryConsent.declined( + policy: _policy, + at: DateTime.utc(2026, 10, 1), + ); + final declined = await uploader.send(demo); + expect(declined.says, contains('said no')); + expect(sink.delivered, isEmpty); + + consent = _granted(); + expect((await uploader.send(demo)).did, isTrue); + expect(sink.delivered, hasLength(1)); + }); + + test('a yes to an older wording is not a yes to this one', () { + // Mutation: comparing only the answer and not the policy. + final prepared = TelemetryUpload.prepare( + game: 'walk', + demo: _record(_level(), stick: 1.0), + consent: _granted('2025-01'), + policy: _policy, + ); + expect(prepared.upload, isNull); + expect(prepared.says, contains('"2025-01"')); + expect(prepared.says, contains('ask again')); + }); + + test('a settings file damaged where the yes was reads as not asked', () { + // Mutation: defaulting an unreadable answer to granted. + for (final Object? damaged in [ + null, + 'granted', + {'answer': 'granted'}, + {'answer': 'granted', 'policy': _policy, 'at': 'x'}, + {'answer': 'maybe', 'policy': _policy, 'at': '2026'}, + ]) { + expect( + TelemetryConsent.fromJson(damaged).asked, + isFalse, + reason: '$damaged', + ); + } + final kept = TelemetryConsent.fromJson( + jsonDecode(jsonEncode(_granted().toJson())), + ); + expect(kept.allows(_policy), isTrue); + expect(kept.at, DateTime.utc(2026, 10, 1)); + final no = TelemetryConsent.fromJson( + jsonDecode( + jsonEncode( + TelemetryConsent.declined( + policy: _policy, + at: DateTime.utc(2026, 10, 1), + ).toJson(), + ), + ), + ); + expect((no.asked, no.granted), (true, false)); + }); + }); + + group('the upload', () { + test('drops who recorded the run, and carries the consent', () { + // Mutation: passing the demo through unchanged sends the player's name. + final upload = TelemetryUpload.prepare( + game: 'walk', + demo: _record(_level(), stick: 1.0), + consent: _granted(), + policy: _policy, + ).upload!; + final read = TelemetryUpload.fromJson( + jsonDecode(jsonEncode(upload.toJson())) as Map, + ); + expect(read.demo.recordedBy, isNull); + expect(jsonEncode(upload.toJson()), isNot(contains('somebody'))); + expect(read.game, 'walk'); + expect(read.policy, _policy); + expect(read.demo.steps, 120); + }); + + test('one that arrives without consent is refused by the reader', () { + // Mutation: a server reading uploads that never carried a grant. + final json = TelemetryUpload.prepare( + game: 'walk', + demo: _record(_level(), stick: 1.0), + consent: _granted(), + policy: _policy, + ).upload!.toJson()..remove('consent'); + expect( + () => TelemetryUpload.fromJson(json), + throwsA( + isA().having( + (e) => e.message, + 'message', + contains('no consent'), + ), + ), + ); + }); + + test('over HTTP, a receipt comes back from a 201 and the server\'s ' + 'sentence from anything else', () async { + // Mutation: treating any 2xx or any JSON as accepted. + final upload = TelemetryUpload.prepare( + game: 'walk', + demo: _record(_level(), stick: 1.0), + consent: _granted(), + policy: _policy, + ).upload!; + final endpoint = Uri.parse('http://localhost/api/telemetry/runs'); + String? sent; + final taken = await HttpTelemetrySink( + endpoint: endpoint, + post: (url, json) async { + sent = json; + return (status: 201, body: '{"run":7,"eraseKey":"abc","says":"ok"}'); + }, + ).deliver(upload); + expect(taken.did, isTrue); + expect(taken.receipt!.run, 7); + expect(taken.receipt!.eraseKey, 'abc'); + expect(jsonDecode(sent!), containsPair('game', 'walk')); + + final refused = await HttpTelemetrySink( + endpoint: endpoint, + post: (url, json) async => + (status: 422, body: '{"says":"this server plays no walk"}'), + ).deliver(upload); + expect(refused.did, isFalse); + expect(refused.says, 'this server plays no walk'); + + final down = await HttpTelemetrySink( + endpoint: endpoint, + post: (url, json) async => throw StateError('connection refused'), + ).deliver(upload); + expect(down.did, isFalse); + expect(down.says, contains('could not reach')); + }); + }); + + group('re-simulation', () { + test('retraces the run and reads where it went and how it ended', () { + // Mutation: sampling the trail from the recording client rather than + // the replay would make an empty trail here, since a demo has none. + final level = _level(); + final demo = _record(level, stick: 1.0); + + final found = resimulate( + game: const _WalkGame(), + level: level, + demo: demo, + sampleEvery: 20, + ); + + final retraced = found as ResimulationRetraced; + expect(retraced.steps, 120); + expect(retraced.checkpoints, 12); + expect(retraced.outcome, RunOutcome.won); + expect(retraced.trail, hasLength(6)); + // Twenty steps of a stick at (1, 0.2), which the input normalises. + expect(retraced.trail.first.$1, closeTo(0.98, 0.001)); + expect(retraced.trail.last.$1, greaterThan(4.0)); + }); + + test('a level edited since the recording is refused, naming both', () { + // Mutation: skipping the hash check replays a tape into other geometry. + final demo = _record(_level(), stick: 1.0); + final found = resimulate( + game: const _WalkGame(), + level: _level(width: 30.0), + demo: demo, + ); + final changed = found as ResimulationLevelChanged; + expect(changed.recorded, demo.levelHash); + expect(changed.found, _level(width: 30.0).digestHex); + }); + + test('a run that does not start where the recording did is refused', () { + final level = _level(); + final found = resimulate( + game: const _WalkGame(from: 1.0), + level: level, + demo: _record(level, stick: 1.0), + ); + expect(found, isA()); + }); + + test('a tape edited after it was recorded diverges, and says where', () { + // Mutation: trusting the client's checkpoints rather than comparing + // the replay's own against them. + final level = _level(); + final honest = _record(level, stick: 1.0); + final forged = Demo( + level: honest.level, + levelHash: honest.levelHash, + start: honest.start, + tape: InputTape( + seed: honest.tape.seed, + frames: [ + ...honest.tape.frames.take(44), + const InputFrame(stickX: -1.0), + ...honest.tape.frames.skip(45), + ], + ), + buildStamp: honest.buildStamp, + checkpoints: honest.checkpoints, + ); + final found = resimulate( + game: const _WalkGame(), + level: level, + demo: forged, + ); + final diverged = found as ResimulationDiverged; + expect(diverged.divergence.step, 50); + expect(diverged.agreedUntil, 40); + }); + }); + + group('the heatmap', () { + final trails = [ + const HeatmapTrail( + run: 1, + steps: 30, + outcome: 'lost', + positions: <(double, double)>[(0.5, 0.5), (0.6, 0.4), (1.5, 0.5)], + endedBadly: true, + ), + const HeatmapTrail( + run: 2, + steps: 20, + outcome: 'won', + positions: <(double, double)>[(0.2, 0.2), (2.5, -0.5)], + ), + ]; + + test('counts samples and distinct runs per cell, and marks only the ' + 'runs that were lost', () { + // Mutation: counting samples under `runs` would make the first cell + // three runs from two trails. + final map = Heatmap.bin( + trails, + outcomeNames: const ['won', 'lost', 'unfinished'], + ); + final origin = map.cells.singleWhere((c) => c.x == 0 && c.z == 0); + expect(origin.samples, 3); + expect(origin.runs, 2); + expect(map.cells.singleWhere((c) => c.x == 2).z, -1); + expect(map.ends.single.run, 1); + expect(map.ends.single.x, 1.5); + expect(map.outcomes, {'won': 1, 'lost': 1, 'unfinished': 0}); + expect(map.runs, 2); + }); + + test('writes the playtest report\'s keys, and reads them back', () { + // Mutation: renaming `deaths`/`seed` breaks every report already on + // disk and the editor that reads them. + final json = + jsonDecode(jsonEncode(Heatmap.bin(trails, cellSize: 2.0).toJson())) + as Map; + expect(json.keys, containsAll(['cellSize', 'cells', 'deaths'])); + expect((json['deaths']! as List).single, containsPair('seed', 1)); + + final read = Heatmap.fromJson(json); + expect(read.cellSize, 2.0); + expect(read.cells.map((c) => c.samples), [4, 1]); + expect(read.ends.single.step, 30); + + expect( + () => Heatmap.fromJson({'cellSize': 1.0}), + throwsA(isA()), + ); + }); + }); +} diff --git a/packages/flutter3d_sim_mcp/CHANGELOG.md b/packages/flutter3d_sim_mcp/CHANGELOG.md index e8096969a..4764f2fd9 100644 --- a/packages/flutter3d_sim_mcp/CHANGELOG.md +++ b/packages/flutter3d_sim_mcp/CHANGELOG.md @@ -4,6 +4,10 @@ step. The swapped level is built by the game that recorded the run, and a session over a bare simulation would answer with a divergence it did not cause. +- `Playtest.heatmap` bins through `flutter3d_sim`'s `Heatmap`, and `verify` + replays through `resimulate`, so a playtest report and a telemetry heatmap + are one format and a run this server calls verified is one a telemetry + server would take. Answers and JSON are unchanged. ## 0.8.0 diff --git a/packages/flutter3d_sim_mcp/lib/src/playtest.dart b/packages/flutter3d_sim_mcp/lib/src/playtest.dart index 2857b16ed..09b55d441 100644 --- a/packages/flutter3d_sim_mcp/lib/src/playtest.dart +++ b/packages/flutter3d_sim_mcp/lib/src/playtest.dart @@ -123,46 +123,28 @@ final class Playtest { /// metres on a side, counting how many positions from how many distinct /// runs landed in each — the JSON `ai-01`'s own row promises, and the /// same shape `ai-02`'s editor layer draws directly over the level. + /// + /// [Heatmap] does the binning, and N7's telemetry server bins real players' + /// runs through it too, so the two reports are one format. static Map heatmap( List runs, { double cellSize = 1.0, - }) { - final counts = <(int, int), int>{}; - final runsThroughCell = <(int, int), Set>{}; - for (final run in runs) { - for (final (x, z) in run.positions) { - final cell = ((x / cellSize).floor(), (z / cellSize).floor()); - counts[cell] = (counts[cell] ?? 0) + 1; - (runsThroughCell[cell] ??= {}).add(run.seed); - } - } - return { - 'cellSize': cellSize, - 'cells': >[ - for (final entry in counts.entries) - { - 'x': entry.key.$1, - 'z': entry.key.$2, - 'samples': entry.value, - 'runs': runsThroughCell[entry.key]!.length, - }, - ], - 'deaths': >[ - for (final run in runs) - if (run.outcome == PlaytestOutcome.died && run.positions.isNotEmpty) - { - 'seed': run.seed, - 'step': run.steps, - 'x': run.positions.last.$1, - 'z': run.positions.last.$2, - }, - ], - 'outcomes': { - for (final outcome in PlaytestOutcome.values) - outcome.name: runs.where((r) => r.outcome == outcome).length, - }, - }; - } + }) => Heatmap.bin( + [ + for (final run in runs) + HeatmapTrail( + run: run.seed, + steps: run.steps, + outcome: run.outcome.name, + positions: run.positions, + endedBadly: run.outcome == PlaytestOutcome.died, + ), + ], + cellSize: cellSize, + outcomeNames: [ + for (final outcome in PlaytestOutcome.values) outcome.name, + ], + ).toJson(); } /// A random policy's intent for one step — held for a stretch rather than diff --git a/packages/flutter3d_sim_mcp/lib/src/sim_session.dart b/packages/flutter3d_sim_mcp/lib/src/sim_session.dart index 9d2a1520d..8b3f969bd 100644 --- a/packages/flutter3d_sim_mcp/lib/src/sim_session.dart +++ b/packages/flutter3d_sim_mcp/lib/src/sim_session.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'dart:io'; -import 'dart:math' as math; import 'dart:typed_data'; import 'package:flutter3d_mcp_kit/flutter3d_mcp_kit.dart'; @@ -231,52 +230,37 @@ final class SimSession { } catch (error) { return _refuse('could not read the run\'s level "${demo.level}": $error'); } - if (level.digestHex != demo.levelHash) { - return _refuse( - 'the level at "${demo.level}" has changed since the run was recorded ' - '(hash ${level.digestHex}, the run says ${demo.levelHash}) — a tape ' - 'played into different geometry proves nothing', - ); - } - - final world = CollisionWorld(); - level.addTo(world); - final input = InputState(); - final run = game.start(level, world, input); - world.update(); - // A run that does not start where the recording started diverges before - // its first step, and saying so is more use than the checkpoint after. - if (StateDigest.of(run.save().toJson()) != - StateDigest.of(demo.start.toJson())) { - return _refuse( - 'diverges before the first step: a fresh ${game.name} run of ' - '"${demo.level}" does not start where "$path" started', - ); - } - - final playback = InputTapePlayback(demo.tape); - final trace = DigestTrace(every: demo.checkpoints.every); - for (var step = 1; step <= demo.steps; step++) { - playback.applyTo(input); - input.beginStep(); - run.step(_dt); - if (step % trace.every == 0) trace.observe(step, run.save().toJson()); - input.endStep(); - } - - final divergence = trace.divergenceFrom(demo.checkpoints.digests); - if (divergence != null) { - return _refuse( - 'diverges — $divergence; the difference arose after step ' - '${math.max(0, divergence.step - trace.every)}', - ); + // The same replay N7's telemetry server reads metrics off, so a run this + // tool calls verified is a run that server would take. + final ResimulationRetraced retraced; + switch (resimulate(game: game, level: level, demo: demo, dt: _dt)) { + case ResimulationLevelChanged(:final found, :final recorded): + return _refuse( + 'the level at "${demo.level}" has changed since the run was ' + 'recorded (hash $found, the run says $recorded) — a tape played ' + 'into different geometry proves nothing', + ); + // A run that does not start where the recording started diverges + // before its first step, and saying so is more use than the + // checkpoint after. + case ResimulationStartDiffers(): + return _refuse( + 'diverges before the first step: a fresh ${game.name} run of ' + '"${demo.level}" does not start where "$path" started', + ); + case final ResimulationDiverged diverged: + return _refuse( + 'diverges — ${diverged.divergence}; the difference arose after ' + 'step ${diverged.agreedUntil}', + ); + case final ResimulationRetraced found: + retraced = found; } - final hex = StateDigest.of( - run.save().toJson(), - ).toRadixString(16).padLeft(8, '0'); + final run = retraced.run; + final hex = retraced.finalDigest.toRadixString(16).padLeft(8, '0'); final agrees = 'replayed ${demo.steps} steps of "$path": agrees at all ' - '${trace.steps.length} checkpoints, ends at step ${demo.steps}, ' + '${retraced.checkpoints} checkpoints, ends at step ${demo.steps}, ' 'digest $hex'; if (claim == null) return _ok('$agrees. ${run.summary}'); try { diff --git a/site/content/quickstart.md b/site/content/quickstart.md index e155e057b..f1d96d219 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11103 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11118 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 2fa68ddfb..ca3bf3a6b 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,17 +1,17 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11103 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11118 tests need a GPU. --- # Testing -11103 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11118 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| | `flutter3d` | 1637 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | -| `flutter3d_sim` | 645 | | `pad_input` | 67 | +| `flutter3d_sim` | 657 | | `pad_input` | 67 | | `flutter3d_lab` | 15 | | `flutter3d_core` | 750 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | @@ -21,7 +21,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 70 | | `apps/flutter3d_demo_platformer` | 204 | | `flutter3d_impeller` | 64 | | `flutter3d_cpu` | 357 | | `apps/flutter3d_demo_strategy` | 50 | -| `apps/flutter3d_editor` | 231 | | `apps/flutter3d_demo_arcade` | 20 | +| `apps/flutter3d_editor` | 234 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11077 rather than 11103: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11092 rather than 11118: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index f4c38b92c..6c3b703d6 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -612,6 +612,38 @@ dungeon's `replay_golden_test.dart` and the racing demo moved onto `testReplay`; the strategy demo's `MatchDemo`, which is not a `Demo`; and a way to drop a tape the running game recorded into `test/tapes/` from the editor or the MCP server instead of a script per game. +**N7 status.** *Done:* `flutter3d_sim` has the client side and the format. +`TelemetryConsent` keeps the player's answer with the wording it was given +to; a grant to an older wording and a damaged settings file both count as no. +`TelemetryUpload.prepare` is the only way to build an upload and refuses +without consent. It drops `recordedBy`, and the consent travels with the run. +`TelemetryUploader` checks on every send and calls the sink only with a +grant. `HttpTelemetrySink` posts through a function the caller hands in. +`resimulate` replays a `Demo` against its level hash, starting state and +checkpoints, and returns the outcome and a trail, or where it parted. +`Heatmap` bins trails in `Playtest.heatmap`'s JSON, and `Playtest.heatmap` +and the sim server's `verify` now go through both. The reference server in +`cloud/server` has `POST /api/telemetry/runs` (refused without consent, +replayed in an isolate, a diverging or unknown run refused with nothing kept, +the input dropped and the trail and outcome kept with the consent record), +`DELETE runs/?key=` with only the key's hash stored, and +`GET heatmap?level=`. Postgres has migration 004. The editor's +report screen fetches that heatmap for the open level's digest and draws it +through `ai-02`'s view, where a lost run is a run id on the server. +*Tests:* consent, upload, replay and heatmap in `telemetry_test.dart`. The +routes run in memory, isolate included, in `telemetry_routes_test.dart`. +`telemetry_db_test.dart` covers the repository against Postgres and was not +run here, since no database was up. The fetch path is in the editor's +report screen test. +*Left:* the question in the games. No settings panel asks it yet, and no +demo sends a finished run, so `TelemetryUploader` needs a `JsonPost` over a +real client in `flutter3d_game`. A genre the server can step under +`dart run` comes next: every genre needs Flutter, so `main.server.dart` +registers no game and answers 503 until one is split out the way +`flutter3d_sim` was. Our instance is not deployed and not configured (decision +23). The heatmap is drawn on its own screen, not over the level in the +viewport. There is no upload rate limit and no retention period, and the +privacy page says nothing about telemetry yet. Lifted exclusions (decision 12), each its own item: From 669765845b6ca609c58b0b8fa46aae9baf4af1e5 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:28:00 +0300 Subject: [PATCH 045/141] A paused game can be photographed at any size, filtered and shared capturePhoto draws a picture in tiles of an offset projection, so its size is not bounded by the largest target a device allows, and PngStripWriter writes it a strip at a time. PhotoCamera flies with the world paused, held on a tether round where the player stood and swept out of walls. PhotoShelf saves or shares the result, natively or in a browser. The platformer has a photo mode over all of it. --- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_demo_platformer/lib/main.dart | 140 +++++- .../lib/src/photo_mode.dart | 163 +++++++ .../test/photo_mode_test.dart | 92 ++++ packages/flutter3d_app/CHANGELOG.md | 6 + packages/flutter3d_app/lib/flutter3d_app.dart | 1 + .../lib/src/photo/photo_shelf.dart | 126 +++++ .../lib/src/photo/photo_shelf_native.dart | 185 ++++++++ .../lib/src/photo/photo_shelf_web.dart | 85 ++++ .../flutter3d_app/test/photo_shelf_test.dart | 124 +++++ packages/flutter3d_core/CHANGELOG.md | 11 + .../flutter3d_core/lib/flutter3d_core.dart | 1 + .../lib/src/engine/render/photo_capture.dart | 438 ++++++++++++++++++ .../lib/src/engine/scene/projection.dart | 67 +++ packages/flutter3d_cpu/CHANGELOG.md | 5 + packages/flutter3d_cpu/lib/src/cpu_png.dart | 122 +++++ .../test/photo_capture_test.dart | 308 ++++++++++++ packages/flutter3d_sim/CHANGELOG.md | 8 + packages/flutter3d_sim/lib/flutter3d_sim.dart | 1 + .../lib/src/camera/photo_camera.dart | 292 ++++++++++++ .../lib/src/loop/pause_gate.dart | 11 + .../flutter3d_sim/test/pause_gate_test.dart | 18 + .../flutter3d_sim/test/photo_camera_test.dart | 204 ++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 14 +- tasks/0.9-engine-roadmap.md | 34 ++ tool/structure/repository.dart | 9 + 28 files changed, 2436 insertions(+), 35 deletions(-) create mode 100644 apps/flutter3d_demo_platformer/lib/src/photo_mode.dart create mode 100644 apps/flutter3d_demo_platformer/test/photo_mode_test.dart create mode 100644 packages/flutter3d_app/lib/src/photo/photo_shelf.dart create mode 100644 packages/flutter3d_app/lib/src/photo/photo_shelf_native.dart create mode 100644 packages/flutter3d_app/lib/src/photo/photo_shelf_web.dart create mode 100644 packages/flutter3d_app/test/photo_shelf_test.dart create mode 100644 packages/flutter3d_core/lib/src/engine/render/photo_capture.dart create mode 100644 packages/flutter3d_cpu/test/photo_capture_test.dart create mode 100644 packages/flutter3d_sim/lib/src/camera/photo_camera.dart create mode 100644 packages/flutter3d_sim/test/photo_camera_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index cc22533db..952f90f8c 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11118 tests** across 43 packages and 10 applications | +| Unit tests | **11141 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 946bb6e01..57c97c8d4 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11118 tests across forty-three packages and nine applications. The +There are 11141 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_demo_platformer/lib/main.dart b/apps/flutter3d_demo_platformer/lib/main.dart index 77174cc27..df519933f 100644 --- a/apps/flutter3d_demo_platformer/lib/main.dart +++ b/apps/flutter3d_demo_platformer/lib/main.dart @@ -30,6 +30,7 @@ import 'src/credits.dart'; import 'src/effects.dart'; import 'src/hud.dart'; import 'src/lens.dart'; +import 'src/photo_mode.dart'; import 'src/reactions.dart'; import 'src/run.dart'; import 'src/run_cubit.dart'; @@ -214,6 +215,9 @@ class _GameScreenState extends State final CameraNode _camera = CameraNode(projection: Lens.base); late final RenderView _view; + /// P stops the world and hands the player a camera — see `photo_mode.dart`. + final PhotoMode _photo = PhotoMode(); + /// The device, for whoever needs it before it exists. /// /// **A level used to be dropped on the floor if it got here first.** @@ -895,6 +899,7 @@ class _GameScreenState extends State pointerIsTheGate: Playing.capturesPointer, pointerHeld: _devices.isCaptured, padConnected: _pad.isConnected, + photoMode: _photo.active, ); // A pause is where most sessions end — the menu opened to quit, the pad // put down — so the run is written on the way in. @@ -917,6 +922,15 @@ class _GameScreenState extends State _particles.advance(_loop.lastFrame); _runnerVisuals.animate(dt, _runner); _placeCamera(dt); + if (_photo.active) { + // The paused loop drains nothing, so the look is taken here, and the + // photo camera is put on the node after the follow camera was. + final look = Vector2.zero(); + _drainLook(look); + _photo + ..fly(_input, look, dt) + ..applyTo(_camera); + } _fixtures?.sync(_frames.elapsed); _burnLamps(); _keepSaved(); @@ -927,9 +941,84 @@ class _GameScreenState extends State // cached outcome off `RunOutcome.playing`. _run.observe(); unawaited(_run.advance()); - if (mounted) setState(() {}); + // Not while a photo is drawn: a rebuild draws a frame on the renderer the + // tiles are drawn on — see `capturePhoto`. + if (mounted && !_photo.busy) setState(() {}); + } + + /// Opens photo mode where the follow camera is, or closes it. + void _togglePhoto() { + final camera = _followCamera; + final level = _loaded; + final runner = _runner; + if (_photo.active) { + setState(_photo.leave); + return; + } + if (camera == null || level == null || runner == null) return; + setState( + () => _photo.enter( + world: level.collision, + eye: camera.eye, + target: camera.target, + anchor: runner.body.position, + fieldOfView: Lens.base.fovYRadians + camera.extraFov, + ), + ); } + /// Draws the photo at [scale] times the window and saves it. + Future _takePhoto(int scale) async { + final renderer = _renderer; + if (renderer == null || _photo.busy) return; + final size = + MediaQuery.sizeOf(context) * MediaQuery.devicePixelRatioOf(context); + setState(() => _photo.busy = true); + // The frame saying so is drawn first; after it nothing redraws until the + // picture is done. + await SchedulerBinding.instance.endOfFrame; + final taken = await takePhoto( + renderer: renderer, + scene: _scene, + camera: _camera, + width: (size.width * scale).round(), + height: (size.height * scale).round(), + settings: _renderSettings(filtered: false), + filter: _photo.filter, + clearColor: _view.clearColor, + shelf: defaultPhotoShelf('platformer'), + name: 'platformer-${DateTime.now().millisecondsSinceEpoch}.png', + ); + if (!mounted) return; + setState(() { + _photo + ..busy = false + ..said = taken.saved.message; + }); + } + + /// What every frame is drawn with; [filtered] puts photo mode's filter on. + RenderSettings _renderSettings({bool filtered = true}) => RenderSettings( + fog: FogSettings( + color: _loaded?.level.fogColor ?? Vector3(0.05, 0.07, 0.12), + density: _loaded?.level.fogDensity ?? 0.0, + ), + // Three cascades, because this level is a hundred and twenty metres by two + // hundred and sixty and one map over that is fourteen centimetres of world + // per texel — which drew the runner's own shadow as a blurred slab beside + // them, and was reported as the character being drawn twice. + // + // 2048 rather than the default 1024, which is a real cost: the atlas is + // `resolution × cascades` wide, so this is 6144 × 2048. What it buys is the + // character's own shadow reading as soft rather than as a staircase — at + // 1024 the near cascade is 1.9 cm of world per texel and the penguin's + // shadow is a visible flight of steps beside it. + shadows: const ShadowSettings(cascades: 3, resolution: 2048), + look: filtered && _photo.active + ? _photo.look(const LookSettings()) + : const LookSettings(), + ); + /// One simulation step. Nothing here draws. /// What the last simulated step reported. See where it is drained. List _lastStep = const []; @@ -1197,6 +1286,26 @@ class _GameScreenState extends State focusNode: _keyboard, autofocus: true, onKeyEvent: (_, KeyEvent event) { + // Photo mode before the settings: Escape there means "back to the + // game", and the panel would take it as "open me". + if (event is KeyDownEvent && + _screen.state.started && + !_settings.state.isOpen && + !_photo.busy && + (event.logicalKey == LogicalKeyboardKey.keyP || + (_photo.active && + event.logicalKey == LogicalKeyboardKey.escape))) { + _togglePhoto(); + return KeyEventResult.handled; + } + final photoSays = _photo.key( + event, + onCapture: (int scale) => unawaited(_takePhoto(scale)), + ); + if (photoSays != null) { + setState(() {}); + return photoSays; + } // The settings get the key first — see `settingsKeys` for the order // and for the bug this call fixed here: R sat above the rebinding, so // a player at the end of a run could not bind R to anything. @@ -1263,29 +1372,7 @@ class _GameScreenState extends State scene: scene, view: _view, onBeforeFrame: () {}, - settings: () => RenderSettings( - fog: FogSettings( - color: - _loaded?.level.fogColor ?? Vector3(0.05, 0.07, 0.12), - density: _loaded?.level.fogDensity ?? 0.0, - ), - // Three cascades, because this level is a hundred and twenty - // metres by two hundred and sixty and one map over that is - // fourteen centimetres of world per texel — which drew the - // runner's own shadow as a blurred slab beside them, and was - // reported as the character being drawn twice. - // - // 2048 rather than the default 1024, which is a real cost: - // the atlas is `resolution × cascades` wide, so this is - // 6144 × 2048. What it buys is the character's own shadow - // reading as soft rather than as a staircase — at 1024 the - // near cascade is 1.9 cm of world per texel and the penguin's - // shadow is a visible flight of steps beside it. - shadows: const ShadowSettings( - cascades: 3, - resolution: 2048, - ), - ), + settings: _renderSettings, presentFrame: presentFrame, ), ), @@ -1313,7 +1400,10 @@ class _GameScreenState extends State // Not behind the title card: the tallies and its own "Click to // play" banner showed through it, saying the same thing twice // and counting a run the player has not started. - if (sim != null && _screen.state.started) + if (_photo.active) PhotoBar(mode: _photo), + // Not in photo mode either: the picture is the level, and the + // tallies over it are not. + if (sim != null && _screen.state.started && !_photo.active) Hud( coins: _runner?.purse['coin'] ?? 0, deaths: sim.deaths, diff --git a/apps/flutter3d_demo_platformer/lib/src/photo_mode.dart b/apps/flutter3d_demo_platformer/lib/src/photo_mode.dart new file mode 100644 index 000000000..1115c8731 --- /dev/null +++ b/apps/flutter3d_demo_platformer/lib/src/photo_mode.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_game_platformer/flutter3d_game_platformer.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:vector_math/vector_math.dart' hide Colors; + +import 'lens.dart'; + +/// This game's photo mode — `N8`: the world stopped, a camera to fly, a +/// filter to pick and a picture larger than the window. +/// +/// The parts are the engine's: [PhotoCamera] keeps the camera in the level, +/// [PhotoFilter] is a change to the composite's look, and `takePhoto` draws, +/// encodes and saves. What is here is only what is this game's: which keys +/// do what, and how the camera meets this game's lens. +/// +/// **The world's own input, read rather than stepped.** The loop is paused, +/// so nothing drains [InputState]; the keys the player already has for +/// running fly the camera instead, read as held each frame, and the jump and +/// the drop take it up and down. +final class PhotoMode { + PhotoCamera? _camera; + int _filter = 0; + + /// Whether the world is stopped for a picture. + bool get active => _camera != null; + + /// Whether a picture is being drawn; the keys wait for it, and the game + /// stops redrawing its surface — see `capturePhoto`. + bool busy = false; + + /// The last thing a picture had to say: where it went, or why not. + String? said; + + PhotoFilter get filter => PhotoFilter.all[_filter]; + + /// Stops the world and takes the camera from where the game had it. + void enter({ + required CollisionWorld world, + required Vector3 eye, + required Vector3 target, + required Vector3 anchor, + required double fieldOfView, + }) { + _camera = PhotoCamera(world: world) + ..begin( + eye: eye, + target: target, + anchor: anchor, + fieldOfView: fieldOfView, + ); + said = null; + } + + void leave() => _camera = null; + + /// One frame of flying. + void fly(InputState input, Vector2 look, double dt) { + final camera = _camera; + if (camera == null || busy) return; + camera.look(-look.x * _sensitivity, -look.y * _sensitivity); + double axis(GameAction plus, GameAction minus) => + (input.held(plus) ? 1.0 : 0.0) - (input.held(minus) ? 1.0 : 0.0); + final intent = Vector3( + axis(GameAction.moveRight, GameAction.moveLeft), + axis(GameAction.jump, PlatformerActions.dropThrough), + axis(GameAction.moveForward, GameAction.moveBack), + ); + // Sprint is a faster camera, as it is a faster runner. + camera.fly(input.held(GameAction.sprint) ? intent * 3.0 : intent, dt); + } + + /// The follow camera's own mouse sensitivity, so the view turns at the + /// rate the player's hand already knows. + static const double _sensitivity = 0.0035; + + /// Puts the photo camera on the scene's [node]. + void applyTo(CameraNode node) { + final camera = _camera; + if (camera == null) return; + node + ..setPositionFrom(camera.eye) + ..lookAt(camera.target, up: camera.up) + ..projection = Lens.base.copyWith(fovYRadians: camera.fieldOfView); + } + + /// The game's look with the chosen filter on it. + LookSettings look(LookSettings game) => filter.applyTo(game); + + /// The keys photo mode owns while it is open, or null for one it does not. + /// + /// [onCapture] is asked for a picture at a multiple of the window's size. + KeyEventResult? key(KeyEvent event, {required void Function(int) onCapture}) { + final camera = _camera; + if (camera == null || event is KeyUpEvent) return null; + if (busy) return KeyEventResult.handled; + final repeatable = event is KeyDownEvent || event is KeyRepeatEvent; + switch (event.logicalKey) { + // Brackets rather than the arrows, which already fly the camera. + case LogicalKeyboardKey.bracketRight when event is KeyDownEvent: + _filter = (_filter + 1) % PhotoFilter.all.length; + case LogicalKeyboardKey.bracketLeft when event is KeyDownEvent: + _filter = (_filter - 1) % PhotoFilter.all.length; + case LogicalKeyboardKey.comma when repeatable: + camera.tilt(-0.05); + case LogicalKeyboardKey.period when repeatable: + camera.tilt(0.05); + case LogicalKeyboardKey.equal when repeatable: + camera.zoom(1.1); + case LogicalKeyboardKey.minus when repeatable: + camera.zoom(1.0 / 1.1); + case LogicalKeyboardKey.enter when event is KeyDownEvent: + onCapture(HardwareKeyboard.instance.isShiftPressed ? 4 : 2); + default: + return null; + } + return KeyEventResult.handled; + } +} + +/// The strip along the bottom of the screen in photo mode: the filter, the +/// keys, and the last picture's sentence. +class PhotoBar extends StatelessWidget { + const PhotoBar({required this.mode, super.key}); + + final PhotoMode mode; + + @override + Widget build(BuildContext context) => IgnorePointer( + child: Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: const EdgeInsets.all(16), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.6), + borderRadius: BorderRadius.circular(8), + ), + child: DefaultTextStyle( + style: const TextStyle(color: Colors.white, fontSize: 14), + textAlign: TextAlign.center, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + mode.busy + ? 'Taking the picture…' + : 'Photo mode · filter: ${mode.filter.name}', + style: const TextStyle(fontWeight: FontWeight.bold), + ), + const Text( + 'WASD fly · Space/C up and down · Shift faster · [ ] filter · ' + ', . tilt · − = zoom · Enter 2× · Shift+Enter 4× · P back', + ), + if (mode.said case final said?) Text(said), + ], + ), + ), + ), + ), + ); +} diff --git a/apps/flutter3d_demo_platformer/test/photo_mode_test.dart b/apps/flutter3d_demo_platformer/test/photo_mode_test.dart new file mode 100644 index 000000000..61b8c01eb --- /dev/null +++ b/apps/flutter3d_demo_platformer/test/photo_mode_test.dart @@ -0,0 +1,92 @@ +/// The platformer's photo mode — `N8`: which keys it takes, and which it +/// leaves to the camera it flies. +/// +/// flutter test test/photo_mode_test.dart +library; + +import 'package:flutter/material.dart' show KeyEventResult; +import 'package:flutter/services.dart'; +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_demo_platformer/src/photo_mode.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +KeyDownEvent _down(LogicalKeyboardKey key) => KeyDownEvent( + physicalKey: PhysicalKeyboardKey.keyA, + logicalKey: key, + timeStamp: Duration.zero, +); + +PhotoMode _open() => PhotoMode() + ..enter( + world: CollisionWorld()..update(), + eye: Vector3(0.0, 2.0, 5.0), + target: Vector3.zero(), + anchor: Vector3(0.0, 1.0, 3.0), + fieldOfView: 1.05, + ); + +void main() { + // Enter asks the keyboard whether Shift is held. + TestWidgetsFlutterBinding.ensureInitialized(); + + test( + 'brackets step through the filters, and Enter asks for a 2x picture', + () { + final mode = _open(); + final asked = []; + void capture(int scale) => asked.add(scale); + + mode.key(_down(LogicalKeyboardKey.bracketRight), onCapture: capture); + expect(mode.filter, PhotoFilter.all[1]); + mode.key(_down(LogicalKeyboardKey.bracketLeft), onCapture: capture); + mode.key(_down(LogicalKeyboardKey.bracketLeft), onCapture: capture); + expect(mode.filter, PhotoFilter.all.last, reason: 'and wraps round'); + + mode.key(_down(LogicalKeyboardKey.enter), onCapture: capture); + expect(asked, [2]); + }, + ); + + test('the arrows are left to fly the camera', () { + // Mutation: put the filters back on the arrows. The arrows are bound to + // moving left and right, and in photo mode they stop flying the camera + // and flick through filters instead. + final mode = _open(); + expect( + mode.key(_down(LogicalKeyboardKey.arrowLeft), onCapture: (_) {}), + isNull, + ); + expect(mode.filter, PhotoFilter.none); + }); + + test('a closed photo mode takes no keys, and a busy one takes them all', () { + final closed = PhotoMode(); + expect( + closed.key(_down(LogicalKeyboardKey.enter), onCapture: (_) {}), + isNull, + ); + // Mutation: drop the `busy` check. A second Enter while a 4x picture is + // drawing starts another capture on the same renderer. + final asked = []; + final busy = _open()..busy = true; + expect( + busy.key(_down(LogicalKeyboardKey.enter), onCapture: asked.add), + KeyEventResult.handled, + ); + expect(asked, isEmpty); + }); + + test('the held run keys fly the camera, and the node follows it', () { + final mode = _open(); + final input = InputState()..press(GameAction.moveForward); + final node = CameraNode(projection: const PerspectiveProjection()); + mode + ..fly(input, Vector2.zero(), 0.5) + ..applyTo(node); + // Two metres along the view, which looks from (0, 2, 5) at the origin. + expect(node.readPosition().z, lessThan(5.0 - 1.0)); + expect(node.readPosition().y, lessThan(2.0)); + }); +} diff --git a/packages/flutter3d_app/CHANGELOG.md b/packages/flutter3d_app/CHANGELOG.md index f48079f18..2dc68c78f 100644 --- a/packages/flutter3d_app/CHANGELOG.md +++ b/packages/flutter3d_app/CHANGELOG.md @@ -1,5 +1,11 @@ ## Unreleased +- **A photo goes somewhere.** `takePhoto` draws with `capturePhoto`, encodes + with `PngStripWriter` and puts the file on a `PhotoShelf`, abandoning it if + the capture fails. `FilePhotoShelf` writes into the player's Pictures folder + on a desktop and the game's own folder on a phone, through a `.part` file and + a rename. `BrowserPhotoShelf` offers the share sheet where the browser takes + files and downloads otherwise. `defaultPhotoShelf` picks the platform's. - **A texture repainted under a running game is drawn at whatever size it now is.** `HotSwap.loadTexture` and `registerTexture` watch an image file; a swap after it changed uploads the new picture as a texture of its own, diff --git a/packages/flutter3d_app/lib/flutter3d_app.dart b/packages/flutter3d_app/lib/flutter3d_app.dart index c59cd17d2..81a7bf83c 100644 --- a/packages/flutter3d_app/lib/flutter3d_app.dart +++ b/packages/flutter3d_app/lib/flutter3d_app.dart @@ -51,6 +51,7 @@ export 'src/level/shared_meshes.dart'; export 'src/level/surface_mesh.dart'; export 'src/level/terrain_tiles.dart'; export 'src/level/visibility_culler.dart'; +export 'src/photo/photo_shelf.dart'; export 'src/storage/storage.dart'; export 'src/surface/did_not_start.dart'; export 'src/surface/frame_clock.dart'; diff --git a/packages/flutter3d_app/lib/src/photo/photo_shelf.dart b/packages/flutter3d_app/lib/src/photo/photo_shelf.dart new file mode 100644 index 000000000..82a14a3de --- /dev/null +++ b/packages/flutter3d_app/lib/src/photo/photo_shelf.dart @@ -0,0 +1,126 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart' show PngStripWriter; +import 'package:vector_math/vector_math.dart'; + +export 'photo_shelf_native.dart' + if (dart.library.js_interop) 'photo_shelf_web.dart'; + +/// Where photo mode puts a picture, and how it is passed on — `N8`. +/// +/// **Opened, written to, closed, rather than handed a file**, because the file +/// is never whole in memory: `capturePhoto` makes a strip at a time and +/// `PngStripWriter` encodes it at once, so a shelf receives the PNG in pieces. +/// On a desktop the pieces go straight to disk; in a browser they become the +/// parts of a `Blob`, which the browser holds outside the Dart heap. +/// +/// **Sharing is the shelf's call, not the game's.** In a phone's browser the +/// shelf offers the system share sheet with the file in it; on a desktop it +/// saves to the Pictures folder and says where. A game that wants the native +/// share sheet on Android or iOS implements this with the plugin it already +/// uses — this package does not pull one in for every game that has no use +/// for it. +abstract interface class PhotoShelf { + /// A place for a PNG called [name], which includes its extension. + Future open(String name); +} + +/// One picture on its way to a shelf. +abstract interface class PhotoSaving { + /// The next bytes of the file, in order. + void add(Uint8List bytes); + + /// Ends the file and passes it on. **Never throws**: a disk that is full + /// and a share sheet the player cancelled are both answers. + Future close(); + + /// Gives up on the file and leaves nothing half-written behind. + Future abandon(); +} + +/// What happened to a picture. +final class PhotoSaved { + const PhotoSaved({ + required this.kept, + required this.message, + this.where, + this.shared = false, + }); + + /// Whether the picture exists somewhere the player can find it. + final bool kept; + + /// One sentence for the screen: where it went, or why it did not. + final String message; + + /// A path or a file name, when there is one to show. + final String? where; + + /// Whether it went to a share sheet rather than only to a folder. + final bool shared; +} + +/// A picture taken: what the capture did, and where the file went. +final class PhotoTaken { + const PhotoTaken({required this.report, required this.saved}); + + /// Null when the capture itself failed; [saved] says how. + final PhotoCaptureReport? report; + final PhotoSaved saved; +} + +/// Draws [scene] through [camera] at [width] × [height] and puts it on +/// [shelf] as [name]. +/// +/// The whole of photo mode's last step in one call: `capturePhoto` for the +/// tiles, `PngStripWriter` for the file, [shelf] for where it goes. A capture +/// that fails part way abandons the file rather than leaving the first half +/// of a picture in the player's Pictures folder, and the failure comes back +/// as a [PhotoSaved] that says so. +Future takePhoto({ + required Renderer renderer, + required Scene scene, + required CameraNode camera, + required int width, + required int height, + required PhotoShelf shelf, + required String name, + RenderSettings settings = const RenderSettings(), + PhotoFilter filter = PhotoFilter.none, + Vector4? clearColor, + int tileWidth = 1024, + int tileHeight = 1024, + void Function(double progress)? onProgress, +}) async { + final saving = await shelf.open(name); + try { + final png = PngStripWriter(width: width, height: height, sink: saving.add); + final report = await capturePhoto( + renderer: renderer, + scene: scene, + camera: camera, + width: width, + height: height, + settings: settings, + filter: filter, + clearColor: clearColor, + tileWidth: tileWidth, + tileHeight: tileHeight, + onProgress: onProgress, + onRows: (rgba, top, rows) => png.addRows(rgba, rows), + ); + png.close(); + return PhotoTaken(report: report, saved: await saving.close()); + } catch (error) { + await saving.abandon(); + return PhotoTaken( + report: null, + saved: PhotoSaved( + kept: false, + message: 'The picture was not taken: $error', + ), + ); + } +} diff --git a/packages/flutter3d_app/lib/src/photo/photo_shelf_native.dart b/packages/flutter3d_app/lib/src/photo/photo_shelf_native.dart new file mode 100644 index 000000000..c453d7c11 --- /dev/null +++ b/packages/flutter3d_app/lib/src/photo/photo_shelf_native.dart @@ -0,0 +1,185 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; + +import '../storage/storage_native.dart' show applicationDirectory; +import 'photo_shelf.dart'; + +/// Where a game's photos go on this platform, or null where nothing can be +/// worked out. +/// +/// **The player's own Pictures folder on a desktop**, in a folder named after +/// the game, because that is where a person looks for a screenshot and the +/// game's settings folder is somewhere they never look. A sandboxed macOS +/// build's `HOME` is its container, so this lands in the container's own +/// `Pictures` unless the application has the pictures entitlement — still a +/// folder the message names, but not the one the player opens. +/// +/// **The game's own folder on a phone**, under `photos/`. Neither phone lets +/// an application write into the shared gallery without a plugin, and this +/// package takes none — see [PhotoShelf]. [FilePhotoShelf] says so in its +/// message rather than claiming the picture is in the gallery. +String? picturesDirectory({ + required String appName, + required TargetPlatform platform, + required Map environment, + required String temporary, +}) { + final home = environment['HOME']; + return switch (platform) { + TargetPlatform.macOS || + TargetPlatform.linux => home == null ? null : '$home/Pictures/$appName', + TargetPlatform.windows => switch (environment['USERPROFILE']) { + final profile? => '$profile\\Pictures\\$appName', + null => null, + }, + TargetPlatform.android || + TargetPlatform.iOS => switch (applicationDirectory( + appName: appName, + platform: platform, + environment: environment, + temporary: temporary, + )) { + final root? => '$root/photos', + null => null, + }, + TargetPlatform.fuchsia => null, + }; +} + +/// Photos written as files into [directory], or into [picturesDirectory] +/// for [appName]. +/// +/// **Through a `.part` file and a rename**, as every other write in this +/// package goes: a game closed half way through a large capture leaves a +/// `.part` behind rather than a PNG that opens as half a picture. +final class FilePhotoShelf implements PhotoShelf { + FilePhotoShelf({required this.appName, Directory? directory}) + : _given = directory; + + final String appName; + final Directory? _given; + + Directory? _resolve() { + if (_given != null) return _given; + try { + final path = picturesDirectory( + appName: appName, + platform: defaultTargetPlatform, + environment: Platform.environment, + temporary: Directory.systemTemp.path, + ); + return path == null ? null : Directory(path); + } catch (_) { + return null; + } + } + + @override + Future open(String name) async { + final directory = _resolve(); + if (directory == null) { + return _Nowhere('This platform has no folder to put "$name" in.'); + } + try { + await directory.create(recursive: true); + final file = File('${directory.path}${Platform.pathSeparator}$name'); + final part = File('${file.path}.part'); + return _FileSaving(file, part, await part.open(mode: FileMode.write)); + } catch (error) { + return _Nowhere( + '"$name" could not be started in ${directory.path}: ' + '$error', + ); + } + } +} + +final class _FileSaving implements PhotoSaving { + _FileSaving(this.file, this.part, this.handle); + + final File file; + final File part; + final RandomAccessFile handle; + Object? _failed; + + /// Writes as the bytes arrive, synchronously, so a strip is on disk before + /// the next is drawn and the memory it took is free. + @override + void add(Uint8List bytes) { + if (_failed != null) return; + try { + handle.writeFromSync(bytes); + } catch (error) { + _failed = error; + } + } + + @override + Future close() async { + final failed = _failed; + if (failed != null) { + await abandon(); + return PhotoSaved( + kept: false, + message: 'The picture could not be written to ${file.path}: $failed', + ); + } + try { + await handle.close(); + await part.rename(file.path); + final phone = + defaultTargetPlatform == TargetPlatform.android || + defaultTargetPlatform == TargetPlatform.iOS; + return PhotoSaved( + kept: true, + where: file.path, + message: phone + ? 'Saved in the game\'s own folder, ${file.path}. It is not in ' + 'the gallery: that needs the game to offer a share sheet.' + : 'Saved to ${file.path}.', + ); + } catch (error) { + await abandon(); + return PhotoSaved( + kept: false, + message: 'The picture could not be written to ${file.path}: $error', + ); + } + } + + @override + Future abandon() async { + try { + await handle.close(); + } catch (_) { + // Already closed by [close], which is how a failed close gets here. + } + try { + if (part.existsSync()) await part.delete(); + } catch (_) { + // A `.part` left behind is the documented failure, and harmless. + } + } +} + +/// A shelf that could not be opened: takes the bytes and drops them, and +/// says why at the end. +final class _Nowhere implements PhotoSaving { + _Nowhere(this.why); + + final String why; + + @override + void add(Uint8List bytes) {} + + @override + Future close() async => PhotoSaved(kept: false, message: why); + + @override + Future abandon() async {} +} + +/// The shelf a build outside the browser gets. +PhotoShelf defaultPhotoShelf(String appName) => + FilePhotoShelf(appName: appName); diff --git a/packages/flutter3d_app/lib/src/photo/photo_shelf_web.dart b/packages/flutter3d_app/lib/src/photo/photo_shelf_web.dart new file mode 100644 index 000000000..95cc8a33a --- /dev/null +++ b/packages/flutter3d_app/lib/src/photo/photo_shelf_web.dart @@ -0,0 +1,85 @@ +import 'dart:js_interop'; +import 'dart:typed_data'; + +import 'package:web/web.dart' as web; + +import 'photo_shelf.dart'; + +/// Photos handed to the browser: the system share sheet where the browser +/// offers one for files, a download everywhere else. +/// +/// **The pieces become `Blob` parts as they arrive**, so the PNG is assembled +/// by the browser outside the Dart heap and is never one buffer here. +/// +/// **The share sheet may refuse, and that is not a failure.** Browsers only +/// open it inside a user gesture, and a large capture can outlast the +/// gesture that started it; a refusal for that reason, like a player +/// cancelling the sheet, falls back to the download, so the picture is kept +/// either way. +final class BrowserPhotoShelf implements PhotoShelf { + BrowserPhotoShelf({this.share = true}); + + /// Whether to try the share sheet at all before downloading. + final bool share; + + @override + Future open(String name) async => _BlobSaving(name, share); +} + +final class _BlobSaving implements PhotoSaving { + _BlobSaving(this.name, this.share); + + final String name; + final bool share; + final List _parts = []; + + @override + void add(Uint8List bytes) => _parts.add(bytes.toJS); + + @override + Future close() async { + try { + final file = web.File( + _parts.toJS, + name, + web.FilePropertyBag(type: 'image/png'), + ); + _parts.clear(); + if (share) { + final data = web.ShareData(files: [file].toJS); + try { + if (web.window.navigator.canShare(data)) { + await web.window.navigator.share(data).toDart; + return PhotoSaved( + kept: true, + shared: true, + where: name, + message: 'Shared $name.', + ); + } + } catch (_) { + // Cancelled, or the gesture ran out while the picture was drawn: + // the download below keeps it all the same. + } + } + final url = web.URL.createObjectURL(file); + (web.document.createElement('a') as web.HTMLAnchorElement) + ..href = url + ..download = name + ..click(); + web.URL.revokeObjectURL(url); + return PhotoSaved(kept: true, where: name, message: 'Downloaded $name.'); + } catch (error) { + return PhotoSaved( + kept: false, + message: 'The browser would not take $name: $error', + ); + } + } + + @override + Future abandon() async => _parts.clear(); +} + +/// The shelf a browser build gets. +PhotoShelf defaultPhotoShelf(String appName) => BrowserPhotoShelf(); diff --git a/packages/flutter3d_app/test/photo_shelf_test.dart b/packages/flutter3d_app/test/photo_shelf_test.dart new file mode 100644 index 000000000..05099fd4b --- /dev/null +++ b/packages/flutter3d_app/test/photo_shelf_test.dart @@ -0,0 +1,124 @@ +/// Photo mode's last step — `N8`: a picture drawn, encoded and on a shelf in +/// one call, and the folder each platform keeps it in. +/// +/// flutter test test/photo_shelf_test.dart +@TestOn('vm') +library; + +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_app/flutter3d_app.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +({Renderer renderer, Scene scene, CameraNode camera}) _game() { + final device = CpuDevice( + width: 64, + height: 48, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final camera = CameraNode( + name: 'eye', + projection: const PerspectiveProjection(fovYRadians: 0.9), + )..setPosition(0.0, 0.0, 3.0); + final scene = Scene() + ..add( + MeshNode( + DeviceMesh.upload(device, CuboidShape(size: Vector3.all(1.0)).build()), + Material(lighting: LightingModel.unlit), + name: 'box', + ), + ) + ..add(camera); + return ( + renderer: Renderer.create(device: device), + scene: scene, + camera: camera, + ); +} + +void main() { + late Directory folder; + setUp(() => folder = Directory.systemTemp.createTempSync('photo_shelf')); + tearDown(() => folder.deleteSync(recursive: true)); + + test('a picture larger than the game\'s screen lands as one PNG', () async { + final game = _game(); + final taken = await takePhoto( + renderer: game.renderer, + scene: game.scene, + camera: game.camera, + width: 150, + height: 70, + tileWidth: 64, + tileHeight: 32, + shelf: FilePhotoShelf(appName: 'game', directory: folder), + name: 'shot.png', + ); + expect(taken.saved.kept, isTrue, reason: taken.saved.message); + expect(taken.report!.tilesX, 3); + // Mutation: skip the rename in `_FileSaving.close`. The picture stays a + // `.part` that no image viewer lists, and the message names a file that + // is not there. + final names = folder.listSync().map((e) => e.uri.pathSegments.last); + expect(names, ['shot.png']); + // The header's size, and the end chunk last: a whole file, of the picture + // asked for. `photo_capture_test.dart` decodes the pixels. + final bytes = File('${folder.path}/shot.png').readAsBytesSync(); + final header = ByteData.sublistView(bytes, 16, 24); + expect((header.getUint32(0), header.getUint32(4)), (150, 70)); + expect( + String.fromCharCodes(bytes.sublist(bytes.length - 8)), + startsWith('IEND'), + ); + }); + + test('a capture that fails leaves nothing behind and says why', () async { + final game = _game(); + final taken = await takePhoto( + renderer: game.renderer, + scene: game.scene, + camera: game.camera, + width: 150, + height: 70, + tileWidth: 0, + tileHeight: 32, + shelf: FilePhotoShelf(appName: 'game', directory: folder), + name: 'shot.png', + ); + // Mutation: drop the `abandon` in `takePhoto`'s catch. A `.part` holding + // the PNG's header is left in the player's Pictures folder for every + // capture that failed. + expect(taken.saved.kept, isFalse); + expect(taken.saved.message, contains('cannot cover a picture')); + expect(folder.listSync(), isEmpty); + }); + + test('a desktop keeps photos in Pictures, a phone in the game\'s folder', () { + String? where(TargetPlatform platform, Map environment) => + picturesDirectory( + appName: 'game', + platform: platform, + environment: environment, + temporary: '/data/user/0/dev.game/cache', + ); + expect( + where(TargetPlatform.linux, {'HOME': '/home/p'}), + '/home/p/Pictures/game', + ); + expect( + where(TargetPlatform.windows, {'USERPROFILE': r'C:\Users\p'}), + r'C:\Users\p\Pictures\game', + ); + // Mutation: put Android under `$HOME/Pictures` like Linux. `HOME` there is + // not the application's, the write fails, and no photo is ever kept. + expect( + where(TargetPlatform.android, {'HOME': '/'}), + '/data/user/0/dev.game/files/game/photos', + ); + expect(where(TargetPlatform.windows, const {}), isNull); + }); +} diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index e34802847..1df36d129 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -76,6 +76,17 @@ `fogHeightFalloff` and `fogBaseHeight` and blends them with the rest. Particles and splats fog as a flat fog as thick as the air at the camera. +- **A photo of any size.** `capturePhoto` draws a picture in tiles on the + game's own renderer and hands it out a row of tiles at a time, so memory + holds one row however large the picture. Each tile is drawn with a margin + of picture round it through the new `CropProjection`, which takes the + frame's aspect itself rather than the tile's, and the margin is cropped: + with 32 pixels a bloom's seams drop from 53 steps to a few. Exposure is held + at what the screen showed; temporal anti-aliasing, motion blur, render scale + and chromatic aberration are set aside, and the report says which. + `PhotoFilter` is eight looks composed onto the game's own `LookSettings`; + `PhotoFinish` puts the vignette and the grain back over the whole frame + rather than once per tile. - **A reload reaches the contributors too.** `Renderer.relinkShaders` asks every `PassContributor` to drop what it linked, through the new `PassContributor.relinkShaders`, which does nothing by default. diff --git a/packages/flutter3d_core/lib/flutter3d_core.dart b/packages/flutter3d_core/lib/flutter3d_core.dart index 0f814170d..b2e903dad 100644 --- a/packages/flutter3d_core/lib/flutter3d_core.dart +++ b/packages/flutter3d_core/lib/flutter3d_core.dart @@ -95,6 +95,7 @@ export 'src/engine/render/mesh_overlay.dart'; export 'src/engine/render/mirror_view.dart'; export 'src/engine/render/pass_contribution.dart'; export 'src/engine/render/pass_contributor.dart'; +export 'src/engine/render/photo_capture.dart'; export 'src/engine/render/physical_sky.dart' show PhysicalSky; export 'src/engine/render/probe_faces.dart'; export 'src/engine/render/procedural_texture.dart'; diff --git a/packages/flutter3d_core/lib/src/engine/render/photo_capture.dart b/packages/flutter3d_core/lib/src/engine/render/photo_capture.dart new file mode 100644 index 000000000..ba4e48be4 --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/render/photo_capture.dart @@ -0,0 +1,438 @@ +/// Photo mode's picture: a frame of any size, drawn in tiles, finished and +/// handed out a strip at a time — `N8`. +/// +/// **Strips rather than a frame, because "any resolution" is a promise about +/// memory.** A 16384 × 16384 picture is a gigabyte of RGBA, which a phone does +/// not have and a browser tab will not be given. Drawn a row of tiles at a +/// time and handed to a sink that writes it out — `PngStripWriter` in +/// `flutter3d_cpu` is the one this was written for — the most this holds is +/// one row of tiles, whatever the size of the picture. +/// +/// **Everything that knows where the frame's edge is happens here, over the +/// whole frame, and not in the tiles.** A tile does not know it is a tile: its +/// composite would darken its own corners for a vignette and lay its own grain +/// from its own origin, and the stitched picture would have a vignette per +/// tile. So those two are taken out of the tiles' settings and put back by +/// [PhotoFinish], which mirrors the composite's arithmetic at the frame's own +/// pixel coordinates. +library; + +import 'dart:async'; +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:vector_math/vector_math.dart' as vm; + +import '../../formats/srgb.dart'; +import '../scene/camera_node.dart'; +import '../scene/projection.dart'; +import '../scene/scene.dart'; +import 'render_view.dart'; +import 'renderer.dart'; + +/// A look a player picks in photo mode. +/// +/// **A change to the game's own [LookSettings] rather than a pass of its +/// own**, so every filter is drawn by the composite every backend already has +/// and the CPU backend already mirrors — four backends agreeing about a filter +/// costs nothing new. The colour parts work per pixel and so survive tiling as +/// they are; the vignette and the grain do not, and [PhotoFinish] puts them +/// back over the whole frame. +/// +/// **Composed onto the game's look, not put in its place**: a game graded +/// cold for a night level stays cold under [warm], only less so. Contrast and +/// saturation multiply, temperature and tint add, the vignette and the grain +/// take the larger of the two, a gain multiplies channel by channel, and a +/// lift replaces the game's because two lifts do not compose into anything a +/// person chose. +final class PhotoFilter { + const PhotoFilter({ + required this.name, + this.contrast = 1.0, + this.saturation = 1.0, + this.temperature = 0.0, + this.tint = 0.0, + this.vignette = 0.0, + this.grain = 0.0, + this.lift, + this.gain, + }); + + /// What a menu calls it; also how a game saves which one was last picked. + final String name; + + final double contrast; + final double saturation; + final double temperature; + final double tint; + final double vignette; + final double grain; + final vm.Vector3? lift; + final vm.Vector3? gain; + + static const PhotoFilter none = PhotoFilter(name: 'none'); + static const PhotoFilter vivid = PhotoFilter( + name: 'vivid', + contrast: 1.15, + saturation: 1.35, + ); + static const PhotoFilter warm = PhotoFilter( + name: 'warm', + temperature: 0.35, + saturation: 1.05, + ); + static const PhotoFilter cool = PhotoFilter( + name: 'cool', + temperature: -0.35, + tint: -0.05, + ); + static const PhotoFilter mono = PhotoFilter( + name: 'mono', + saturation: 0.0, + contrast: 1.1, + ); + + /// Mono with a brown gain — the print rather than the negative. + static final PhotoFilter sepia = PhotoFilter( + name: 'sepia', + saturation: 0.0, + contrast: 0.95, + gain: vm.Vector3(1.08, 0.96, 0.78), + lift: vm.Vector3(0.03, 0.02, 0.0), + vignette: 0.25, + ); + static const PhotoFilter noir = PhotoFilter( + name: 'noir', + saturation: 0.0, + contrast: 1.45, + vignette: 0.55, + grain: 0.04, + ); + static final PhotoFilter faded = PhotoFilter( + name: 'faded', + contrast: 0.85, + saturation: 0.75, + lift: vm.Vector3.all(0.06), + ); + + /// The order a menu shows them in. + static final List all = [ + none, + vivid, + warm, + cool, + mono, + sepia, + noir, + faded, + ]; + + /// The filter called [name], or [none] for a name no filter has — a saved + /// choice from a build that had a filter this one does not. + static PhotoFilter named(String name) => + all.firstWhere((filter) => filter.name == name, orElse: () => none); + + /// [look] with this filter on it, for the live view. + LookSettings applyTo(LookSettings look) => look.copyWith( + contrast: look.contrast * contrast, + saturation: look.saturation * saturation, + temperature: (look.temperature + temperature).clamp(-1.0, 1.0), + tint: (look.tint + tint).clamp(-1.0, 1.0), + vignette: math.max(look.vignette, vignette), + grain: math.max(look.grain, grain), + lift: lift ?? look.lift, + gain: switch ((look.gain, gain)) { + (final a?, final b?) => vm.Vector3(a.x * b.x, a.y * b.y, a.z * b.z), + (final a, final b) => b ?? a, + }, + ); +} + +/// The vignette and the grain, put over a finished frame at its own +/// coordinates. +/// +/// **The composite's arithmetic, moved after the encode.** The composite +/// darkens linear light by `1 − vignette · r` and then encodes; here the +/// encoded byte is decoded, darkened by the same factor and encoded again, +/// which is the same picture to within the rounding of one extra trip through +/// eight bits. The grain is the composite's own hash of the pixel's position, +/// added after the encode as the composite adds it — at the frame's position +/// rather than the tile's, which is the whole reason this exists. +final class PhotoFinish { + PhotoFinish({ + required this.width, + required this.height, + required this.vignette, + required this.roundness, + required this.grain, + }); + + /// What [look] asks of the frame's edges, for a frame [width] × [height]. + factory PhotoFinish.of(LookSettings look, int width, int height) => + PhotoFinish( + width: width, + height: height, + vignette: look.vignette.clamp(0.0, 1.0), + roundness: look.vignetteRoundness.clamp(0.0, 1.0), + grain: math.max(look.grain, 0.0), + ); + + final int width; + final int height; + final double vignette; + final double roundness; + final double grain; + + bool get isNeutral => vignette == 0.0 && grain == 0.0; + + static final Float64List _decode = Float64List.fromList( + List.generate(256, (i) => srgbToLinear(i / 255.0)), + ); + + /// Applies itself to [rows] rows of RGBA starting at frame row [top]. + void apply(Uint8List rgba, int top, int rows) { + if (isNeutral) return; + final aspect = width / height; + final stretch = 1.0 + (aspect - 1.0) * roundness; + for (var row = 0; row < rows; row++) { + final y = top + row; + final fy = (y + 0.5) / height - 0.5; + for (var x = 0; x < width; x++) { + final at = (row * width + x) * 4; + final fx = ((x + 0.5) / width - 0.5) * stretch; + final r = math.min(math.sqrt(fx * fx + fy * fy) * 1.41421356, 1.0); + final keep = 1.0 - vignette * r; + final noise = grain == 0.0 + ? 0.0 + : (_hash(x + 0.5, y + 0.5) - 0.5) * grain; + for (var c = 0; c < 3; c++) { + final encoded = vignette == 0.0 + ? rgba[at + c] / 255.0 + : linearToSrgb(_decode[rgba[at + c]] * keep); + rgba[at + c] = ((encoded + noise).clamp(0.0, 1.0) * 255.0).round(); + } + } + } + } + + /// `Hash` from `composite.frag`, as the software backend writes it. + static double _hash(double x, double y) { + final t = math.sin(x * 12.9898 + y * 78.233) * 43758.5453; + return t - t.floorToDouble(); + } +} + +/// What a capture did to the game's settings, and why. +/// +/// **Said rather than done quietly.** Each of these is a real difference +/// between the picture on screen and the picture saved, and a player who +/// notices one deserves a sentence rather than a bug report. +final class PhotoCaptureReport { + const PhotoCaptureReport({ + required this.width, + required this.height, + required this.tilesX, + required this.tilesY, + required this.margin, + required this.exposure, + required this.setAside, + }); + + final int width; + final int height; + final int tilesX; + final int tilesY; + + /// Pixels of picture drawn round each tile and cropped away. + final int margin; + + /// The exposure every tile was drawn at. + final double exposure; + + /// One sentence per setting the capture could not carry over. + final List setAside; +} + +const String _noTemporal = + 'temporal anti-aliasing is off: it resolves over frames a still tile ' + 'does not have'; +const String _noMotionBlur = + 'motion blur is off: the camera moved since the world stopped, and that ' + 'is not motion in the picture'; +const String _noAberration = + 'chromatic aberration is left out: it is centred on each tile, and no ' + 'whole-frame version is written yet'; +const String _noScale = + 'render scale and upscaling are off: the picture is drawn at its own size'; +const String _noExtended = + 'the extended-range output is written as standard range'; + +/// The settings every tile of a photo is drawn with, and what had to change. +/// +/// [exposure] is the one the game last drew with — `Renderer.exposure` — so +/// the picture is as bright as the screen was. A meter running per tile would +/// expose a tile of sky darker than a tile of shadow and the seams would show. +({RenderSettings tile, LookSettings look, List setAside}) photoSettings( + RenderSettings settings, + PhotoFilter filter, + double exposure, +) { + final look = filter.applyTo(settings.look); + final held = + 'auto exposure is held at ${exposure.toStringAsFixed(3)}, the value on ' + 'screen, so every tile is exposed alike'; + final setAside = [ + if (settings.autoExposure.enabled) held, + if (settings.antiAlias.temporal.enabled) _noTemporal, + if (settings.motionBlur.enabled) _noMotionBlur, + if (look.chromaticAberration > 0.0) _noAberration, + if (settings.renderScale != 1.0 || settings.spatialUpscale.enabled) + _noScale, + if (settings.outputTransform != OutputTransform.sdr) _noExtended, + ]; + final tile = settings.copyWith( + exposure: exposure, + autoExposure: const AutoExposureSettings(), + antiAlias: settings.antiAlias.copyWith(temporal: const TemporalSettings()), + motionBlur: settings.motionBlur.copyWith(enabled: false), + renderScale: 1.0, + spatialUpscale: const SpatialUpscaleSettings(), + outputTransform: OutputTransform.sdr, + look: look.copyWith(vignette: 0.0, grain: 0.0, chromaticAberration: 0.0), + ); + return (tile: tile, look: look, setAside: setAside); +} + +/// Draws [scene] through [camera] at [width] × [height] and hands it to +/// [onRows] a row of tiles at a time, top to bottom, finished and opaque. +/// +/// Each tile is [tileWidth] × [tileHeight] of the picture drawn with [margin] +/// pixels more on every side, through a [CropProjection] of [camera]'s own +/// projection; the margin is thrown away. **What the margin is for:** bloom, +/// ambient occlusion, depth of field and screen-space reflections read +/// neighbours, and at a tile's edge without one they read the clear colour — +/// a seam down the picture wherever a highlight sits near one. With a margin +/// wider than what they read, the neighbours are real. Bloom's widest levels +/// reach further than any margin, so a capture with bloom is close to the +/// screen rather than equal to it; `photo_capture_test.dart` measures how +/// close. +/// +/// The camera's projection is borrowed for the capture and put back after it, +/// failure included. The renderer is the game's own: a photo is drawn on the +/// device that is already up, at the size the game asks, so nothing about it +/// needs a second device. **Nothing else may draw on it until this returns.** +/// A hardware readback can resolve a frame or two after the ask, and a game +/// frame drawn in between takes its targets from the same pool as the tile +/// waiting to be read — so a game stops redrawing its surface while a photo +/// is taken, which it is free to do with the world paused. +/// +/// [onRows] gets `width × rows × 4` bytes for frame rows `top` to +/// `top + rows`, and is awaited before the next row of tiles is drawn, so a +/// sink writing to a disk holds this back rather than letting it run ahead. +Future capturePhoto({ + required Renderer renderer, + required Scene scene, + required CameraNode camera, + required int width, + required int height, + required FutureOr Function(Uint8List rgba, int top, int rows) onRows, + RenderSettings settings = const RenderSettings(), + PhotoFilter filter = PhotoFilter.none, + vm.Vector4? clearColor, + int layerMask = ~0, + int tileWidth = 1024, + int tileHeight = 1024, + int margin = 32, + void Function(double progress)? onProgress, +}) async { + if (width <= 0 || height <= 0) { + throw ArgumentError('A photo of ${width}x$height has no pixels to draw.'); + } + if (tileWidth <= 0 || tileHeight <= 0 || margin < 0) { + throw ArgumentError( + 'Tiles of ${tileWidth}x$tileHeight with a margin of $margin cannot ' + 'cover a picture; tiles need a size and a margin cannot be negative.', + ); + } + final tilesX = (width + tileWidth - 1) ~/ tileWidth; + final tilesY = (height + tileHeight - 1) ~/ tileHeight; + final chosen = photoSettings(settings, filter, renderer.exposure); + final finish = PhotoFinish.of(chosen.look, width, height); + final drawWidth = tileWidth + 2 * margin; + final drawHeight = tileHeight + 2 * margin; + + final whole = camera.projection; + try { + for (var tileY = 0; tileY < tilesY; tileY++) { + final top = tileY * tileHeight; + final rows = math.min(tileHeight, height - top); + final strip = Uint8List(width * rows * 4); + for (var tileX = 0; tileX < tilesX; tileX++) { + final left = tileX * tileWidth; + final columns = math.min(tileWidth, width - left); + camera.projection = CropProjection( + whole, + frameAspect: width / height, + left: (left - margin) / width, + right: (left + tileWidth + margin) / width, + top: (top - margin) / height, + bottom: (top + tileHeight + margin) / height, + ); + final result = renderer.render( + width: drawWidth, + height: drawHeight, + scene: scene, + views: [ + RenderView( + camera: camera, + clearColor: clearColor, + layerMask: layerMask, + cut: true, + ), + ], + settings: chosen.tile, + ); + final pixels = await renderer.device.readPixels(result.frame); + if (pixels == null) { + throw StateError( + 'Tile $tileX,$tileY of the photo could not be read back from the ' + 'device it was drawn on; nothing was written past row $top.', + ); + } + final tile = pixels.buffer.asUint8List( + pixels.offsetInBytes, + pixels.lengthInBytes, + ); + for (var row = 0; row < rows; row++) { + final from = ((row + margin) * drawWidth + margin) * 4; + strip.setRange( + (row * width + left) * 4, + (row * width + left + columns) * 4, + tile, + from, + ); + } + onProgress?.call((tileY * tilesX + tileX + 1) / (tilesX * tilesY)); + } + // Opaque: a saved photo with the sky see-through is a picture of the + // viewer's background, and a clear colour's alpha was never a choice + // about the picture. + for (var i = 3; i < strip.length; i += 4) { + strip[i] = 255; + } + finish.apply(strip, top, rows); + await onRows(strip, top, rows); + } + } finally { + camera.projection = whole; + } + return PhotoCaptureReport( + width: width, + height: height, + tilesX: tilesX, + tilesY: tilesY, + margin: margin, + exposure: renderer.exposure, + setAside: chosen.setAside, + ); +} diff --git a/packages/flutter3d_core/lib/src/engine/scene/projection.dart b/packages/flutter3d_core/lib/src/engine/scene/projection.dart index 0e19a12f4..f73baf8d2 100644 --- a/packages/flutter3d_core/lib/src/engine/scene/projection.dart +++ b/packages/flutter3d_core/lib/src/engine/scene/projection.dart @@ -420,6 +420,73 @@ final class TiledProjection extends Projection { } } +/// Any rectangle of a larger virtual frame — `N8`'s photo capture. +/// +/// [TiledProjection]'s crop of NDC space, with the rectangle given directly +/// rather than as a square of a grid: [left], [top], [right] and [bottom] are +/// fractions of the whole frame's width and height, rows counting down from +/// the top as an image's do. They may run outside `[0, 1]`, and that is the +/// point of the class: a photo tile is drawn with a margin of the picture +/// around it on every side — past the frame's own edge, for the outer tiles — +/// so a blur reaching across a tile's edge has something real to read there, +/// and the margin is cropped away afterwards. +/// +/// **The whole frame's aspect is held here, and the one [toMatrix] is handed +/// is ignored.** The renderer passes the viewport's own, which for a tile is +/// the tile's — and a tile with a margin round it is never the shape of the +/// frame. [TiledProjection] asks its caller to pass the frame's aspect +/// instead, which the renderer has no way to do. +final class CropProjection extends Projection { + const CropProjection( + this.base, { + required this.frameAspect, + required this.left, + required this.top, + required this.right, + required this.bottom, + }); + + final Projection base; + + /// Width over height of the whole frame the rectangle is a piece of. + final double frameAspect; + + final double left; + final double top; + final double right; + final double bottom; + + @override + double get near => base.near; + + @override + double get far => base.far; + + @override + double? get verticalFieldOfView => base.verticalFieldOfView; + + @override + Matrix4 toMatrix(double aspect) { + if (right <= left || bottom <= top) { + throw ArgumentError( + 'Degenerate crop: left/right are $left/$right and top/bottom are ' + '$top/$bottom; a crop needs some width and some height.', + ); + } + // The rectangle in NDC: x runs as the fractions do, y the other way. + final x0 = -1.0 + 2.0 * left; + final x1 = -1.0 + 2.0 * right; + final y0 = 1.0 - 2.0 * bottom; + final y1 = 1.0 - 2.0 * top; + final crop = Matrix4.identity() + ..setEntry(0, 0, 2.0 / (x1 - x0)) + ..setEntry(0, 3, -(x1 + x0) / (x1 - x0)) + ..setEntry(1, 1, 2.0 / (y1 - y0)) + ..setEntry(1, 3, -(y1 + y0) / (y1 - y0)); + return crop * base.toMatrix(frameAspect); + } +} + /// [base] moved a fraction of a pixel across the screen — `R1`. /// /// The same crop of NDC space [TiledProjection] makes, with a scale of one: diff --git a/packages/flutter3d_cpu/CHANGELOG.md b/packages/flutter3d_cpu/CHANGELOG.md index fcafaea3a..c2ba672e3 100644 --- a/packages/flutter3d_cpu/CHANGELOG.md +++ b/packages/flutter3d_cpu/CHANGELOG.md @@ -35,6 +35,11 @@ linear across the screen. No golden of this set moved. rounded to single precision as the GPU computes it, and **height fog in `applyFog`**, as `color.glsl` has it. +- **`PngStripWriter` writes a PNG a strip at a time**, one IDAT chunk per + strip and an empty final deflate block at `close`, keeping only the running + Adler-32 between strips. It refuses rows past the bottom and a `close` with + rows missing. `encodePng` is unchanged, byte for byte. + ## 0.8.2+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_cpu/lib/src/cpu_png.dart b/packages/flutter3d_cpu/lib/src/cpu_png.dart index 8f71eec7d..85ba6c6ae 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_png.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_png.dart @@ -6,6 +6,7 @@ library; import 'dart:convert'; +import 'dart:math' as math; import 'dart:typed_data'; /// [rgba] as a PNG: 8-bit RGBA, one IDAT, rows from the top. @@ -53,6 +54,127 @@ Uint8List encodePng(Uint8List rgba, int width, int height) { return out.toBytes(); } +/// A PNG written a strip of rows at a time — `N8`'s photo of any size. +/// +/// **One IDAT chunk per strip**, which the format allows outright: the +/// chunks' contents are concatenated into one zlib stream before anything +/// reads them. So each strip becomes stored deflate blocks in a chunk of its +/// own and goes to [sink] as soon as it arrives, and the writer keeps only +/// the running Adler-32 between strips — never the picture. +/// +/// **The last block is not known to be last until [close]**, and deflate +/// marks the final block in its header. Rather than hold back a block on the +/// chance it is the end, every block is written as not-final and [close] +/// adds an empty final block, five bytes, which every decoder reads. +/// +/// Not [encodePng] rewritten on top of this: that one writes a single IDAT, +/// and every golden in the tree was written by it. Byte-identical files are +/// worth more than one fewer function. +final class PngStripWriter { + /// Writes the signature and the header to [sink] at once. + PngStripWriter({ + required this.width, + required this.height, + required this.sink, + }) { + if (width <= 0 || height <= 0 || width > 0x7FFFFFFF ~/ 4) { + throw ArgumentError('A PNG of ${width}x$height cannot be written.'); + } + sink(Uint8List.fromList(const [137, 80, 78, 71, 13, 10, 26, 10])); + final header = ByteData(13) + ..setUint32(0, width) + ..setUint32(4, height) + ..setUint8(8, 8) + ..setUint8(9, 6); + _chunk('IHDR', header.buffer.asUint8List()); + } + + final int width; + final int height; + + /// Where the bytes go, in order. + final void Function(Uint8List bytes) sink; + + int _rowsWritten = 0; + int _adlerA = 1; + int _adlerB = 0; + bool _headerWritten = false; + + /// Adds [rows] rows of RGBA, top first, `width × rows × 4` bytes. + void addRows(Uint8List rgba, int rows) { + if (rgba.length < width * rows * 4) { + throw ArgumentError( + '$rows rows of $width pixels are ${width * rows * 4} bytes, and ' + '${rgba.length} were given.', + ); + } + if (_rowsWritten + rows > height) { + throw StateError( + 'The PNG is $height rows tall; $_rowsWritten are written and $rows ' + 'more would run past the bottom.', + ); + } + if (rows == 0) return; + final stride = width * 4 + 1; + final raw = Uint8List(rows * stride); + for (var y = 0; y < rows; y++) { + raw.setRange(y * stride + 1, (y + 1) * stride, rgba, y * width * 4); + } + for (final byte in raw) { + _adlerA = (_adlerA + byte) % 65521; + _adlerB = (_adlerB + _adlerA) % 65521; + } + final out = BytesBuilder(copy: false); + if (!_headerWritten) { + out.add(const [0x78, 0x01]); + _headerWritten = true; + } + for (var offset = 0; offset < raw.length; offset += 65535) { + final length = math.min(65535, raw.length - offset); + _storedBlock(out, Uint8List.sublistView(raw, offset, offset + length)); + } + _chunk('IDAT', out.toBytes()); + _rowsWritten += rows; + } + + /// Ends the stream and the file. Every row has to be in. + void close() { + if (_rowsWritten != height) { + throw StateError( + 'The PNG is $height rows tall and only $_rowsWritten were added; a ' + 'file closed now would be read as damaged.', + ); + } + final out = BytesBuilder(copy: false); + if (!_headerWritten) out.add(const [0x78, 0x01]); + out.add(const [1, 0, 0, 0xFF, 0xFF]); + out.add( + (ByteData( + 4, + )..setUint32(0, (_adlerB << 16) | _adlerA)).buffer.asUint8List(), + ); + _chunk('IDAT', out.toBytes()); + _chunk('IEND', Uint8List(0)); + } + + static void _storedBlock(BytesBuilder out, Uint8List data) { + final n = 0xFFFF - data.length; + out + ..add([0, data.length & 0xFF, (data.length >> 8) & 0xFF]) + ..add([n & 0xFF, (n >> 8) & 0xFF]) + ..add(data); + } + + void _chunk(String type, Uint8List data) { + final body = Uint8List(data.length + 4) + ..setRange(0, 4, ascii.encode(type)) + ..setRange(4, data.length + 4, data); + sink((ByteData(4)..setUint32(0, data.length)).buffer.asUint8List()); + sink(body); + sink((ByteData(4)..setUint32(0, _crc32(body))).buffer.asUint8List()); + } +} + final List _crcTable = List.generate(256, (n) { var c = n; for (var k = 0; k < 8; k++) { diff --git a/packages/flutter3d_cpu/test/photo_capture_test.dart b/packages/flutter3d_cpu/test/photo_capture_test.dart new file mode 100644 index 000000000..72d9bd953 --- /dev/null +++ b/packages/flutter3d_cpu/test/photo_capture_test.dart @@ -0,0 +1,308 @@ +/// Photo mode's capture — `N8`: a picture of any size drawn in tiles on the +/// game's own device, finished over the whole frame and written out a strip +/// at a time. +/// +/// dart test test/photo_capture_test.dart +/// +/// The reference every claim is held to is the same scene drawn as one frame +/// on one device, with the settings the capture says it used. +library; + +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:flutter3d_core/flutter3d_core.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +const int _width = 480; +const int _height = 360; + +/// Two cubes off the axes, one of them bright enough to bloom, so a tile edge +/// in the wrong place moves geometry and a tile edge with no margin cuts a +/// glow. +({Scene scene, CameraNode camera}) _scene(GraphicsDevice device) { + final scene = Scene() + ..add( + LightNode(type: LightType.directional, name: 'key') + ..intensity = 3.0 + ..setLocalForward(Vector3(-0.4, -1.0, -0.3)), + ) + ..add( + MeshNode( + DeviceMesh.upload(device, CuboidShape(size: Vector3.all(1.4)).build()), + Material( + lighting: LightingModel.pbr, + baseColor: Vector4(0.8, 0.3, 0.2, 1.0), + ), + name: 'a', + )..setPosition(0.6, 0.2, 0.0), + ) + ..add( + MeshNode( + DeviceMesh.upload(device, CuboidShape(size: Vector3.all(0.5)).build()), + Material( + lighting: LightingModel.unlit, + baseColor: Vector4(1.0, 1.0, 1.0, 1.0), + emissive: Vector3.all(12.0), + ), + name: 'lamp', + )..setPosition(-0.55, 0.15, 0.4), + ); + final camera = + CameraNode( + name: 'eye', + projection: const PerspectiveProjection(fovYRadians: 0.9), + ) + ..setPosition(2.2, 1.4, 3.4) + ..lookAt(Vector3.zero()); + scene.add(camera); + return (scene: scene, camera: camera); +} + +({Renderer renderer, Scene scene, CameraNode camera}) _game() { + final device = CpuDevice( + width: _width, + height: _height, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final built = _scene(device); + return ( + renderer: Renderer.create(device: device), + scene: built.scene, + camera: built.camera, + ); +} + +/// The scene as one frame, opaque, with [settings] as they stand. +Future _whole(RenderSettings settings) async { + final game = _game(); + final result = game.renderer.render( + width: _width, + height: _height, + scene: game.scene, + views: [RenderView(camera: game.camera)], + settings: settings, + ); + final pixels = (await game.renderer.device.readPixels( + result.frame, + ))!.buffer.asUint8List(); + for (var i = 3; i < pixels.length; i += 4) { + pixels[i] = 255; + } + return pixels; +} + +/// The capture, stitched back into one buffer. +Future<({Uint8List pixels, PhotoCaptureReport report, CameraNode camera})> +_capture( + RenderSettings settings, { + PhotoFilter filter = PhotoFilter.none, + int margin = 32, +}) async { + final game = _game(); + final out = Uint8List(_width * _height * 4); + final report = await capturePhoto( + renderer: game.renderer, + scene: game.scene, + camera: game.camera, + width: _width, + height: _height, + settings: settings, + filter: filter, + // Ragged on purpose: three columns of 200, 200 and 80, three rows of 152, + // 152 and 56. Multiples of four, so the composite's 4x4 dither cell lines + // up across tiles and is not a difference this file has to explain. + tileWidth: 200, + tileHeight: 152, + margin: margin, + onRows: (rgba, top, rows) => + out.setRange(top * _width * 4, (top + rows) * _width * 4, rgba), + ); + return (pixels: out, report: report, camera: game.camera); +} + +/// The worst difference in any channel, over the pixels within [band] of a +/// tile edge. +int _worstAtSeams(Uint8List a, Uint8List b, {int band = 3}) { + bool nearEdge(int v, int tile, int size) { + for (var edge = tile; edge < size; edge += tile) { + if ((v - edge).abs() <= band) return true; + } + return false; + } + + var worst = 0; + for (var y = 0; y < _height; y++) { + for (var x = 0; x < _width; x++) { + if (!nearEdge(x, 200, _width) && !nearEdge(y, 152, _height)) continue; + for (var c = 0; c < 3; c++) { + final i = (y * _width + x) * 4 + c; + final d = (a[i] - b[i]).abs(); + if (d > worst) worst = d; + } + } + } + return worst; +} + +const RenderSettings _noBloom = RenderSettings( + bloom: BloomSettings(enabled: false), +); + +void main() { + test('a ragged grid of tiles stitches into the whole frame', () async { + final capture = await _capture(_noBloom, margin: 0); + final reference = await _whole(_noBloom); + // Mutation: build the crop from the aspect the renderer hands + // `CropProjection.toMatrix` — the tile's — instead of `frameAspect`. The + // 200 x 152 tiles and the 80-pixel column each squash the cubes their own + // way, and thousands of pixels move. + // + // Within one step, as `tiled_projection_stitch_test.dart` explains: a + // crop folded into the matrix interpolates through different arithmetic. + final difference = compareFrames( + capture.pixels, + reference, + channel: 1, + alpha: true, + ); + expect(difference.differing, 0, reason: '$difference'); + }); + + test('a margin round each tile takes the seams out of a bloom', () async { + const bloom = RenderSettings( + bloom: BloomSettings(enabled: true, intensity: 0.6), + ); + final reference = await _whole(bloom); + final bare = await _capture(bloom, margin: 0); + final framed = await _capture(bloom, margin: 32); + final bareSeam = _worstAtSeams(bare.pixels, reference); + final framedSeam = _worstAtSeams(framed.pixels, reference); + // Mutation: crop the tile without the margin (`from` at row `row`, column + // 0). The glow is cut off at every tile edge it reaches, which is the + // seam with no margin at all. + expect( + framedSeam, + lessThan(bareSeam), + reason: 'seams: $bareSeam without a margin, $framedSeam with one', + ); + expect(framedSeam, lessThanOrEqualTo(6), reason: 'seam $framedSeam'); + }); + + test( + 'the vignette darkens the picture\'s corners, not every tile\'s', + () async { + final look = const RenderSettings( + bloom: BloomSettings(enabled: false), + ).copyWith(look: const LookSettings(vignette: 0.6)); + final capture = await _capture(look, margin: 0); + final reference = await _whole(look); + // Mutation: leave `vignette` in the tiles' look in `photoSettings`. Every + // tile darkens its own corners, and the picture has nine vignettes — the + // centre of the frame, a tile corner, comes out darker than its edges. + // + // Within two steps: the reference darkens linear light before the + // encode, the finish decodes a byte, darkens it and encodes again. + final difference = compareFrames( + capture.pixels, + reference, + channel: 2, + alpha: true, + ); + expect(difference.differing, 0, reason: '$difference'); + }, + ); + + test('a capture says what it could not carry over, and puts the camera ' + 'back', () async { + final settings = const RenderSettings( + bloom: BloomSettings(enabled: false), + autoExposure: AutoExposureSettings(enabled: true), + motionBlur: MotionBlurSettings(enabled: true), + ); + final capture = await _capture(settings, filter: PhotoFilter.noir); + // Mutation: drop the `finally` that restores the projection. The game's + // next frame is drawn through the last tile's crop — the bottom-right + // corner of the picture, magnified to fill the screen. + expect(capture.camera.projection, isA()); + expect(capture.report.tilesX, 3); + expect(capture.report.tilesY, 3); + expect( + capture.report.setAside.join('\n'), + allOf(contains('auto exposure is held'), contains('motion blur')), + ); + }); + + test('a filter composes onto the game\'s own look', () { + const game = LookSettings(saturation: 0.8, temperature: -0.2); + final graded = PhotoFilter.warm.applyTo(game); + // Mutation: replace the game's look with the filter's. A night level + // graded cold turns orange under "warm" rather than a little less cold. + expect(graded.temperature, closeTo(0.15, 1e-12)); + expect(graded.saturation, closeTo(0.84, 1e-12)); + expect(PhotoFilter.named('noir'), same(PhotoFilter.noir)); + expect(PhotoFilter.named('gone in this build'), same(PhotoFilter.none)); + }); + + group('PngStripWriter', () { + Uint8List gradient(int width, int height) { + final out = Uint8List(width * height * 4); + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + final i = (y * width + x) * 4; + out + ..[i] = x * 7 & 0xFF + ..[i + 1] = y * 11 & 0xFF + ..[i + 2] = (x ^ y) & 0xFF + ..[i + 3] = 255 - x; + } + } + return out; + } + + test('three strips read back as one picture, checksum and all', () { + const width = 37; + const height = 23; + final pixels = gradient(width, height); + final file = BytesBuilder(); + final writer = PngStripWriter( + width: width, + height: height, + sink: file.add, + ); + for (final (top, rows) in const [(0, 10), (10, 1), (11, 12)]) { + writer.addRows(Uint8List.sublistView(pixels, top * width * 4), rows); + } + writer.close(); + final bytes = file.toBytes(); + + final decoded = decodePng(bytes)!; + expect(decoded.width, width); + expect(decoded.rgba, pixels); + + // The decoder above does not check the Adler-32; zlib does. Mutation: + // fold the filter bytes out of the running checksum, and a picture + // that decodes perfectly is refused by every browser. + final idat = BytesBuilder(); + var at = 8; + while (at < bytes.length) { + final length = ByteData.sublistView(bytes, at).getUint32(0); + final type = String.fromCharCodes(bytes.sublist(at + 4, at + 8)); + if (type == 'IDAT') idat.add(bytes.sublist(at + 8, at + 8 + length)); + at += 12 + length; + } + expect(ZLibDecoder().convert(idat.toBytes()), hasLength(height * 149)); + }); + + test('a file closed short is refused rather than written damaged', () { + final writer = PngStripWriter(width: 4, height: 4, sink: (_) {}); + writer.addRows(Uint8List(4 * 2 * 4), 2); + // Mutation: let `close` write the end with rows missing. The file is a + // valid-looking PNG that every reader rejects at the end of the data. + expect(writer.close, throwsStateError); + expect(() => writer.addRows(Uint8List(4 * 3 * 4), 3), throwsStateError); + }); + }); +} diff --git a/packages/flutter3d_sim/CHANGELOG.md b/packages/flutter3d_sim/CHANGELOG.md index e07f340fd..4e99b88cf 100644 --- a/packages/flutter3d_sim/CHANGELOG.md +++ b/packages/flutter3d_sim/CHANGELOG.md @@ -69,6 +69,14 @@ and marks where runs were lost. Its JSON is the playtest report's, so the editor reads both. +- **Photo mode's camera.** `PhotoCamera` flies with the world paused: look, + tilt, zoom, and moves along its own axes with up being the world's. It is + held on a tether round where the player stood, inside the level's box when + there is one, and out of the walls by sweeping each move and sliding along + what it meets. It starts by sweeping out from the player to where the game's + camera was, so a chase camera left behind a wall does not start the photo + there. `shouldPause` takes `photoMode`, which pauses whatever the pointer and + the pad say, since both are flying the camera. - **A number tuned while the game runs is on the tape.** `InputState.tune` sets a tunable for one step, `InputFrame.tunes` records it, playback applies it, and `Tunables` is the step's side: named values with defaults, diff --git a/packages/flutter3d_sim/lib/flutter3d_sim.dart b/packages/flutter3d_sim/lib/flutter3d_sim.dart index a83d66b72..7ffbbeb19 100644 --- a/packages/flutter3d_sim/lib/flutter3d_sim.dart +++ b/packages/flutter3d_sim/lib/flutter3d_sim.dart @@ -51,6 +51,7 @@ export 'src/actors/brain.dart'; export 'src/actors/damageable.dart'; export 'src/actors/health.dart'; export 'src/camera/camera_rig.dart'; +export 'src/camera/photo_camera.dart'; export 'src/camera/rig_tuning.dart'; export 'src/ecs/ecs_world.dart'; export 'src/ecs/entity.dart'; diff --git a/packages/flutter3d_sim/lib/src/camera/photo_camera.dart b/packages/flutter3d_sim/lib/src/camera/photo_camera.dart new file mode 100644 index 000000000..b4288a92b --- /dev/null +++ b/packages/flutter3d_sim/lib/src/camera/photo_camera.dart @@ -0,0 +1,292 @@ +import 'dart:math' as math; + +import 'package:flutter3d_physics/flutter3d_physics.dart'; +import 'package:vector_math/vector_math.dart'; + +import '../physics/layers.dart'; + +/// The camera a paused game hands the player for taking a picture — `N8`. +/// +/// Flown rather than chased: it has a position, a heading, a pitch, a roll and +/// a field of view, and nothing it watches. What makes it more than a debug +/// fly-camera is where it is **not allowed to go**, which is the whole of the +/// design. +/// +/// ## Held inside the level, three ways +/// +/// * **On a tether** of [reach] metres from [anchor] — where the player was +/// standing when photo mode opened. A camera that can fly anywhere shows the +/// back of every wall, the skybox seam and the half-built room past the end +/// of the level, and a player who posts that has posted a bug report. +/// * **Inside [bounds]**, when the game has them — the level's own box, so the +/// tether cannot reach under the floor of a level built on a slab. +/// * **Out of the walls**: every move is a sweep of a small box through the +/// collision world against [wallMask], sliding along what it meets the way +/// the player does. The tether alone would stop at the right distance on the +/// wrong side of a wall. +/// +/// The first two together are a sphere cut by a box, which is convex, and that +/// is what makes the third safe to combine with them: from a point inside the +/// region, a straight line to another point inside it never leaves it. The +/// target of every move is pulled into the region first and then swept to. +/// A slide along one wall only pushes the point back towards the side it came +/// from, which keeps it inside; the skin step off each wall and a corner of +/// three faces are not held by that argument, so a slid point is checked, and +/// one that has left the region is thrown away and the move made again +/// straight, stopping short at the first wall. +/// +/// **Not in the step, and not in `Portable` maths.** The simulation is paused +/// while this flies, and nothing it does reaches a snapshot or a tape. A +/// photo is the one camera in a game that is allowed to be non-deterministic. +final class PhotoCamera { + PhotoCamera({ + required this.world, + this.reach = 12.0, + this.bounds, + this.radius = 0.2, + this.wallMask = CollisionLayers.world, + this.speed = 4.0, + this.minFieldOfView = 0.17, + this.maxFieldOfView = 1.75, + this.maxRoll = math.pi / 4.0, + }) : assert(reach > 0.0, 'a tether is a distance'), + assert(radius > 0.0, 'a camera with no size touches nothing'), + assert( + minFieldOfView > 0.0 && maxFieldOfView > minFieldOfView, + 'a zoom range runs from a narrow view to a wider one', + ); + + /// Where the walls are. + final CollisionWorld world; + + /// How far from [anchor] the camera may go, in metres. + final double reach; + + /// The level's own box, or null where the game has none to give. + final Aabb3? bounds; + + /// Half the side of the box swept through the world. A near plane has to + /// stay this far in front of a wall or it cuts into it. + final double radius; + + /// Which layers stop the camera. + final int wallMask; + + /// Metres a second at full stick. + final double speed; + + /// How far [zoom] may go each way, in radians of vertical field of view — + /// about ten degrees to a hundred. + final double minFieldOfView; + final double maxFieldOfView; + + /// How far the horizon may be tilted either way. Past forty-five degrees a + /// picture stops reading as tilted and starts reading as rotated. + final double maxRoll; + + final Vector3 _anchor = Vector3.zero(); + final Vector3 _eye = Vector3.zero(); + double _yaw = 0.0; + double _pitch = 0.0; + double _roll = 0.0; + double _fieldOfView = 0.9; + + /// Where the player was when this began; the centre of the tether. + Vector3 get anchor => _anchor; + + /// Where the camera is. + Vector3 get eye => _eye; + + /// Heading about +Y, in radians; zero looks down −Z. + double get yaw => _yaw; + + /// Up is positive, held short of straight up or down, where heading stops + /// meaning anything and `lookAt` has no way to tell which way is up. + double get pitch => _pitch; + + /// Tilt of the horizon; positive leans the top of the camera to the right. + double get roll => _roll; + + /// Vertical field of view, in radians. + double get fieldOfView => _fieldOfView; + + /// The unit vector the camera looks along. + Vector3 get forward => Vector3( + -math.sin(_yaw) * math.cos(_pitch), + math.sin(_pitch), + -math.cos(_yaw) * math.cos(_pitch), + ); + + /// A point one metre ahead, for `CameraNode.lookAt`. + Vector3 get target => _eye + forward; + + /// The camera's up, turned by [roll] — the vector `lookAt` wants. + Vector3 get up { + final ahead = forward; + final right = ahead.cross(Vector3(0.0, 1.0, 0.0))..normalize(); + final level = right.cross(ahead)..normalize(); + return level * math.cos(_roll) + right * math.sin(_roll); + } + + /// Takes over from the game's own camera, wherever it was. + /// + /// [anchor] is the tether's centre — the player, not the camera: a chase + /// camera already sits several metres back, and a tether centred on it lets + /// the player see a little less on one side of themselves than the other. + /// + /// **The eye is reached from the anchor, not set.** A chase camera can be + /// further out than [reach], and a third-person camera pulled in by a wall + /// can be half a centimetre from it; either way the starting point is swept + /// to from where the player stands, so it begins inside the region and out of + /// the walls like every point after it. + void begin({ + required Vector3 eye, + required Vector3 target, + required Vector3 anchor, + double? fieldOfView, + }) { + _anchor.setFrom(anchor); + final ahead = target - eye; + if (ahead.length2 > 1e-12) { + ahead.normalize(); + _yaw = math.atan2(-ahead.x, -ahead.z); + _pitch = _clampPitch(math.asin(ahead.y.clamp(-1.0, 1.0))); + } + _roll = 0.0; + if (fieldOfView != null) { + _fieldOfView = fieldOfView.clamp(minFieldOfView, maxFieldOfView); + } + _eye.setFrom(_anchor); + _sweepStraight(_eye, _allowed(eye) - _eye); + } + + /// Turns the camera by [yaw] and [pitch] radians. + void look(double yaw, double pitch) { + _yaw += yaw; + _pitch = _clampPitch(_pitch + pitch); + } + + /// Tilts the horizon by [radians], within [maxRoll]. + void tilt(double radians) => + _roll = (_roll + radians).clamp(-maxRoll, maxRoll); + + /// Narrows the view by [factor] — above one zooms in. + void zoom(double factor) { + if (factor <= 0.0) return; + _fieldOfView = (_fieldOfView / factor).clamp( + minFieldOfView, + maxFieldOfView, + ); + } + + /// Flies for [dt] seconds along [intent], in the camera's own axes: x to the + /// right, y up the world, z forward. A length above one is cut to one. + /// + /// **Up is the world's, not the camera's.** A camera pitched down that rose + /// along its own up would drift backwards as it climbed, and a player + /// lining up a shot reads that as the controls fighting them. + /// + /// Returns whether anything held the camera back — a wall, the tether or the + /// level's box — so a game can say why the camera stopped. + bool fly(Vector3 intent, double dt) { + final length = intent.length; + if (length == 0.0 || dt <= 0.0) return false; + final scale = speed * dt / math.max(1.0, length); + final ahead = Vector3(-math.sin(_yaw), 0.0, -math.cos(_yaw)); + final right = Vector3(math.cos(_yaw), 0.0, -math.sin(_yaw)); + final delta = + (right * intent.x + + Vector3(0.0, intent.y, 0.0) + + (ahead * math.cos(_pitch) + Vector3(0.0, math.sin(_pitch), 0.0)) * + intent.z) + ..scale(scale); + return moveBy(delta); + } + + /// Moves the eye by [delta] in world space, held inside the level. + /// + /// Returns whether anything held it back. + bool moveBy(Vector3 delta) { + final wanted = _eye + delta; + final allowed = _allowed(wanted); + final clipped = allowed.distanceToSquared(wanted) > 1e-12; + + final slid = _eye.clone(); + final blocked = _slide(slid, allowed - _eye); + if (_isAllowed(slid)) { + _eye.setFrom(slid); + return clipped || blocked; + } + // The slide carried the move out of the region. Straight is inside it, by + // convexity; see the class comment. + _sweepStraight(_eye, allowed - _eye); + return true; + } + + double _clampPitch(double pitch) => pitch.clamp(-1.5, 1.5); + + /// [point] pulled into the tether and the box. + Vector3 _allowed(Vector3 point) { + final out = point.clone(); + final box = bounds; + if (box != null) { + out + ..x = out.x.clamp(box.min.x + radius, box.max.x - radius) + ..y = out.y.clamp(box.min.y + radius, box.max.y - radius) + ..z = out.z.clamp(box.min.z + radius, box.max.z - radius); + } + final away = out - _anchor; + final distance = away.length; + if (distance > reach) { + out + ..setFrom(_anchor) + ..addScaled(away, reach / distance); + } + return out; + } + + bool _isAllowed(Vector3 point) => + _allowed(point).distanceToSquared(point) <= _tolerance * _tolerance; + + /// The few millimetres a skin step off a wall may carry the eye past the + /// tether. Below anything that could take it through a wall. + static const double _tolerance = 0.01; + + static const double _skin = 0.001; + + late final CollisionShape _shape = CollisionSphere(radius); + final SweepHit _hit = SweepHit(); + + /// The character controller's slide, three passes for a room's corner. + bool _slide(Vector3 point, Vector3 delta) { + var blocked = false; + for (var pass = 0; pass < 3; pass++) { + if (delta.length2 == 0.0) break; + if (!world.sweep(_shape, point, delta, _hit, mask: wallMask)) { + point.add(delta); + break; + } + blocked = true; + final travel = math.max(0.0, _hit.fraction); + point + ..addScaled(delta, travel) + ..addScaled(_hit.normal, _skin); + delta.scale(1.0 - travel); + final into = delta.dot(_hit.normal); + if (into < 0.0) delta.addScaled(_hit.normal, -into); + } + return blocked; + } + + /// One sweep, no slide: the move stops where it meets something. + void _sweepStraight(Vector3 point, Vector3 delta) { + if (delta.length2 == 0.0) return; + if (!world.sweep(_shape, point, delta, _hit, mask: wallMask)) { + point.add(delta); + return; + } + point + ..addScaled(delta, math.max(0.0, _hit.fraction)) + ..addScaled(_hit.normal, _skin); + } +} diff --git a/packages/flutter3d_sim/lib/src/loop/pause_gate.dart b/packages/flutter3d_sim/lib/src/loop/pause_gate.dart index 4af305fc4..2510c386a 100644 --- a/packages/flutter3d_sim/lib/src/loop/pause_gate.dart +++ b/packages/flutter3d_sim/lib/src/loop/pause_gate.dart @@ -16,12 +16,19 @@ /// So it has a name and a test now. It is a function of four facts and nothing /// else, which is what makes it one: every previous version reached for a device /// and answered a question about the *player's attention* with it. +/// +/// [photoMode] is a fifth, and a separate one rather than a menu that happens +/// to be open: photo mode is the one state in which the player is looking hard +/// at the game and moving every stick — so every device clause below says +/// *run*, and a game that folded it into [menuOpen] would have to remember not +/// to close that "menu" when the pointer is captured again to fly the camera. bool shouldPause({ required bool ready, required bool menuOpen, required bool pointerIsTheGate, required bool pointerHeld, required bool padConnected, + bool photoMode = false, }) { // Nothing to run yet: the level is still loading, or failed to. if (!ready) return true; @@ -30,6 +37,10 @@ bool shouldPause({ // depend on any device. This is the clause that was missing. if (menuOpen) return true; + // The picture is being framed, so the world holds still for it — whatever + // the pointer and the pad say, since both are flying the camera. + if (photoMode) return true; + // Where the pointer can be captured, not having it means the player is // somewhere else — that is what Escape does and what clicking back in undoes. // A controller is a second way of being here, and it is enough on its own. diff --git a/packages/flutter3d_sim/test/pause_gate_test.dart b/packages/flutter3d_sim/test/pause_gate_test.dart index 8f1c2232c..8928e555d 100644 --- a/packages/flutter3d_sim/test/pause_gate_test.dart +++ b/packages/flutter3d_sim/test/pause_gate_test.dart @@ -76,4 +76,22 @@ void main() { reason: 'the panel never paused a browser or a phone at all', ); }); + + test('photo mode holds the world still while every device flies the camera', () { + // Mutation: drop the `photoMode` clause. The pointer is captured and the + // pad is in hand, because both are moving the camera, so every clause after + // it says run — and the picture is of a level carrying on without the + // player in it. + bool framing({required bool pointerIsTheGate}) => shouldPause( + ready: true, + menuOpen: false, + pointerIsTheGate: pointerIsTheGate, + pointerHeld: true, + padConnected: true, + photoMode: true, + ); + expect(framing(pointerIsTheGate: true), isTrue); + expect(framing(pointerIsTheGate: false), isTrue); + expect(desktop(), isFalse, reason: 'and closing it lets the game go on'); + }); } diff --git a/packages/flutter3d_sim/test/photo_camera_test.dart b/packages/flutter3d_sim/test/photo_camera_test.dart new file mode 100644 index 000000000..5042f1d3e --- /dev/null +++ b/packages/flutter3d_sim/test/photo_camera_test.dart @@ -0,0 +1,204 @@ +/// The photo-mode camera, and the three ways it is held inside the level — +/// `N8`. +/// +/// dart test test/photo_camera_test.dart +/// +/// A free camera is easy; the claims here are about where it cannot go. Each +/// test names the line whose removal would let it through. +library; + +import 'dart:math' as math; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +/// A closed room twenty metres square and six high, walls half a metre thick, +/// with a pillar in the middle of its −Z half. The boxes are kept so a test can +/// ask whether a point is inside one without going through the code under test. +({CollisionWorld world, List solids}) _room() { + final world = CollisionWorld(); + final solids = []; + void box(Vector3 centre, Vector3 size) { + world.addBox(centre, size); + solids.add(Aabb3.centerAndHalfExtents(centre, size / 2.0)); + } + + box(Vector3(0.0, -0.25, 0.0), Vector3(21.0, 0.5, 21.0)); // floor + box(Vector3(0.0, 6.25, 0.0), Vector3(21.0, 0.5, 21.0)); // ceiling + box(Vector3(10.25, 3.0, 0.0), Vector3(0.5, 6.0, 21.0)); + box(Vector3(-10.25, 3.0, 0.0), Vector3(0.5, 6.0, 21.0)); + box(Vector3(0.0, 3.0, 10.25), Vector3(21.0, 6.0, 0.5)); + box(Vector3(0.0, 3.0, -10.25), Vector3(21.0, 6.0, 0.5)); + box(Vector3(0.0, 3.0, -5.0), Vector3(2.0, 6.0, 2.0)); // pillar + world.update(); + return (world: world, solids: solids); +} + +bool _inside(List solids, Vector3 point) => + solids.any((box) => box.containsVector3(point)); + +void main() { + test('begins looking where the game camera looked', () { + final room = _room(); + final camera = PhotoCamera(world: room.world) + ..begin( + eye: Vector3(0.0, 2.0, 6.0), + target: Vector3(3.0, 1.0, 2.0), + anchor: Vector3(0.0, 1.0, 4.0), + ); + final wanted = (Vector3(3.0, 1.0, 2.0) - Vector3(0.0, 2.0, 6.0)) + ..normalize(); + // Mutation: swap the arguments of the `atan2` in `begin`. The camera opens + // ninety degrees off the shot the player paused to take. + expect(camera.forward.distanceTo(wanted), lessThan(1e-6)); + expect(camera.eye.distanceTo(Vector3(0.0, 2.0, 6.0)), lessThan(1e-6)); + }); + + test('a chase camera behind a wall starts on the player\'s side of it', () { + final room = _room(); + // The player stands just in front of the pillar, and the game's camera has + // been left on its far side — where a chase camera ends up for a frame + // when the player turns sharply. + final camera = PhotoCamera(world: room.world) + ..begin( + eye: Vector3(0.0, 2.0, -8.0), + target: Vector3(0.0, 2.0, 0.0), + anchor: Vector3(0.0, 2.0, -2.0), + ); + // Mutation: `_eye.setFrom(eye)` in `begin` instead of sweeping to it from + // the anchor. The first picture is taken from inside the level's far side + // of a wall the player is standing in front of. + expect(camera.eye.z, greaterThan(-4.0 + 0.2 - 1e-3)); + expect(_inside(room.solids, camera.eye), isFalse); + }); + + test('flying into a wall stops in front of it', () { + final room = _room(); + final camera = PhotoCamera(world: room.world, reach: 50.0) + ..begin( + eye: Vector3(0.0, 2.0, 4.0), + target: Vector3(0.0, 2.0, 20.0), + anchor: Vector3(0.0, 2.0, 4.0), + ); + for (var i = 0; i < 300; i++) { + camera.fly(Vector3(0.0, 0.0, 1.0), 1.0 / 60.0); + } + // Mutation: `point.add(delta)` in place of the sweep in `_slide`. Five + // seconds at four metres a second carries the eye through the wall at + // z = 10 and out of the level. + expect(camera.eye.z, lessThanOrEqualTo(10.0 - 0.2 + 1e-2)); + expect(camera.eye.z, greaterThan(9.0), reason: 'it did get there'); + }); + + test('flying along a wall at a slant slides rather than sticks', () { + final room = _room(); + final camera = PhotoCamera(world: room.world, reach: 50.0) + ..begin( + eye: Vector3(-5.0, 2.0, 9.5), + target: Vector3(-5.0, 2.0, 20.0), + anchor: Vector3(-5.0, 2.0, 9.5), + ); + camera.look(math.pi / 4.0, 0.0); + for (var i = 0; i < 60; i++) { + camera.fly(Vector3(0.0, 0.0, 1.0), 1.0 / 60.0); + } + // Mutation: stop at the first hit in `_slide` rather than keeping the part + // of the move that runs along the wall. A camera pressed against a wall at + // forty-five degrees then freezes, and lining up a shot along a corridor + // is impossible. + expect(camera.eye.x, greaterThan(-5.0 + 2.0)); + expect(camera.eye.z, lessThanOrEqualTo(10.0 - 0.2 + 1e-2)); + }); + + test('the tether holds the camera within reach of the player', () { + final room = _room(); + final camera = PhotoCamera(world: room.world, reach: 6.0) + ..begin( + eye: Vector3(5.0, 2.0, 5.0), + target: Vector3(-5.0, 2.0, 5.0), + anchor: Vector3(5.0, 1.0, 5.0), + ); + for (var i = 0; i < 600; i++) { + camera.fly(Vector3(0.3, 0.2, 1.0), 1.0 / 60.0); + } + // Mutation: drop the reach clamp in `_allowed`. Ten seconds of flying + // takes the camera to the room's far wall, eleven metres from the player. + expect(camera.eye.distanceTo(camera.anchor), lessThanOrEqualTo(6.0 + 1e-2)); + expect( + camera.fly(Vector3(0.0, 0.0, 1.0), 1.0 / 60.0), + isTrue, + reason: 'and says it was held, so a game can show the edge', + ); + }); + + test('the level box holds the camera where no wall does', () { + // No ceiling and no floor in this world, only the box — a level on a slab + // with open sky above it. + final world = CollisionWorld()..update(); + final camera = + PhotoCamera( + world: world, + reach: 100.0, + bounds: Aabb3.minMax( + Vector3(-4.0, 0.0, -4.0), + Vector3(4.0, 3.0, 4.0), + ), + )..begin( + eye: Vector3(0.0, 1.0, 0.0), + target: Vector3(0.0, -1.0, 0.0), + anchor: Vector3(0.0, 1.0, 0.0), + ); + for (var i = 0; i < 120; i++) { + camera.fly(Vector3(0.0, -1.0, 0.0), 1.0 / 60.0); + } + // Mutation: drop the box clamp in `_allowed`. The camera goes under the + // level and photographs the underside of the floor. + expect(camera.eye.y, greaterThanOrEqualTo(0.0 + 0.2 - 1e-9)); + }); + + test('five hundred random moves in a furnished room never leave it', () { + // The property `CameraRig` was held to, three hundred random impulses in a + // boxed room; this is the same check for a camera the player steers. + final room = _room(); + final random = math.Random(8); + final camera = PhotoCamera(world: room.world, reach: 9.0) + ..begin( + eye: Vector3(0.0, 2.0, 3.0), + target: Vector3(0.0, 2.0, -3.0), + anchor: Vector3(0.0, 1.0, 3.0), + ); + double signed() => random.nextDouble() * 2.0 - 1.0; + for (var i = 0; i < 500; i++) { + camera + ..look(signed() * 0.8, signed() * 0.6) + ..fly(Vector3(signed(), signed(), signed()) * 3.0, 0.25); + // Mutation: sweep with the camera's own size halved in `_shape`. The + // walk ends a step with the eye a few centimetres into the floor, the + // ceiling or the pillar, which is the near plane cutting into a wall. + expect(_inside(room.solids, camera.eye), isFalse, reason: 'step $i'); + expect( + camera.eye.distanceTo(camera.anchor), + lessThanOrEqualTo(9.0 + 1e-2), + reason: 'step $i', + ); + } + }); + + test('a tilted camera keeps its up at right angles to where it looks', () { + final camera = PhotoCamera(world: CollisionWorld()..update()) + ..begin( + eye: Vector3.zero(), + target: Vector3(0.0, -0.5, -1.0), + anchor: Vector3.zero(), + ) + ..tilt(10.0) + ..zoom(100.0); + // Mutation: drop the `maxRoll` clamp in `tilt`. The horizon turns past + // vertical and the picture is upside down. + expect(camera.roll, closeTo(math.pi / 4.0, 1e-9)); + expect(camera.up.dot(camera.forward).abs(), lessThan(1e-6)); + expect(camera.up.length, closeTo(1.0, 1e-6)); + expect(camera.fieldOfView, camera.minFieldOfView); + }); +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index f1d96d219..d092a77ba 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11118 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11141 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index ca3bf3a6b..24714ca16 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,17 +1,17 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11118 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11141 tests need a GPU. --- # Testing -11118 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11141 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| | `flutter3d` | 1637 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | -| `flutter3d_sim` | 657 | | `pad_input` | 67 | +| `flutter3d_sim` | 666 | | `pad_input` | 67 | | `flutter3d_lab` | 15 | | `flutter3d_core` | 750 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | @@ -19,15 +19,15 @@ description: Four independent golden sets, mutation-checking every new test, det | | | | `flutter3d_audio` | 4 | | `flutter3d_game_racing` | 223 | | `flutter3d_webgl` | 62 | | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 70 | -| `apps/flutter3d_demo_platformer` | 204 | | `flutter3d_impeller` | 64 | -| `flutter3d_cpu` | 357 | | `apps/flutter3d_demo_strategy` | 50 | +| `apps/flutter3d_demo_platformer` | 208 | | `flutter3d_impeller` | 64 | +| `flutter3d_cpu` | 364 | | `apps/flutter3d_demo_strategy` | 50 | | `apps/flutter3d_editor` | 234 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 38 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | -| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 143 | +| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 146 | | `flutter3d_game` | 325 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11092 rather than 11118: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11115 rather than 11141: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 6c3b703d6..cee79a0b3 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -644,6 +644,40 @@ registers no game and answers 503 until one is split out the way 23). The heatmap is drawn on its own screen, not over the level in the viewport. There is no upload rate limit and no retention period, and the privacy page says nothing about telemetry yet. +**N8 status.** *Done:* `shouldPause(photoMode:)` stops the world whatever the +pointer and the pad say. `PhotoCamera` in `flutter3d_sim` flies with look, +tilt and zoom, and is held three ways: a tether round where the player stood, +the level's box when there is one, and the walls, by sweeping each move and +sliding along what it meets; it starts by sweeping out from the player, so a +chase camera left behind a wall does not start the photo there. `PhotoFilter` +in `flutter3d_core` is eight looks (none, vivid, warm, cool, mono, sepia, +noir, faded) composed onto the game's `LookSettings`, so all four backends +draw them through the composite they already have. `capturePhoto` draws a +picture of any size in tiles on the game's own renderer, each with a margin +through the new `CropProjection`, and hands it out a row of tiles at a time; +`PhotoFinish` puts the vignette and the grain back over the whole frame. +Exposure is held at the screen's, and what cannot be carried over (temporal +anti-aliasing, motion blur, render scale, chromatic aberration) is listed in +the report. `PngStripWriter` in `flutter3d_cpu` writes the PNG a strip at a +time. `takePhoto` and `PhotoShelf` in `flutter3d_app` save it: the Pictures +folder on a desktop, the game's folder on a phone, the share sheet or a +download in a browser. The platformer has it on P: WASD, Space and C fly, +`[` `]` pick a filter, `,` `.` tilt, `-` `=` zoom, Enter saves at twice the +window and Shift+Enter at four times. +*Tests:* `photo_camera_test.dart` (walls, slide, tether, box, 500 random +moves in a furnished room) and `pause_gate_test.dart` in `flutter3d_sim`; +`photo_capture_test.dart` in `flutter3d_cpu` holds a ragged 3×3 grid to the +whole frame within one step, the margin to bloom seams of at most 6 steps +against 53 without, the vignette to the whole frame's within two, and the +strips to a PNG that zlib reads with its checksum; `photo_shelf_test.dart` in +`flutter3d_app`; `photo_mode_test.dart` in the platformer. +*Left:* the native share sheet on Android and iOS, which needs a plugin this +package does not take (a game passes its own `PhotoShelf`); chromatic +aberration over the whole frame; touch and pad controls for photo mode, and +the other demos; bloom's widest levels reach past any margin, so a capture +with bloom is close to the screen rather than equal; the browser share sheet +is untested in a browser, and the platformer's photo mode has not been +driven in a running window yet. Lifted exclusions (decision 12), each its own item: diff --git a/tool/structure/repository.dart b/tool/structure/repository.dart index c8f91fc11..438247870 100644 --- a/tool/structure/repository.dart +++ b/tool/structure/repository.dart @@ -218,6 +218,12 @@ const Map> genreBridgeHalf = >{ /// it does instead: a camera with no subject to follow has no rig to turn, and /// that is a reason; "it was easier" is not. const Map notARigCamera = { + 'flutter3d_sim/lib/src/camera/photo_camera.dart': + 'photo mode\'s camera, flown by the player with the world paused: it ' + 'follows nothing, so there is no smoothing to share, and a shake or a ' + 'kick on a camera lining up a still picture is the one thing it must ' + 'not have. It keeps out of walls by sweeping each move, which the rig\'s ' + 'ray back from a subject cannot do for a camera with no subject', 'flutter3d_core/lib/src/engine/scene/camera_node.dart': 'the scene-graph camera the rig steers, not a camera that follows ' 'anything: it holds the projection and the view, sits below ' @@ -1190,6 +1196,9 @@ const Map> portableStepExempt = 'flutter3d_sim': { 'lib/src/camera/camera_rig.dart': 'a camera is where the picture is taken from; no run depends on it', + 'lib/src/camera/photo_camera.dart': + 'flown only while the run is paused for a photo, and nothing it ' + 'does reaches a snapshot or a tape', 'lib/src/world/light_fixture.dart': 'how bright a lamp looks on a frame, which nothing steps on', 'lib/src/level/lightmap_baker.dart': From e463283fcde4acb88dcee324b5d0afef9208d1d6 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:32:31 +0300 Subject: [PATCH 046/141] A tool attached to a running game can read the frame it drew Renderer.captureNextFrame(draws: true) writes every mesh and shadow-caster draw into a DrawJournal: pass, node, material, counts, pipeline state and the values bound. Off in every other frame, where a draw site pays one null check. registerRenderExtensions answers ext.flutter3d.render.passes, passOutput, draws, draw, readPixel, scanNan and stats from a capture of the game's next GPU frame, and the three demos register them. --- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_demo_dungeon/lib/main.dart | 3 + apps/flutter3d_demo_platformer/lib/main.dart | 3 + apps/flutter3d_demo_racing/lib/main.dart | 2 + packages/flutter3d/README.md | 4 +- .../flutter3d/test/draw_journal_test.dart | 175 +++++++ packages/flutter3d_app/CHANGELOG.md | 12 + packages/flutter3d_app/lib/flutter3d_app.dart | 2 + .../src/diagnostics/render_extensions.dart | 170 +++++++ .../src/diagnostics/render_inspection.dart | 477 ++++++++++++++++++ .../test/render_inspection_test.dart | 410 +++++++++++++++ packages/flutter3d_core/CHANGELOG.md | 11 + .../flutter3d_core/lib/flutter3d_core.dart | 1 + .../lib/src/engine/render/draw_journal.dart | 157 ++++++ .../lib/src/engine/render/frame_capture.dart | 74 ++- .../src/engine/render/pass_contributor.dart | 6 + .../lib/src/engine/render/renderer.dart | 35 +- .../engine/render/renderer_mesh_encode.dart | 39 ++ .../engine/render/renderer_shadow_pass.dart | 52 ++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 10 +- tasks/0.9-engine-roadmap.md | 15 + 23 files changed, 1650 insertions(+), 14 deletions(-) create mode 100644 packages/flutter3d/test/draw_journal_test.dart create mode 100644 packages/flutter3d_app/lib/src/diagnostics/render_extensions.dart create mode 100644 packages/flutter3d_app/lib/src/diagnostics/render_inspection.dart create mode 100644 packages/flutter3d_app/test/render_inspection_test.dart create mode 100644 packages/flutter3d_core/lib/src/engine/render/draw_journal.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 952f90f8c..7c569bb2e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11141 tests** across 43 packages and 10 applications | +| Unit tests | **11168 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 57c97c8d4..88d89a6bb 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11141 tests across forty-three packages and nine applications. The +There are 11168 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_demo_dungeon/lib/main.dart b/apps/flutter3d_demo_dungeon/lib/main.dart index 3da46c91f..59d598c70 100644 --- a/apps/flutter3d_demo_dungeon/lib/main.dart +++ b/apps/flutter3d_demo_dungeon/lib/main.dart @@ -406,6 +406,9 @@ class _GameScreenState extends State frameTimes: _frameTimes, bugReport: _remoteBugReport, ); + // `P12`: the frame this game draws, pass by pass and draw by draw, for + // whichever renderer is open when somebody asks. + registerRenderExtensions(() => _renderer); _view = RenderView(camera: _camera); diff --git a/apps/flutter3d_demo_platformer/lib/main.dart b/apps/flutter3d_demo_platformer/lib/main.dart index df519933f..96bef8e31 100644 --- a/apps/flutter3d_demo_platformer/lib/main.dart +++ b/apps/flutter3d_demo_platformer/lib/main.dart @@ -492,6 +492,9 @@ class _GameScreenState extends State // `rp-02`: harmless where the VM service is off — `registerExtension` // just adds an entry nothing ever asks for. registerTimelineExtensions(_timeline, bugReport: _remoteBugReport); + // `P12`: the frame this game draws, pass by pass and draw by draw, for + // whichever renderer is open when somebody asks. + registerRenderExtensions(() => _renderer); // `HR4`: after every hot reload, the last three seconds lived again under // the new code, and the console says whether they came out the same. _stopReplays = replayAfterHotSwap(_timeline, capture: _present); diff --git a/apps/flutter3d_demo_racing/lib/main.dart b/apps/flutter3d_demo_racing/lib/main.dart index 162da5f5b..220ebe416 100644 --- a/apps/flutter3d_demo_racing/lib/main.dart +++ b/apps/flutter3d_demo_racing/lib/main.dart @@ -691,6 +691,8 @@ class _RaceScreenState extends State // `rp-02`: harmless where the VM service is off — `registerExtension` // just adds an entry nothing ever asks for. registerTimelineExtensions(_timeline, bugReport: _remoteBugReport); + // `P12`: the frame this game draws, pass by pass and draw by draw. + registerRenderExtensions(() => _renderer); await _loadCircuit(device); } diff --git a/packages/flutter3d/README.md b/packages/flutter3d/README.md index 9441b009a..f6ee90e7c 100644 --- a/packages/flutter3d/README.md +++ b/packages/flutter3d/README.md @@ -104,7 +104,7 @@ What works today: `CUBICSPLINE` with authored tangents), slerped rotations, an `AnimationPlayer` with play/pause/seek/speed and once/loop/ping-pong, and the decoded node hierarchy rebuilt on instantiation so an animated parent carries its subtree; -- 1637 tests covering projection, scene, sorting, debug draw, raycasting, +- 1642 tests covering projection, scene, sorting, debug draw, raycasting, animation, skinning, lighting, render targets, BVH, LOD, glTF, OBJ and `.f3d`, plus a real frame drawn through `flutter3d_cpu`'s software rasteriser for the ones that need one, all without a GPU. The geometry the engine is written in @@ -322,7 +322,7 @@ lib/src/engine/assets/ glTF, OBJ and .f3d decoders, isolate loading, ca example/lib/ the demo, and the frame capture hook skills/ the conventions, as agent skills — see below bin/skills.dart what copies them into a project that uses this -test/ 1637 tests, all runnable without a GPU +test/ 1642 tests, all runnable without a GPU ``` The GLSL is not here. Every shader this package draws with lives in diff --git a/packages/flutter3d/test/draw_journal_test.dart b/packages/flutter3d/test/draw_journal_test.dart new file mode 100644 index 000000000..6853da5a7 --- /dev/null +++ b/packages/flutter3d/test/draw_journal_test.dart @@ -0,0 +1,175 @@ +/// `P12`: every draw of one frame written down, when somebody asked for it. +/// +/// flutter test test/draw_journal_test.dart +/// +/// The counters on `FrameResult.passes` say a pass drew forty things; the +/// journal says which forty, with what. It costs an allocation per draw, so it +/// is on for the one captured frame and off in every other — and these tests +/// hold both halves of that, plus the arithmetic that makes the list honest: +/// the draws a pass described and the draws it only counted add up to the +/// draws it reported. +library; + +import 'dart:typed_data'; + +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +const int _size = 24; + +({CpuDevice device, Renderer renderer, Scene scene, DeviceMesh mesh}) _built() { + final device = CpuDevice( + width: _size, + height: _size, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final mesh = DeviceMesh.upload( + device, + CuboidShape(size: Vector3.all(1.4)).build(), + ); + final scene = Scene() + ..add( + MeshNode( + mesh, + Material(name: 'crate', baseColor: Vector4(0.9, 0.5, 0.2, 1.0)), + name: 'crate', + ), + ) + ..add( + LightNode(intensity: 6.0) + ..setPosition(2.0, 3.0, 4.0) + ..lookAt(Vector3.zero()), + ) + ..add( + CameraNode() + ..setPosition(0.0, 0.0, 4.0) + ..lookAt(Vector3.zero()), + ); + return ( + device: device, + renderer: Renderer.create(device: device), + scene: scene, + mesh: mesh, + ); +} + +Future _capture( + ({CpuDevice device, Renderer renderer, Scene scene, DeviceMesh mesh}) it, { + bool draws = false, + Float32List? Function(TextureHandle texture)? readFloats, +}) { + final capture = it.renderer.captureNextFrame( + draws: draws, + readFloats: readFloats, + ); + it.renderer.render( + width: _size, + height: _size, + scene: it.scene, + views: [ + RenderView( + camera: it.scene.cameras.single, + clearColor: Vector4(0.0, 0.0, 0.0, 1.0), + ), + ], + ); + return capture; +} + +void main() { + test( + 'a journaled frame names the mesh and material each draw used', + () async { + final it = _built(); + final capture = await _capture(it, draws: true); + + final crate = capture.draws.where((d) => d.kind == 'mesh').single; + expect(crate.pass, 'scene'); + expect(crate.mesh, 'crate'); + expect(crate.material, 'crate'); + expect(crate.vertices, it.mesh.vertexCount); + expect(crate.indices, it.mesh.indexCount); + expect(crate.triangles, it.mesh.indexCount ~/ 3); + expect(crate.uniforms['baseColor'], [ + closeTo(0.9, 1e-6), + closeTo(0.5, 1e-6), + closeTo(0.2, 1e-6), + 1.0, + ]); + expect(crate.uniforms['mvp'], hasLength(16)); + expect(crate.state['cull'], 'back'); + // Mutation: recording `draws.length` before the add, or the pass before + // `beginPass`, numbers every draw zero or files it under the wrong pass. + expect( + capture.draws.map((d) => d.index), + List.generate(capture.draws.length, (i) => i), + ); + }, + ); + + test( + 'described and undescribed draws add up to what each pass counted', + () async { + // **The journal does not describe a full-screen triangle**, and must not + // pretend the pass drew nothing either. Mutation: dropping `endPass` + // leaves `undetailedDraws` empty, and the composite's draws vanish from a + // list that claims to be the frame's. + final capture = await _capture(_built(), draws: true); + + for (final pass in capture.passes) { + final described = capture.draws + .where((d) => d.pass == pass.name) + .length; + expect( + described + (capture.undetailedDraws[pass.name] ?? 0), + pass.drawCalls, + reason: 'pass ${pass.name}', + ); + } + expect(capture.passNamed('composite')!.drawCalls, greaterThan(0)); + expect(capture.undetailedDraws, contains('composite')); + }, + ); + + test('a capture that did not ask for draws journals none', () async { + // The journal is for the one frame; the frame after it pays nothing. + // Mutation: leaving `passState.journal` set from `_captureDraws` without + // clearing it between requests journals every later capture too. + final it = _built(); + await _capture(it, draws: true); + final plain = await _capture(it); + + expect(plain.draws, isEmpty); + expect(plain.undetailedDraws, isEmpty); + expect(plain.passNamed('scene')!.drawCalls, greaterThan(0)); + }); + + test('a capture carries each pass its cost', () async { + final capture = await _capture(_built()); + final scene = capture.passNamed('scene')!; + + expect(scene.drawCalls, greaterThan(0)); + expect(scene.triangles, greaterThan(0)); + }); + + test('readFloats puts each output its own floats beside its bytes', () async { + // On the software backend, whose targets are floats already: what lets a + // NaN be told apart from the byte it clamps to. + final it = _built(); + final capture = await _capture(it, readFloats: it.device.readHdrPixels); + final image = capture + .passNamed('scene')! + .images + .firstWhere((i) => i.pixels != null); + + expect(image.floats, isNotNull); + expect(image.floats!.length, image.width * image.height * 4); + final plain = await _capture(it); + expect( + plain.passNamed('scene')!.images.every((i) => i.floats == null), + isTrue, + ); + }); +} diff --git a/packages/flutter3d_app/CHANGELOG.md b/packages/flutter3d_app/CHANGELOG.md index 2dc68c78f..45094d2bd 100644 --- a/packages/flutter3d_app/CHANGELOG.md +++ b/packages/flutter3d_app/CHANGELOG.md @@ -6,6 +6,18 @@ on a desktop and the game's own folder on a phone, through a `.part` file and a rename. `BrowserPhotoShelf` offers the share sheet where the browser takes files and downloads otherwise. `defaultPhotoShelf` picks the platform's. + +- **A tool attached to a running game can read the game's own frame.** + `registerRenderExtensions` puts `ext.flutter3d.render.passes`, + `passOutput`, `draws`, `draw`, `readPixel`, `scanNan` and `stats` on the VM + service, each answered from a capture of the next frame the game draws, so + what comes back is the GPU frame with its passes, targets and draws rather + than a second picture drawn on the software backend. The answers are + worked out by `renderPasses`, `renderDraws` and the rest, plain functions of + a `FrameCapture` that a test calls directly. `scanNan` reports a float + target it could only read as bytes as unread rather than clean: a NaN + clamps to an ordinary byte on every hardware readback. + - **A texture repainted under a running game is drawn at whatever size it now is.** `HotSwap.loadTexture` and `registerTexture` watch an image file; a swap after it changed uploads the new picture as a texture of its own, diff --git a/packages/flutter3d_app/lib/flutter3d_app.dart b/packages/flutter3d_app/lib/flutter3d_app.dart index 81a7bf83c..b86713d20 100644 --- a/packages/flutter3d_app/lib/flutter3d_app.dart +++ b/packages/flutter3d_app/lib/flutter3d_app.dart @@ -43,6 +43,8 @@ export 'src/backend_native.dart' if (dart.library.js_interop) 'src/backend_web.dart' show kFixedResolution, openDevice, presentFrame; export 'src/diagnostics/issues.dart'; +export 'src/diagnostics/render_extensions.dart'; +export 'src/diagnostics/render_inspection.dart'; export 'src/hot_swap/hot_swap.dart'; export 'src/level/level_loader.dart'; export 'src/level/model_visuals.dart'; diff --git a/packages/flutter3d_app/lib/src/diagnostics/render_extensions.dart b/packages/flutter3d_app/lib/src/diagnostics/render_extensions.dart new file mode 100644 index 000000000..bf5067b28 --- /dev/null +++ b/packages/flutter3d_app/lib/src/diagnostics/render_extensions.dart @@ -0,0 +1,170 @@ +/// Puts the renderer's own frame on the VM service as `ext.flutter3d.render.*` +/// — `P12`. +/// +/// **The frame the player sees, not a second one drawn for the tool.** The +/// diagnostic renderers in `flutter3d_sim_mcp` and `flutter3d_editor_mcp` draw +/// a level again on the software backend at 320×200, which answers "what is +/// in the level" and nothing about why the game's own frame is black, has a +/// hole in it, or costs twice what it did yesterday. Those are questions about +/// the GPU frame that actually ran, with its passes, its targets and its +/// draws, and only the running game has it. So every request here is a +/// `Renderer.captureNextFrame(draws: true)`: the next frame the game draws +/// anyway, recorded pass by pass, and answered by the functions in +/// `render_inspection.dart`. +/// +/// ## What a caller sees +/// +/// Seven extensions, each `ext.flutter3d.render.`, string parameters as +/// the protocol requires: +/// +/// * `passes`, `draws`, `stats`, `scanNan` — take a fresh capture unless +/// `fresh=false` and one is held. +/// * `passOutput`, `draw`, `readPixel` — read the capture already held, so an +/// index from `draws` or a pass from `passes` means the same frame; pass +/// `fresh=true` to take a new one. With none held they take one. +/// +/// Every answer carries `frame`, a count of captures this game has taken, so +/// a caller can see whether two answers are about the same frame. +/// +/// **Harmless where the VM service is off**, the same way the timeline's are: +/// `registerExtension` adds an entry nothing ever asks for, and nothing is +/// captured until something does. +library; + +import 'dart:async'; +import 'dart:convert'; +import 'dart:developer' as developer; + +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart' show CpuDevice; + +import 'render_inspection.dart'; + +/// Registers `ext.flutter3d.render.*` against whatever [renderer] answers at +/// the moment of each request — null while the game's renderer has not +/// opened, which is refused with that reason. +/// +/// Calling it again replaces the getter rather than registering twice, which +/// `registerExtension` would refuse: a game that builds its renderer again, +/// or a second screen with its own, points the same extensions at the new +/// one. +void registerRenderExtensions( + Renderer? Function() renderer, { + Duration timeout = const Duration(seconds: 2), +}) { + _inspector.renderer = renderer; + _inspector.timeout = timeout; + if (_registered) return; + _registered = true; + + void answer( + String verb, { + required bool freshByDefault, + required Map Function( + FrameCapture capture, + Map parameters, + ) + ask, + }) { + developer.registerExtension('ext.flutter3d.render.$verb', ( + method, + parameters, + ) async { + final fresh = switch (parameters['fresh']) { + 'true' => true, + 'false' => false, + _ => freshByDefault, + }; + final taken = await _inspector.capture(fresh: fresh); + if (taken case _Unavailable(:final why)) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.extensionError, + why, + ); + } + final (frame, capture) = (taken as _Taken).value; + final result = ask(capture, parameters); + if (isRefusal(result)) { + return developer.ServiceExtensionResponse.error( + developer.ServiceExtensionResponse.invalidParams, + result['refused']! as String, + ); + } + return developer.ServiceExtensionResponse.result( + jsonEncode({'frame': frame, ...result}), + ); + }); + } + + answer('passes', freshByDefault: true, ask: (c, _) => renderPasses(c)); + answer('draws', freshByDefault: true, ask: renderDraws); + answer('stats', freshByDefault: true, ask: (c, _) => renderStats(c)); + answer('scanNan', freshByDefault: true, ask: renderScanNan); + answer('passOutput', freshByDefault: false, ask: renderPassOutput); + answer('draw', freshByDefault: false, ask: renderDraw); + answer('readPixel', freshByDefault: false, ask: renderReadPixel); +} + +bool _registered = false; +final _Inspector _inspector = _Inspector(); + +sealed class _Capture { + const _Capture(); +} + +final class _Taken extends _Capture { + const _Taken(this.value); + final (int, FrameCapture) value; +} + +final class _Unavailable extends _Capture { + const _Unavailable(this.why); + final String why; +} + +/// The one capture the extensions share, and the renderer it came from. +final class _Inspector { + Renderer? Function() renderer = () => null; + Duration timeout = const Duration(seconds: 2); + + (int, FrameCapture)? _held; + Renderer? _heldFrom; + int _frames = 0; + + Future<_Capture> capture({required bool fresh}) async { + final current = renderer(); + if (current == null) { + return const _Unavailable( + 'the game has no renderer yet; ask again once it is drawing', + ); + } + final held = _held; + if (!fresh && held != null && identical(_heldFrom, current)) { + return _Taken(held); + } + final device = current.device; + final FrameCapture capture; + try { + capture = await current + .captureNextFrame( + draws: true, + // Only the software backend keeps its targets as floats; on the + // others a NaN is reported unread rather than absent. + readFloats: device is CpuDevice ? device.readHdrPixels : null, + ) + .timeout(timeout); + } on TimeoutException { + return _Unavailable( + 'no frame was drawn within ${timeout.inMilliseconds} ms; a capture ' + 'is the next frame the game draws, so the game has to be drawing — ' + 'a paused game that stops presenting draws nothing to capture', + ); + } on Object catch (error) { + return _Unavailable('the capture failed: $error'); + } + final taken = (++_frames, capture); + _held = taken; + _heldFrom = current; + return _Taken(taken); + } +} diff --git a/packages/flutter3d_app/lib/src/diagnostics/render_inspection.dart b/packages/flutter3d_app/lib/src/diagnostics/render_inspection.dart new file mode 100644 index 000000000..ae2c3e57d --- /dev/null +++ b/packages/flutter3d_app/lib/src/diagnostics/render_inspection.dart @@ -0,0 +1,477 @@ +/// The answers `ext.flutter3d.render.*` gives, worked out of one +/// [FrameCapture] — `P12`. +/// +/// **Functions from a capture and a parameter map to a JSON map, and nothing +/// else.** The VM service is a transport: what it carries is "which pass", +/// "which pixel", "which draw", and every one of those questions can be asked +/// of a capture built by hand in a test. Kept apart from the registration in +/// `render_extensions.dart` so that the part with the decisions in it — what an +/// unknown pass is told, what a pixel past the edge is told, what a NaN looks +/// like in JSON — is tested without a VM, a window or a GPU. +/// +/// **A refusal is an answer**: every function returns `{"refused": "..."}` +/// rather than throwing when it cannot do what was asked, and the sentence +/// names what was asked, why it cannot be, and what would work. +library; + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart' show encodePng; + +/// What a refusal looks like, so the registration can tell one apart. +Map refusal(String why) => {'refused': why}; + +/// Whether [answer] is a refusal from one of the functions here. +bool isRefusal(Map answer) => answer['refused'] is String; + +/// `passes`: every pass of the frame, in the order it ran. +Map renderPasses(FrameCapture capture) => { + 'width': capture.width, + 'height': capture.height, + 'passes': >[ + for (final (index, pass) in capture.passes.indexed) + { + 'index': index, + 'name': pass.name, + 'active': pass.active, + 'reads': pass.reads, + 'optionalReads': pass.optionalReads, + 'writes': pass.writes, + 'keeps': pass.keeps, + 'outputs': >[ + for (final image in pass.images) _describeImage(image), + ], + 'micros': pass.micros, + 'drawCalls': pass.drawCalls, + 'triangles': pass.triangles, + }, + ], +}; + +/// `passOutput`: one output of one pass as a PNG, base64. +/// +/// [parameters]: `pass` (an index or a name), `resource` (optional; the +/// pass's first output when absent). The PNG holds the eight-bit readback, +/// premultiplied, which is what every backend can hand back — see +/// `GraphicsDevice.readPixels`. +Map renderPassOutput( + FrameCapture capture, + Map parameters, +) => _answering(() { + final (pass, image) = _imageFor(capture, parameters); + final pixels = image.pixels; + if (pixels == null) { + throw _Refusal( + 'pass "${pass.name}" has no picture of "${image.resource}": ' + '${image.refused ?? 'the backend answered no pixels'}', + ); + } + return { + 'pass': pass.name, + ..._describeImage(image), + 'png': base64Encode( + encodePng( + pixels.buffer.asUint8List(pixels.offsetInBytes, pixels.lengthInBytes), + image.width, + image.height, + ), + ), + }; +}); + +/// `draws`: the frame's described draws, one row each. +/// +/// [parameters]: `pass` (optional; an index or a name to keep only that +/// pass's draws), `offset` and `limit` (a page; two hundred rows by default, +/// because a frame of a few thousand draws is a response nobody reads whole). +Map renderDraws( + FrameCapture capture, + Map parameters, +) => _answering(() { + final counted = capture.passes.fold(0, (sum, p) => sum + p.drawCalls); + if (capture.draws.isEmpty && capture.undetailedDraws.isEmpty && counted > 0) { + throw _Refusal( + 'this capture was taken without the draw journal, so its $counted ' + 'draws are counted and not described; capture again with ' + '`captureNextFrame(draws: true)`', + ); + } + final passName = switch (parameters['pass']) { + final key? => _passFor(capture, key).$2.name, + null => null, + }; + final offset = int.tryParse(parameters['offset'] ?? '') ?? 0; + final limit = int.tryParse(parameters['limit'] ?? '') ?? 200; + if (offset < 0 || limit < 1) { + throw _Refusal( + 'offset must be zero or more and limit one or more; ' + 'got offset $offset, limit $limit', + ); + } + final matching = [ + for (final draw in capture.draws) + if (passName == null || draw.pass == passName) draw, + ]; + return { + 'total': matching.length, + 'offset': offset, + 'draws': >[ + for (final draw in matching.skip(offset).take(limit)) + draw.toSummaryJson(), + ], + 'undetailed': { + for (final MapEntry(:key, :value) in capture.undetailedDraws.entries) + if (passName == null || key == passName) key: value, + }, + }; +}); + +/// `draw`: one draw, with its state and the values bound for it. +/// +/// [parameters]: `index`, as [renderDraws] numbered it. +Map renderDraw( + FrameCapture capture, + Map parameters, +) => _answering(() { + final index = int.tryParse(parameters['index'] ?? ''); + if (index == null) { + throw const _Refusal('draw takes an index, an integer from `draws`'); + } + if (index < 0 || index >= capture.draws.length) { + throw _Refusal( + capture.draws.isEmpty + ? 'there is no draw $index: this frame described no draws' + : 'there is no draw $index: this frame described ' + '${capture.draws.length}, numbered 0 to ' + '${capture.draws.length - 1}', + ); + } + final draw = capture.draws[index]; + return { + ...draw.toJson(), + // A NaN in a matrix is exactly what somebody opens one draw to find, and + // `jsonEncode` throws on one rather than writing it. + 'uniforms': { + for (final MapEntry(:key, :value) in draw.uniforms.entries) + key: [for (final v in value) _number(v)], + }, + }; +}); + +/// `readPixel`: one pixel of one output, as stored and as read back. +/// +/// [parameters]: `pass`, `x`, `y`, and `resource` (optional). `uint` is the +/// eight-bit readback every backend has; `float` is the texture's own value +/// where the backend keeps one, and null with `floatUnread` saying why +/// otherwise. A NaN or an infinity arrives as the string `"NaN"`, +/// `"Infinity"` or `"-Infinity"`, because JSON has no number for them. +Map renderReadPixel( + FrameCapture capture, + Map parameters, +) => _answering(() { + final x = int.tryParse(parameters['x'] ?? ''); + final y = int.tryParse(parameters['y'] ?? ''); + if (x == null || y == null) { + throw const _Refusal('readPixel takes x and y, integers from the top left'); + } + final (pass, image) = _imageFor(capture, parameters); + if (image.pixels == null && image.floats == null) { + throw _Refusal( + 'pass "${pass.name}" has no pixels of "${image.resource}": ' + '${image.refused ?? 'the backend answered none'}', + ); + } + if (x < 0 || x >= image.width || y < 0 || y >= image.height) { + throw _Refusal( + '($x, $y) lies outside "${image.resource}", which is ' + '${image.width}x${image.height}: x runs 0 to ${image.width - 1} and ' + 'y 0 to ${image.height - 1}', + ); + } + final uint = [for (var c = 0; c < 4; c++) image.channelAt(x, y, c)]; + final readable = !uint.contains(null); + final floats = _floatsAt(image, x, y); + return { + 'pass': pass.name, + 'resource': image.resource, + 'format': image.format.name, + 'x': x, + 'y': y, + 'uint': readable ? uint : null, + 'unorm': readable ? [for (final v in uint) v! / 255.0] : null, + 'float': floats == null + ? null + : [for (final v in floats) _number(v)], + if (floats == null) 'floatUnread': _floatsUnread(image), + }; +}); + +/// `scanNan`: every NaN and infinity in the frame's outputs, by pass. +/// +/// [parameters]: `pass` (optional; one pass rather than all), `first` (how +/// many coordinates to name per output; eight by default). +/// +/// Only a texture's floats can hold a NaN, and only a backend that keeps +/// floats can hand them back. An eight-bit output cannot hold one and is +/// counted as such; a float output this backend read back as eight bits is +/// listed under `unread` with the reason, rather than reported clean — a +/// NaN clamps to an ordinary byte on the way, and "no NaN found" would be a +/// claim nothing checked. +Map renderScanNan( + FrameCapture capture, + Map parameters, +) => _answering(() { + final first = int.tryParse(parameters['first'] ?? '') ?? 8; + final passes = switch (parameters['pass']) { + final key? => <(int, CapturedPass)>[_passFor(capture, key)], + null => capture.passes.indexed.toList(), + }; + final outputs = <(int, CapturedPass, CapturedImage)>[ + for (final (index, pass) in passes) + for (final image in pass.images) (index, pass, image), + ]; + final scanned = <(int, CapturedPass, CapturedImage, _Hits)>[ + for (final (index, pass, image) in outputs) + if (image.floats case final floats?) + (index, pass, image, _scan(image, floats, first)), + ]; + return { + 'clean': scanned.every((s) => s.$4.total == 0), + 'scanned': scanned.length, + 'eightBit': outputs + .where( + (o) => + o.$3.floats == null && + o.$3.pixels != null && + !_holdsFloats(o.$3.format), + ) + .length, + 'found': >[ + for (final (index, pass, image, hits) in scanned) + if (hits.total > 0) + { + 'passIndex': index, + 'pass': pass.name, + 'resource': image.resource, + 'nan': hits.nan, + 'infinite': hits.infinite, + 'first': hits.first, + }, + ], + 'unread': >[ + for (final (index, pass, image) in outputs) + if (image.floats == null && + (image.pixels == null || _holdsFloats(image.format))) + { + 'passIndex': index, + 'pass': pass.name, + 'resource': image.resource, + 'format': image.format.name, + 'why': image.refused ?? _floatsUnread(image), + }, + ], + }; +}); + +/// `stats`: what the frame cost and what it held. +/// +/// `targetBytes` sums each named resource once, at its captured size and +/// format; a pooled texture two names share is counted under both, and a +/// format whose size this does not know is listed in `unsizedFormats` rather +/// than guessed. +Map renderStats(FrameCapture capture) { + final targets = { + for (final pass in capture.passes) + for (final image in pass.images) + if (image.width > 0 && image.height > 0) image.resource: image, + }; + final sized = >[ + for (final image in targets.values) + { + 'resource': image.resource, + 'width': image.width, + 'height': image.height, + 'format': image.format.name, + 'bytes': switch (_bytesPerPixel(image.format)) { + final int b => b * image.width * image.height, + null => null, + }, + }, + ]; + return { + 'width': capture.width, + 'height': capture.height, + 'passes': capture.passes.length, + 'activePasses': capture.passes.where((p) => p.active).length, + 'drawCalls': capture.passes.fold(0, (s, p) => s + p.drawCalls), + 'describedDraws': capture.draws.length, + 'triangles': capture.passes.fold(0, (s, p) => s + p.triangles), + 'cpuMicros': capture.passes.fold(0, (s, p) => s + p.micros), + 'targetBytes': sized.fold( + 0, + (s, t) => s + ((t['bytes'] as int?) ?? 0), + ), + 'targets': sized, + 'unsizedFormats': { + for (final t in sized) + if (t['bytes'] == null) t['format']! as String, + }.toList(), + }; +} + +/// Raised inside a function here and answered at its edge by [_answering], +/// so a helper several calls down can refuse without every caller between +/// passing the refusal along by hand. Never escapes this file. +final class _Refusal implements Exception { + const _Refusal(this.why); + final String why; +} + +Map _answering(Map Function() body) { + try { + return body(); + } on _Refusal catch (refused) { + return refusal(refused.why); + } +} + +Map _describeImage(CapturedImage image) => { + 'resource': image.resource, + 'width': image.width, + 'height': image.height, + 'format': image.format.name, + 'readable': image.pixels != null, + 'floats': image.floats != null, + if (image.refused != null) 'refused': image.refused, +}; + +/// The pass [key] names — an index or a name — with its index. +(int, CapturedPass) _passFor(FrameCapture capture, String key) { + final names = capture.passes.map((p) => p.name).join(', '); + if (int.tryParse(key) case final index?) { + if (index >= 0 && index < capture.passes.length) { + return (index, capture.passes[index]); + } + throw _Refusal( + 'there is no pass $index: this frame ran ${capture.passes.length}, ' + 'numbered 0 to ${capture.passes.length - 1} ($names)', + ); + } + final index = capture.passes.indexWhere((p) => p.name == key); + if (index >= 0) return (index, capture.passes[index]); + throw _Refusal('no pass is called "$key"; this frame ran $names'); +} + +/// The output [parameters] name, as `pass` and `resource`. +(CapturedPass, CapturedImage) _imageFor( + FrameCapture capture, + Map parameters, +) { + final key = parameters['pass']; + if (key == null) { + throw const _Refusal('name a pass, by index or by name, as `pass`'); + } + final (_, pass) = _passFor(capture, key); + if (pass.images.isEmpty) { + throw _Refusal( + 'pass "${pass.name}" wrote and kept nothing this frame' + '${pass.active ? '' : ' — it was inactive'}', + ); + } + final resource = parameters['resource']; + if (resource == null) return (pass, pass.images.first); + final image = pass.imageOf(resource); + if (image == null) { + throw _Refusal( + 'pass "${pass.name}" has no output "$resource"; it wrote ' + '${pass.images.map((i) => i.resource).join(', ')}', + ); + } + return (pass, image); +} + +/// The floats at ([x], [y]), as many channels as the texture keeps a pixel. +List? _floatsAt(CapturedImage image, int x, int y) { + final floats = image.floats; + if (floats == null) return null; + final channels = _channelsOf(image, floats); + if (channels == 0) return null; + final at = (y * image.width + x) * channels; + return floats.sublist(at, at + channels); +} + +int _channelsOf(CapturedImage image, Float32List floats) { + final pixels = image.width * image.height; + return pixels == 0 ? 0 : floats.length ~/ pixels; +} + +typedef _Hits = ({ + int nan, + int infinite, + int total, + List> first, +}); + +_Hits _scan(CapturedImage image, Float32List floats, int first) { + final channels = _channelsOf(image, floats); + final bad = [ + for (var i = 0; i < image.width * image.height * channels; i++) + if (!floats[i].isFinite) i, + ]; + final nan = bad.where((i) => floats[i].isNaN).length; + return ( + nan: nan, + infinite: bad.length - nan, + total: bad.length, + first: >[ + for (final i in bad.take(first)) + { + 'x': (i ~/ channels) % image.width, + 'y': (i ~/ channels) ~/ image.width, + 'channel': i % channels, + 'value': _number(floats[i]), + }, + ], + ); +} + +String _floatsUnread(CapturedImage image) => _holdsFloats(image.format) + ? 'this backend reads ${image.format.name} back as eight bits a ' + 'channel, and a NaN clamps to an ordinary byte on the way; run ' + 'the frame on the software backend to read its floats' + : '${image.format.name} holds no floats to read'; + +bool _holdsFloats(TextureFormat format) => switch (format) { + TextureFormat.r32g32b32a32Float || + TextureFormat.r16g16b16a16Float || + TextureFormat.r32Float || + TextureFormat.d32FloatS8UInt => true, + _ => false, +}; + +int? _bytesPerPixel(TextureFormat format) => switch (format) { + TextureFormat.a8UNormInt || + TextureFormat.r8UNormInt || + TextureFormat.s8UInt => 1, + TextureFormat.r8g8UNormInt => 2, + TextureFormat.r8g8b8a8UNormInt || + TextureFormat.r8g8b8a8UNormIntSRGB || + TextureFormat.b8g8r8a8UNormInt || + TextureFormat.b8g8r8a8UNormIntSRGB || + TextureFormat.r32Float || + TextureFormat.d24UnormS8Uint => 4, + TextureFormat.r16g16b16a16Float || TextureFormat.d32FloatS8UInt => 8, + TextureFormat.r32g32b32a32Float => 16, + _ => null, +}; + +/// [v] as JSON can carry it: a number, or the name of one JSON has not got. +Object _number(double v) => v.isNaN + ? 'NaN' + : v == double.infinity + ? 'Infinity' + : v == double.negativeInfinity + ? '-Infinity' + : v; diff --git a/packages/flutter3d_app/test/render_inspection_test.dart b/packages/flutter3d_app/test/render_inspection_test.dart new file mode 100644 index 000000000..04299ab9e --- /dev/null +++ b/packages/flutter3d_app/test/render_inspection_test.dart @@ -0,0 +1,410 @@ +/// `P12`: what `ext.flutter3d.render.*` answers, asked of captures built by +/// hand — no VM, no window, no GPU. +/// +/// flutter test test/render_inspection_test.dart +/// +/// The extensions are a transport; the decisions are here. What an unknown +/// pass is told, what a pixel past the edge is told, how a NaN crosses JSON, +/// and — the one that matters most — that a float target this backend could +/// not read as floats is reported unread rather than clean. +library; + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_app/flutter3d_app.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +/// A 2×2 RGBA8 image whose pixel at (x, y) has red `10 * (y * 2 + x)`. +CapturedImage _bytes(String resource) => CapturedImage( + resource: resource, + width: 2, + height: 2, + format: TextureFormat.r8g8b8a8UNormInt, + pixels: ByteData.sublistView( + Uint8List.fromList([ + 0, 1, 2, 255, // + 10, 11, 12, 255, + 20, 21, 22, 255, + 30, 31, 32, 255, + ]), + ), +); + +/// A 2×2 half-float target with floats beside its bytes, one of them [bad]. +CapturedImage _hdr({double bad = 0.5, bool floats = true}) => CapturedImage( + resource: 'hdr colour', + width: 2, + height: 2, + format: TextureFormat.r16g16b16a16Float, + pixels: ByteData(16), + floats: floats + ? Float32List.fromList([ + 0.1, 0.2, 0.3, 1.0, // + 0.1, 0.2, 0.3, 1.0, + 0.1, bad, 0.3, 1.0, + 0.1, 0.2, 0.3, 1.0, + ]) + : null, +); + +FrameCapture _frame({CapturedImage? hdr}) => FrameCapture( + width: 2, + height: 2, + passes: [ + CapturedPass( + name: 'scene', + active: true, + reads: const [], + optionalReads: const [], + writes: const ['hdr colour'], + keeps: const [], + images: [hdr ?? _hdr()], + micros: 400, + drawCalls: 2, + triangles: 24, + ), + CapturedPass( + name: 'composite', + active: true, + reads: const ['hdr colour'], + optionalReads: const [], + writes: const ['frame'], + keeps: const [], + images: [_bytes('frame')], + micros: 100, + drawCalls: 1, + triangles: 1, + ), + ], + draws: [ + for (final (i, name) in ['crate', 'barrel'].indexed) + DrawRecord( + index: i, + passIndex: 0, + pass: 'scene', + kind: 'mesh', + mesh: name, + material: 'wood', + lighting: 'pbr', + vertices: 24, + indices: 36, + instances: 1, + triangles: 12, + state: const {'cull': 'back'}, + uniforms: >{ + 'tint': [1.0, double.nan, 1.0, 1.0], + }, + ), + ], + undetailedDraws: const {'composite': 1}, +); + +void main() { + group('passes', () { + test('lists each pass with its inputs, outputs, size and format', () { + final answer = renderPasses(_frame()); + final passes = answer['passes']! as List>; + + expect(passes.map((p) => p['name']), ['scene', 'composite']); + expect(passes[1]['index'], 1); + expect(passes[1]['reads'], ['hdr colour']); + final output = (passes[0]['outputs']! as List).single as Map; + expect(output['format'], 'r16g16b16a16Float'); + expect(output['width'], 2); + expect(output['floats'], isTrue); + }); + }); + + group('a pass is named by index or by name', () { + test('and an unknown name is refused with the names there are', () { + // Mutation: answering an empty map for a name nobody has hands the + // caller "nothing wrong" about a pass that does not exist. + final answer = renderPassOutput(_frame(), { + 'pass': 'bloom', + }); + expect(isRefusal(answer), isTrue); + expect(answer['refused'], contains('scene, composite')); + }); + + test('and an index past the end says how many passes ran', () { + final answer = renderReadPixel(_frame(), { + 'pass': '7', + 'x': '0', + 'y': '0', + }); + expect(answer['refused'], contains('numbered 0 to 1')); + }); + + test('and an output the pass did not write is refused by name', () { + final answer = renderPassOutput(_frame(), { + 'pass': 'composite', + 'resource': 'depth', + }); + expect(answer['refused'], contains('it wrote frame')); + }); + }); + + group('passOutput', () { + test('is a PNG of the pass output', () { + final answer = renderPassOutput(_frame(), {'pass': '1'}); + final png = base64Decode(answer['png']! as String); + expect(png.sublist(0, 8), [137, 80, 78, 71, 13, 10, 26, 10]); + expect(answer['resource'], 'frame'); + }); + + test('of an unreadable output passes on why', () { + final frame = _frame( + hdr: const CapturedImage( + resource: 'hdr colour', + width: 2, + height: 2, + format: TextureFormat.r16g16b16a16Float, + refused: 'tile memory: there is nothing to read', + ), + ); + final answer = renderPassOutput(frame, {'pass': '0'}); + expect(answer['refused'], contains('tile memory')); + }); + }); + + group('readPixel', () { + test('answers the byte at x, y and not its neighbour', () { + // Mutation: `(x * width + y)` reads (0, 1) as (1, 0) — 10 instead of 20. + final answer = renderReadPixel(_frame(), { + 'pass': 'composite', + 'x': '0', + 'y': '1', + }); + expect(answer['uint'], [20, 21, 22, 255]); + expect(answer['float'], isNull); + expect(answer['floatUnread'], contains('holds no floats')); + }); + + test('answers the float where the backend kept one', () { + final answer = renderReadPixel(_frame(), { + 'pass': 'scene', + 'x': '1', + 'y': '0', + }); + expect(answer['float'], [ + closeTo(0.1, 1e-6), + closeTo(0.2, 1e-6), + closeTo(0.3, 1e-6), + 1.0, + ]); + }); + + test('refuses a pixel past the edge, naming the size', () { + // Mutation: checking only `x + y * width < pixels` lets (2, 0) through + // as the first pixel of the next row — a real answer about the wrong + // place. + final answer = renderReadPixel(_frame(), { + 'pass': 'composite', + 'x': '2', + 'y': '0', + }); + expect(answer['refused'], contains('2x2')); + expect( + renderReadPixel(_frame(), { + 'pass': 'composite', + 'x': '0', + 'y': '-1', + }), + contains('refused'), + ); + }); + + test('carries a NaN as a string JSON can hold', () { + final answer = renderReadPixel( + _frame(hdr: _hdr(bad: double.nan)), + {'pass': 'scene', 'x': '0', 'y': '1'}, + ); + expect((answer['float']! as List)[1], 'NaN'); + expect(() => jsonEncode(answer), returnsNormally); + }); + }); + + group('scanNan', () { + test('finds a NaN and names the pass, the count and where', () { + final answer = renderScanNan( + _frame(hdr: _hdr(bad: double.nan)), + const {}, + ); + expect(answer['clean'], isFalse); + final found = (answer['found']! as List).single as Map; + expect(found['pass'], 'scene'); + expect(found['nan'], 1); + expect(found['infinite'], 0); + expect((found['first']! as List).single, { + 'x': 0, + 'y': 1, + 'channel': 1, + 'value': 'NaN', + }); + expect(() => jsonEncode(answer), returnsNormally); + }); + + test('counts an infinity apart from a NaN', () { + final answer = renderScanNan( + _frame(hdr: _hdr(bad: double.negativeInfinity)), + const {}, + ); + final found = (answer['found']! as List).single as Map; + expect(found['nan'], 0); + expect(found['infinite'], 1); + }); + + test('a clean frame is clean, and its eight-bit output is counted', () { + final answer = renderScanNan(_frame(), const {}); + expect(answer['clean'], isTrue); + expect(answer['scanned'], 1); + expect(answer['eightBit'], 1); + expect(answer['unread'], isEmpty); + }); + + test('a float target read back as bytes is unread, not clean', () { + // **The whole reason this is not a loop over `pixels`.** A NaN clamps + // to an ordinary byte on every hardware backend's readback, so a scan + // of the bytes finds nothing and says so. Mutation: treating an image + // with no floats as scanned reports this frame clean. + final answer = renderScanNan( + _frame(hdr: _hdr(floats: false)), + const {}, + ); + expect(answer['scanned'], 0); + final unread = (answer['unread']! as List).single as Map; + expect(unread['resource'], 'hdr colour'); + expect(unread['why'], contains('eight bits')); + }); + + test('an unknown pass is refused', () { + final answer = renderScanNan(_frame(), {'pass': 'sky'}); + expect(answer['refused'], contains('no pass is called "sky"')); + }); + }); + + group('draws', () { + test('lists every described draw and what each pass only counted', () { + final answer = renderDraws(_frame(), const {}); + expect(answer['total'], 2); + expect( + (answer['draws']! as List).map((d) => (d as Map)['mesh']), + ['crate', 'barrel'], + ); + expect(answer['undetailed'], {'composite': 1}); + }); + + test('pages, and filters by pass', () { + final page = renderDraws(_frame(), { + 'offset': '1', + 'limit': '1', + }); + expect((page['draws']! as List).single, containsPair('mesh', 'barrel')); + final composite = renderDraws(_frame(), { + 'pass': 'composite', + }); + expect(composite['total'], 0); + expect(composite['undetailed'], {'composite': 1}); + }); + + test('a capture taken without the journal says so', () { + final frame = FrameCapture(width: 2, height: 2, passes: _frame().passes); + expect( + renderDraws(frame, const {})['refused'], + contains('without the draw journal'), + ); + }); + }); + + group('draw', () { + test('answers one draw with its state and uniforms, JSON-safe', () { + final answer = renderDraw(_frame(), {'index': '1'}); + expect(answer['mesh'], 'barrel'); + expect(answer['state'], {'cull': 'back'}); + expect((answer['uniforms']! as Map)['tint'], [ + 1.0, + 'NaN', + 1.0, + 1.0, + ]); + expect(() => jsonEncode(answer), returnsNormally); + }); + + test('an index past the end says how many there are', () { + final answer = renderDraw(_frame(), {'index': '2'}); + expect(answer['refused'], contains('numbered 0 to 1')); + }); + }); + + group('stats', () { + test('sums the passes and sizes each target by its format', () { + final answer = renderStats(_frame()); + expect(answer['passes'], 2); + expect(answer['drawCalls'], 3); + expect(answer['triangles'], 25); + expect(answer['cpuMicros'], 500); + // 2×2 at eight bytes, and 2×2 at four. + expect(answer['targetBytes'], 2 * 2 * 8 + 2 * 2 * 4); + }); + }); + + test('a real software frame answers every question end to end', () async { + // The capture the extensions take, on the one backend that keeps floats: + // a lit cube scans clean and its scene pass reads back as floats. + const size = 16; + final device = CpuDevice( + width: size, + height: size, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final scene = Scene() + ..add( + MeshNode( + DeviceMesh.upload( + device, + CuboidShape(size: Vector3.all(1.4)).build(), + ), + Material(name: 'crate'), + name: 'crate', + ), + ) + ..add(LightNode(intensity: 6.0)..setPosition(2.0, 3.0, 4.0)) + ..add( + CameraNode() + ..setPosition(0.0, 0.0, 4.0) + ..lookAt(Vector3.zero()), + ); + final renderer = Renderer.create(device: device); + final pending = renderer.captureNextFrame( + draws: true, + readFloats: device.readHdrPixels, + ); + renderer.render( + width: size, + height: size, + scene: scene, + views: [RenderView(camera: scene.cameras.single)], + ); + final capture = await pending; + + final scan = renderScanNan(capture, const {}); + expect(scan['clean'], isTrue); + expect(scan['scanned'], greaterThan(0)); + expect( + renderDraws(capture, const {})['draws'], + contains(containsPair('mesh', 'crate')), + ); + final pixel = renderReadPixel(capture, { + 'pass': 'scene', + 'x': '8', + 'y': '8', + }); + expect(pixel['float'], hasLength(4)); + expect(() => jsonEncode(renderPasses(capture)), returnsNormally); + expect(() => jsonEncode(renderStats(capture)), returnsNormally); + }); +} diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index 1df36d129..0bf92438c 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -87,6 +87,17 @@ `PhotoFilter` is eight looks composed onto the game's own `LookSettings`; `PhotoFinish` puts the vignette and the grain back over the whole frame rather than once per tile. + +- **A captured frame can say which draws it made.** + `Renderer.captureNextFrame(draws: true)` writes every mesh and shadow-caster + draw into a `DrawJournal` — pass, node, material, counts, pipeline state and + the values bound — and `FrameCapture.draws` hands them back, with + `undetailedDraws` counting the full-screen draws a pass made without a mesh + to name. Off in every other frame, where a draw site pays one null check. + `readFloats` puts a backend's own floats beside each output's bytes, which + the software backend's `readHdrPixels` can answer, and each `CapturedPass` + now carries its time, draws and triangles. + - **A reload reaches the contributors too.** `Renderer.relinkShaders` asks every `PassContributor` to drop what it linked, through the new `PassContributor.relinkShaders`, which does nothing by default. diff --git a/packages/flutter3d_core/lib/flutter3d_core.dart b/packages/flutter3d_core/lib/flutter3d_core.dart index b2e903dad..777ff64c7 100644 --- a/packages/flutter3d_core/lib/flutter3d_core.dart +++ b/packages/flutter3d_core/lib/flutter3d_core.dart @@ -79,6 +79,7 @@ export 'src/engine/render/adaptive_scale.dart'; export 'src/engine/render/cluster_draws.dart'; export 'src/engine/render/debug_draw.dart'; export 'src/engine/render/debug_draw_gizmos.dart'; +export 'src/engine/render/draw_journal.dart'; export 'src/engine/render/empty_frame.dart'; export 'src/engine/render/environment_map.dart'; export 'src/engine/render/field_pass.dart'; diff --git a/packages/flutter3d_core/lib/src/engine/render/draw_journal.dart b/packages/flutter3d_core/lib/src/engine/render/draw_journal.dart new file mode 100644 index 000000000..2e6a11a30 --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/render/draw_journal.dart @@ -0,0 +1,157 @@ +/// Every draw of one frame, written down as it was encoded — `P12`. +/// +/// **Why a journal beside the counters, and why it is off.** `gfx-01n` gave +/// each pass its draw count, and the count says a pass drew forty things +/// without saying which forty. A tool asking "is the crate drawn at all, and +/// with which material" needs the list, and the list costs an allocation per +/// draw — exactly the cost the counters were designed to avoid. So the +/// journal exists for the one frame somebody asked about +/// (`Renderer.captureNextFrame(draws: true)`) and is null in every other one; +/// each site writes through `state.journal?.add(...)`, whose arguments Dart +/// does not evaluate when the journal is null, so an ordinary frame pays one +/// null check per draw and builds nothing. +/// +/// **Not every draw is described.** The mesh encoder and the shadow casters +/// know a node and a material and record them; a full-screen triangle in a +/// post pass knows neither, and inventing a "mesh" for it would make the list +/// look more complete than it is. Those are counted per pass in +/// [DrawJournal.undetailed] instead — the pass's own draw count minus what it +/// described — so the total still adds up to what the pass reported. +library; + +import 'dart:typed_data'; + +/// One draw, as the encoder saw it. +final class DrawRecord { + const DrawRecord({ + required this.index, + required this.passIndex, + required this.pass, + required this.kind, + required this.mesh, + required this.material, + required this.lighting, + required this.vertices, + required this.indices, + required this.instances, + required this.triangles, + required this.state, + required this.uniforms, + }); + + /// Position in the frame, across every pass. + final int index; + + /// The graph node that encoded it, by position and by name. + final int passIndex; + final String pass; + + /// `mesh` for the scene's own encoder, `shadow` for a caster. + final String kind; + + /// The node's name — a mesh has none of its own — or null when unnamed. + final String? mesh; + final String? material; + + /// The lighting model's label: which pipeline drew it. + final String lighting; + + final int vertices; + final int indices; + final int instances; + final int triangles; + + /// Pipeline state the draw was encoded with: cull, blend, depth, winding, + /// and the variant flags that pick a pipeline. + final Map state; + + /// The values bound for this draw, by name — matrices column-major. + final Map> uniforms; + + /// The row a list shows. + Map toSummaryJson() => { + 'index': index, + 'passIndex': passIndex, + 'pass': pass, + 'kind': kind, + 'mesh': mesh, + 'material': material, + 'vertices': vertices, + 'indices': indices, + 'instances': instances, + }; + + /// Everything. + Map toJson() => { + ...toSummaryJson(), + 'lighting': lighting, + 'triangles': triangles, + 'state': state, + 'uniforms': uniforms, + }; +} + +/// Collects [DrawRecord]s while one frame runs. +final class DrawJournal { + final List _records = []; + final Map _undetailed = {}; + + int _passIndex = -1; + String _pass = ''; + int _passStart = 0; + + List get records => List.unmodifiable(_records); + + /// Draws a pass counted and did not describe, by pass name; passes that + /// described every draw are absent. + Map get undetailed => Map.unmodifiable(_undetailed); + + /// Called by the renderer before a graph node runs. + void beginPass(int index, String name) { + _passIndex = index; + _pass = name; + _passStart = _records.length; + } + + /// Called by the renderer after the node, with the draws it counted. + void endPass(int drawCalls) { + final missing = drawCalls - (_records.length - _passStart); + if (missing > 0) _undetailed[_pass] = missing; + } + + /// Writes one draw down. Call as `journal?.add(...)`, so nothing below is + /// evaluated in a frame that is not being journaled. + void add({ + required String kind, + required String? mesh, + required String? material, + required String lighting, + required int vertices, + required int indices, + required int instances, + required Map state, + Map uniforms = const {}, + }) { + _records.add( + DrawRecord( + index: _records.length, + passIndex: _passIndex, + pass: _pass, + kind: kind, + mesh: mesh, + material: material, + lighting: lighting, + vertices: vertices, + indices: indices, + instances: instances, + triangles: (indices ~/ 3) * instances, + state: Map.unmodifiable(state), + // Copied: the renderer refills its staging blocks for the next draw. + uniforms: >{ + for (final MapEntry(:key, :value) in uniforms.entries) + key: List.unmodifiable(value), + }, + ), + ); + } +} diff --git a/packages/flutter3d_core/lib/src/engine/render/frame_capture.dart b/packages/flutter3d_core/lib/src/engine/render/frame_capture.dart index db411a32e..9765ee1c2 100644 --- a/packages/flutter3d_core/lib/src/engine/render/frame_capture.dart +++ b/packages/flutter3d_core/lib/src/engine/render/frame_capture.dart @@ -28,6 +28,7 @@ import 'dart:typed_data'; import 'package:flutter3d_hardware/flutter3d_hardware.dart'; +import 'draw_journal.dart'; import 'frame_graph.dart'; /// One resource as one pass left it. @@ -38,6 +39,7 @@ final class CapturedImage { required this.height, required this.format, this.pixels, + this.floats, this.refused, }); @@ -52,6 +54,15 @@ final class CapturedImage { /// there are none. final ByteData? pixels; + /// The texture's own RGBA floats, unclamped, or null where the backend + /// cannot hand them back — `P12`. + /// + /// [pixels] is eight bits a channel on every backend, and a NaN a shader + /// wrote clamps to an ordinary byte on the way there. Only a backend that + /// keeps its textures as floats can answer this, and the capture asks it + /// through [FrameCaptureBuilder.readFloats]. + final Float32List? floats; + /// Why there is no image, in a sentence a reader can act on. /// /// A capture with a gap in it is worse than no capture: the reader assumes @@ -111,10 +122,19 @@ final class CapturedPass { required this.writes, required this.keeps, required this.images, + this.micros = 0, + this.drawCalls = 0, + this.triangles = 0, }); final String name; + /// What the pass cost, as `FrameResult.passes` reported it — `P12`, so a + /// capture answers "how big was this frame" on its own. + final int micros; + final int drawCalls; + final int triangles; + /// Whether the node said it had anything to do. /// /// A pass can be in the order and inactive — see `FrameGraphNode.isActive` — @@ -150,12 +170,22 @@ final class FrameCapture { required this.width, required this.height, required this.passes, + this.draws = const [], + this.undetailedDraws = const {}, }); final int width; final int height; final List passes; + /// Every draw the frame described, when the capture asked for them — `P12`. + /// Empty otherwise; see [DrawJournal]. + final List draws; + + /// Draws a pass counted without describing, by pass name — see + /// [DrawJournal.undetailed]. + final Map undetailedDraws; + /// The pass called [name], or null. CapturedPass? passNamed(String name) { for (final pass in passes) { @@ -191,11 +221,24 @@ final class FrameCapture { /// queued at the pass boundary and answered afterwards — and a caller should /// not be handed a half-filled capture. final class FrameCaptureBuilder { - FrameCaptureBuilder({required this.width, required this.height}); + FrameCaptureBuilder({ + required this.width, + required this.height, + this.journal, + this.readFloats, + }); final int width; final int height; + /// Where the frame's draws are written down, or null when nobody asked. + final DrawJournal? journal; + + /// Reads a texture's floats as they stand, for a backend that keeps them — + /// the software rasteriser's `readHdrPixels`. Null everywhere else, and + /// then [CapturedImage.floats] is null too. + final Float32List? Function(TextureHandle texture)? readFloats; + final List<_PendingPass> _passes = <_PendingPass>[]; /// Records [node] and queues a readback of everything it wrote or maintains. @@ -203,6 +246,9 @@ final class FrameCaptureBuilder { FrameGraphNode node, { required GraphicsDevice device, required TextureHandle? Function(ResourceId id) lookup, + int micros = 0, + int drawCalls = 0, + int triangles = 0, }) { final pending = >[]; // Written *and* maintained: a node that keeps an atlas between frames — the @@ -223,6 +269,9 @@ final class FrameCaptureBuilder { writes: [for (final id in node.writes) id.name], keeps: [for (final id in node.keeps) id.name], images: pending, + micros: micros, + drawCalls: drawCalls, + triangles: triangles, ), ); } @@ -280,9 +329,21 @@ final class FrameCaptureBuilder { height: texture.height, format: texture.format, pixels: bytes, + floats: _floatsOf(texture), ); } + /// [readFloats]' answer for [texture], or null when it has none or throws: + /// the eight-bit image is still evidence, and losing it to a float read + /// that failed would be worse than having only it. + Float32List? _floatsOf(TextureHandle texture) { + try { + return readFloats?.call(texture); + } on Object { + return null; + } + } + /// The finished capture, once every queued readback has answered. Future build() async => FrameCapture( width: width, @@ -297,8 +358,13 @@ final class FrameCaptureBuilder { writes: pass.writes, keeps: pass.keeps, images: await Future.wait(pass.images), + micros: pass.micros, + drawCalls: pass.drawCalls, + triangles: pass.triangles, ), ], + draws: journal?.records ?? const [], + undetailedDraws: journal?.undetailed ?? const {}, ); } @@ -311,6 +377,9 @@ final class _PendingPass { required this.writes, required this.keeps, required this.images, + required this.micros, + required this.drawCalls, + required this.triangles, }); final String name; @@ -320,4 +389,7 @@ final class _PendingPass { final List writes; final List keeps; final List> images; + final int micros; + final int drawCalls; + final int triangles; } diff --git a/packages/flutter3d_core/lib/src/engine/render/pass_contributor.dart b/packages/flutter3d_core/lib/src/engine/render/pass_contributor.dart index caca5e00c..258fac71f 100644 --- a/packages/flutter3d_core/lib/src/engine/render/pass_contributor.dart +++ b/packages/flutter3d_core/lib/src/engine/render/pass_contributor.dart @@ -6,6 +6,7 @@ import 'package:flutter3d_shaders/typed_blocks.dart'; import 'package:vector_math/vector_math.dart' as vm; import '../scene/scene.dart'; +import 'draw_journal.dart'; import 'frame_graph.dart'; import 'frame_plan.dart'; import 'frame_resources.dart'; @@ -58,6 +59,11 @@ final class FramePassState { /// not one instance of itself, so it does not add to this count. int instances = 0; + /// Where each draw is written down, for the one frame somebody asked to + /// see them — `P12`. Null otherwise; see [DrawJournal] for why a draw site + /// writes `journal?.add(...)` and nothing more. + DrawJournal? journal; + /// Call after encoding anything that binds its own pipeline. /// /// The tracker describes the mesh pipelines only, so a pass that replaced diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer.dart b/packages/flutter3d_core/lib/src/engine/render/renderer.dart index edda738f7..30c09fea3 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer.dart @@ -31,6 +31,7 @@ import 'cluster_draws.dart'; import 'composite_mix.dart'; import 'debug_draw.dart'; import 'debug_draw_gizmos.dart'; +import 'draw_journal.dart'; import 'empty_frame.dart'; import 'engine_tables.dart'; import 'field_pass.dart'; @@ -2996,13 +2997,25 @@ final class Renderer implements RenderServices { /// /// Asking twice before a frame runs replaces the first request: there is one /// next frame. - Future captureNextFrame() { + /// + /// [draws] also writes every draw down — see [DrawJournal] — and + /// [readFloats] reads each output's floats beside its bytes, for a backend + /// that can: `P12`'s inspector needs both, and a capture for a person + /// looking at pictures needs neither. + Future captureNextFrame({ + bool draws = false, + Float32List? Function(TextureHandle texture)? readFloats, + }) { final completer = Completer(); _captureWanted = completer; + _captureDraws = draws; + _captureFloats = readFloats; return completer.future; } Completer? _captureWanted; + bool _captureDraws = false; + Float32List? Function(TextureHandle texture)? _captureFloats; /// Hands [wanted] the capture this frame built, and clears the builder. /// @@ -4415,7 +4428,13 @@ final class Renderer implements RenderServices { _captureWanted = null; _capture = capturing == null ? null - : FrameCaptureBuilder(width: width, height: height); + : FrameCaptureBuilder( + width: width, + height: height, + journal: _captureDraws ? DrawJournal() : null, + readFloats: _captureFloats, + ); + passState.journal = _capture?.journal; try { for (var i = 0; i < frameGraph.order.length; i++) { resources.beginNode(i); @@ -4436,6 +4455,7 @@ final class Renderer implements RenderServices { // so a GPU debugger and `GraphicsDevice.onGpuTimings` see the graph's // own names rather than a pass nobody can place. _passLabel = node.name; + passState.journal?.beginPass(i, node.name); developer.Timeline.startSync(node.name); try { node.execute( @@ -4486,7 +4506,16 @@ final class Renderer implements RenderServices { // pool, and the next pass draws over it. A capture taken after the // frame would hold whatever the last pass to borrow that shape left // there, attributed to whichever pass wrote it first. - _capture?.record(node, device: device, lookup: resources.tryTexture); + passState.journal?.endPass(passState.drawCalls - drawsBefore); + final cost = passTimings.last; + _capture?.record( + node, + device: device, + lookup: resources.tryTexture, + micros: cost.micros, + drawCalls: cost.drawCalls, + triangles: cost.triangles, + ); resources.endNode(i); } } catch (error, stack) { diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart index 72121b0c0..845bfe7b1 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart @@ -923,6 +923,45 @@ extension _MeshEncode on Renderer { state.drawCalls++; state.triangles += (indexCount ~/ 3) * (instanced?.count ?? 1); if (instanced != null) state.instances += instanced.count; + // `P12`: nothing past the `?.` runs unless this frame is being journaled. + state.journal?.add( + kind: 'mesh', + mesh: node.name, + material: material.name, + lighting: material.lighting.label, + vertices: mesh.vertexCount, + indices: indexCount, + instances: instanced?.count ?? 1, + state: { + 'cull': cull ? 'back' : 'none', + 'blend': orderIndependent != null + ? 'weighted' + : override != null + ? 'override' + : (blend ? 'alpha' : 'opaque'), + 'depthWrite': + orderIndependent == null && (material.depthWrite ?? !blend), + 'depthCompare': depthCompare.name, + 'winding': node.worldIsMirrored != mirrored ? 'cw' : 'ccw', + 'skinned': skinned, + 'instanced': batched, + 'lightmapped': lightmapped, + 'clustered': clustered != null, + 'xray': override != null, + }, + uniforms: { + 'mvp': Float32List.fromList(mvp.storage), + 'model': Float32List.fromList(modelMatrix.storage), + 'tint': Float32List.fromList(node.tint.storage), + 'baseColor': Float32List.fromList(material.baseColor.storage), + 'emissive': Float32List.fromList(material.emissive.storage), + 'metallicRoughness': Float32List.fromList([ + material.metallic, + material.roughness, + ]), + ...material.parameters, + }, + ); } /// The index buffer that draws the clusters of [node]'s split mesh the diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart index 9a0224ed1..818e729be 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart @@ -50,6 +50,38 @@ CullMode _casterCull(ShadowCasterFaces faces) => switch (faces) { /// The pass that draws what the sun cannot see, and the one that draws what a /// lamp cannot. extension _ShadowPasses on Renderer { + /// Writes one caster's draw into [journal] — `P12`. + /// + /// Called only behind `if (_frameCounters?.journal case final journal?)`, + /// so a frame nobody is journaling never reaches it. + void _journalCaster( + DrawJournal journal, + MeshNode node, + DrawableGeometry mesh, + vm.Matrix4 mvp, { + required int instances, + required String target, + }) => journal.add( + kind: 'shadow', + mesh: node.name, + material: node.material.name, + lighting: node.material.lighting.label, + vertices: mesh.vertexCount, + indices: mesh.indexCount, + instances: instances, + state: { + 'target': target, + 'masked': _castsMasked(node), + 'skinned': node.skeleton != null, + 'instanced': node is InstancedMeshNode, + 'winding': node.worldIsMirrored ? 'cw' : 'ccw', + }, + uniforms: { + 'mvp': Float32List.fromList(mvp.storage), + 'model': Float32List.fromList(node.worldMatrix.storage), + }, + ); + /// Whether [node] casts through the cut-out shadow stages — `gfx-60n`. /// /// glTF's MASK mode and nothing else. A blended material is a different @@ -470,6 +502,16 @@ extension _ShadowPasses on Renderer { pass.bindBlock(fragment, _shadowLight); pass.draw(instanceCount: instanced?.count ?? 1); _frameCounters?.drawCalls++; + if (_frameCounters?.journal case final journal?) { + _journalCaster( + journal, + node, + mesh, + mvp, + instances: instanced?.count ?? 1, + target: 'cube', + ); + } drawn++; } finishTile(tileIndex, tileStart); @@ -1148,6 +1190,16 @@ extension _ShadowPasses on Renderer { // reported one caster and no draws at all was hiding the cost of the // cascade count from every measurement made of it — `gfx-01n`. _frameCounters?.drawCalls++; + if (_frameCounters?.journal case final journal?) { + _journalCaster( + journal, + node, + mesh, + mvp, + instances: instanced?.count ?? 1, + target: 'cascade', + ); + } } } diff --git a/site/content/quickstart.md b/site/content/quickstart.md index d092a77ba..9c03ac575 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11141 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11168 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 24714ca16..d948fe47b 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,14 +1,14 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11141 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11168 tests need a GPU. --- # Testing -11141 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11168 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| -| `flutter3d` | 1637 | | | | +| `flutter3d` | 1642 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | | `flutter3d_sim` | 666 | | `pad_input` | 67 | @@ -27,7 +27,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 38 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | -| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 146 | +| `apps/flutter3d_demo_dungeon` | 115 | | `flutter3d_app` | 168 | | `flutter3d_game` | 325 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11115 rather than 11141: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11142 rather than 11168: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index cee79a0b3..0d67a2104 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -536,6 +536,21 @@ render: `list_draws`, `get_draw`, `list_passes`, `pass_output`, `read_pixel`, `reload_level`, console and event subscription from MCP. A `skills` command installing per-package skills (idioms, performance, looks, verification). Desync bisect as MCP tools on top of N4. +*Game side done:* `registerRenderExtensions` in `flutter3d_app` puts +`ext.flutter3d.render.{passes, passOutput, draws, draw, readPixel, scanNan, +stats}` on the VM service, registered by the three demos beside their +timeline extensions. Each request is `Renderer.captureNextFrame(draws: +true)`, so the answer is the GPU frame the game drew; `passOutput`, `draw` +and `readPixel` read the capture already held so an index means the same +frame. The draws come from a `DrawJournal` that the mesh encoder and the +shadow casters write through `state.journal?.add(...)`, null outside the +captured frame; full-screen draws are counted per pass as `undetailed`. The +answers are plain functions of a `FrameCapture` in `render_inspection.dart`, +tested without a VM. *Left:* `scanNan` reads floats only on the software +backend (`CpuDevice.readHdrPixels`); on the hardware ones a float target is +reported unread, and a real answer there needs a float readback or a pass +that writes a NaN mask into an eight-bit target. The MCP tools that call +these extensions, and everything else in this item, are still to come. ## 5. New features flutter_scene does not have From 6abaf880d505ac8b35f01babb0f8b5073a5b30df Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:37:03 +0300 Subject: [PATCH 047/141] A body can turn, when it is built to RigidBody has an orientation, an angular velocity and an inertia tensor from its shape, integrated in the step; a body built without them moves as before, bit for bit. The step's trigonometry comes from portable_math, the same fdlibm polynomials flutter3d_sim uses, so a turning body replays to the same bits on the VM and in a browser. Snapshots carry the new state. --- ARCHITECTURE.md | 2 +- README.md | 2 +- .../Flutter/GeneratedPluginRegistrant.swift | 2 - packages/flutter3d_physics/CHANGELOG.md | 24 ++ .../lib/flutter3d_physics.dart | 1 + .../flutter3d_physics/lib/src/dynamics.dart | 8 +- .../flutter3d_physics/lib/src/inertia.dart | 83 ++++ .../lib/src/portable_math.dart | 266 ++++++++++++ .../flutter3d_physics/lib/src/rigid_body.dart | 334 ++++++++++++++- .../flutter3d_physics/lib/src/snapshot.dart | 34 ++ .../test/portable_math_test.dart | 221 ++++++++++ .../flutter3d_physics/test/rotation_test.dart | 388 ++++++++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- tasks/0.9-engine-roadmap.md | 20 + 15 files changed, 1368 insertions(+), 27 deletions(-) create mode 100644 packages/flutter3d_physics/lib/src/inertia.dart create mode 100644 packages/flutter3d_physics/lib/src/portable_math.dart create mode 100644 packages/flutter3d_physics/test/portable_math_test.dart create mode 100644 packages/flutter3d_physics/test/rotation_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 7c569bb2e..cab1fff65 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11168 tests** across 43 packages and 10 applications | +| Unit tests | **11195 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 88d89a6bb..c3c57a84d 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11168 tests across forty-three packages and nine applications. The +There are 11195 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_demo_arcade/macos/Flutter/GeneratedPluginRegistrant.swift b/apps/flutter3d_demo_arcade/macos/Flutter/GeneratedPluginRegistrant.swift index 4c6c08a37..859b13e8e 100644 --- a/apps/flutter3d_demo_arcade/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/apps/flutter3d_demo_arcade/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,10 @@ import FlutterMacOS import Foundation -import flutter_soloud import pad_input import pointer_lock func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - FlutterSoloudPlugin.register(with: registry.registrar(forPlugin: "FlutterSoloudPlugin")) GamepadPlugin.register(with: registry.registrar(forPlugin: "GamepadPlugin")) PointerLockPlugin.register(with: registry.registrar(forPlugin: "PointerLockPlugin")) } diff --git a/packages/flutter3d_physics/CHANGELOG.md b/packages/flutter3d_physics/CHANGELOG.md index 7a4e76f4e..aca25871a 100644 --- a/packages/flutter3d_physics/CHANGELOG.md +++ b/packages/flutter3d_physics/CHANGELOG.md @@ -1,3 +1,27 @@ +## Unreleased + +- **A body can turn, if it is built to.** `RigidBody` has an `orientation`, + an `angularVelocity`, an inertia tensor in its own axes + (`inertiaLocal`, `inverseInertiaLocal`) and one in the world's + (`inverseInertiaWorld`), plus `applyImpulseAt` and `applyTorqueImpulse`. + `inertiaFor(shape, mass)` gives the principal moments of a box, a sphere + and a capsule; a wedge and a heightfield are taken as their bounding box. + Only a body built with `canRotate: true` turns, because every shipped + level was tuned against crates that do not tip. The others have an + inverse inertia of zero and step exactly as before. A turning body + keeps its angular momentum rather than its angular velocity, which is + the gyroscopic term taken implicitly: a box spun off its principal axes + for ten seconds holds L to within 1e-4. `angularDamping` is zero by + default. No contact turns a body yet, and the collider stays + axis-aligned. `save()` writes the orientation and spin only for a body + that can turn, so saved levels and their digests do not change. + `readQuaternion` reads one back. +- **The step has trigonometry it may call.** `src/portable_math.dart` + holds `sin`, `cos`, `sinCos`, `atan`, `atan2`, `asin` and `acos` built + from fdlibm's kernels out of IEEE arithmetic alone, for the hinge + angles and cone limits that come next. It is not exported, because + `flutter3d_sim` re-exports this package beside its own `Portable`. + ## 0.8.2+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_physics/lib/flutter3d_physics.dart b/packages/flutter3d_physics/lib/flutter3d_physics.dart index 2a3198247..236200eef 100644 --- a/packages/flutter3d_physics/lib/flutter3d_physics.dart +++ b/packages/flutter3d_physics/lib/flutter3d_physics.dart @@ -39,6 +39,7 @@ export 'src/collision_shape.dart'; export 'src/collision_world.dart'; export 'src/contact.dart'; export 'src/dynamics.dart'; +export 'src/inertia.dart'; export 'src/rigid_body.dart'; export 'src/snapshot.dart'; export 'src/spatial_grid.dart'; diff --git a/packages/flutter3d_physics/lib/src/dynamics.dart b/packages/flutter3d_physics/lib/src/dynamics.dart index 08cc97851..de2139fc2 100644 --- a/packages/flutter3d_physics/lib/src/dynamics.dart +++ b/packages/flutter3d_physics/lib/src/dynamics.dart @@ -22,7 +22,10 @@ /// /// ## What it does not do /// -/// No rotation, no joints, no continuous collision. A body moving fast enough +/// No contact that turns a body — a body built with `canRotate` spins from the +/// impulses a game gives it and keeps its angular momentum, but the solver +/// below pushes only along the normal, through the centre. No joints, no +/// continuous collision. A body moving fast enough /// to cross a wall between two steps will cross it; the sweep that stops that /// happening for the character controller is not applied here, and stage two is /// where it belongs. Stated rather than discovered, and the numbers are in @@ -213,6 +216,9 @@ final class Dynamics { ..setFrom(body.velocity) ..scale(dt); body.collider.moveTo(body.position + _scratch); + // After the solve, like the position: the spin this step ends with is + // the one the body turns by. Nothing for a body that cannot turn. + body.integrateOrientation(dt); } _separate(); diff --git a/packages/flutter3d_physics/lib/src/inertia.dart b/packages/flutter3d_physics/lib/src/inertia.dart new file mode 100644 index 000000000..9846d350a --- /dev/null +++ b/packages/flutter3d_physics/lib/src/inertia.dart @@ -0,0 +1,83 @@ +/// How hard a shape is to turn. +/// +/// ## Three numbers, not nine +/// +/// Every shape here is symmetric about its own three axes, and a solid with +/// that symmetry has a diagonal inertia tensor in those axes: the products of +/// inertia integrate an odd function over a symmetric body and vanish. So a +/// tensor in body space is three principal moments, carried as a [Vector3], +/// and a full matrix appears only once the body is turned — see +/// `RigidBody.inverseInertiaWorld`. +/// +/// ## Two of the five are approximations, and say so +/// +/// * **A wedge is treated as its bounding box.** The true solid is half the +/// box, with its centre of mass a sixth of the way towards the low side and +/// a tensor that is no longer diagonal about the box centre. A body's centre +/// is its collider's centre here, so honouring the wedge means moving one of +/// the two; until a wedge has to tumble convincingly, the box it sits in +/// turns close enough. +/// * **A heightfield is treated as its bounding box too.** It is ground, and +/// ground does not turn; the answer exists so that [inertiaFor] has one for +/// every shape the sealed class has, rather than a throw a game finds later. +library; + +import 'package:vector_math/vector_math.dart'; + +import 'collision_shape.dart'; + +/// The principal moments of inertia of [shape] with [mass] kilograms spread +/// evenly through it, about its own centre and in its own axes. +/// +/// Kilogram square metres. Zero for a [mass] of zero or less — what such a +/// body's inverse should be is `RigidBody`'s decision, not this one's. +Vector3 inertiaFor(CollisionShape shape, double mass) { + if (mass <= 0.0) return Vector3.zero(); + return switch (shape) { + CollisionBox(:final halfExtents) => _box(halfExtents, mass), + CollisionSphere(:final radius) => Vector3.all(0.4 * mass * radius * radius), + CollisionCapsule(:final radius, :final halfHeight) => _capsule( + radius, + halfHeight, + mass, + ), + CollisionWedge(:final halfExtents) => _box(halfExtents, mass), + CollisionHeightfield() => _box(shape.boundsHalfExtents, mass), + }; +} + +/// `m/12 · (w² + d²)` with the full widths, which is `m/3 · (b² + c²)` with +/// the half extents the shape keeps. +Vector3 _box(Vector3 half, double mass) { + final x2 = half.x * half.x; + final y2 = half.y * half.y; + final z2 = half.z * half.z; + final third = mass / 3.0; + return Vector3(third * (y2 + z2), third * (x2 + z2), third * (x2 + y2)); +} + +/// An upright capsule: a cylinder of half length [halfHeight] and two +/// hemispherical caps, the mass split between them by volume. +/// +/// **Not a cylinder of the total height**, which is the easy answer and puts +/// mass in the corners a capsule has rounded off. The difference is largest +/// for the short, round capsules a character stands in, and at a [halfHeight] +/// of zero this has to be a sphere — `rotation_test.dart` holds both ends. +/// +/// Each cap about the centre is `ms · (2/5 r² + h² + 3/4 h r)` for the pair: +/// a hemisphere's own `2/5 mr²` about its flat face, moved to its centroid +/// three-eighths of a radius out, and then on to the capsule's middle. +Vector3 _capsule(double r, double h, double mass) { + final r2 = r * r; + // π cancels out of the ratio: a cylinder of 2h·r² against a sphere of 4/3·r³. + final cylinderVolume = 2.0 * h * r2; + final capsVolume = 4.0 / 3.0 * r2 * r; + final cylinder = mass * cylinderVolume / (cylinderVolume + capsVolume); + final caps = mass - cylinder; + + final axial = cylinder * 0.5 * r2 + caps * 0.4 * r2; + final across = + cylinder * (0.25 * r2 + h * h / 3.0) + + caps * (0.4 * r2 + h * h + 0.75 * h * r); + return Vector3(across, axial, across); +} diff --git a/packages/flutter3d_physics/lib/src/portable_math.dart b/packages/flutter3d_physics/lib/src/portable_math.dart new file mode 100644 index 000000000..8a912fed1 --- /dev/null +++ b/packages/flutter3d_physics/lib/src/portable_math.dart @@ -0,0 +1,266 @@ +/// The trigonometry a rigid body step is allowed to call. +/// +/// ## Why not `dart:math` +/// +/// `dart:math`'s `sin`, `cos`, `atan2` and `acos` are the host's libm on the VM +/// and whatever a browser ships on the web, and neither IEEE 754 nor the Dart +/// specification says two of them agree on the last bit. `flutter3d_sim` +/// measured it: twenty thousand arguments, every transcendental answering +/// differently under the VM and under Chrome, and a car replayed from the same +/// tape diverging at twenty-three checkpoints of forty. A body that turns is +/// made of exactly these functions — a hinge's angle is an `atan2`, a cone +/// limit is an `acos` — so the step that turns one has to ask something that +/// cannot disagree with itself. `sqrt` stays `dart:math`'s: IEEE 754 requires +/// it to be correctly rounded, so there is one right answer and every platform +/// gives it. +/// +/// ## Why a second copy rather than `flutter3d_sim`'s +/// +/// **The arrow points the other way.** `flutter3d_sim` depends on this package, +/// so this package cannot import its `Portable`. The kernels below are the same +/// fdlibm polynomials and the same argument reduction, transcribed rather than +/// re-derived, so the two answer the same bits for the same argument — +/// `portable_math_test.dart` records digests that hold that. +/// +/// **Not exported.** `flutter3d_sim` re-exports this library whole beside its +/// own `Portable`, and two public classes of one name in one export is an +/// error for every caller of the simulation. Nothing outside the step needs +/// these; a game that wants portable trigonometry already has the fuller set. +/// +/// **Only what a body needs.** Sine and cosine to turn an angle into an axis, +/// and the inverses to turn a pair of axes back into an angle. No `exp`, no +/// `log`, no `pow`: damping here is a rational factor and nothing else in a +/// step raises anything to a power. A function added later belongs in this +/// file, and the rule in `tool/structure.dart` that refuses `math.sin` in a +/// step is what sends it here. +/// +/// ## What is guaranteed +/// +/// **The same bits everywhere, by construction.** Everything below is `+`, +/// `-`, `*`, `/`, `sqrt` and comparison, which the specification pins. **Not +/// the last bit of `dart:math`**: these are minimax polynomials, within two +/// units in the last place of the libm answer and held there by the test. +library; + +import 'dart:math' as math; + +/// Sine, cosine and their inverses, computed identically on every platform. +abstract final class Portable { + /// Sine of [radians]. NaN for an argument that is not finite. + static double sin(double radians) { + if (!radians.isFinite) return double.nan; + final reduced = _quarterTurns(radians); + return switch (reduced.quadrant) { + 0 => _sin(reduced.r, reduced.tail), + 1 => _cos(reduced.r, reduced.tail), + 2 => -_sin(reduced.r, reduced.tail), + _ => -_cos(reduced.r, reduced.tail), + }; + } + + /// Cosine of [radians]. NaN for an argument that is not finite. + static double cos(double radians) { + if (!radians.isFinite) return double.nan; + final reduced = _quarterTurns(radians); + return switch (reduced.quadrant) { + 0 => _cos(reduced.r, reduced.tail), + 1 => -_sin(reduced.r, reduced.tail), + 2 => -_cos(reduced.r, reduced.tail), + _ => _sin(reduced.r, reduced.tail), + }; + } + + /// Both at once, which is what an angle becoming an axis wants. + /// + /// The argument reduction is the expensive half of either function, and this + /// does it once. + static ({double sin, double cos}) sinCos(double radians) { + if (!radians.isFinite) return (sin: double.nan, cos: double.nan); + final reduced = _quarterTurns(radians); + final s = _sin(reduced.r, reduced.tail); + final c = _cos(reduced.r, reduced.tail); + return switch (reduced.quadrant) { + 0 => (sin: s, cos: c), + 1 => (sin: c, cos: -s), + 2 => (sin: -s, cos: -c), + _ => (sin: -c, cos: s), + }; + } + + /// Arc tangent of [x], in radians, between -π/2 and π/2. + static double atan(double x) { + if (x.isNaN) return double.nan; + final negative = x.isNegative; + final magnitude = x.abs(); + if (magnitude.isInfinite) return negative ? -_pio2 : _pio2; + + // Which of fdlibm's four intervals the argument lands in (split at 7/16, + // 11/16, 19/16 and 39/16), and the substitution that folds it onto the + // one the polynomial is fitted over. + final (piece, folded) = switch (magnitude) { + < 0.4375 => (-1, magnitude), + < 0.6875 => (0, (2.0 * magnitude - 1.0) / (2.0 + magnitude)), + < 1.1875 => (1, (magnitude - 1.0) / (magnitude + 1.0)), + < 2.4375 => (2, (magnitude - 1.5) / (1.0 + 1.5 * magnitude)), + _ => (3, -1.0 / magnitude), + }; + + final z = folded * folded; + final w = z * z; + // Even and odd halves evaluated apart, as fdlibm does: the coefficients + // are fitted to that arrangement. + final odd = + z * + (_aT0 + w * (_aT2 + w * (_aT4 + w * (_aT6 + w * (_aT8 + w * _aT10))))); + final even = w * (_aT1 + w * (_aT3 + w * (_aT5 + w * (_aT7 + w * _aT9)))); + + final result = piece < 0 + ? folded - folded * (odd + even) + : _atanHi[piece] - ((folded * (odd + even) - _atanLo[piece]) - folded); + return negative ? -result : result; + } + + /// The angle of the vector ([y], [x]), in radians, between -π and π. + /// + /// The quadrant logic is comparison and exact arithmetic; only [atan] under + /// it had to be replaced. + static double atan2(double y, double x) { + if (x.isNaN || y.isNaN) return double.nan; + if (y == 0.0) { + // The sign of zero decides the side, as IEEE says: atan2(-0, -1) is -π. + if (x > 0.0 || (x == 0.0 && !x.isNegative)) return y; + return y.isNegative ? -_pi : _pi; + } + if (x == 0.0) return y.isNegative ? -_pio2 : _pio2; + if (x.isInfinite) { + if (y.isInfinite) { + final quarter = x.isNegative ? 3.0 * _pio4 : _pio4; + return y.isNegative ? -quarter : quarter; + } + final flat = x.isNegative ? _pi : 0.0; + return y.isNegative ? -flat : flat; + } + if (y.isInfinite) return y.isNegative ? -_pio2 : _pio2; + + final angle = atan((y / x).abs()); + final folded = x.isNegative ? _pi - angle : angle; + return y.isNegative ? -folded : folded; + } + + /// Arc sine of [x], in radians. NaN outside -1 to 1. + /// + /// `atan2(x, sqrt((1 - x)(1 + x)))`: the factored `1 - x²` is what keeps the + /// digits as `|x|` reaches one, where the plain square loses half of them. + static double asin(double x) { + if (x.isNaN || x < -1.0 || x > 1.0) return double.nan; + return atan2(x, math.sqrt((1.0 - x) * (1.0 + x))); + } + + /// Arc cosine of [x], in radians, between 0 and π. NaN outside -1 to 1. + /// + /// **Not `π/2 - asin(x)`.** Near `x = 1` the answer is nearly nothing and + /// that subtraction is between two numbers near π/2, so it cancels most of + /// the digits the answer was meant to have — which is the angle a joint at + /// rest sits at. The two arguments of [atan2] swapped is two units in the + /// last place across the whole domain. + static double acos(double x) { + if (x.isNaN || x < -1.0 || x > 1.0) return double.nan; + return atan2(math.sqrt((1.0 - x) * (1.0 + x)), x); + } + + /// [x] as a count of quarter turns, modulo four, and what is left over. + /// + /// **π/2 is taken away in two pieces.** [_pio2Hi] has its low thirty-three + /// bits clear, so `n × pio2Hi` is exact for any `n` a step reaches, and + /// [_pio2Lo] carries the rest; with one double the product rounds before the + /// subtraction and the remainder is wrong in a way that grows with `n`. The + /// quadrant comes from `n % 4.0`, exact arithmetic on the double, rather than + /// from masking an integer conversion, which is where the VM and a browser + /// part company. + static ({int quadrant, double r, double tail}) _quarterTurns(double x) { + final n = (x * _twoOverPi).roundToDouble(); + final hi = x - n * _pio2Hi; + final lo = n * _pio2Lo; + final r = hi - lo; + return (quadrant: (n % 4.0).toInt(), r: r, tail: (hi - r) - lo); + } + + /// Sine on -π/4 to π/4, given the remainder and its tail. + static double _sin(double x, double tail) { + final z = x * x; + final v = z * x; + final r = _sinS2 + z * (_sinS3 + z * (_sinS4 + z * (_sinS5 + z * _sinS6))); + if (tail == 0.0) return x + v * (_sinS1 + z * r); + return x - ((z * (0.5 * tail - v * r) - tail) - v * _sinS1); + } + + /// Cosine on -π/4 to π/4, given the remainder and its tail. + /// + /// `(1 - w) - hz` recovers the bits `1 - z/2` throws away; written the + /// obvious way this loses about ten of them near ±π/4. + static double _cos(double x, double tail) { + final z = x * x; + final r = + z * + (_cosC1 + + z * + (_cosC2 + + z * (_cosC3 + z * (_cosC4 + z * (_cosC5 + z * _cosC6))))); + final hz = 0.5 * z; + final w = 1.0 - hz; + return w + (((1.0 - w) - hz) + (z * r - x * tail)); + } + + // fdlibm's constants, digit for digit the ones `flutter3d_sim` uses. + + static const double _pi = 3.14159265358979311600; + static const double _pio2 = 1.57079632679489655800; + static const double _pio4 = 0.78539816339744827900; + static const double _twoOverPi = 0.63661977236758134308; + + /// π/2 with the low thirty-three bits of its mantissa clear. + static const double _pio2Hi = 1.57079632673412561417; + + /// What [_pio2Hi] is missing. + static const double _pio2Lo = 6.07710050650619224932e-11; + + static const double _sinS1 = -1.66666666666666324348e-01; + static const double _sinS2 = 8.33333333332248946124e-03; + static const double _sinS3 = -1.98412698298579493134e-04; + static const double _sinS4 = 2.75573137070700676789e-06; + static const double _sinS5 = -2.50507602534068634195e-08; + static const double _sinS6 = 1.58969099521155010221e-10; + + static const double _cosC1 = 4.16666666666666019037e-02; + static const double _cosC2 = -1.38888888888741095749e-03; + static const double _cosC3 = 2.48015872894767294178e-05; + static const double _cosC4 = -2.75573143513906633035e-07; + static const double _cosC5 = 2.08757232129817482790e-09; + static const double _cosC6 = -1.13596475577881948265e-11; + + static const double _aT0 = 3.33333333333329318027e-01; + static const double _aT1 = -1.99999999998764832476e-01; + static const double _aT2 = 1.42857142725034663711e-01; + static const double _aT3 = -1.11111104054623557880e-01; + static const double _aT4 = 9.09088713343650656196e-02; + static const double _aT5 = -7.69187620504482999495e-02; + static const double _aT6 = 6.66107313738753120669e-02; + static const double _aT7 = -5.83357013379057348645e-02; + static const double _aT8 = 4.97687799461593236017e-02; + static const double _aT9 = -3.65315727442169155270e-02; + static const double _aT10 = 1.62858201153657823623e-02; + + /// atan at the middle of each folded interval, in two pieces apiece. + static const List _atanHi = [ + 4.63647609000806093515e-01, // atan(0.5) + 7.85398163397448278999e-01, // atan(1.0) + 9.82793723247329054082e-01, // atan(1.5) + 1.57079632679489655800e+00, // atan(∞) + ]; + static const List _atanLo = [ + 2.26987774529616870924e-17, + 3.06161699786838301793e-17, + 1.39033110312309984516e-17, + 6.12323399573676603587e-17, + ]; +} diff --git a/packages/flutter3d_physics/lib/src/rigid_body.dart b/packages/flutter3d_physics/lib/src/rigid_body.dart index f4fb219f9..651f0c9f9 100644 --- a/packages/flutter3d_physics/lib/src/rigid_body.dart +++ b/packages/flutter3d_physics/lib/src/rigid_body.dart @@ -1,23 +1,29 @@ /// Something the world pushes around. /// -/// ## No rotation, and it is not a shortcut +/// ## No rotation unless asked for, and that is not a shortcut /// -/// Stage one of the specification's two: mass, gravity, impulses, being pushed, -/// and coming to rest. A body has a velocity and no angular velocity, no -/// inertia tensor and no orientation. +/// Stage one of the specification's two was mass, gravity, impulses, being +/// pushed, and coming to rest, with no orientation at all. Its consequence is +/// what keeps it tractable: **a box that never rotates stays axis-aligned.** +/// Contact between two of them is then exact and cheap — the overlap on each +/// axis, and the normal is the axis of least overlap — where the general case +/// needs a separating-axis search and a manifold with several points. /// -/// The consequence is worth stating because it is what makes stage one -/// tractable: **a box that never rotates stays axis-aligned.** Contact between -/// two of them is then exact and cheap — the overlap on each axis, and the -/// normal is the axis of least overlap — where the general case needs a -/// separating-axis search and a manifold with several points. Nothing here -/// approximates a rotating box; there simply are none. +/// Stage two starts here, and it starts switched off. Every body now has an +/// [RigidBody.orientation], an [RigidBody.angularVelocity] and an inertia +/// tensor, but only a body built with `canRotate: true` turns; the rest have +/// an inverse inertia of zero, which is what "cannot be turned" means to the +/// arithmetic, and step exactly as they did. **Off by default because every +/// crate in every shipped level was tuned against bodies that do not tip**, +/// and a platformer's crate tumbling off the ledge it was placed on is a +/// broken level rather than a better simulation. /// -/// What that costs, stated rather than discovered: a crate you push into a -/// corner will not tip, a barrel will not roll, and a ragdoll is out of the -/// question. Those are stage two, and stage two is a separate decision — see -/// `ARCHITECTURE.md` §10, which says to port a solver rather than write one if it -/// is ever needed. +/// **What a turning body does not do yet: collide as a turned shape.** Its +/// collider is still the axis-aligned one, and no contact turns it — torque +/// comes from [RigidBody.applyImpulseAt] and [RigidBody.applyTorqueImpulse] +/// and nothing else. Contacts that tip a crate are the next piece of the same +/// work, and until they land a spinning box is drawn turning inside a +/// collision box that is not. /// /// ## It lives in the collision world everything else lives in /// @@ -28,11 +34,14 @@ /// door refuse to close on one without a line of new code anywhere. library; +import 'dart:math' as math; + import 'package:vector_math/vector_math.dart'; import 'collider.dart'; import 'collision_shape.dart'; import 'collision_world.dart'; +import 'inertia.dart'; import 'snapshot.dart'; final class RigidBody { @@ -43,10 +52,20 @@ final class RigidBody { this.mass = 1.0, this.restitution = 0.0, this.friction = 0.6, + this.canRotate = false, + Quaternion? orientation, + this.angularDamping = 0.0, int layer = 1 << 0, int mask = Layers.all, Object? userData, - }) : inverseMass = mass <= 0.0 ? 0.0 : 1.0 / mass { + }) : inverseMass = mass <= 0.0 ? 0.0 : 1.0 / mass, + inertiaLocal = inertiaFor(shape, mass), + inverseInertiaLocal = _inverted( + inertiaFor(shape, mass), + canRotate && mass > 0.0, + ) { + if (orientation != null) _setUnit(orientation); + refreshInertia(); collider = world.add( Collider( shape: shape, @@ -86,6 +105,115 @@ final class RigidBody { /// Coulomb friction, roughly. One is grippy, zero is ice. double friction; + /// Whether anything may turn this body. Fixed at construction, because the + /// inverse tensor below is. + final bool canRotate; + + /// Whether it actually turns: allowed to, and movable at all. + bool get rotates => canRotate && isMovable; + + /// How the body is turned, from its own axes into the world's. + /// + /// In the convention of `asRotationMatrix` and `Matrix4.compose`, which is + /// what a renderer draws with. `Quaternion.rotated` applies the inverse + /// turn, `q* v q`, and is the wrong way to carry a point out of this body. + /// + /// For a body that does not [rotates], whatever it was built or set with, + /// and nothing a step does changes it. **Assign + /// rather than edit in place:** the setter normalises and refreshes + /// [inverseInertiaWorld], and a quaternion changed through this getter + /// leaves that tensor describing the old turn until [refreshInertia] is + /// called. + Quaternion get orientation => _orientation; + set orientation(Quaternion value) { + _setUnit(value); + refreshInertia(); + } + + final Quaternion _orientation = Quaternion.identity(); + + /// Radians per second about each world axis. Zero, always, for a body that + /// does not [rotates]. + final Vector3 angularVelocity = Vector3.zero(); + + /// Per second, the fraction of spin taken away, as `1 / (1 + dt · this)`. + /// + /// **Zero by default, because a free body keeping its spin is the right + /// answer** and a damping that hides a solver bug is not one. It is here + /// for the game that wants a thrown object to settle sooner than friction + /// alone would make it. Rational rather than `exp(-dt · this)`, which is + /// the same to first order and asks the machine nothing. + double angularDamping; + + /// The principal moments of inertia in the body's own axes — see + /// `inertia.dart` for why three numbers are the whole tensor. + /// + /// The shape's real answer whether or not the body may turn; it is + /// [inverseInertiaLocal] that says whether it does. + final Vector3 inertiaLocal; + + /// One over each of [inertiaLocal], or zero for a body that does not + /// [rotates] — infinite inertia, which is the whole of how "does not turn" + /// reaches the arithmetic. + final Vector3 inverseInertiaLocal; + + /// The inverse tensor in world axes: `R · diag(inverseInertiaLocal) · Rᵀ`. + /// + /// What a solver multiplies a torque by. Refreshed whenever [orientation] is + /// assigned, restored or stepped, rather than computed per use: a contact + /// solver asks for it twenty times a step for every pair. Symmetric, so + /// reading it by rows or by columns is the same. + final Matrix3 inverseInertiaWorld = Matrix3.zero(); + + /// [inverseInertiaWorld] at full precision, which is what the arithmetic + /// reads. The matrix above is for a caller; `vector_math` stores it in + /// single precision. + _Symmetric _inverseWorld = _Symmetric.zero; + + /// Brings [inverseInertiaWorld] up to date with [orientation]. + /// + /// Called by the setter, by [restore] and by the step. A caller that turned + /// the quaternion in place rather than assigning it calls this. + void refreshInertia() { + _inverseWorld = _Symmetric.turned(_orientation, inverseInertiaLocal); + final m = _inverseWorld; + inverseInertiaWorld.setValues( + m.xx, + m.xy, + m.xz, + m.xy, + m.yy, + m.yz, + m.xz, + m.yz, + m.zz, + ); + } + + void _setUnit(Quaternion value) { + final length = value.length; + // A zero quaternion turns nothing into nothing; the identity is the only + // honest reading of "no rotation was given". + if (length == 0.0 || !length.isFinite) { + _orientation.setValues(0.0, 0.0, 0.0, 1.0); + return; + } + _orientation.setValues( + value.x / length, + value.y / length, + value.z / length, + value.w / length, + ); + } + + static Vector3 _inverted(Vector3 inertia, bool turns) => turns + ? Vector3( + inertia.x > 0.0 ? 1.0 / inertia.x : 0.0, + inertia.y > 0.0 ? 1.0 / inertia.y : 0.0, + inertia.z > 0.0 ? 1.0 / inertia.z : 0.0, + ) + : Vector3.zero(); + /// Whether the solver may skip this body. /// /// A stack of ten crates that has settled costs nothing to leave alone, and @@ -110,6 +238,7 @@ final class RigidBody { _asleep = true; _still = 0.0; velocity.setZero(); + angularVelocity.setZero(); } /// Changes the velocity by an impulse, in newton-seconds. @@ -124,12 +253,114 @@ final class RigidBody { velocity.addScaled(impulse, inverseMass); } + /// An impulse delivered at [worldPoint] rather than through the centre. + /// + /// The same change of velocity as [applyImpulse] — where it lands does not + /// change how far the centre is thrown — plus the spin of `r × impulse`, + /// with `r` from the centre to the point. A body that does not [rotates] + /// takes the first half only, which is what infinite inertia means. + void applyImpulseAt(Vector3 impulse, Vector3 worldPoint) { + if (!isMovable) return; + wake(); + velocity.addScaled(impulse, inverseMass); + if (!rotates) return; + final rx = worldPoint.x - position.x; + final ry = worldPoint.y - position.y; + final rz = worldPoint.z - position.z; + _spin( + ry * impulse.z - rz * impulse.y, + rz * impulse.x - rx * impulse.z, + rx * impulse.y - ry * impulse.x, + ); + } + + /// Changes the spin by an angular impulse, in newton-metre-seconds about + /// world axes. Nothing, for a body that does not [rotates]. + void applyTorqueImpulse(Vector3 impulse) { + if (!rotates) return; + wake(); + _spin(impulse.x, impulse.y, impulse.z); + } + + void _spin(double x, double y, double z) { + final (wx, wy, wz) = _inverseWorld.times(x, y, z); + angularVelocity.setValues( + angularVelocity.x + wx, + angularVelocity.y + wy, + angularVelocity.z + wz, + ); + } + + /// Turns the body through one step of its spin. Called by `Dynamics`, not + /// by a game. + /// + /// ## Angular momentum is what is kept, not angular velocity + /// + /// A free body's momentum `L = I·ω` is constant in the world; its velocity + /// is not, because `I` turns with the body. A rod spun about a tilted axis + /// wobbles, and that wobble is `ω` changing while `L` does not. So the step + /// is: take `L` with the old orientation, turn the body by `ω`, and read `ω` + /// back out of `L` with the new one. That *is* the gyroscopic term of + /// Euler's equations, taken implicitly — written out explicitly as + /// `ω × Iω` it is the part of a rigid body integrator that blows up first, + /// and here there is nothing to blow up: `L` is carried through unchanged. + /// + /// The turn itself is `q += ½·dt·(ω ⊗ q)` and a normalisation — first + /// order, and correct to that order for any `ω` because the + /// normalisation is what keeps `q` a rotation. The cost of first order is + /// a slow drift in rotational energy for a body spun off its principal + /// axes; `rotation_test.dart` measures it, and a body spun about one of + /// them keeps both exactly. + void integrateOrientation(double dt) { + if (!rotates) return; + final wx = angularVelocity.x; + final wy = angularVelocity.y; + final wz = angularVelocity.z; + if (wx == 0.0 && wy == 0.0 && wz == 0.0) return; + + final (lx, ly, lz) = _Symmetric.turned( + _orientation, + inertiaLocal, + ).times(wx, wy, wz); + + final x = _orientation.x; + final y = _orientation.y; + final z = _orientation.z; + final s = _orientation.w; + final h = 0.5 * dt; + final nx = x + h * (s * wx + wy * z - wz * y); + final ny = y + h * (s * wy + wz * x - wx * z); + final nz = z + h * (s * wz + wx * y - wy * x); + final ns = s - h * (wx * x + wy * y + wz * z); + final length = math.sqrt(nx * nx + ny * ny + nz * nz + ns * ns); + _orientation.setValues(nx / length, ny / length, nz / length, ns / length); + refreshInertia(); + + final keep = 1.0 / (1.0 + dt * angularDamping); + final (ox, oy, oz) = _inverseWorld.times(lx, ly, lz); + angularVelocity.setValues(ox * keep, oy * keep, oz * keep); + } + /// Everything a snapshot has to carry. + /// + /// **The orientation and spin only for a body that can turn.** One that + /// cannot has no spin and an orientation no step changes, and writing them + /// anyway would change the digest of every saved level that holds a crate — + /// the simulation's run digests fold these maps in — for no information. Map save() => { 'at': [position.x, position.y, position.z], 'velocity': [velocity.x, velocity.y, velocity.z], 'asleep': _asleep, 'still': _still, + if (canRotate) ...{ + 'orientation': [ + _orientation.x, + _orientation.y, + _orientation.z, + _orientation.w, + ], + 'spin': [angularVelocity.x, angularVelocity.y, angularVelocity.z], + }, }; void restore(Map from) { @@ -139,12 +370,38 @@ final class RigidBody { collider.clearDelta(); _asleep = from['asleep'] == true; _still = (from['still'] as num?)?.toDouble() ?? 0.0; + + // A body that cannot turn wrote neither key, and nothing a step does moves + // its orientation, so there is nothing to put back. + if (!canRotate) return; + + // **Absent is not the same as unreadable.** A snapshot without the keys + // was written for a body that did not turn, and what it says is "the + // identity and no spin" — so that is what is restored, rather than + // whatever this body happens to be doing. A key that is there and cannot + // be read leaves the body as it is, as `readVector` does. + if (from.containsKey('orientation')) { + readQuaternion(from['orientation'], _orientation); + } else { + _orientation.setValues(0.0, 0.0, 0.0, 1.0); + } + if (from.containsKey('spin')) { + readVector(from['spin'], angularVelocity); + } else { + angularVelocity.setZero(); + } + refreshInertia(); } /// Advances the sleep clock. Called by the world, not by a game. + /// + /// Spin counts as moving, in radians per second against the same number: + /// a body turning on the spot is not at rest, and putting it to sleep would + /// stop it dead. void updateSleep(double dt, double speedThreshold, double after) { if (_asleep) return; - if (velocity.length2 > speedThreshold * speedThreshold) { + final threshold = speedThreshold * speedThreshold; + if (velocity.length2 > threshold || angularVelocity.length2 > threshold) { _still = 0.0; return; } @@ -152,3 +409,46 @@ final class RigidBody { if (_still >= after) sleep(); } } + +/// A symmetric 3×3 matrix, in doubles: the six numbers that are not mirrors. +/// +/// What an inertia tensor is once it has been turned into world axes. Doubles +/// rather than `vector_math`'s `Matrix3`, which stores single precision — a +/// tensor rounded to seven digits between every use is a body whose angular +/// momentum leaks out through the rounding. +final class _Symmetric { + const _Symmetric(this.xx, this.yy, this.zz, this.xy, this.xz, this.yz); + + static const _Symmetric zero = _Symmetric(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + + /// `R · diag(d) · Rᵀ`, with `R` the rotation of the unit quaternion [q]. + factory _Symmetric.turned(Quaternion q, Vector3 d) { + final x = q.x, y = q.y, z = q.z, w = q.w; + final r00 = 1.0 - 2.0 * (y * y + z * z); + final r01 = 2.0 * (x * y - z * w); + final r02 = 2.0 * (x * z + y * w); + final r10 = 2.0 * (x * y + z * w); + final r11 = 1.0 - 2.0 * (x * x + z * z); + final r12 = 2.0 * (y * z - x * w); + final r20 = 2.0 * (x * z - y * w); + final r21 = 2.0 * (y * z + x * w); + final r22 = 1.0 - 2.0 * (x * x + y * y); + final dx = d.x, dy = d.y, dz = d.z; + return _Symmetric( + r00 * r00 * dx + r01 * r01 * dy + r02 * r02 * dz, + r10 * r10 * dx + r11 * r11 * dy + r12 * r12 * dz, + r20 * r20 * dx + r21 * r21 * dy + r22 * r22 * dz, + r00 * r10 * dx + r01 * r11 * dy + r02 * r12 * dz, + r00 * r20 * dx + r01 * r21 * dy + r02 * r22 * dz, + r10 * r20 * dx + r11 * r21 * dy + r12 * r22 * dz, + ); + } + + final double xx, yy, zz, xy, xz, yz; + + (double, double, double) times(double x, double y, double z) => ( + xx * x + xy * y + xz * z, + xy * x + yy * y + yz * z, + xz * x + yz * y + zz * z, + ); +} diff --git a/packages/flutter3d_physics/lib/src/snapshot.dart b/packages/flutter3d_physics/lib/src/snapshot.dart index db4c60347..0948c6581 100644 --- a/packages/flutter3d_physics/lib/src/snapshot.dart +++ b/packages/flutter3d_physics/lib/src/snapshot.dart @@ -1,3 +1,5 @@ +import 'dart:math' as math; + import 'package:vector_math/vector_math.dart'; /// Reading a saved body back. @@ -26,6 +28,38 @@ bool readVector(Object? value, Vector3 out) { return true; } +/// Reading a saved orientation back, by the same rules as [readVector]. +/// +/// **Written as it was saved, not renormalised**, unless it is plainly not a +/// rotation. What `save()` wrote is already unit length to the precision it is +/// stored in, and normalising it again moves its last bits — which is a +/// restored run that is not the run that was saved. A hand-edited document +/// that says `[0, 0, 1, 1]` is a different matter, and is scaled to unit +/// length rather than left to stretch the body it turns. Zero length and +/// non-finite values are refused, leaving [out] as it was. +bool readQuaternion(Object? value, Quaternion out) { + if (value is! List || value.length < 4) return false; + final x = value[0], y = value[1], z = value[2], w = value[3]; + if (x is! num || y is! num || z is! num || w is! num) return false; + final (qx, qy, qz, qw) = ( + x.toDouble(), + y.toDouble(), + z.toDouble(), + w.toDouble(), + ); + final length2 = qx * qx + qy * qy + qz * qz + qw * qw; + if (!(length2 > 0.0) || !length2.isFinite) return false; + // A single-precision unit quaternion is within a few millionths of length + // one; anything further out was not written by `save()`. + if ((length2 - 1.0).abs() <= 1e-5) { + out.setValues(qx, qy, qz, qw); + } else { + final length = math.sqrt(length2); + out.setValues(qx / length, qy / length, qz / length, qw / length); + } + return true; +} + /// A number from a snapshot, or nought. /// /// Nought rather than a thrown error for the same reason: what these carry are diff --git a/packages/flutter3d_physics/test/portable_math_test.dart b/packages/flutter3d_physics/test/portable_math_test.dart new file mode 100644 index 000000000..6b4d04d8c --- /dev/null +++ b/packages/flutter3d_physics/test/portable_math_test.dart @@ -0,0 +1,221 @@ +/// The trigonometry a turning body is allowed, asked two questions. +/// +/// dart test test/portable_math_test.dart +/// +/// 1. **The same bits every time and everywhere.** One recorded digest per +/// function over a fixed sweep of arguments. A second answer appearing on +/// some platform is the failure the library exists to rule out. +/// 2. **The right answer.** The same sweep against `dart:math`, in units in +/// the last place. Both platforms agreeing on a wrong number would pass +/// every digest. +library; + +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:flutter3d_physics/src/portable_math.dart'; +import 'package:test/test.dart'; + +void main() { + group('the same answer everywhere', () { + // Recorded on macOS-arm64 under the VM, 2026-10-01, where the same sweep + // through `flutter3d_sim`'s `Portable` answered the same bits for every + // one of the 20000 arguments of every function. + // + // Mutation: a coefficient in either kernel changed in its last digit, or + // `_quarterTurns` taking π/2 in one piece, changes every one of these. + for (final row in <(String, int, double Function(int))>[ + ('sin', 1047661232, (int i) => Portable.sin(_a[i])), + ('cos', 19596382, (int i) => Portable.cos(_a[i])), + ('atan', 3618538760, (int i) => Portable.atan(_a[i])), + ('atan2', 273388763, (int i) => Portable.atan2(_a[i], _b[i])), + ('asin', 1248226849, (int i) => Portable.asin(_a[i].abs() % 1.0)), + ('acos', 730462323, (int i) => Portable.acos(_a[i].abs() % 2.0 - 1.0)), + ]) { + final (name, expected, at) = row; + test('$name gives one answer, not one per platform', () { + expect( + _digest([for (var i = 0; i < _sweep; i++) at(i)]), + expected, + reason: + 'Portable.$name answered differently here than where this was ' + 'recorded. Everything in it is IEEE arithmetic over the same ' + 'bits, so that is an edit that changed its answers or a platform ' + 'whose `*` is not the specification\'s. Either is news.', + ); + }); + } + + test('sinCos agrees with sin and cos taken apart', () { + // Mutation: a quadrant table in `sinCos` with two rows swapped passes + // every digest above and turns an axis the wrong way a quarter of the + // time. + for (var i = 0; i < _sweep; i++) { + final both = Portable.sinCos(_a[i]); + expect(both.sin, Portable.sin(_a[i])); + expect(both.cos, Portable.cos(_a[i])); + } + }); + }); + + group('and the right answer', () { + // Mutation: any coefficient typed wrong in its fourth digit — which no + // digest notices, because it is wrong the same way everywhere. + for (final row + in <(String, double Function(double), double Function(double))>[ + ('sin', Portable.sin, math.sin), + ('cos', Portable.cos, math.cos), + ('atan', Portable.atan, math.atan), + ]) { + final (name, ours, theirs) = row; + test('$name is within two ulp of dart:math', () { + final (worst, at) = _worst( + _sweep, + (int i) => (ours(_a[i]), theirs(_a[i]), _a[i]), + ); + expect(worst, lessThanOrEqualTo(2.0), reason: '$name at $at'); + }); + } + + test('atan2 is within two ulp of dart:math, in all four quadrants', () { + final (worst, at) = _worst( + _sweep, + (int i) => + (Portable.atan2(_a[i], _b[i]), math.atan2(_a[i], _b[i]), _a[i]), + ); + expect(worst, lessThanOrEqualTo(2.0), reason: 'atan2 at $at'); + // The signed zeros and infinities, which are conventions rather than + // limits and are where a hand port usually slips. + for (final (y, x) in <(double, double)>[ + (0.0, -1.0), + (-0.0, -1.0), + (0.0, 1.0), + (1.0, 0.0), + (-1.0, 0.0), + (double.infinity, double.infinity), + (double.infinity, double.negativeInfinity), + (1.0, double.negativeInfinity), + ]) { + expect(Portable.atan2(y, x), math.atan2(y, x), reason: 'atan2($y, $x)'); + } + }); + + test('asin is within four ulp of dart:math, including at the ends', () { + final (worst, at) = _worst(_sweep + 4, (int i) { + final x = i < _sweep + ? _a[i].abs() % 1.0 + : const [1.0, -1.0, 0.9999999999, -0.9999999999][i - + _sweep]; + return (Portable.asin(x), math.asin(x), x); + }); + expect(worst, lessThanOrEqualTo(4.0), reason: 'asin at $at'); + }); + + test('acos is within two ulp of dart:math, including near one', () { + // Near one is where a joint at rest sits, and where `π/2 - asin(x)` + // would cancel away nearly every digit. + // + // Mutation: `acos` written as `π/2 - asin(x)` is out by thousands of + // ulp at 0.9999999999. + const ends = [1.0, -1.0, 0.9999999999, -0.9999999999, 0.0]; + final (worst, at) = _worst(_sweep + ends.length, (int i) { + final x = i < _sweep ? _a[i].abs() % 2.0 - 1.0 : ends[i - _sweep]; + return (Portable.acos(x), math.acos(x), x); + }); + expect(worst, lessThanOrEqualTo(2.0), reason: 'acos at $at'); + }); + + test('and an argument it cannot answer is NaN, not a throw', () { + expect(Portable.sin(double.infinity), isNaN); + expect(Portable.cos(double.nan), isNaN); + expect(Portable.acos(1.5), isNaN); + expect(Portable.asin(-1.5), isNaN); + expect(Portable.sinCos(double.infinity).sin, isNaN); + }); + }); +} + +/// The largest distance in ulp across [count] cases, and the argument it was +/// found at. +(double, double) _worst(int count, (double, double, double) Function(int) at) { + return Iterable.generate(count).map(at).fold<(double, double)>( + (0.0, 0.0), + (worst, row) { + final (ours, theirs, x) = row; + final error = _ulpsApart(ours, theirs); + return error > worst.$1 ? (error, x) : worst; + }, + ); +} + +/// How far apart [a] and [b] are, in units of the last place at [b]. +/// +/// A distance over an ulp rather than a difference of bit patterns: those are +/// near 2^62 and their difference taken in a double rounds to a multiple of +/// 1024, which is what an instrument measuring itself looks like. +double _ulpsApart(double a, double b) { + if (a == b) return 0.0; + if (a.isNaN && b.isNaN) return 0.0; + if (a.isNaN || b.isNaN || a.isInfinite || b.isInfinite) { + return double.infinity; + } + return (a - b).abs() / _ulpSize(b); +} + +/// The gap between [x] and the next double away from zero, found by adding one +/// to the mantissa — through `ByteData` in halves, because a browser's integers +/// stop being exact at 2^53. +double _ulpSize(double x) { + final magnitude = x.abs(); + if (magnitude == 0.0 || !magnitude.isFinite) return double.minPositive; + _bits.setFloat64(0, magnitude); + final low = _bits.getUint32(4); + if (low == 0xFFFFFFFF) { + _bits.setUint32(0, _bits.getUint32(0) + 1); + _bits.setUint32(4, 0); + } else { + _bits.setUint32(4, low + 1); + } + return _bits.getFloat64(0) - magnitude; +} + +final ByteData _bits = ByteData(8); + +/// FNV-1a over the eight bytes of every double, as an unsigned 32-bit number. +/// +/// The multiply by the FNV prime is split as `2^24 + 403` so no product passes +/// 2^53: on the web an integer is a double, and a product past that rounds — +/// the digest would then be a property of the platform it was checking. +int _digest(List values) { + final bytes = ByteData(8); + return values.fold(0x811C9DC5, (hash, value) { + bytes.setFloat64(0, value); + return Iterable.generate(8).fold(hash, (h, i) { + final mixed = h ^ bytes.getUint8(i); + return ((mixed % 256) * 16777216 + mixed * 403) % 4294967296; + }); + }); +} + +const int _sweep = 20000; + +/// Arguments in four bands — a unit interval, two turns either way, a thousand +/// radians, and a ten-thousandth — drawn from a golden-ratio sequence. +/// +/// **Built from double arithmetic alone**, so the arguments are the same bits +/// on every platform before any function is asked about them: `%` on doubles +/// is exact, and so is a product rounded once. +List _sweepArguments(double offset) => [ + for (var i = 0; i < _sweep; i++) + switch (((i + offset) * 0.6180339887498949) % 1.0 * 2.0 - 1.0) { + final u => switch (i % 4) { + 0 => u, + 1 => u * math.pi * 2.0, + 2 => u * 1000.0, + _ => u * 1e-4, + }, + }, +]; + +final List _a = _sweepArguments(0.5); +final List _b = _sweepArguments(0.25); diff --git a/packages/flutter3d_physics/test/rotation_test.dart b/packages/flutter3d_physics/test/rotation_test.dart new file mode 100644 index 000000000..62ec3c11b --- /dev/null +++ b/packages/flutter3d_physics/test/rotation_test.dart @@ -0,0 +1,388 @@ +/// Bodies that turn: inertia per shape, spin from an off-centre impulse, a +/// free body keeping its angular momentum, and a snapshot that resumes the +/// same run. +/// +/// dart test test/rotation_test.dart +/// +/// The first piece of stage two. No contact turns a body yet, so every scene +/// here is a body in free flight; what is being held is the integrator, not +/// the solver. +library; + +import 'dart:convert'; + +import 'package:flutter3d_physics/flutter3d_physics.dart'; +import 'package:flutter3d_physics/src/portable_math.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +const double _dt = 1.0 / 60.0; + +void main() { + group('inertia', () { + test('a unit cube of six kilograms is one about every axis', () { + // m·s²/6 for a cube of side s. Mutation: the box formula with full + // widths where it wants half extents is four times this. + final i = inertiaFor(CollisionBox(Vector3.all(0.5)), 6.0); + expect(i.x, closeTo(1.0, 1e-6)); + expect(i.y, closeTo(1.0, 1e-6)); + expect(i.z, closeTo(1.0, 1e-6)); + }); + + test('a box is heaviest about the axis its two long sides are across', () { + // Mutation: any two of the three moments swapped. + final i = inertiaFor(CollisionBox(Vector3(0.5, 1.0, 1.5)), 3.0); + expect(i.x, closeTo(1.0 * (1.0 + 2.25), 1e-6)); + expect(i.y, closeTo(1.0 * (0.25 + 2.25), 1e-6)); + expect(i.z, closeTo(1.0 * (0.25 + 1.0), 1e-6)); + }); + + test('a sphere is two fifths of m r²', () { + final i = inertiaFor(CollisionSphere(2.0), 5.0); + expect(i, Vector3.all(8.0)); + }); + + test('a capsule with no cylinder is the sphere it is', () { + // Mutation: the caps' parallel-axis term with `h` where it wants `h²` + // still passes here, which is why the next test exists. + final capsule = inertiaFor( + CollisionCapsule(radius: 0.4, halfHeight: 0.0), + 2.0, + ); + final sphere = inertiaFor(CollisionSphere(0.4), 2.0); + expect(capsule.x, closeTo(sphere.x, 1e-6)); + expect(capsule.y, closeTo(sphere.y, 1e-6)); + }); + + test('a long capsule agrees with its volume summed point by point', () { + // A grid of points through the bounding box, kept where they fall + // inside the capsule, each carrying an equal share of the mass. Half a + // percent at this resolution; the closed form is exact. + // + // Mutation: the capsule treated as a cylinder of its whole height is + // several percent out on both axes. + const r = 0.3, h = 0.6, mass = 4.0, n = 80; + final shape = CollisionCapsule(radius: r, halfHeight: h); + final step = 2.0 * (h + r) / n; + var (count, across, axial) = (0, 0.0, 0.0); + for (var ix = 0; ix < n; ix++) { + for (var iy = 0; iy < n; iy++) { + for (var iz = 0; iz < n; iz++) { + final x = -(h + r) + (ix + 0.5) * step; + final y = -(h + r) + (iy + 0.5) * step; + final z = -(h + r) + (iz + 0.5) * step; + final dy = y.abs() > h ? y.abs() - h : 0.0; + if (x * x + z * z + dy * dy > r * r) continue; + count++; + across += y * y + z * z; + axial += x * x + z * z; + } + } + } + final i = inertiaFor(shape, mass); + expect(i.x, closeTo(mass * across / count, i.x * 0.005)); + expect(i.y, closeTo(mass * axial / count, i.y * 0.005)); + expect(i.z, i.x); + }); + + test('a wedge is its bounding box, as the library says it is', () { + final wedge = inertiaFor(CollisionWedge(Vector3(1.0, 0.5, 2.0)), 3.0); + expect(wedge, inertiaFor(CollisionBox(Vector3(1.0, 0.5, 2.0)), 3.0)); + }); + + test('and nothing for no mass', () { + expect(inertiaFor(CollisionSphere(1.0), 0.0), Vector3.zero()); + }); + }); + + group('a body that was not asked to turn', () { + test('does not, whatever it is given', () { + // The promise every shipped level rests on. Mutation: `canRotate` + // defaulting to true, or `inverseInertiaLocal` taken from the shape + // regardless of it. + final (dynamics, body) = _floating(canRotate: false); + body + ..applyTorqueImpulse(Vector3(5.0, 5.0, 5.0)) + ..applyImpulseAt( + Vector3(0.0, 0.0, 10.0), + body.position + Vector3(1.0, 0.0, 0.0), + ); + for (var i = 0; i < 60; i++) { + dynamics.step(_dt); + } + expect(body.angularVelocity, Vector3.zero()); + expect(body.orientation, Quaternion.identity()); + expect(body.inverseInertiaLocal, Vector3.zero()); + }); + + test('and its snapshot has the keys it always had', () { + // Mutation: writing the orientation for every body changes the digest + // of every saved level holding a crate. + final (_, body) = _floating(canRotate: false); + expect(body.save().keys, ['at', 'velocity', 'asleep', 'still']); + }); + + test('and an off-centre impulse still moves it as a central one would', () { + final (_, a) = _floating(canRotate: false); + final (_, b) = _floating(canRotate: false); + a.applyImpulse(Vector3(0.0, 0.0, 6.0)); + b.applyImpulseAt( + Vector3(0.0, 0.0, 6.0), + b.position + Vector3(1.0, 0.0, 0.0), + ); + expect(b.velocity, a.velocity); + }); + }); + + group('a body that turns', () { + test('spins by r × J through the inverse tensor', () { + // A unit cube of six kilograms has I = 1, so the spin is the angular + // impulse itself: a push along +z one metre out along +x is a turn + // about -y. Mutation: the cross product's operands swapped turns it + // the other way. + final (_, body) = _floating(); + body.applyImpulseAt( + Vector3(0.0, 0.0, 2.0), + body.position + Vector3(1.0, 0.0, 0.0), + ); + expect(body.velocity.z, closeTo(2.0 / 6.0, 1e-6)); + expect(body.angularVelocity.x, closeTo(0.0, 1e-6)); + expect(body.angularVelocity.y, closeTo(-2.0, 1e-6)); + expect(body.angularVelocity.z, closeTo(0.0, 1e-6)); + }); + + test('turns the way a matrix drawn from it turns, by the angle spun', () { + // A quarter turn about +y in one second takes +x to -z. Held through + // `asRotationMatrix`, the matrix `Matrix4.compose` builds and a renderer + // draws with, rather than `Quaternion.rotated`: that one computes + // `q* v q`, the inverse turn, and would put this body's +x at +z. + // + // Mutation: `ω ⊗ q` written as `q ⊗ ω` turns about the body's axes + // rather than the world's, which agrees here and nowhere else — so the + // second half starts the body already turned. The sign of `ω` flipped in + // the update fails the first half. + final (dynamics, body) = _floating(); + body.applyTorqueImpulse(Vector3(0.0, Portable.atan(1.0) * 2.0, 0.0)); + for (var i = 0; i < 60; i++) { + dynamics.step(_dt); + } + final x = body.orientation.asRotationMatrix().transformed( + Vector3(1.0, 0.0, 0.0), + ); + expect(x.x, closeTo(0.0, 1e-3)); + expect(x.z, closeTo(-1.0, 1e-3)); + + final (turnedDynamics, turned) = _floating( + orientation: Quaternion( + 0.0, + 0.0, + 0.7071067811865476, + 0.7071067811865476, + ), + ); + turned.applyTorqueImpulse(Vector3(0.0, Portable.atan(1.0) * 2.0, 0.0)); + for (var i = 0; i < 60; i++) { + turnedDynamics.step(_dt); + } + // Body +x was world +y before; a world-axis turn about +y leaves it there. + final up = turned.orientation.asRotationMatrix().transformed( + Vector3(1.0, 0.0, 0.0), + ); + expect(up.y, closeTo(1.0, 1e-3)); + }); + + test('has a world tensor that is R · I⁻¹ · Rᵀ', () { + final (_, body) = _floating( + shape: CollisionBox(Vector3(0.2, 0.5, 1.0)), + orientation: Quaternion(0.3, -0.2, 0.5, 0.8), + ); + final expected = _turned(body.orientation, body.inverseInertiaLocal); + for (var i = 0; i < 9; i++) { + expect( + body.inverseInertiaWorld.storage[i], + closeTo(expected.storage[i], 1e-4), + ); + } + }); + + test( + 'keeps its angular momentum, and nearly its energy, spinning free', + () { + // Spun about no principal axis, so ω wobbles while L stays put. Ten + // seconds at sixty steps a second. + // + // Mutation: `integrateOrientation` keeping ω and dropping the turn of + // the tensor — no gyroscopic term — lets L wander by tens of percent + // within the first second. + final (dynamics, body) = _floating( + shape: CollisionBox(Vector3(0.2, 0.5, 1.0)), + ); + body.applyTorqueImpulse(Vector3(0.4, 0.3, 0.2)); + final l0 = _momentum(body); + final e0 = 0.5 * body.angularVelocity.dot(l0); + var worstL = 0.0; + var worstE = 0.0; + for (var i = 0; i < 600; i++) { + dynamics.step(_dt); + final l = _momentum(body); + final e = 0.5 * body.angularVelocity.dot(l); + final dl = (l - l0).length / l0.length; + final de = (e - e0).abs() / e0; + if (dl > worstL) worstL = dl; + if (de > worstE) worstE = de; + } + expect(worstL, lessThan(1e-4), reason: 'angular momentum drifted'); + expect(worstE, lessThan(0.02), reason: 'rotational energy drifted'); + expect(body.isAsleep, isFalse); + }, + ); + + test('is not put to sleep for standing still while it spins', () { + // Mutation: `updateSleep` reading only the linear velocity stops this + // dead half a second in. + final (dynamics, body) = _floating(); + body.applyTorqueImpulse(Vector3(0.0, 3.0, 0.0)); + for (var i = 0; i < 120; i++) { + dynamics.step(_dt); + } + expect(body.isAsleep, isFalse); + expect(body.angularVelocity.y, closeTo(3.0, 1e-5)); + }); + + test( + 'loses spin to damping as one over one plus dt times it, per step', + () { + final (dynamics, body) = _floating(angularDamping: 2.0); + body.applyTorqueImpulse(Vector3(0.0, 3.0, 0.0)); + for (var i = 0; i < 60; i++) { + dynamics.step(_dt); + } + var expected = 3.0; + for (var i = 0; i < 60; i++) { + expected /= 1.0 + _dt * 2.0; + } + expect(body.angularVelocity.y, closeTo(expected, 1e-4)); + }, + ); + }); + + group('a snapshot', () { + test('resumes the same run, bit for bit', () { + // Through JSON, as a save on disk is. No contacts in the scene: the + // warm-start impulses `Dynamics` carries between steps are not part of + // a body's snapshot, and a contact would make this a test of that. + // + // Mutation: `restore` renormalising the orientation it reads, or + // forgetting to refresh the world tensor, changes the last bits. + final (straight, through) = (_tumbling(), _tumbling()); + for (var i = 0; i < 100; i++) { + straight.$1.step(_dt); + through.$1.step(_dt); + } + final saved = + jsonDecode(jsonEncode(through.$2.save())) as Map; + + final resumed = _tumbling(); + resumed.$2.restore(saved); + for (var i = 0; i < 100; i++) { + straight.$1.step(_dt); + resumed.$1.step(_dt); + } + final a = straight.$2, b = resumed.$2; + expect(b.position.storage, a.position.storage); + expect(b.velocity.storage, a.velocity.storage); + expect(b.orientation.storage, a.orientation.storage); + expect(b.angularVelocity.storage, a.angularVelocity.storage); + }); + + test('without the new keys is a body that is not turning', () { + // A snapshot written for a body that could not turn says "the identity + // and no spin" by leaving them out, and that is what it restores to. + final (_, body) = _floating(); + body + ..orientation = Quaternion(0.0, 0.0, 1.0, 1.0) + ..applyTorqueImpulse(Vector3(1.0, 2.0, 3.0)); + body.restore({ + 'at': [0.0, 10.0, 0.0], + 'velocity': [0.0, 0.0, 0.0], + }); + expect(body.orientation, Quaternion.identity()); + expect(body.angularVelocity, Vector3.zero()); + }); + + test('with a key it cannot read leaves the body as it was', () { + final (_, body) = _floating(); + body.orientation = Quaternion(0.0, 0.0, 1.0, 1.0); + final before = body.orientation.clone(); + body.restore({ + 'at': [0.0, 10.0, 0.0], + 'orientation': [0.0, 'up', 0.0, 1.0], + 'spin': [0.0, 1.0, 0.0], + }); + expect(body.orientation, before); + expect(body.angularVelocity.y, 1.0); + }); + + test('and an orientation edited by hand is made a rotation', () { + final out = Quaternion.identity(); + expect(readQuaternion([0.0, 0.0, 1.0, 1.0], out), isTrue); + expect(out.length, closeTo(1.0, 1e-6)); + expect(readQuaternion([0.0, 0.0, 0.0, 0.0], out), isFalse); + }); + }); +} + +/// A six-kilogram unit cube, or [shape], hanging in a world with no gravity. +(Dynamics, RigidBody) _floating({ + bool canRotate = true, + CollisionShape? shape, + Quaternion? orientation, + double angularDamping = 0.0, +}) { + final dynamics = Dynamics(world: CollisionWorld(), gravity: Vector3.zero()); + final body = dynamics.add( + RigidBody( + world: dynamics.world, + shape: shape ?? CollisionBox(Vector3.all(0.5)), + position: Vector3(0.0, 10.0, 0.0), + mass: 6.0, + canRotate: canRotate, + orientation: orientation, + angularDamping: angularDamping, + ), + ); + return (dynamics, body); +} + +/// A long box thrown upwards and tumbling under gravity, far from anything. +(Dynamics, RigidBody) _tumbling() { + final dynamics = Dynamics(world: CollisionWorld()); + final body = dynamics.add( + RigidBody( + world: dynamics.world, + shape: CollisionBox(Vector3(0.2, 0.5, 1.0)), + position: Vector3(0.0, 100.0, 0.0), + mass: 3.0, + canRotate: true, + angularDamping: 0.1, + ), + ); + body.applyImpulseAt( + Vector3(0.5, 30.0, 0.2), + body.position + Vector3(0.3, 0.0, 0.8), + ); + return (dynamics, body); +} + +/// `I_world · ω`, worked out the long way, independently of the body. +Vector3 _momentum(RigidBody body) => _turned( + body.orientation, + body.inertiaLocal, +).transformed(body.angularVelocity); + +/// `R · diag(d) · Rᵀ` through `vector_math`'s own rotation matrix, so the +/// body's hand-written one is checked against something it did not write. +Matrix3 _turned(Quaternion q, Vector3 d) { + final r = q.asRotationMatrix(); + return r.multiplied(Matrix3.zero()..setDiagonal(d))..multiply(r.transposed()); +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 9c03ac575..9895ca44a 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11168 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11195 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index d948fe47b..32aa05b1e 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11168 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11195 tests need a GPU. --- # Testing -11168 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11195 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -23,7 +23,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_cpu` | 364 | | `apps/flutter3d_demo_strategy` | 50 | | `apps/flutter3d_editor` | 234 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | -| `flutter3d_physics` | 187 | | `flutter3d_webgpu` | 193 | +| `flutter3d_physics` | 214 | | `flutter3d_webgpu` | 193 | | `flutter3d_game_strategy` | 133 | | `flutter3d_editor_mcp` | 36 | | `apps/flutter3d_demo_river` | 54 | | `flutter3d_testing` | 38 | | `flutter3d_editor_core` | 141 | | `flutter3d_editor_widgets` | 91 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11142 rather than 11168: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11169 rather than 11195: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 0d67a2104..2e2e5b345 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -510,6 +510,26 @@ Ragdoll in §5 depends on it. *Tests:* stacking, tipping and pendulum scenes compared by digest on two operating systems; energy drift bounds. +**P9 progress.** *Done:* `RigidBody` has an `orientation`, an +`angularVelocity`, principal moments from `inertiaFor` (box, sphere, capsule +exact; wedge and heightfield as their bounding box) and a world inverse +tensor kept in doubles; `applyImpulseAt` and `applyTorqueImpulse` spin it. +Only a body built with `canRotate: true` turns, so every shipped level steps +and saves exactly as before. The step keeps angular momentum rather than +angular velocity (the gyroscopic term, implicitly) and turns the body in the +convention `asRotationMatrix` draws. `save()` carries orientation and spin +for a turning body only. `src/portable_math.dart` gives the step `sin`, +`cos`, `atan`, `atan2`, `asin` and `acos` with the same bits as +`flutter3d_sim`'s `Portable`, for the joints. +*Tests:* `flutter3d_physics/test/rotation_test.dart` (inertia per shape, +r × J, a free tumble holding L to 1e-4 over ten seconds, sleep, damping, a +snapshot through JSON that resumes bit for bit) and `portable_math_test.dart` +(digests and ulp bounds against `dart:math`). +*Left:* contact manifolds with friction that turn bodies, and an oriented +collider (a turning box still collides axis-aligned); joints; swept shapes; +a triangle-mesh shape; a 3D broadphase; the stacking, tipping and pendulum +digests on a second operating system; ragdoll. + ### 4.4 Declarative widget API **P10. Scene as widgets (L).** `Scene3D`, `SceneNode`, `SceneMesh`, From fb401c946951cd590eec1d893598ebfa5699efd5 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:38:18 +0300 Subject: [PATCH 048/141] The level editor has Windows and Linux runners, and Stop ends the tool there The editor gains runners for Windows and Linux beside macOS, from the Flutter 3.47.0 templates, with the Linux application id matching the macOS bundle id. The desktop CI job builds the editor on both beside the dungeon demo. Play starts flutter through cmd.exe on Windows, where Process.kill ended the shell and left the tool building. A stop before the app has started now kills the whole tree with taskkill there; elsewhere the pid is already the tool's. --- .github/workflows/ci.yml | 27 +- ARCHITECTURE.md | 2 +- README.md | 2 +- apps/flutter3d_editor/.metadata | 6 + apps/flutter3d_editor/README.md | 12 +- apps/flutter3d_editor/lib/main.dart | 6 +- apps/flutter3d_editor/linux/.gitignore | 1 + apps/flutter3d_editor/linux/CMakeLists.txt | 128 ++++++++ .../linux/flutter/CMakeLists.txt | 88 ++++++ .../flutter/generated_plugin_registrant.cc | 15 + .../flutter/generated_plugin_registrant.h | 15 + .../linux/flutter/generated_plugins.cmake | 26 ++ .../linux/runner/CMakeLists.txt | 26 ++ apps/flutter3d_editor/linux/runner/main.cc | 6 + .../linux/runner/my_application.cc | 148 +++++++++ .../linux/runner/my_application.h | 21 ++ apps/flutter3d_editor/pubspec.yaml | 11 +- apps/flutter3d_editor/windows/.gitignore | 17 ++ apps/flutter3d_editor/windows/CMakeLists.txt | 108 +++++++ .../windows/flutter/CMakeLists.txt | 109 +++++++ .../flutter/generated_plugin_registrant.cc | 14 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 26 ++ .../windows/runner/CMakeLists.txt | 40 +++ .../flutter3d_editor/windows/runner/Runner.rc | 121 ++++++++ .../windows/runner/flutter_window.cpp | 71 +++++ .../windows/runner/flutter_window.h | 33 ++ apps/flutter3d_editor/windows/runner/main.cpp | 43 +++ .../windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 14 + .../flutter3d_editor/windows/runner/utils.cpp | 69 +++++ apps/flutter3d_editor/windows/runner/utils.h | 19 ++ .../windows/runner/win32_window.cpp | 288 ++++++++++++++++++ .../windows/runner/win32_window.h | 102 +++++++ packages/flutter3d_editor_play/CHANGELOG.md | 5 + .../lib/src/flutter_run.dart | 30 +- .../test/flutter_run_test.dart | 32 ++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- 40 files changed, 1692 insertions(+), 30 deletions(-) create mode 100644 apps/flutter3d_editor/linux/.gitignore create mode 100644 apps/flutter3d_editor/linux/CMakeLists.txt create mode 100644 apps/flutter3d_editor/linux/flutter/CMakeLists.txt create mode 100644 apps/flutter3d_editor/linux/flutter/generated_plugin_registrant.cc create mode 100644 apps/flutter3d_editor/linux/flutter/generated_plugin_registrant.h create mode 100644 apps/flutter3d_editor/linux/flutter/generated_plugins.cmake create mode 100644 apps/flutter3d_editor/linux/runner/CMakeLists.txt create mode 100644 apps/flutter3d_editor/linux/runner/main.cc create mode 100644 apps/flutter3d_editor/linux/runner/my_application.cc create mode 100644 apps/flutter3d_editor/linux/runner/my_application.h create mode 100644 apps/flutter3d_editor/windows/.gitignore create mode 100644 apps/flutter3d_editor/windows/CMakeLists.txt create mode 100644 apps/flutter3d_editor/windows/flutter/CMakeLists.txt create mode 100644 apps/flutter3d_editor/windows/flutter/generated_plugin_registrant.cc create mode 100644 apps/flutter3d_editor/windows/flutter/generated_plugin_registrant.h create mode 100644 apps/flutter3d_editor/windows/flutter/generated_plugins.cmake create mode 100644 apps/flutter3d_editor/windows/runner/CMakeLists.txt create mode 100644 apps/flutter3d_editor/windows/runner/Runner.rc create mode 100644 apps/flutter3d_editor/windows/runner/flutter_window.cpp create mode 100644 apps/flutter3d_editor/windows/runner/flutter_window.h create mode 100644 apps/flutter3d_editor/windows/runner/main.cpp create mode 100644 apps/flutter3d_editor/windows/runner/resource.h create mode 100644 apps/flutter3d_editor/windows/runner/resources/app_icon.ico create mode 100644 apps/flutter3d_editor/windows/runner/runner.exe.manifest create mode 100644 apps/flutter3d_editor/windows/runner/utils.cpp create mode 100644 apps/flutter3d_editor/windows/runner/utils.h create mode 100644 apps/flutter3d_editor/windows/runner/win32_window.cpp create mode 100644 apps/flutter3d_editor/windows/runner/win32_window.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fee5b0c2c..d471d1665 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -362,17 +362,24 @@ jobs: # this workflow has no way to drive. What a build proves is the half that # actually breaks: the CMake, the plugin registration, and every package in # the graph compiling for a platform none of them had been asked about. + # + # The level editor as well, for P11: it ships desktop downloads for both, so + # its runners are compiled here rather than first by whoever downloads one. + # Its graph brings `file_selector`'s own GTK and Win32 plugins, which the + # dungeon demo does not. desktop: strategy: - # Both, whichever fails: a matrix that stops at the first failure hides - # whether the other one was ever going to work. + # Every cell, whichever fails: a matrix that stops at the first failure + # hides whether the others were ever going to work. fail-fast: false matrix: + target: [linux, windows] + app: [flutter3d_demo_dungeon, flutter3d_editor] include: - - os: ubuntu-latest - target: linux - - os: windows-latest - target: windows + - target: linux + os: ubuntu-latest + - target: windows + os: windows-latest runs-on: ${{ matrix.os }} timeout-minutes: 30 steps: @@ -385,8 +392,8 @@ jobs: cache: true # libasound2-dev is not part of the Flutter desktop toolchain: it is what - # flutter_soloud's own ALSA backend compiles against, and the dungeon demo - # is the application that pulls the audio plugin in. Without it the build + # flutter_soloud's own ALSA backend compiles against, and both applications + # pull the audio plugin in, the editor through `flutter3d_game`. Without it the build # gets all the way to soloud_alsa.cpp and stops at a missing header. - name: linux desktop toolchain if: matrix.target == 'linux' @@ -402,8 +409,8 @@ jobs: - run: flutter precache --${{ matrix.target }} - run: cd packages/flutter3d_impeller && dart run bin/build_shader_bundle.dart - - name: build the dungeon demo - run: cd apps/flutter3d_demo_dungeon && flutter build ${{ matrix.target }} --debug + - name: build ${{ matrix.app }} + run: cd apps/${{ matrix.app }} && flutter build ${{ matrix.target }} --debug godot: runs-on: ubuntu-latest diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index cab1fff65..ef4fb6bdf 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11195 tests** across 43 packages and 10 applications | +| Unit tests | **11197 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index c3c57a84d..5061c90fb 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11195 tests across forty-three packages and nine applications. The +There are 11197 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_editor/.metadata b/apps/flutter3d_editor/.metadata index b15f453e2..75cae063a 100644 --- a/apps/flutter3d_editor/.metadata +++ b/apps/flutter3d_editor/.metadata @@ -18,6 +18,12 @@ migration: - platform: macos create_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05 base_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05 + - platform: linux + create_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05 + base_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05 + - platform: windows + create_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05 + base_revision: 4cf24164269a5ebf0c16a028a00727d0e77bbb05 # User provided section diff --git a/apps/flutter3d_editor/README.md b/apps/flutter3d_editor/README.md index 251184790..e23ee35e6 100644 --- a/apps/flutter3d_editor/README.md +++ b/apps/flutter3d_editor/README.md @@ -348,11 +348,13 @@ so. `⌘S` refuses and says who owns the file. `⇧⌘S` writes `crypt.edited.js beside it and takes ownership of the copy, because a copy still naming the generator invites the same accident. -The editor is desktop only, with no web half. The other three applications -choose between Impeller and WebGL at compile time because they ship to a -browser as well. This one exists to write a file back over itself, which a -browser will not do, so there is no backend to choose and no -`backend_web.dart`. +The editor runs on the desktop: there are runners for macOS, Windows and +Linux, and CI builds the last two. The other applications choose between +Impeller and WebGL at compile time because they ship to a browser as well. +This one was written to save a file back over itself, which a browser will not +do, so for now there is no backend to choose and no `backend_web.dart`. A web +build that opens and edits but cannot Play another project is a later step of +the same roadmap item. The editor has no vocabulary of its own. A level says `monster` or `coin` or `checkpoint`, and what those are worth belongs to the game. The engine's own diff --git a/apps/flutter3d_editor/lib/main.dart b/apps/flutter3d_editor/lib/main.dart index deebecb0d..e995e1398 100644 --- a/apps/flutter3d_editor/lib/main.dart +++ b/apps/flutter3d_editor/lib/main.dart @@ -3,9 +3,9 @@ /// cd apps/flutter3d_editor /// flutter run -d macos --dart-define=level=../flutter3d_demo_dungeon/assets/levels/crypt.json /// -/// **Desktop only, and that is not an omission.** This application exists to -/// write a file back over itself, which a browser will not do — so unlike the -/// three games there is no web build and no backend to choose between. +/// **Desktop first: macOS, Windows and Linux.** This application was written +/// to save a file back over itself, which a browser will not do — so unlike +/// the games there is no web build yet and no backend to choose between. /// /// What is here is the shell: a window, a camera, a mouse and a keyboard. The /// parts that can lose somebody's work are `package:flutter3d_editor_core`, diff --git a/apps/flutter3d_editor/linux/.gitignore b/apps/flutter3d_editor/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/apps/flutter3d_editor/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/apps/flutter3d_editor/linux/CMakeLists.txt b/apps/flutter3d_editor/linux/CMakeLists.txt new file mode 100644 index 000000000..966f2a3b1 --- /dev/null +++ b/apps/flutter3d_editor/linux/CMakeLists.txt @@ -0,0 +1,128 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "flutter3d_editor") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "dev.flutter3d.editor") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/apps/flutter3d_editor/linux/flutter/CMakeLists.txt b/apps/flutter3d_editor/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..d5bd01648 --- /dev/null +++ b/apps/flutter3d_editor/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/apps/flutter3d_editor/linux/flutter/generated_plugin_registrant.cc b/apps/flutter3d_editor/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..64a0ecea4 --- /dev/null +++ b/apps/flutter3d_editor/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); + file_selector_plugin_register_with_registrar(file_selector_linux_registrar); +} diff --git a/apps/flutter3d_editor/linux/flutter/generated_plugin_registrant.h b/apps/flutter3d_editor/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..e0f0a47bc --- /dev/null +++ b/apps/flutter3d_editor/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/apps/flutter3d_editor/linux/flutter/generated_plugins.cmake b/apps/flutter3d_editor/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..423b96a34 --- /dev/null +++ b/apps/flutter3d_editor/linux/flutter/generated_plugins.cmake @@ -0,0 +1,26 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_selector_linux +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + flutter_soloud + jni +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/apps/flutter3d_editor/linux/runner/CMakeLists.txt b/apps/flutter3d_editor/linux/runner/CMakeLists.txt new file mode 100644 index 000000000..e97dabc70 --- /dev/null +++ b/apps/flutter3d_editor/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/apps/flutter3d_editor/linux/runner/main.cc b/apps/flutter3d_editor/linux/runner/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/apps/flutter3d_editor/linux/runner/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/apps/flutter3d_editor/linux/runner/my_application.cc b/apps/flutter3d_editor/linux/runner/my_application.cc new file mode 100644 index 000000000..dfc1179ea --- /dev/null +++ b/apps/flutter3d_editor/linux/runner/my_application.cc @@ -0,0 +1,148 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Called when first Flutter frame received. +static void first_frame_cb(MyApplication* self, FlView* view) { + gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view))); +} + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "flutter3d_editor"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "flutter3d_editor"); + } + + gtk_window_set_default_size(window, 1280, 720); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments( + project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + GdkRGBA background_color; + // Background defaults to black, override it here if necessary, e.g. #00000000 + // for transparent. + gdk_rgba_parse(&background_color, "#000000"); + fl_view_set_background_color(view, &background_color); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + // Show the window when Flutter renders. + // Requires the view to be realized so we can start rendering. + g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb), + self); + gtk_widget_realize(GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, + gchar*** arguments, + int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + // MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = + my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, "flags", + G_APPLICATION_NON_UNIQUE, nullptr)); +} diff --git a/apps/flutter3d_editor/linux/runner/my_application.h b/apps/flutter3d_editor/linux/runner/my_application.h new file mode 100644 index 000000000..db16367a7 --- /dev/null +++ b/apps/flutter3d_editor/linux/runner/my_application.h @@ -0,0 +1,21 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, + my_application, + MY, + APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/apps/flutter3d_editor/pubspec.yaml b/apps/flutter3d_editor/pubspec.yaml index c883be2c3..6240ee52d 100644 --- a/apps/flutter3d_editor/pubspec.yaml +++ b/apps/flutter3d_editor/pubspec.yaml @@ -11,10 +11,13 @@ dependencies: flutter: sdk: flutter - # **Desktop only, and there is no web half.** The other three applications - # pick a backend at compile time because they ship to a browser; this one - # exists to write a file back to the disk it came from, which a browser will - # not do. One backend, named here, and no `backend_web.dart`. + # **Desktop runners for macOS, Windows and Linux; the web build is a step of + # its own.** The other applications pick a backend at compile time because + # they ship to a browser. This one was written to save a file back to the + # disk it came from, which a browser will not do, so for now there is one + # backend, named here, and no `backend_web.dart`. P11 in + # `tasks/0.9-engine-roadmap.md` asks for a web build that cannot Play another + # project; it brings its backend with it when it lands. flutter3d_impeller: path: ../../packages/flutter3d_impeller diff --git a/apps/flutter3d_editor/windows/.gitignore b/apps/flutter3d_editor/windows/.gitignore new file mode 100644 index 000000000..d492d0d98 --- /dev/null +++ b/apps/flutter3d_editor/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/apps/flutter3d_editor/windows/CMakeLists.txt b/apps/flutter3d_editor/windows/CMakeLists.txt new file mode 100644 index 000000000..7dbd62538 --- /dev/null +++ b/apps/flutter3d_editor/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(flutter3d_editor LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "flutter3d_editor") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/apps/flutter3d_editor/windows/flutter/CMakeLists.txt b/apps/flutter3d_editor/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000..903f4899d --- /dev/null +++ b/apps/flutter3d_editor/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/apps/flutter3d_editor/windows/flutter/generated_plugin_registrant.cc b/apps/flutter3d_editor/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..77ab7a09a --- /dev/null +++ b/apps/flutter3d_editor/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); +} diff --git a/apps/flutter3d_editor/windows/flutter/generated_plugin_registrant.h b/apps/flutter3d_editor/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..dc139d85a --- /dev/null +++ b/apps/flutter3d_editor/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/apps/flutter3d_editor/windows/flutter/generated_plugins.cmake b/apps/flutter3d_editor/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000..52b5e4127 --- /dev/null +++ b/apps/flutter3d_editor/windows/flutter/generated_plugins.cmake @@ -0,0 +1,26 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_selector_windows +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + flutter_soloud + jni +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/apps/flutter3d_editor/windows/runner/CMakeLists.txt b/apps/flutter3d_editor/windows/runner/CMakeLists.txt new file mode 100644 index 000000000..394917c05 --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/apps/flutter3d_editor/windows/runner/Runner.rc b/apps/flutter3d_editor/windows/runner/Runner.rc new file mode 100644 index 000000000..a3fb216cb --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "dev.flutter3d" "\0" + VALUE "FileDescription", "flutter3d_editor" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "flutter3d_editor" "\0" + VALUE "LegalCopyright", "Copyright (C) 2026 dev.flutter3d. All rights reserved." "\0" + VALUE "OriginalFilename", "flutter3d_editor.exe" "\0" + VALUE "ProductName", "flutter3d_editor" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/apps/flutter3d_editor/windows/runner/flutter_window.cpp b/apps/flutter3d_editor/windows/runner/flutter_window.cpp new file mode 100644 index 000000000..955ee3038 --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/apps/flutter3d_editor/windows/runner/flutter_window.h b/apps/flutter3d_editor/windows/runner/flutter_window.h new file mode 100644 index 000000000..6da0652f0 --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/apps/flutter3d_editor/windows/runner/main.cpp b/apps/flutter3d_editor/windows/runner/main.cpp new file mode 100644 index 000000000..e4d356312 --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"flutter3d_editor", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/apps/flutter3d_editor/windows/runner/resource.h b/apps/flutter3d_editor/windows/runner/resource.h new file mode 100644 index 000000000..66a65d1e4 --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/apps/flutter3d_editor/windows/runner/resources/app_icon.ico b/apps/flutter3d_editor/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/apps/flutter3d_editor/windows/runner/runner.exe.manifest b/apps/flutter3d_editor/windows/runner/runner.exe.manifest new file mode 100644 index 000000000..153653e8d --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/apps/flutter3d_editor/windows/runner/utils.cpp b/apps/flutter3d_editor/windows/runner/utils.cpp new file mode 100644 index 000000000..3cb714665 --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/utils.cpp @@ -0,0 +1,69 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + // First, find the length of the string with a safe upper bound (CWE-126). + // UNICODE_STRING_MAX_CHARS (32767) is the maximum length of a UNICODE_STRING. + int input_length = static_cast(wcsnlen(utf16_string, UNICODE_STRING_MAX_CHARS)); + // Now use that bounded length to determine the required buffer size. + // When an explicit length is passed, WideCharToMultiByte does not include + // the null terminator in its returned size. + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, nullptr, 0, nullptr, nullptr); + std::string utf8_string; + if (target_length == 0 || static_cast(target_length) > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/apps/flutter3d_editor/windows/runner/utils.h b/apps/flutter3d_editor/windows/runner/utils.h new file mode 100644 index 000000000..3879d5475 --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/apps/flutter3d_editor/windows/runner/win32_window.cpp b/apps/flutter3d_editor/windows/runner/win32_window.cpp new file mode 100644 index 000000000..60608d0fe --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/apps/flutter3d_editor/windows/runner/win32_window.h b/apps/flutter3d_editor/windows/runner/win32_window.h new file mode 100644 index 000000000..e901dde68 --- /dev/null +++ b/apps/flutter3d_editor/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/flutter3d_editor_play/CHANGELOG.md b/packages/flutter3d_editor_play/CHANGELOG.md index 00ffb574f..8d9c6be9b 100644 --- a/packages/flutter3d_editor_play/CHANGELOG.md +++ b/packages/flutter3d_editor_play/CHANGELOG.md @@ -24,6 +24,11 @@ the `ws://…/ws` one. `pushLevel` takes a `connect` for tests. - **`FakeGame` and `fakeAttachedRun`** in `testing.dart`: a VM service with a flutter tool on it, or without one, that a test controls. +- **A stop on Windows ends the tool, not only its shell.** `flutter` there + starts through `cmd.exe`, and `Process.kill` left the Dart process behind + it building; a stop before the game has started now runs `taskkill /t`. + `FlutterRun` takes the kill as a parameter, and `treeKillCommand` says + which command a platform needs. - **Play leaves the editor application.** `FlutterRun`, `projectRootFor` and `pushLevel` move here from `apps/flutter3d_editor/lib/src/play`, with diff --git a/packages/flutter3d_editor_play/lib/src/flutter_run.dart b/packages/flutter3d_editor_play/lib/src/flutter_run.dart index 9092c2110..148475e0c 100644 --- a/packages/flutter3d_editor_play/lib/src/flutter_run.dart +++ b/packages/flutter3d_editor_play/lib/src/flutter_run.dart @@ -31,6 +31,31 @@ Future _startFlutter( runInShell: Platform.isWindows, ); +/// Ends a `flutter` process [StartFlutter] started. A parameter of +/// [FlutterRun] so a test sees a stop reach the process. +typedef KillFlutter = void Function(Process process); + +void _killFlutter(Process process) { + switch (treeKillCommand(process.pid, windows: Platform.isWindows)) { + case [final executable, ...final arguments]: + unawaited(Process.run(executable, arguments)); + default: + process.kill(); + } +} + +/// The command that ends [pid] together with everything it started, or null +/// where [Process.kill] already reaches the tool. +/// +/// **On Windows the process is a shell, not the tool.** `flutter` there is +/// `flutter.bat`, so [StartFlutter] goes through `cmd.exe`, and +/// [Process.kill] ends the `cmd.exe` and leaves the Dart process behind it +/// building, holding the project's `build` directory open. `taskkill /t` +/// takes the tree. On macOS and Linux `bin/flutter` `exec`s the tool, so the +/// pid is already the tool's. +List? treeKillCommand(int pid, {required bool windows}) => + windows ? ['taskkill', '/pid', '$pid', '/t', '/f'] : null; + /// One `flutter run --machine`, from start to exit. /// /// **Its console is the tool's, line for line.** What the game prints, what @@ -44,6 +69,7 @@ final class FlutterRun implements PlayedGame { required this.projectRoot, this.device, this._start = _startFlutter, + this._kill = _killFlutter, }); /// The directory with the project's `pubspec.yaml`. @@ -55,6 +81,8 @@ final class FlutterRun implements PlayedGame { final StartFlutter _start; + final KillFlutter _kill; + @override String get title => projectRoot; @@ -133,7 +161,7 @@ final class FlutterRun implements PlayedGame { Future stop() async { final appId = _appId; if (appId == null) { - _process?.kill(); + if (_process case final Process process) _kill(process); return; } try { diff --git a/packages/flutter3d_editor_play/test/flutter_run_test.dart b/packages/flutter3d_editor_play/test/flutter_run_test.dart index b6087200e..73808558e 100644 --- a/packages/flutter3d_editor_play/test/flutter_run_test.dart +++ b/packages/flutter3d_editor_play/test/flutter_run_test.dart @@ -5,6 +5,8 @@ /// dart test test/flutter_run_test.dart library; +import 'dart:io'; + import 'package:flutter3d_editor_play/flutter3d_editor_play.dart'; import 'package:flutter3d_editor_play/testing.dart'; import 'package:test/test.dart'; @@ -110,6 +112,36 @@ void main() { }, ); + test('a stop before the game has started ends the process', () async { + final tool = FakeFlutterTool(); + final killed = []; + final run = FlutterRun( + projectRoot: '/game', + start: (_, _) async => tool, + kill: killed.add, + ); + await run.start(); + await run.stop(); + // Mutation: `stop` calling `Process.kill` directly passes everything + // else here and leaves the tool building on Windows, where the process + // it was handed is `cmd.exe`. + expect(killed, [tool]); + expect(tool.sent, isEmpty); + }); + + test('on Windows a stop takes the whole tree the shell started', () { + // Mutation: dropping `/t` ends `cmd.exe` and orphans the tool; dropping + // the Windows branch altogether does the same through `Process.kill`. + expect(treeKillCommand(42, windows: true), [ + 'taskkill', + '/pid', + '42', + '/t', + '/f', + ]); + expect(treeKillCommand(42, windows: false), isNull); + }); + test('a level is run as part of the nearest project above it', () { final projects = {'/work/game'}; bool hasPubspec(String directory) => projects.contains(directory); diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 9895ca44a..103759b04 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11195 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11197 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 32aa05b1e..b2685a8a6 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11195 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11197 tests need a GPU. --- # Testing -11195 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11197 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -31,7 +31,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game` | 325 | | `flutter3d_shaders` | 5 | | `flutter3d_particles` | 97 | | `flutter3d_stereo` | 50 | | `flutter3d_model_core` | 1172 | | `flutter3d_model_mcp` | 196 | -| `flutter3d_editor_play` | 26 | | `flutter3d_net` | 15 | +| `flutter3d_editor_play` | 28 | | `flutter3d_net` | 15 | | | | | `flutter3d_net_webrtc` | 2 | | | | | `flutter3d_sim_mcp` | 26 | | `flutter3d_mcp_kit` | 2 | | `flutter3d_build` | 136 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11169 rather than 11195: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11171 rather than 11197: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. From b572b5dd8434ebf387623f05ab528a36151e6b0c Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:39:58 +0300 Subject: [PATCH 049/141] A level bakes into a navigation mesh beside its grid NavMesh.bake voxelises the brushes and the heightfield, keeps the floors an agent fits on and can step between, erodes them by its radius, cuts them into regions, outlines those and cuts the outlines into convex polygons with their neighbours. Unlike NavGrid it keeps a walkway and the floor under it and walks up a ramp. Integer arithmetic from the voxeliser on, so its digest is the same on every platform. The path search over it comes next. --- ARCHITECTURE.md | 2 +- README.md | 2 +- .../linux/flutter/generated_plugins.cmake | 2 - .../windows/flutter/generated_plugins.cmake | 2 - packages/flutter3d_sim/CHANGELOG.md | 14 + packages/flutter3d_sim/lib/flutter3d_sim.dart | 2 + .../flutter3d_sim/lib/src/nav/nav_grid.dart | 38 +- .../lib/src/nav/navmesh/contours.dart | 247 ++++++++++ .../lib/src/nav/navmesh/distance_field.dart | 131 +++++ .../lib/src/nav/navmesh/navmesh.dart | 239 ++++++++++ .../lib/src/nav/navmesh/navmesh_config.dart | 108 +++++ .../lib/src/nav/navmesh/open_field.dart | 125 +++++ .../lib/src/nav/navmesh/poly_mesh.dart | 354 ++++++++++++++ .../lib/src/nav/navmesh/regions.dart | 97 ++++ .../lib/src/nav/navmesh/span_field.dart | 314 ++++++++++++ .../flutter3d_sim/test/navmesh_bake_test.dart | 451 ++++++++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- 18 files changed, 2113 insertions(+), 25 deletions(-) create mode 100644 packages/flutter3d_sim/lib/src/nav/navmesh/contours.dart create mode 100644 packages/flutter3d_sim/lib/src/nav/navmesh/distance_field.dart create mode 100644 packages/flutter3d_sim/lib/src/nav/navmesh/navmesh.dart create mode 100644 packages/flutter3d_sim/lib/src/nav/navmesh/navmesh_config.dart create mode 100644 packages/flutter3d_sim/lib/src/nav/navmesh/open_field.dart create mode 100644 packages/flutter3d_sim/lib/src/nav/navmesh/poly_mesh.dart create mode 100644 packages/flutter3d_sim/lib/src/nav/navmesh/regions.dart create mode 100644 packages/flutter3d_sim/lib/src/nav/navmesh/span_field.dart create mode 100644 packages/flutter3d_sim/test/navmesh_bake_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index ef4fb6bdf..275ea12e6 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11197 tests** across 43 packages and 10 applications | +| Unit tests | **11212 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 5061c90fb..a5b24b517 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11197 tests across forty-three packages and nine applications. The +There are 11212 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_editor/linux/flutter/generated_plugins.cmake b/apps/flutter3d_editor/linux/flutter/generated_plugins.cmake index 423b96a34..2db3c22ae 100644 --- a/apps/flutter3d_editor/linux/flutter/generated_plugins.cmake +++ b/apps/flutter3d_editor/linux/flutter/generated_plugins.cmake @@ -7,8 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST - flutter_soloud - jni ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/apps/flutter3d_editor/windows/flutter/generated_plugins.cmake b/apps/flutter3d_editor/windows/flutter/generated_plugins.cmake index 52b5e4127..a423a0247 100644 --- a/apps/flutter3d_editor/windows/flutter/generated_plugins.cmake +++ b/apps/flutter3d_editor/windows/flutter/generated_plugins.cmake @@ -7,8 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST - flutter_soloud - jni ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/packages/flutter3d_sim/CHANGELOG.md b/packages/flutter3d_sim/CHANGELOG.md index 4e99b88cf..b1c37d5df 100644 --- a/packages/flutter3d_sim/CHANGELOG.md +++ b/packages/flutter3d_sim/CHANGELOG.md @@ -77,6 +77,20 @@ camera was, so a chase camera left behind a wall does not start the photo there. `shouldPause` takes `photoMode`, which pauses whatever the pointer and the pad say, since both are flying the camera. + +- **A level bakes into a navigation mesh as well as a grid.** + `NavMesh.bake` and `NavMesh.bakeLevel` voxelise the brushes and the + `Heightfield`, keep the floors an agent fits on and can step between + (`NavMeshConfig`: height, step, radius, slope), erode them by the radius, + cut them into regions, outline those, and cut the outlines into convex + polygons with their neighbours and an area each. Unlike `NavGrid` it keeps + a walkway and the floor under it, and walks up a ramp rather than reading + it as a riser. Integers from the voxeliser on, so `NavMesh.digest` is the + same on every platform; the VM and Chrome agree on six scenes, and the + test holds them. The mesh is eroded by `NavGrid`'s own clearance rule and + covers exactly the cells a flow field for the same body accepts. It is + the first part of N2; the path search over it comes next. + - **A number tuned while the game runs is on the tape.** `InputState.tune` sets a tunable for one step, `InputFrame.tunes` records it, playback applies it, and `Tunables` is the step's side: named values with defaults, diff --git a/packages/flutter3d_sim/lib/flutter3d_sim.dart b/packages/flutter3d_sim/lib/flutter3d_sim.dart index 7ffbbeb19..3385e1130 100644 --- a/packages/flutter3d_sim/lib/flutter3d_sim.dart +++ b/packages/flutter3d_sim/lib/flutter3d_sim.dart @@ -99,6 +99,8 @@ export 'src/nav/flow_field.dart'; export 'src/nav/jump_links.dart'; export 'src/nav/nav_grid.dart'; export 'src/nav/navigation.dart'; +export 'src/nav/navmesh/navmesh.dart'; +export 'src/nav/navmesh/navmesh_config.dart'; export 'src/physics/layers.dart'; export 'src/save/data_source_trace.dart'; export 'src/save/demo.dart'; diff --git a/packages/flutter3d_sim/lib/src/nav/nav_grid.dart b/packages/flutter3d_sim/lib/src/nav/nav_grid.dart index fffc0aca0..1a364bec1 100644 --- a/packages/flutter3d_sim/lib/src/nav/nav_grid.dart +++ b/packages/flutter3d_sim/lib/src/nav/nav_grid.dart @@ -1,21 +1,31 @@ /// Where an agent can stand, baked once from the level's brushes. /// -/// ## Why a grid and not a navigation mesh +/// ## A grid, and a navigation mesh beside it /// -/// A navmesh earns its complexity by representing *arbitrary* walkable -/// surfaces. Brushes are not that: a `Brush` is a centre and a size, and -/// `level.dart` says so outright — there are no slopes. A navmesh over -/// axis-aligned boxes is a voxelise → region → contour → triangulate pipeline -/// whose output is the rectangles you could have rasterised directly. The most -/// code, the hardest to test, and it buys a representation the format cannot -/// express. +/// This grid was the whole of navigation for as long as a level was boxes: +/// over axis-aligned brushes a navmesh is a voxelise → region → contour → +/// triangulate pipeline whose output is the rectangles a grid rasterises +/// directly. The level stopped being only boxes. A brush can be a ramp, which +/// this grid reads as the top of its box and so as a riser as tall as the ramp; +/// ground can be a `Heightfield`; and a walkway over a floor is a level this +/// grid can only warn about (see below). `NavMesh` in `navmesh/` is the +/// pipeline, baked from the same brushes on the same lattice, and it is +/// beside this grid rather than instead of it. /// -/// **Ground made of samples arrived later and did not change the answer.** -/// [bakeHeightfield] walks a `Heightfield`, which is terrain and does have -/// slopes — and a height field is already a lattice, so rasterising it into -/// this one is a resample rather than a pipeline. The argument against a -/// navmesh got stronger rather than weaker: the surface it would triangulate -/// is a grid to begin with. +/// **What the mesh is for.** It keeps every floor in a column, so a walkway +/// and the floor under it are both there to walk on. It follows a ramp's +/// surface. And it is a few dozen convex polygons where this is thousands of +/// cells, which is what a single agent's search wants: across a convex polygon +/// the way is a straight line, so a path over the mesh comes out as the few +/// corners it turns at rather than as a staircase of cell centres. +/// +/// **What the grid stays for.** A flow field answers every agent at once from +/// one sweep, which is what a horde wants and a search per agent cannot match, +/// and its clearance and headroom filters let one bake serve every size of +/// body. The two agree where they overlap, and a test holds them to it: the +/// mesh is eroded by this grid's own rule — [clearanceForRadius] over the same +/// eight-neighbour edges — so it covers exactly the cells a field for a body +/// of the same radius accepts. /// /// ## Why it is baked from brushes and not from the collision world /// diff --git a/packages/flutter3d_sim/lib/src/nav/navmesh/contours.dart b/packages/flutter3d_sim/lib/src/nav/navmesh/contours.dart new file mode 100644 index 000000000..13cc16b12 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/nav/navmesh/contours.dart @@ -0,0 +1,247 @@ +/// The outline of each region, traced on the lattice and then simplified. +/// +/// **Every corner is a lattice point and every comparison is on integers**, +/// apart from the one distance the simplifier measures — and that is a ratio +/// of integers, computed by operations IEEE 754 pins, so it is the same number +/// on every machine. +/// +/// ## Why two neighbouring regions agree about their border +/// +/// Two regions share an edge in the finished mesh only if both outlines put +/// the same corners along it. So an edge between two regions is never +/// simplified by measuring: only the corners where the neighbour changes are +/// kept on it, and both sides find the same ones. Outer edges are the ones the +/// simplifier measures, and it walks each in one fixed direction whichever +/// region it is tracing, so even those would agree if they were shared. +library; + +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'open_field.dart'; + +/// One region's outline: corners as lattice `(x, y, z)`, flattened. +final class Contour { + Contour(this.region, this.area, this.vertices); + + final int region; + final int area; + final Int32List vertices; + + int get length => vertices.length ~/ 3; +} + +/// A raw outline corner: where it is, and which region lies beyond the edge +/// that leaves it. +typedef _Corner = ({int x, int y, int z, int beyond, bool areaBorder}); + +/// Traces every region's outline and simplifies it to within [maxError] +/// cells, in the order the regions are first met in a sweep of the field. +List buildContours( + OpenField field, + Int32List region, { + required double maxError, +}) { + final count = field.spanCount; + + // Which sides of each span face something other than its own region. + final sides = Uint8List(count); + for (var s = 0; s < count; s++) { + if (region[s] == 0) continue; + sides[s] = [0, 1, 2, 3].fold(0, (bits, dir) { + final n = field.neighbour(s, dir); + return n >= 0 && region[n] == region[s] ? bits : bits | (1 << dir); + }); + } + + final contours = []; + for (var cz = 0; cz < field.rows; cz++) { + for (var cx = 0; cx < field.columns; cx++) { + final c = cz * field.columns + cx; + for (var s = field.cellStart[c]; s < field.cellStart[c + 1]; s++) { + if (sides[s] == 0 || region[s] == 0) continue; + final raw = _walk(field, region, sides, cx, cz, s); + final simple = _removeDegenerate(_simplify(raw, maxError)); + if (simple.length < 3) continue; + contours.add( + Contour( + region[s], + field.area[s], + Int32List.fromList([ + for (final corner in simple) ...[ + corner.x, + corner.y, + corner.z, + ], + ]), + ), + ); + } + } + } + return contours; +} + +/// Follows the region's border from span [s], keeping the region on one +/// side, until it is back where it started; clears each side it passes. +List<_Corner> _walk( + OpenField field, + Int32List region, + Uint8List sides, + int startX, + int startZ, + int start, +) { + final corners = <_Corner>[]; + var x = startX; + var z = startZ; + var s = start; + var dir = 0; + while ((sides[s] & (1 << dir)) == 0) { + dir++; + } + final startDir = dir; + final area = field.area[start]; + + // Bounded, because a field that broke the invariants this walk relies on + // must stop rather than hang a bake. + for (var guard = 0; guard < 4 * field.spanCount + 16; guard++) { + if ((sides[s] & (1 << dir)) != 0) { + // The corner at the clockwise end of this side. + final px = x + (dir == 1 || dir == 2 ? 1 : 0); + final pz = z + (dir == 0 || dir == 1 ? 1 : 0); + final n = field.neighbour(s, dir); + corners.add(( + x: px, + y: _cornerHeight(field, s, dir), + z: pz, + beyond: n >= 0 ? region[n] : 0, + areaBorder: n >= 0 && field.area[n] != area, + )); + sides[s] &= ~(1 << dir); + dir = (dir + 1) & 3; + } else { + final n = field.neighbour(s, dir); + // A side with no border is a link to the same region by construction. + if (n < 0) break; + x += dirX[dir]; + z += dirZ[dir]; + s = n; + dir = (dir + 3) & 3; + } + if (s == start && dir == startDir) break; + } + return corners; +} + +/// The highest floor among the spans that meet at a corner, so a corner +/// shared by two regions has one height whichever of them is asking. +int _cornerHeight(OpenField field, int s, int dir) { + final turn = (dir + 1) & 3; + final a = field.neighbour(s, dir); + final b = field.neighbour(s, turn); + final viaA = a < 0 ? -1 : field.neighbour(a, turn); + final viaB = b < 0 ? -1 : field.neighbour(b, dir); + return [a, b, viaA, viaB].fold( + field.floor[s], + (high, n) => n < 0 ? high : math.max(high, field.floor[n]), + ); +} + +/// Keeps the corners where the region beyond changes, then adds back the +/// worst-fitting raw corner of any outer edge until every raw corner is +/// within [maxError] cells of the outline. +List<_Corner> _simplify(List<_Corner> raw, double maxError) { + final n = raw.length; + if (n == 0) return raw; + // Indices into [raw], in outline order. + final kept = [ + for (var i = 0; i < n; i++) + if (raw[i].beyond != raw[(i + 1) % n].beyond || + raw[i].areaBorder != raw[(i + 1) % n].areaBorder) + i, + ]; + + if (kept.isEmpty) { + // An island: start from its lower-left and upper-right corners, which + // every trace of the same outline finds whatever corner it began at. + final (lowest, highest) = Iterable.generate(n).fold((0, 0), (pair, i) { + final c = raw[i]; + final lo = raw[pair.$1]; + final hi = raw[pair.$2]; + return ( + c.x < lo.x || (c.x == lo.x && c.z < lo.z) ? i : pair.$1, + c.x > hi.x || (c.x == hi.x && c.z > hi.z) ? i : pair.$2, + ); + }); + kept.addAll([lowest, highest]); + } + + final limit = maxError * maxError; + var i = 0; + while (i < kept.length) { + final a = kept[i]; + final b = kept[(i + 1) % kept.length]; + final ra = raw[a]; + final rb = raw[b]; + // Walk the edge from its lexically smaller end, so the same edge measured + // from either side finds the same worst corner. + final forward = rb.x > ra.x || (rb.x == ra.x && rb.z > ra.z); + final from = forward ? ra : rb; + final to = forward ? rb : ra; + final step = forward ? 1 : n - 1; + final end = forward ? b : a; + var worst = -1; + var worstDistance = 0.0; + var k = forward ? (a + step) % n : (b + step) % n; + // Only an outer edge or an edge between areas is measured; one between + // two regions keeps the corners both sides agreed on. + if (raw[k].beyond == 0 || raw[k].areaBorder) { + while (k != end) { + final d = _distanceSquared(raw[k], from, to); + if (d > worstDistance) { + worstDistance = d; + worst = k; + } + k = (k + step) % n; + } + } + if (worst >= 0 && worstDistance > limit) { + kept.insert(i + 1, worst); + } else { + i++; + } + } + return <_Corner>[for (final k in kept) raw[k]]; +} + +/// The squared distance in cells from [p] to the segment [a]–[b], in the +/// horizontal plane. +double _distanceSquared(_Corner p, _Corner a, _Corner b) { + final ux = b.x - a.x; + final uz = b.z - a.z; + final px = p.x - a.x; + final pz = p.z - a.z; + final length = ux * ux + uz * uz; + final t = length == 0 + ? 0.0 + : ((ux * px + uz * pz) / length).clamp(0.0, 1.0).toDouble(); + final dx = ux * t - px; + final dz = uz * t - pz; + return dx * dx + dz * dz; +} + +/// Drops a corner that sits on the next one in plan. +List<_Corner> _removeDegenerate(List<_Corner> corners) { + final out = <_Corner>[]; + for (var i = 0; i < corners.length; i++) { + final next = corners[(i + 1) % corners.length]; + if (corners[i].x == next.x && + corners[i].z == next.z && + corners.length > 1) { + continue; + } + out.add(corners[i]); + } + return out; +} diff --git a/packages/flutter3d_sim/lib/src/nav/navmesh/distance_field.dart b/packages/flutter3d_sim/lib/src/nav/navmesh/distance_field.dart new file mode 100644 index 000000000..f05dadf7a --- /dev/null +++ b/packages/flutter3d_sim/lib/src/nav/navmesh/distance_field.dart @@ -0,0 +1,131 @@ +/// How far each floor is from the nearest place an agent cannot go, and the +/// two filters that read it. +/// +/// **The metric is `NavGrid`'s on purpose.** A span is at distance one when +/// any of its eight neighbours is missing or a step too far; the rest is a +/// Chebyshev distance by the two-sweep chamfer `NavGrid._clearances` uses. +/// The mesh then keeps exactly the floors whose clearance a flow field for a +/// body of the same radius would accept, and the two navigations of one level +/// disagree about nothing but shape. +library; + +import 'dart:typed_data'; + +import 'open_field.dart'; +import 'span_field.dart'; + +/// The clearance of every span, in cells; zero for a refused span. +Uint16List distanceField(OpenField field, int climb) { + final count = field.spanCount; + const cap = 0xffff; + final dist = Uint16List(count); + + // The diagonal from [s] between [dir] and the next direction round, reached + // along either side of the corner, or −1. + int diagonal(int s, int dir) { + final turn = (dir + 1) & 3; + final a = field.neighbour(s, dir); + final viaA = a < 0 ? -1 : field.neighbour(a, turn); + if (viaA >= 0) return viaA; + final b = field.neighbour(s, turn); + return b < 0 ? -1 : field.neighbour(b, dir); + } + + bool atEdge(int s) { + for (var dir = 0; dir < 4; dir++) { + if (field.neighbour(s, dir) < 0) return true; + final d = diagonal(s, dir); + if (d < 0 || (field.floor[d] - field.floor[s]).abs() > climb) return true; + } + return false; + } + + for (var s = 0; s < count; s++) { + if (field.area[s] == nullArea) continue; + dist[s] = atEdge(s) ? 1 : cap; + } + + // The read of a neighbour's distance through a path of directions; an edge + // span is never relaxed, so an interior one always has all eight. + int through(int s, int first, [int? second]) { + final a = field.neighbour(s, first); + if (a < 0) return 0; + if (second == null) return dist[a]; + final b = field.neighbour(a, second); + return b < 0 ? 0 : dist[b]; + } + + int relaxed(int s, List candidates) => + candidates.fold(dist[s] - 1, (best, d) => d < best ? d : best) + 1; + + for (var cz = 0; cz < field.rows; cz++) { + for (var cx = 0; cx < field.columns; cx++) { + final c = cz * field.columns + cx; + for (var s = field.cellStart[c]; s < field.cellStart[c + 1]; s++) { + if (dist[s] <= 1) continue; + dist[s] = relaxed(s, [ + through(s, 0), + through(s, 0, 3), + through(s, 3), + through(s, 3, 2), + ]); + } + } + } + for (var cz = field.rows - 1; cz >= 0; cz--) { + for (var cx = field.columns - 1; cx >= 0; cx--) { + final c = cz * field.columns + cx; + for (var s = field.cellStart[c + 1] - 1; s >= field.cellStart[c]; s--) { + if (dist[s] <= 1) continue; + dist[s] = relaxed(s, [ + through(s, 2), + through(s, 2, 1), + through(s, 1), + through(s, 1, 0), + ]); + } + } + } + return dist; +} + +/// Refuses every span closer to an edge than [need] cells. +/// +/// All at once, from one distance field, rather than peeling a ring at a +/// time: a ring peeled first would change what the next ring measures from. +void erode(OpenField field, Uint16List dist, int need) { + for (var s = 0; s < field.spanCount; s++) { + if (dist[s] < need) field.area[s] = nullArea; + } +} + +/// Refuses every connected island of fewer than [minCells] spans. +/// +/// Flooded from the lowest-numbered span of each island, neighbours in +/// direction order, so the islands and what survives of them are the same on +/// every run. +void dropIslands(OpenField field, int minCells) { + if (minCells <= 1) return; + final seen = Uint8List(field.spanCount); + final island = []; + for (var start = 0; start < field.spanCount; start++) { + if (seen[start] != 0 || field.area[start] == nullArea) continue; + island + ..clear() + ..add(start); + seen[start] = 1; + for (var k = 0; k < island.length; k++) { + for (var dir = 0; dir < 4; dir++) { + final n = field.neighbour(island[k], dir); + if (n < 0 || seen[n] != 0) continue; + seen[n] = 1; + island.add(n); + } + } + if (island.length < minCells) { + for (final s in island) { + field.area[s] = nullArea; + } + } + } +} diff --git a/packages/flutter3d_sim/lib/src/nav/navmesh/navmesh.dart b/packages/flutter3d_sim/lib/src/nav/navmesh/navmesh.dart new file mode 100644 index 000000000..52ca534e5 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/nav/navmesh/navmesh.dart @@ -0,0 +1,239 @@ +/// Where an agent can stand, as convex polygons rather than cells. +/// +/// ## The bake +/// +/// Brushes and terrain are voxelised into columns of solid spans; the tops of +/// those spans are filtered into floors an agent fits on and can step between; +/// the floors are eroded by the agent's radius, cut into regions, outlined, +/// and the outlines cut into convex polygons that know their neighbours. Each +/// stage is a file in this directory and says why it is shaped the way it is. +/// +/// ## Why it is the same mesh everywhere +/// +/// The voxeliser is the only stage that sees a floating-point number, and it +/// turns each into a whole number of voxels by one division and one rounding. +/// Everything after compares and adds integers, sweeps the lattice in one +/// fixed order, and keeps no map it walks. So [digest] is a fact about the +/// level and the [config] and not about the machine, and CI holds it fixed on +/// every operating system it runs on. +library; + +import 'dart:typed_data'; + +import 'package:vector_math/vector_math.dart'; + +import '../../level/heightfield.dart'; +import '../../level/level.dart'; +import '../../save/state_digest.dart'; +import 'contours.dart'; +import 'distance_field.dart'; +import 'navmesh_config.dart'; +import 'open_field.dart'; +import 'poly_mesh.dart'; +import 'regions.dart'; +import 'span_field.dart'; + +/// The surfaces a polygon can be, by number. +/// +/// A number and not an enum because the set is the game's: mud, a shallow +/// ford, a road a cart prefers. The mesh only carries the number to whatever +/// path search will price it, and keeps two values for itself. +abstract final class NavArea { + /// Not a surface. No polygon has it; a brush given it is not walked on. + static const int none = 0; + + /// Ground, when nothing says otherwise. + static const int ground = 1; +} + +/// Convex polygons over a level's floors, and which of them touch. +final class NavMesh { + NavMesh._({ + required this.config, + required this.originX, + required this.originY, + required this.originZ, + required this._vertices, + required this._polygons, + required this._neighbours, + required this._areas, + }); + + /// What the mesh was baked to. Part of [digest]. + final NavMeshConfig config; + + /// The world position of lattice point `(0, 0, 0)`. + final double originX; + final double originY; + final double originZ; + + final Int32List _vertices; + final Int32List _polygons; + final Int32List _neighbours; + final Uint8List _areas; + + int get maxVerticesPerPolygon => config.maxVerticesPerPolygon; + + int get vertexCount => _vertices.length ~/ 3; + int get polygonCount => _areas.length; + + /// True when there is nowhere to stand: an empty level, or one whose every + /// floor the filters refused. + bool get isEmpty => polygonCount == 0; + + /// A vertex on the lattice, in cells across and voxels up. Whole numbers, + /// which is what a test compares and what [digest] is taken of. + int latticeX(int vertex) => _vertices[vertex * 3]; + int latticeY(int vertex) => _vertices[vertex * 3 + 1]; + int latticeZ(int vertex) => _vertices[vertex * 3 + 2]; + + /// A vertex in the world. + void vertexAt(int vertex, Vector3 out) => out.setValues( + originX + latticeX(vertex) * config.cellSize, + originY + latticeY(vertex) * config.cellHeight, + originZ + latticeZ(vertex) * config.cellSize, + ); + + /// How many corners [polygon] has. + int polygonVertexCount(int polygon) { + final base = polygon * maxVerticesPerPolygon; + var n = 0; + while (n < maxVerticesPerPolygon && _polygons[base + n] >= 0) { + n++; + } + return n; + } + + /// Corner [k] of [polygon]. Corners run so that the polygon turns left at + /// each in the `(x, z)` plane: x across, z up the page. + int polygonVertex(int polygon, int k) => + _polygons[polygon * maxVerticesPerPolygon + k]; + + /// The polygon across edge [k] of [polygon] — the edge from corner `k` to + /// corner `k + 1` — or −1 where the edge is a wall or a drop. + int neighbourAt(int polygon, int k) => + _neighbours[polygon * maxVerticesPerPolygon + k]; + + /// The surface [polygon] is, from the bake's `areaOf`. Never [NavArea.none]. + int areaOf(int polygon) => _areas[polygon]; + + /// Whether the world point `(x, z)` lies on [polygon] in plan, edges + /// included. + bool containsPoint(int polygon, double x, double z) { + final u = (x - originX) / config.cellSize; + final v = (z - originZ) / config.cellSize; + final n = polygonVertexCount(polygon); + for (var k = 0; k < n; k++) { + final a = polygonVertex(polygon, k); + final b = polygonVertex(polygon, (k + 1) % n); + final ax = latticeX(a); + final az = latticeZ(a); + final turn = + (latticeX(b) - ax) * (v - az) - (latticeZ(b) - az) * (u - ax); + if (turn < 0.0) return false; + } + return true; + } + + /// Every polygon over `(x, z)` in plan, lowest index first — one per floor + /// where floors are stacked. + List polygonsAt(double x, double z) => [ + for (var p = 0; p < polygonCount; p++) + if (containsPoint(p, x, z)) p, + ]; + + /// One number for the whole mesh and what it was baked to. + /// + /// Taken of the lattice — whole numbers of cells and voxels — and the + /// config, through [StateDigest], so it is the same bits on every platform + /// a run is replayed on. Two bakes of one level agree on it or one of them + /// is wrong. + int get digest => + (StateDigest() + ..add(config.values) + ..add([originX, originY, originZ]) + ..add(_vertices) + ..add(_polygons) + ..add(_neighbours) + ..add(_areas)) + .value; + + /// [digest] as eight hexadecimal digits, the way a test writes it down. + String get digestHex => digest.toRadixString(16).padLeft(8, '0'); + + /// Bakes the level's architecture and its ground. + /// + /// Recipes are expanded first, as everything that uses a level does — see + /// `expandRecipes`. + static NavMesh bakeLevel( + Level level, { + NavMeshConfig config = const NavMeshConfig(), + int Function(Brush brush)? areaOf, + int groundArea = NavArea.ground, + }) => bake( + expandRecipes(level).brushes, + ground: level.heightfield, + config: config, + areaOf: areaOf, + groundArea: groundArea, + ); + + /// Bakes [brushes] and, when given, [ground] into one mesh. + /// + /// [areaOf] names the surface of each brush's top, [NavArea.ground] unless + /// it says otherwise; [NavArea.none] keeps agents off a brush entirely — + /// lava, a roof the game does not want walked. [groundArea] is the + /// terrain's. + /// + /// **From brushes, not from the collision world**, for `NavGrid`'s reason: + /// the collision world has doors in it, and a mesh baked with a door closed + /// is a mesh with a wall where the door is. + static NavMesh bake( + Iterable brushes, { + Heightfield? ground, + NavMeshConfig config = const NavMeshConfig(), + int Function(Brush brush)? areaOf, + int groundArea = NavArea.ground, + }) { + final solid = + SpanField.rasterise( + brushes, + config: config, + ground: ground, + areaOf: areaOf ?? (_) => NavArea.ground, + groundArea: groundArea, + ) + ..filterLowHangingObstacles(config.walkableClimb) + ..filterLowHeight(config.walkableHeight); + + final open = OpenField.build( + solid, + height: config.walkableHeight, + climb: config.walkableClimb, + ); + erode(open, distanceField(open, config.walkableClimb), config.erosion); + dropIslands(open, config.minIslandCells); + + final regions = monotoneRegions(open); + final contours = buildContours( + open, + regions.region, + maxError: config.maxEdgeError, + ); + final parts = buildPolyMesh( + contours, + maxCorners: config.maxVerticesPerPolygon, + ); + + return NavMesh._( + config: config, + originX: solid.originX, + originY: solid.originY, + originZ: solid.originZ, + vertices: parts.vertices, + polygons: parts.polygons, + neighbours: parts.neighbours, + areas: parts.areas, + ); + } +} diff --git a/packages/flutter3d_sim/lib/src/nav/navmesh/navmesh_config.dart b/packages/flutter3d_sim/lib/src/nav/navmesh/navmesh_config.dart new file mode 100644 index 000000000..71406110e --- /dev/null +++ b/packages/flutter3d_sim/lib/src/nav/navmesh/navmesh_config.dart @@ -0,0 +1,108 @@ +import 'dart:math' as math; + +import '../../math/tolerances.dart'; + +/// The numbers a navigation mesh is baked to, in metres and radians. +/// +/// **Turned into whole voxels once, here, and never again.** Every stage after +/// the voxeliser compares integers: a floor is so many voxels up, a step is so +/// many voxels tall. Two platforms that agree on the conversion therefore agree +/// on everything downstream, and the conversion is one division and one +/// rounding apiece — operations IEEE 754 pins — so they do agree. +final class NavMeshConfig { + const NavMeshConfig({ + this.cellSize = 0.5, + this.cellHeight = 0.1, + this.agentHeight = 1.7, + this.stepHeight = 0.4, + this.agentRadius = 0.3, + this.maxSlope = 0.698, + this.maxEdgeError = 1.3, + this.maxVerticesPerPolygon = 6, + this.minIslandArea = 0.0, + }) : assert(cellSize > 0.0, 'a cell of no width has no place to stand'), + assert( + cellHeight > 0.0, + 'a voxel of no height cannot tell floors apart', + ), + assert( + maxVerticesPerPolygon >= 3, + 'a polygon has three corners at least', + ); + + /// The horizontal lattice, in metres. Half a metre by default, the same as + /// `NavGrid`, so the two bakes of one level stand on the same cells and a + /// test can hold one against the other. + final double cellSize; + + /// The vertical lattice, in metres. A tenth of a metre resolves every step a + /// level is built with and keeps a forty-metre level under four hundred + /// voxels tall. + final double cellHeight; + + /// Room an agent needs above a floor for the floor to count. + final double agentHeight; + + /// The tallest ledge an agent walks up. Matches `CharacterTuning.stepHeight` + /// for the reason `NavGrid.stepHeight` gives: a mesh that promises a step + /// the controller will not take sends agents into walls for ever. + /// + /// On terrain, `cellSize * tan(maxSlope)` has to stay under this, or the + /// rise between two neighbouring cells of a walkable hillside reads as a + /// ledge and the hill is cut into terraces. + final double stepHeight; + + /// How far the mesh keeps away from walls and edges. A body of this radius + /// standing anywhere on the mesh does not overlap anything. + final double agentRadius; + + /// The steepest ground that is still ground, in radians from flat. Forty + /// degrees, as in `NavGrid.bakeHeightfield`. + final double maxSlope; + + /// How far a simplified outline may stray from the voxel edge it replaces, + /// in cells. Corners of anything built on the lattice deviate by a whole + /// cell or more, so they survive; the staircase a diagonal edge makes on the + /// lattice does not. + final double maxEdgeError; + + /// The most corners a polygon may have after triangles are merged. + final int maxVerticesPerPolygon; + + /// An island of walkable ground smaller than this, in square metres, is + /// dropped. Zero keeps everything, which is the default because the top of + /// a crate is a place to stand and only the game knows whether it matters. + final double minIslandArea; + + /// [agentHeight] in voxels, rounded up: a ceiling one voxel short of the + /// agent's head is a ceiling the agent does not fit under. + int get walkableHeight => _up(agentHeight / cellHeight); + + /// [stepHeight] in voxels, rounded down: a step a hair taller than the + /// agent climbs is a wall. + int get walkableClimb => _down(stepHeight / cellHeight); + + /// The clearance in cells an eroded span needs, by `NavGrid`'s own rule — + /// `NavGrid.clearanceForRadius` — so that the mesh covers exactly the cells + /// a flow field for a body of [agentRadius] would accept. + int get erosion => math.max(1, (agentRadius / cellSize + 0.5).ceil()); + + /// [minIslandArea] in cells. + int get minIslandCells => _up(minIslandArea / (cellSize * cellSize)); + + static int _up(double voxels) => (voxels - Tolerance.gridBias).ceil(); + static int _down(double voxels) => (voxels + Tolerance.gridBias).floor(); + + /// The numbers themselves, in a fixed order, for a digest. + List get values => [ + cellSize, + cellHeight, + agentHeight, + stepHeight, + agentRadius, + maxSlope, + maxEdgeError, + maxVerticesPerPolygon.toDouble(), + minIslandArea, + ]; +} diff --git a/packages/flutter3d_sim/lib/src/nav/navmesh/open_field.dart b/packages/flutter3d_sim/lib/src/nav/navmesh/open_field.dart new file mode 100644 index 000000000..40641193a --- /dev/null +++ b/packages/flutter3d_sim/lib/src/nav/navmesh/open_field.dart @@ -0,0 +1,125 @@ +/// The space above every floor, and which floors an agent walks between. +library; + +import 'dart:typed_data'; + +import 'span_field.dart'; + +/// Directions, in the order every stage walks them: −x, +z, +x, −z. Each is +/// the previous one turned a quarter the same way round, so `dir + 1` and +/// `dir + 3` are the two turns the contour walk takes. +const List dirX = [-1, 0, 1, 0]; +const List dirZ = [0, 1, 0, -1]; + +/// One open span per floor: from the top of a solid span up to the bottom of +/// the next one, with links to the floors beside it. +/// +/// Flat arrays rather than objects, indexed by span, because every stage after +/// this one is a sweep over all of them in the same fixed order — column by +/// column, lowest floor first — and that order is the bake's determinism. +final class OpenField { + OpenField._({ + required this.columns, + required this.rows, + required this.cellStart, + required this.floor, + required this.ceiling, + required this.area, + required this.links, + }); + + final int columns; + final int rows; + + /// Spans of column `c` are `cellStart[c]` up to `cellStart[c + 1]`. + final Int32List cellStart; + + /// In voxels. + final Int32List floor; + final Int32List ceiling; + + /// The walking surface, or [nullArea] once a filter has refused the span. + final Uint8List area; + + /// `links[span * 4 + dir]`: the span stepped onto in that direction, or −1. + final Int32List links; + + int get spanCount => floor.length; + + /// The span an agent reaches from [span] in [dir], or −1 when it reaches + /// none — no link, or a link to a span a later filter refused. + int neighbour(int span, int dir) { + final next = links[span * 4 + dir]; + return next < 0 || area[next] == nullArea ? -1 : next; + } + + /// Builds the open spans over every floor of [solid] and links them. + /// + /// Two floors side by side are linked when the step between them is at + /// most [climb] and the gap they share is at least [height] tall — an agent + /// has to fit through the opening, not only under each ceiling apart. Of + /// the floors in the next column the lowest that qualifies wins; for an + /// agent shorter than twice its step there can only be one. + static OpenField build( + SpanField solid, { + required int height, + required int climb, + }) { + final count = solid.columns * solid.rows; + final cellStart = Int32List(count + 1); + final floors = []; + final ceilings = []; + final areas = []; + for (var c = 0; c < count; c++) { + cellStart[c] = floors.length; + final column = solid.spans[c]; + for (var i = 0; i < column.length; i++) { + if (column[i].area == nullArea) continue; + floors.add(column[i].max); + ceilings.add(i + 1 < column.length ? column[i + 1].min : SpanField.sky); + areas.add(column[i].area); + } + } + cellStart[count] = floors.length; + + final floor = Int32List.fromList(floors); + final ceiling = Int32List.fromList(ceilings); + final links = Int32List(floors.length * 4) + ..fillRange(0, floors.length * 4, -1); + + for (var cz = 0; cz < solid.rows; cz++) { + for (var cx = 0; cx < solid.columns; cx++) { + final c = cz * solid.columns + cx; + for (var s = cellStart[c]; s < cellStart[c + 1]; s++) { + for (var dir = 0; dir < 4; dir++) { + final nx = cx + dirX[dir]; + final nz = cz + dirZ[dir]; + if (nx < 0 || nz < 0 || nx >= solid.columns || nz >= solid.rows) { + continue; + } + final n = nz * solid.columns + nx; + for (var t = cellStart[n]; t < cellStart[n + 1]; t++) { + final bottom = floor[s] > floor[t] ? floor[s] : floor[t]; + final top = ceiling[s] < ceiling[t] ? ceiling[s] : ceiling[t]; + if (top - bottom >= height && + (floor[t] - floor[s]).abs() <= climb) { + links[s * 4 + dir] = t; + break; + } + } + } + } + } + } + + return OpenField._( + columns: solid.columns, + rows: solid.rows, + cellStart: cellStart, + floor: floor, + ceiling: ceiling, + area: Uint8List.fromList(areas), + links: links, + ); + } +} diff --git a/packages/flutter3d_sim/lib/src/nav/navmesh/poly_mesh.dart b/packages/flutter3d_sim/lib/src/nav/navmesh/poly_mesh.dart new file mode 100644 index 000000000..04fd2a167 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/nav/navmesh/poly_mesh.dart @@ -0,0 +1,354 @@ +/// Outlines into convex polygons that know their neighbours. +/// +/// Each outline is cut into triangles by ear clipping, the triangles are +/// merged back into convex polygons of at most so many corners, and polygons +/// that share an edge are told about each other. +/// +/// **All of it on integers.** Corners are lattice points, so every turn test +/// is an exact cross product and no tolerance is involved anywhere: a corner +/// is convex, flat or reflex, and two machines cannot see it differently. +library; + +import 'dart:typed_data'; + +import 'contours.dart'; + +/// Vertices as lattice `(x, y, z)`; polygons as [maxCorners] vertex indices +/// apiece padded with −1; the neighbour across each edge, or −1; an area per +/// polygon. +typedef PolyMeshParts = ({ + Int32List vertices, + Int32List polygons, + Int32List neighbours, + Uint8List areas, +}); + +/// Builds the polygon mesh of [contours]. +/// +/// Every polygon turns left at each corner in the `(x, z)` plane — its signed +/// area there is positive — and none has a reflex corner. +PolyMeshParts buildPolyMesh(List contours, {required int maxCorners}) { + final vertices = []; + // Lattice column to the vertices already standing in it. Only ever looked + // up, never walked, so its order is nobody's business. + final byColumn = <(int, int), List>{}; + + // **Corners that differ by a voxel or two are one corner.** Two outlines + // meeting at a corner each take the highest floor they can see from their + // own side of it, and at the edge of a step the two can see different + // floors; welding them is what lets the polygons on each side share it. + int weld(int x, int y, int z) { + final column = byColumn.putIfAbsent((x, z), () => []); + for (final v in column) { + if ((vertices[v * 3 + 1] - y).abs() <= 2) return v; + } + final v = vertices.length ~/ 3; + vertices.addAll([x, y, z]); + column.add(v); + return v; + } + + final polygons = >[]; + final areas = []; + + for (final contour in contours) { + final welded = []; + for (var i = 0; i < contour.length; i++) { + final v = weld( + contour.vertices[i * 3], + contour.vertices[i * 3 + 1], + contour.vertices[i * 3 + 2], + ); + if (welded.isEmpty || welded.last != v) welded.add(v); + } + while (welded.length > 1 && welded.first == welded.last) { + welded.removeLast(); + } + if (welded.length < 3) continue; + + int px(int v) => vertices[v * 3]; + int pz(int v) => vertices[v * 3 + 2]; + final outline = _signedArea(welded, px, pz) < 0 + ? welded.reversed.toList() + : welded; + + final pieces = _merge( + >[ + for (final t in _triangulate(outline, px, pz)) + if (_cross(px, pz, t[0], t[1], t[2]) > 0) t, + ], + maxCorners, + px, + pz, + ); + polygons.addAll(pieces); + areas.addAll(List.filled(pieces.length, contour.area)); + } + + final polyArray = Int32List(polygons.length * maxCorners) + ..fillRange(0, polygons.length * maxCorners, -1); + for (var p = 0; p < polygons.length; p++) { + polyArray.setRange( + p * maxCorners, + p * maxCorners + polygons[p].length, + polygons[p], + ); + } + + return ( + vertices: Int32List.fromList(vertices), + polygons: polyArray, + neighbours: _adjacency(polygons, maxCorners), + areas: Uint8List.fromList(areas), + ); +} + +/// Twice the signed area of the polygon in `(x, z)`. +int _signedArea(List poly, int Function(int) px, int Function(int) pz) { + var sum = 0; + for (var i = 0; i < poly.length; i++) { + final a = poly[i]; + final b = poly[(i + 1) % poly.length]; + sum += px(a) * pz(b) - px(b) * pz(a); + } + return sum; +} + +/// Positive when `a → b → c` turns left in `(x, z)`, zero when it runs +/// straight on. +int _cross(int Function(int) px, int Function(int) pz, int a, int b, int c) => + (px(b) - px(a)) * (pz(c) - pz(a)) - (pz(b) - pz(a)) * (px(c) - px(a)); + +/// Cuts a polygon that turns left into triangles. +/// +/// **The shortest diagonal first.** Clipping the ear whose new edge is the +/// shortest keeps slivers out of the triangles, which keeps them out of the +/// polygons merged from them; and since the choice is the first shortest in +/// outline order, it is the same choice every time. +List> _triangulate( + List outline, + int Function(int) px, + int Function(int) pz, +) { + final ring = List.of(outline); + final triangles = >[]; + + int cross(int a, int b, int c) => _cross(px, pz, a, b, c); + bool left(int a, int b, int c) => cross(a, b, c) > 0; + bool leftOn(int a, int b, int c) => cross(a, b, c) >= 0; + bool collinear(int a, int b, int c) => cross(a, b, c) == 0; + + bool between(int a, int b, int c) { + if (!collinear(a, b, c)) return false; + if (px(a) != px(b)) { + return (px(a) <= px(c) && px(c) <= px(b)) || + (px(a) >= px(c) && px(c) >= px(b)); + } + return (pz(a) <= pz(c) && pz(c) <= pz(b)) || + (pz(a) >= pz(c) && pz(c) >= pz(b)); + } + + bool intersects(int a, int b, int c, int d) { + final proper = + !(collinear(a, b, c) || + collinear(a, b, d) || + collinear(c, d, a) || + collinear(c, d, b)) && + (left(a, b, c) != left(a, b, d)) && + (left(c, d, a) != left(c, d, b)); + return proper || + between(a, b, c) || + between(a, b, d) || + between(c, d, a) || + between(c, d, b); + } + + bool samePlace(int a, int b) => px(a) == px(b) && pz(a) == pz(b); + + // Whether the segment from ring[i] to ring[j] crosses no edge it does not + // end on. + bool clearOfEdges(int i, int j) { + final a = ring[i]; + final b = ring[j]; + for (var k = 0; k < ring.length; k++) { + final k1 = (k + 1) % ring.length; + if (k == i || k1 == i || k == j || k1 == j) continue; + final c = ring[k]; + final d = ring[k1]; + if (samePlace(a, c) || + samePlace(b, c) || + samePlace(a, d) || + samePlace(b, d)) { + continue; + } + if (intersects(a, b, c, d)) return false; + } + return true; + } + + // Whether ring[j] is inside the cone of the corner at ring[i]. + bool inCone(int i, int j) { + final n = ring.length; + final here = ring[i]; + final there = ring[j]; + final next = ring[(i + 1) % n]; + final previous = ring[(i + n - 1) % n]; + if (leftOn(previous, here, next)) { + return left(here, there, previous) && left(there, here, next); + } + return !(leftOn(here, there, next) && leftOn(there, here, previous)); + } + + bool diagonal(int i, int j) => inCone(i, j) && clearOfEdges(i, j); + + // ear[i]: the corner after ring[i] can be clipped. + bool earAfter(int i) => diagonal(i, (i + 2) % ring.length); + final ear = [for (var i = 0; i < ring.length; i++) earAfter(i)]; + + while (ring.length > 3) { + final n = ring.length; + var best = -1; + var bestLength = 0; + for (var i = 0; i < n; i++) { + if (!ear[i]) continue; + final a = ring[i]; + final c = ring[(i + 2) % n]; + final dx = px(c) - px(a); + final dz = pz(c) - pz(a); + final length = dx * dx + dz * dz; + if (best < 0 || length < bestLength) { + best = i; + bestLength = length; + } + } + // No ear: the outline touches itself. Clip the first corner that is not + // reflex, which loses at worst a sliver, rather than give up on the rest. + if (best < 0) { + best = Iterable.generate(n).firstWhere( + (i) => leftOn(ring[i], ring[(i + 1) % n], ring[(i + 2) % n]), + orElse: () => -1, + ); + if (best < 0) return triangles; + } + + final middle = (best + 1) % n; + triangles.add([ring[best], ring[middle], ring[(best + 2) % n]]); + ring.removeAt(middle); + ear.removeAt(middle); + final m = ring.length; + // The two corners either side of the one clipped have new neighbours, and + // so does every ear whose diagonal starts at or crosses one of them. + final before = middle == 0 ? m - 1 : middle - 1; + for (final k in [(before + m - 1) % m, before, (before + 1) % m]) { + ear[k] = earAfter(k); + } + } + triangles.add([ring[0], ring[1], ring[2]]); + return triangles; +} + +/// Merges polygons that share an edge while the result stays convex and +/// within [maxCorners], longest shared edge first. +List> _merge( + List> polygons, + int maxCorners, + int Function(int) px, + int Function(int) pz, +) { + if (maxCorners <= 3) return polygons; + final polys = List>.of(polygons); + + // The shared edge's squared length, with where it sits in each, or null + // when the two cannot be merged. + (int, int, int)? value(List a, List b) { + if (a.length + b.length - 2 > maxCorners) return null; + for (var i = 0; i < a.length; i++) { + final a0 = a[i]; + final a1 = a[(i + 1) % a.length]; + for (var j = 0; j < b.length; j++) { + if (b[j] != a1 || b[(j + 1) % b.length] != a0) continue; + // Convex at both ends of the seam once it is gone. **Straight on is + // allowed**: a corner where the neighbour changes along a straight + // wall has to stay a corner, so both sides of the border have it, + // and refusing to merge across it cut every such rectangle into + // three. + if (_cross( + px, + pz, + a[(i + a.length - 1) % a.length], + a0, + b[(j + 2) % b.length], + ) < + 0) { + return null; + } + if (_cross( + px, + pz, + b[(j + b.length - 1) % b.length], + a1, + a[(i + 2) % a.length], + ) < + 0) { + return null; + } + final dx = px(a1) - px(a0); + final dz = pz(a1) - pz(a0); + return (dx * dx + dz * dz, i, j); + } + } + return null; + } + + while (true) { + var best = 0; + var bestA = -1; + var bestB = -1; + var edgeA = 0; + var edgeB = 0; + for (var p = 0; p < polys.length; p++) { + for (var q = p + 1; q < polys.length; q++) { + final v = value(polys[p], polys[q]); + if (v == null || v.$1 <= best) continue; + (best, bestA, bestB, edgeA, edgeB) = (v.$1, p, q, v.$2, v.$3); + } + } + if (bestA < 0) return polys; + + final a = polys[bestA]; + final b = polys[bestB]; + polys[bestA] = [ + for (var k = 0; k < a.length - 1; k++) a[(edgeA + 1 + k) % a.length], + for (var k = 0; k < b.length - 1; k++) b[(edgeB + 1 + k) % b.length], + ]; + polys[bestB] = polys.last; + polys.removeLast(); + } +} + +/// For each polygon edge, the polygon on the other side of it, or −1. +/// +/// Edge `k` of a polygon runs from its corner `k` to corner `k + 1`. Two +/// polygons are neighbours across an edge when they both have it, in opposite +/// directions; the first two to claim an edge keep it. +Int32List _adjacency(List> polygons, int maxCorners) { + final out = Int32List(polygons.length * maxCorners) + ..fillRange(0, polygons.length * maxCorners, -1); + final open = <(int, int), int>{}; + for (var p = 0; p < polygons.length; p++) { + final poly = polygons[p]; + for (var k = 0; k < poly.length; k++) { + final a = poly[k]; + final b = poly[(k + 1) % poly.length]; + final slot = p * maxCorners + k; + final other = open.remove((b, a)); + if (other != null) { + out[slot] = other ~/ maxCorners; + out[other] = p; + } else { + open[(a, b)] = slot; + } + } + } + return out; +} diff --git a/packages/flutter3d_sim/lib/src/nav/navmesh/regions.dart b/packages/flutter3d_sim/lib/src/nav/navmesh/regions.dart new file mode 100644 index 000000000..343242526 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/nav/navmesh/regions.dart @@ -0,0 +1,97 @@ +/// The floor cut into regions, each of which becomes one outline. +/// +/// ## Monotone, not watershed +/// +/// A watershed grows regions from the middle of open floor outward and gives +/// rounder ones, which make better polygons. It also gives regions with holes +/// — a region that flows round a pillar and meets itself on the far side — +/// and an outline with a hole in it needs a pass that cuts the hole into the +/// outline before anything can triangulate it. That pass, and the merging of +/// small regions that a watershed needs to be usable, are most of the code +/// and most of the ways a bake can disagree with itself. +/// +/// A monotone sweep cannot make a hole. It goes row by row, and a run of floor +/// continues the region of the run before it only when that region continues +/// into nothing else; so every region is one run per row, the runs of +/// consecutive rows overlap, and a shape like that has no inside to go round. +/// What it costs is regions that are longer and thinner than they need be, +/// which the triangles merged into polygons afterwards mostly hide. +library; + +import 'dart:typed_data'; + +import 'open_field.dart'; +import 'span_field.dart'; + +/// Regions numbered from one, and how many there are. Zero is no region. +({Int32List region, int count}) monotoneRegions(OpenField field) { + final region = Int32List(field.spanCount); + // Per sweep in the current row: the region of the row before it continues, + // how many of its spans reached that region, and the id it ends up with. + final continues = []; + final samples = []; + final ids = []; + // How many spans of the current row reached each region of the previous. + final reached = {}; + const several = -1; + var next = 1; + + for (var cz = 0; cz < field.rows; cz++) { + continues.clear(); + samples.clear(); + ids.clear(); + reached.clear(); + + for (var cx = 0; cx < field.columns; cx++) { + final c = cz * field.columns + cx; + for (var s = field.cellStart[c]; s < field.cellStart[c + 1]; s++) { + if (field.area[s] == nullArea) continue; + + // The sweep this span extends along the row, or a new one. + final west = field.neighbour(s, 0); + final sweep = + west >= 0 && field.area[west] == field.area[s] && region[west] > 0 + ? region[west] + : () { + continues.add(0); + samples.add(0); + ids.add(0); + return continues.length; + }(); + + // What the row before says about it. + final south = field.neighbour(s, 3); + if (south >= 0 && field.area[south] == field.area[s]) { + final before = region[south]; + if (before > 0) { + final k = sweep - 1; + if (continues[k] == 0 || continues[k] == before) { + continues[k] = before; + samples[k]++; + reached[before] = (reached[before] ?? 0) + 1; + } else { + continues[k] = several; + } + } + } + region[s] = sweep; + } + } + + // A sweep keeps the region below it when all of that region's spans in + // this row are the sweep's — otherwise the region forks here, and each + // branch is a region of its own. + for (var k = 0; k < continues.length; k++) { + final before = continues[k]; + ids[k] = before > 0 && reached[before] == samples[k] ? before : next++; + } + + for (var cx = 0; cx < field.columns; cx++) { + final c = cz * field.columns + cx; + for (var s = field.cellStart[c]; s < field.cellStart[c + 1]; s++) { + if (region[s] > 0) region[s] = ids[region[s] - 1]; + } + } + } + return (region: region, count: next - 1); +} diff --git a/packages/flutter3d_sim/lib/src/nav/navmesh/span_field.dart b/packages/flutter3d_sim/lib/src/nav/navmesh/span_field.dart new file mode 100644 index 000000000..188174c42 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/nav/navmesh/span_field.dart @@ -0,0 +1,314 @@ +/// The level as columns of solid voxels, and the filters that decide which +/// tops of them are floors. +/// +/// **Sampled at cell centres, the way `NavGrid` samples.** A brush gives a +/// column a floor only when the column's centre is inside it; a brush that +/// only clips the column still fills it, as a span nobody stands on. That is +/// `NavGrid`'s rule — a thin wall crossing a cell blocks it even when no +/// centre lands inside — said in spans, and it is why the two bakes of one +/// level can be held against each other cell by cell. +library; + +import 'dart:math' as math; + +import '../../level/brush.dart'; +import '../../level/heightfield.dart'; +import '../../math/portable_math.dart'; +import '../../math/tolerances.dart'; +import 'navmesh_config.dart'; + +/// The area of a span nobody walks on. +const int nullArea = 0; + +/// One run of solid voxels in a column, `[min, max)` in voxels. +final class SolidSpan { + SolidSpan(this.min, this.max, this.area); + + final int min; + final int max; + + /// What the top of this span is, as a walking surface; [nullArea] when it + /// is not one. The one field the filters change. + int area; +} + +/// Solid voxels, column by column, lowest span first. +final class SpanField { + SpanField._({ + required this.originX, + required this.originY, + required this.originZ, + required this.cellSize, + required this.cellHeight, + required this.columns, + required this.rows, + required this.spans, + }); + + /// The world position of voxel `(0, 0, 0)`'s lowest corner. + final double originX; + final double originY; + final double originZ; + + final double cellSize; + final double cellHeight; + final int columns; + final int rows; + + /// Per column, `cz * columns + cx`, merged and sorted by height. + final List> spans; + + bool get isEmpty => columns == 0 || rows == 0; + + /// Rasterises the solid [brushes] and the [ground] into columns of spans. + /// + /// [areaOf] names a brush's walking surface; [groundArea] is the terrain's. + /// A surface steeper than `config.maxSlope` is solid and not a floor. + /// + /// **The lattice is `NavGrid.bake`'s**: its corner at the least x and z any + /// solid brush reaches, its count the extent over the cell size rounded up. + /// Terrain widens it to cover the field. + static SpanField rasterise( + Iterable brushes, { + required NavMeshConfig config, + Heightfield? ground, + required int Function(Brush brush) areaOf, + required int groundArea, + }) { + final solid = [ + for (final brush in brushes) + if (brush.solid) brush, + ]; + final cs = config.cellSize; + final ch = config.cellHeight; + final samples = ground?.copyOfSamples(); + final groundLow = samples == null || samples.isEmpty + ? null + : samples.reduce(math.min).toDouble(); + + if (solid.isEmpty && ground == null) { + return SpanField._( + originX: 0.0, + originY: 0.0, + originZ: 0.0, + cellSize: cs, + cellHeight: ch, + columns: 0, + rows: 0, + spans: const >[], + ); + } + + final (minX, minY, minZ, maxX, maxZ) = solid.fold( + ground == null + ? ( + double.infinity, + double.infinity, + double.infinity, + double.negativeInfinity, + double.negativeInfinity, + ) + : ( + ground.origin.x, + groundLow!, + ground.origin.z, + ground.origin.x + ground.width, + ground.origin.z + ground.depth, + ), + (box, brush) => ( + math.min(box.$1, brush.min.x), + math.min(box.$2, brush.min.y), + math.min(box.$3, brush.min.z), + math.max(box.$4, brush.max.x), + math.max(box.$5, brush.max.z), + ), + ); + + final columns = math.max(1, ((maxX - minX) / cs).ceil()); + final rows = math.max(1, ((maxZ - minZ) / cs).ceil()); + final raw = List>.generate( + columns * rows, + (_) => [], + ); + + int floorVoxel(double y) => + math.max(0, (((y - minY) / ch) + Tolerance.gridBias).floor()); + int topVoxel(double y) => (((y - minY) / ch) - Tolerance.gridBias).ceil(); + + // Compared as a ratio rather than as an angle, so the only transcendental + // in the whole bake is this one portable tangent. + final steepest = Portable.tan(config.maxSlope); + + for (final brush in solid) { + final lo = brush.min; + final hi = brush.max; + final ramp = brush.ramp; + final rampWalkable = + ramp == null || + brush.size.y <= + steepest * (ramp.x != 0.0 ? brush.size.x : brush.size.z); + final area = areaOf(brush); + + // Every column the brush overlaps by more than a hair, exactly as + // `NavGrid` buckets them. + final x0 = _clamp( + ((lo.x - minX) / cs + Tolerance.gridBias).floor(), + columns, + ); + final x1 = _clamp( + ((hi.x - minX) / cs - Tolerance.gridBias).floor(), + columns, + ); + final z0 = _clamp( + ((lo.z - minZ) / cs + Tolerance.gridBias).floor(), + rows, + ); + final z1 = _clamp( + ((hi.z - minZ) / cs - Tolerance.gridBias).floor(), + rows, + ); + + for (var cz = z0; cz <= z1; cz++) { + final pz = minZ + (cz + 0.5) * cs; + for (var cx = x0; cx <= x1; cx++) { + final px = minX + (cx + 0.5) * cs; + final centred = px >= lo.x && px < hi.x && pz >= lo.z && pz < hi.z; + final top = ramp == null + ? hi.y + : _rampHeight(brush, ramp.x, ramp.z, px, pz); + final bottom = floorVoxel(lo.y); + raw[cz * columns + cx].add( + SolidSpan( + bottom, + math.max(bottom, topVoxel(top)), + centred && rampWalkable ? area : nullArea, + ), + ); + } + } + } + + if (ground != null) { + final bottom = floorVoxel(groundLow!); + for (var cz = 0; cz < rows; cz++) { + final pz = minZ + (cz + 0.5) * cs; + for (var cx = 0; cx < columns; cx++) { + final px = minX + (cx + 0.5) * cs; + // Off the field is not ground, for `bakeHeightfield`'s reason: the + // field answers beyond its edge with the edge's height, and a floor + // laid on that answer is a floor that is not there. + if (!ground.contains(px, pz)) continue; + raw[cz * columns + cx].add( + SolidSpan( + bottom, + math.max(bottom, topVoxel(ground.heightAt(px, pz))), + ground.slopeAt(px, pz) <= config.maxSlope ? groundArea : nullArea, + ), + ); + } + } + } + + return SpanField._( + originX: minX, + originY: minY, + originZ: minZ, + cellSize: cs, + cellHeight: ch, + columns: columns, + rows: rows, + spans: >[for (final column in raw) _merged(column)], + ); + } + + /// Joins spans that overlap or touch. + /// + /// **Sorted before it is merged, so the order brushes are listed in cannot + /// matter.** Merging in arrival order makes the area of a shared top + /// depend on which brush came first; merging in sorted order makes it the + /// largest area among the spans that reach the top, whoever listed them. + static List _merged(List column) { + if (column.length < 2) return column; + column.sort( + (a, b) => a.min != b.min + ? a.min - b.min + : (a.max != b.max ? a.max - b.max : a.area - b.area), + ); + final out = []; + var current = column.first; + for (final next in column.skip(1)) { + if (next.min > current.max) { + out.add(current); + current = next; + } else if (next.max > current.max) { + current = SolidSpan(current.min, next.max, next.area); + } else if (next.max == current.max) { + current = SolidSpan( + current.min, + current.max, + math.max(current.area, next.area), + ); + } + } + out.add(current); + return out; + } + + /// The ramp's surface over `(x, z)`, held inside its footprint so that a + /// column the ramp only clips is filled to the nearest height it has. + static double _rampHeight( + Brush brush, + double uphillX, + double uphillZ, + double x, + double z, + ) { + final lo = brush.min; + final size = brush.size; + final along = uphillX != 0.0 ? (x - lo.x) / size.x : (z - lo.z) / size.z; + final t = (uphillX + uphillZ > 0.0 ? along : 1.0 - along).clamp(0.0, 1.0); + return lo.y + size.y * t; + } + + /// A top that is not a floor becomes one when it is a step up from a floor + /// in the same column. + /// + /// What it is for is a ledge the slope test refused and the agent can still + /// step onto — a kerb on a hillside, the lip of a steep ramp. Decided from + /// what each span was before this pass, so a stack of such tops does not + /// climb itself one step at a time. + void filterLowHangingObstacles(int climb) { + for (final column in spans) { + var belowWalkable = false; + var belowArea = nullArea; + var belowTop = 0; + for (final span in column) { + final walkable = span.area != nullArea; + if (!walkable && + belowWalkable && + (span.max - belowTop).abs() <= climb) { + span.area = belowArea; + } + belowWalkable = walkable; + belowArea = span.area; + belowTop = span.max; + } + } + } + + /// A floor with less room above it than [height] is not a floor. + void filterLowHeight(int height) { + for (final column in spans) { + for (var i = 0; i < column.length; i++) { + final ceiling = i + 1 < column.length ? column[i + 1].min : sky; + if (ceiling - column[i].max < height) column[i].area = nullArea; + } + } + } + + /// The height of open sky, in voxels: more room than any agent asks for. + static const int sky = 0x3fffffff; + + static int _clamp(int value, int count) => + value < 0 ? 0 : (value >= count ? count - 1 : value); +} diff --git a/packages/flutter3d_sim/test/navmesh_bake_test.dart b/packages/flutter3d_sim/test/navmesh_bake_test.dart new file mode 100644 index 000000000..ba32a55bc --- /dev/null +++ b/packages/flutter3d_sim/test/navmesh_bake_test.dart @@ -0,0 +1,451 @@ +/// The navigation mesh bake, held against the grid it sits beside. +/// +/// dart test test/navmesh_bake_test.dart +/// +/// Three claims run through every scene here. Every polygon is convex and +/// turns the same way, because the path search that comes next walks across +/// a polygon in a straight line and a reflex corner would put that line +/// through a wall. Adjacency is symmetric, because a search that can go from +/// one polygon to the next and not back finds routes that only work one way. +/// And the mesh covers exactly the cells `NavGrid` says a body of the same +/// radius fits in, because the two are two answers to one question about one +/// level and a disagreement between them is a bug in one or the other. +/// +/// Then the digests: each scene's mesh written down as one number, so that CI +/// on every operating system bakes the same mesh or says it did not. +library; + +import 'dart:typed_data'; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +Brush _box(double x0, double y0, double z0, double x1, double y1, double z1) => + Brush( + centre: Vector3((x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2), + size: Vector3(x1 - x0, y1 - y0, z1 - z0), + ); + +/// Ten metres square, its top at nought. +List _floor() => [_box(-5, -1, -5, 5, 0, 5)]; + +/// Two rooms either side of a wall half a metre thick, joined by a doorway +/// two metres wide under a lintel. +List _rooms() => [ + _box(-6, -1, -3, 6, 0, 3), + _box(0, 0, -3, 0.5, 3, -1), + _box(0, 0, 1, 0.5, 3, 3), + _box(0, 2.5, -1, 0.5, 3, 1), +]; + +/// A floor and two steps up from it, each taller than an agent climbs. +List _tallSteps() => [ + _box(-2, -1, -4, 2, 0, 0), + _box(-2, -1, 0, 2, 0.6, 2), + _box(-2, -1, 2, 2, 1.2, 4), +]; + +/// The same, each step short enough to walk up. +List _shortSteps() => [ + _box(-2, -1, -4, 2, 0, 0), + _box(-2, -1, 0, 2, 0.3, 2), + _box(-2, -1, 2, 2, 0.6, 4), +]; + +/// A floor, a ramp climbing a metre and a half over six, and the platform at +/// its top. +List _ramp() => [ + _box(-2, -1, -4, 2, 0, 0), + Brush( + centre: Vector3(0, 0.75, 3), + size: Vector3(4, 1.5, 6), + ramp: WedgeUphill.positiveZ, + ), + _box(-2, -1, 6, 2, 1.5, 10), +]; + +/// The floor with a pillar a metre square standing in the middle of it. +List _pillar() => [..._floor(), _box(0, 0, 0, 1, 3, 1)]; + +/// Twenty degrees of slope and the default step: ground steeper than the one +/// and gentler than the other exists, which is the point of [_hillside]. +const NavMeshConfig _hills = NavMeshConfig(maxSlope: 0.35); + +/// Terrain: flat for eight metres, then a hillside rising one in five, then +/// a bank rising three in five, then flat again at the top. +/// +/// **The bank is steep and not tall.** Thirty-one degrees, past the twenty +/// [_hills] allows, but each half-metre cell of it rises 0.3 — under the +/// step. So the slope test is the only thing between the hillside and the +/// top; a cliff would be refused by the step as well, and a test of it would +/// pass with the slope test deleted. +Heightfield _hillside() { + const columns = 21; + const rows = 9; + double height(int x) => switch (x) { + < 8 => 0.0, + < 14 => (x - 8) * 0.2, + < 17 => 1.2 + (x - 14) * 0.6, + _ => 3.0, + }; + return Heightfield( + columns: columns, + rows: rows, + cellSize: 1.0, + heights: Float32List.fromList([ + for (var z = 0; z < rows; z++) + for (var x = 0; x < columns; x++) height(x), + ]), + ); +} + +/// Twice the signed area of corner [k] of [polygon], turning from the corner +/// before it to the one after, in lattice units. +int _turn(NavMesh mesh, int polygon, int k) { + final n = mesh.polygonVertexCount(polygon); + final a = mesh.polygonVertex(polygon, (k + n - 1) % n); + final b = mesh.polygonVertex(polygon, k); + final c = mesh.polygonVertex(polygon, (k + 1) % n); + return (mesh.latticeX(b) - mesh.latticeX(a)) * + (mesh.latticeZ(c) - mesh.latticeZ(a)) - + (mesh.latticeZ(b) - mesh.latticeZ(a)) * + (mesh.latticeX(c) - mesh.latticeX(a)); +} + +/// The claims every mesh in this file has to meet, whatever it was baked +/// from. +void _expectWellFormed(NavMesh mesh) { + for (var p = 0; p < mesh.polygonCount; p++) { + final n = mesh.polygonVertexCount(p); + expect(n, inInclusiveRange(3, mesh.maxVerticesPerPolygon)); + expect(mesh.areaOf(p), isNot(NavArea.none)); + // Mutation: dropping the reversal of outlines that turn right leaves + // whole polygons wound the other way, and every corner of them fails. + for (var k = 0; k < n; k++) { + expect( + _turn(mesh, p, k), + greaterThanOrEqualTo(0), + reason: 'polygon $p has a reflex corner at $k', + ); + } + final area = Iterable.generate(n).fold(0, (sum, k) { + final a = mesh.polygonVertex(p, k); + final b = mesh.polygonVertex(p, (k + 1) % n); + return sum + + mesh.latticeX(a) * mesh.latticeZ(b) - + mesh.latticeX(b) * mesh.latticeZ(a); + }); + expect(area, greaterThan(0), reason: 'polygon $p covers nothing'); + + // Mutation: recording a match on one side only — `out[slot] = …` without + // `out[other] = p` — leaves every second polygon blind to the first. + for (var k = 0; k < n; k++) { + final q = mesh.neighbourAt(p, k); + if (q < 0) continue; + final a = mesh.polygonVertex(p, k); + final b = mesh.polygonVertex(p, (k + 1) % n); + final m = mesh.polygonVertexCount(q); + final back = Iterable.generate(m).where( + (j) => + mesh.neighbourAt(q, j) == p && + mesh.polygonVertex(q, j) == b && + mesh.polygonVertex(q, (j + 1) % m) == a, + ); + expect(back, hasLength(1), reason: '$p sees $q across $k, not back'); + } + } +} + +/// Polygons reachable from [start] across shared edges. +Set _reachable(NavMesh mesh, int start) { + final seen = {start}; + final queue = [start]; + for (var i = 0; i < queue.length; i++) { + final p = queue[i]; + for (var k = 0; k < mesh.polygonVertexCount(p); k++) { + final q = mesh.neighbourAt(p, k); + if (q >= 0 && seen.add(q)) queue.add(q); + } + } + return seen; +} + +/// The lowest and highest corner of [polygon], in metres. +(double, double) _heights(NavMesh mesh, int polygon) { + final v = Vector3.zero(); + return Iterable.generate(mesh.polygonVertexCount(polygon)).fold( + (double.infinity, double.negativeInfinity), + (range, k) { + mesh.vertexAt(mesh.polygonVertex(polygon, k), v); + return (range.$1 < v.y ? range.$1 : v.y, range.$2 > v.y ? range.$2 : v.y); + }, + ); +} + +/// The polygon a body stands on at the centre of grid cell [cell], or −1. +int _standingOn(NavMesh mesh, NavGrid grid, int cell) { + final centre = grid.centreOfCell(cell); + return mesh.polygonsAt(centre.x, centre.z).firstWhere((p) { + final (low, high) = _heights(mesh, p); + return low - grid.stepHeight <= centre.y && + centre.y <= high + grid.stepHeight; + }, orElse: () => -1); +} + +/// Cell by cell: the mesh covers a cell's centre exactly when the grid says a +/// body of [radius] fits there. +void _expectCoverageMatches(NavMesh mesh, NavGrid grid, double radius) { + expect(mesh.originX, grid.originX, reason: 'not the same lattice'); + expect(mesh.originZ, grid.originZ, reason: 'not the same lattice'); + final need = grid.clearanceForRadius(radius); + final wrong = [ + for (var cell = 0; cell < grid.cellCount; cell++) + if ((grid.isWalkable(cell) && grid.clearanceAt(cell) >= need) != + (_standingOn(mesh, grid, cell) >= 0)) + '(${grid.cellX(cell)}, ${grid.cellZ(cell)})', + ]; + expect(wrong, isEmpty, reason: 'the mesh and the grid disagree at $wrong'); +} + +void main() { + const config = NavMeshConfig(); + + group('a flat floor', () { + final mesh = NavMesh.bake(_floor()); + + test('is one quadrilateral, a body\'s radius in from the edge', () { + _expectWellFormed(mesh); + // Mutation: eroding by `agentRadius / cellSize` rounded down, rather + // than by `NavGrid`'s rule, keeps the outer ring and the corners land on + // the brush's own edge at (0, 0) and (20, 20). + expect(mesh.polygonCount, 1); + expect(mesh.polygonVertexCount(0), 4); + final corners = <(int, int)>{ + for (var k = 0; k < 4; k++) + ( + mesh.latticeX(mesh.polygonVertex(0, k)), + mesh.latticeZ(mesh.polygonVertex(0, k)), + ), + }; + expect(corners, <(int, int)>{(1, 1), (19, 1), (19, 19), (1, 19)}); + final v = Vector3.zero(); + mesh.vertexAt(mesh.polygonVertex(0, 0), v); + expect(v.y, closeTo(0.0, 1e-9), reason: 'the floor is at nought'); + }); + + test('covers what the grid covers', () { + _expectCoverageMatches(mesh, NavGrid.bake(_floor()), config.agentRadius); + }); + }); + + group('two rooms and a doorway', () { + test('are one walk for a body that fits the doorway', () { + final mesh = NavMesh.bake(_rooms()); + _expectWellFormed(mesh); + final west = _standingOnPoint(mesh, -4.0, 0.0); + final east = _standingOnPoint(mesh, 4.0, 0.0); + expect(west, isNonNegative); + expect(east, isNonNegative); + // Mutation: matching an edge against itself rather than its reverse — + // `open.remove((a, b))` — finds no neighbour anywhere, and the rooms + // fall apart along with everything else. + expect(_reachable(mesh, west), contains(east)); + _expectCoverageMatches(mesh, NavGrid.bake(_rooms()), config.agentRadius); + }); + + test('and two for a body too wide for it', () { + const wide = NavMeshConfig(agentRadius: 0.8); + final mesh = NavMesh.bake(_rooms(), config: wide); + _expectWellFormed(mesh); + final west = _standingOnPoint(mesh, -4.0, 0.0); + final east = _standingOnPoint(mesh, 4.0, 0.0); + // Mutation: the erosion rule of the flat floor's test, the radius over + // the cell rounded down, keeps a cell of the doorway for this body too. + expect(_reachable(mesh, west), isNot(contains(east))); + _expectCoverageMatches(mesh, NavGrid.bake(_rooms()), wide.agentRadius); + }); + + test('and the wall\'s own top is not a floor anybody fits on', () { + final mesh = NavMesh.bake(_rooms()); + final v = Vector3.zero(); + for (var i = 0; i < mesh.vertexCount; i++) { + mesh.vertexAt(i, v); + expect(v.y, lessThan(0.5), reason: 'a vertex on the wall at $v'); + } + }); + }); + + group('steps', () { + test('taller than a step are floors that do not touch', () { + final mesh = NavMesh.bake(_tallSteps()); + _expectWellFormed(mesh); + final bottom = _standingOnPoint(mesh, 0.0, -2.0); + final top = _standingOnPoint(mesh, 0.0, 3.0); + expect(bottom, isNonNegative); + expect(top, isNonNegative); + expect(_reachable(mesh, bottom), isNot(contains(top))); + _expectCoverageMatches( + mesh, + NavGrid.bake(_tallSteps()), + config.agentRadius, + ); + }); + + test('even for a body with no width, which nothing erodes', () { + // **The radius hides a riser.** Eroded by a body's width, the floor + // either side of a riser is gone before anything links across it, so + // the claim above holds whether or not the link is right. Here there + // is no erosion and the link is all that keeps the floors apart. + const thin = NavMeshConfig(agentRadius: 0.0); + final mesh = NavMesh.bake(_tallSteps(), config: thin); + _expectWellFormed(mesh); + final bottom = _standingOnPoint(mesh, 0.0, -2.0); + final top = _standingOnPoint(mesh, 0.0, 3.0); + expect(_reachable(mesh, bottom), isNot(contains(top))); + // Mutation: comparing the rise without `.abs()` in `OpenField.build` + // links every step down to the floor below it. The first row of each + // step joins the region under it, and the mesh stops covering that + // row where the grid does — rows 8 and 12, measured. + _expectCoverageMatches(mesh, NavGrid.bake(_tallSteps()), 0.0); + }); + + test('short enough to climb are one walk', () { + final mesh = NavMesh.bake(_shortSteps()); + _expectWellFormed(mesh); + final bottom = _standingOnPoint(mesh, 0.0, -2.0); + final top = _standingOnPoint(mesh, 0.0, 3.0); + expect(_reachable(mesh, bottom), contains(top)); + _expectCoverageMatches( + mesh, + NavGrid.bake(_shortSteps()), + config.agentRadius, + ); + }); + }); + + group('a ramp', () { + test('is a walk from the floor to the platform it climbs to', () { + final mesh = NavMesh.bake(_ramp()); + _expectWellFormed(mesh); + final bottom = _standingOnPoint(mesh, 0.0, -2.0); + final top = _standingOnPoint(mesh, 0.0, 8.0); + expect(bottom, isNonNegative); + expect(top, isNonNegative); + // Mutation: rasterising a ramp at the top of its box, as the grid + // does, makes its foot a riser a metre and a half tall. + expect(_reachable(mesh, bottom), contains(top)); + }); + }); + + group('a pillar in the middle of the floor', () { + final mesh = NavMesh.bake(_pillar()); + + test( + 'is a hole the polygons go round, and the floor is still one walk', + () { + _expectWellFormed(mesh); + // Mutation: keeping the lower top when two spans in a column merge — + // the floor's, rather than the pillar's standing on it — lays a floor + // under the pillar that the agent walks through. + expect(mesh.polygonsAt(0.5, 0.5), isEmpty); + expect(_reachable(mesh, 0), hasLength(mesh.polygonCount)); + _expectCoverageMatches( + mesh, + NavGrid.bake(_pillar()), + config.agentRadius, + ); + }, + ); + }); + + group('terrain', () { + final field = _hillside(); + final mesh = NavMesh.bake(const [], ground: field, config: _hills); + + test('walks up a hillside and stops at a bank too steep for it', () { + _expectWellFormed(mesh); + final low = _standingOnPoint(mesh, 3.0, 4.0); + final slope = _standingOnPoint(mesh, 11.0, 4.0); + final top = _standingOnPoint(mesh, 19.0, 4.0); + expect(low, isNonNegative); + expect(slope, isNonNegative); + expect(top, isNonNegative); + // Mutation: rasterising terrain without the slope test makes the bank + // a floor, and at 0.3 a cell it is a walk up to the top. + expect(mesh.polygonsAt(15.5, 4.0), isEmpty); + expect(_reachable(mesh, low), contains(slope)); + expect(_reachable(mesh, low), isNot(contains(top))); + final (_, high) = _heights(mesh, slope); + expect(high, greaterThan(0.5), reason: 'the hillside baked flat'); + }); + + test('covers what the grid covers', () { + _expectCoverageMatches( + mesh, + NavGrid.bakeHeightfield( + field, + stepHeight: _hills.stepHeight, + maxSlope: _hills.maxSlope, + ), + _hills.agentRadius, + ); + }); + }); + + group('an empty level', () { + test('bakes an empty mesh rather than refusing', () { + final mesh = NavMesh.bakeLevel(Level(name: 'empty')); + expect(mesh.isEmpty, isTrue); + expect(mesh.vertexCount, 0); + }); + }); + + group('the same mesh everywhere', () { + test('listing the brushes in another order bakes the same mesh', () { + // Mutation: merging spans in arrival order rather than sorted makes the + // area of a shared top depend on which brush came first. + expect( + NavMesh.bake(_rooms().reversed).digest, + NavMesh.bake(_rooms()).digest, + ); + expect( + NavMesh.bake(_pillar().reversed).digest, + NavMesh.bake(_pillar()).digest, + ); + }); + + // Written down on macOS-arm64 under the VM, matched the same day under + // Chrome, and compared on every other machine CI runs on. A change here + // is a change to the mesh: say why in the commit that makes it, or find + // out what moved. + // + // Mutation: any float the voxeliser rounds differently on another + // platform — a libm `tan` for the ramp test, an `atan2` in the slope — + // moves a span by a voxel and the digest with it, on that platform only. + final golden = <(String, NavMesh Function(), String)>[ + ('empty', () => NavMesh.bakeLevel(Level(name: 'empty')), '39326bec'), + ('floor', () => NavMesh.bake(_floor()), '2634bd3b'), + ('rooms', () => NavMesh.bake(_rooms()), '6fced5bb'), + ('tall steps', () => NavMesh.bake(_tallSteps()), '14aa4be5'), + ('pillar', () => NavMesh.bake(_pillar()), '1ca6d0b9'), + ( + 'hillside', + () => + NavMesh.bake(const [], ground: _hillside(), config: _hills), + '999240fe', + ), + ]; + for (final (name, bake, digest) in golden) { + test('the $name mesh is $digest', () { + expect(bake().digestHex, digest); + }); + } + }); +} + +/// The polygon under world `(x, z)`, or −1. +int _standingOnPoint(NavMesh mesh, double x, double z) { + final found = mesh.polygonsAt(x, z); + return found.isEmpty ? -1 : found.first; +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 103759b04..a652af21b 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11197 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11212 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index b2685a8a6..96fee1b60 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,17 +1,17 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11197 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11212 tests need a GPU. --- # Testing -11197 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11212 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| | `flutter3d` | 1642 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | -| `flutter3d_sim` | 666 | | `pad_input` | 67 | +| `flutter3d_sim` | 681 | | `pad_input` | 67 | | `flutter3d_lab` | 15 | | `flutter3d_core` | 750 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11171 rather than 11197: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11186 rather than 11212: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. From 5ff47d97a878804c83a53af93bee267f273be87e Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 05:44:38 +0300 Subject: [PATCH 050/141] An animation graph picks the clip in the fixed step AnimationGraph runs a state machine over typed parameters in the fixed step, so which clip plays is part of the simulation and replays with it; transitions crossfade through Pose.blendFrom, the way the player already blends. The shortest-arc slerp the player kept to itself is shared now. --- ARCHITECTURE.md | 2 +- README.md | 2 +- packages/flutter3d_core/CHANGELOG.md | 20 + .../flutter3d_core/lib/flutter3d_core.dart | 5 + .../src/engine/animation/animation_layer.dart | 42 ++ .../engine/animation/animation_player.dart | 41 +- .../animation/graph/animation_graph.dart | 265 +++++++++++ .../animation/graph/animation_parameters.dart | 210 +++++++++ .../graph/animation_state_machine.dart | 281 +++++++++++ .../lib/src/engine/animation/pose.dart | 58 +++ .../test/animation_graph_test.dart | 435 ++++++++++++++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- 13 files changed, 1326 insertions(+), 45 deletions(-) create mode 100644 packages/flutter3d_core/lib/src/engine/animation/graph/animation_graph.dart create mode 100644 packages/flutter3d_core/lib/src/engine/animation/graph/animation_parameters.dart create mode 100644 packages/flutter3d_core/lib/src/engine/animation/graph/animation_state_machine.dart create mode 100644 packages/flutter3d_core/test/animation_graph_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 275ea12e6..f0dea4f57 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11212 tests** across 43 packages and 10 applications | +| Unit tests | **11224 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index a5b24b517..d9d1706f1 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11212 tests across forty-three packages and nine applications. The +There are 11224 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index 0bf92438c..8637090aa 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -98,6 +98,26 @@ the software backend's `readHdrPixels` can answer, and each `CapturedPass` now carries its time, draws and triangles. +- **An animation graph decides which clip plays, in the fixed step.** + `AnimationStateMachine` holds states over named clips and transitions + between them, each with conditions on typed parameters, a crossfade + duration, a priority and an optional exit time; `AnimationGraph` runs it, + and `evaluate(dt)` advances by the simulation's step and returns a `Pose`. + Parameters are float, integer, boolean or trigger, declared in an + `AnimationParameterSchema`, and a write of the wrong type or to a name the + schema lacks is refused with a `ParameterWrite` saying what to call + instead. A trigger stays set until a transition uses it, so an attack + pressed during a swing lands when the swing allows. A definition that + cannot run lists its problems through `problems(clips)` rather than + building a graph whose transitions are never taken. Until now the choice + of clip was left to each game, written against `AnimationPlayer` by hand. + +- **`Pose.blendFrom` crossfades whole poses the way the player does.** + The player's shortest-arc slerp is now one shared function, + `shortestArcSlerp`, used by both, so a graph and a player fading between + the same two clips turn each joint the same way. `Pose.restCopy` gives a + second pose over the same rest for the outgoing clip. + - **A reload reaches the contributors too.** `Renderer.relinkShaders` asks every `PassContributor` to drop what it linked, through the new `PassContributor.relinkShaders`, which does nothing by default. diff --git a/packages/flutter3d_core/lib/flutter3d_core.dart b/packages/flutter3d_core/lib/flutter3d_core.dart index 777ff64c7..ef9676e1a 100644 --- a/packages/flutter3d_core/lib/flutter3d_core.dart +++ b/packages/flutter3d_core/lib/flutter3d_core.dart @@ -48,6 +48,11 @@ export 'src/engine/animation/animation.dart'; export 'src/engine/animation/animation_player.dart'; export 'src/engine/animation/animation_target.dart'; export 'src/engine/animation/baked_poses.dart'; +// The animation graph, N1: typed parameters, a state machine over clips, and +// the fixed-step evaluation that turns them into a `Pose`. +export 'src/engine/animation/graph/animation_graph.dart'; +export 'src/engine/animation/graph/animation_parameters.dart'; +export 'src/engine/animation/graph/animation_state_machine.dart'; export 'src/engine/animation/inverse_kinematics.dart'; export 'src/engine/animation/pose.dart'; export 'src/engine/animation/skin_blend.dart'; diff --git a/packages/flutter3d_core/lib/src/engine/animation/animation_layer.dart b/packages/flutter3d_core/lib/src/engine/animation/animation_layer.dart index 35f7d25f4..1e583478b 100644 --- a/packages/flutter3d_core/lib/src/engine/animation/animation_layer.dart +++ b/packages/flutter3d_core/lib/src/engine/animation/animation_layer.dart @@ -1,4 +1,8 @@ +import 'dart:math' as math; + import 'package:flutter3d_core/formats.dart'; +import 'package:vector_math/vector_math.dart'; + import 'animation_target.dart'; /// Where a track sits in a pose: one number for a node and a path together. @@ -242,3 +246,41 @@ final class AnimationLayer { return (time: next, reversing: turned, stopped: false); } + +/// Shortest-arc interpolation between two rotations, [t] of the way from +/// [from] to [to]. +/// +/// **One copy, for the same reason [advanceTime] is one.** It was the +/// player's own, and the animation graph's crossfade needs exactly it: two +/// copies would be two chances for a graph and a player fading between the +/// same clips to turn a joint a different way. +/// +/// The sign flip is the part that matters: a quaternion and its negation are +/// the same rotation, so without choosing the closer of the two, half of all +/// blends spin the long way. +Quaternion shortestArcSlerp(Quaternion from, Quaternion to, double t) { + final raw = from.x * to.x + from.y * to.y + from.z * to.z + from.w * to.w; + final sign = raw < 0.0 ? -1.0 : 1.0; + final dot = raw * sign; + + final (scaleFrom, scaleTo) = dot > 0.9995 + // Nearly identical: the arc is so short that a straight line is closer + // than the trigonometry's own error. + ? (1.0 - t, t) + : _arcScales(math.acos(dot), t); + + return Quaternion( + scaleFrom * from.x + scaleTo * sign * to.x, + scaleFrom * from.y + scaleTo * sign * to.y, + scaleFrom * from.z + scaleTo * sign * to.z, + scaleFrom * from.w + scaleTo * sign * to.w, + )..normalize(); +} + +(double, double) _arcScales(double theta, double t) { + final sinTheta = math.sin(theta); + return ( + math.sin((1.0 - t) * theta) / sinTheta, + math.sin(t * theta) / sinTheta, + ); +} diff --git a/packages/flutter3d_core/lib/src/engine/animation/animation_player.dart b/packages/flutter3d_core/lib/src/engine/animation/animation_player.dart index d3af52fdb..3c35b2eaa 100644 --- a/packages/flutter3d_core/lib/src/engine/animation/animation_player.dart +++ b/packages/flutter3d_core/lib/src/engine/animation/animation_player.dart @@ -241,41 +241,6 @@ final class AnimationPlayer { static double _mix(double from, double to, double t) => from + (to - from) * t; - /// Shortest-arc interpolation between two rotations. - /// - /// The sign flip is the part that matters: a quaternion and its negation are - /// the same rotation, so without choosing the closer of the two, half of all - /// blends spin the long way. - static Quaternion _slerp(Quaternion from, Quaternion to, double t) { - var dot = from.x * to.x + from.y * to.y + from.z * to.z + from.w * to.w; - var sign = 1.0; - if (dot < 0.0) { - dot = -dot; - sign = -1.0; - } - - double scaleFrom; - double scaleTo; - if (dot > 0.9995) { - // Nearly identical: the arc is so short that a straight line is closer - // than the trigonometry's own error. - scaleFrom = 1.0 - t; - scaleTo = t; - } else { - final theta = math.acos(dot); - final sinTheta = math.sin(theta); - scaleFrom = math.sin((1.0 - t) * theta) / sinTheta; - scaleTo = math.sin(t * theta) / sinTheta; - } - - return Quaternion( - scaleFrom * from.x + scaleTo * sign * to.x, - scaleFrom * from.y + scaleTo * sign * to.y, - scaleFrom * from.z + scaleTo * sign * to.z, - scaleFrom * from.w + scaleTo * sign * to.w, - )..normalize(); - } - /// [q] scaled by [t]: a quarter of a turn, not a quarter of four numbers. /// /// The slerp from no rotation, written out — the identity end collapses most @@ -285,8 +250,8 @@ final class AnimationPlayer { /// nowhere else — which is why the fixture for it uses a quarter. static Quaternion _scaledRotation(Quaternion q, double t) { // A quaternion and its negation are the same rotation, and the one with a - // positive w is the short way round — the same choice [_slerp] makes, and - // for the same reason. + // positive w is the short way round — the same choice [shortestArcSlerp] + // makes, and for the same reason. final sign = q.w < 0.0 ? -1.0 : 1.0; final w = sign * q.w; if (w > 0.9995) { @@ -790,7 +755,7 @@ final class AnimationPlayer { // Slerp, not a component lerp: blending quaternions linearly and // renormalising takes the long way round whenever the two are more than // a quarter turn apart, which is exactly what a hurt reaction is. - final mixed = _slerp(_fadeQuaternion, _quaternion, weight); + final mixed = shortestArcSlerp(_fadeQuaternion, _quaternion, weight); into[0] = mixed.x; into[1] = mixed.y; into[2] = mixed.z; diff --git a/packages/flutter3d_core/lib/src/engine/animation/graph/animation_graph.dart b/packages/flutter3d_core/lib/src/engine/animation/graph/animation_graph.dart new file mode 100644 index 000000000..48476f7d9 --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/animation/graph/animation_graph.dart @@ -0,0 +1,265 @@ +import 'package:flutter3d_core/formats.dart'; + +import '../animation_layer.dart'; +import '../pose.dart'; +import 'animation_parameters.dart'; +import 'animation_state_machine.dart'; + +/// Runs an [AnimationStateMachine] over a set of clips, one fixed step at a +/// time, into a [Pose] — N1's core. +/// +/// **Advanced by the step, not by the frame.** [evaluate] is handed the +/// simulation's own `stepSeconds`, the way everything else a run steps is, so +/// the same parameters written on the same steps take the same transitions on +/// every machine and on every replay. Which state the graph is in, how far +/// into it and how far through a fade are sums and quotients of step lengths +/// and nothing else; the pose it hands back passes through the same slerp a +/// clip's own keys do, which is the frame's arithmetic and not part of what a +/// run has to agree on. +/// +/// **One transition a step, and none during a crossfade.** A fade that could +/// be interrupted would need the half-blended pose frozen as the new source, +/// which is a second kind of state for a snapshot to carry; until a graph +/// needs that, a trigger set during a fade waits for it to finish, which is +/// what [AnimationParameterType.trigger]'s buffering is for. +/// +/// **The crossfade is the player's.** The outgoing state keeps playing while +/// the incoming one comes in, and the two are mixed by [Pose.blendFrom], which +/// is `AnimationPlayer`'s own blend on whole poses. Whole poses is the one +/// difference: a joint the outgoing clip does not animate is faded from its +/// rest, where the player, which fades track by track, has nothing to fade +/// it from and cuts. +final class AnimationGraph { + /// Builds a graph over [machine], playing [clips] into [pose]. + /// + /// Throws an [ArgumentError] listing [AnimationStateMachine.problems] when + /// there are any: a definition that cannot run is a bug in whoever built + /// it, and a tool that wants to refuse politely asks `problems` first. + factory AnimationGraph({ + required AnimationStateMachine machine, + required List clips, + required Pose pose, + }) { + final problems = machine.problems(clips); + if (problems.isNotEmpty) { + throw ArgumentError( + 'This animation graph cannot run:\n- ${problems.join('\n- ')}', + ); + } + final byName = { + for (final clip in clips.reversed) ?clip.name: clip, + }; + final outgoing = >[ + for (final state in machine.states) _outgoingFrom(machine, state.name), + ]; + return AnimationGraph._( + machine, + AnimationParameters(machine.parameters), + [for (final state in machine.states) byName[state.clip]!], + outgoing, + pose, + ); + } + + AnimationGraph._( + this.machine, + this.parameters, + this._clips, + this._outgoing, + this.pose, + ) : _from = pose.restCopy(), + _current = machine.indexOfState(machine.entry) { + _sample(); + } + + final AnimationStateMachine machine; + + /// The values the transitions test; written by the game between steps. + final AnimationParameters parameters; + + /// What [evaluate] writes into and returns. + final Pose pose; + + /// The clip each state plays, index-aligned with the machine's states. + final List _clips; + + /// Each state's way out, highest priority first. + final List> _outgoing; + + /// Where the outgoing state is sampled during a crossfade. + final Pose _from; + + int _current; + _Playhead _head = _Playhead.start; + + /// The state being faded out of, or -1 when there is no crossfade. + int _previous = -1; + _Playhead _previousHead = _Playhead.start; + double _fadeElapsed = 0.0; + double _fadeDuration = 0.0; + + /// The state the graph is in — during a crossfade, the one it is entering. + String get state => machine.states[_current].name; + + /// The state being faded out of, or null. + String? get fadingFrom => + _previous < 0 ? null : machine.states[_previous].name; + + /// How much of [state] the pose holds: 1 unless a crossfade is under way. + double get fadeWeight => _previous < 0 ? 1.0 : _fadeElapsed / _fadeDuration; + + /// The playhead in [state]'s clip, in seconds. + double get stateTime => _head.time; + + /// How far into [state] the graph is, in lengths of its clip — what an exit + /// time is compared against. + double get normalizedTime => _normalized(_current, _head); + + /// Advances by [dt] seconds, takes at most one transition, and returns + /// [pose] holding the result. + /// + /// A [dt] that is not a finite positive number advances nothing, the way + /// `FixedStep.advance` treats a clock that went backwards; transitions are + /// still tested, so a parameter written between steps is seen on the next + /// one whatever the clock did. + Pose evaluate(double dt) { + final step = dt.isFinite && dt > 0.0 ? dt : 0.0; + _head = _advance(_current, _head, step); + if (_previous >= 0) { + _previousHead = _advance(_previous, _previousHead, step); + _fadeElapsed += step; + if (_fadeElapsed >= _fadeDuration) _previous = -1; + } + if (_previous < 0) _takeTransition(); + _sample(); + return pose; + } + + void _takeTransition() { + final normalized = _normalized(_current, _head); + for (final transition in _outgoing[_current]) { + if (transition.exitTime case final exit? when normalized < exit) { + continue; + } + if (!transition.checks.every(_holds)) continue; + for (final check in transition.checks) { + if (check.condition is TriggerCondition) { + parameters.consumeTriggerAt(check.parameter); + } + } + if (transition.duration > 0.0) { + _previous = _current; + _previousHead = _head; + _fadeElapsed = 0.0; + _fadeDuration = transition.duration; + } + _current = transition.to; + _head = _Playhead.start; + return; + } + } + + bool _holds(_Check check) { + final actual = parameters.valueAt(check.parameter); + return switch (check.condition) { + final CompareCondition c => c.holds(actual), + BoolCondition(:final value) => (actual != 0.0) == value, + TriggerCondition() => actual != 0.0, + }; + } + + _Playhead _advance(int state, _Playhead head, double step) { + final length = _clips[state].duration; + final delta = step * machine.states[state].speed; + if (length <= 0.0 || delta == 0.0) { + return _Playhead(head.time, head.elapsed + delta, head.reversing); + } + final moved = advanceTime( + time: head.time, + deltaSeconds: delta, + length: length, + wrap: machine.states[state].wrap, + reversing: head.reversing, + ); + return _Playhead(moved.time, head.elapsed + delta, moved.reversing); + } + + /// A clip with no length is a single pose, done as soon as it is entered. + double _normalized(int state, _Playhead head) { + final length = _clips[state].duration; + return length <= 0.0 ? 1.0 : head.elapsed / length; + } + + void _sample() { + pose.sampleClip(_clips[_current], _head.time); + if (_previous < 0) return; + _from.sampleClip(_clips[_previous], _previousHead.time); + pose.blendFrom(_from, fadeWeight); + } + + static List<_Transition> _outgoingFrom( + AnimationStateMachine machine, + String from, + ) { + final declared = <(int, AnimationTransition)>[ + for (final (i, t) in machine.transitions.indexed) + if (t.from == from) (i, t), + ]; + // `List.sort` is not stable, so declaration order is made part of the key + // rather than hoped for. + declared.sort( + (a, b) => switch (b.$2.priority.compareTo(a.$2.priority)) { + 0 => a.$1.compareTo(b.$1), + final order => order, + }, + ); + return <_Transition>[ + for (final (_, t) in declared) + _Transition( + to: machine.indexOfState(t.to), + duration: t.duration, + exitTime: t.exitTime, + checks: <_Check>[ + for (final c in t.conditions) + _Check(machine.parameters.indexOf(c.parameter), c), + ], + ), + ]; + } +} + +/// Where a state's clip is, and how long the graph has been in the state. +/// +/// [elapsed] is not wrapped: an exit time counts from entering the state, so +/// a loop that has gone round must not read as having just begun. +final class _Playhead { + const _Playhead(this.time, this.elapsed, this.reversing); + + static const _Playhead start = _Playhead(0.0, 0.0, false); + + final double time; + final double elapsed; + final bool reversing; +} + +/// A transition with its names resolved to indices. +final class _Transition { + const _Transition({ + required this.to, + required this.duration, + required this.exitTime, + required this.checks, + }); + + final int to; + final double duration; + final double? exitTime; + final List<_Check> checks; +} + +final class _Check { + const _Check(this.parameter, this.condition); + + final int parameter; + final AnimationCondition condition; +} diff --git a/packages/flutter3d_core/lib/src/engine/animation/graph/animation_parameters.dart b/packages/flutter3d_core/lib/src/engine/animation/graph/animation_parameters.dart new file mode 100644 index 000000000..5d353b8e4 --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/animation/graph/animation_parameters.dart @@ -0,0 +1,210 @@ +import 'dart:typed_data'; + +/// What kind of value an animation graph parameter holds — N1. +/// +/// **A class with four constants rather than an enum**, so that a fifth kind +/// — a vector for a 2D blend space is the likely one — is an addition rather +/// than a break in every `switch` somebody wrote against the four. +final class AnimationParameterType { + const AnimationParameterType._(this.name, this.writer); + + final String name; + + /// The [AnimationParameters] call that writes this kind, named in a + /// refusal so the caller is told what to call instead. + final String writer; + + /// A real number: a speed, a lean, an aim angle. + static const AnimationParameterType float = AnimationParameterType._( + 'float', + 'setFloat', + ); + + /// A whole number: a weapon slot, a combo stage. + static const AnimationParameterType integer = AnimationParameterType._( + 'integer', + 'setInteger', + ); + + /// On or off, and stays where it is put: grounded, crouching, armed. + static const AnimationParameterType boolean = AnimationParameterType._( + 'boolean', + 'setBool', + ); + + /// On until a transition uses it, then off: attack, jump, hit. + /// + /// **Kept until it is consumed, not cleared at the end of the step.** An + /// attack pressed in the middle of a swing whose transition waits for an + /// exit time is the buffered input a player expects to see land; a trigger + /// that lasted one step would be dropped on the floor. A caller who wants + /// the opposite calls [AnimationParameters.resetTrigger]. + static const AnimationParameterType trigger = AnimationParameterType._( + 'trigger', + 'fire', + ); + + @override + String toString() => name; +} + +/// One named parameter in a graph's schema, with the value it starts at. +final class AnimationParameter { + const AnimationParameter.float(this.name, {this.initial = 0.0}) + : type = AnimationParameterType.float; + + const AnimationParameter.integer(this.name, {int initial = 0}) + : type = AnimationParameterType.integer, + initial = initial + 0.0; + + const AnimationParameter.boolean(this.name, {bool initial = false}) + : type = AnimationParameterType.boolean, + initial = initial ? 1.0 : 0.0; + + const AnimationParameter.trigger(this.name) + : type = AnimationParameterType.trigger, + initial = 0.0; + + final String name; + final AnimationParameterType type; + + /// The starting value as stored: see [AnimationParameters.values]. + final double initial; +} + +/// The parameters a graph has, by name and type — the only ones a write may +/// name. +/// +/// **A schema rather than a map that grows on first write.** A misspelt +/// `"grounded"` written into a map that accepts anything is a parameter +/// nobody reads, and the character never lands; written against a schema it +/// is a refusal naming the parameters there are. +final class AnimationParameterSchema { + AnimationParameterSchema(List parameters) + : parameters = List.unmodifiable(parameters), + _index = { + // Reversed so the first of two same-named parameters wins; the + // duplicate itself is one of `AnimationStateMachine.problems`. + for (final (i, p) in parameters.indexed.toList().reversed) p.name: i, + }; + + final List parameters; + final Map _index; + + /// Where [name] sits in [parameters], or -1. + int indexOf(String name) => _index[name] ?? -1; + + /// The parameter called [name], or null. + AnimationParameter? operator [](String name) => switch (_index[name]) { + final i? => parameters[i], + null => null, + }; +} + +/// What a parameter write did: written, or refused and why. +/// +/// **A refusal is returned, not thrown.** A game writing a parameter every +/// step and an agent writing one over MCP both want to know what went wrong, +/// and neither wants its step unwound because a name was misspelt. +final class ParameterWrite { + const ParameterWrite._(this.refusal); + + static const ParameterWrite done = ParameterWrite._(null); + + /// What was asked, why it could not be, and what to do instead; null when + /// the value was written. + final String? refusal; + + bool get written => refusal == null; + + @override + String toString() => refusal ?? 'written'; +} + +/// The current values of a graph's parameters, held to its schema. +/// +/// **Every value is a double in one typed list**, booleans and triggers as +/// 0 and 1 and integers exactly. That keeps the state a graph carries to one +/// array a snapshot can copy and a digest can hash, with the type kept where +/// it is checked — at the write — rather than in the storage. +final class AnimationParameters { + AnimationParameters(this.schema) + : _values = Float64List.fromList([ + for (final p in schema.parameters) p.initial, + ]); + + final AnimationParameterSchema schema; + final Float64List _values; + + /// Every value, index-aligned with [AnimationParameterSchema.parameters]. + List get values => _values.asUnmodifiableView(); + + /// The value at [index], for a caller that resolved the name once. + double valueAt(int index) => _values[index]; + + ParameterWrite setFloat(String name, double value) => value.isFinite + ? _write(name, AnimationParameterType.float, 'setFloat', value) + : ParameterWrite._( + 'Cannot set `$name` to $value: a parameter holds a finite number, ' + 'and every comparison against NaN or infinity answers the same ' + 'thing whatever the threshold. Clamp it first.', + ); + + ParameterWrite setInteger(String name, int value) => _write( + name, + AnimationParameterType.integer, + 'setInteger', + value.toDouble(), + ); + + ParameterWrite setBool(String name, bool value) => _write( + name, + AnimationParameterType.boolean, + 'setBool', + value ? 1.0 : 0.0, + ); + + /// Sets the trigger [name]; it stays set until a transition consumes it. + ParameterWrite fire(String name) => + _write(name, AnimationParameterType.trigger, 'fire', 1.0); + + /// Clears the trigger [name] without anything having used it. + ParameterWrite resetTrigger(String name) => + _write(name, AnimationParameterType.trigger, 'resetTrigger', 0.0); + + /// Clears the trigger at [index] — what a transition does to the triggers + /// it fired on. + void consumeTriggerAt(int index) => _values[index] = 0.0; + + /// Every parameter back to its schema's initial value. + void reset() { + for (final (i, p) in schema.parameters.indexed) { + _values[i] = p.initial; + } + } + + ParameterWrite _write( + String name, + AnimationParameterType type, + String call, + double value, + ) { + final index = schema.indexOf(name); + if (index < 0) { + final known = schema.parameters.map((p) => '`${p.name}`').join(', '); + return ParameterWrite._( + 'No parameter `$name` in this graph. ' + '${known.isEmpty ? 'It has none.' : 'It has $known.'}', + ); + } + final actual = schema.parameters[index].type; + if (actual != type) { + return ParameterWrite._( + '`$name` is a ${actual.name} parameter, and $call writes a ' + '${type.name}. Call ${actual.writer} instead.', + ); + } + _values[index] = value; + return ParameterWrite.done; + } +} diff --git a/packages/flutter3d_core/lib/src/engine/animation/graph/animation_state_machine.dart b/packages/flutter3d_core/lib/src/engine/animation/graph/animation_state_machine.dart new file mode 100644 index 000000000..2af421196 --- /dev/null +++ b/packages/flutter3d_core/lib/src/engine/animation/graph/animation_state_machine.dart @@ -0,0 +1,281 @@ +import 'package:flutter3d_core/formats.dart'; + +import '../animation_target.dart'; +import 'animation_parameters.dart'; + +/// How a [CompareCondition] measures a number parameter against its value. +/// +/// **Constants of a class rather than an enum**, for the reason +/// [AnimationParameterType] is: "at least" and "at most" are the obvious +/// next two, and adding them should not break anybody's `switch`. +final class AnimationComparison { + const AnimationComparison._(this.name, this._test, {this.exact = false}); + + final String name; + final bool Function(double actual, num value) _test; + + /// Whether this asks for exact equality — which a float parameter reached + /// by arithmetic almost never meets, and so is refused on one. + final bool exact; + + static const AnimationComparison greater = AnimationComparison._( + 'greater', + _greater, + ); + static const AnimationComparison less = AnimationComparison._('less', _less); + static const AnimationComparison equals = AnimationComparison._( + 'equals', + _equals, + exact: true, + ); + static const AnimationComparison notEquals = AnimationComparison._( + 'notEquals', + _notEquals, + exact: true, + ); + + bool test(double actual, num value) => _test(actual, value); + + static bool _greater(double actual, num value) => actual > value; + static bool _less(double actual, num value) => actual < value; + static bool _equals(double actual, num value) => actual == value; + static bool _notEquals(double actual, num value) => actual != value; + + @override + String toString() => name; +} + +/// One test a transition makes against the graph's parameters. +/// +/// **Named by parameter, resolved once.** A condition is data — the shape a +/// `.f3d` will store and an MCP tool will build — so it names its parameter; +/// `AnimationGraph` resolves the name to an index when it is built, and a +/// name the schema lacks is one of [AnimationStateMachine.problems] rather +/// than a condition that is silently never true. +sealed class AnimationCondition { + const AnimationCondition(this.parameter); + + final String parameter; +} + +/// A float or integer parameter against a number. +final class CompareCondition extends AnimationCondition { + const CompareCondition(super.parameter, this.comparison, this.value); + + final AnimationComparison comparison; + final num value; + + bool holds(double actual) => comparison.test(actual, value); +} + +/// A boolean parameter being [value]. +final class BoolCondition extends AnimationCondition { + const BoolCondition(super.parameter, {this.value = true}); + + final bool value; +} + +/// A trigger parameter being set — and consumed when the transition fires. +final class TriggerCondition extends AnimationCondition { + const TriggerCondition(super.parameter); +} + +/// A state: which clip plays while the graph is in it, and how. +final class AnimationState { + const AnimationState({ + required this.name, + required this.clip, + this.speed = 1.0, + this.wrap = AnimationWrap.loop, + }); + + final String name; + + /// The [AnimationClip.name] this state plays. + /// + /// A name rather than an index into the clip list, because the list is + /// whatever order a file's exporter wrote; a re-export that sorts the clips + /// must not move a character from idling to dying. + final String clip; + + /// Playback rate, zero or more. Zero holds the first frame. + final double speed; + + final AnimationWrap wrap; +} + +/// A way out of one state into another. +/// +/// **Fires when every condition holds and the exit time, if any, has +/// passed.** The conditions are a conjunction; an "or" is two transitions, +/// which is also how it reads in a graph editor. +final class AnimationTransition { + AnimationTransition({ + required this.from, + required this.to, + List conditions = const [], + this.duration = 0.0, + this.priority = 0, + this.exitTime, + }) : conditions = List.unmodifiable(conditions); + + /// The state this transition leaves, by name. + final String from; + + /// The state it enters, by name. + final String to; + + final List conditions; + + /// The crossfade, in seconds; zero cuts. + final double duration; + + /// Which of several transitions that could fire on the same step does. + /// + /// Higher first, and declaration order between equals — so the answer to + /// "hit and jump on the same step" is written down rather than left to + /// whichever was added first by accident. + final int priority; + + /// How far through [from]'s clip the graph must be, as a fraction of the + /// clip's length, before this transition may fire; null for no such wait. + /// + /// **Counted from entering the state, not from the last loop**: 0.8 on a + /// looping clip is true from 80% of the first cycle on. 1.0 on a `once` + /// clip is "when it has finished". Combined with conditions as an "and", + /// so an attack that may be cancelled only late in its swing is an exit + /// time and a trigger on one transition. + final double? exitTime; +} + +/// The states, transitions and parameters of one animation graph — what a +/// file stores and an editor edits. `AnimationGraph` is what runs it. +/// +/// **The definition is checked as a whole, and asked rather than trusted.** +/// [problems] lists everything wrong with it at once, in words a person or +/// an agent can act on; `AnimationGraph` refuses to be built over a +/// definition that has any. +final class AnimationStateMachine { + AnimationStateMachine({ + required this.parameters, + required List states, + required List transitions, + String? entry, + }) : states = List.unmodifiable(states), + transitions = List.unmodifiable(transitions), + entry = entry ?? (states.isEmpty ? '' : states.first.name); + + final AnimationParameterSchema parameters; + final List states; + final List transitions; + + /// The state the graph starts in: the first one unless named. + final String entry; + + /// Where the state called [name] sits in [states], or -1. + int indexOfState(String name) => states.indexWhere((s) => s.name == name); + + /// Everything that would stop this definition running over [clips]; empty + /// when it can. + List problems(List clips) { + final clipNames = {for (final clip in clips) ?clip.name}; + final stateNames = {for (final s in states) s.name}; + String? duplicate(Iterable names, String what) { + final seen = {}; + final twice = names.where((n) => !seen.add(n)).toSet(); + return twice.isEmpty + ? null + : 'More than one $what is called ${_listed(twice, '')}; names are ' + 'how everything else refers to them, so each must be unique.'; + } + + return [ + ?duplicate(parameters.parameters.map((p) => p.name), 'parameter'), + ?duplicate(states.map((s) => s.name), 'state'), + if (states.isEmpty) + 'There are no states; a graph has to be in one. Add at least one.', + if (states.isNotEmpty && !stateNames.contains(entry)) + 'The entry state `$entry` is not one of the states.', + for (final s in states) ...[ + ?_if( + !clipNames.contains(s.clip), + 'State `${s.name}` plays clip `${s.clip}`, which is not among ' + '${_listed(clipNames, 'the clips (none is named)')}.', + ), + ?_if( + !s.speed.isFinite || s.speed < 0.0, + 'State `${s.name}` has speed ${s.speed}; it must be finite and not ' + 'negative, or its exit times could never be reached.', + ), + ], + for (final t in transitions) ..._transitionProblems(t, stateNames), + ]; + } + + /// [message] when [failed]; a collection `if` would do, but would hold a + /// message split over lines as adjacent strings in the list itself. + static String? _if(bool failed, String message) => failed ? message : null; + + static String _listed(Iterable names, String whenNone) => + names.isEmpty ? whenNone : names.map((n) => '`$n`').join(', '); + + List _transitionProblems( + AnimationTransition t, + Set stateNames, + ) { + final label = '`${t.from}` → `${t.to}`'; + return [ + if (!stateNames.contains(t.from)) + 'Transition $label leaves `${t.from}`, which is not a state.', + if (!stateNames.contains(t.to)) + 'Transition $label enters `${t.to}`, which is not a state.', + ?_if( + !t.duration.isFinite || t.duration < 0.0, + 'Transition $label fades over ${t.duration} s; use zero to cut or a ' + 'positive number of seconds.', + ), + if (t.exitTime case final exit? when !exit.isFinite || exit < 0.0) + 'Transition $label has exit time $exit; it must be finite, from 0 up.', + ?_if( + t.conditions.isEmpty && t.exitTime == null, + 'Transition $label has neither a condition nor an exit time, so it ' + 'would fire on the first step in `${t.from}` and the state would ' + 'never play. Give it one or the other.', + ), + for (final c in t.conditions) ?_conditionProblem(c, label), + ]; + } + + String? _conditionProblem(AnimationCondition condition, String label) { + final parameter = parameters[condition.parameter]; + if (parameter == null) { + return 'Transition $label tests `${condition.parameter}`, which is not ' + 'a parameter.'; + } + final type = parameter.type; + final wrong = 'Transition $label tests ${type.name} `${parameter.name}`'; + final instead = 'Use a ${_conditionFor(type)}.'; + final isNumber = + type == AnimationParameterType.float || + type == AnimationParameterType.integer; + return switch (condition) { + CompareCondition(:final comparison) + when comparison.exact && type == AnimationParameterType.float => + '$wrong for exact equality, which a float reached by arithmetic ' + 'almost never meets. Compare with greater or less.', + CompareCondition() when !isNumber => '$wrong against a number. $instead', + BoolCondition() when type != AnimationParameterType.boolean => + '$wrong as a boolean. $instead', + TriggerCondition() when type != AnimationParameterType.trigger => + '$wrong as a trigger. $instead', + _ => null, + }; + } + + static String _conditionFor(AnimationParameterType type) => + type == AnimationParameterType.boolean + ? 'BoolCondition' + : type == AnimationParameterType.trigger + ? 'TriggerCondition' + : 'CompareCondition'; +} diff --git a/packages/flutter3d_core/lib/src/engine/animation/pose.dart b/packages/flutter3d_core/lib/src/engine/animation/pose.dart index bb6d03b6e..12f2daa65 100644 --- a/packages/flutter3d_core/lib/src/engine/animation/pose.dart +++ b/packages/flutter3d_core/lib/src/engine/animation/pose.dart @@ -4,6 +4,7 @@ import 'package:flutter3d_core/formats.dart'; import 'package:vector_math/vector_math.dart'; import '../scene/skeleton.dart'; +import 'animation_layer.dart'; import 'animation_target.dart'; /// A flat, scene-graph-free pose: one hierarchy's local TRS, sampled from a @@ -124,6 +125,63 @@ final class Pose { scales.setAll(0, _restScales); } + /// A second pose over the same hierarchy and rest, at rest. + /// + /// What a crossfade needs: somewhere to sample the clip being left while + /// this pose holds the one being entered. The rest arrays are shared, not + /// copied — nothing writes to them after construction. + Pose restCopy() => Pose( + parents: parents, + restTranslations: _restTranslations, + restRotations: _restRotations, + restScales: _restScales, + ); + + /// Mixes [from] into this pose by [weight]: 0 leaves [from], 1 leaves this + /// pose as it is. + /// + /// **The player's crossfade, on whole poses.** Rotation goes through + /// [shortestArcSlerp] and translation and scale through a straight line — + /// `AnimationPlayer`'s own `_blendInto`, with the same argument order, so a + /// graph fading between two clips and a player fading between the same two + /// agree joint for joint. + void blendFrom(Pose from, double weight) { + if (from.nodeCount != nodeCount) { + throw ArgumentError( + 'Cannot blend a pose of ${from.nodeCount} nodes into one of ' + '$nodeCount.', + ); + } + for (var i = 0; i < translations.length; i++) { + final t = from.translations[i]; + final s = from.scales[i]; + translations[i] = t + (translations[i] - t) * weight; + scales[i] = s + (scales[i] - s) * weight; + } + for (var node = 0; node < nodeCount; node++) { + final at = node * 4; + final mixed = shortestArcSlerp( + Quaternion( + from.rotations[at], + from.rotations[at + 1], + from.rotations[at + 2], + from.rotations[at + 3], + ), + Quaternion( + rotations[at], + rotations[at + 1], + rotations[at + 2], + rotations[at + 3], + ), + weight, + ); + rotations[at] = mixed.x; + rotations[at + 1] = mixed.y; + rotations[at + 2] = mixed.z; + rotations[at + 3] = mixed.w; + } + } + final Float32List _sample3 = Float32List(3); final Float32List _sample4 = Float32List(4); diff --git a/packages/flutter3d_core/test/animation_graph_test.dart b/packages/flutter3d_core/test/animation_graph_test.dart new file mode 100644 index 000000000..c4b92c330 --- /dev/null +++ b/packages/flutter3d_core/test/animation_graph_test.dart @@ -0,0 +1,435 @@ +/// The animation graph takes the transitions its parameters say, once, and +/// the same ones on every run — N1. +/// +/// dart test test/animation_graph_test.dart +/// +/// Every step here is 1/64 s and every fade a power of two, so the weights the +/// tests name are exact rather than close. +library; + +import 'dart:math' as math; +import 'dart:typed_data'; + +import 'package:flutter3d_core/flutter3d_core.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +const double _dt = 1.0 / 64.0; + +/// A one-node clip [length] seconds long holding translation x = [x] and, when +/// [yaw] is given, that rotation about Y. +AnimationClip _clip(String name, double x, {double length = 1.0, double? yaw}) { + final half = (yaw ?? 0.0) / 2.0; + final rotation = [0.0, math.sin(half), 0.0, math.cos(half)]; + return AnimationClip( + name: name, + tracks: [ + AnimationTrack( + nodeIndex: 0, + path: AnimationPath.translation, + interpolation: AnimationInterpolation.linear, + times: Float32List.fromList([0.0, length]), + values: Float32List.fromList([x, 0, 0, x, 0, 0]), + componentCount: 3, + ), + if (yaw != null) + AnimationTrack( + nodeIndex: 0, + path: AnimationPath.rotation, + interpolation: AnimationInterpolation.linear, + times: Float32List.fromList([0.0, length]), + values: Float32List.fromList([...rotation, ...rotation]), + componentCount: 4, + ), + ], + ); +} + +Pose _onePose() => Pose( + parents: const [-1], + restTranslations: Float32List(3), + restRotations: Float32List.fromList([0, 0, 0, 1]), + restScales: Float32List.fromList([1, 1, 1]), +); + +final List _clips = [ + // A rotation track at rest, rather than none: the player fades only a track + // both clips have, and the graph is compared with it below. + _clip('idle', 0.0, yaw: 0.0), + _clip('walk', 10.0, yaw: math.pi / 2.0), + _clip('attack', 20.0, length: 0.5), +]; + +AnimationStateMachine _machine({ + List transitions = const [], +}) => AnimationStateMachine( + parameters: AnimationParameterSchema(const [ + AnimationParameter.float('speed'), + AnimationParameter.boolean('grounded', initial: true), + AnimationParameter.integer('stance'), + AnimationParameter.trigger('attack'), + ]), + states: const [ + AnimationState(name: 'idle', clip: 'idle'), + AnimationState(name: 'walk', clip: 'walk'), + AnimationState(name: 'attack', clip: 'attack', wrap: AnimationWrap.once), + ], + transitions: transitions, +); + +AnimationGraph _graph(List transitions) => AnimationGraph( + machine: _machine(transitions: transitions), + clips: _clips, + pose: _onePose(), +); + +/// The yaw [pose]'s node 0 holds, in radians. +double _yaw(Pose pose) => + 2.0 * math.atan2(pose.rotations[1], pose.rotations[3]); + +void main() { + group('conditions', () { + // Mutation: drop the `checks.every(_holds)` test in `_takeTransition` and + // the graph walks at speed 0.4; flip `greater` to `less` in + // `CompareCondition.holds` and it never walks. + test('a transition waits for its condition and then fires', () { + final graph = _graph([ + AnimationTransition( + from: 'idle', + to: 'walk', + conditions: const [ + CompareCondition('speed', AnimationComparison.greater, 0.5), + ], + ), + ]); + + expect(graph.parameters.setFloat('speed', 0.4).written, isTrue); + for (var i = 0; i < 10; i++) { + graph.evaluate(_dt); + } + expect(graph.state, 'idle'); + expect(graph.pose.translations[0], 0.0); + + graph.parameters.setFloat('speed', 0.6); + final pose = graph.evaluate(_dt); + expect(graph.state, 'walk'); + expect(pose.translations[0], 10.0, reason: 'a zero fade cuts'); + }); + + // Mutation: compare a boolean against `== 0.0` rather than `!= 0.0` and + // the bool transition fires while grounded; read `int` through a float + // comparison only and `equals` on an integer never matches. + test('bool and integer conditions all have to hold', () { + final graph = _graph([ + AnimationTransition( + from: 'idle', + to: 'walk', + conditions: const [ + BoolCondition('grounded', value: false), + CompareCondition('stance', AnimationComparison.equals, 2), + ], + ), + ]); + + graph.parameters.setInteger('stance', 2); + graph.evaluate(_dt); + expect(graph.state, 'idle', reason: 'still grounded'); + + graph.parameters.setBool('grounded', false); + graph.evaluate(_dt); + expect(graph.state, 'walk'); + }); + + // Mutation: sort by priority ascending, or drop the sort, and the + // lower-priority walk declared first wins. + test('of two transitions ready on one step, the higher priority wins', () { + final graph = _graph([ + AnimationTransition( + from: 'idle', + to: 'walk', + conditions: const [ + CompareCondition('speed', AnimationComparison.greater, 0.5), + ], + ), + AnimationTransition( + from: 'idle', + to: 'attack', + priority: 1, + conditions: const [TriggerCondition('attack')], + ), + ]); + + graph.parameters + ..setFloat('speed', 1.0) + ..fire('attack'); + graph.evaluate(_dt); + expect(graph.state, 'attack'); + }); + }); + + group('triggers and exit time', () { + List attackAndBack() => [ + AnimationTransition( + from: 'idle', + to: 'attack', + conditions: const [TriggerCondition('attack')], + ), + AnimationTransition(from: 'attack', to: 'idle', exitTime: 1.0), + ]; + + // Mutation: drop `consumeTriggerAt` from `_takeTransition` and the graph + // goes straight back into the attack the moment it returns to idle. + test('a trigger fires its transition once and is consumed', () { + final graph = _graph(attackAndBack()); + final attack = graph.machine.parameters.indexOf('attack'); + + graph.parameters.fire('attack'); + graph.evaluate(_dt); + expect(graph.state, 'attack'); + expect(graph.parameters.valueAt(attack), 0.0); + + final visited = [ + for (var i = 0; i < 200; i++) (graph..evaluate(_dt)).state, + ]; + expect(visited.where((s) => s == 'attack'), hasLength(31)); + expect(visited.skip(31), everyElement('idle')); + }); + + // Mutation: test the exit time against the wrapped playhead rather than + // the time in the state, or drop the test, and the attack leaves early. + test('an exit time holds the state until that much of its clip', () { + final graph = _graph(attackAndBack()); + graph.parameters.fire('attack'); + graph.evaluate(_dt); + + // The attack clip is half a second, 32 steps; it is entered with its + // playhead at zero and leaves on the step that reaches the end. + for (var i = 0; i < 31; i++) { + graph.evaluate(_dt); + } + expect(graph.state, 'attack'); + expect(graph.normalizedTime, closeTo(31 / 32, 1e-12)); + graph.evaluate(_dt); + expect(graph.state, 'idle'); + }); + + // Mutation: clear every trigger at the end of `evaluate` and the press + // made during the fade is lost. + test('a trigger set while a transition cannot fire waits for it', () { + final graph = _graph([ + AnimationTransition( + from: 'idle', + to: 'walk', + duration: 0.25, + conditions: const [ + CompareCondition('speed', AnimationComparison.greater, 0.5), + ], + ), + AnimationTransition( + from: 'walk', + to: 'attack', + conditions: const [TriggerCondition('attack')], + ), + ]); + graph.parameters.setFloat('speed', 1.0); + graph.evaluate(_dt); + graph.parameters.fire('attack'); + + for (var i = 0; i < 15; i++) { + graph.evaluate(_dt); + } + expect(graph.state, 'walk', reason: 'no transition during a fade'); + graph.evaluate(_dt); + expect(graph.state, 'attack'); + }); + }); + + group('crossfade', () { + AnimationGraph fading() => _graph([ + AnimationTransition( + from: 'idle', + to: 'walk', + duration: 0.25, + conditions: const [ + CompareCondition('speed', AnimationComparison.greater, 0.5), + ], + ), + ])..parameters.setFloat('speed', 1.0); + + // Mutation: blend rotation by lerping the four components and + // normalising, and a quarter of the way through reads 21.6° rather than + // 22.5° — the two agree only at a half, which is why this checks a quarter. + test('a quarter of the way through, the pose is a quarter of the way', () { + final graph = fading(); + graph.evaluate(_dt); + expect(graph.fadingFrom, 'idle'); + expect(graph.fadeWeight, 0.0); + expect(graph.pose.translations[0], 0.0, reason: 'weight 0 is the source'); + + for (var i = 0; i < 4; i++) { + graph.evaluate(_dt); + } + expect(graph.fadeWeight, 0.25); + expect(graph.pose.translations[0], closeTo(2.5, 1e-6)); + expect(_yaw(graph.pose), closeTo(math.pi / 8.0, 1e-6)); + + for (var i = 0; i < 4; i++) { + graph.evaluate(_dt); + } + expect(graph.pose.translations[0], closeTo(5.0, 1e-6)); + expect(_yaw(graph.pose), closeTo(math.pi / 4.0, 1e-6)); + }); + + // Mutation: end the fade one step late, or leave `_previous` set, and the + // pose stops short of the walk. + test('when the fade is over the pose is the target clip alone', () { + final graph = fading(); + for (var i = 0; i < 17; i++) { + graph.evaluate(_dt); + } + expect(graph.fadingFrom, isNull); + expect(graph.fadeWeight, 1.0); + expect(graph.pose.translations[0], 10.0); + expect(_yaw(graph.pose), closeTo(math.pi / 2.0, 1e-6)); + }); + + // Mutation: swap the arguments of `shortestArcSlerp` in `Pose.blendFrom` + // and the graph's half-faded pose stops matching the player's. + test('the graph fades as the player does', () { + final graph = fading(); + final node = _Node(); + final player = AnimationPlayer( + clips: _clips, + targets: [node], + )..play(0); + + graph.evaluate(_dt); + player.crossFadeTo(1, duration: 0.25); + for (var i = 0; i < 6; i++) { + graph.evaluate(_dt); + player + ..update(_dt) + ..apply(); + } + // Close rather than equal only because a pose holds 32-bit floats and + // the player hands its targets doubles. + expect(player.fadeWeight, graph.fadeWeight); + expect(node.x, closeTo(graph.pose.translations[0], 1e-6)); + for (var i = 0; i < 4; i++) { + expect(node.rotation[i], closeTo(graph.pose.rotations[i], 1e-6)); + } + }); + }); + + // Mutation: advance by a wall clock, or iterate the outgoing transitions + // out of a hash set, and two runs over the same script part ways. + test('the same parameters on the same steps give the same run', () { + List run() { + final graph = _graph([ + AnimationTransition( + from: 'idle', + to: 'walk', + duration: 0.125, + conditions: const [ + CompareCondition('speed', AnimationComparison.greater, 0.5), + ], + ), + AnimationTransition( + from: 'walk', + to: 'attack', + duration: 0.0625, + conditions: const [TriggerCondition('attack')], + ), + AnimationTransition( + from: 'attack', + to: 'idle', + duration: 0.25, + exitTime: 1.0, + ), + ]); + final trace = []; + for (var step = 0; step < 600; step++) { + graph.parameters.setFloat('speed', (step % 90) / 60.0); + if (step % 47 == 0) graph.parameters.fire('attack'); + final pose = graph.evaluate(_dt); + trace + ..add(graph.state) + ..add(graph.fadeWeight) + ..addAll(pose.translations) + ..addAll(pose.rotations); + } + return trace; + } + + final first = run(); + expect( + first.toSet().containsAll(['idle', 'walk', 'attack']), + isTrue, + ); + expect(run(), first); + }); + + group('refusals', () { + // Mutation: drop the type check in `_write` and a bool lands in a float. + test('a write of the wrong type is refused, naming the right call', () { + final graph = _graph(const []); + final refused = graph.parameters.setBool('speed', true); + expect(refused.written, isFalse); + expect(refused.refusal, contains('setFloat')); + expect( + graph.parameters.setFloat('sped', 1.0).refusal, + contains('`speed`'), + ); + expect(graph.parameters.setFloat('speed', double.nan).written, isFalse); + expect(graph.parameters.values, [0.0, 1.0, 0.0, 0.0]); + }); + + // Mutation: drop `_conditionProblem` and a trigger tested as a bool + // builds a graph whose transition is never taken. + test('a definition that cannot run says why and is not built', () { + final machine = _machine( + transitions: [ + AnimationTransition( + from: 'idle', + to: 'walk', + conditions: const [BoolCondition('attack')], + ), + AnimationTransition( + from: 'idle', + to: 'run', + conditions: const [ + CompareCondition('speed', AnimationComparison.equals, 1.0), + ], + ), + AnimationTransition(from: 'walk', to: 'idle'), + ], + ); + final problems = machine.problems(_clips.take(2).toList()); + expect(problems, hasLength(5)); + expect(problems, contains(contains('TriggerCondition'))); + expect(problems, contains(contains('`run`'))); + expect(problems, contains(contains('exact equality'))); + expect(problems, contains(contains('neither a condition'))); + expect(problems, contains(contains('`attack`, which is not among'))); + expect( + () => AnimationGraph(machine: machine, clips: _clips, pose: _onePose()), + throwsArgumentError, + ); + }); + }); +} + +final class _Node implements AnimationTarget { + double x = 0.0; + List rotation = const [0, 0, 0, 1]; + + @override + void setPosition(double x, double y, double z) => this.x = x; + + @override + void setRotation(Quaternion value) => + rotation = [value.x, value.y, value.z, value.w]; + + @override + void setScale(double x, double y, double z) {} +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index a652af21b..35a5000d4 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11212 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11224 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 96fee1b60..546f85625 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11212 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11224 tests need a GPU. --- # Testing -11212 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11224 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -12,7 +12,7 @@ description: Four independent golden sets, mutation-checking every new test, det | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | | `flutter3d_sim` | 681 | | `pad_input` | 67 | -| `flutter3d_lab` | 15 | | `flutter3d_core` | 750 | +| `flutter3d_lab` | 15 | | `flutter3d_core` | 762 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | | `flutter3d_game_shooter` | 340 | | `flutter3d_audio_core` | 55 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11186 rather than 11212: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11198 rather than 11224: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. From 6f7bc70eaf54fba05067d54eade332deea3c33c4 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 12:51:44 +0300 Subject: [PATCH 051/141] A level can be shared behind a short code ShareBundle in flutter3d_sim is a level document, its hash and optionally a run through it, refused when the run was recorded in another version of the level. RunService speaks the v1/shares routes over a transport the game hands in. cloud/server keeps the shares beside the telemetry and in the same shape: a pure service, a store in memory and one in Postgres, routes under /api/v1/shares and /api/v1/moderation. A bundle is filed by the SHA-256 of its bytes behind a Crockford code derived from it. Moderation starts in review, where nothing opens until a moderator publishes it, or open, where enough reports send a level back to review. With review and no moderator token the server refuses shares rather than keep what nobody can publish. --- ARCHITECTURE.md | 2 +- README.md | 2 +- .../Flutter/GeneratedPluginRegistrant.swift | 2 - cloud/README.md | 43 ++ cloud/server/lib/main.server.dart | 6 +- cloud/server/lib/src/config.dart | 49 ++ cloud/server/lib/src/db/migrations.g.dart | 40 ++ .../lib/src/db/migrations/005_shares.sql | 38 ++ .../server/lib/src/db/shares_repository.dart | 189 +++++++ cloud/server/lib/src/http/app.dart | 5 + cloud/server/lib/src/http/share_routes.dart | 73 +++ cloud/server/lib/src/services.dart | 15 + .../server/lib/src/shares/share_service.dart | 313 +++++++++++ cloud/server/lib/src/shares/share_store.dart | 238 ++++++++ cloud/server/lib/src/shares/short_code.dart | 51 ++ cloud/server/test/share_db_test.dart | 131 +++++ cloud/server/test/share_routes_test.dart | 522 ++++++++++++++++++ packages/flutter3d_sim/CHANGELOG.md | 7 + packages/flutter3d_sim/lib/flutter3d_sim.dart | 2 + .../lib/src/share/run_service.dart | 245 ++++++++ .../lib/src/share/share_bundle.dart | 128 +++++ .../flutter3d_sim/test/run_service_test.dart | 209 +++++++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- tasks/0.9-engine-roadmap.md | 21 + 25 files changed, 2331 insertions(+), 10 deletions(-) create mode 100644 cloud/server/lib/src/db/migrations/005_shares.sql create mode 100644 cloud/server/lib/src/db/shares_repository.dart create mode 100644 cloud/server/lib/src/http/share_routes.dart create mode 100644 cloud/server/lib/src/shares/share_service.dart create mode 100644 cloud/server/lib/src/shares/share_store.dart create mode 100644 cloud/server/lib/src/shares/short_code.dart create mode 100644 cloud/server/test/share_db_test.dart create mode 100644 cloud/server/test/share_routes_test.dart create mode 100644 packages/flutter3d_sim/lib/src/share/run_service.dart create mode 100644 packages/flutter3d_sim/lib/src/share/share_bundle.dart create mode 100644 packages/flutter3d_sim/test/run_service_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f0dea4f57..0fc449395 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11224 tests** across 43 packages and 10 applications | +| Unit tests | **11234 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index d9d1706f1..03b7026dc 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11224 tests across forty-three packages and nine applications. The +There are 11234 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 86 scenes checkable in a headless run. diff --git a/apps/flutter3d_demo_strategy/macos/Flutter/GeneratedPluginRegistrant.swift b/apps/flutter3d_demo_strategy/macos/Flutter/GeneratedPluginRegistrant.swift index 4c6c08a37..859b13e8e 100644 --- a/apps/flutter3d_demo_strategy/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/apps/flutter3d_demo_strategy/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,12 +5,10 @@ import FlutterMacOS import Foundation -import flutter_soloud import pad_input import pointer_lock func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - FlutterSoloudPlugin.register(with: registry.registrar(forPlugin: "FlutterSoloudPlugin")) GamepadPlugin.register(with: registry.registrar(forPlugin: "GamepadPlugin")) PointerLockPlugin.register(with: registry.registrar(forPlugin: "PointerLockPlugin")) } diff --git a/cloud/README.md b/cloud/README.md index b357e8859..098fbbbff 100644 --- a/cloud/README.md +++ b/cloud/README.md @@ -23,6 +23,11 @@ keep are counts of packages. | `docker-compose.yml` | Postgres for development and the integration test | | `monitoring/` | Prometheus and Grafana for the service's own numbers (accounts, models, disk). [README](monitoring/README.md) | +Two more services live beside it, each with its own README: `lessons/` +(a lesson page and its embed) and `lti/` (LTI 1.3 launch and grade passback). +Shared levels and opt-in telemetry are part of this server; see +[Sharing](#sharing) and [Telemetry](#telemetry). + ## Running it ```bash @@ -61,6 +66,9 @@ names all of them. | `MODELS_VIEWER_DIR` | Optional; serves the viewer at `/app/` when set | | `MODELS_LEARN_DIR` | Default `content/learn/modeler`, which is right in a checkout and nowhere else. A directory with no case in it gives an empty tutorial and one line in the log; the start does not fail | | `MODELS_TELEMETRY_LEVELS_DIR` | Optional; the levels telemetry runs are played again in. See [Telemetry](#telemetry) | +| `MODELS_SHARES_MODERATION` | `review` (default) or `open`. See [Sharing](#sharing) | +| `MODELS_SHARES_MODERATOR_TOKEN` | Optional, 24 characters or more; without it moderation is off | +| `MODELS_SHARES_REPORTS_TO_HIDE` | How many reports send a published level back to review. Default 3 | ## Tests @@ -94,6 +102,41 @@ The games are `HeadlessGame`s handed to `Services(telemetryGames:)` in code. Flutter and this process runs under `dart run`, so as shipped the endpoint answers 503 and says why. +## Sharing + +The reference server for shared levels (N10 in `tasks/0.9-engine-roadmap.md`). +A player shares a level, or a level with a run through it, and gets a short +code back. The client and the format are `RunService` and `ShareBundle` in +`flutter3d_sim`; a game points `RunService` at `https:///api/`. Every +answer is JSON, and every refusal is `{"message": …}`, which `RunService` hands +to the game as the reason. No account is involved. Under `/api/v1/`: + +| | | +|---|---| +| `POST shares` | A `ShareBundle`, at most 4 MB. A run recorded in another version of the level is refused. `201 {code, address, status}`, or `200` with the same code when these bytes were shared before | +| `GET shares/` | `200 {bundle}` when published, `202` while pending, `410` with the moderator's reason when removed, `404` | +| `POST shares//reports` | `{reason}`, answered `202 {message}` | +| `GET moderation/queue` | Token. Pending levels and anything reported | +| `GET moderation/shares/` | Token. One level with its record, whatever its status | +| `POST moderation/shares/` | Token, `{decision: publish \| remove, reason}`. A removal needs a reason; publishing again drops the reports | + +A level is filed under the SHA-256 of the bytes the server writes for it, not +the bytes it was sent, so the same level shared twice gets the same code. Those +bytes are kept as `text` in `shares`, since `jsonb` would not keep them. The +code is the first seven characters of the hash in Crockford's base 32, and gets +longer only if a different level already holds it. People can type it in lower +case, with dashes, and with O for 0 or L for 1. + +In `review` mode nothing opens until a moderator publishes it, so without a +token `POST shares` answers 503 instead of keeping levels nobody could ever +publish. In `open` mode a level opens at once, and +`MODELS_SHARES_REPORTS_TO_HIDE` reports send it back to review. The moderation +routes take `Authorization: Bearer ` and answer +503 when no token is set. + +Not deployed: the production environment sets none of these, so models.pleion.dev +shares nothing yet. + ## Deploying ```bash diff --git a/cloud/server/lib/main.server.dart b/cloud/server/lib/main.server.dart index ff4b33840..f283f75ab 100644 --- a/cloud/server/lib/main.server.dart +++ b/cloud/server/lib/main.server.dart @@ -86,9 +86,13 @@ Future main(List arguments) async { } }); + final sharing = services.shares.takesShares + ? 'sharing in ${config.shareModeration.name} mode' + : 'sharing off: review mode with no moderator token'; stdout.writeln( 'models on http://${server.address.host}:${server.port} ' - '(${config.sendsMail ? 'sending mail' : 'printing mail to the log'})', + '(${config.sendsMail ? 'sending mail' : 'printing mail to the log'}; ' + '$sharing)', ); // systemd stops a unit with SIGTERM; answering it lets requests in flight diff --git a/cloud/server/lib/src/config.dart b/cloud/server/lib/src/config.dart index cd74e37a9..0f869ec16 100644 --- a/cloud/server/lib/src/config.dart +++ b/cloud/server/lib/src/config.dart @@ -9,6 +9,17 @@ library; import 'dart:io'; +/// N10: what happens to a level the moment somebody shares it. +enum ShareModeration { + /// Kept as pending until a moderator publishes it. The default: a server + /// somebody stands up and forgets about should not be an open file host on + /// the day it starts. + review, + + /// Published at once; reports send it back to review. + open, +} + /// Where the service listens, what it calls itself, and the secrets it holds. class Config { const Config({ @@ -24,6 +35,9 @@ class Config { this.viewerDirectory, this.learnDirectory = 'content/learn/modeler', this.telemetryLevelsDirectory, + this.shareModeration = ShareModeration.review, + this.shareModeratorToken, + this.shareReportsToHide = 3, }); /// Reads the configuration from the process environment. @@ -47,6 +61,26 @@ class Config { // development wants and what a test can read. final resendApiKey = env['MODELS_RESEND_API_KEY']; + // N10. Absent is a setting too — sharing with nobody to moderate it — but + // present and wrong is a deploy that believes it set something it did not, + // so those are named with the missing ones and stop the start. + final moderationName = + env['MODELS_SHARES_MODERATION'] ?? ShareModeration.review.name; + final moderation = ShareModeration.values.asNameMap()[moderationName]; + if (moderation == null) { + missing.add('MODELS_SHARES_MODERATION (review or open)'); + } + final rawToken = env['MODELS_SHARES_MODERATOR_TOKEN'] ?? ''; + final moderatorToken = rawToken.isEmpty ? null : rawToken; + if (moderatorToken != null && moderatorToken.length < 24) { + missing.add('MODELS_SHARES_MODERATOR_TOKEN (24 characters or more)'); + } + final rawReports = env['MODELS_SHARES_REPORTS_TO_HIDE'] ?? ''; + final reportsToHide = rawReports.isEmpty ? 3 : int.tryParse(rawReports); + if (reportsToHide == null || reportsToHide <= 0) { + missing.add('MODELS_SHARES_REPORTS_TO_HIDE (a positive number)'); + } + final config = Config( // 8794: on bob, 8790–8792 are the documentation site, tooth and its API, // and 8793 is the nginx that sits in front of this. @@ -65,6 +99,9 @@ class Config { viewerDirectory: env['MODELS_VIEWER_DIR'], learnDirectory: env['MODELS_LEARN_DIR'] ?? 'content/learn/modeler', telemetryLevelsDirectory: env['MODELS_TELEMETRY_LEVELS_DIR'], + shareModeration: moderation ?? ShareModeration.review, + shareModeratorToken: moderatorToken, + shareReportsToHide: reportsToHide ?? 3, ); if (missing.isNotEmpty) throw ConfigError(missing); @@ -127,6 +164,18 @@ class Config { /// models service have no game to play them with. final String? telemetryLevelsDirectory; + /// N10: whether a shared level waits for a moderator or opens at once. + final ShareModeration shareModeration; + + /// What `Authorization: Bearer …` has to say on `/api/v1/moderation/`, or + /// null for a server nobody moderates — whose moderation routes are off, + /// and which in [ShareModeration.review] takes no shares at all, since none + /// could ever open. + final String? shareModeratorToken; + + /// How many reports send a published level back to review. + final int shareReportsToHide; + /// Whether letters are actually sent. bool get sendsMail => resendApiKey != null; diff --git a/cloud/server/lib/src/db/migrations.g.dart b/cloud/server/lib/src/db/migrations.g.dart index cf6ce224a..054249988 100644 --- a/cloud/server/lib/src/db/migrations.g.dart +++ b/cloud/server/lib/src/db/migrations.g.dart @@ -239,5 +239,45 @@ create table telemetry_runs ( -- A heatmap reads one level's newest runs. create index telemetry_runs_by_level on telemetry_runs (level_hash, id desc); +'''), + Migration(5, '005_shares.sql', r''' +-- N10: levels shared behind short codes, and the reports against them. See +-- `shares/share_store.dart`. + +create table shares ( + -- Crockford base 32 of the address, seven characters unless a different + -- bundle already holds those; see `shares/short_code.dart`. + code text primary key check (code ~ '^[0-9A-HJKMNP-TV-Z]{7,51}$'), + -- The SHA-256 of `bundle`, hex. Unique, so a bundle shared twice is one row + -- however the two requests interleave. + address text not null unique check (address ~ '^[0-9a-f]{64}$'), + game text not null, + level_hash text not null, + title text, + has_run boolean not null, + -- **Text, not jsonb.** jsonb keeps neither key order nor the exact spelling + -- of a number, and the address is the hash of these bytes: a code has to + -- open exactly what its address names. + bundle text not null, + status text not null + check (status in ('published', 'pending', 'removed')), + -- Why a moderator decided as they did; what a removed code answers with. + note text, + created_at timestamptz not null default now() +); + +-- The moderation queue reads what still waits, oldest first. +create index shares_pending on shares (created_at) where status = 'pending'; + +-- Reports a moderator has not answered yet. Publishing a level again answers +-- them, and they are deleted then. +create table share_reports ( + id bigserial primary key, + code text not null references shares (code) on delete cascade, + reason text not null, + reported_at timestamptz not null default now() +); + +create index share_reports_by_code on share_reports (code, id); '''), ]; diff --git a/cloud/server/lib/src/db/migrations/005_shares.sql b/cloud/server/lib/src/db/migrations/005_shares.sql new file mode 100644 index 000000000..20b381cbb --- /dev/null +++ b/cloud/server/lib/src/db/migrations/005_shares.sql @@ -0,0 +1,38 @@ +-- N10: levels shared behind short codes, and the reports against them. See +-- `shares/share_store.dart`. + +create table shares ( + -- Crockford base 32 of the address, seven characters unless a different + -- bundle already holds those; see `shares/short_code.dart`. + code text primary key check (code ~ '^[0-9A-HJKMNP-TV-Z]{7,51}$'), + -- The SHA-256 of `bundle`, hex. Unique, so a bundle shared twice is one row + -- however the two requests interleave. + address text not null unique check (address ~ '^[0-9a-f]{64}$'), + game text not null, + level_hash text not null, + title text, + has_run boolean not null, + -- **Text, not jsonb.** jsonb keeps neither key order nor the exact spelling + -- of a number, and the address is the hash of these bytes: a code has to + -- open exactly what its address names. + bundle text not null, + status text not null + check (status in ('published', 'pending', 'removed')), + -- Why a moderator decided as they did; what a removed code answers with. + note text, + created_at timestamptz not null default now() +); + +-- The moderation queue reads what still waits, oldest first. +create index shares_pending on shares (created_at) where status = 'pending'; + +-- Reports a moderator has not answered yet. Publishing a level again answers +-- them, and they are deleted then. +create table share_reports ( + id bigserial primary key, + code text not null references shares (code) on delete cascade, + reason text not null, + reported_at timestamptz not null default now() +); + +create index share_reports_by_code on share_reports (code, id); diff --git a/cloud/server/lib/src/db/shares_repository.dart b/cloud/server/lib/src/db/shares_repository.dart new file mode 100644 index 000000000..f934e82eb --- /dev/null +++ b/cloud/server/lib/src/db/shares_repository.dart @@ -0,0 +1,189 @@ +/// Shared levels, as rows — `ShareStore` over Postgres. +library; + +import 'package:flutter3d_sim/flutter3d_sim.dart' show ShareStatus; +import 'package:postgres/postgres.dart'; + +import '../shares/share_store.dart'; +import '../shares/short_code.dart'; +import 'database.dart'; + +/// A record with its open reports, read in one query: the reports come back +/// as one `json` array per row rather than a second round trip a share. +const String _record = ''' + select s.code, s.address, s.game, s.level_hash, s.title, s.has_run, + s.created_at, s.status, s.note, + coalesce( + (select json_agg(json_build_object( + 'reason', r.reason, 'at', r.reported_at) order by r.id) + from share_reports r where r.code = s.code), + '[]'::json) + from shares s +'''; + +class SharesRepository implements ShareStore { + const SharesRepository(this._db); + + final Database _db; + + /// **One insert a candidate code, and the unique address decides the + /// race.** `on conflict do nothing` covers both constraints: when nothing + /// was inserted, either the same bundle is already filed — whoever filed it + /// — or a different one holds this code and the next, longer one is tried. + @override + Future<({ShareRecord record, bool created})> file(NewShare share) => _db.run(( + session, + ) async { + for (final code in codesOf(share.address)) { + final inserted = await session.execute( + Sql.named(''' + insert into shares + (code, address, game, level_hash, title, has_run, bundle, + status, created_at) + values + (@code, @address, @game, @levelHash, @title:text, @hasRun, + @bundle, @status, @createdAt) + on conflict do nothing + returning code + '''), + parameters: { + 'code': code, + 'address': share.address, + 'game': share.game, + 'levelHash': share.levelHash, + 'title': share.title, + 'hasRun': share.hasRun, + 'bundle': share.bundle, + 'status': share.status.name, + 'createdAt': share.createdAt.toUtc(), + }, + ); + if (inserted.isNotEmpty) { + return (record: (await _one(session, 's.code', code))!, created: true); + } + final known = await _one(session, 's.address', share.address); + if (known != null) return (record: known, created: false); + } + throw StateError('every code of ${share.address} is taken'); + }); + + @override + Future byCode(String code) => + _db.run((session) => _one(session, 's.code', code)); + + @override + Future bundleText(String code) => _db.run((session) async { + final rows = await session.execute( + Sql.named('select bundle from shares where code = @code'), + parameters: {'code': code}, + ); + return rows.isEmpty ? null : rows.first[0]! as String; + }); + + /// The row is locked first, so two reports at once count each other. + @override + Future report( + String code, + ShareReport report, { + required int reportsToHide, + }) => _db.transaction((session) async { + final locked = await session.execute( + Sql.named('select 1 from shares where code = @code for update'), + parameters: {'code': code}, + ); + if (locked.isEmpty) return null; + await session.execute( + Sql.named(''' + insert into share_reports (code, reason, reported_at) + values (@code, @reason, @at) + '''), + parameters: { + 'code': code, + 'reason': report.reason, + 'at': report.at.toUtc(), + }, + ); + await session.execute( + Sql.named(''' + update shares set status = 'pending' + where code = @code and status = 'published' + and (select count(*) from share_reports where code = @code) + >= @reportsToHide + '''), + parameters: {'code': code, 'reportsToHide': reportsToHide}, + ); + return _one(session, 's.code', code); + }); + + @override + Future decide(String code, ShareStatus status, String? note) => + _db.transaction((session) async { + final updated = await session.execute( + Sql.named(''' + update shares set status = @status, note = @note:text + where code = @code + returning code + '''), + parameters: {'code': code, 'status': status.name, 'note': note}, + ); + if (updated.isEmpty) return null; + if (status == ShareStatus.published) { + await session.execute( + Sql.named('delete from share_reports where code = @code'), + parameters: {'code': code}, + ); + } + return _one(session, 's.code', code); + }); + + @override + Future> queue({required int limit}) => + _db.run((session) async { + final rows = await session.execute( + Sql.named(''' + $_record + where s.status = 'pending' + or exists (select 1 from share_reports r where r.code = s.code) + order by s.created_at, s.code + limit @limit + '''), + parameters: {'limit': limit}, + ); + return [for (final row in rows) _recordOf(row)]; + }); + + /// The record whose [column] is [value]; the column is one of two names + /// written in this file, never anything a request supplied. + static Future _one( + Session session, + String column, + String value, + ) async { + final rows = await session.execute( + Sql.named('$_record where $column = @value'), + parameters: {'value': value}, + ); + return rows.isEmpty ? null : _recordOf(rows.first); + } + + static ShareRecord _recordOf(ResultRow row) => ShareRecord( + code: row[0]! as String, + address: row[1]! as String, + game: row[2]! as String, + levelHash: row[3]! as String, + title: row[4] as String?, + hasRun: row[5]! as bool, + createdAt: row[6]! as DateTime, + // The check constraint holds the column to the three names. + status: ShareStatus.named(row[7])!, + note: row[8] as String?, + reports: [ + for (final Map report + in (row[9]! as List).cast()) + ShareReport( + reason: report['reason']! as String, + at: DateTime.parse(report['at']! as String), + ), + ], + ); +} diff --git a/cloud/server/lib/src/http/app.dart b/cloud/server/lib/src/http/app.dart index ae763b5ec..ec8069ac1 100644 --- a/cloud/server/lib/src/http/app.dart +++ b/cloud/server/lib/src/http/app.dart @@ -37,6 +37,7 @@ import 'learn_routes.dart'; import 'metrics.dart'; import 'render.dart'; import 'request.dart'; +import 'share_routes.dart'; import 'static_files.dart'; import 'telemetry_routes.dart'; @@ -64,6 +65,10 @@ Handler buildHandler(Services services) { // N7: runs a player agreed to send, played again here; no session and no // cookie, so nothing for a forged form to ride on. ..mount('/api/telemetry/', telemetryRoutes(services.telemetry).call) + // N10: levels shared behind short codes, at the paths `RunService` asks + // for from a base of `/api/`. What it does not answer falls through to + // the routes below, `/api/v1/models` among them. + ..mount('/api/', shareRoutes(services.shares).call) // Nothing here checks who is asking. The endpoint is not linked from any // page and nginx keeps it off the public vhost (see // `cloud/monitoring/deploy/nginx-grafana.pleion.dev.conf` and diff --git a/cloud/server/lib/src/http/share_routes.dart b/cloud/server/lib/src/http/share_routes.dart new file mode 100644 index 000000000..20426bd68 --- /dev/null +++ b/cloud/server/lib/src/http/share_routes.dart @@ -0,0 +1,73 @@ +/// N10's routes, mounted under `/api/`: what `RunService` in `flutter3d_sim` +/// calls from a base of `https:///api/`, and the moderation routes for +/// whoever runs the server. +/// +/// **No session and no cookie, the same as telemetry.** A share is anonymous, +/// so there is no account for a forged form to ride on; the moderation routes +/// are a bearer token a tool sends, never a cookie a browser would send on its +/// own. +library; + +import 'dart:convert'; + +import 'package:shelf/shelf.dart'; +import 'package:shelf_router/shelf_router.dart'; + +import '../shares/share_service.dart'; +import 'request.dart'; + +/// The longest report or decision read: both are a sentence. +const int _smallBodyBytes = 4096; + +Response _json(ShareAnswer answer) => Response( + answer.status, + body: jsonEncode(answer.body), + headers: { + 'content-type': 'application/json; charset=utf-8', + 'cache-control': 'no-store', + }, +); + +/// The body as text, or null when it is over [limit] — counted as it arrives, +/// so a body that never says how long it is is still refused before it is +/// held whole. +Future _text(Request request, int limit) async { + final bytes = await readBody(request, limit: limit); + return bytes == null ? null : utf8.decode(bytes, allowMalformed: true); +} + +/// Unmatched paths fall through to the routes mounted after this, which is +/// why the router keeps shelf_router's own not-found: `/api/v1/models` is +/// under the same prefix. +Router shareRoutes(ShareService service) => Router() + ..post('/v1/shares', (Request request) async { + final body = await _text(request, service.maxBodyBytes); + if (body == null) return _json(tooLarge(service.maxBodyBytes)); + return _json(await service.share(body)); + }) + ..get( + '/v1/shares/', + (Request request, String code) async => _json(await service.open(code)), + ) + ..post('/v1/shares//reports', (Request request, String code) async { + final body = await _text(request, _smallBodyBytes); + if (body == null) return _json(tooLarge(_smallBodyBytes)); + return _json(await service.report(code, body)); + }) + ..get( + '/v1/moderation/queue', + (Request request) async => + _json(await service.queue(request.headers['authorization'])), + ) + ..get( + '/v1/moderation/shares/', + (Request request, String code) async => + _json(await service.inspect(request.headers['authorization'], code)), + ) + ..post('/v1/moderation/shares/', (Request request, String code) async { + final body = await _text(request, _smallBodyBytes); + if (body == null) return _json(tooLarge(_smallBodyBytes)); + return _json( + await service.decide(request.headers['authorization'], code, body), + ); + }); diff --git a/cloud/server/lib/src/services.dart b/cloud/server/lib/src/services.dart index 76b7732f6..84e6eed93 100644 --- a/cloud/server/lib/src/services.dart +++ b/cloud/server/lib/src/services.dart @@ -12,11 +12,13 @@ import 'db/models_repository.dart'; import 'db/projects_repository.dart'; import 'db/rate_limit.dart'; import 'db/sessions_repository.dart'; +import 'db/shares_repository.dart'; import 'db/telemetry_repository.dart'; import 'db/users_repository.dart'; import 'http/cookies.dart'; import 'http/gallery_catalogue.dart'; import 'mail/mailer.dart'; +import 'shares/share_service.dart'; import 'storage/blob_store.dart'; import 'telemetry/telemetry_service.dart'; @@ -35,6 +37,12 @@ class Services { levels: telemetryLevels, store: TelemetryRepository(db), ), + shares = ShareService( + store: SharesRepository(db), + moderation: config.shareModeration, + moderatorToken: config.shareModeratorToken, + reportsToHide: config.shareReportsToHide, + ), users = UsersRepository(db), sessions = SessionsRepository(db), tokens = EmailTokensRepository(db), @@ -84,6 +92,13 @@ class Services { /// built; the levels come from `MODELS_TELEMETRY_LEVELS_DIR`. final TelemetryService telemetry; + /// N10: levels shared behind short codes. + /// + /// **Refuses every share until somebody can moderate them**, unless the + /// deploy chose `MODELS_SHARES_MODERATION=open`: in review, a level nobody + /// could ever publish is a level kept for nothing. + final ShareService shares; + final UsersRepository users; final SessionsRepository sessions; final EmailTokensRepository tokens; diff --git a/cloud/server/lib/src/shares/share_service.dart b/cloud/server/lib/src/shares/share_service.dart new file mode 100644 index 000000000..296c2a3a5 --- /dev/null +++ b/cloud/server/lib/src/shares/share_service.dart @@ -0,0 +1,313 @@ +/// N10: levels shared behind short codes, and what a moderator and a report do +/// to them. +library; + +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart' + show ShareBundle, ShareFormatException, ShareStatus; + +import '../config.dart'; +import 'share_store.dart'; +import 'short_code.dart'; + +/// An answer: the status, and a JSON body. A refusal's body is +/// `{"message": …}` — the sentence `RunService` hands the player, saying what +/// was asked for, why it cannot be, and what to do instead. +typedef ShareAnswer = ({int status, Map body}); + +ShareAnswer _say(int status, String message) => + (status: status, body: {'message': message}); + +/// The longest title and reason kept. A title is a line under a code; a +/// reason is a sentence to a moderator. +const int _titleLimit = 80; +const int _reasonLimit = 500; + +final RegExp _gameName = RegExp(r'^[a-z0-9][a-z0-9_-]{0,39}$'); + +/// Files levels by their bytes, hands out codes, and answers them. +/// +/// **No account, on purpose, the same as telemetry.** The person pressing +/// Share is the consent, the code is the only handle, and what is shared is a +/// level and a tape — nothing that names who made it unless they typed it +/// into the title. Tying a share to a session would make the code's reader +/// able to find its author, which a short code read aloud should not. +final class ShareService { + ShareService({ + required this.store, + this.moderation = ShareModeration.review, + this.moderatorToken, + this.reportsToHide = 3, + this.maxBodyBytes = 4 * 1024 * 1024, + DateTime Function()? now, + }) : _now = now ?? DateTime.now; + + final ShareStore store; + final ShareModeration moderation; + + /// What `Authorization: Bearer …` has to carry on the moderation routes; + /// null switches them off. + final String? moderatorToken; + + final int reportsToHide; + + /// The largest bundle accepted. A tape is a few bytes a step, so four + /// megabytes is hours of play; a level with a heightfield is the larger + /// half. + final int maxBodyBytes; + + final DateTime Function() _now; + + /// Whether anything shared here could ever open: in review, only with a + /// moderator to publish it. + bool get takesShares => + moderation == ShareModeration.open || moderatorToken != null; + + /// `POST /api/v1/shares`. + Future share(String body) async { + if (!takesShares) { + return _say( + 503, + 'this server takes no shares: every one waits for a moderator, and ' + 'it has none (MODELS_SHARES_MODERATOR_TOKEN is not set)', + ); + } + if (body.length > maxBodyBytes) return tooLarge(maxBodyBytes); + final json = _object(body); + if (json == null) return _say(400, 'the body is not a JSON object'); + final ShareBundle bundle; + try { + bundle = ShareBundle.fromJson(json); + } on ShareFormatException catch (error) { + return _say(422, 'this is not a bundle: ${error.message}'); + } + if (!_gameName.hasMatch(bundle.game)) { + return _say( + 422, + 'the game is called "${bundle.game}"; a game name here is up to 40 ' + 'lower-case letters, digits, "-" and "_"', + ); + } + final title = bundle.title; + if (title != null && (title.length > _titleLimit || _hasControl(title))) { + return _say( + 422, + 'the title is ${title.length} characters or has a line break in it; ' + 'a title is one line of up to $_titleLimit', + ); + } + // Filed by the bytes this server writes, not the ones it was sent: the + // same bundle with its keys in another order is the same bundle. + final text = jsonEncode(bundle.toJson()); + final (:record, :created) = await store.file( + NewShare( + address: sha256.convert(utf8.encode(text)).toString(), + bundle: text, + game: bundle.game, + levelHash: bundle.levelHash, + title: title, + hasRun: bundle.run != null, + status: switch (moderation) { + ShareModeration.review => ShareStatus.pending, + ShareModeration.open => ShareStatus.published, + }, + createdAt: _now(), + ), + ); + return ( + status: created ? 201 : 200, + body: { + 'code': record.code, + 'address': record.address, + 'status': record.status.name, + 'message': _statusSentence(record), + }, + ); + } + + /// `GET /api/v1/shares/`: the bundle when it is published, and where + /// it stands when it is not. + Future open(String typed) async { + final record = await _find(typed); + if (record == null) return _unknownCode(typed); + if (record.status == ShareStatus.published) { + final text = await store.bundleText(record.code); + return ( + status: 200, + body: { + 'code': record.code, + 'status': record.status.name, + 'bundle': jsonDecode(text!), + }, + ); + } + return ( + status: record.status == ShareStatus.pending ? 202 : 410, + body: { + 'code': record.code, + 'status': record.status.name, + 'message': _statusSentence(record), + }, + ); + } + + /// `POST /api/v1/shares//reports` with `{reason}`. + Future report(String typed, String body) async { + final code = normaliseCode(Uri.decodeComponent(typed)); + final reason = _object(body)?['reason']; + if (reason is! String || reason.trim().isEmpty) { + return _say(422, 'a report says what is wrong with it: {"reason": …}'); + } + final record = code == null + ? null + : await store.report( + code, + ShareReport( + reason: reason.length > _reasonLimit + ? reason.substring(0, _reasonLimit) + : reason, + at: _now(), + ), + reportsToHide: reportsToHide, + ); + if (record == null) return _unknownCode(typed); + return _say(202, 'reported; a moderator will look at "${record.code}"'); + } + + /// `GET /api/v1/moderation/queue`. + Future queue(String? authorization) async { + if (_refusedModerator(authorization) case final refused?) return refused; + final records = await store.queue(limit: 500); + return ( + status: 200, + body: { + 'items': >[ + for (final record in records) record.toJson(), + ], + }, + ); + } + + /// `GET /api/v1/moderation/shares/`: one bundle with its record, + /// whatever its status. + Future inspect(String? authorization, String typed) async { + if (_refusedModerator(authorization) case final refused?) return refused; + final record = await _find(typed); + if (record == null) return _unknownCode(typed); + final text = await store.bundleText(record.code); + return ( + status: 200, + body: {...record.toJson(), 'bundle': jsonDecode(text!)}, + ); + } + + /// `POST /api/v1/moderation/shares/` with + /// `{decision: publish | remove, reason}`. + Future decide( + String? authorization, + String typed, + String body, + ) async { + if (_refusedModerator(authorization) case final refused?) return refused; + final json = _object(body); + if (json == null) return _say(400, 'the body is not a JSON object'); + final decision = switch (json['decision']) { + 'publish' => ShareStatus.published, + 'remove' => ShareStatus.removed, + _ => null, + }; + if (decision == null) { + return _say(422, 'a decision is "publish" or "remove"'); + } + final note = switch (json['reason']) { + final String reason when reason.trim().isNotEmpty => reason, + _ => null, + }; + if (decision == ShareStatus.removed && note == null) { + return _say( + 422, + 'a removal says why: the reason is what the code answers with', + ); + } + final code = normaliseCode(Uri.decodeComponent(typed)); + final record = code == null + ? null + : await store.decide(code, decision, note); + if (record == null) return _unknownCode(typed); + return ( + status: 200, + body: { + 'code': record.code, + 'status': record.status.name, + 'message': _statusSentence(record), + }, + ); + } + + Future _find(String typed) async { + final code = normaliseCode(Uri.decodeComponent(typed)); + return code == null ? null : store.byCode(code); + } + + /// Null when [authorization] carries the moderator token; otherwise the + /// refusal. + /// + /// Compared in full whatever the first difference, so how long a wrong + /// token took to refuse says nothing about how much of it was right. + ShareAnswer? _refusedModerator(String? authorization) { + final token = moderatorToken; + if (token == null) { + return _say( + 503, + 'moderation is off on this server: MODELS_SHARES_MODERATOR_TOKEN is ' + 'not set', + ); + } + final header = authorization ?? ''; + final given = utf8.encode( + header.startsWith('Bearer ') ? header.substring(7) : '', + ); + final wanted = utf8.encode(token); + final difference = Iterable.generate(wanted.length).fold( + given.length ^ wanted.length, + (sum, i) => sum | (wanted[i] ^ (i < given.length ? given[i] : 0)), + ); + return difference == 0 + ? null + : _say( + 401, + 'this is for moderators: send Authorization: Bearer ' + '', + ); + } +} + +/// What a code answers, as a sentence. +String _statusSentence(ShareRecord record) => switch (record.status) { + ShareStatus.published => '"${record.code}" is open to anyone with the code', + ShareStatus.pending => + '"${record.code}" is kept and waits for a moderator before anyone can ' + 'open it', + _ => '"${record.code}" was taken down: ${record.note ?? 'no reason given'}', +}; + +Map? _object(String body) { + try { + final json = jsonDecode(body); + return json is Map ? json : null; + } on FormatException { + return null; + } +} + +bool _hasControl(String text) => text.runes.any((int r) => r < 0x20); + +/// The answer for a body over [limit] bytes — here, and in the routes that +/// refuse one before it is read whole. +ShareAnswer tooLarge(int limit) => + _say(413, 'the body is over the $limit bytes this server takes'); + +ShareAnswer _unknownCode(String typed) => + _say(404, 'no level is shared under "$typed"; check the code'); diff --git a/cloud/server/lib/src/shares/share_store.dart b/cloud/server/lib/src/shares/share_store.dart new file mode 100644 index 000000000..65642d20b --- /dev/null +++ b/cloud/server/lib/src/shares/share_store.dart @@ -0,0 +1,238 @@ +/// Where shared levels are kept, as the service needs them. +/// +/// **The bytes as this server wrote them, not as they were sent.** A bundle is +/// read, checked and written again by `ShareBundle.toJson`, and those bytes +/// are what is kept and what the address is the SHA-256 of — so the same +/// level shared twice with its keys in another order is one row, and what a +/// code opens is byte for byte what its address names. +library; + +import 'package:flutter3d_sim/flutter3d_sim.dart' show ShareStatus; + +import 'short_code.dart'; + +/// One report against a shared level. +final class ShareReport { + const ShareReport({required this.reason, required this.at}); + + final String reason; + final DateTime at; + + Map toJson() => { + 'reason': reason, + 'at': at.toUtc().toIso8601String(), + }; +} + +/// What is known about a shared level without opening it. +final class ShareRecord { + const ShareRecord({ + required this.code, + required this.address, + required this.game, + required this.levelHash, + required this.title, + required this.hasRun, + required this.createdAt, + required this.status, + this.note, + this.reports = const [], + }); + + /// The short code handed out, as [normaliseCode] writes it. + final String code; + + /// The SHA-256 of the kept bytes, hex. + final String address; + + final String game; + final String levelHash; + final String? title; + final bool hasRun; + final DateTime createdAt; + final ShareStatus status; + + /// Why a moderator decided as they did; what a removed code answers with. + final String? note; + + /// Reports not yet answered by a moderator publishing it again. + final List reports; + + ShareRecord copyWith({ + ShareStatus? status, + String? note, + List? reports, + }) => ShareRecord( + code: code, + address: address, + game: game, + levelHash: levelHash, + title: title, + hasRun: hasRun, + createdAt: createdAt, + status: status ?? this.status, + note: note ?? this.note, + reports: reports ?? this.reports, + ); + + Map toJson() => { + 'code': code, + 'address': address, + 'game': game, + 'levelHash': levelHash, + if (title != null) 'title': title, + 'hasRun': hasRun, + 'createdAt': createdAt.toUtc().toIso8601String(), + 'status': status.name, + if (note != null) 'note': note, + 'reports': [for (final report in reports) report.toJson()], + }; +} + +/// A level about to be filed: everything but its code, which the store picks. +final class NewShare { + const NewShare({ + required this.address, + required this.bundle, + required this.game, + required this.levelHash, + required this.title, + required this.hasRun, + required this.status, + required this.createdAt, + }); + + final String address; + + /// The bundle as this server wrote it — the text [address] is the hash of. + final String bundle; + + final String game; + final String levelHash; + final String? title; + final bool hasRun; + final ShareStatus status; + final DateTime createdAt; +} + +abstract interface class ShareStore { + /// Files [share] under the first of [codesOf] its address that no other + /// bundle holds, and answers the record with whether it is new. A share + /// whose address is filed already answers that record, untouched — its + /// status included, so sharing a removed level again does not reopen it. + Future<({ShareRecord record, bool created})> file(NewShare share); + + /// The record filed under [code], as [normaliseCode] wrote it. + Future byCode(String code); + + /// The kept bytes behind [code], as text. + Future bundleText(String code); + + /// Adds [report] to [code] and, when that makes [reportsToHide] reports + /// against a published level, sends it back to pending — in one step, so + /// two reports at once cannot both miss the threshold. + Future report( + String code, + ShareReport report, { + required int reportsToHide, + }); + + /// A moderator's decision. Publishing answers the reports that sent it to + /// review, and they are dropped; leaving them would send it straight back + /// at the next one. + Future decide(String code, ShareStatus status, String? note); + + /// What a moderator should look at — pending, or reported — oldest first. + Future> queue({required int limit}); +} + +/// A store in memory: the tests', and a server's that keeps nothing past a +/// restart. +/// +/// Every method finishes without yielding between its read and its write, so +/// in one isolate nothing interleaves inside one. +final class MemoryShareStore implements ShareStore { + final Map records = {}; + final Map bundles = {}; + + @override + Future<({ShareRecord record, bool created})> file(NewShare share) async { + final known = records.values + .where((record) => record.address == share.address) + .firstOrNull; + if (known != null) return (record: known, created: false); + final code = codesOf( + share.address, + ).firstWhere((code) => !records.containsKey(code)); + final record = ShareRecord( + code: code, + address: share.address, + game: share.game, + levelHash: share.levelHash, + title: share.title, + hasRun: share.hasRun, + createdAt: share.createdAt, + status: share.status, + ); + records[code] = record; + bundles[code] = share.bundle; + return (record: record, created: true); + } + + @override + Future byCode(String code) async => records[code]; + + @override + Future bundleText(String code) async => bundles[code]; + + @override + Future report( + String code, + ShareReport report, { + required int reportsToHide, + }) async { + final record = records[code]; + if (record == null) return null; + final reports = [...record.reports, report]; + final hidden = + record.status == ShareStatus.published && + reports.length >= reportsToHide; + return records[code] = record.copyWith( + reports: reports, + status: hidden ? ShareStatus.pending : null, + ); + } + + @override + Future decide( + String code, + ShareStatus status, + String? note, + ) async { + final record = records[code]; + if (record == null) return null; + return records[code] = ShareRecord( + code: record.code, + address: record.address, + game: record.game, + levelHash: record.levelHash, + title: record.title, + hasRun: record.hasRun, + createdAt: record.createdAt, + status: status, + note: note, + reports: status == ShareStatus.published + ? const [] + : record.reports, + ); + } + + @override + Future> queue({required int limit}) async => records.values + .where( + (record) => + record.status == ShareStatus.pending || record.reports.isNotEmpty, + ) + .take(limit) + .toList(); +} diff --git a/cloud/server/lib/src/shares/short_code.dart b/cloud/server/lib/src/shares/short_code.dart new file mode 100644 index 000000000..751080c8f --- /dev/null +++ b/cloud/server/lib/src/shares/short_code.dart @@ -0,0 +1,51 @@ +/// N10: codes a person types or reads aloud, taken from a bundle's address. +library; + +/// Crockford's base 32: no I, L, O or U, so nothing reads as a digit it is +/// not, and nothing spells much by accident. +const String _alphabet = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; + +/// The shortest code handed out. Seven characters are 35 bits: with a +/// million bundles filed, a new one lands on a taken code about once in +/// thirty-four thousand, and when it does it takes an eighth character rather +/// than the other bundle's code. +const int shortestCode = 7; + +/// As long as a SHA-256 goes: 256 bits are 51 whole characters of five. +const int longestCode = 256 ~/ 5; + +/// The first [length] characters of [address] (hex SHA-256) in base 32. +/// +/// **Derived, not drawn.** The same bundle shared twice gets the same code +/// without a lookup, and a code says nothing a stranger could not already +/// compute from the bytes. +String codeOf(String address, int length) { + final bits = [ + for (final digit in address.split('')) + int.parse(digit, radix: 16).toRadixString(2).padLeft(4, '0'), + ].join(); + return [ + for (var i = 0; i < length; i++) + _alphabet[int.parse(bits.substring(i * 5, i * 5 + 5), radix: 2)], + ].join(); +} + +/// Every code [address] may be filed under, shortest first: the next one is +/// tried only when a different bundle already holds the one before. +Iterable codesOf(String address) => Iterable.generate( + longestCode - shortestCode + 1, + (int extra) => codeOf(address, shortestCode + extra), +); + +/// [typed] as a code is stored: upper case, with the letters Crockford +/// reads as digits read as those digits, and dashes and spaces dropped, since +/// people group codes when they copy them. Null when it cannot be a code. +String? normaliseCode(String typed) { + final upper = typed + .toUpperCase() + .replaceAll(RegExp(r'[\s-]'), '') + .replaceAll('O', '0') + .replaceAll(RegExp('[IL]'), '1'); + if (upper.length < shortestCode || upper.length > longestCode) return null; + return upper.split('').every(_alphabet.contains) ? upper : null; +} diff --git a/cloud/server/test/share_db_test.dart b/cloud/server/test/share_db_test.dart new file mode 100644 index 000000000..77a2d4bbc --- /dev/null +++ b/cloud/server/test/share_db_test.dart @@ -0,0 +1,131 @@ +/// [SharesRepository] against a real database: the bytes through `text` and +/// back unchanged, the unique address deciding a second filing, a code that +/// lengthens when a different bundle holds it, and reports that hide and are +/// answered. +@Tags(['db']) +library; + +import 'dart:io'; + +import 'package:flutter3d_models/src/db/database.dart'; +import 'package:flutter3d_models/src/db/shares_repository.dart'; +import 'package:flutter3d_models/src/shares/share_store.dart'; +import 'package:flutter3d_models/src/shares/short_code.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart' show ShareStatus; +import 'package:test/test.dart'; + +/// Not a real SHA-256 of [bundle] — the repository takes the address it is +/// handed — but the shape the column's check asks for. +NewShare _share({ + String address = + 'a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90', + String bundle = '{"version":1,"game":"walk","levelHash":"abcd1234"}', + ShareStatus status = ShareStatus.pending, +}) => NewShare( + address: address, + bundle: bundle, + game: 'walk', + levelHash: 'abcd1234', + title: null, + hasRun: false, + status: status, + createdAt: DateTime.utc(2026, 10, 1, 12), +); + +void main() { + late Database db; + late SharesRepository shares; + + setUpAll(() async { + final url = + Platform.environment['MODELS_TEST_DATABASE_URL'] ?? + 'postgres://models:models@localhost:55432/models'; + db = await Database.open(url); + shares = SharesRepository(db); + }); + + tearDownAll(() => db.close()); + + setUp( + () => db.run( + (s) => s.execute('truncate shares, share_reports restart identity'), + ), + ); + + test('a bundle is kept byte for byte and filed once', () async { + // Mutation: a `jsonb` column re-orders keys and re-spells numbers, and + // the code would open bytes its address is not the hash of. + const bundle = '{"version":1,"z":1.50,"a":{"b":[2,1]}}'; + final first = await shares.file(_share(bundle: bundle)); + final second = await shares.file(_share(bundle: bundle)); + + expect(first.created, isTrue); + expect(second.created, isFalse); + expect(second.record.code, first.record.code); + expect(first.record.code, codeOf(first.record.address, shortestCode)); + expect(await shares.bundleText(first.record.code), bundle); + expect(first.record.createdAt, DateTime.utc(2026, 10, 1, 12)); + }); + + test('a code a different bundle holds is lengthened, not shared', () async { + const one = + 'a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90'; + // Agrees with `one` for its first 39 bits, so its seven-character code + // (35 bits) is the same and its eight-character one (40) is not. + const other = + 'a1b2c3d4e4f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90'; + expect(codeOf(other, shortestCode), codeOf(one, shortestCode)); + + final first = await shares.file(_share(address: one)); + final second = await shares.file(_share(address: other)); + + expect(second.created, isTrue); + expect(second.record.code, codeOf(other, shortestCode + 1)); + expect(first.record.code, hasLength(shortestCode)); + }); + + test('reports hide a published share at the threshold, and publishing it ' + 'answers them', () async { + final code = (await shares.file( + _share(status: ShareStatus.published), + )).record.code; + + final once = await shares.report( + code, + ShareReport(reason: 'spam', at: DateTime.utc(2026, 10, 2)), + reportsToHide: 2, + ); + expect(once!.status, ShareStatus.published); + final twice = await shares.report( + code, + ShareReport(reason: 'still spam', at: DateTime.utc(2026, 10, 3)), + reportsToHide: 2, + ); + expect(twice!.status, ShareStatus.pending); + expect(twice.reports.map((r) => r.reason), ['spam', 'still spam']); + expect(twice.reports.first.at, DateTime.utc(2026, 10, 2)); + expect((await shares.queue(limit: 10)).single.code, code); + + final published = await shares.decide(code, ShareStatus.published, null); + expect(published!.reports, isEmpty); + expect(await shares.queue(limit: 10), isEmpty); + + final removed = await shares.decide(code, ShareStatus.removed, 'spam'); + expect(removed!.note, 'spam'); + expect((await shares.byCode(code))!.status, ShareStatus.removed); + }); + + test('a code nobody holds is reported and decided as nothing', () async { + expect( + await shares.report( + 'ZZZZZZZ', + ShareReport(reason: 'x', at: DateTime.utc(2026)), + reportsToHide: 1, + ), + isNull, + ); + expect(await shares.decide('ZZZZZZZ', ShareStatus.removed, 'x'), isNull); + expect(await shares.byCode('ZZZZZZZ'), isNull); + expect(await shares.bundleText('ZZZZZZZ'), isNull); + }); +} diff --git a/cloud/server/test/share_routes_test.dart b/cloud/server/test/share_routes_test.dart new file mode 100644 index 000000000..7e0e56481 --- /dev/null +++ b/cloud/server/test/share_routes_test.dart @@ -0,0 +1,522 @@ +/// N10: a level filed by its bytes behind a short code, what a moderator and a +/// report do to it, and `RunService` from `flutter3d_sim` against the real +/// routes — the two halves of one protocol, checked together rather than each +/// against a description of the other. +/// +/// dart test test/share_routes_test.dart +/// +/// Mutation: file by the bytes as sent rather than as re-written and the same +/// bundle with its keys reordered gets a second code; publish on arrival in +/// review mode and an unmoderated upload is open to anyone; rename `code`, +/// `bundle` or `message` on either side and the client answers a refusal for +/// a call that worked. +library; + +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; +import 'package:flutter3d_models/src/config.dart'; +import 'package:flutter3d_models/src/http/share_routes.dart'; +import 'package:flutter3d_models/src/shares/share_service.dart'; +import 'package:flutter3d_models/src/shares/share_store.dart'; +import 'package:flutter3d_models/src/shares/short_code.dart'; +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:shelf/shelf.dart'; +import 'package:shelf_router/shelf_router.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +const String _token = 'a-moderator-token-long-enough'; + +Level _level({String name = 'strip'}) => Level( + name: name, + materials: {'stone': LevelMaterial()}, + brushes: [ + Brush( + centre: Vector3(0.0, -0.5, 0.0), + size: Vector3(20.0, 1.0, 4.0), + material: 'stone', + ), + ], + entities: const [], +); + +/// A run of [steps] steps through [level]. Nothing here plays it: a share is +/// checked against its level's hash, not re-simulated, so a tape and a trace +/// of made-up states are all it needs. +Demo _run(Level level, {int steps = 120}) { + final trace = DigestTrace(every: 10); + for (var step = 1; step <= steps; step++) { + trace.observe(step, {'x': step / 20.0}); + } + return Demo( + level: 'levels/${level.name}.json', + levelHash: level.digestHex, + start: Snapshot({'x': 0.0}), + tape: InputTape( + seed: 1, + frames: [ + for (var step = 0; step < steps; step++) const InputFrame(stickX: 1.0), + ], + ), + buildStamp: 'test', + checkpoints: trace, + ); +} + +ShareBundle _bundle({Demo? run, String? title, String game = 'walk'}) => + ShareBundle(game: game, level: _level().toJson(), run: run, title: title); + +/// The routes over a store in memory, mounted the way `app.dart` mounts them, +/// with a route after them under the same prefix so falling through is +/// tested too. +final class _Server { + _Server({ + ShareModeration moderation = ShareModeration.review, + String? token = _token, + int reportsToHide = 3, + int maxBodyBytes = 1024 * 1024, + }) : service = ShareService( + store: MemoryShareStore(), + moderation: moderation, + moderatorToken: token, + reportsToHide: reportsToHide, + maxBodyBytes: maxBodyBytes, + now: () => DateTime.utc(2026, 10, 1, 12), + ); + + final ShareService service; + + MemoryShareStore get store => service.store as MemoryShareStore; + + late final Handler handler = + (Router(notFoundHandler: (Request request) => Response.notFound('none')) + ..mount('/api/', shareRoutes(service).call) + ..get( + '/api/v1/models', + (Request request) => Response.ok('the models route'), + )) + .call; + + Future<(int, Map)> call( + String method, + String path, { + Object? body, + bool moderator = false, + String? authorization, + }) async { + final response = await handler( + Request( + method, + Uri.parse('https://models.example/api$path'), + body: body == null ? null : (body is String ? body : jsonEncode(body)), + headers: { + if (moderator) 'authorization': 'Bearer $_token', + 'authorization': ?authorization, + }, + ), + ); + final text = await response.readAsString(); + return (response.statusCode, jsonDecode(text) as Map); + } + + Future share(ShareBundle bundle) async { + final (_, filed) = await call('POST', '/v1/shares', body: bundle.toJson()); + return filed['code']! as String; + } + + Future decide(String code, Map decision) async => + (await call( + 'POST', + '/v1/moderation/shares/$code', + body: decision, + moderator: true, + )).$1; + + /// [RunService] talking to these routes without a socket, from the base a + /// game would be handed. + RunService client() => RunService( + base: Uri.parse('https://models.example/api/'), + transport: (RunRequest request) async { + final response = await handler( + Request(request.method, request.uri, body: request.body), + ); + return RunResponse(response.statusCode, await response.readAsString()); + }, + ); +} + +void main() { + group('in review mode', () { + test('a share waits for a moderator before anyone can open it', () async { + final server = _Server(); + final (status, filed) = await server.call( + 'POST', + '/v1/shares', + body: _bundle(run: _run(_level()), title: 'east').toJson(), + ); + expect(status, 201, reason: '${filed['message']}'); + expect(filed['status'], 'pending'); + final code = filed['code']! as String; + expect(code, hasLength(shortestCode)); + + final (early, waiting) = await server.call('GET', '/v1/shares/$code'); + expect(early, 202); + expect(waiting['bundle'], isNull); + + expect(await server.decide(code, {'decision': 'publish'}), 200); + final (opened, json) = await server.call('GET', '/v1/shares/$code'); + expect(opened, 200); + final bundle = ShareBundle.fromJson( + json['bundle']! as Map, + ); + expect(bundle.title, 'east'); + expect(bundle.run!.steps, 120); + }); + + test( + 'the same bundle shared twice is filed once, under one code', + () async { + final server = _Server(); + final json = _bundle().toJson(); + final reordered = { + for (final key in json.keys.toList().reversed) key: json[key], + }; + + final first = await server.call('POST', '/v1/shares', body: json); + final second = await server.call('POST', '/v1/shares', body: reordered); + + expect(first.$1, 201); + expect(second.$1, 200); + expect(second.$2['code'], first.$2['code']); + expect(server.store.records, hasLength(1)); + // What is kept is what the address names, byte for byte. + final code = first.$2['code']! as String; + final kept = server.store.bundles[code]!; + expect(kept, jsonEncode(json)); + expect( + first.$2['address'], + sha256.convert(utf8.encode(kept)).toString(), + ); + expect(code, codeOf(first.$2['address']! as String, shortestCode)); + }, + ); + + test('a code is read the way people type it', () async { + final server = _Server(); + final code = await server.share(_bundle()); + final typed = '${code.substring(0, 3).toLowerCase()}-${code.substring(3)}' + .replaceAll('0', 'o') + .replaceAll('1', 'l'); + + expect((await server.call('GET', '/v1/shares/$typed')).$1, 202); + expect((await server.call('GET', '/v1/shares/not-a-code')).$1, 404); + }); + + test('a bundle whose run is from another level is refused', () async { + final server = _Server(); + final json = _bundle().toJson() + ..['run'] = _run(_level(name: 'elsewhere')).toJson(); + + final (status, answer) = await server.call( + 'POST', + '/v1/shares', + body: json, + ); + + expect(status, 422); + expect(answer['message'], contains('another version of the level')); + expect(server.store.records, isEmpty); + }); + + test( + 'a game name or a title this server will not show is refused', + () async { + final server = _Server(); + + final (game, gameAnswer) = await server.call( + 'POST', + '/v1/shares', + body: _bundle(game: 'Walk Two').toJson(), + ); + final (title, titleAnswer) = await server.call( + 'POST', + '/v1/shares', + body: _bundle(title: 'east\nwest').toJson(), + ); + + expect(game, 422); + expect(gameAnswer['message'], contains('"Walk Two"')); + expect(title, 422); + expect(titleAnswer['message'], contains('one line')); + expect(server.store.records, isEmpty); + }, + ); + + test('a removal says why, and the code answers with it', () async { + final server = _Server(); + final code = await server.share(_bundle()); + + expect(await server.decide(code, {'decision': 'remove'}), 422); + expect( + await server.decide(code, {'decision': 'remove', 'reason': 'spam'}), + 200, + ); + + final (status, answer) = await server.call('GET', '/v1/shares/$code'); + expect(status, 410); + expect(answer['message'], contains('spam')); + // Shared again, the level is the same removed row, not a fresh one. + final (again, refiled) = await server.call( + 'POST', + '/v1/shares', + body: _bundle().toJson(), + ); + expect(again, 200); + expect(refiled['status'], 'removed'); + }); + + test('moderation refuses anyone without the token', () async { + final server = _Server(); + final code = await server.share(_bundle()); + + expect((await server.call('GET', '/v1/moderation/queue')).$1, 401); + final (wrong, answer) = await server.call( + 'GET', + '/v1/moderation/queue', + authorization: 'Bearer $_token-', + ); + expect(wrong, 401); + expect(answer['message'], contains('MODELS_SHARES_MODERATOR_TOKEN')); + expect( + (await server.call( + 'POST', + '/v1/moderation/shares/$code', + body: {'decision': 'publish'}, + authorization: 'Bearer ${_token.substring(1)}', + )).$1, + 401, + ); + expect(server.store.records[code]!.status, ShareStatus.pending); + + final (queued, queue) = await server.call( + 'GET', + '/v1/moderation/queue', + moderator: true, + ); + expect(queued, 200); + expect( + (queue['items']! as List).single, + containsPair('code', code), + ); + final (looked, record) = await server.call( + 'GET', + '/v1/moderation/shares/$code', + moderator: true, + ); + expect(looked, 200); + expect(record['bundle'], isA>()); + }); + + test( + 'with no moderator token, nothing is taken and moderation is off', + () async { + // Mutation: a review server with nobody to review would keep every + // upload pending forever, and an empty token would let an empty + // `Bearer ` header through. + final server = _Server(token: null); + + final (status, answer) = await server.call( + 'POST', + '/v1/shares', + body: _bundle().toJson(), + ); + expect(status, 503); + expect(answer['message'], contains('MODELS_SHARES_MODERATOR_TOKEN')); + expect(server.store.records, isEmpty); + expect( + (await server.call( + 'GET', + '/v1/moderation/queue', + authorization: 'Bearer ', + )).$1, + 503, + ); + }, + ); + + test('what these routes do not answer falls through to the rest of ' + '/api/', () async { + final server = _Server(); + final response = await server.handler( + Request('GET', Uri.parse('https://models.example/api/v1/models')), + ); + expect(await response.readAsString(), 'the models route'); + }); + }); + + group('in open mode', () { + test('reports send a published level back to review, and publishing it ' + 'again answers them', () async { + final server = _Server( + moderation: ShareModeration.open, + reportsToHide: 2, + ); + final (_, filed) = await server.call( + 'POST', + '/v1/shares', + body: _bundle().toJson(), + ); + final code = filed['code']! as String; + expect(filed['status'], 'published'); + + final (empty, _) = await server.call( + 'POST', + '/v1/shares/$code/reports', + body: {'reason': ' '}, + ); + expect(empty, 422); + for (final reason in ['offensive title', 'offensive title']) { + final (reported, answer) = await server.call( + 'POST', + '/v1/shares/$code/reports', + body: {'reason': reason}, + ); + expect(reported, 202); + expect(answer['message'], contains(code)); + } + + expect((await server.call('GET', '/v1/shares/$code')).$1, 202); + final (_, queue) = await server.call( + 'GET', + '/v1/moderation/queue', + moderator: true, + ); + final item = (queue['items']! as List).single! as Map; + expect(item['reports'], hasLength(2)); + + expect(await server.decide(code, {'decision': 'publish'}), 200); + expect((await server.call('GET', '/v1/shares/$code')).$1, 200); + expect(server.store.records[code]!.reports, isEmpty); + }); + + test('a body over the limit is refused before it is read whole', () async { + final server = _Server( + moderation: ShareModeration.open, + maxBodyBytes: 2048, + ); + + final (status, answer) = await server.call( + 'POST', + '/v1/shares', + body: ' ' * 4096, + ); + + expect(status, 413); + expect(answer['message'], contains('2048')); + final (report, _) = await server.call( + 'POST', + '/v1/shares/0000000/reports', + body: ' ' * 5000, + ); + expect(report, 413); + }); + }); + + group('RunService against these routes', () { + test('a bundle shared, published and opened is the bundle sent', () async { + final server = _Server(); + final client = server.client(); + final sent = _bundle(run: _run(_level()), title: 'east'); + + final shared = await client.share(sent); + final code = (shared as ServiceDone).value.code; + expect(shared.value.status, ShareStatus.pending); + expect(shared.value.address, hasLength(64)); + + final early = await client.open(code); + expect( + early, + isA>().having( + (r) => r.reason, + 'reason', + contains('moderator'), + ), + ); + + await server.decide(code, {'decision': 'publish'}); + final opened = await client.open(code.toLowerCase()); + + final bundle = (opened as ServiceDone).value; + expect(bundle.levelHash, sent.levelHash); + expect( + bundle.run!.checkpoints.hexDigests, + sent.run!.checkpoints.hexDigests, + ); + }); + + test('a report reaches the moderation queue, and a refusal is the ' + "server's sentence", () async { + final server = _Server(); + final client = server.client(); + final code = + ((await client.share(_bundle())) as ServiceDone) + .value + .code; + + final answer = await client.report(code, 'not a level'); + expect(answer, isA>()); + expect(server.store.records[code]!.reports.single.reason, 'not a level'); + + final unknown = await client.open('ZZZZZZZ'); + expect( + unknown, + isA>().having( + (r) => r.reason, + 'reason', + contains('no level is shared under "ZZZZZZZ"'), + ), + ); + }); + }); + + group('configuration', () { + const base = { + 'MODELS_BASE_URL': 'https://models.example', + 'MODELS_DATABASE_URL': 'postgres://nobody@127.0.0.1/none', + 'MODELS_BLOB_DIR': '/tmp/none', + 'MODELS_SECRET': 'a-secret', + }; + + test('sharing is reviewed and unmoderated unless a deploy says so', () { + final config = Config.fromEnvironment(base); + expect(config.shareModeration, ShareModeration.review); + expect(config.shareModeratorToken, isNull); + expect(config.shareReportsToHide, 3); + + final open = Config.fromEnvironment({ + ...base, + 'MODELS_SHARES_MODERATION': 'open', + 'MODELS_SHARES_MODERATOR_TOKEN': _token, + 'MODELS_SHARES_REPORTS_TO_HIDE': '5', + }); + expect(open.shareModeration, ShareModeration.open); + expect(open.shareModeratorToken, _token); + expect(open.shareReportsToHide, 5); + }); + + test('a setting present and wrong stops the start, named', () { + expect( + () => Config.fromEnvironment({ + ...base, + 'MODELS_SHARES_MODERATION': 'lax', + 'MODELS_SHARES_MODERATOR_TOKEN': 'short', + 'MODELS_SHARES_REPORTS_TO_HIDE': '0', + }), + throwsA( + isA().having((e) => e.missing, 'missing', hasLength(3)), + ), + ); + }); + }); +} diff --git a/packages/flutter3d_sim/CHANGELOG.md b/packages/flutter3d_sim/CHANGELOG.md index b1c37d5df..ef36a34c6 100644 --- a/packages/flutter3d_sim/CHANGELOG.md +++ b/packages/flutter3d_sim/CHANGELOG.md @@ -91,6 +91,13 @@ covers exactly the cells a flow field for the same body accepts. It is the first part of N2; the path search over it comes next. +- **A level can be shared behind a short code.** `ShareBundle` is a level + document, its hash and optionally a `.f3drun` through it, refused when the + run was recorded in another version of the level. `RunService` speaks the + `v1/shares` routes over a transport the game hands in, so the package + still has no dependency for it, and answers every call with `ServiceDone` + or `ServiceRefused` rather than throwing. `cloud/server` speaks this + protocol. - **A number tuned while the game runs is on the tape.** `InputState.tune` sets a tunable for one step, `InputFrame.tunes` records it, playback applies it, and `Tunables` is the step's side: named values with defaults, diff --git a/packages/flutter3d_sim/lib/flutter3d_sim.dart b/packages/flutter3d_sim/lib/flutter3d_sim.dart index 3385e1130..bc2ed3939 100644 --- a/packages/flutter3d_sim/lib/flutter3d_sim.dart +++ b/packages/flutter3d_sim/lib/flutter3d_sim.dart @@ -118,6 +118,8 @@ export 'src/save/state_digest.dart'; export 'src/save/step_time_trace.dart'; export 'src/save/tally.dart'; export 'src/save/tape_bisect.dart'; +export 'src/share/run_service.dart'; +export 'src/share/share_bundle.dart'; export 'src/telemetry/heatmap.dart'; export 'src/telemetry/resimulation.dart'; export 'src/telemetry/telemetry_consent.dart'; diff --git a/packages/flutter3d_sim/lib/src/share/run_service.dart b/packages/flutter3d_sim/lib/src/share/run_service.dart new file mode 100644 index 000000000..66a5cc26e --- /dev/null +++ b/packages/flutter3d_sim/lib/src/share/run_service.dart @@ -0,0 +1,245 @@ +import 'dart:convert'; + +import 'share_bundle.dart'; + +/// One HTTP request, as [RunService] makes it. +final class RunRequest { + const RunRequest(this.method, this.uri, {this.body}); + + final String method; + final Uri uri; + + /// JSON, or null for a request with no body. + final String? body; +} + +/// What came back: the status and the body as text. +final class RunResponse { + const RunResponse(this.status, this.body); + + final int status; + final String body; +} + +/// Sends a [RunRequest] and answers with what the server said. +/// +/// **Handed in rather than imported, because this package runs everywhere.** +/// `dart:io`'s client does not exist in a browser and `package:http` would be +/// this package's first dependency for one call; a game passes the client it +/// already has in three lines, and a test passes the server's own handler. +typedef RunTransport = Future Function(RunRequest request); + +/// What a call to a service came to. +sealed class ServiceAnswer { + const ServiceAnswer(); +} + +final class ServiceDone extends ServiceAnswer { + const ServiceDone(this.value); + + final T value; +} + +/// Not done, and why — in the server's words when it answered. +final class ServiceRefused extends ServiceAnswer { + const ServiceRefused(this.reason); + + final String reason; +} + +/// Where a shared bundle stands with the service's moderation. +/// +/// **Constants rather than an enum, because the server decides the set.** A +/// service that grows an `expired` is a service every client written against +/// three values must still understand; [named] answers null for a word it +/// does not know, and a caller treats that as "not open", which is what any +/// status but [published] means to a player. +final class ShareStatus { + const ShareStatus._(this.name); + + /// Anyone with the code can open it. + static const ShareStatus published = ShareStatus._('published'); + + /// Kept, and waiting for a moderator before anyone can open it. + static const ShareStatus pending = ShareStatus._('pending'); + + /// Taken down; the code answers with the reason. + static const ShareStatus removed = ShareStatus._('removed'); + + static const List values = [ + published, + pending, + removed, + ]; + + /// The word the protocol writes it as. + final String name; + + /// The status written as [name], or null for one this build does not know. + static ShareStatus? named(Object? name) { + for (final status in values) { + if (status.name == name) return status; + } + return null; + } + + @override + String toString() => name; +} + +/// A bundle the service has filed: the code to hand out and where it stands. +final class SharedBundle { + const SharedBundle({ + required this.code, + required this.address, + required this.status, + }); + + /// The short code a person types or reads out. + final String code; + + /// The SHA-256 of the bundle's bytes, which is what it is filed under. + final String address; + + final ShareStatus status; +} + +/// The client side of a sharing service — the `v1/shares` routes of +/// `cloud/server` in this repository, or anybody's server that speaks them. +/// +/// **Nothing here shares on its own.** The person pressing Share is the +/// consent; a player's runs sent for telemetry go through +/// `TelemetryUploader`, which asks for its own. +/// +/// Every call answers rather than throws: an unreachable server, a refusal and +/// a reply that is not JSON are all a [ServiceRefused] with a sentence. +final class RunService { + RunService({required this.base, required this.transport}); + + /// Where the service is, e.g. `https://runs.pleion.dev/`. + final Uri base; + + final RunTransport transport; + + /// Files [bundle] and answers with its code. + /// + /// Sending the same bundle twice gives the same code: the service files by + /// the bytes, not by who sent them. + Future> share(ShareBundle bundle) async { + return switch (await _call('POST', 'v1/shares', bundle.toJson())) { + _Answered(:final json) => _shared(json), + _Refusal(:final reason) => ServiceRefused(reason), + }; + } + + /// The bundle behind [code], when it is published. + Future> open(String code) async { + final Map json; + switch (await _call('GET', 'v1/shares/${Uri.encodeComponent(code)}')) { + case _Refusal(:final reason): + return ServiceRefused(reason); + case _Answered(json: final answered): + json = answered; + } + final bundle = json['bundle']; + if (bundle is! Map) { + return ServiceRefused( + json['message'] as String? ?? '"$code" is not open to anyone yet', + ); + } + try { + return ServiceDone(ShareBundle.fromJson(bundle)); + } on ShareFormatException catch (error) { + return ServiceRefused( + '"$code" could not be read: ${error.message}', + ); + } + } + + /// Tells the service's moderators that [code] should not be shared, and why. + Future> report(String code, String reason) async { + final reply = await _call( + 'POST', + 'v1/shares/${Uri.encodeComponent(code)}/reports', + {'reason': reason}, + ); + return _message(reply); + } + + ServiceAnswer _shared(Map json) { + final code = json['code']; + final address = json['address']; + final status = ShareStatus.named(json['status']); + if (code is! String || address is! String || status == null) { + return const ServiceRefused( + 'the service filed the bundle but did not say under what', + ); + } + return ServiceDone( + SharedBundle(code: code, address: address, status: status), + ); + } + + ServiceAnswer _message(_Reply reply) => switch (reply) { + _Refusal(:final reason) => ServiceRefused(reason), + _Answered(:final json) => ServiceDone( + json['message'] as String? ?? 'done', + ), + }; + + Future<_Reply> _call( + String method, + String path, [ + Map? body, + ]) async { + final uri = base.resolve(path); + final RunResponse response; + try { + response = await transport( + RunRequest(method, uri, body: body == null ? null : jsonEncode(body)), + ); + } catch (error) { + return _Refusal('could not reach $base: $error', 0); + } + final Object? decoded; + try { + decoded = response.body.isEmpty ? null : jsonDecode(response.body); + } on FormatException { + return _Refusal( + 'the service answered ${response.status} with something that is ' + 'not JSON', + response.status, + ); + } + final json = decoded is Map ? decoded : null; + if (response.status < 200 || response.status >= 300 || json == null) { + return _Refusal( + json?['message'] as String? ?? + 'the service answered ${response.status}', + response.status, + ); + } + return _Answered(json); + } +} + +/// What [RunService._call] came back with. +sealed class _Reply { + const _Reply(); +} + +/// A success status and a JSON object. +final class _Answered extends _Reply { + const _Answered(this.json); + + final Map json; +} + +/// Anything else, as a sentence — the server's `message` when it sent one — +/// and the status it came with, 0 when the server was not reached. +final class _Refusal extends _Reply { + const _Refusal(this.reason, this.status); + + final String reason; + final int status; +} diff --git a/packages/flutter3d_sim/lib/src/share/share_bundle.dart b/packages/flutter3d_sim/lib/src/share/share_bundle.dart new file mode 100644 index 000000000..10d55af01 --- /dev/null +++ b/packages/flutter3d_sim/lib/src/share/share_bundle.dart @@ -0,0 +1,128 @@ +import '../save/demo.dart'; +import '../save/state_digest.dart'; + +/// A level somebody shares, and optionally a run through it — what a short +/// code stands for. +/// +/// **A level, its hash and a `.f3drun`, and nothing a server could make up.** +/// The level is a document rather than a parsed [Level] so that any genre's +/// own format travels the same way — a level here, a track there — the way +/// [Demo.levelHash] already compares [contentDigestHex] of whatever document +/// recorded it. A reader that knows the format parses [level]; a server that +/// does not can still check the hash and store the bytes. +/// +/// **The run is checked against the level before anything leaves.** A bundle +/// whose run was recorded in another version of the level is a ghost running +/// through walls, and it is refused here and again on the server with the two +/// hashes named, because the person who made it can fix it and the person who +/// opens it cannot. +/// +/// The address a server files it under is not here: a 32-bit [StateDigest] is +/// right for telling two states apart and wrong for naming a document among +/// every document strangers upload, so the server takes a SHA-256 of the bytes +/// [toJson] writes. +final class ShareBundle { + ShareBundle({required this.game, required this.level, this.run, this.title}) + : levelHash = contentDigestHex(level) { + final run = this.run; + if (run != null && run.levelHash != levelHash) { + throw ShareFormatException( + 'the run was recorded in another version of the level ' + '(${run.levelHash}; the level being shared is $levelHash) — record ' + 'it again in this one', + ); + } + } + + /// Bumped when an existing field changes meaning. + static const int formatVersion = 1; + + /// What the game calls itself, so that a viewer knows which game opens it. + final String game; + + /// The level document, as its own `toJson()` wrote it. + final Map level; + + /// [contentDigestHex] of [level]: which version of it this is. + final String levelHash; + + /// A run through [level], or null for a level shared on its own. + final Demo? run; + + /// What the person sharing it called it, or null. + final String? title; + + Map toJson() => { + 'version': formatVersion, + 'game': game, + if (title != null) 'title': title, + 'levelHash': levelHash, + 'level': level, + if (run != null) 'run': run!.toJson(), + }; + + /// Reads a bundle, or throws a [ShareFormatException] that says why not. + /// + /// [levelHash] is read and compared rather than trusted: a bundle edited by + /// hand after it was written names a version of the level it does not hold. + factory ShareBundle.fromJson(Map json) { + final version = json['version']; + if (version is! num) { + throw const ShareFormatException('the bundle has no version in it'); + } + if (version > formatVersion) { + throw ShareFormatException( + 'the bundle was written by a newer build (format $version, this build ' + 'reads $formatVersion) — update flutter3d to open it', + ); + } + final game = json['game']; + if (game is! String || game.isEmpty) { + throw const ShareFormatException('the bundle names no game'); + } + final level = json['level']; + if (level is! Map) { + throw const ShareFormatException('the bundle has no level in it'); + } + final title = json['title']; + if (title != null && title is! String) { + throw const ShareFormatException('the title is not text'); + } + final rawRun = json['run']; + if (rawRun != null && rawRun is! Map) { + throw const ShareFormatException('the run is not a document'); + } + final Demo? run; + try { + run = rawRun == null + ? null + : Demo.fromJson(rawRun as Map); + } on DemoFormatException catch (error) { + throw ShareFormatException('the run: ${error.message}'); + } + final bundle = ShareBundle( + game: game, + level: level, + run: run, + title: title as String?, + ); + final claimed = json['levelHash']; + if (claimed != bundle.levelHash) { + throw ShareFormatException( + 'the bundle says its level is $claimed, and the level in it is ' + '${bundle.levelHash}', + ); + } + return bundle; + } +} + +/// Thrown when a bundle cannot be read, or would not mean what it says. +final class ShareFormatException implements Exception { + const ShareFormatException(this.message); + + final String message; + + @override + String toString() => 'ShareFormatException: $message'; +} diff --git a/packages/flutter3d_sim/test/run_service_test.dart b/packages/flutter3d_sim/test/run_service_test.dart new file mode 100644 index 000000000..24965990d --- /dev/null +++ b/packages/flutter3d_sim/test/run_service_test.dart @@ -0,0 +1,209 @@ +/// The client side of sharing: the bundle a short code stands for, and +/// `RunService`, which sends it through whatever transport the game hands it. +/// +/// dart test test/run_service_test.dart +/// +/// The end-to-end half — this client against the reference server — is in +/// `cloud/server/test/`, which can depend on both. +/// +/// Mutation: drop the level hash check from `ShareBundle` and a run recorded +/// in another version of the level is shared as if it fitted. +library; + +import 'dart:convert'; + +import 'package:flutter3d_sim/flutter3d_sim.dart'; +import 'package:test/test.dart'; + +Map _level() => Level(name: 'yard').toJson(); + +Demo _run({String? levelHash, String? recordedBy}) => Demo( + level: 'yard.json', + levelHash: levelHash ?? contentDigestHex(_level()), + start: const Snapshot({'x': 0}), + tape: InputTape(seed: 3, frames: [const InputFrame()]), + buildStamp: 'test', + checkpoints: DigestTrace(every: 1)..observe(1, 0), + recordedBy: recordedBy, +); + +/// A transport that writes down what it was asked and answers [status] +/// with [body]. +final class _Wire { + _Wire([this.status = 200, this.body = '{}']); + + final int status; + final String body; + final List sent = []; + + Future call(RunRequest request) async { + sent.add(request); + return RunResponse(status, body); + } +} + +RunService _service(_Wire wire) => + RunService(base: Uri.parse('https://runs.example/'), transport: wire.call); + +void main() { + group('a share bundle', () { + test('reads back what it wrote, hash and run included', () { + final bundle = ShareBundle( + game: 'walk', + level: _level(), + run: _run(), + title: 'the yard', + ); + + final read = ShareBundle.fromJson( + jsonDecode(jsonEncode(bundle.toJson())) as Map, + ); + + expect(read.levelHash, bundle.levelHash); + expect(read.title, 'the yard'); + expect(read.run!.steps, 1); + }); + + test('refuses a run recorded in another version of the level', () { + expect( + () => ShareBundle( + game: 'walk', + level: _level(), + run: _run(levelHash: '00000000'), + ), + throwsA( + isA().having( + (e) => e.message, + 'message', + contains('00000000'), + ), + ), + ); + }); + + test('refuses a document whose level is not the one it names', () { + final json = ShareBundle(game: 'walk', level: _level()).toJson(); + (json['level']! as Map)['name'] = 'edited by hand'; + + expect( + () => ShareBundle.fromJson(json), + throwsA(isA()), + ); + }); + + test('refuses a newer format by saying so', () { + final json = ShareBundle(game: 'walk', level: _level()).toJson() + ..['version'] = 99; + + expect( + () => ShareBundle.fromJson(json), + throwsA( + isA().having( + (e) => e.message, + 'message', + contains('newer build'), + ), + ), + ); + }); + }); + + group('the service', () { + test('a share posts the bundle and answers with its code', () async { + final wire = _Wire( + 201, + '{"code":"ABCDEFG","address":"ff","status":"pending"}', + ); + + final answer = await _service( + wire, + ).share(ShareBundle(game: 'walk', level: _level(), run: _run())); + + final request = wire.sent.single; + expect(request.method, 'POST'); + expect(request.uri.toString(), 'https://runs.example/v1/shares'); + expect( + answer, + isA>() + .having((d) => d.value.code, 'code', 'ABCDEFG') + .having((d) => d.value.status, 'status', ShareStatus.pending), + ); + }); + + test('a status this build does not know is a refusal', () async { + final answer = await _service( + _Wire(201, '{"code":"ABCDEFG","address":"ff","status":"expired"}'), + ).share(ShareBundle(game: 'walk', level: _level())); + + expect(answer, isA>()); + }); + + test( + 'a code that is not open yet says so in the server\'s words', + () async { + final answer = await _service( + _Wire(200, '{"status":"pending","message":"waiting for review"}'), + ).open('ABCDEFG'); + + expect( + answer, + isA>().having( + (r) => r.reason, + 'reason', + 'waiting for review', + ), + ); + }, + ); + + test('a report carries the reason to the code\'s own route', () async { + final wire = _Wire(200, '{"message":"noted"}'); + + await _service(wire).report('ABC DEF', 'not a level'); + + final request = wire.sent.single; + expect( + request.uri.toString(), + 'https://runs.example/v1/shares/ABC%20DEF/reports', + ); + expect(jsonDecode(request.body!), { + 'reason': 'not a level', + }); + }); + }); + + group('the transport failing', () { + test('an unreachable server is a refusal with a sentence', () async { + final service = RunService( + base: Uri.parse('https://runs.example/'), + transport: (_) async => throw StateError('offline'), + ); + + final answer = await service.open('ABCDEFG'); + + expect( + answer, + isA>().having( + (r) => r.reason, + 'reason', + contains('could not reach'), + ), + ); + }); + + test('a reply that is not JSON is a refusal, not a throw', () async { + final answer = await _service( + _Wire(502, ''), + ).share(ShareBundle(game: 'walk', level: _level())); + + expect( + answer, + isA>().having( + (r) => r.reason, + 'reason', + contains('not JSON'), + ), + ); + }); + }); +} diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 35a5000d4..3c2cefc3a 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11224 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11234 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 546f85625..be1da9c15 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,17 +1,17 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11224 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11234 tests need a GPU. --- # Testing -11224 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11234 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| | `flutter3d` | 1642 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | -| `flutter3d_sim` | 681 | | `pad_input` | 67 | +| `flutter3d_sim` | 691 | | `pad_input` | 67 | | `flutter3d_lab` | 15 | | `flutter3d_core` | 762 | | `flutter3d_lti` | 26 | | `apps/flutter3d_lab_pendulum` | 7 | | | | | `apps/flutter3d_lab_incident` | 13 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11198 rather than 11224: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11208 rather than 11234: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 2e2e5b345..005ff6f06 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -859,3 +859,24 @@ the client side and the formats; `cloud/` gets a reference server anyone can run, and the demos on the site use our instance (decision 23). Moderation of shared levels and consent for telemetry are that server's job, and the client never uploads without an explicit opt-in. + +*Done, as code and tests, deployed nowhere:* the format and the client are +in `flutter3d_sim`. `ShareBundle` holds a level document, its hash and +optionally a `.f3drun`, and a run recorded in another version of the level +is refused. `RunService` speaks the `v1/shares` routes over a transport the +game hands in. The server half is in `cloud/server`, beside N7's telemetry +and in the same shape: a pure `ShareService`, a store in memory and one in +Postgres (migration 005), routes under `/api/v1/shares` and +`/api/v1/moderation`. A bundle is filed by the SHA-256 of the bytes the +server writes and gets a short Crockford code derived from it. Moderation +starts in `review` (nothing opens until a moderator publishes it) or +`open` (enough reports send it back to review); a removal needs a reason and +the code answers with it. In `review` with no moderator token the server +refuses shares rather than keeping what nobody could publish. Telemetry is +N7's and is not duplicated here. +*Tests:* the format and the client in `flutter3d_sim`; the routes with +`RunService` against the real handler in `cloud/server`, and the Postgres +store under the `db` tag. +*Left:* deploying our instance; the web viewer for a code and ghosts from +pose tracks (N10); a game that actually shares, which is where the client +is first used outside tests. From 1efddf565fcd906a410bc74022fb2fb2ea5285b0 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 14:12:07 +0300 Subject: [PATCH 052/141] A material channel can be shown in place of the light, beside it RenderSettings.debugView shows albedo, the shading normal, roughness, metalness, occlusion, emission, the UV, or magenta where the light came out NaN or infinite, over the whole frame or right of a split, so one draw wipes between the light and the channel. The lit models write the channel themselves through WriteDebugView, so it shows what the maps did; the composite passes that side through its encode alone, with no exposure, curve or grade. Reflections and probes clear it. Off by default; debug-view-split is in all four golden sets. FrameResult.targetBytes says what the targets a frame touched hold, each texture once, without a capture asked for. The roadmap's renderer table had rows twice over from earlier merges; it has each once, with P1 to P6 marked done. --- ARCHITECTURE.md | 8 +- README.md | 4 +- .../example/lib/src/spike/backend_web.dart | 2 +- .../example/lib/src/spike/golden_runner.dart | 2 +- .../example/lib/src/spike/golden_scenes.dart | 9 + .../example/lib/src/spike/golden_stages.dart | 13 + .../lib/src/spike/golden_store_web.dart | 4 +- packages/flutter3d/test/ambient_test.dart | 2 +- packages/flutter3d/test/anisotropy_test.dart | 4 +- packages/flutter3d/test/auto_batch_test.dart | 4 +- packages/flutter3d/test/fog_test.dart | 2 +- .../test/goldens/debug-view-split.png | Bin 0 -> 25801 bytes packages/flutter3d/test/grade_test.dart | 2 +- packages/flutter3d/test/halation_test.dart | 2 +- .../test/irradiance_bounce_test.dart | 2 +- .../flutter3d/test/masked_shadow_test.dart | 2 +- .../test/material_vertex_stage_test.dart | 2 +- .../test/per_view_exposure_test.dart | 2 +- .../flutter3d/test/render_scale_test.dart | 2 +- packages/flutter3d/test/sharpen_test.dart | 2 +- .../flutter3d/test/sky_procedural_test.dart | 2 +- packages/flutter3d/test/ssao_blur_test.dart | 2 +- packages/flutter3d_core/CHANGELOG.md | 15 + .../src/engine/render/frame_resources.dart | 45 +- .../lib/src/engine/render/frame_result.dart | 10 + .../src/engine/render/render_settings.dart | 114 +- .../lib/src/engine/render/renderer.dart | 3 +- .../engine/render/renderer_planar_pass.dart | 3 + .../src/engine/render/renderer_post_pass.dart | 9 +- .../engine/render/renderer_probe_pass.dart | 3 + .../engine/render/renderer_scene_pass.dart | 10 + .../engine/render/renderer_shadow_pass.dart | 2 +- .../formats/ktx2_supercompression_test.dart | 2 +- packages/flutter3d_cpu/CHANGELOG.md | 4 + .../lib/src/cpu_shaders_impostor.dart | 2 + .../lib/src/cpu_shaders_lit.dart | 10 + .../lib/src/cpu_shaders_post.dart | 21 +- .../lib/src/cpu_shaders_surface.dart | 83 + .../flutter3d_cpu/lib/src/cpu_texture.dart | 2 +- .../flutter3d_cpu/lib/src/portable_log2.dart | 2 +- .../test/cross_backend_test.dart | 1 + .../flutter3d_cpu/test/debug_view_test.dart | 145 + .../test/goldens/debug-view-split.png | Bin 0 -> 25801 bytes .../flutter3d_cpu/test/render_stats_test.dart | 97 + .../test/gpu_formats_test.dart | 2 +- packages/flutter3d_shaders/CHANGELOG.md | 6 + .../flutter3d_shaders/lib/typed_blocks.dart | 4 + .../flutter3d_shaders/lib/uniform_blocks.dart | 8 + .../flutter3d_shaders/shaders/lib/pbr.glsl | 18 +- .../shaders/lib/surface.glsl | 69 + .../shaders/lighting/blinn_phong.frag | 7 +- .../shaders/lighting/impostor.frag | 4 +- .../shaders/lighting/lambert.frag | 7 +- .../shaders/lighting/toon.frag | 9 +- .../shaders/lighting/unlit.frag | 1 + .../shaders/post/composite.frag | 18 +- packages/flutter3d_webgl/CHANGELOG.md | 3 + .../flutter3d_webgl/lib/engine_shaders.dart | 634 +- .../test/cross_backend_test.dart | 4 + .../test/goldens/debug-view-split.png | Bin 0 -> 26206 bytes .../test/lighting_models_test.dart | 2 +- packages/flutter3d_webgl/tool/golden_web.sh | 2 +- packages/flutter3d_webgpu/CHANGELOG.md | 3 + .../flutter3d_webgpu/lib/engine_shaders.dart | 31159 ++++++++-------- .../test/cross_backend_test.dart | 1 + .../test/goldens/debug-view-split.png | Bin 0 -> 25814 bytes site/content/core/architecture.md | 2 +- site/content/core/index.md | 2 +- site/content/core/rendering.md | 2 +- site/content/quickstart.md | 2 +- site/content/reference/packages.md | 2 +- site/content/reference/testing.md | 12 +- tasks/0.9-engine-roadmap.md | 55 +- tool/ci.sh | 2 +- 74 files changed, 17532 insertions(+), 15165 deletions(-) create mode 100644 packages/flutter3d/test/goldens/debug-view-split.png create mode 100644 packages/flutter3d_cpu/test/debug_view_test.dart create mode 100644 packages/flutter3d_cpu/test/goldens/debug-view-split.png create mode 100644 packages/flutter3d_cpu/test/render_stats_test.dart create mode 100644 packages/flutter3d_webgl/test/goldens/debug-view-split.png create mode 100644 packages/flutter3d_webgpu/test/goldens/debug-view-split.png diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0fc449395..9c49e63c1 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11234 tests** across 43 packages and 10 applications | +| Unit tests | **11243 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | @@ -2731,7 +2731,7 @@ material parameter — is the case `doc/boundary-0.5.0.md` worked through for set belongs to whoever builds on the package rather than to the package itself. -**Golden render tests.** 86 scenes against **four complete independent +**Golden render tests.** 87 scenes against **four complete independent reference sets** — Impeller, the software rasteriser, WebGL2 and WebGPU — each held to zero differing pixels against its own set, with a per-channel tolerance of 8. @@ -3143,7 +3143,7 @@ metres. The directional light's cascades fit the view up to that distance and nothing beyond it casts — a level whose far end matters visually wants the number raised, and pays for it in texels. -**The web backend draws all eighty-six golden scenes the way Impeller does**, +**The web backend draws all eighty-seven golden scenes the way Impeller does**, between 0.01% and 0.42% of pixels differing by more than 8 per channel — the silhouette's worth of disagreement two rasterisers always have. Two of those numbers fell when the minification filter learned to read a sampler's @@ -3259,7 +3259,7 @@ and charge each of them the bytes, and neither is a change to make on somebody else's behalf. A build that wants it says so in one flag, and the engine's own example takes the same answer from `?backend=webgpu` in the URL — a query parameter rather than a define, because the browser golden stand's whole saving -is one dart2js run serving eighty-six scenes and both browser backends. +is one dart2js run serving eighty-seven scenes and both browser backends. **`flutter3d_shaders` is one text and no two backends take it the same way.** Impeller compiles the GLSL with `impellerc`; the WebGL2 generator translates it diff --git a/README.md b/README.md index 03b7026dc..6e3060dfd 100644 --- a/README.md +++ b/README.md @@ -158,9 +158,9 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11234 tests across forty-three packages and nine applications. The +There are 11243 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other -half is rendered by the software backend, which is what makes 86 scenes +half is rendered by the software backend, which is what makes 87 scenes checkable in a headless run. Several steps run in a browser. `flutter test --platform chrome` covers the two diff --git a/packages/flutter3d/example/lib/src/spike/backend_web.dart b/packages/flutter3d/example/lib/src/spike/backend_web.dart index 4c966c993..7ddfdcad3 100644 --- a/packages/flutter3d/example/lib/src/spike/backend_web.dart +++ b/packages/flutter3d/example/lib/src/spike/backend_web.dart @@ -13,7 +13,7 @@ import 'golden_store.dart'; /// /// Read from the URL, exactly the way the golden scene and the record/compare /// direction are read, and for exactly the same arithmetic: one dart2js run -/// serves eighty-six scenes because the scene is a query parameter, and one +/// serves eighty-seven scenes because the scene is a query parameter, and one /// dart2js run serves the browser backends because this is one too. A /// `--dart-define` here would be a build per backend, which is the whole saving /// of the browser golden stand spent on a single word. diff --git a/packages/flutter3d/example/lib/src/spike/golden_runner.dart b/packages/flutter3d/example/lib/src/spike/golden_runner.dart index 8385776cb..923b290c3 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_runner.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_runner.dart @@ -122,7 +122,7 @@ final class GoldenRunner { scene, // The store's answer wins where it has one. A desktop run takes the // direction from the environment and a browser run from the URL, for the - // same reason on both: one build has to serve eighty-six scenes in both + // same reason on both: one build has to serve eighty-seven scenes in both // directions, and anything the compiler sees is another build. update: updateOverride ?? diff --git a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart index c0d0e5ce3..b29e2451f 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart @@ -1221,6 +1221,15 @@ final List kGoldenScenes = [ stage: GoldenStages.skyPhysicalNight, configure: GoldenStages.skyPhysicalNightSettings, ), + + // P6. `shadow-teapot` wiped at the middle: lit on the left, the shading + // normal on the right. + const GoldenScene( + name: 'debug-view-split', + source: 'obj: Teapot', + bloom: false, + configure: GoldenStages.debugViewSplit, + ), ]; /// Looks up a scene by name. diff --git a/packages/flutter3d/example/lib/src/spike/golden_stages.dart b/packages/flutter3d/example/lib/src/spike/golden_stages.dart index 319823ea6..30721de57 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_stages.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_stages.dart @@ -1170,6 +1170,19 @@ abstract final class GoldenStages { static RenderSettings lensDistortion(RenderSettings settings) => settings.copyWith(look: settings.look.copyWith(distortion: 0.3)); + // ------------------------------------------------------------------ P6 + + /// `debug-view-split`: the lit teapot left of the middle and its shading + /// normal right of it, in one draw — the wipe `DebugViewSettings.split` + /// makes, and the composite leaving the right half out of the tone curve. + static RenderSettings debugViewSplit(RenderSettings settings) => + settings.copyWith( + debugView: const DebugViewSettings( + view: DebugView.normal, + split: 0.5, + ), + ); + // ------------------------------------------------------------------ R6 /// `motion-blur-spin`: a wheel of three spokes round a hub, turning at a diff --git a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart index abb92a617..e62327b92 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart @@ -40,7 +40,7 @@ void reportLine(String message) { /// The scene named in the page's URL, if any. /// /// A run-time choice here, where the desktop path takes a compile-time define. -/// The reason is arithmetic: the suite is eighty-six scenes, and rebuilding +/// The reason is arithmetic: the suite is eighty-seven scenes, and rebuilding /// the bundle for each is seventy-eight dart2js runs to compare seventy-eight /// pictures. One build and seventy-eight navigations is the same information /// in a fraction of the time. @@ -58,7 +58,7 @@ String? get sceneOverride { /// Whether this run records rather than compares, from the page's URL. /// /// A run-time choice for the same reason [sceneOverride] is one: the suite is -/// eighty-six scenes and rebuilding for each would be seventy-eight dart2js +/// eighty-seven scenes and rebuilding for each would be seventy-eight dart2js /// runs. One build serves both directions, and the URL says which. bool get updateOverride => Uri.base.queryParameters['update'] == '1'; diff --git a/packages/flutter3d/test/ambient_test.dart b/packages/flutter3d/test/ambient_test.dart index 349195e1b..e25a66e6b 100644 --- a/packages/flutter3d/test/ambient_test.dart +++ b/packages/flutter3d/test/ambient_test.dart @@ -141,7 +141,7 @@ void main() { // approximately, bit for bit. // // Mutation: default either end of the hemisphere to anything but white. - // Eighty-six goldens move at once. + // Eighty-seven goldens move at once. final lit = await _pixels(_ball(), _noSky); final top = _band(lit, 0.15, 0.35); final bottom = _band(lit, 0.65, 0.85); diff --git a/packages/flutter3d/test/anisotropy_test.dart b/packages/flutter3d/test/anisotropy_test.dart index f16541d01..0666050c6 100644 --- a/packages/flutter3d/test/anisotropy_test.dart +++ b/packages/flutter3d/test/anisotropy_test.dart @@ -24,7 +24,7 @@ /// which spends the taps and throws away what they bought — contrast drops /// *below* the trilinear baseline and the first assertion fails; and letting /// a sampler that asked for one reach the new path at all, which would move a -/// backend eighty-six golden scenes are recorded on. +/// backend eighty-seven golden scenes are recorded on. /// /// **One they do not catch, said rather than hidden:** taking the taps along /// the *short* axis instead of the long one passes every line here. The @@ -213,7 +213,7 @@ void main() { }); test('asking for one tap is the old sampler exactly', () async { - // What makes this safe to add to the backend eighty-six golden scenes are + // What makes this safe to add to the backend eighty-seven golden scenes are // recorded on. Not "close": the same bytes, because with `anisotropy` at // one the new path is not reached at all. expect(await _floorAt(1), await _floorAt(1)); diff --git a/packages/flutter3d/test/auto_batch_test.dart b/packages/flutter3d/test/auto_batch_test.dart index 64ae42457..9abd00adf 100644 --- a/packages/flutter3d/test/auto_batch_test.dart +++ b/packages/flutter3d/test/auto_batch_test.dart @@ -22,7 +22,7 @@ /// three backends.** It computes in Dart doubles; Impeller, WebGL and WebGPU /// compute in 32-bit floats, where those two expressions have far less room /// before they part. Nothing here can run them, so the setting stays off by -/// default — which is also what keeps the eighty-six goldens where they are. +/// default — which is also what keeps the eighty-seven goldens where they are. library; import 'dart:math' as math; @@ -191,7 +191,7 @@ void main() { }); test('nothing is batched unless it is asked for', () async { - // Which is what keeps the eighty-six goldens where they are: the default + // Which is what keeps the eighty-seven goldens where they are: the default // frame is the frame it always was, to the byte. final device = _device(); final scene = _field(device, count: 100); diff --git a/packages/flutter3d/test/fog_test.dart b/packages/flutter3d/test/fog_test.dart index e4c9e1406..73bc1b6d7 100644 --- a/packages/flutter3d/test/fog_test.dart +++ b/packages/flutter3d/test/fog_test.dart @@ -151,7 +151,7 @@ void main() { test('no fog is byte-identical to the fog nobody asked for', () async { // The early return at zero density is not an optimisation. The golden sets - // — eighty-six scenes, zero-pixel threshold, three backends — are all + // — eighty-seven scenes, zero-pixel threshold, three backends — are all // recorded with the default `FogSettings()`, and this is the property that // lets them stay recorded. final none = FogSettings(); diff --git a/packages/flutter3d/test/goldens/debug-view-split.png b/packages/flutter3d/test/goldens/debug-view-split.png new file mode 100644 index 0000000000000000000000000000000000000000..274a31f94cd2b89f8e1a8e9e47c51817974e9971 GIT binary patch literal 25801 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 _ramp(LookSettings look, {int width = 256}) async { void main() { test('every default is an exact identity', () async { - // The promise eighty-six goldens rest on. Neutral for these three is + // The promise eighty-seven goldens rest on. Neutral for these three is // (0,0,0) added, (1,1,1) as an exponent and (1,1,1) multiplied, and the // composite has to take all three without moving a byte. final plain = await _ramp(const LookSettings()); diff --git a/packages/flutter3d/test/halation_test.dart b/packages/flutter3d/test/halation_test.dart index fe11cec0b..bfb5940e6 100644 --- a/packages/flutter3d/test/halation_test.dart +++ b/packages/flutter3d/test/halation_test.dart @@ -87,7 +87,7 @@ void main() { test('zero is an exact identity, and is the default', () async { // The multiplier is one on every channel at zero, so the chain comes out - // byte for byte as it did — which is what eighty-six goldens need. + // byte for byte as it did — which is what eighty-seven goldens need. expect(const BloomSettings().halation, 0.0); final a = await _glow(halation: 0.0); final b = await _glow(halation: 0.0); diff --git a/packages/flutter3d/test/irradiance_bounce_test.dart b/packages/flutter3d/test/irradiance_bounce_test.dart index 9ba28c138..c383c26f4 100644 --- a/packages/flutter3d/test/irradiance_bounce_test.dart +++ b/packages/flutter3d/test/irradiance_bounce_test.dart @@ -136,7 +136,7 @@ double _tint(Uint8List rgba) { void main() { test('off, the picture is the bytes it was', () async { // The clause every recorded frame depends on. A field is null by default, - // so this is the path eighty-six goldens take. + // so this is the path eighty-seven goldens take. final a = await _draw(field: false, redWall: true); final b = await _draw(field: false, redWall: true); expect(a, orderedEquals(b)); diff --git a/packages/flutter3d/test/masked_shadow_test.dart b/packages/flutter3d/test/masked_shadow_test.dart index bee52e65d..6074eadd4 100644 --- a/packages/flutter3d/test/masked_shadow_test.dart +++ b/packages/flutter3d/test/masked_shadow_test.dart @@ -223,7 +223,7 @@ void main() { test('a caster with no map keeps the shadow it always cast', () async { // **The half that lets this land.** A material that is not cut out goes // through the stage it has always gone through, with no sampler in the - // pipeline and no texture bound per draw, so the eighty-six goldens + // pipeline and no texture bound per draw, so the eighty-seven goldens // recorded against that stage cannot move. Asking for MASK with no map to // read is the same case: there is nothing to cut out. final plain = await _frame(masked: false, withTexture: false); diff --git a/packages/flutter3d/test/material_vertex_stage_test.dart b/packages/flutter3d/test/material_vertex_stage_test.dart index 5b39b8bf7..da90b63ce 100644 --- a/packages/flutter3d/test/material_vertex_stage_test.dart +++ b/packages/flutter3d/test/material_vertex_stage_test.dart @@ -172,7 +172,7 @@ void main() { // **The half that lets this land**, and the reason the seam is a null field // rather than a new required one: a material saying nothing about its vertex // stage goes through the pipeline it has always gone through, which is also - // why the eighty-six goldens cannot move. + // why the eighty-seven goldens cannot move. final withSeam = await _draw(lighting: _plain); final device = CpuDevice( diff --git a/packages/flutter3d/test/per_view_exposure_test.dart b/packages/flutter3d/test/per_view_exposure_test.dart index f7fd07c01..ab4b8fe3b 100644 --- a/packages/flutter3d/test/per_view_exposure_test.dart +++ b/packages/flutter3d/test/per_view_exposure_test.dart @@ -157,7 +157,7 @@ void main() { // **The strongest form of "this costs nothing where it is not wanted".** // One view is the whole frame, so its rectangle is the whole histogram // and its exposure is the frame's. The composite takes its single-draw - // path, and eighty-six goldens stay where they are. + // path, and eighty-seven goldens stay where they are. final off = await frame( const RenderSettings(autoExposure: AutoExposureSettings(enabled: true)), ); diff --git a/packages/flutter3d/test/render_scale_test.dart b/packages/flutter3d/test/render_scale_test.dart index 5fa871466..4f9434d9e 100644 --- a/packages/flutter3d/test/render_scale_test.dart +++ b/packages/flutter3d/test/render_scale_test.dart @@ -36,7 +36,7 @@ import 'package:vector_math/vector_math.dart'; void main() { test('one is the whole resolution, and is the default', () { - // Eighty-six goldens are recorded at the size they asked for. A default + // Eighty-seven goldens are recorded at the size they asked for. A default // that shrank anything would move every one of them. expect(const RenderSettings().renderScale, 1.0); expect(_drawn(1.0), (width: 64, height: 64)); diff --git a/packages/flutter3d/test/sharpen_test.dart b/packages/flutter3d/test/sharpen_test.dart index 672474a9a..d519fcd73 100644 --- a/packages/flutter3d/test/sharpen_test.dart +++ b/packages/flutter3d/test/sharpen_test.dart @@ -95,7 +95,7 @@ int _steepest(List row) { void main() { test('zero is an exact identity, and is the default', () async { - // Eighty-six goldens go through this pass. The shader returns the centre + // Eighty-seven goldens go through this pass. The shader returns the centre // untouched at zero rather than running a kernel that rounds to nothing, // because "rounds to nothing" is a claim about the target's bit depth. expect(const AntiAliasSettings().sharpen, 0.0); diff --git a/packages/flutter3d/test/sky_procedural_test.dart b/packages/flutter3d/test/sky_procedural_test.dart index 3c086e0fb..53e485e12 100644 --- a/packages/flutter3d/test/sky_procedural_test.dart +++ b/packages/flutter3d/test/sky_procedural_test.dart @@ -167,7 +167,7 @@ double _mean(Uint8List rgba, {required int channel}) { void main() { _hardEdgedSun(); test('no sky is byte-identical to the sky nobody asked for', () async { - // The property eighty-six goldens rest on: a frame with no sky in it is + // The property eighty-seven goldens rest on: a frame with no sky in it is // the frame this renderer has always drawn. // // Mutation: drop the early return. The sky is drawn into every scene that diff --git a/packages/flutter3d/test/ssao_blur_test.dart b/packages/flutter3d/test/ssao_blur_test.dart index 6d8ae33c4..64b236e92 100644 --- a/packages/flutter3d/test/ssao_blur_test.dart +++ b/packages/flutter3d/test/ssao_blur_test.dart @@ -75,7 +75,7 @@ void main() { test('no taps is no pass, and is the default', () async { // The occlusion is already off by default, and this is a second // full-screen pass over it — so nothing pays for either until two things - // are switched on. Eighty-six goldens depend on the frame being what it + // are switched on. Eighty-seven goldens depend on the frame being what it // was. expect(const AmbientOcclusionSettings().blurTaps, 0); expect(await _frame(blurTaps: 0), await _frame(blurTaps: 0)); diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index 8637090aa..415e07cfc 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -1,5 +1,20 @@ ## Unreleased +- **A material channel in place of the light, over all or part of the + frame.** `RenderSettings.debugView` takes a `DebugViewSettings`: a + `DebugView` — albedo, the shading normal, roughness, metalness, + occlusion, emission, the UV, or magenta where the light came out NaN or + infinite — and a `split`, the share of the width left lit, so one draw + wipes between the light and the channel. The lit models write the channel + themselves, so it shows what the maps did; the composite leaves that side + out of the exposure, the curve and the grade. Off by default and an exact + no-op; `debug-view-split` is in all four golden sets. + +- **`FrameResult.targetBytes`** says what the targets a frame drew into or + read from hold, each texture once, without a capture asked for. + `textureBytes` is how one is counted: its base level, every slice and + sample. + - **The lens: distortion, flare, and tables from a grading tool.** `LookSettings.distortion` bends the frame radially, barrel above nought and pincushion below, held on its border, and everything laid over the diff --git a/packages/flutter3d_core/lib/src/engine/render/frame_resources.dart b/packages/flutter3d_core/lib/src/engine/render/frame_resources.dart index 06753c993..23673f7cc 100644 --- a/packages/flutter3d_core/lib/src/engine/render/frame_resources.dart +++ b/packages/flutter3d_core/lib/src/engine/render/frame_resources.dart @@ -109,11 +109,24 @@ final class FrameResources { /// otherwise the source's. TextureHandle _acquire(RenderTargetSpec spec) { final free = _reusable[spec]; - return free != null && free.isNotEmpty + final texture = free != null && free.isNotEmpty ? free.removeLast() : source.acquire(spec); + _seen.add(texture); + return texture; } + /// Every texture this frame's nodes drew into or were handed, once each. + final Set _seen = Set.identity(); + + /// What the targets this frame touched hold, in bytes — `P6`, + /// `FrameResult.targetBytes`: the pooled scratch the nodes took and every + /// texture a node provided, the long-lived ones (the scene's colour, the + /// shadow atlas) among them, counted once however many versions stood on + /// one. See [textureBytes] for what one texture is counted as. + int get targetBytes => + _seen.fold(0, (int sum, TextureHandle t) => sum + textureBytes(t)); + /// Ends a texture's lifetime in this frame. /// /// The newest version of a frame output is never lent again: it is read @@ -224,6 +237,7 @@ final class FrameResources { /// in nobody else's hands, and once the key names the provided texture no /// retirement would ever find it again. void provide(ResourceId id, TextureHandle texture) { + _seen.add(texture); final key = ResourceVersion(id, _writeVersionFor(id)); final replaced = _live[key]; // **The node's own scratch, handed in as its output, is still pooled** — @@ -531,3 +545,32 @@ final class FrameResources { int _writeVersionFor(ResourceId id) => _node < 0 ? 0 : graph.writeVersionOf(_node, id) ?? 0; } + +/// What [texture] holds, in bytes: its base level, every slice, every +/// sample — `P6`. +/// +/// **An estimate a driver would round up, never down.** A device pads rows +/// and keeps tiles of its own, and none of the four backends says by how +/// much; what this counts is what the texels themselves need, the number a +/// budget is set against. A mip chain is not counted, because a handle does +/// not say it has one: the engine's own targets have none, and the pictures +/// it samples are not targets. +int textureBytes(TextureHandle texture) { + final perTexel = switch (texture.format) { + TextureFormat.a8UNormInt || + TextureFormat.r8UNormInt || + TextureFormat.s8UInt => 1, + TextureFormat.r8g8UNormInt => 2, + TextureFormat.r16g16b16a16Float || TextureFormat.d32FloatS8UInt => 8, + TextureFormat.r32g32b32a32Float => 16, + // Four bytes: the eight-bit colours, a float channel, depth with stencil. + // A compressed format is never a target and is counted as the colour it + // decodes to. + _ => 4, + }; + return texture.width * + texture.height * + texture.sliceCount * + texture.sampleCount * + perTexel; +} diff --git a/packages/flutter3d_core/lib/src/engine/render/frame_result.dart b/packages/flutter3d_core/lib/src/engine/render/frame_result.dart index 325dcce00..bde00deb0 100644 --- a/packages/flutter3d_core/lib/src/engine/render/frame_result.dart +++ b/packages/flutter3d_core/lib/src/engine/render/frame_result.dart @@ -123,6 +123,7 @@ final class FrameResult { this.shadowsDenied = 0, this.batchedDraws = 0, this.wireframeDeclined = false, + this.targetBytes = 0, this.exposure = RenderSettings.defaultExposure, this.passes = const [], this.skipped = const [], @@ -259,6 +260,15 @@ final class FrameResult { /// [lightsDropped] is: a setting that did nothing should say so. final bool wireframeDeclined; + /// What the targets this frame drew into or read from hold, in bytes — + /// `P6`. Each texture once, its base level, every slice and sample: the + /// scene's colour and depth, the surface buffer, the glow's chain, the + /// shadow atlas, every scratch target a pass took from the pool. What a + /// frame costs in memory before a driver's padding, and what grows when a + /// render scale or an effect is turned up — `textureBytes` says how one is + /// counted. + final int targetBytes; + /// Pipelines the renderer has built so far. /// /// Reported per frame because it is the number that has to stay put: light diff --git a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart index e7ba2de53..d57645d4d 100644 --- a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart +++ b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart @@ -1010,6 +1010,109 @@ final class ViewportShadingSettings { ); } +/// What a debug view shows in place of the light — `P6`. +/// +/// **A final class with const instances rather than an enum**, for the reason +/// [ViewportShading] is one: [code] is what `FragInfo.debug_view.x` carries +/// to four backends, a number this type owns. +final class DebugView { + const DebugView._(this.name, this.code); + + /// The name it is written down as. + final String name; + + /// What goes into `FragInfo.debug_view.x`. Part of the shader contract. + final double code; + + /// The lit picture, untouched — the default, and an exact no-op. + static const DebugView off = DebugView._('off', 0.0); + + /// The base colour after its texture, tint and vertex colour, as the sRGB + /// colour a texture is painted in. + static const DebugView albedo = DebugView._('albedo', 1.0); + + /// The shading normal, after the normal map, as `n · 0.5 + 0.5` in world + /// space — the normal map's work, which the surface buffer's geometric + /// normal does not show. + static const DebugView normal = DebugView._('normal', 2.0); + + /// Perceptual roughness as a grey, after the metal-rough map. + static const DebugView roughness = DebugView._('roughness', 3.0); + + /// Metalness as a grey, after the metal-rough map. + static const DebugView metallic = DebugView._('metallic', 4.0); + + /// The occlusion map's share of light left, as a grey. + static const DebugView occlusion = DebugView._('occlusion', 5.0); + + /// Emission as its sRGB colour, clipped at one. + static const DebugView emissive = DebugView._('emissive', 6.0); + + /// The base colour map's coordinate, its fraction in red and green: a seam + /// is a step in colour, a stretched island a long gradient. + static const DebugView uv = DebugView._('uv', 7.0); + + /// Magenta wherever the light came out NaN or infinite, over the light's + /// own luminance in a dark grey — where a bad normal, a zero-length vector + /// or a division by nought turned shading into nothing. + static const DebugView nonFinite = DebugView._('nonFinite', 8.0); + + /// All of them, off first. + static const List values = [ + off, + albedo, + normal, + roughness, + metallic, + occlusion, + emissive, + uv, + nonFinite, + ]; + + @override + String toString() => 'DebugView.$name'; +} + +/// A material channel shown in place of the light, over all or part of the +/// frame — `P6`. +/// +/// **Written by the materials, not read back from a buffer**, which is what +/// sets this apart from [ViewportShadingSettings]: the surface buffer keeps a +/// geometric normal, a roughness and a depth, and the channels a look-dev +/// question is about — an albedo too bright, a metalness map read as sRGB, a +/// seam in the UVs — exist only inside the lit stage. Each lit model asks +/// `WriteDebugView` before it writes its light. A stage that is not a lit +/// material — the sky, particles, splats, glass's own pass — draws as it +/// always does, and right of the split it reaches the screen without the +/// tone curve, so it reads brighter there than it is. +/// +/// **The split is a wipe.** Left of [split] the frame is lit as ever, right +/// of it the channel, in one draw: a material shows where its map and its +/// light disagree without two captures to line up. Nought is the whole frame. +/// +/// **Passes between the materials and the composite still run.** Reflections, +/// depth of field, motion blur and a temporal resolve work on whatever the +/// scene holds, so turn them off for a clean read of a channel. The +/// composite's exposure, curve, bloom and grade do not touch the debug side. +final class DebugViewSettings { + const DebugViewSettings({this.view = DebugView.off, this.split = 0.0}); + + /// Which channel. [DebugView.off] is the default and an exact no-op. + final DebugView view; + + /// Where the debug view starts, as a share of the width from the left: + /// nought the whole frame, a half the right half. Held inside nought and + /// one. + final double split; + + /// Whether a frame drawn with these shows a channel anywhere. + bool get active => view != DebugView.off && split < 1.0; + + DebugViewSettings copyWith({DebugView? view, double? split}) => + DebugViewSettings(view: view ?? this.view, split: split ?? this.split); +} + /// Distance fog, and fog that lies on the ground. /// /// Exponential per metre, which is what the level format already stores. A @@ -1241,6 +1344,7 @@ final class RenderSettings { this.planarReflections = const PlanarReflectionSettings(), this.decals = const DecalSettings(), this.viewportShading = const ViewportShadingSettings(), + this.debugView = const DebugViewSettings(), this.transparency = TransparencyMode.sorted, }) : assert(anisotropy >= 1, 'anisotropy is a count of taps, one or more'), assert(lightFadeBand >= 0.0, 'a fade band is a width, not a direction'); @@ -1358,7 +1462,7 @@ final class RenderSettings { /// eight-bit answer — `auto_batch_test.dart` holds a hundred cubes, turned and /// scaled, to byte equality. Impeller, WebGL and WebGPU compute in 32-bit /// floats, where those expressions have far less room before they part, and - /// nothing headless can run them. So the eighty-six goldens keep the frame + /// nothing headless can run them. So the eighty-seven goldens keep the frame /// they have, and an application that wants the draw calls back asks. /// /// Shadows and picking are unaffected: both walk the scene themselves and @@ -1458,6 +1562,10 @@ final class RenderSettings { /// than out of the materials. final ViewportShadingSettings viewportShading; + /// `P6`'s material channels in place of the light, over all or part of the + /// frame. + final DebugViewSettings debugView; + final FogSettings fog; /// What is behind everything, when there is anything. @@ -1758,6 +1866,7 @@ final class RenderSettings { PlanarReflectionSettings? planarReflections, DecalSettings? decals, ViewportShadingSettings? viewportShading, + DebugViewSettings? debugView, TransparencyMode? transparency, }) => RenderSettings( specular: specular ?? this.specular, @@ -1806,6 +1915,7 @@ final class RenderSettings { planarReflections: planarReflections ?? this.planarReflections, decals: decals ?? this.decals, viewportShading: viewportShading ?? this.viewportShading, + debugView: debugView ?? this.debugView, transparency: transparency ?? this.transparency, ); @@ -2136,7 +2246,7 @@ final class DisplayTransform { /// **Everything here defaults to doing nothing, exactly.** Not nearly nothing: /// a vignette of zero multiplies by one and grain of zero adds zero, so a scene /// that asks for none of it composites to the same bytes it did before this -/// existed. Eighty-six goldens depend on that being exact, and the composite +/// existed. Eighty-seven goldens depend on that being exact, and the composite /// pass already keeps the same promise for ambient occlusion. /// /// Applied in the composite rather than as passes of their own, which is the diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer.dart b/packages/flutter3d_core/lib/src/engine/render/renderer.dart index 30c09fea3..53fcea565 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer.dart @@ -1565,7 +1565,7 @@ final class Renderer implements RenderServices { /// `gfx-76n`'s strength, in x. Neutral is zero, which the composite reads as /// a multiplier of exactly one — the same arrangement the occlusion's - /// strength has, and for the same reason: eighty-six goldens go through this + /// strength has, and for the same reason: eighty-seven goldens go through this /// block and "off" has to be a number the shader cancels, not one it nearly /// cancels. Float32List get _compositeContact => _compositeInfo.contact; @@ -4668,6 +4668,7 @@ final class Renderer implements RenderServices { // are the compile's own answers, and a second derivation is a second // thing to disagree with the frame. skipped: frameGraph.skipped, + targetBytes: resources.targetBytes, ); } diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart index b44416bfb..e37f72b54 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart @@ -93,6 +93,9 @@ extension _PlanarPasses on Renderer { _targetOrigin[1] = 0.0; _targetOrigin[2] = settings.energyCompensation ? 1.0 : 0.0; _targetOrigin[3] = -1.0; + // A reflection shows the light, whatever the frame's debug view: the + // mirror is part of the picture being debugged, not a material in it. + _fragInfo.debugView.fillRange(0, 4, 0.0); pass.setState( Renderer._kSceneViewState.copyWith( viewport: rect, diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart index e345de193..1f0745afb 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart @@ -205,7 +205,7 @@ extension _PostPasses on Renderer { _fxaaParams[3] = settings.blend.clamp(0.0, 1.0); // `gfx-29n`. Zero exactly when nobody asked: the shader returns the // centre untouched at zero rather than running a kernel that rounds to - // nothing, and eighty-six goldens depend on that being the same bytes. + // nothing, and eighty-seven goldens depend on that being the same bytes. // // After a temporal resolve the robust kernel, at the resolve's own // strength — `R2`: what softens a resolved picture is the history, and @@ -1678,7 +1678,7 @@ extension _PostPasses on Renderer { // itself — then one draw per view, scissored to its own rectangle, so the // exposure in the uniform is the one that view metered. With per-view // metering off, or with a single view, this is the one full-frame draw it - // has always been and the bytes are the bytes eighty-six goldens hold. + // has always been and the bytes are the bytes eighty-seven goldens hold. final perView = settings.autoExposure.enabled && settings.autoExposure.perView && @@ -1793,6 +1793,11 @@ extension _PostPasses on Renderer { : settings.localExposure.strength.clamp(0.0, 1.0); // `P2`: the lens's bend, nought off exactly. _compositeInfo.lens[0] = settings.look.distortion; + // `P6`: right of the split the scene holds display values, which the + // composite passes through its encode alone. + final debug = settings.debugView; + _compositeInfo.lens[1] = debug.active ? 1.0 : 0.0; + _compositeInfo.lens[2] = debug.split.clamp(0.0, 1.0); pass.bindTexture( compositeShader, 'local_exposure_texture', diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_probe_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_probe_pass.dart index 3172188b5..cc2d078cc 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_probe_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_probe_pass.dart @@ -284,6 +284,9 @@ extension _ProbePasses on Renderer { _targetOrigin[0] = device.framebufferOrigin == FramebufferOrigin.bottomLeft ? size.toDouble() : 0.0; + // A probe captures light for the materials to reflect; a debug view + // baked into it would outlive the frame that asked for one. + _fragInfo.debugView.fillRange(0, 4, 0.0); final rect = ScreenRect(width: size, height: size); pass.setState( diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart index fa5101dd4..56e40af73 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart @@ -175,6 +175,16 @@ extension _ScenePasses on Renderer { // shadow's taps anew each frame for the history to average; minus one // otherwise, and the turn stays put. _targetOrigin[3] = temporal ? (_frameIndex % 32).toDouble() : -1.0; + // `P6`: the channel a debug view shows in place of the light, and the + // column it starts at — the share of the width times the width of the + // target the materials draw into, which is the scene's and not the + // output's under a render scale. + final debug = settings.debugView; + _fragInfo.debugView + ..[0] = debug.active ? debug.view.code : 0.0 + ..[1] = debug.split.clamp(0.0, 1.0) + ..[2] = hdr.width.toDouble() + ..[3] = 0.0; final cameraPosition = vm.Vector3.zero(); for (var viewNumber = 0; viewNumber < ordered.length; viewNumber++) { diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart index 818e729be..46ef3ead1 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart @@ -1105,7 +1105,7 @@ extension _ShadowPasses on Renderer { final skinned = skeleton != null; // `gfx-60n`. A cut-out caster goes through a stage with a sampler in // it; everything else keeps the stage it has always had, which is why - // the masked half costs the common path nothing and why eighty-six + // the masked half costs the common path nothing and why eighty-seven // goldens recorded against the plain stage cannot move. final masked = maskedShadowShader != shadowShader && _castsMasked(node); final kind = diff --git a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart index d6b2d4f3f..519537ba7 100644 --- a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart +++ b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart @@ -157,7 +157,7 @@ void main() { }); test('an uncompressed file is untouched by any of this', () { - // The path eighty-six goldens and every existing asset take. A level with + // The path eighty-seven goldens and every existing asset take. A level with // no supercompression is handed back as a view on the file's own bytes, // with nothing copied and nothing decoded. final file = buildKtx2( diff --git a/packages/flutter3d_cpu/CHANGELOG.md b/packages/flutter3d_cpu/CHANGELOG.md index c2ba672e3..f99798321 100644 --- a/packages/flutter3d_cpu/CHANGELOG.md +++ b/packages/flutter3d_cpu/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +- **Debug views in software.** `writeDebugView` mirrors `WriteDebugView` + for every lit model and the impostor, and the composite passes the debug + side of the split through its encode alone, as the GLSL does. + - **The lens in software.** The composite bends its coordinate as `composite.frag` does, and `LensFlareShader` mirrors `lens_flare.frag`; `lens-flare` and `lens-distortion` match Impeller's references to the diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_impostor.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_impostor.dart index 377fcf029..041102bf7 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_impostor.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_impostor.dart @@ -254,6 +254,8 @@ final class ImpostorShader implements CpuFragmentShader { // on a back face as every lit stage turns it. final geometric = worldFacing.clone(); if (!c.frontFacing) geometric.negate(); + final debug = writeDebugView(c, v, b, s, total, geometric: geometric); + if (debug != null) return debug; return writeLit( c, v, diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_lit.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_lit.dart index 63a94098c..5c40db92d 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_lit.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_lit.dart @@ -25,6 +25,8 @@ final class UnlitShader implements CpuFragmentShader { // `L5`: reflects no light, as `unlit.frag` says. c.albedo = null; if (s == null) return null; + final debug = writeDebugView(c, v, bindings, s, s.albedo); + if (debug != null) return debug; // Fully rough, which is what WriteSurface's one-argument form means: a // surface that cannot say how polished it is should not be reflected off. return writeLit( @@ -99,6 +101,8 @@ final class LambertShader implements CpuFragmentShader { (s.albedo.clone()..multiply(s.ambient + sampleLightmap(v, b, c))) .scaled(s.occlusion); final total = lit + ambient + s.emissive; + final debug = writeDebugView(c, v, b, s, total); + if (debug != null) return debug; return writeLit( c, v, @@ -147,6 +151,8 @@ final class BlinnPhongShader implements CpuFragmentShader { (s.albedo.clone()..multiply(s.ambient + sampleLightmap(v, b, c))) .scaled(s.occlusion); final total = lit + ambient + s.emissive; + final debug = writeDebugView(c, v, b, s, total); + if (debug != null) return debug; return writeLit( c, v, @@ -978,6 +984,8 @@ final class PbrShader implements CpuFragmentShader { .scaled(through) + coatAmbient, }; + final debug = writeDebugView(c, v, b, s, total, transformed: layered); + if (debug != null) return debug; return writeLit( c, v, @@ -1125,6 +1133,8 @@ final class ToonShader implements CpuFragmentShader { (s.albedo.clone()..multiply(s.ambient + sampleLightmap(v, b, c))) .scaled(s.occlusion); final total = lit + ambient + Vector3.all(rim) + s.emissive; + final debug = writeDebugView(c, v, b, s, total); + if (debug != null) return debug; return writeLit( c, v, diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart index 588a165e0..9dafbc251 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart @@ -44,7 +44,12 @@ final class CompositeShader implements CpuFragmentShader { /// `Distort` in `composite.frag`: radially by `1 + k·r²` on the frame's /// aspect, held on the border — at the corners for a barrel, at the /// nearest edge for a pincushion. - static (double, double) _distort(double u, double w, double k, double aspect) { + static (double, double) _distort( + double u, + double w, + double k, + double aspect, + ) { final a = math.max(aspect, 1e-4); final dx = u - 0.5; final dy = w - 0.5; @@ -79,6 +84,18 @@ final class CompositeShader implements CpuFragmentShader { // over the scene bends with it — `Distort` in `composite.frag`. Nought // leaves the coordinate the very value it was. final lens = bindings.vec4('CompositeInfo', 'lens', Vector4.zero()); + // `P6`: right of a debug view's split the scene holds display values, + // and only the encode touches them. A return here where the GLSL decides + // at its end: this rasteriser has no uniform control flow to keep. + if (lens.y > 0.5 && v[0] >= lens.z) { + final raw = scene.sample(v[0], v[1]); + return Vector4( + toSrgb(raw.x.clamp(0.0, 1.0)), + toSrgb(raw.y.clamp(0.0, 1.0)), + toSrgb(raw.z.clamp(0.0, 1.0)), + raw.w, + ); + } final (u, w) = lens.x != 0.0 ? _distort(v[0], v[1], lens.x, lookMore.w) : (v[0], v[1]); @@ -163,7 +180,7 @@ final class CompositeShader implements CpuFragmentShader { // Skipped at exactly one, which is what both settings off comes to: a // multiply by one is exact, so this is a shortcut rather than a difference, - // and it keeps the frames eighty-six goldens hold untouched by arithmetic + // and it keeps the frames eighty-seven goldens hold untouched by arithmetic // they never used to go through. if (shade != 1.0) colour.scale(shade); diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_surface.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_surface.dart index ee7b74e5e..6d3dd78a3 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_surface.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_surface.dart @@ -500,3 +500,86 @@ void applyCommonMaps( applyOcclusionMap(s, v, b, c, transformed: transformed); applyEmissiveMap(s, v, b, c, transformed: transformed); } + +/// `NonFinite` from `surface.glsl`: NaN or infinite in any channel. +bool nonFinite(Vector3 c) => + c.x.isNaN || + c.y.isNaN || + c.z.isNaN || + c.x.isInfinite || + c.y.isInfinite || + c.z.isInfinite; + +/// `WriteDebugView` from `surface.glsl` — `P6`: the material channel +/// `FragInfo.debug_view` asks for, in place of [lit], or null when no view +/// is on or the fragment sits left of the split, and the caller writes the +/// light. +/// +/// Display values converted to linear, through the surface buffer and the +/// weighted-blended targets as `writeLit` writes them, and without the fog, +/// for the reasons the GLSL gives. [geometric] is the normal the surface +/// buffer takes when the stage hands `writeLit` another than [Surface.normal] +/// — the impostor's card. +Vector4? writeDebugView( + FragmentContext c, + Float32List v, + ShaderBindings b, + Surface s, + Vector3 lit, { + bool transformed = false, + Vector3? geometric, +}) { + final debug = b.vec4('FragInfo', 'debug_view', Vector4.zero()); + if (debug.x < 0.5) return null; + if (c.coord.x < debug.y * debug.z) return null; + Vector3 srgbOf(Vector3 linear) => Vector3( + toSrgb(linear.x.clamp(0.0, 1.0)), + toSrgb(linear.y.clamp(0.0, 1.0)), + toSrgb(linear.z.clamp(0.0, 1.0)), + ); + final shown = switch ((debug.x + 0.5).floor()) { + 1 => srgbOf(s.albedo), + 2 => Vector3( + s.normal.x * 0.5 + 0.5, + s.normal.y * 0.5 + 0.5, + s.normal.z * 0.5 + 0.5, + ), + 3 => Vector3.all(s.roughness.clamp(0.0, 1.0)), + 4 => Vector3.all(s.metallic.clamp(0.0, 1.0)), + 5 => Vector3.all(s.occlusion.clamp(0.0, 1.0)), + 6 => srgbOf(s.emissive), + 7 => () { + final (u: u, v: w, footprint: _) = mapUv( + kMapBaseColor, + v, + b, + c, + transformed: transformed, + ); + return Vector3(fract(u), fract(w), 0.0); + }(), + 8 => + nonFinite(lit) + ? Vector3(1.0, 0.0, 1.0) + : () { + final e = srgbOf(lit); + return Vector3.all( + (0.2126 * e.x + 0.7152 * e.y + 0.0722 * e.z) * 0.5, + ); + }(), + _ => Vector3.zero(), + }; + writeSurface(c, v, b, geometric ?? s.normal, s.roughness); + final weight = premultiplies(b) ? s.alpha : 1.0; + return writeWeightedBlended( + c, + v, + b, + Vector4( + toLinear(shown.x) * weight, + toLinear(shown.y) * weight, + toLinear(shown.z) * weight, + s.alpha, + ), + ); +} diff --git a/packages/flutter3d_cpu/lib/src/cpu_texture.dart b/packages/flutter3d_cpu/lib/src/cpu_texture.dart index 1723733da..596a7ba55 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_texture.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_texture.dart @@ -300,7 +300,7 @@ final class BoundTexture { // from the short one. // // **A sampler that did not ask is untouched**, which is what makes this - // safe to add to a backend eighty-six golden scenes are recorded on: with + // safe to add to a backend eighty-seven golden scenes are recorded on: with // `anisotropy` at one — the default everywhere in this engine — the // arithmetic below is not reached and the bytes are the ones that were // recorded. `anisotropic-floor` is the one scene that asks. diff --git a/packages/flutter3d_cpu/lib/src/portable_log2.dart b/packages/flutter3d_cpu/lib/src/portable_log2.dart index 5e22444e8..9cad43934 100644 --- a/packages/flutter3d_cpu/lib/src/portable_log2.dart +++ b/packages/flutter3d_cpu/lib/src/portable_log2.dart @@ -10,7 +10,7 @@ /// twenty-five five. `anisotropic-floor` is the scene built to sit exactly /// there: 2059 of its 172800 pixels differed between a reference recorded on /// macOS and the same code run on a Linux runner, and nothing else in -/// eighty-six scenes differed at all. +/// eighty-seven scenes differed at all. /// /// **The same rule the simulation already keeps.** `flutter3d_sim`'s /// `Portable` exists for this, and a step in that package is forbidden from diff --git a/packages/flutter3d_cpu/test/cross_backend_test.dart b/packages/flutter3d_cpu/test/cross_backend_test.dart index 3b28c42dd..d1112c396 100644 --- a/packages/flutter3d_cpu/test/cross_backend_test.dart +++ b/packages/flutter3d_cpu/test/cross_backend_test.dart @@ -54,6 +54,7 @@ import 'package:test/test.dart'; /// multisampling on silhouettes, and an additive quad's edge deposits too /// little to cross a channel threshold of eight. const Map _budgets = { + 'debug-view-split': 0.01, // 0.025% measured, down from 0.633%. Screen-space reflections are a march, // and what is left is the two rasterisers disagreeing about where a ray ends // — an edge inside the reflection rather than an edge in the scene. The rest diff --git a/packages/flutter3d_cpu/test/debug_view_test.dart b/packages/flutter3d_cpu/test/debug_view_test.dart new file mode 100644 index 000000000..c5dc04f57 --- /dev/null +++ b/packages/flutter3d_cpu/test/debug_view_test.dart @@ -0,0 +1,145 @@ +/// A material channel in place of the light — `P6`: what the materials write +/// when `RenderSettings.debugView` asks, and the wipe between the two. +/// +/// dart test test/debug_view_test.dart +/// +/// Read through the composite, so every number here is what reaches the +/// screen: a channel has to come out as the value the material holds, with +/// the exposure and the tone curve left off its side of the split. +library; + +import 'dart:typed_data'; + +import 'package:flutter3d_core/flutter3d_core.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +const int _width = 64; +const int _height = 48; + +/// A card filling the middle of the frame, of [material], through +/// [settings], lit by one light from the front. +Float32List _render(RenderSettings settings, {Material? material}) { + final device = CpuDevice( + width: _width, + height: _height, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final card = + MeshNode( + DeviceMesh.upload(device, CuboidShape().build()), + material ?? + Material( + baseColor: Vector4(0.6, 0.3, 0.1, 1.0), + roughness: 0.35, + metallic: 0.8, + ), + ) + ..setPosition(0.0, 0.0, -2.0) + ..setScale(2.4, 1.8, 0.05); + final camera = CameraNode(); + final light = LightNode(intensity: 3.0)..lookAt(Vector3(0.0, 0.0, -1.0)); + final result = Renderer.create(device: device).render( + width: _width, + height: _height, + scene: Scene() + ..add(card) + ..add(light) + ..add(camera), + views: [ + RenderView(camera: camera, clearColor: Vector4(0.0, 0.0, 0.0, 1.0)), + ], + settings: settings, + ); + return Float32List.fromList(device.readHdrPixels(result.frame)); +} + +/// The colour at pixel ([x], [y]). +Vector3 _at(Float32List frame, int x, int y) { + final i = (y * _width + x) * 4; + return Vector3(frame[i], frame[i + 1], frame[i + 2]); +} + +const RenderSettings _plain = RenderSettings( + bloom: BloomSettings(enabled: false), + look: LookSettings(dither: 0.0), +); + +RenderSettings _showing(DebugView view, {double split = 0.0}) => + _plain.copyWith( + debugView: DebugViewSettings(view: view, split: split), + ); + +void main() { + test('off is the picture as it was, to the bit', () { + // Mutation: write `debug_view.x` whatever `active` says, or leave the + // composite's `lens.y` at one, and a frame nobody asked to debug moves. + expect(_render(_showing(DebugView.off)), _render(_plain)); + }); + + test('a channel reaches the screen as the value the material holds', () { + // Mutation: leave the debug side under the exposure or the tone curve in + // `composite.frag`, and the greys come out brighter or flatter than the + // material's own numbers. A doubled exposure is there to show it. + final exposed = _plain.copyWith(exposure: 2.0); + Vector3 centre(DebugView view) => _at( + _render(exposed.copyWith(debugView: DebugViewSettings(view: view))), + _width ~/ 2, + _height ~/ 2, + ); + + // `baseColor` is given in sRGB and the view shows it in sRGB: the + // numbers come back as they went in. + final albedo = centre(DebugView.albedo); + expect(albedo.x, closeTo(0.6, 0.01)); + expect(albedo.y, closeTo(0.3, 0.01)); + expect(albedo.z, closeTo(0.1, 0.01)); + + expect(centre(DebugView.roughness).x, closeTo(0.35, 0.01)); + expect(centre(DebugView.metallic).x, closeTo(0.8, 0.01)); + expect(centre(DebugView.occlusion).x, closeTo(1.0, 0.01)); + expect(centre(DebugView.emissive).x, closeTo(0.0, 0.01)); + + // The card faces the camera, so its normal is +z: (0.5, 0.5, 1). + final normal = centre(DebugView.normal); + expect(normal.x, closeTo(0.5, 0.02)); + expect(normal.y, closeTo(0.5, 0.02)); + expect(normal.z, closeTo(1.0, 0.02)); + }); + + test('the coordinate runs across the card', () { + // Mutation: read `v_world_position` for the UV view, or swap its lanes. + final frame = _render(_showing(DebugView.uv)); + final left = _at(frame, _width ~/ 4, _height ~/ 2); + final right = _at(frame, _width * 3 ~/ 4, _height ~/ 2); + expect(right.x, greaterThan(left.x + 0.1)); + expect(left.z, 0.0); + }); + + test('the split leaves the left lit and shows the channel on the right', () { + // Mutation: compare against the output's width instead of the scene's, + // or let the composite pass the whole frame through. + final lit = _render(_plain); + final wiped = _render(_showing(DebugView.roughness, split: 0.5)); + final y = _height ~/ 2; + expect(_at(wiped, _width ~/ 4, y), _at(lit, _width ~/ 4, y)); + expect(_at(wiped, _width * 3 ~/ 4, y).x, closeTo(0.35, 0.01)); + }); + + test('a NaN in the light shows as magenta, and nothing else does', () { + // Mutation: test the light with `isNaN` on one channel only, or show the + // grey for every fragment, and one of the two cards below goes wrong. + final broken = _render( + _showing(DebugView.nonFinite), + // Emission is added as it is, so a NaN there reaches the light. + material: Material(emissive: Vector3(double.nan, 0.0, 0.0)), + ); + expect(_at(broken, _width ~/ 2, _height ~/ 2), Vector3(1.0, 0.0, 1.0)); + + final sound = _render(_showing(DebugView.nonFinite)); + final grey = _at(sound, _width ~/ 2, _height ~/ 2); + expect(grey.x, grey.y); + expect(grey.x, lessThan(0.5)); + }); +} diff --git a/packages/flutter3d_cpu/test/goldens/debug-view-split.png b/packages/flutter3d_cpu/test/goldens/debug-view-split.png new file mode 100644 index 0000000000000000000000000000000000000000..274a31f94cd2b89f8e1a8e9e47c51817974e9971 GIT binary patch literal 25801 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[RenderView(camera: camera)], + settings: settings, + ); +} + +void main() { + test('a texture is counted as its texels, every slice and sample', () { + // Mutation: leave out the slices, and a cube target is a sixth of itself. + TextureHandle handle(TextureFormat format, {TextureType? type}) => + TextureHandle( + backend: Object(), + width: 8, + height: 4, + format: format, + sampleCount: 2, + type: type ?? TextureType.texture2D, + ); + expect(textureBytes(handle(TextureFormat.r8UNormInt)), 8 * 4 * 2); + expect( + textureBytes(handle(TextureFormat.r16g16b16a16Float)), + 8 * 4 * 2 * 8, + ); + expect( + textureBytes( + handle(TextureFormat.r8g8b8a8UNormInt, type: TextureType.textureCube), + ), + 8 * 4 * 6 * 2 * 4, + ); + }); + + test('a frame says what its targets hold', () { + // Mutation: count only the pooled scratch, and the scene's own colour — + // provided, not pooled — drops out, taking the frame below what one + // half-float target of its size holds. + final frame = _render(const RenderSettings()); + expect(frame.targetBytes, greaterThanOrEqualTo(_width * _height * 8)); + }); + + test('a glow is paid for in targets, and a smaller scene in fewer bytes', () { + // Mutation: count a texture each time a version names it, and the glow's + // chain, read and written in place, is counted twice over. + final plain = _render( + const RenderSettings(bloom: BloomSettings(enabled: false)), + ); + final glowing = _render( + const RenderSettings(bloom: BloomSettings(enabled: true)), + ); + expect(glowing.targetBytes, greaterThan(plain.targetBytes)); + expect( + glowing.targetBytes, + lessThan(plain.targetBytes * 3), + reason: 'a chain of halvings is about a third of its first level', + ); + }); + + test('more cubes are more draws and more triangles', () { + final one = _render(const RenderSettings(), cubes: 1); + final three = _render(const RenderSettings(), cubes: 3); + expect(three.drawCalls, greaterThan(one.drawCalls)); + expect(three.triangles, greaterThanOrEqualTo(one.triangles + 24)); + }); +} diff --git a/packages/flutter3d_impeller/test/gpu_formats_test.dart b/packages/flutter3d_impeller/test/gpu_formats_test.dart index aaa1d8ed0..01b0a5d4d 100644 --- a/packages/flutter3d_impeller/test/gpu_formats_test.dart +++ b/packages/flutter3d_impeller/test/gpu_formats_test.dart @@ -2,7 +2,7 @@ /// /// The hazard this exists for: a wrong mapping compiles, runs, and renders /// wrong only for the values a scene happens to use. The goldens cover the -/// values the eighty-six scenes exercise and nothing else — `BlendFactor` has +/// values the eighty-seven scenes exercise and nothing else — `BlendFactor` has /// fifteen members and the engine draws with two of them — so a mistake in the /// other thirteen would be found by a user rather than by us. /// diff --git a/packages/flutter3d_shaders/CHANGELOG.md b/packages/flutter3d_shaders/CHANGELOG.md index 58e2615c4..7d42330f2 100644 --- a/packages/flutter3d_shaders/CHANGELOG.md +++ b/packages/flutter3d_shaders/CHANGELOG.md @@ -1,5 +1,11 @@ ## Unreleased +- **`FragInfo.debug_view` and `WriteDebugView`** in `surface.glsl` — `P6`: + every lit model asks it before writing its light. NaN is found by + comparison, because `impellerc`'s GLSL ES output has no bit casts. + `CompositeInfo.lens` y and z say whether a debug view is on and where it + starts. + - **`LensFlare`**, with the `LensFlareInfo` block, and a `lens` member appended to `CompositeInfo` for the distortion. - **Three stages for SMAA 1x**: `SmaaEdges`, `SmaaWeights` and `SmaaBlend`, diff --git a/packages/flutter3d_shaders/lib/typed_blocks.dart b/packages/flutter3d_shaders/lib/typed_blocks.dart index 0f896fe1b..e5d3ee2f5 100644 --- a/packages/flutter3d_shaders/lib/typed_blocks.dart +++ b/packages/flutter3d_shaders/lib/typed_blocks.dart @@ -490,6 +490,9 @@ final class FragInfoBlock extends UniformBlock { /// `target_origin`: Vector4, at byte 880. final Float32List targetOrigin = Float32List(4); + /// `debug_view`: Vector4, at byte 896. + final Float32List debugView = Float32List(4); + @override late final Map members = { 'light_position': lightPosition, @@ -511,6 +514,7 @@ final class FragInfoBlock extends UniformBlock { 'ambient_ground': ambientGround, 'shadow_bias': shadowBias, 'target_origin': targetOrigin, + 'debug_view': debugView, }; } diff --git a/packages/flutter3d_shaders/lib/uniform_blocks.dart b/packages/flutter3d_shaders/lib/uniform_blocks.dart index 7db282981..8a6ac8c77 100644 --- a/packages/flutter3d_shaders/lib/uniform_blocks.dart +++ b/packages/flutter3d_shaders/lib/uniform_blocks.dart @@ -119,6 +119,7 @@ uniformBlocks = >>{ elements: 1, type: 'Vector4', ), + 'debug_view': (offset: 896, byteLength: 16, elements: 1, type: 'Vector4'), }, 'IrradianceInfo': { 'origin': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), @@ -434,6 +435,7 @@ uniformBlocks = >>{ elements: 1, type: 'Vector4', ), + 'debug_view': (offset: 896, byteLength: 16, elements: 1, type: 'Vector4'), }, 'LightListInfo': { 'list': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), @@ -588,6 +590,7 @@ uniformBlocks = >>{ elements: 1, type: 'Vector4', ), + 'debug_view': (offset: 896, byteLength: 16, elements: 1, type: 'Vector4'), }, 'IrradianceInfo': { 'origin': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), @@ -1132,6 +1135,7 @@ uniformBlocks = >>{ elements: 1, type: 'Vector4', ), + 'debug_view': (offset: 896, byteLength: 16, elements: 1, type: 'Vector4'), }, 'IrradianceInfo': { 'origin': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), @@ -1274,6 +1278,7 @@ uniformBlocks = >>{ elements: 1, type: 'Vector4', ), + 'debug_view': (offset: 896, byteLength: 16, elements: 1, type: 'Vector4'), }, 'IrradianceInfo': { 'origin': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), @@ -1648,6 +1653,7 @@ uniformBlocks = >>{ elements: 1, type: 'Vector4', ), + 'debug_view': (offset: 896, byteLength: 16, elements: 1, type: 'Vector4'), }, 'IrradianceInfo': { 'origin': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), @@ -1790,6 +1796,7 @@ uniformBlocks = >>{ elements: 1, type: 'Vector4', ), + 'debug_view': (offset: 896, byteLength: 16, elements: 1, type: 'Vector4'), }, }, 'Velocity': >{ @@ -2125,6 +2132,7 @@ uniformBlocks = >>{ elements: 1, type: 'Vector4', ), + 'debug_view': (offset: 896, byteLength: 16, elements: 1, type: 'Vector4'), }, }, }; diff --git a/packages/flutter3d_shaders/shaders/lib/pbr.glsl b/packages/flutter3d_shaders/shaders/lib/pbr.glsl index e1aed95f0..5e47fc984 100644 --- a/packages/flutter3d_shaders/shaders/lib/pbr.glsl +++ b/packages/flutter3d_shaders/shaders/lib/pbr.glsl @@ -869,19 +869,15 @@ void main() { // emission included — glTF's own layering. The direct light was scaled in // `ShadeLight`. vec3 sheenAmbient = g_sheen * g_sheen_albedo * sheenIncoming * s.occlusion; - WriteSurface( - AccumulateLights(s) * s.occlusion + - (ambient * g_sheen_scale + sheenAmbient + s.emissive) * - g_coat_through + - coatAmbient, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + + (ambient * g_sheen_scale + sheenAmbient + s.emissive) * + g_coat_through + + coatAmbient; #else - WriteSurface( - AccumulateLights(s) * s.occlusion + ambient + s.emissive, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + ambient + s.emissive; #endif + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, s.alpha, s.roughness); } #endif // PBR_GLSL_ diff --git a/packages/flutter3d_shaders/shaders/lib/surface.glsl b/packages/flutter3d_shaders/shaders/lib/surface.glsl index 5303f0ddf..1ed3833c2 100644 --- a/packages/flutter3d_shaders/shaders/lib/surface.glsl +++ b/packages/flutter3d_shaders/shaders/lib/surface.glsl @@ -154,6 +154,15 @@ uniform FragInfo { /// while a temporal resolve runs, minus one otherwise — `S3`, which steps /// the soft shadow's rotation by it. vec4 target_origin; + + /// x: which debug view replaces the light — `P6`, `DebugView.code`, nought + /// for none. y: where it starts, as a share of the target's width from the + /// left; nought is the whole frame. z: the target's width in pixels, which + /// turns the share into a column. w unused. + /// + /// Appended for the reason `ambient_sky` was: every offset above stays + /// where the four backends already agree on it. + vec4 debug_view; } frag_info; @@ -1105,4 +1114,64 @@ vec3 AccumulateLights(Surface s) { return total; } +/// Whether any channel of [c] is NaN or infinite. +/// +/// **Comparisons, not `isnan` and not the bits.** WGSL has no `isNan`, and +/// reading the exponent through `floatBitsToUint` takes `impellerc` down in +/// its GLSL ES output, which has no bit casts. A NaN is the one value unequal +/// to itself, and an infinity the one above every finite float. +bool NonFinite(vec3 c) { + return any(notEqual(c, c)) || any(greaterThan(abs(c), vec3(3.0e38))); +} + +/// `P6`: the material channel `FragInfo.debug_view` asks for, written in +/// place of [lit]. False, and nothing written, when no view is on or the +/// fragment sits left of the split; the caller then writes the light. +/// +/// **Display values, through the same exits the light takes.** The channel +/// is what an artist would read off the texture — an albedo as its sRGB +/// colour, a roughness as a grey — converted to linear so the composite's +/// encode hands it back unchanged; the composite leaves this side of the +/// split out of the exposure and the tone curve (`CompositeInfo.lens.y`). +/// The surface buffer and the weighted-blended targets are written as +/// [WriteSurface] writes them, so a debug view changes what the frame shows +/// and nothing the passes after it read. No fog: a channel seen through fog +/// is not the channel. +/// +/// [lit] is read by one view only, [DebugView.nonFinite], which shows a NaN +/// or an infinity as magenta over the light's own luminance in grey. +bool WriteDebugView(Surface s, vec3 lit) { + float view = frag_info.debug_view.x; + if (view < 0.5) return false; + if (gl_FragCoord.x < frag_info.debug_view.y * frag_info.debug_view.z) { + return false; + } + int code = int(view + 0.5); + vec3 shown = vec3(0.0); + if (code == 1) { + shown = LinearToSrgb(clamp(s.albedo, vec3(0.0), vec3(1.0))); + } else if (code == 2) { + shown = s.n * 0.5 + vec3(0.5); + } else if (code == 3) { + shown = vec3(clamp(s.roughness, 0.0, 1.0)); + } else if (code == 4) { + shown = vec3(clamp(s.metallic, 0.0, 1.0)); + } else if (code == 5) { + shown = vec3(clamp(s.occlusion, 0.0, 1.0)); + } else if (code == 6) { + shown = LinearToSrgb(clamp(s.emissive, vec3(0.0), vec3(1.0))); + } else if (code == 7) { + shown = vec3(fract(MapUv(kMapBaseColor)), 0.0); + } else if (code == 8) { + float grey = dot(LinearToSrgb(clamp(lit, vec3(0.0), vec3(1.0))), + vec3(0.2126, 0.7152, 0.0722)); + shown = NonFinite(lit) ? vec3(1.0, 0.0, 1.0) : vec3(grey * 0.5); + } + float weight = g_premultiply ? s.alpha : 1.0; + frag_color = vec4(SrgbToLinear(shown) * weight, s.alpha); + WriteSurfaceGeometry(s.roughness); + WriteWeightedBlended(); + return true; +} + #endif // SURFACE_GLSL_ diff --git a/packages/flutter3d_shaders/shaders/lighting/blinn_phong.frag b/packages/flutter3d_shaders/shaders/lighting/blinn_phong.frag index 8f9b5ea37..b666bbf6b 100644 --- a/packages/flutter3d_shaders/shaders/lighting/blinn_phong.frag +++ b/packages/flutter3d_shaders/shaders/lighting/blinn_phong.frag @@ -30,8 +30,7 @@ void main() { // output here. ApplyMetallicRoughnessMap(s); vec3 ambient = s.albedo * (s.ambient + SampleLightmap()) * s.occlusion; - WriteSurface( - AccumulateLights(s) * s.occlusion + ambient + s.emissive, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + ambient + s.emissive; + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, s.alpha, s.roughness); } diff --git a/packages/flutter3d_shaders/shaders/lighting/impostor.frag b/packages/flutter3d_shaders/shaders/lighting/impostor.frag index e0781ce28..809703bbe 100644 --- a/packages/flutter3d_shaders/shaders/lighting/impostor.frag +++ b/packages/flutter3d_shaders/shaders/lighting/impostor.frag @@ -116,5 +116,7 @@ void main() { frag_info.material.z; vec3 ambient = s.albedo * s.ambient; - WriteSurface(AccumulateLights(s) + ambient, 1.0, 1.0); + vec3 lit = AccumulateLights(s) + ambient; + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, 1.0, 1.0); } diff --git a/packages/flutter3d_shaders/shaders/lighting/lambert.frag b/packages/flutter3d_shaders/shaders/lighting/lambert.frag index 2703e50a1..76ade5a2f 100644 --- a/packages/flutter3d_shaders/shaders/lighting/lambert.frag +++ b/packages/flutter3d_shaders/shaders/lighting/lambert.frag @@ -22,8 +22,7 @@ void main() { // roughness, so sampling it would leave a slot the compiler then drops. ApplyCommonMaps(s); vec3 ambient = s.albedo * (s.ambient + SampleLightmap()) * s.occlusion; - WriteSurface( - AccumulateLights(s) * s.occlusion + ambient + s.emissive, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + ambient + s.emissive; + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, s.alpha, s.roughness); } diff --git a/packages/flutter3d_shaders/shaders/lighting/toon.frag b/packages/flutter3d_shaders/shaders/lighting/toon.frag index 803930c1e..e874a98de 100644 --- a/packages/flutter3d_shaders/shaders/lighting/toon.frag +++ b/packages/flutter3d_shaders/shaders/lighting/toon.frag @@ -42,9 +42,8 @@ void main() { float rim = pow(1.0 - s.n_dot_v, 3.0) * frag_info.material.w; vec3 ambient = s.albedo * (s.ambient + SampleLightmap()) * s.occlusion; - WriteSurface( - AccumulateLights(s) * s.occlusion + ambient + vec3(rim * 0.35) + - s.emissive, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + ambient + vec3(rim * 0.35) + + s.emissive; + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, s.alpha, s.roughness); } diff --git a/packages/flutter3d_shaders/shaders/lighting/unlit.frag b/packages/flutter3d_shaders/shaders/lighting/unlit.frag index 6673ab2f7..d8e8b5c45 100644 --- a/packages/flutter3d_shaders/shaders/lighting/unlit.frag +++ b/packages/flutter3d_shaders/shaders/lighting/unlit.frag @@ -34,5 +34,6 @@ void main() { // The albedo is already linear, and an unlit surface is best // understood as emitting exactly it, so it goes into the HDR // target as light like everything else. + if (WriteDebugView(s, s.albedo)) return; WriteSurface(s.albedo, s.alpha); } diff --git a/packages/flutter3d_shaders/shaders/post/composite.frag b/packages/flutter3d_shaders/shaders/post/composite.frag index 085a56af5..11686356c 100644 --- a/packages/flutter3d_shaders/shaders/post/composite.frag +++ b/packages/flutter3d_shaders/shaders/post/composite.frag @@ -118,8 +118,10 @@ uniform CompositeInfo { vec4 contact; /// x: radial lens distortion — `P2`, barrel above nought, pincushion - /// below, nought off exactly. y, z, w: unclaimed. Appended for the reason - /// `contact` was. + /// below, nought off exactly. y: one while a debug view is on — `P6`, + /// nought otherwise. z: where the debug view starts, as a share of the + /// width from the left. w: unclaimed. Appended for the reason `contact` + /// was. vec4 lens; } composite_info; @@ -670,4 +672,16 @@ void main() { } frag_color = vec4(encoded, scene.a); + + // `P6`: right of the split the materials wrote display values, not light — + // see `WriteDebugView` in `surface.glsl` — so the scene goes to the screen + // through the encode alone, with no exposure, curve, glow or grade on it. + // Chosen here rather than returned from the top: an early return on a + // per-pixel condition would leave every read above in non-uniform control + // flow, which WGSL refuses. + if (composite_info.lens.y > 0.5 && v_uv.x >= composite_info.lens.z) { + vec4 raw = textureLod(scene_texture, v_uv, 0.0); + frag_color = vec4(LinearToSrgb(clamp(raw.rgb, vec3(0.0), vec3(1.0))), + raw.a); + } } diff --git a/packages/flutter3d_webgl/CHANGELOG.md b/packages/flutter3d_webgl/CHANGELOG.md index 151543ddd..ee4ed099d 100644 --- a/packages/flutter3d_webgl/CHANGELOG.md +++ b/packages/flutter3d_webgl/CHANGELOG.md @@ -1,5 +1,8 @@ ## Unreleased +- **The generated tables carry the debug views**, and `debug-view-split` + is in the browser reference set. + - **`LensFlare` translated, and the composite's distortion**; `lens-flare` and `lens-distortion` are in the browser reference set. - **SMAA 1x's three stages translated**, from the same sources as every diff --git a/packages/flutter3d_webgl/lib/engine_shaders.dart b/packages/flutter3d_webgl/lib/engine_shaders.dart index efba3f680..9127317d5 100644 --- a/packages/flutter3d_webgl/lib/engine_shaders.dart +++ b/packages/flutter3d_webgl/lib/engine_shaders.dart @@ -3277,6 +3277,15 @@ layout(std140) uniform FragInfo { /// while a temporal resolve runs, minus one otherwise — `S3`, which steps /// the soft shadow's rotation by it. vec4 target_origin; + + /// x: which debug view replaces the light — `P6`, `DebugView.code`, nought + /// for none. y: where it starts, as a share of the target's width from the + /// left; nought is the whole frame. z: the target's width in pixels, which + /// turns the share into a column. w unused. + /// + /// Appended for the reason `ambient_sky` was: every offset above stays + /// where the four backends already agree on it. + vec4 debug_view; } frag_info; @@ -4319,6 +4328,66 @@ vec3 AccumulateLights(Surface s) { return total; } +/// Whether any channel of [c] is NaN or infinite. +/// +/// **Comparisons, not `isnan` and not the bits.** WGSL has no `isNan`, and +/// reading the exponent through `floatBitsToUint` takes `impellerc` down in +/// its GLSL ES output, which has no bit casts. A NaN is the one value unequal +/// to itself, and an infinity the one above every finite float. +bool NonFinite(vec3 c) { + return any(notEqual(c, c)) || any(greaterThan(abs(c), vec3(3.0e38))); +} + +/// `P6`: the material channel `FragInfo.debug_view` asks for, written in +/// place of [lit]. False, and nothing written, when no view is on or the +/// fragment sits left of the split; the caller then writes the light. +/// +/// **Display values, through the same exits the light takes.** The channel +/// is what an artist would read off the texture — an albedo as its sRGB +/// colour, a roughness as a grey — converted to linear so the composite's +/// encode hands it back unchanged; the composite leaves this side of the +/// split out of the exposure and the tone curve (`CompositeInfo.lens.y`). +/// The surface buffer and the weighted-blended targets are written as +/// [WriteSurface] writes them, so a debug view changes what the frame shows +/// and nothing the passes after it read. No fog: a channel seen through fog +/// is not the channel. +/// +/// [lit] is read by one view only, [DebugView.nonFinite], which shows a NaN +/// or an infinity as magenta over the light's own luminance in grey. +bool WriteDebugView(Surface s, vec3 lit) { + float view = frag_info.debug_view.x; + if (view < 0.5) return false; + if (gl_FragCoord.x < frag_info.debug_view.y * frag_info.debug_view.z) { + return false; + } + int code = int(view + 0.5); + vec3 shown = vec3(0.0); + if (code == 1) { + shown = LinearToSrgb(clamp(s.albedo, vec3(0.0), vec3(1.0))); + } else if (code == 2) { + shown = s.n * 0.5 + vec3(0.5); + } else if (code == 3) { + shown = vec3(clamp(s.roughness, 0.0, 1.0)); + } else if (code == 4) { + shown = vec3(clamp(s.metallic, 0.0, 1.0)); + } else if (code == 5) { + shown = vec3(clamp(s.occlusion, 0.0, 1.0)); + } else if (code == 6) { + shown = LinearToSrgb(clamp(s.emissive, vec3(0.0), vec3(1.0))); + } else if (code == 7) { + shown = vec3(fract(MapUv(kMapBaseColor)), 0.0); + } else if (code == 8) { + float grey = dot(LinearToSrgb(clamp(lit, vec3(0.0), vec3(1.0))), + vec3(0.2126, 0.7152, 0.0722)); + shown = NonFinite(lit) ? vec3(1.0, 0.0, 1.0) : vec3(grey * 0.5); + } + float weight = g_premultiply ? s.alpha : 1.0; + frag_color = vec4(SrgbToLinear(shown) * weight, s.alpha); + WriteSurfaceGeometry(s.roughness); + WriteWeightedBlended(); + return true; +} + #endif // SURFACE_GLSL_ @@ -4344,6 +4413,7 @@ void main() { // The albedo is already linear, and an unlit surface is best // understood as emitting exactly it, so it goes into the HDR // target as light like everything else. + if (WriteDebugView(s, s.albedo)) return; WriteSurface(s.albedo, s.alpha); } @@ -5077,6 +5147,15 @@ layout(std140) uniform FragInfo { /// while a temporal resolve runs, minus one otherwise — `S3`, which steps /// the soft shadow's rotation by it. vec4 target_origin; + + /// x: which debug view replaces the light — `P6`, `DebugView.code`, nought + /// for none. y: where it starts, as a share of the target's width from the + /// left; nought is the whole frame. z: the target's width in pixels, which + /// turns the share into a column. w unused. + /// + /// Appended for the reason `ambient_sky` was: every offset above stays + /// where the four backends already agree on it. + vec4 debug_view; } frag_info; @@ -6119,6 +6198,66 @@ vec3 AccumulateLights(Surface s) { return total; } +/// Whether any channel of [c] is NaN or infinite. +/// +/// **Comparisons, not `isnan` and not the bits.** WGSL has no `isNan`, and +/// reading the exponent through `floatBitsToUint` takes `impellerc` down in +/// its GLSL ES output, which has no bit casts. A NaN is the one value unequal +/// to itself, and an infinity the one above every finite float. +bool NonFinite(vec3 c) { + return any(notEqual(c, c)) || any(greaterThan(abs(c), vec3(3.0e38))); +} + +/// `P6`: the material channel `FragInfo.debug_view` asks for, written in +/// place of [lit]. False, and nothing written, when no view is on or the +/// fragment sits left of the split; the caller then writes the light. +/// +/// **Display values, through the same exits the light takes.** The channel +/// is what an artist would read off the texture — an albedo as its sRGB +/// colour, a roughness as a grey — converted to linear so the composite's +/// encode hands it back unchanged; the composite leaves this side of the +/// split out of the exposure and the tone curve (`CompositeInfo.lens.y`). +/// The surface buffer and the weighted-blended targets are written as +/// [WriteSurface] writes them, so a debug view changes what the frame shows +/// and nothing the passes after it read. No fog: a channel seen through fog +/// is not the channel. +/// +/// [lit] is read by one view only, [DebugView.nonFinite], which shows a NaN +/// or an infinity as magenta over the light's own luminance in grey. +bool WriteDebugView(Surface s, vec3 lit) { + float view = frag_info.debug_view.x; + if (view < 0.5) return false; + if (gl_FragCoord.x < frag_info.debug_view.y * frag_info.debug_view.z) { + return false; + } + int code = int(view + 0.5); + vec3 shown = vec3(0.0); + if (code == 1) { + shown = LinearToSrgb(clamp(s.albedo, vec3(0.0), vec3(1.0))); + } else if (code == 2) { + shown = s.n * 0.5 + vec3(0.5); + } else if (code == 3) { + shown = vec3(clamp(s.roughness, 0.0, 1.0)); + } else if (code == 4) { + shown = vec3(clamp(s.metallic, 0.0, 1.0)); + } else if (code == 5) { + shown = vec3(clamp(s.occlusion, 0.0, 1.0)); + } else if (code == 6) { + shown = LinearToSrgb(clamp(s.emissive, vec3(0.0), vec3(1.0))); + } else if (code == 7) { + shown = vec3(fract(MapUv(kMapBaseColor)), 0.0); + } else if (code == 8) { + float grey = dot(LinearToSrgb(clamp(lit, vec3(0.0), vec3(1.0))), + vec3(0.2126, 0.7152, 0.0722)); + shown = NonFinite(lit) ? vec3(1.0, 0.0, 1.0) : vec3(grey * 0.5); + } + float weight = g_premultiply ? s.alpha : 1.0; + frag_color = vec4(SrgbToLinear(shown) * weight, s.alpha); + WriteSurfaceGeometry(s.roughness); + WriteWeightedBlended(); + return true; +} + #endif // SURFACE_GLSL_ @@ -7334,6 +7473,15 @@ layout(std140) uniform FragInfo { /// while a temporal resolve runs, minus one otherwise — `S3`, which steps /// the soft shadow's rotation by it. vec4 target_origin; + + /// x: which debug view replaces the light — `P6`, `DebugView.code`, nought + /// for none. y: where it starts, as a share of the target's width from the + /// left; nought is the whole frame. z: the target's width in pixels, which + /// turns the share into a column. w unused. + /// + /// Appended for the reason `ambient_sky` was: every offset above stays + /// where the four backends already agree on it. + vec4 debug_view; } frag_info; @@ -8376,6 +8524,66 @@ vec3 AccumulateLights(Surface s) { return total; } +/// Whether any channel of [c] is NaN or infinite. +/// +/// **Comparisons, not `isnan` and not the bits.** WGSL has no `isNan`, and +/// reading the exponent through `floatBitsToUint` takes `impellerc` down in +/// its GLSL ES output, which has no bit casts. A NaN is the one value unequal +/// to itself, and an infinity the one above every finite float. +bool NonFinite(vec3 c) { + return any(notEqual(c, c)) || any(greaterThan(abs(c), vec3(3.0e38))); +} + +/// `P6`: the material channel `FragInfo.debug_view` asks for, written in +/// place of [lit]. False, and nothing written, when no view is on or the +/// fragment sits left of the split; the caller then writes the light. +/// +/// **Display values, through the same exits the light takes.** The channel +/// is what an artist would read off the texture — an albedo as its sRGB +/// colour, a roughness as a grey — converted to linear so the composite's +/// encode hands it back unchanged; the composite leaves this side of the +/// split out of the exposure and the tone curve (`CompositeInfo.lens.y`). +/// The surface buffer and the weighted-blended targets are written as +/// [WriteSurface] writes them, so a debug view changes what the frame shows +/// and nothing the passes after it read. No fog: a channel seen through fog +/// is not the channel. +/// +/// [lit] is read by one view only, [DebugView.nonFinite], which shows a NaN +/// or an infinity as magenta over the light's own luminance in grey. +bool WriteDebugView(Surface s, vec3 lit) { + float view = frag_info.debug_view.x; + if (view < 0.5) return false; + if (gl_FragCoord.x < frag_info.debug_view.y * frag_info.debug_view.z) { + return false; + } + int code = int(view + 0.5); + vec3 shown = vec3(0.0); + if (code == 1) { + shown = LinearToSrgb(clamp(s.albedo, vec3(0.0), vec3(1.0))); + } else if (code == 2) { + shown = s.n * 0.5 + vec3(0.5); + } else if (code == 3) { + shown = vec3(clamp(s.roughness, 0.0, 1.0)); + } else if (code == 4) { + shown = vec3(clamp(s.metallic, 0.0, 1.0)); + } else if (code == 5) { + shown = vec3(clamp(s.occlusion, 0.0, 1.0)); + } else if (code == 6) { + shown = LinearToSrgb(clamp(s.emissive, vec3(0.0), vec3(1.0))); + } else if (code == 7) { + shown = vec3(fract(MapUv(kMapBaseColor)), 0.0); + } else if (code == 8) { + float grey = dot(LinearToSrgb(clamp(lit, vec3(0.0), vec3(1.0))), + vec3(0.2126, 0.7152, 0.0722)); + shown = NonFinite(lit) ? vec3(1.0, 0.0, 1.0) : vec3(grey * 0.5); + } + float weight = g_premultiply ? s.alpha : 1.0; + frag_color = vec4(SrgbToLinear(shown) * weight, s.alpha); + WriteSurfaceGeometry(s.roughness); + WriteWeightedBlended(); + return true; +} + #endif // SURFACE_GLSL_ // --- lib/irradiance.glsl --- @@ -9099,10 +9307,9 @@ void main() { // roughness, so sampling it would leave a slot the compiler then drops. ApplyCommonMaps(s); vec3 ambient = s.albedo * (s.ambient + SampleLightmap()) * s.occlusion; - WriteSurface( - AccumulateLights(s) * s.occlusion + ambient + s.emissive, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + ambient + s.emissive; + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, s.alpha, s.roughness); } ''', @@ -9830,6 +10037,15 @@ layout(std140) uniform FragInfo { /// while a temporal resolve runs, minus one otherwise — `S3`, which steps /// the soft shadow's rotation by it. vec4 target_origin; + + /// x: which debug view replaces the light — `P6`, `DebugView.code`, nought + /// for none. y: where it starts, as a share of the target's width from the + /// left; nought is the whole frame. z: the target's width in pixels, which + /// turns the share into a column. w unused. + /// + /// Appended for the reason `ambient_sky` was: every offset above stays + /// where the four backends already agree on it. + vec4 debug_view; } frag_info; @@ -10872,6 +11088,66 @@ vec3 AccumulateLights(Surface s) { return total; } +/// Whether any channel of [c] is NaN or infinite. +/// +/// **Comparisons, not `isnan` and not the bits.** WGSL has no `isNan`, and +/// reading the exponent through `floatBitsToUint` takes `impellerc` down in +/// its GLSL ES output, which has no bit casts. A NaN is the one value unequal +/// to itself, and an infinity the one above every finite float. +bool NonFinite(vec3 c) { + return any(notEqual(c, c)) || any(greaterThan(abs(c), vec3(3.0e38))); +} + +/// `P6`: the material channel `FragInfo.debug_view` asks for, written in +/// place of [lit]. False, and nothing written, when no view is on or the +/// fragment sits left of the split; the caller then writes the light. +/// +/// **Display values, through the same exits the light takes.** The channel +/// is what an artist would read off the texture — an albedo as its sRGB +/// colour, a roughness as a grey — converted to linear so the composite's +/// encode hands it back unchanged; the composite leaves this side of the +/// split out of the exposure and the tone curve (`CompositeInfo.lens.y`). +/// The surface buffer and the weighted-blended targets are written as +/// [WriteSurface] writes them, so a debug view changes what the frame shows +/// and nothing the passes after it read. No fog: a channel seen through fog +/// is not the channel. +/// +/// [lit] is read by one view only, [DebugView.nonFinite], which shows a NaN +/// or an infinity as magenta over the light's own luminance in grey. +bool WriteDebugView(Surface s, vec3 lit) { + float view = frag_info.debug_view.x; + if (view < 0.5) return false; + if (gl_FragCoord.x < frag_info.debug_view.y * frag_info.debug_view.z) { + return false; + } + int code = int(view + 0.5); + vec3 shown = vec3(0.0); + if (code == 1) { + shown = LinearToSrgb(clamp(s.albedo, vec3(0.0), vec3(1.0))); + } else if (code == 2) { + shown = s.n * 0.5 + vec3(0.5); + } else if (code == 3) { + shown = vec3(clamp(s.roughness, 0.0, 1.0)); + } else if (code == 4) { + shown = vec3(clamp(s.metallic, 0.0, 1.0)); + } else if (code == 5) { + shown = vec3(clamp(s.occlusion, 0.0, 1.0)); + } else if (code == 6) { + shown = LinearToSrgb(clamp(s.emissive, vec3(0.0), vec3(1.0))); + } else if (code == 7) { + shown = vec3(fract(MapUv(kMapBaseColor)), 0.0); + } else if (code == 8) { + float grey = dot(LinearToSrgb(clamp(lit, vec3(0.0), vec3(1.0))), + vec3(0.2126, 0.7152, 0.0722)); + shown = NonFinite(lit) ? vec3(1.0, 0.0, 1.0) : vec3(grey * 0.5); + } + float weight = g_premultiply ? s.alpha : 1.0; + frag_color = vec4(SrgbToLinear(shown) * weight, s.alpha); + WriteSurfaceGeometry(s.roughness); + WriteWeightedBlended(); + return true; +} + #endif // SURFACE_GLSL_ // --- lib/irradiance.glsl --- @@ -11601,10 +11877,9 @@ void main() { // output here. ApplyMetallicRoughnessMap(s); vec3 ambient = s.albedo * (s.ambient + SampleLightmap()) * s.occlusion; - WriteSurface( - AccumulateLights(s) * s.occlusion + ambient + s.emissive, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + ambient + s.emissive; + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, s.alpha, s.roughness); } ''', @@ -12361,6 +12636,15 @@ layout(std140) uniform FragInfo { /// while a temporal resolve runs, minus one otherwise — `S3`, which steps /// the soft shadow's rotation by it. vec4 target_origin; + + /// x: which debug view replaces the light — `P6`, `DebugView.code`, nought + /// for none. y: where it starts, as a share of the target's width from the + /// left; nought is the whole frame. z: the target's width in pixels, which + /// turns the share into a column. w unused. + /// + /// Appended for the reason `ambient_sky` was: every offset above stays + /// where the four backends already agree on it. + vec4 debug_view; } frag_info; @@ -13403,6 +13687,66 @@ vec3 AccumulateLights(Surface s) { return total; } +/// Whether any channel of [c] is NaN or infinite. +/// +/// **Comparisons, not `isnan` and not the bits.** WGSL has no `isNan`, and +/// reading the exponent through `floatBitsToUint` takes `impellerc` down in +/// its GLSL ES output, which has no bit casts. A NaN is the one value unequal +/// to itself, and an infinity the one above every finite float. +bool NonFinite(vec3 c) { + return any(notEqual(c, c)) || any(greaterThan(abs(c), vec3(3.0e38))); +} + +/// `P6`: the material channel `FragInfo.debug_view` asks for, written in +/// place of [lit]. False, and nothing written, when no view is on or the +/// fragment sits left of the split; the caller then writes the light. +/// +/// **Display values, through the same exits the light takes.** The channel +/// is what an artist would read off the texture — an albedo as its sRGB +/// colour, a roughness as a grey — converted to linear so the composite's +/// encode hands it back unchanged; the composite leaves this side of the +/// split out of the exposure and the tone curve (`CompositeInfo.lens.y`). +/// The surface buffer and the weighted-blended targets are written as +/// [WriteSurface] writes them, so a debug view changes what the frame shows +/// and nothing the passes after it read. No fog: a channel seen through fog +/// is not the channel. +/// +/// [lit] is read by one view only, [DebugView.nonFinite], which shows a NaN +/// or an infinity as magenta over the light's own luminance in grey. +bool WriteDebugView(Surface s, vec3 lit) { + float view = frag_info.debug_view.x; + if (view < 0.5) return false; + if (gl_FragCoord.x < frag_info.debug_view.y * frag_info.debug_view.z) { + return false; + } + int code = int(view + 0.5); + vec3 shown = vec3(0.0); + if (code == 1) { + shown = LinearToSrgb(clamp(s.albedo, vec3(0.0), vec3(1.0))); + } else if (code == 2) { + shown = s.n * 0.5 + vec3(0.5); + } else if (code == 3) { + shown = vec3(clamp(s.roughness, 0.0, 1.0)); + } else if (code == 4) { + shown = vec3(clamp(s.metallic, 0.0, 1.0)); + } else if (code == 5) { + shown = vec3(clamp(s.occlusion, 0.0, 1.0)); + } else if (code == 6) { + shown = LinearToSrgb(clamp(s.emissive, vec3(0.0), vec3(1.0))); + } else if (code == 7) { + shown = vec3(fract(MapUv(kMapBaseColor)), 0.0); + } else if (code == 8) { + float grey = dot(LinearToSrgb(clamp(lit, vec3(0.0), vec3(1.0))), + vec3(0.2126, 0.7152, 0.0722)); + shown = NonFinite(lit) ? vec3(1.0, 0.0, 1.0) : vec3(grey * 0.5); + } + float weight = g_premultiply ? s.alpha : 1.0; + frag_color = vec4(SrgbToLinear(shown) * weight, s.alpha); + WriteSurfaceGeometry(s.roughness); + WriteWeightedBlended(); + return true; +} + #endif // SURFACE_GLSL_ // --- lib/irradiance.glsl --- @@ -14947,19 +15291,15 @@ void main() { // emission included — glTF's own layering. The direct light was scaled in // `ShadeLight`. vec3 sheenAmbient = g_sheen * g_sheen_albedo * sheenIncoming * s.occlusion; - WriteSurface( - AccumulateLights(s) * s.occlusion + - (ambient * g_sheen_scale + sheenAmbient + s.emissive) * - g_coat_through + - coatAmbient, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + + (ambient * g_sheen_scale + sheenAmbient + s.emissive) * + g_coat_through + + coatAmbient; #else - WriteSurface( - AccumulateLights(s) * s.occlusion + ambient + s.emissive, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + ambient + s.emissive; #endif + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, s.alpha, s.roughness); } #endif // PBR_GLSL_ @@ -15724,6 +16064,15 @@ layout(std140) uniform FragInfo { /// while a temporal resolve runs, minus one otherwise — `S3`, which steps /// the soft shadow's rotation by it. vec4 target_origin; + + /// x: which debug view replaces the light — `P6`, `DebugView.code`, nought + /// for none. y: where it starts, as a share of the target's width from the + /// left; nought is the whole frame. z: the target's width in pixels, which + /// turns the share into a column. w unused. + /// + /// Appended for the reason `ambient_sky` was: every offset above stays + /// where the four backends already agree on it. + vec4 debug_view; } frag_info; @@ -16766,6 +17115,66 @@ vec3 AccumulateLights(Surface s) { return total; } +/// Whether any channel of [c] is NaN or infinite. +/// +/// **Comparisons, not `isnan` and not the bits.** WGSL has no `isNan`, and +/// reading the exponent through `floatBitsToUint` takes `impellerc` down in +/// its GLSL ES output, which has no bit casts. A NaN is the one value unequal +/// to itself, and an infinity the one above every finite float. +bool NonFinite(vec3 c) { + return any(notEqual(c, c)) || any(greaterThan(abs(c), vec3(3.0e38))); +} + +/// `P6`: the material channel `FragInfo.debug_view` asks for, written in +/// place of [lit]. False, and nothing written, when no view is on or the +/// fragment sits left of the split; the caller then writes the light. +/// +/// **Display values, through the same exits the light takes.** The channel +/// is what an artist would read off the texture — an albedo as its sRGB +/// colour, a roughness as a grey — converted to linear so the composite's +/// encode hands it back unchanged; the composite leaves this side of the +/// split out of the exposure and the tone curve (`CompositeInfo.lens.y`). +/// The surface buffer and the weighted-blended targets are written as +/// [WriteSurface] writes them, so a debug view changes what the frame shows +/// and nothing the passes after it read. No fog: a channel seen through fog +/// is not the channel. +/// +/// [lit] is read by one view only, [DebugView.nonFinite], which shows a NaN +/// or an infinity as magenta over the light's own luminance in grey. +bool WriteDebugView(Surface s, vec3 lit) { + float view = frag_info.debug_view.x; + if (view < 0.5) return false; + if (gl_FragCoord.x < frag_info.debug_view.y * frag_info.debug_view.z) { + return false; + } + int code = int(view + 0.5); + vec3 shown = vec3(0.0); + if (code == 1) { + shown = LinearToSrgb(clamp(s.albedo, vec3(0.0), vec3(1.0))); + } else if (code == 2) { + shown = s.n * 0.5 + vec3(0.5); + } else if (code == 3) { + shown = vec3(clamp(s.roughness, 0.0, 1.0)); + } else if (code == 4) { + shown = vec3(clamp(s.metallic, 0.0, 1.0)); + } else if (code == 5) { + shown = vec3(clamp(s.occlusion, 0.0, 1.0)); + } else if (code == 6) { + shown = LinearToSrgb(clamp(s.emissive, vec3(0.0), vec3(1.0))); + } else if (code == 7) { + shown = vec3(fract(MapUv(kMapBaseColor)), 0.0); + } else if (code == 8) { + float grey = dot(LinearToSrgb(clamp(lit, vec3(0.0), vec3(1.0))), + vec3(0.2126, 0.7152, 0.0722)); + shown = NonFinite(lit) ? vec3(1.0, 0.0, 1.0) : vec3(grey * 0.5); + } + float weight = g_premultiply ? s.alpha : 1.0; + frag_color = vec4(SrgbToLinear(shown) * weight, s.alpha); + WriteSurfaceGeometry(s.roughness); + WriteWeightedBlended(); + return true; +} + #endif // SURFACE_GLSL_ // --- lib/irradiance.glsl --- @@ -18310,19 +18719,15 @@ void main() { // emission included — glTF's own layering. The direct light was scaled in // `ShadeLight`. vec3 sheenAmbient = g_sheen * g_sheen_albedo * sheenIncoming * s.occlusion; - WriteSurface( - AccumulateLights(s) * s.occlusion + - (ambient * g_sheen_scale + sheenAmbient + s.emissive) * - g_coat_through + - coatAmbient, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + + (ambient * g_sheen_scale + sheenAmbient + s.emissive) * + g_coat_through + + coatAmbient; #else - WriteSurface( - AccumulateLights(s) * s.occlusion + ambient + s.emissive, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + ambient + s.emissive; #endif + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, s.alpha, s.roughness); } #endif // PBR_GLSL_ @@ -19055,6 +19460,15 @@ layout(std140) uniform FragInfo { /// while a temporal resolve runs, minus one otherwise — `S3`, which steps /// the soft shadow's rotation by it. vec4 target_origin; + + /// x: which debug view replaces the light — `P6`, `DebugView.code`, nought + /// for none. y: where it starts, as a share of the target's width from the + /// left; nought is the whole frame. z: the target's width in pixels, which + /// turns the share into a column. w unused. + /// + /// Appended for the reason `ambient_sky` was: every offset above stays + /// where the four backends already agree on it. + vec4 debug_view; } frag_info; @@ -20097,6 +20511,66 @@ vec3 AccumulateLights(Surface s) { return total; } +/// Whether any channel of [c] is NaN or infinite. +/// +/// **Comparisons, not `isnan` and not the bits.** WGSL has no `isNan`, and +/// reading the exponent through `floatBitsToUint` takes `impellerc` down in +/// its GLSL ES output, which has no bit casts. A NaN is the one value unequal +/// to itself, and an infinity the one above every finite float. +bool NonFinite(vec3 c) { + return any(notEqual(c, c)) || any(greaterThan(abs(c), vec3(3.0e38))); +} + +/// `P6`: the material channel `FragInfo.debug_view` asks for, written in +/// place of [lit]. False, and nothing written, when no view is on or the +/// fragment sits left of the split; the caller then writes the light. +/// +/// **Display values, through the same exits the light takes.** The channel +/// is what an artist would read off the texture — an albedo as its sRGB +/// colour, a roughness as a grey — converted to linear so the composite's +/// encode hands it back unchanged; the composite leaves this side of the +/// split out of the exposure and the tone curve (`CompositeInfo.lens.y`). +/// The surface buffer and the weighted-blended targets are written as +/// [WriteSurface] writes them, so a debug view changes what the frame shows +/// and nothing the passes after it read. No fog: a channel seen through fog +/// is not the channel. +/// +/// [lit] is read by one view only, [DebugView.nonFinite], which shows a NaN +/// or an infinity as magenta over the light's own luminance in grey. +bool WriteDebugView(Surface s, vec3 lit) { + float view = frag_info.debug_view.x; + if (view < 0.5) return false; + if (gl_FragCoord.x < frag_info.debug_view.y * frag_info.debug_view.z) { + return false; + } + int code = int(view + 0.5); + vec3 shown = vec3(0.0); + if (code == 1) { + shown = LinearToSrgb(clamp(s.albedo, vec3(0.0), vec3(1.0))); + } else if (code == 2) { + shown = s.n * 0.5 + vec3(0.5); + } else if (code == 3) { + shown = vec3(clamp(s.roughness, 0.0, 1.0)); + } else if (code == 4) { + shown = vec3(clamp(s.metallic, 0.0, 1.0)); + } else if (code == 5) { + shown = vec3(clamp(s.occlusion, 0.0, 1.0)); + } else if (code == 6) { + shown = LinearToSrgb(clamp(s.emissive, vec3(0.0), vec3(1.0))); + } else if (code == 7) { + shown = vec3(fract(MapUv(kMapBaseColor)), 0.0); + } else if (code == 8) { + float grey = dot(LinearToSrgb(clamp(lit, vec3(0.0), vec3(1.0))), + vec3(0.2126, 0.7152, 0.0722)); + shown = NonFinite(lit) ? vec3(1.0, 0.0, 1.0) : vec3(grey * 0.5); + } + float weight = g_premultiply ? s.alpha : 1.0; + frag_color = vec4(SrgbToLinear(shown) * weight, s.alpha); + WriteSurfaceGeometry(s.roughness); + WriteWeightedBlended(); + return true; +} + #endif // SURFACE_GLSL_ // --- lib/irradiance.glsl --- @@ -20836,11 +21310,10 @@ void main() { float rim = pow(1.0 - s.n_dot_v, 3.0) * frag_info.material.w; vec3 ambient = s.albedo * (s.ambient + SampleLightmap()) * s.occlusion; - WriteSurface( - AccumulateLights(s) * s.occlusion + ambient + vec3(rim * 0.35) + - s.emissive, - s.alpha, - s.roughness); + vec3 lit = AccumulateLights(s) * s.occlusion + ambient + vec3(rim * 0.35) + + s.emissive; + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, s.alpha, s.roughness); } ''', @@ -21634,8 +22107,10 @@ layout(std140) uniform CompositeInfo { vec4 contact; /// x: radial lens distortion — `P2`, barrel above nought, pincushion - /// below, nought off exactly. y, z, w: unclaimed. Appended for the reason - /// `contact` was. + /// below, nought off exactly. y: one while a debug view is on — `P6`, + /// nought otherwise. z: where the debug view starts, as a share of the + /// width from the left. w: unclaimed. Appended for the reason `contact` + /// was. vec4 lens; } composite_info; @@ -22186,6 +22661,18 @@ void main() { } frag_color = vec4(encoded, scene.a); + + // `P6`: right of the split the materials wrote display values, not light — + // see `WriteDebugView` in `surface.glsl` — so the scene goes to the screen + // through the encode alone, with no exposure, curve, glow or grade on it. + // Chosen here rather than returned from the top: an early return on a + // per-pixel condition would leave every read above in non-uniform control + // flow, which WGSL refuses. + if (composite_info.lens.y > 0.5 && v_uv.x >= composite_info.lens.z) { + vec4 raw = textureLod(scene_texture, v_uv, 0.0); + frag_color = vec4(LinearToSrgb(clamp(raw.rgb, vec3(0.0), vec3(1.0))), + raw.a); + } } ''', @@ -33914,6 +34401,15 @@ layout(std140) uniform FragInfo { /// while a temporal resolve runs, minus one otherwise — `S3`, which steps /// the soft shadow's rotation by it. vec4 target_origin; + + /// x: which debug view replaces the light — `P6`, `DebugView.code`, nought + /// for none. y: where it starts, as a share of the target's width from the + /// left; nought is the whole frame. z: the target's width in pixels, which + /// turns the share into a column. w unused. + /// + /// Appended for the reason `ambient_sky` was: every offset above stays + /// where the four backends already agree on it. + vec4 debug_view; } frag_info; @@ -34956,6 +35452,66 @@ vec3 AccumulateLights(Surface s) { return total; } +/// Whether any channel of [c] is NaN or infinite. +/// +/// **Comparisons, not `isnan` and not the bits.** WGSL has no `isNan`, and +/// reading the exponent through `floatBitsToUint` takes `impellerc` down in +/// its GLSL ES output, which has no bit casts. A NaN is the one value unequal +/// to itself, and an infinity the one above every finite float. +bool NonFinite(vec3 c) { + return any(notEqual(c, c)) || any(greaterThan(abs(c), vec3(3.0e38))); +} + +/// `P6`: the material channel `FragInfo.debug_view` asks for, written in +/// place of [lit]. False, and nothing written, when no view is on or the +/// fragment sits left of the split; the caller then writes the light. +/// +/// **Display values, through the same exits the light takes.** The channel +/// is what an artist would read off the texture — an albedo as its sRGB +/// colour, a roughness as a grey — converted to linear so the composite's +/// encode hands it back unchanged; the composite leaves this side of the +/// split out of the exposure and the tone curve (`CompositeInfo.lens.y`). +/// The surface buffer and the weighted-blended targets are written as +/// [WriteSurface] writes them, so a debug view changes what the frame shows +/// and nothing the passes after it read. No fog: a channel seen through fog +/// is not the channel. +/// +/// [lit] is read by one view only, [DebugView.nonFinite], which shows a NaN +/// or an infinity as magenta over the light's own luminance in grey. +bool WriteDebugView(Surface s, vec3 lit) { + float view = frag_info.debug_view.x; + if (view < 0.5) return false; + if (gl_FragCoord.x < frag_info.debug_view.y * frag_info.debug_view.z) { + return false; + } + int code = int(view + 0.5); + vec3 shown = vec3(0.0); + if (code == 1) { + shown = LinearToSrgb(clamp(s.albedo, vec3(0.0), vec3(1.0))); + } else if (code == 2) { + shown = s.n * 0.5 + vec3(0.5); + } else if (code == 3) { + shown = vec3(clamp(s.roughness, 0.0, 1.0)); + } else if (code == 4) { + shown = vec3(clamp(s.metallic, 0.0, 1.0)); + } else if (code == 5) { + shown = vec3(clamp(s.occlusion, 0.0, 1.0)); + } else if (code == 6) { + shown = LinearToSrgb(clamp(s.emissive, vec3(0.0), vec3(1.0))); + } else if (code == 7) { + shown = vec3(fract(MapUv(kMapBaseColor)), 0.0); + } else if (code == 8) { + float grey = dot(LinearToSrgb(clamp(lit, vec3(0.0), vec3(1.0))), + vec3(0.2126, 0.7152, 0.0722)); + shown = NonFinite(lit) ? vec3(1.0, 0.0, 1.0) : vec3(grey * 0.5); + } + float weight = g_premultiply ? s.alpha : 1.0; + frag_color = vec4(SrgbToLinear(shown) * weight, s.alpha); + WriteSurfaceGeometry(s.roughness); + WriteWeightedBlended(); + return true; +} + #endif // SURFACE_GLSL_ @@ -35356,7 +35912,9 @@ void main() { frag_info.material.z; vec3 ambient = s.albedo * s.ambient; - WriteSurface(AccumulateLights(s) + ambient, 1.0, 1.0); + vec3 lit = AccumulateLights(s) + ambient; + if (WriteDebugView(s, lit)) return; + WriteSurface(lit, 1.0, 1.0); } ''', diff --git a/packages/flutter3d_webgl/test/cross_backend_test.dart b/packages/flutter3d_webgl/test/cross_backend_test.dart index 95d1b11e1..a4bf3b100 100644 --- a/packages/flutter3d_webgl/test/cross_backend_test.dart +++ b/packages/flutter3d_webgl/test/cross_backend_test.dart @@ -98,6 +98,10 @@ const int _channel = 8; /// pointed at and agreed with Impeller to the pixel for six sessions. /// const Map _budgets = { + // 0.359% measured, on the silhouette: `shadow-teapot`'s edge between + // WebGL2 and Impeller, and the normals' half shows it in brighter colours + // than the lit half's. + 'debug-view-split': 0.45, // **Zero measured, and it was 0.558%.** Both this and the occlusion below // reconstruct a world point from the depth stored in the surface buffer, and // both were handed a matrix adjusted to this backend's `[-1, 1]` clip range diff --git a/packages/flutter3d_webgl/test/goldens/debug-view-split.png b/packages/flutter3d_webgl/test/goldens/debug-view-split.png new file mode 100644 index 0000000000000000000000000000000000000000..ac925cf613e287f32fae945f5c4f0835bf95e472 GIT binary patch literal 26206 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, shadow_bias: vec4, target_origin: vec4, + debug_view: vec4, } struct FragmentOutput { @@ -4240,14 +4241,15 @@ var base_color_texture_tex: texture_2d; var base_color_texture_smp: sampler; var v_texcoord_1: vec2; var v_color_1: vec4; +var gl_FragCoord_1: vec4; var v_tangent_1: vec4; var v_lightmap_uv_1: vec2; fn ViewDepth_u0028_() -> f32 { - let _e76 = v_world_position_1; - let _e78 = fog_info.eye; - let _e82 = fog_info.forward; - return dot((_e76 - _e78.xyz), _e82.xyz); + let _e87 = v_world_position_1; + let _e89 = fog_info.eye; + let _e93 = fog_info.forward; + return dot((_e87 - _e89.xyz), _e93.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -4256,22 +4258,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e81 = ViewDepth_u0028_(); - z = abs(_e81); - let _e83 = z; - near = (_e83 / 5f); - let _e85 = z; - far = (_e85 / 200f); - let _e87 = far; - let _e88 = far; - let _e90 = far; - far3_ = ((_e87 * _e88) * _e90); - let _e92 = (*alpha); - let _e93 = near; - let _e94 = near; - let _e97 = far3_; - let _e98 = far3_; - return (_e92 * clamp((10f / ((0.00001f + (_e93 * _e94)) + (_e97 * _e98))), 0.01f, 3000f)); + let _e92 = ViewDepth_u0028_(); + z = abs(_e92); + let _e94 = z; + near = (_e94 / 5f); + let _e96 = z; + far = (_e96 / 200f); + let _e98 = far; + let _e99 = far; + let _e101 = far; + far3_ = ((_e98 * _e99) * _e101); + let _e103 = (*alpha); + let _e104 = near; + let _e105 = near; + let _e108 = far3_; + let _e109 = far3_; + return (_e103 * clamp((10f / ((0.00001f + (_e104 * _e105)) + (_e108 * _e109))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -4283,39 +4285,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e85 = fog_info.forward[3u]; - mode = _e85; - let _e86 = mode; - if (_e86 > 0.5f) { - let _e89 = frag_color[3u]; - alpha_1 = _e89; - let _e90 = alpha_1; - param = _e90; - let _e91 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e91; - let _e92 = frag_color; - let _e94 = weight; - let _e95 = (_e92.xyz * _e94); - let _e96 = alpha_1; - let _e97 = weight; - accumulate = vec4(_e95.x, _e95.y, _e95.z, (_e96 * _e97)); - let _e103 = mode; - let _e105 = mode; - revealage = ((_e103 > 1.5f) && (_e105 < 2.5f)); - let _e108 = revealage; - if _e108 { - let _e109 = alpha_1; - local = vec4(_e109); + let _e96 = fog_info.forward[3u]; + mode = _e96; + let _e97 = mode; + if (_e97 > 0.5f) { + let _e100 = frag_color[3u]; + alpha_1 = _e100; + let _e101 = alpha_1; + param = _e101; + let _e102 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e102; + let _e103 = frag_color; + let _e105 = weight; + let _e106 = (_e103.xyz * _e105); + let _e107 = alpha_1; + let _e108 = weight; + accumulate = vec4(_e106.x, _e106.y, _e106.z, (_e107 * _e108)); + let _e114 = mode; + let _e116 = mode; + revealage = ((_e114 > 1.5f) && (_e116 < 2.5f)); + let _e119 = revealage; + if _e119 { + let _e120 = alpha_1; + local = vec4(_e120); } else { - let _e111 = accumulate; - local = _e111; + let _e122 = accumulate; + local = _e122; } - let _e112 = local; - frag_color = _e112; - let _e113 = mode; - if (_e113 > 2.5f) { - let _e115 = alpha_1; - frag_surface = vec4(_e115); + let _e123 = local; + frag_color = _e123; + let _e124 = mode; + if (_e124 > 2.5f) { + let _e126 = alpha_1; + frag_surface = vec4(_e126); } } return; @@ -4324,29 +4326,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e79 = (*n)[0u]; - let _e82 = (*n)[1u]; - let _e86 = (*n)[2u]; - let _e89 = (*n); - (*n) = (_e89 / vec3(((abs(_e79) + abs(_e82)) + abs(_e86)))); - let _e92 = (*n); - e = _e92.xy; - let _e95 = (*n)[2u]; - if (_e95 < 0f) { - let _e97 = (*n); - let _e103 = (*n)[0u]; - let _e107 = (*n)[1u]; - e = ((vec2(1f) - abs(_e97.yx)) * vec2(select(-1f, 1f, (_e103 >= 0f)), select(-1f, 1f, (_e107 >= 0f)))); + let _e90 = (*n)[0u]; + let _e93 = (*n)[1u]; + let _e97 = (*n)[2u]; + let _e100 = (*n); + (*n) = (_e100 / vec3(((abs(_e90) + abs(_e93)) + abs(_e97)))); + let _e103 = (*n); + e = _e103.xy; + let _e106 = (*n)[2u]; + if (_e106 < 0f) { + let _e108 = (*n); + let _e114 = (*n)[0u]; + let _e118 = (*n)[1u]; + e = ((vec2(1f) - abs(_e108.yx)) * vec2(select(-1f, 1f, (_e114 >= 0f)), select(-1f, 1f, (_e118 >= 0f)))); } - let _e112 = e; - return ((_e112 * 0.5f) + vec2(0.5f)); + let _e123 = e; + return ((_e123 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e77 = (*linear); - let _e79 = (*linear); - let _e83 = (*linear); - return mix((_e77 * 12.92f), ((pow(_e79, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e83)); + let _e88 = (*linear); + let _e90 = (*linear); + let _e94 = (*linear); + return mix((_e88 * 12.92f), ((pow(_e90, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e94)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -4354,37 +4356,37 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e80 = g_albedo; - param_1 = clamp(_e80, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e82 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e82.x, _e82.y, _e82.z, 1f); - let _e87 = g_debug_surface_on; - if _e87 { - let _e88 = g_debug_surface; - let _e89 = ViewDepth_u0028_(); - frag_surface = vec4(_e88.x, _e88.y, _e88.z, _e89); + let _e91 = g_albedo; + param_1 = clamp(_e91, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e93 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e93.x, _e93.y, _e93.z, 1f); + let _e98 = g_debug_surface_on; + if _e98 { + let _e99 = g_debug_surface; + let _e100 = ViewDepth_u0028_(); + frag_surface = vec4(_e99.x, _e99.y, _e99.z, _e100); return; } - let _e94 = v_normal_1; - geometric = normalize(_e94); - let _e96 = gl_FrontFacing_1; - if !(_e96) { - let _e98 = geometric; - geometric = -(_e98); - } - let _e100 = geometric; - param_2 = _e100; - let _e101 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e102 = (*roughness); - let _e104 = ViewDepth_u0028_(); - frag_surface = vec4(_e101.x, _e101.y, clamp(_e102, 0f, 1f), _e104); + let _e105 = v_normal_1; + geometric = normalize(_e105); + let _e107 = gl_FrontFacing_1; + if !(_e107) { + let _e109 = geometric; + geometric = -(_e109); + } + let _e111 = geometric; + param_2 = _e111; + let _e112 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e113 = (*roughness); + let _e115 = ViewDepth_u0028_(); + frag_surface = vec4(_e112.x, _e112.y, clamp(_e113, 0f, 1f), _e115); return; } fn EyeDistance_u0028_() -> f32 { - let _e76 = v_world_position_1; - let _e78 = fog_info.eye; - return distance(_e76, _e78.xyz); + let _e87 = v_world_position_1; + let _e89 = fog_info.eye; + return distance(_e87, _e89.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -4394,41 +4396,41 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var k: f32; var local_1: f32; - let _e84 = fog_info.fog[3u]; - density = _e84; - let _e85 = density; - if (_e85 <= 0f) { - let _e87 = (*color); - return _e87; - } - let _e88 = EyeDistance_u0028_(); - d = _e88; - let _e91 = fog_info.eye[3u]; - falloff = _e91; - let _e92 = falloff; - if (_e92 > 0f) { - let _e94 = falloff; - let _e96 = v_world_position_1[1u]; - let _e99 = fog_info.eye[1u]; - k = (_e94 * (_e96 - _e99)); - let _e102 = k; - if (abs(_e102) > 0.001f) { - let _e105 = k; - let _e109 = k; - local_1 = ((1f - exp(-(_e105))) / _e109); + let _e95 = fog_info.fog[3u]; + density = _e95; + let _e96 = density; + if (_e96 <= 0f) { + let _e98 = (*color); + return _e98; + } + let _e99 = EyeDistance_u0028_(); + d = _e99; + let _e102 = fog_info.eye[3u]; + falloff = _e102; + let _e103 = falloff; + if (_e103 > 0f) { + let _e105 = falloff; + let _e107 = v_world_position_1[1u]; + let _e110 = fog_info.eye[1u]; + k = (_e105 * (_e107 - _e110)); + let _e113 = k; + if (abs(_e113) > 0.001f) { + let _e116 = k; + let _e120 = k; + local_1 = ((1f - exp(-(_e116))) / _e120); } else { - let _e111 = k; - local_1 = (1f - (0.5f * _e111)); + let _e122 = k; + local_1 = (1f - (0.5f * _e122)); } - let _e114 = local_1; - let _e115 = density; - density = (_e115 * _e114); + let _e125 = local_1; + let _e126 = density; + density = (_e126 * _e125); } - let _e118 = fog_info.fog; - let _e120 = (*color); - let _e121 = density; - let _e123 = d; - return mix(_e118.xyz, _e120, vec3(clamp(exp((-(_e121) * _e123)), 0f, 1f))); + let _e129 = fog_info.fog; + let _e131 = (*color); + let _e132 = density; + let _e134 = d; + return mix(_e129.xyz, _e131, vec3(clamp(exp((-(_e132) * _e134)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -4436,18 +4438,18 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e82 = g_premultiply; - let _e83 = (*alpha_2); - weight_1 = select(1f, _e83, _e82); - let _e85 = (*linearColor); - param_3 = _e85; - let _e86 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e87 = weight_1; - let _e88 = (_e86 * _e87); - let _e89 = (*alpha_2); - frag_color = vec4(_e88.x, _e88.y, _e88.z, _e89); - let _e94 = (*roughness_1); - param_4 = _e94; + let _e93 = g_premultiply; + let _e94 = (*alpha_2); + weight_1 = select(1f, _e94, _e93); + let _e96 = (*linearColor); + param_3 = _e96; + let _e97 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e98 = weight_1; + let _e99 = (_e97 * _e98); + let _e100 = (*alpha_2); + frag_color = vec4(_e99.x, _e99.y, _e99.z, _e100); + let _e105 = (*roughness_1); + param_4 = _e105; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; @@ -4458,144 +4460,288 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b(linearColor_1: ptr> var param_6: f32; var param_7: f32; - let _e81 = (*linearColor_1); - param_5 = _e81; - let _e82 = (*alpha_3); - param_6 = _e82; + let _e92 = (*linearColor_1); + param_5 = _e92; + let _e93 = (*alpha_3); + param_6 = _e93; param_7 = 1f; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_5), (¶m_6), (¶m_7)); return; } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e77 = (*srgb); - let _e80 = (*srgb); - let _e85 = (*srgb); - return mix((_e77 / vec3(12.92f)), pow(((_e80 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e85)); + let _e88 = (*srgb); + let _e91 = (*srgb); + let _e96 = (*srgb); + return mix((_e88 / vec3(12.92f)), pow(((_e91 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e96)); +} + +fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { + var phi_602_: bool; + + let _e88 = (*c); + let _e89 = (*c); + let _e91 = any((_e88 != _e89)); + phi_602_ = _e91; + if !(_e91) { + let _e93 = (*c); + phi_602_ = any((abs(_e93) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + } + let _e98 = phi_602_; + return _e98; +} + +fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b(s: ptr, lit: ptr>) -> bool { + var view: f32; + var code: i32; + var shown: vec3; + var param_8: vec3; + var param_9: vec3; + var grey: f32; + var param_10: vec3; + var param_11: vec3; + var local_2: vec3; + var weight_2: f32; + var local_3: f32; + var param_12: vec3; + var param_13: f32; + + let _e104 = frag_info.debug_view[0u]; + view = _e104; + let _e105 = view; + if (_e105 < 0.5f) { + return false; + } + let _e108 = gl_FragCoord_1[0u]; + let _e111 = frag_info.debug_view[1u]; + let _e114 = frag_info.debug_view[2u]; + if (_e108 < (_e111 * _e114)) { + return false; + } + let _e117 = view; + code = i32((_e117 + 0.5f)); + shown = vec3(0f, 0f, 0f); + let _e120 = code; + if (_e120 == 1i) { + let _e123 = (*s).albedo; + param_8 = clamp(_e123, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e125 = LinearToSrgb_u0028_vf3_u003b((¶m_8)); + shown = _e125; + } else { + let _e126 = code; + if (_e126 == 2i) { + let _e129 = (*s).n; + shown = ((_e129 * 0.5f) + vec3(0.5f, 0.5f, 0.5f)); + } else { + let _e132 = code; + if (_e132 == 3i) { + let _e135 = (*s).roughness; + shown = vec3(clamp(_e135, 0f, 1f)); + } else { + let _e138 = code; + if (_e138 == 4i) { + let _e141 = (*s).metallic; + shown = vec3(clamp(_e141, 0f, 1f)); + } else { + let _e144 = code; + if (_e144 == 5i) { + let _e147 = (*s).occlusion; + shown = vec3(clamp(_e147, 0f, 1f)); + } else { + let _e150 = code; + if (_e150 == 6i) { + let _e153 = (*s).emissive; + param_9 = clamp(_e153, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e155 = LinearToSrgb_u0028_vf3_u003b((¶m_9)); + shown = _e155; + } else { + let _e156 = code; + if (_e156 == 7i) { + let _e158 = v_texcoord_1; + let _e159 = fract(_e158); + shown = vec3(_e159.x, _e159.y, 0f); + } else { + let _e163 = code; + if (_e163 == 8i) { + let _e165 = (*lit); + param_10 = clamp(_e165, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e167 = LinearToSrgb_u0028_vf3_u003b((¶m_10)); + grey = dot(_e167, vec3(0.2126f, 0.7152f, 0.0722f)); + let _e169 = (*lit); + param_11 = _e169; + let _e170 = NonFinite_u0028_vf3_u003b((¶m_11)); + if _e170 { + local_2 = vec3(1f, 0f, 1f); + } else { + let _e171 = grey; + local_2 = vec3((_e171 * 0.5f)); + } + let _e174 = local_2; + shown = _e174; + } + } + } + } + } + } + } + } + let _e175 = g_premultiply; + if _e175 { + let _e177 = (*s).alpha; + local_3 = _e177; + } else { + local_3 = 1f; + } + let _e178 = local_3; + weight_2 = _e178; + let _e179 = shown; + param_12 = _e179; + let _e180 = SrgbToLinear_u0028_vf3_u003b((¶m_12)); + let _e181 = weight_2; + let _e182 = (_e180 * _e181); + let _e184 = (*s).alpha; + frag_color = vec4(_e182.x, _e182.y, _e182.z, _e184); + let _e190 = (*s).roughness; + param_13 = _e190; + WriteSurfaceGeometry_u0028_f1_u003b((¶m_13)); + WriteWeightedBlended_u0028_(); + return true; } fn MaterialLodBias_u0028_() -> f32 { - let _e78 = frag_info.target_origin[1u]; - return _e78; + let _e89 = frag_info.target_origin[1u]; + return _e89; } fn ReadSurface_u0028_() -> Surface { var texel: vec4; - var s: Surface; - var param_8: vec3; - var param_9: vec3; + var s_1: Surface; + var param_14: vec3; + var param_15: vec3; var cutoff: f32; var anchored: vec3; var noise: f32; - let _e83 = v_texcoord_1; - let _e84 = MaterialLodBias_u0028_(); - let _e85 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e83, _e84); - texel = _e85; - let _e86 = texel; - param_8 = _e86.xyz; - let _e88 = SrgbToLinear_u0028_vf3_u003b((¶m_8)); - let _e90 = frag_info.base_color; - param_9 = _e90.xyz; - let _e92 = SrgbToLinear_u0028_vf3_u003b((¶m_9)); - let _e94 = v_color_1; - s.albedo = ((_e88 * _e92) * _e94.xyz); - let _e99 = texel[3u]; - let _e102 = frag_info.base_color[3u]; - let _e105 = v_color_1[3u]; - s.alpha = ((_e99 * _e102) * _e105); - let _e109 = s.albedo; - g_albedo = _e109; - let _e112 = frag_info.material2_[0u]; - cutoff = _e112; - let _e113 = cutoff; - if (_e113 >= 0f) { - let _e116 = s.alpha; - let _e117 = cutoff; - if (_e116 < _e117) { + let _e94 = v_texcoord_1; + let _e95 = MaterialLodBias_u0028_(); + let _e96 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e94, _e95); + texel = _e96; + let _e97 = texel; + param_14 = _e97.xyz; + let _e99 = SrgbToLinear_u0028_vf3_u003b((¶m_14)); + let _e101 = frag_info.base_color; + param_15 = _e101.xyz; + let _e103 = SrgbToLinear_u0028_vf3_u003b((¶m_15)); + let _e105 = v_color_1; + s_1.albedo = ((_e99 * _e103) * _e105.xyz); + let _e110 = texel[3u]; + let _e113 = frag_info.base_color[3u]; + let _e116 = v_color_1[3u]; + s_1.alpha = ((_e110 * _e113) * _e116); + let _e120 = s_1.albedo; + g_albedo = _e120; + let _e123 = frag_info.material2_[0u]; + cutoff = _e123; + let _e124 = cutoff; + if (_e124 >= 0f) { + let _e127 = s_1.alpha; + let _e128 = cutoff; + if (_e127 < _e128) { discard; } } else { - let _e119 = cutoff; - if (_e119 < -1.5f) { - let _e121 = v_world_position_1; - anchored = floor((_e121 * 16f)); - let _e124 = anchored; - noise = fract((sin(dot(_e124, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e130 = s.alpha; - let _e131 = noise; - if (_e130 < _e131) { + let _e130 = cutoff; + if (_e130 < -1.5f) { + let _e132 = v_world_position_1; + anchored = floor((_e132 * 16f)); + let _e135 = anchored; + noise = fract((sin(dot(_e135, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e141 = s_1.alpha; + let _e142 = noise; + if (_e141 < _e142) { discard; } } } - let _e133 = cutoff; - let _e135 = cutoff; - g_premultiply = ((_e133 < 0f) && (_e135 > -0.75f)); - let _e138 = v_normal_1; - s.n = normalize(_e138); - let _e141 = gl_FrontFacing_1; - if !(_e141) { - let _e144 = s.n; - s.n = -(_e144); - } - let _e148 = frag_info.camera_position; - let _e150 = v_world_position_1; - s.v = normalize((_e148.xyz - _e150)); - let _e155 = s.n; - let _e157 = s.v; - s.n_dot_v = max(dot(_e155, _e157), 0.0001f); - let _e163 = frag_info.material[0u]; - s.metallic = clamp(_e163, 0f, 1f); - let _e168 = frag_info.material[1u]; - s.roughness = clamp(_e168, 0.02f, 1f); - let _e172 = frag_info.ambient_ground; - let _e175 = frag_info.ambient_sky; - let _e179 = s.n[1u]; - let _e186 = frag_info.material[2u]; - s.ambient = (mix(_e172.xyz, _e175.xyz, vec3(((_e179 * 0.5f) + 0.5f))) * _e186); - let _e191 = frag_info.frame_params[0u]; - s.exposure = max(_e191, 0f); - s.occlusion = 1f; - s.emissive = vec3(0f, 0f, 0f); - let _e196 = s; - return _e196; + let _e144 = cutoff; + let _e146 = cutoff; + g_premultiply = ((_e144 < 0f) && (_e146 > -0.75f)); + let _e149 = v_normal_1; + s_1.n = normalize(_e149); + let _e152 = gl_FrontFacing_1; + if !(_e152) { + let _e155 = s_1.n; + s_1.n = -(_e155); + } + let _e159 = frag_info.camera_position; + let _e161 = v_world_position_1; + s_1.v = normalize((_e159.xyz - _e161)); + let _e166 = s_1.n; + let _e168 = s_1.v; + s_1.n_dot_v = max(dot(_e166, _e168), 0.0001f); + let _e174 = frag_info.material[0u]; + s_1.metallic = clamp(_e174, 0f, 1f); + let _e179 = frag_info.material[1u]; + s_1.roughness = clamp(_e179, 0.02f, 1f); + let _e183 = frag_info.ambient_ground; + let _e186 = frag_info.ambient_sky; + let _e190 = s_1.n[1u]; + let _e197 = frag_info.material[2u]; + s_1.ambient = (mix(_e183.xyz, _e186.xyz, vec3(((_e190 * 0.5f) + 0.5f))) * _e197); + let _e202 = frag_info.frame_params[0u]; + s_1.exposure = max(_e202, 0f); + s_1.occlusion = 1f; + s_1.emissive = vec3(0f, 0f, 0f); + let _e207 = s_1; + return _e207; } fn main_1() { - var s_1: Surface; - var param_10: vec3; - var param_11: f32; + var s_2: Surface; + var param_16: Surface; + var param_17: vec3; + var param_18: vec3; + var param_19: f32; g_albedo = vec3(0f, 0f, 0f); g_debug_surface = vec3(0f, 0f, 0f); g_debug_surface_on = false; g_premultiply = false; - let _e79 = ReadSurface_u0028_(); - s_1 = _e79; + let _e92 = ReadSurface_u0028_(); + s_2 = _e92; g_albedo = vec3(0f, 0f, 0f); - let _e81 = s_1.albedo; - param_10 = _e81; - let _e83 = s_1.alpha; - param_11 = _e83; - WriteSurface_u0028_vf3_u003b_f1_u003b((¶m_10), (¶m_11)); + let _e93 = s_2; + param_16 = _e93; + let _e95 = s_2.albedo; + param_17 = _e95; + let _e96 = WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b((¶m_16), (¶m_17)); + if _e96 { + return; + } + let _e98 = s_2.albedo; + param_18 = _e98; + let _e100 = s_2.alpha; + param_19 = _e100; + WriteSurface_u0028_vf3_u003b_f1_u003b((¶m_18), (¶m_19)); return; } @fragment -fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, @builtin(front_facing) gl_FrontFacing: bool, @location(4) v_texcoord: vec2, @location(0) v_color: vec4, @location(3) v_tangent: vec4, @location(1) v_lightmap_uv: vec2) -> FragmentOutput { +fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, @builtin(front_facing) gl_FrontFacing: bool, @location(4) v_texcoord: vec2, @location(0) v_color: vec4, @builtin(position) gl_FragCoord: vec4, @location(3) v_tangent: vec4, @location(1) v_lightmap_uv: vec2) -> FragmentOutput { v_world_position_1 = v_world_position; v_normal_1 = v_normal; gl_FrontFacing_1 = gl_FrontFacing; v_texcoord_1 = v_texcoord; v_color_1 = v_color; + gl_FragCoord_1 = gl_FragCoord; v_tangent_1 = v_tangent; v_lightmap_uv_1 = v_lightmap_uv; main_1(); - let _e17 = frag_albedo; - let _e18 = frag_surface; - let _e19 = frag_color; - return FragmentOutput(_e17, _e18, _e19); + let _e19 = frag_albedo; + let _e20 = frag_surface; + let _e21 = frag_color; + return FragmentOutput(_e19, _e20, _e21); } ''', attributes: [], @@ -4619,7 +4765,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember( name: 'light_position', @@ -4716,6 +4862,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, shadow_bias: vec4, target_origin: vec4, + debug_view: vec4, } var g_albedo: vec3; @@ -5106,7 +5258,7 @@ fn main(@location(6) v_world_position: vec3, @location(4) v_texcoord: vec2< name: 'FragInfo', group: 1, binding: 1, - sizeInBytes: 896, + sizeInBytes: 912, members: [ WebGpuBlockMember( name: 'light_position', @@ -5203,6 +5355,11 @@ fn main(@location(6) v_world_position: vec3, @location(4) v_texcoord: vec2< offsetInBytes: 880, sizeInBytes: 16, ), + WebGpuBlockMember( + name: 'debug_view', + offsetInBytes: 896, + sizeInBytes: 16, + ), ], ), ], @@ -5518,6 +5675,7 @@ struct FragInfo { ambient_ground: vec4, shadow_bias: vec4, target_origin: vec4, + debug_view: vec4, } struct PointShadow { @@ -5616,10 +5774,10 @@ var metallic_roughness_texture_tex: texture_2d; var metallic_roughness_texture_smp: sampler; fn ViewDepth_u0028_() -> f32 { - let _e175 = v_world_position_1; - let _e177 = fog_info.eye; - let _e181 = fog_info.forward; - return dot((_e175 - _e177.xyz), _e181.xyz); + let _e184 = v_world_position_1; + let _e186 = fog_info.eye; + let _e190 = fog_info.forward; + return dot((_e184 - _e186.xyz), _e190.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -5628,22 +5786,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e180 = ViewDepth_u0028_(); - z = abs(_e180); - let _e182 = z; - near = (_e182 / 5f); - let _e184 = z; - far = (_e184 / 200f); - let _e186 = far; - let _e187 = far; - let _e189 = far; - far3_ = ((_e186 * _e187) * _e189); - let _e191 = (*alpha); - let _e192 = near; - let _e193 = near; - let _e196 = far3_; - let _e197 = far3_; - return (_e191 * clamp((10f / ((0.00001f + (_e192 * _e193)) + (_e196 * _e197))), 0.01f, 3000f)); + let _e189 = ViewDepth_u0028_(); + z = abs(_e189); + let _e191 = z; + near = (_e191 / 5f); + let _e193 = z; + far = (_e193 / 200f); + let _e195 = far; + let _e196 = far; + let _e198 = far; + far3_ = ((_e195 * _e196) * _e198); + let _e200 = (*alpha); + let _e201 = near; + let _e202 = near; + let _e205 = far3_; + let _e206 = far3_; + return (_e200 * clamp((10f / ((0.00001f + (_e201 * _e202)) + (_e205 * _e206))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -5655,39 +5813,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e184 = fog_info.forward[3u]; - mode = _e184; - let _e185 = mode; - if (_e185 > 0.5f) { - let _e188 = frag_color[3u]; - alpha_1 = _e188; - let _e189 = alpha_1; - param = _e189; - let _e190 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e190; - let _e191 = frag_color; - let _e193 = weight; - let _e194 = (_e191.xyz * _e193); - let _e195 = alpha_1; - let _e196 = weight; - accumulate = vec4(_e194.x, _e194.y, _e194.z, (_e195 * _e196)); - let _e202 = mode; - let _e204 = mode; - revealage = ((_e202 > 1.5f) && (_e204 < 2.5f)); - let _e207 = revealage; - if _e207 { - let _e208 = alpha_1; - local = vec4(_e208); + let _e193 = fog_info.forward[3u]; + mode = _e193; + let _e194 = mode; + if (_e194 > 0.5f) { + let _e197 = frag_color[3u]; + alpha_1 = _e197; + let _e198 = alpha_1; + param = _e198; + let _e199 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e199; + let _e200 = frag_color; + let _e202 = weight; + let _e203 = (_e200.xyz * _e202); + let _e204 = alpha_1; + let _e205 = weight; + accumulate = vec4(_e203.x, _e203.y, _e203.z, (_e204 * _e205)); + let _e211 = mode; + let _e213 = mode; + revealage = ((_e211 > 1.5f) && (_e213 < 2.5f)); + let _e216 = revealage; + if _e216 { + let _e217 = alpha_1; + local = vec4(_e217); } else { - let _e210 = accumulate; - local = _e210; + let _e219 = accumulate; + local = _e219; } - let _e211 = local; - frag_color = _e211; - let _e212 = mode; - if (_e212 > 2.5f) { - let _e214 = alpha_1; - frag_surface = vec4(_e214); + let _e220 = local; + frag_color = _e220; + let _e221 = mode; + if (_e221 > 2.5f) { + let _e223 = alpha_1; + frag_surface = vec4(_e223); } } return; @@ -5696,29 +5854,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e178 = (*n)[0u]; - let _e181 = (*n)[1u]; - let _e185 = (*n)[2u]; - let _e188 = (*n); - (*n) = (_e188 / vec3(((abs(_e178) + abs(_e181)) + abs(_e185)))); - let _e191 = (*n); - e = _e191.xy; + let _e187 = (*n)[0u]; + let _e190 = (*n)[1u]; let _e194 = (*n)[2u]; - if (_e194 < 0f) { - let _e196 = (*n); - let _e202 = (*n)[0u]; - let _e206 = (*n)[1u]; - e = ((vec2(1f) - abs(_e196.yx)) * vec2(select(-1f, 1f, (_e202 >= 0f)), select(-1f, 1f, (_e206 >= 0f)))); - } - let _e211 = e; - return ((_e211 * 0.5f) + vec2(0.5f)); + let _e197 = (*n); + (*n) = (_e197 / vec3(((abs(_e187) + abs(_e190)) + abs(_e194)))); + let _e200 = (*n); + e = _e200.xy; + let _e203 = (*n)[2u]; + if (_e203 < 0f) { + let _e205 = (*n); + let _e211 = (*n)[0u]; + let _e215 = (*n)[1u]; + e = ((vec2(1f) - abs(_e205.yx)) * vec2(select(-1f, 1f, (_e211 >= 0f)), select(-1f, 1f, (_e215 >= 0f)))); + } + let _e220 = e; + return ((_e220 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e176 = (*linear); - let _e178 = (*linear); - let _e182 = (*linear); - return mix((_e176 * 12.92f), ((pow(_e178, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e182)); + let _e185 = (*linear); + let _e187 = (*linear); + let _e191 = (*linear); + return mix((_e185 * 12.92f), ((pow(_e187, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e191)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -5726,37 +5884,37 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e179 = g_albedo; - param_1 = clamp(_e179, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e181 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e181.x, _e181.y, _e181.z, 1f); - let _e186 = g_debug_surface_on; - if _e186 { - let _e187 = g_debug_surface; - let _e188 = ViewDepth_u0028_(); - frag_surface = vec4(_e187.x, _e187.y, _e187.z, _e188); + let _e188 = g_albedo; + param_1 = clamp(_e188, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e190 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e190.x, _e190.y, _e190.z, 1f); + let _e195 = g_debug_surface_on; + if _e195 { + let _e196 = g_debug_surface; + let _e197 = ViewDepth_u0028_(); + frag_surface = vec4(_e196.x, _e196.y, _e196.z, _e197); return; } - let _e193 = v_normal_1; - geometric = normalize(_e193); - let _e195 = gl_FrontFacing_1; - if !(_e195) { - let _e197 = geometric; - geometric = -(_e197); - } - let _e199 = geometric; - param_2 = _e199; - let _e200 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e201 = (*roughness); - let _e203 = ViewDepth_u0028_(); - frag_surface = vec4(_e200.x, _e200.y, clamp(_e201, 0f, 1f), _e203); + let _e202 = v_normal_1; + geometric = normalize(_e202); + let _e204 = gl_FrontFacing_1; + if !(_e204) { + let _e206 = geometric; + geometric = -(_e206); + } + let _e208 = geometric; + param_2 = _e208; + let _e209 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e210 = (*roughness); + let _e212 = ViewDepth_u0028_(); + frag_surface = vec4(_e209.x, _e209.y, clamp(_e210, 0f, 1f), _e212); return; } fn EyeDistance_u0028_() -> f32 { - let _e175 = v_world_position_1; - let _e177 = fog_info.eye; - return distance(_e175, _e177.xyz); + let _e184 = v_world_position_1; + let _e186 = fog_info.eye; + return distance(_e184, _e186.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -5766,41 +5924,41 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var k: f32; var local_1: f32; - let _e183 = fog_info.fog[3u]; - density = _e183; - let _e184 = density; - if (_e184 <= 0f) { - let _e186 = (*color); - return _e186; - } - let _e187 = EyeDistance_u0028_(); - d = _e187; - let _e190 = fog_info.eye[3u]; - falloff = _e190; - let _e191 = falloff; - if (_e191 > 0f) { - let _e193 = falloff; - let _e195 = v_world_position_1[1u]; - let _e198 = fog_info.eye[1u]; - k = (_e193 * (_e195 - _e198)); - let _e201 = k; - if (abs(_e201) > 0.001f) { - let _e204 = k; - let _e208 = k; - local_1 = ((1f - exp(-(_e204))) / _e208); + let _e192 = fog_info.fog[3u]; + density = _e192; + let _e193 = density; + if (_e193 <= 0f) { + let _e195 = (*color); + return _e195; + } + let _e196 = EyeDistance_u0028_(); + d = _e196; + let _e199 = fog_info.eye[3u]; + falloff = _e199; + let _e200 = falloff; + if (_e200 > 0f) { + let _e202 = falloff; + let _e204 = v_world_position_1[1u]; + let _e207 = fog_info.eye[1u]; + k = (_e202 * (_e204 - _e207)); + let _e210 = k; + if (abs(_e210) > 0.001f) { + let _e213 = k; + let _e217 = k; + local_1 = ((1f - exp(-(_e213))) / _e217); } else { - let _e210 = k; - local_1 = (1f - (0.5f * _e210)); + let _e219 = k; + local_1 = (1f - (0.5f * _e219)); } - let _e213 = local_1; - let _e214 = density; - density = (_e214 * _e213); + let _e222 = local_1; + let _e223 = density; + density = (_e223 * _e222); } - let _e217 = fog_info.fog; - let _e219 = (*color); - let _e220 = density; - let _e222 = d; - return mix(_e217.xyz, _e219, vec3(clamp(exp((-(_e220) * _e222)), 0f, 1f))); + let _e226 = fog_info.fog; + let _e228 = (*color); + let _e229 = density; + let _e231 = d; + return mix(_e226.xyz, _e228, vec3(clamp(exp((-(_e229) * _e231)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -5808,55 +5966,195 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e181 = g_premultiply; - let _e182 = (*alpha_2); - weight_1 = select(1f, _e182, _e181); - let _e184 = (*linearColor); - param_3 = _e184; - let _e185 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e186 = weight_1; - let _e187 = (_e185 * _e186); - let _e188 = (*alpha_2); - frag_color = vec4(_e187.x, _e187.y, _e187.z, _e188); - let _e193 = (*roughness_1); - param_4 = _e193; + let _e190 = g_premultiply; + let _e191 = (*alpha_2); + weight_1 = select(1f, _e191, _e190); + let _e193 = (*linearColor); + param_3 = _e193; + let _e194 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e195 = weight_1; + let _e196 = (_e194 * _e195); + let _e197 = (*alpha_2); + frag_color = vec4(_e196.x, _e196.y, _e196.z, _e197); + let _e202 = (*roughness_1); + param_4 = _e202; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; } -fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s: ptr, light: ptr) -> vec3 { - let _e178 = (*s).albedo; - return _e178; +fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { + let _e185 = (*srgb); + let _e188 = (*srgb); + let _e193 = (*srgb); + return mix((_e185 / vec3(12.92f)), pow(((_e188 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e193)); +} + +fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { + var phi_2501_: bool; + + let _e185 = (*c); + let _e186 = (*c); + let _e188 = any((_e185 != _e186)); + phi_2501_ = _e188; + if !(_e188) { + let _e190 = (*c); + phi_2501_ = any((abs(_e190) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + } + let _e195 = phi_2501_; + return _e195; +} + +fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b(s: ptr, lit: ptr>) -> bool { + var view: f32; + var code: i32; + var shown: vec3; + var param_5: vec3; + var param_6: vec3; + var grey: f32; + var param_7: vec3; + var param_8: vec3; + var local_2: vec3; + var weight_2: f32; + var local_3: f32; + var param_9: vec3; + var param_10: f32; + + let _e201 = frag_info.debug_view[0u]; + view = _e201; + let _e202 = view; + if (_e202 < 0.5f) { + return false; + } + let _e205 = gl_FragCoord_1[0u]; + let _e208 = frag_info.debug_view[1u]; + let _e211 = frag_info.debug_view[2u]; + if (_e205 < (_e208 * _e211)) { + return false; + } + let _e214 = view; + code = i32((_e214 + 0.5f)); + shown = vec3(0f, 0f, 0f); + let _e217 = code; + if (_e217 == 1i) { + let _e220 = (*s).albedo; + param_5 = clamp(_e220, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e222 = LinearToSrgb_u0028_vf3_u003b((¶m_5)); + shown = _e222; + } else { + let _e223 = code; + if (_e223 == 2i) { + let _e226 = (*s).n; + shown = ((_e226 * 0.5f) + vec3(0.5f, 0.5f, 0.5f)); + } else { + let _e229 = code; + if (_e229 == 3i) { + let _e232 = (*s).roughness; + shown = vec3(clamp(_e232, 0f, 1f)); + } else { + let _e235 = code; + if (_e235 == 4i) { + let _e238 = (*s).metallic; + shown = vec3(clamp(_e238, 0f, 1f)); + } else { + let _e241 = code; + if (_e241 == 5i) { + let _e244 = (*s).occlusion; + shown = vec3(clamp(_e244, 0f, 1f)); + } else { + let _e247 = code; + if (_e247 == 6i) { + let _e250 = (*s).emissive; + param_6 = clamp(_e250, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e252 = LinearToSrgb_u0028_vf3_u003b((¶m_6)); + shown = _e252; + } else { + let _e253 = code; + if (_e253 == 7i) { + let _e255 = v_texcoord_1; + let _e256 = fract(_e255); + shown = vec3(_e256.x, _e256.y, 0f); + } else { + let _e260 = code; + if (_e260 == 8i) { + let _e262 = (*lit); + param_7 = clamp(_e262, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e264 = LinearToSrgb_u0028_vf3_u003b((¶m_7)); + grey = dot(_e264, vec3(0.2126f, 0.7152f, 0.0722f)); + let _e266 = (*lit); + param_8 = _e266; + let _e267 = NonFinite_u0028_vf3_u003b((¶m_8)); + if _e267 { + local_2 = vec3(1f, 0f, 1f); + } else { + let _e268 = grey; + local_2 = vec3((_e268 * 0.5f)); + } + let _e271 = local_2; + shown = _e271; + } + } + } + } + } + } + } + } + let _e272 = g_premultiply; + if _e272 { + let _e274 = (*s).alpha; + local_3 = _e274; + } else { + local_3 = 1f; + } + let _e275 = local_3; + weight_2 = _e275; + let _e276 = shown; + param_9 = _e276; + let _e277 = SrgbToLinear_u0028_vf3_u003b((¶m_9)); + let _e278 = weight_2; + let _e279 = (_e277 * _e278); + let _e281 = (*s).alpha; + frag_color = vec4(_e279.x, _e279.y, _e279.z, _e281); + let _e287 = (*s).roughness; + param_10 = _e287; + WriteSurfaceGeometry_u0028_f1_u003b((¶m_10)); + WriteWeightedBlended_u0028_(); + return true; +} + +fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_1: ptr, light: ptr) -> vec3 { + let _e187 = (*s_1).albedo; + return _e187; } fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e176 = (*i); - if (_e176 == 0i) { + let _e185 = (*i); + if (_e185 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e178 = (*i); - if (_e178 == 1i) { + let _e187 = (*i); + if (_e187 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e180 = (*i); - if (_e180 == 2i) { + let _e189 = (*i); + if (_e189 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e182 = (*i); - if (_e182 == 3i) { + let _e191 = (*i); + if (_e191 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e184 = (*i); - if (_e184 == 4i) { + let _e193 = (*i); + if (_e193 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e186 = (*i); - if (_e186 == 5i) { + let _e195 = (*i); + if (_e195 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e188 = (*i); - if (_e188 == 6i) { + let _e197 = (*i); + if (_e197 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -5864,208 +6162,208 @@ fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr, ca: ptr, sa: ptr, radius: ptr) -> vec2 { var p: vec2; - var param_5: i32; - - let _e181 = (*i_1); - param_5 = _e181; - let _e182 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); - p = _e182; - let _e184 = p[0u]; - let _e185 = (*ca); - let _e188 = p[1u]; - let _e189 = (*sa); + var param_11: i32; + + let _e190 = (*i_1); + param_11 = _e190; + let _e191 = PointShadowDiskTap_u0028_i1_u003b((¶m_11)); + p = _e191; let _e193 = p[0u]; - let _e194 = (*sa); + let _e194 = (*ca); let _e197 = p[1u]; - let _e198 = (*ca); - let _e202 = (*radius); - return (vec2(((_e184 * _e185) - (_e188 * _e189)), ((_e193 * _e194) + (_e197 * _e198))) * _e202); + let _e198 = (*sa); + let _e202 = p[0u]; + let _e203 = (*sa); + let _e206 = p[1u]; + let _e207 = (*ca); + let _e211 = (*radius); + return (vec2(((_e193 * _e194) - (_e197 * _e198)), ((_e202 * _e203) + (_e206 * _e207))) * _e211); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_2: vec2; + var local_4: vec2; var atlas: vec2; - let _e184 = point_shadow.params[0u]; - inset = _e184; - let _e185 = (*uv); - let _e186 = (*offset); - let _e188 = inset; - let _e189 = inset; - local_2 = clamp((_e185 + _e186), vec2(_e188), vec2((1f - _e189))); - let _e194 = local_2; - let _e195 = (*tile); - atlas = ((_e194 + _e195) * vec2(0.16666667f, 0.16666667f)); - let _e200 = point_shadow.params3_[0u]; - if (_e200 > 0.5f) { - let _e203 = atlas[1u]; - atlas[1u] = (1f - _e203); - } - let _e206 = atlas; - let _e207 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e206, 0f); - let _e209 = atlas; - let _e210 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e209, 0f); - let _e213 = (*range); - return (min(_e207.x, _e210.x) * _e213); + let _e193 = point_shadow.params[0u]; + inset = _e193; + let _e194 = (*uv); + let _e195 = (*offset); + let _e197 = inset; + let _e198 = inset; + local_4 = clamp((_e194 + _e195), vec2(_e197), vec2((1f - _e198))); + let _e203 = local_4; + let _e204 = (*tile); + atlas = ((_e203 + _e204) * vec2(0.16666667f, 0.16666667f)); + let _e209 = point_shadow.params3_[0u]; + if (_e209 > 0.5f) { + let _e212 = atlas[1u]; + atlas[1u] = (1f - _e212); + } + let _e215 = atlas; + let _e216 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e215, 0f); + let _e218 = atlas; + let _e219 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e218, 0f); + let _e222 = (*range); + return (min(_e216.x, _e219.x) * _e222); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { var stored: f32; - var param_6: vec2; - var param_7: vec2; - var param_8: vec2; - var param_9: f32; + var param_12: vec2; + var param_13: vec2; + var param_14: vec2; + var param_15: f32; - let _e185 = (*uv_1); - param_6 = _e185; - let _e186 = (*offset_1); - param_7 = _e186; - let _e187 = (*tile_1); - param_8 = _e187; - let _e188 = (*range_1); - param_9 = _e188; - let _e189 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); - stored = _e189; - let _e190 = stored; - let _e191 = (*range_1); - if (_e190 >= (_e191 * 0.999f)) { + let _e194 = (*uv_1); + param_12 = _e194; + let _e195 = (*offset_1); + param_13 = _e195; + let _e196 = (*tile_1); + param_14 = _e196; + let _e197 = (*range_1); + param_15 = _e197; + let _e198 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_12), (¶m_13), (¶m_14), (¶m_15)); + stored = _e198; + let _e199 = stored; + let _e200 = (*range_1); + if (_e199 >= (_e200 * 0.999f)) { return 1f; } - let _e194 = (*receiver); - let _e195 = stored; - return select(1f, 0f, (_e194 > _e195)); + let _e203 = (*receiver); + let _e204 = stored; + return select(1f, 0f, (_e203 > _e204)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { var lightRadius: f32; var minRadius: f32; var maxRadius: f32; - var param_10: vec2; - var param_11: vec2; - var param_12: vec2; - var param_13: f32; + var param_16: vec2; + var param_17: vec2; + var param_18: vec2; + var param_19: f32; var sum: f32; var count: f32; var i_2: i32; var stored_1: f32; - var param_14: i32; - var param_15: f32; - var param_16: f32; - var param_17: f32; - var param_18: vec2; - var param_19: vec2; - var param_20: vec2; + var param_20: i32; var param_21: f32; + var param_22: f32; + var param_23: f32; + var param_24: vec2; + var param_25: vec2; + var param_26: vec2; + var param_27: f32; var blocker: f32; var world: f32; (*blockerOut) = -1f; - let _e206 = point_shadow.params2_[1u]; - lightRadius = _e206; - let _e209 = point_shadow.params2_[0u]; - minRadius = _e209; - let _e212 = point_shadow.params2_[2u]; - maxRadius = _e212; - let _e213 = lightRadius; - if (_e213 <= 0f) { - let _e215 = (*uv_2); - param_10 = _e215; - param_11 = vec2(0f, 0f); - let _e216 = (*tile_2); - param_12 = _e216; - let _e217 = (*range_2); - param_13 = _e217; - let _e218 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); - (*blockerOut) = _e218; - let _e219 = minRadius; - return _e219; + let _e215 = point_shadow.params2_[1u]; + lightRadius = _e215; + let _e218 = point_shadow.params2_[0u]; + minRadius = _e218; + let _e221 = point_shadow.params2_[2u]; + maxRadius = _e221; + let _e222 = lightRadius; + if (_e222 <= 0f) { + let _e224 = (*uv_2); + param_16 = _e224; + param_17 = vec2(0f, 0f); + let _e225 = (*tile_2); + param_18 = _e225; + let _e226 = (*range_2); + param_19 = _e226; + let _e227 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_16), (¶m_17), (¶m_18), (¶m_19)); + (*blockerOut) = _e227; + let _e228 = minRadius; + return _e228; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e220 = i_2; - if (_e220 < 8i) { - let _e222 = i_2; - param_14 = _e222; - let _e223 = (*ca_1); - param_15 = _e223; - let _e224 = (*sa_1); - param_16 = _e224; - let _e225 = maxRadius; - param_17 = _e225; - let _e226 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); - let _e227 = (*uv_2); - param_18 = _e227; - param_19 = _e226; - let _e228 = (*tile_2); - param_20 = _e228; - let _e229 = (*range_2); - param_21 = _e229; - let _e230 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); - stored_1 = _e230; - let _e231 = stored_1; - let _e232 = (*range_2); - if (_e231 >= (_e232 * 0.999f)) { + let _e229 = i_2; + if (_e229 < 8i) { + let _e231 = i_2; + param_20 = _e231; + let _e232 = (*ca_1); + param_21 = _e232; + let _e233 = (*sa_1); + param_22 = _e233; + let _e234 = maxRadius; + param_23 = _e234; + let _e235 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_20), (¶m_21), (¶m_22), (¶m_23)); + let _e236 = (*uv_2); + param_24 = _e236; + param_25 = _e235; + let _e237 = (*tile_2); + param_26 = _e237; + let _e238 = (*range_2); + param_27 = _e238; + let _e239 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_24), (¶m_25), (¶m_26), (¶m_27)); + stored_1 = _e239; + let _e240 = stored_1; + let _e241 = (*range_2); + if (_e240 >= (_e241 * 0.999f)) { continue; } - let _e235 = stored_1; - let _e236 = (*receiver_1); - if (_e235 >= _e236) { + let _e244 = stored_1; + let _e245 = (*receiver_1); + if (_e244 >= _e245) { continue; } - let _e238 = stored_1; - let _e239 = sum; - sum = (_e239 + _e238); - let _e241 = count; - count = (_e241 + 1f); + let _e247 = stored_1; + let _e248 = sum; + sum = (_e248 + _e247); + let _e250 = count; + count = (_e250 + 1f); continue; } else { break; } continuing { - let _e243 = i_2; - i_2 = (_e243 + 1i); + let _e252 = i_2; + i_2 = (_e252 + 1i); } } - let _e245 = count; - if (_e245 < 0.5f) { + let _e254 = count; + if (_e254 < 0.5f) { return -1f; } - let _e247 = sum; - let _e248 = count; - blocker = max((_e247 / _e248), 0.0001f); - let _e251 = blocker; - (*blockerOut) = _e251; - let _e252 = lightRadius; - let _e253 = (*receiver_1); - let _e254 = blocker; - let _e258 = blocker; - world = ((_e252 * max((_e253 - _e254), 0f)) / _e258); - let _e260 = world; - let _e261 = (*receiver_1); - let _e263 = (*tanHalf); - let _e266 = minRadius; - let _e267 = maxRadius; - return clamp((_e260 / ((2f * _e261) * _e263)), _e266, _e267); + let _e256 = sum; + let _e257 = count; + blocker = max((_e256 / _e257), 0.0001f); + let _e260 = blocker; + (*blockerOut) = _e260; + let _e261 = lightRadius; + let _e262 = (*receiver_1); + let _e263 = blocker; + let _e267 = blocker; + world = ((_e261 * max((_e262 - _e263), 0f)) / _e267); + let _e269 = world; + let _e270 = (*receiver_1); + let _e272 = (*tanHalf); + let _e275 = minRadius; + let _e276 = maxRadius; + return clamp((_e269 / ((2f * _e270) * _e272)), _e275, _e276); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_3: vec2; + var local_5: vec2; - let _e177 = (*rows); - if (_e177 > 0f) { - let _e180 = gl_FragCoord_1[0u]; - let _e181 = (*rows); - let _e183 = gl_FragCoord_1[1u]; - local_3 = vec2(_e180, (_e181 - _e183)); + let _e186 = (*rows); + if (_e186 > 0f) { + let _e189 = gl_FragCoord_1[0u]; + let _e190 = (*rows); + let _e192 = gl_FragCoord_1[1u]; + local_5 = vec2(_e189, (_e190 - _e192)); } else { - let _e186 = gl_FragCoord_1; - local_3 = _e186.xy; + let _e195 = gl_FragCoord_1; + local_5 = _e195.xy; } - let _e188 = local_3; - return _e188; + let _e197 = local_5; + return _e197; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -6088,292 +6386,292 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var receiver_2: f32; var noise: f32; - var param_22: f32; + var param_28: f32; var angle: f32; var ca_2: f32; var sa_2: f32; var tanHalf_1: f32; var blocker_1: f32; var radius_1: f32; - var param_23: vec2; - var param_24: vec2; - var param_25: f32; - var param_26: f32; - var param_27: f32; - var param_28: f32; - var param_29: f32; - var param_30: f32; - var local_4: vec3; - var lit: f32; - var param_31: vec2; - var param_32: vec2; - var param_33: vec2; + var param_29: vec2; + var param_30: vec2; + var param_31: f32; + var param_32: f32; + var param_33: f32; var param_34: f32; var param_35: f32; + var param_36: f32; + var local_6: vec3; + var lit_1: f32; + var param_37: vec2; + var param_38: vec2; + var param_39: vec2; + var param_40: f32; + var param_41: f32; var i_3: i32; - var param_36: i32; - var param_37: f32; - var param_38: f32; - var param_39: f32; - var param_40: vec2; - var param_41: vec2; - var param_42: vec2; + var param_42: i32; var param_43: f32; var param_44: f32; - var phi_2181_: bool; - var phi_2242_: bool; - - let _e229 = (*lightIndex); - let _e233 = point_shadow.slots[_e229][0u]; - slot = i32((_e233 + 0.5f)); - let _e236 = (*lightIndex); - let _e240 = point_shadow.slots[_e236][0u]; - if (_e240 < 0f) { + var param_45: f32; + var param_46: vec2; + var param_47: vec2; + var param_48: vec2; + var param_49: f32; + var param_50: f32; + var phi_2190_: bool; + var phi_2251_: bool; + + let _e238 = (*lightIndex); + let _e242 = point_shadow.slots[_e238][0u]; + slot = i32((_e242 + 0.5f)); + let _e245 = (*lightIndex); + let _e249 = point_shadow.slots[_e245][0u]; + if (_e249 < 0f) { return 1f; } - let _e244 = point_shadow.params[2u]; - strength = _e244; - let _e245 = strength; - if (_e245 <= 0f) { + let _e253 = point_shadow.params[2u]; + strength = _e253; + let _e254 = strength; + if (_e254 <= 0f) { return 1f; } - let _e247 = slot; - let _e250 = point_shadow.lights[_e247]; - let _e252 = (*world_1); - toLight = (_e250.xyz - _e252); - let _e254 = toLight; - toLightLength = max(length(_e254), 0.000001f); - let _e257 = (*normal); - let _e258 = toLight; - let _e259 = toLightLength; - nDotL = max(dot(_e257, (_e258 / vec3(_e259))), 0.15f); - let _e264 = nDotL; - let _e265 = nDotL; - let _e270 = nDotL; - let _e271 = nDotL; - slope = min((sqrt(max((1f - (_e264 * _e265)), 0f)) / (_e270 * _e271)), 8f); - let _e275 = toLightLength; - let _e277 = (*lightIndex); - let _e281 = point_shadow.slots[_e277][2u]; - let _e286 = point_shadow.params3_[1u]; - texel = (((2f * _e275) * max(_e281, 0.0001f)) * _e286); - let _e288 = (*world_1); - let _e289 = (*normal); - let _e290 = texel; - let _e294 = point_shadow.params[3u]; - let _e296 = slope; - origin = (_e288 + (((_e289 * _e290) * _e294) * (1f + _e296))); - let _e300 = origin; - let _e301 = slot; - let _e304 = point_shadow.lights[_e301]; - toFragment = (_e300 - _e304.xyz); - let _e307 = toFragment; - distance_ = length(_e307); - let _e309 = slot; - let _e313 = point_shadow.lights[_e309][3u]; - range_3 = max(_e313, 0.0001f); - let _e315 = distance_; - let _e316 = range_3; - if (_e315 >= _e316) { + let _e256 = slot; + let _e259 = point_shadow.lights[_e256]; + let _e261 = (*world_1); + toLight = (_e259.xyz - _e261); + let _e263 = toLight; + toLightLength = max(length(_e263), 0.000001f); + let _e266 = (*normal); + let _e267 = toLight; + let _e268 = toLightLength; + nDotL = max(dot(_e266, (_e267 / vec3(_e268))), 0.15f); + let _e273 = nDotL; + let _e274 = nDotL; + let _e279 = nDotL; + let _e280 = nDotL; + slope = min((sqrt(max((1f - (_e273 * _e274)), 0f)) / (_e279 * _e280)), 8f); + let _e284 = toLightLength; + let _e286 = (*lightIndex); + let _e290 = point_shadow.slots[_e286][2u]; + let _e295 = point_shadow.params3_[1u]; + texel = (((2f * _e284) * max(_e290, 0.0001f)) * _e295); + let _e297 = (*world_1); + let _e298 = (*normal); + let _e299 = texel; + let _e303 = point_shadow.params[3u]; + let _e305 = slope; + origin = (_e297 + (((_e298 * _e299) * _e303) * (1f + _e305))); + let _e309 = origin; + let _e310 = slot; + let _e313 = point_shadow.lights[_e310]; + toFragment = (_e309 - _e313.xyz); + let _e316 = toFragment; + distance_ = length(_e316); + let _e318 = slot; + let _e322 = point_shadow.lights[_e318][3u]; + range_3 = max(_e322, 0.0001f); + let _e324 = distance_; + let _e325 = range_3; + if (_e324 >= _e325) { return 1f; } face = 0i; - let _e318 = (*lightIndex); - let _e322 = point_shadow.slots[_e318][1u]; - if (_e322 < 0.5f) { - let _e324 = toFragment; - a = abs(_e324); - let _e327 = a[0u]; - let _e329 = a[1u]; - let _e330 = (_e327 >= _e329); - phi_2181_ = _e330; - if _e330 { - let _e332 = a[0u]; - let _e334 = a[2u]; - phi_2181_ = (_e332 >= _e334); - } - let _e337 = phi_2181_; - if _e337 { - let _e339 = toFragment[0u]; - face = select(1i, 0i, (_e339 > 0f)); + let _e327 = (*lightIndex); + let _e331 = point_shadow.slots[_e327][1u]; + if (_e331 < 0.5f) { + let _e333 = toFragment; + a = abs(_e333); + let _e336 = a[0u]; + let _e338 = a[1u]; + let _e339 = (_e336 >= _e338); + phi_2190_ = _e339; + if _e339 { + let _e341 = a[0u]; + let _e343 = a[2u]; + phi_2190_ = (_e341 >= _e343); + } + let _e346 = phi_2190_; + if _e346 { + let _e348 = toFragment[0u]; + face = select(1i, 0i, (_e348 > 0f)); } else { - let _e343 = a[1u]; - let _e345 = a[2u]; - if (_e343 >= _e345) { - let _e348 = toFragment[1u]; - face = select(3i, 2i, (_e348 > 0f)); + let _e352 = a[1u]; + let _e354 = a[2u]; + if (_e352 >= _e354) { + let _e357 = toFragment[1u]; + face = select(3i, 2i, (_e357 > 0f)); } else { - let _e352 = toFragment[2u]; - face = select(5i, 4i, (_e352 > 0f)); + let _e361 = toFragment[2u]; + face = select(5i, 4i, (_e361 > 0f)); } } } - let _e355 = slot; - let _e357 = face; - let _e361 = point_shadow.faces[((_e355 * 6i) + _e357)]; - let _e362 = origin; - clip = (_e361 * vec4(_e362.x, _e362.y, _e362.z, 1f)); - let _e369 = clip[3u]; - if (_e369 <= 0f) { + let _e364 = slot; + let _e366 = face; + let _e370 = point_shadow.faces[((_e364 * 6i) + _e366)]; + let _e371 = origin; + clip = (_e370 * vec4(_e371.x, _e371.y, _e371.z, 1f)); + let _e378 = clip[3u]; + if (_e378 <= 0f) { return 1f; } - let _e371 = clip; - let _e374 = clip[3u]; - ndc = (_e371.xy / vec2(_e374)); - let _e378 = ndc[0u]; - let _e380 = (abs(_e378) > 1f); - phi_2242_ = _e380; - if !(_e380) { - let _e383 = ndc[1u]; - phi_2242_ = (abs(_e383) > 1f); - } - let _e387 = phi_2242_; - if _e387 { + let _e380 = clip; + let _e383 = clip[3u]; + ndc = (_e380.xy / vec2(_e383)); + let _e387 = ndc[0u]; + let _e389 = (abs(_e387) > 1f); + phi_2251_ = _e389; + if !(_e389) { + let _e392 = ndc[1u]; + phi_2251_ = (abs(_e392) > 1f); + } + let _e396 = phi_2251_; + if _e396 { return 1f; } - let _e389 = ndc[0u]; - let _e393 = ndc[1u]; - uv_3 = vec2(((_e389 * 0.5f) + 0.5f), (0.5f - (_e393 * 0.5f))); - let _e397 = face; - let _e399 = slot; - tile_3 = vec2(f32(_e397), f32(_e399)); - let _e402 = distance_; - let _e405 = point_shadow.params[1u]; - receiver_2 = (_e402 - _e405); - let _e409 = frag_info.target_origin[0u]; - param_22 = _e409; - let _e410 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); - noise = fract((52.982918f * fract(dot(_e410, vec2(0.06711056f, 0.00583715f))))); - let _e415 = noise; - angle = (_e415 * 6.2831855f); - let _e417 = angle; - ca_2 = cos(_e417); - let _e419 = angle; - sa_2 = sin(_e419); - let _e421 = (*lightIndex); - let _e425 = point_shadow.slots[_e421][2u]; - tanHalf_1 = max(_e425, 0.0001f); + let _e398 = ndc[0u]; + let _e402 = ndc[1u]; + uv_3 = vec2(((_e398 * 0.5f) + 0.5f), (0.5f - (_e402 * 0.5f))); + let _e406 = face; + let _e408 = slot; + tile_3 = vec2(f32(_e406), f32(_e408)); + let _e411 = distance_; + let _e414 = point_shadow.params[1u]; + receiver_2 = (_e411 - _e414); + let _e418 = frag_info.target_origin[0u]; + param_28 = _e418; + let _e419 = FragCoordFromTop_u0028_f1_u003b((¶m_28)); + noise = fract((52.982918f * fract(dot(_e419, vec2(0.06711056f, 0.00583715f))))); + let _e424 = noise; + angle = (_e424 * 6.2831855f); + let _e426 = angle; + ca_2 = cos(_e426); + let _e428 = angle; + sa_2 = sin(_e428); + let _e430 = (*lightIndex); + let _e434 = point_shadow.slots[_e430][2u]; + tanHalf_1 = max(_e434, 0.0001f); blocker_1 = -1f; - let _e427 = uv_3; - param_23 = _e427; - let _e428 = tile_3; - param_24 = _e428; - let _e429 = range_3; - param_25 = _e429; - let _e430 = receiver_2; - param_26 = _e430; - let _e431 = ca_2; - param_27 = _e431; - let _e432 = sa_2; - param_28 = _e432; - let _e433 = tanHalf_1; - param_29 = _e433; - let _e434 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e435 = param_30; - blocker_1 = _e435; - radius_1 = _e434; - let _e438 = point_shadow.params2_[3u]; - if (_e438 > 0.5f) { + let _e436 = uv_3; + param_29 = _e436; + let _e437 = tile_3; + param_30 = _e437; + let _e438 = range_3; + param_31 = _e438; + let _e439 = receiver_2; + param_32 = _e439; + let _e440 = ca_2; + param_33 = _e440; + let _e441 = sa_2; + param_34 = _e441; + let _e442 = tanHalf_1; + param_35 = _e442; + let _e443 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_29), (¶m_30), (¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35), (¶m_36)); + let _e444 = param_36; + blocker_1 = _e444; + radius_1 = _e443; + let _e447 = point_shadow.params2_[3u]; + if (_e447 > 0.5f) { g_debug_surface_on = true; - let _e440 = radius_1; - if (_e440 < 0f) { - local_4 = vec3(0f, 0f, 1f); + let _e449 = radius_1; + if (_e449 < 0f) { + local_6 = vec3(0f, 0f, 1f); } else { - let _e442 = radius_1; - let _e445 = point_shadow.params2_[2u]; - let _e449 = blocker_1; - let _e450 = range_3; - local_4 = vec3(clamp((_e442 / max(_e445, 0.000001f)), 0f, 1f), clamp((_e449 / _e450), 0f, 1f), 0f); + let _e451 = radius_1; + let _e454 = point_shadow.params2_[2u]; + let _e458 = blocker_1; + let _e459 = range_3; + local_6 = vec3(clamp((_e451 / max(_e454, 0.000001f)), 0f, 1f), clamp((_e458 / _e459), 0f, 1f), 0f); } - let _e454 = local_4; - g_debug_surface = _e454; + let _e463 = local_6; + g_debug_surface = _e463; } - let _e455 = radius_1; - if (_e455 < 0f) { + let _e464 = radius_1; + if (_e464 < 0f) { return 1f; } - let _e457 = uv_3; - param_31 = _e457; - param_32 = vec2(0f, 0f); - let _e458 = tile_3; - param_33 = _e458; - let _e459 = range_3; - param_34 = _e459; - let _e460 = receiver_2; - param_35 = _e460; - let _e461 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); - lit = _e461; - let _e462 = radius_1; - if (_e462 > 0f) { + let _e466 = uv_3; + param_37 = _e466; + param_38 = vec2(0f, 0f); + let _e467 = tile_3; + param_39 = _e467; + let _e468 = range_3; + param_40 = _e468; + let _e469 = receiver_2; + param_41 = _e469; + let _e470 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_37), (¶m_38), (¶m_39), (¶m_40), (¶m_41)); + lit_1 = _e470; + let _e471 = radius_1; + if (_e471 > 0f) { i_3 = 0i; loop { - let _e464 = i_3; - if (_e464 < 8i) { - let _e466 = i_3; - param_36 = _e466; - let _e467 = ca_2; - param_37 = _e467; - let _e468 = sa_2; - param_38 = _e468; - let _e469 = radius_1; - param_39 = _e469; - let _e470 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - let _e471 = uv_3; - param_40 = _e471; - param_41 = _e470; - let _e472 = tile_3; - param_42 = _e472; - let _e473 = range_3; - param_43 = _e473; - let _e474 = receiver_2; - param_44 = _e474; - let _e475 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); - let _e476 = lit; - lit = (_e476 + _e475); + let _e473 = i_3; + if (_e473 < 8i) { + let _e475 = i_3; + param_42 = _e475; + let _e476 = ca_2; + param_43 = _e476; + let _e477 = sa_2; + param_44 = _e477; + let _e478 = radius_1; + param_45 = _e478; + let _e479 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_42), (¶m_43), (¶m_44), (¶m_45)); + let _e480 = uv_3; + param_46 = _e480; + param_47 = _e479; + let _e481 = tile_3; + param_48 = _e481; + let _e482 = range_3; + param_49 = _e482; + let _e483 = receiver_2; + param_50 = _e483; + let _e484 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_46), (¶m_47), (¶m_48), (¶m_49), (¶m_50)); + let _e485 = lit_1; + lit_1 = (_e485 + _e484); continue; } else { break; } continuing { - let _e478 = i_3; - i_3 = (_e478 + 1i); + let _e487 = i_3; + i_3 = (_e487 + 1i); } } - let _e480 = lit; - lit = (_e480 * 0.11111111f); + let _e489 = lit_1; + lit_1 = (_e489 * 0.11111111f); } - let _e482 = lit; - let _e483 = strength; - return mix(1f, _e482, clamp(_e483, 0f, 1f)); + let _e491 = lit_1; + let _e492 = strength; + return mix(1f, _e491, clamp(_e492, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r: f32; var theta: f32; - let _e180 = (*i_4); - let _e183 = (*n_1); - r = sqrt(((f32(_e180) + 0.5f) / f32(_e183))); - let _e187 = (*i_4); - let _e190 = (*turn); - theta = ((f32(_e187) * 2.3999631f) + _e190); - let _e192 = r; - let _e193 = theta; - let _e195 = theta; - return (vec2(cos(_e193), sin(_e195)) * _e192); + let _e189 = (*i_4); + let _e192 = (*n_1); + r = sqrt(((f32(_e189) + 0.5f) / f32(_e192))); + let _e196 = (*i_4); + let _e199 = (*turn); + theta = ((f32(_e196) * 2.3999631f) + _e199); + let _e201 = r; + let _e202 = theta; + let _e204 = theta; + return (vec2(cos(_e202), sin(_e204)) * _e201); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; - var param_45: f32; + var param_51: f32; - let _e179 = frag_info.target_origin[0u]; - param_45 = _e179; - let _e180 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); - let _e183 = frag_info.target_origin[3u]; - at = (_e180 + vec2((5.588238f * max(_e183, 0f)))); - let _e188 = at; - return fract((52.982918f * fract(dot(_e188, vec2(0.06711056f, 0.00583715f))))); + let _e188 = frag_info.target_origin[0u]; + param_51 = _e188; + let _e189 = FragCoordFromTop_u0028_f1_u003b((¶m_51)); + let _e192 = frag_info.target_origin[3u]; + at = (_e189 + vec2((5.588238f * max(_e192, 0f)))); + let _e197 = at; + return fract((52.982918f * fract(dot(_e197, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -6382,88 +6680,88 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_2: f32; - let _e177 = (*depth); - d_2 = ((2f * clamp(_e177, 0f, 1f)) - 1f); - let _e181 = d_2; - let _e184 = d_2; - return vec2(exp((40f * _e181)), -(exp((-5f * _e184)))); + let _e186 = (*depth); + d_2 = ((2f * clamp(_e186, 0f, 1f)) - 1f); + let _e190 = d_2; + let _e193 = d_2; + return vec2(exp((40f * _e190)), -(exp((-5f * _e193)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { var warped: vec2; - var param_46: f32; + var param_52: f32; var scale: vec2; var positive: f32; - var param_47: vec2; - var param_48: f32; - var param_49: f32; - var param_50: f32; - var negative: f32; - var param_51: vec2; - var param_52: f32; - var param_53: f32; + var param_53: vec2; var param_54: f32; + var param_55: f32; + var param_56: f32; + var negative: f32; + var param_57: vec2; + var param_58: f32; + var param_59: f32; + var param_60: f32; - let _e191 = (*depth_1); - param_46 = _e191; - let _e192 = EvsmWarp_u0028_f1_u003b((¶m_46)); - warped = _e192; - let _e193 = warped; - scale = (vec2(0.004f, 0.0005f) * _e193); - let _e196 = scale[0u]; - let _e198 = scale[0u]; - let _e200 = (*moments_1); - param_47 = _e200.xy; - let _e203 = warped[0u]; - param_48 = _e203; - param_49 = (_e196 * _e198); - let _e204 = (*bleed_1); - param_50 = _e204; - let _e205 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); - positive = _e205; - let _e207 = scale[1u]; - let _e209 = scale[1u]; - let _e211 = (*moments_1); - param_51 = _e211.zw; - let _e214 = warped[1u]; - param_52 = _e214; - param_53 = (_e207 * _e209); - let _e215 = (*bleed_1); - param_54 = _e215; - let _e216 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); - negative = _e216; - let _e217 = positive; - let _e218 = negative; - return min(_e217, _e218); + let _e200 = (*depth_1); + param_52 = _e200; + let _e201 = EvsmWarp_u0028_f1_u003b((¶m_52)); + warped = _e201; + let _e202 = warped; + scale = (vec2(0.004f, 0.0005f) * _e202); + let _e205 = scale[0u]; + let _e207 = scale[0u]; + let _e209 = (*moments_1); + param_53 = _e209.xy; + let _e212 = warped[0u]; + param_54 = _e212; + param_55 = (_e205 * _e207); + let _e213 = (*bleed_1); + param_56 = _e213; + let _e214 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_53), (¶m_54), (¶m_55), (¶m_56)); + positive = _e214; + let _e216 = scale[1u]; + let _e218 = scale[1u]; + let _e220 = (*moments_1); + param_57 = _e220.zw; + let _e223 = warped[1u]; + param_58 = _e223; + param_59 = (_e216 * _e218); + let _e224 = (*bleed_1); + param_60 = _e224; + let _e225 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_57), (¶m_58), (¶m_59), (¶m_60)); + negative = _e225; + let _e226 = positive; + let _e227 = negative; + return min(_e226, _e227); } -fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_1: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { +fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { var strength_1: f32; var cascadeCount: i32; var viewDistance: f32; @@ -6477,8 +6775,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_5: mat4x4; - var local_6: mat4x4; + var local_7: mat4x4; + var local_8: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -6492,17 +6790,17 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_7: f32; - var local_8: f32; + var local_9: f32; + var local_10: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; var softness: f32; - var lit_1: f32; + var lit_2: f32; var moments_2: vec4; - var param_55: vec4; - var param_56: f32; - var param_57: f32; + var param_61: vec4; + var param_62: f32; + var param_63: f32; var y: i32; var x: i32; var occluder: f32; @@ -6513,65 +6811,65 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var blockerCount: f32; var i_5: i32; var disc: vec2; - var param_58: i32; - var param_59: i32; - var param_60: f32; + var param_64: i32; + var param_65: i32; + var param_66: f32; var tapBias: f32; var occluder_1: f32; var gap: f32; var radius_2: f32; var i_6: i32; var disc_1: vec2; - var param_61: i32; - var param_62: i32; - var param_63: f32; + var param_67: i32; + var param_68: i32; + var param_69: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3276_: bool; - var phi_3287_: bool; - var phi_3448_: bool; - var phi_3455_: bool; - var phi_3462_: bool; - - let _e243 = frag_info.shadow_params[3u]; - strength_1 = _e243; - let _e244 = strength_1; - if (_e244 <= 0f) { + var phi_3446_: bool; + var phi_3457_: bool; + var phi_3618_: bool; + var phi_3625_: bool; + var phi_3632_: bool; + + let _e252 = frag_info.shadow_params[3u]; + strength_1 = _e252; + let _e253 = strength_1; + if (_e253 <= 0f) { return 1f; } - let _e246 = (*lightIndex_1); - let _e249 = frag_info.frame_params[2u]; - if (_e246 != i32((_e249 + 0.5f))) { + let _e255 = (*lightIndex_1); + let _e258 = frag_info.frame_params[2u]; + if (_e255 != i32((_e258 + 0.5f))) { return 1f; } - let _e255 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e255 + 0.5f)); - let _e258 = v_world_position_1; - let _e260 = frag_info.camera_position; - viewDistance = length((_e258 - _e260.xyz)); + let _e264 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e264 + 0.5f)); + let _e267 = v_world_position_1; + let _e269 = frag_info.camera_position; + viewDistance = length((_e267 - _e269.xyz)); cascade = 0i; - let _e264 = cascadeCount; - let _e265 = (_e264 > 1i); - phi_3276_ = _e265; - if _e265 { - let _e266 = viewDistance; - let _e269 = frag_info.shadow_cascades[0u]; - phi_3276_ = (_e266 > _e269); - } - let _e272 = phi_3276_; - if _e272 { - cascade = 1i; - } let _e273 = cascadeCount; - let _e274 = (_e273 > 2i); - phi_3287_ = _e274; + let _e274 = (_e273 > 1i); + phi_3446_ = _e274; if _e274 { let _e275 = viewDistance; - let _e278 = frag_info.shadow_cascades[1u]; - phi_3287_ = (_e275 > _e278); + let _e278 = frag_info.shadow_cascades[0u]; + phi_3446_ = (_e275 > _e278); } - let _e281 = phi_3287_; + let _e281 = phi_3446_; if _e281 { + cascade = 1i; + } + let _e282 = cascadeCount; + let _e283 = (_e282 > 2i); + phi_3457_ = _e283; + if _e283 { + let _e284 = viewDistance; + let _e287 = frag_info.shadow_cascades[1u]; + phi_3457_ = (_e284 > _e287); + } + let _e290 = phi_3457_; + if _e290 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -6582,232 +6880,232 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e282 = attempt; - if (_e282 < 3i) { - let _e284 = cascade; - let _e285 = attempt; - which = (_e284 + _e285); - let _e287 = which; - let _e288 = cascadeCount; - if (_e287 >= _e288) { + let _e291 = attempt; + if (_e291 < 3i) { + let _e293 = cascade; + let _e294 = attempt; + which = (_e293 + _e294); + let _e296 = which; + let _e297 = cascadeCount; + if (_e296 >= _e297) { break; } - let _e290 = which; - if (_e290 == 0i) { - let _e293 = frag_info.shadow_matrix; - local_5 = _e293; + let _e299 = which; + if (_e299 == 0i) { + let _e302 = frag_info.shadow_matrix; + local_7 = _e302; } else { - let _e294 = which; - if (_e294 == 1i) { - let _e297 = frag_info.shadow_matrix_far; - local_6 = _e297; + let _e303 = which; + if (_e303 == 1i) { + let _e306 = frag_info.shadow_matrix_far; + local_8 = _e306; } else { - let _e299 = frag_info.shadow_matrix_farthest; - local_6 = _e299; + let _e308 = frag_info.shadow_matrix_farthest; + local_8 = _e308; } - let _e300 = local_6; - local_5 = _e300; - } - let _e301 = local_5; - matrix = _e301; - let _e304 = matrix[0][0u]; - let _e307 = matrix[1][0u]; - let _e310 = matrix[2][0u]; - axisX = vec3(_e304, _e307, _e310); - let _e314 = matrix[0][1u]; - let _e317 = matrix[1][1u]; - let _e320 = matrix[2][1u]; - axisY = vec3(_e314, _e317, _e320); - let _e322 = axisX; - rowX = max(length(_e322), 0.000001f); - let _e325 = axisY; - rowY = max(length(_e325), 0.000001f); - let _e330 = frag_info.shadow_cascades[3u]; - let _e332 = rowX; - texelMetres = ((2f * _e330) / _e332); - let _e336 = frag_info.shadow_cascades[3u]; - let _e339 = (*s_1).n; - let _e340 = axisX; - let _e343 = rowX; - let _e344 = rowX; - let _e348 = (*s_1).n; - let _e349 = axisY; - let _e352 = rowY; - let _e353 = rowY; - reach = ((3f * _e336) * ((abs(dot(_e339, _e340)) / (_e343 * _e344)) + (abs(dot(_e348, _e349)) / (_e352 * _e353)))); - let _e360 = frag_info.shadow_params[2u]; - let _e361 = texelMetres; - flatOffset = min(_e360, _e361); - let _e363 = v_world_position_1; - let _e365 = (*s_1).n; - let _e366 = flatOffset; - let _e367 = reach; - origin_1 = (_e363 + (_e365 * (_e366 + _e367))); - let _e371 = matrix; - let _e372 = origin_1; - lightSpace = (_e371 * vec4(_e372.x, _e372.y, _e372.z, 1f)); - let _e379 = lightSpace[3u]; - if (_e379 <= 0f) { + let _e309 = local_8; + local_7 = _e309; + } + let _e310 = local_7; + matrix = _e310; + let _e313 = matrix[0][0u]; + let _e316 = matrix[1][0u]; + let _e319 = matrix[2][0u]; + axisX = vec3(_e313, _e316, _e319); + let _e323 = matrix[0][1u]; + let _e326 = matrix[1][1u]; + let _e329 = matrix[2][1u]; + axisY = vec3(_e323, _e326, _e329); + let _e331 = axisX; + rowX = max(length(_e331), 0.000001f); + let _e334 = axisY; + rowY = max(length(_e334), 0.000001f); + let _e339 = frag_info.shadow_cascades[3u]; + let _e341 = rowX; + texelMetres = ((2f * _e339) / _e341); + let _e345 = frag_info.shadow_cascades[3u]; + let _e348 = (*s_2).n; + let _e349 = axisX; + let _e352 = rowX; + let _e353 = rowX; + let _e357 = (*s_2).n; + let _e358 = axisY; + let _e361 = rowY; + let _e362 = rowY; + reach = ((3f * _e345) * ((abs(dot(_e348, _e349)) / (_e352 * _e353)) + (abs(dot(_e357, _e358)) / (_e361 * _e362)))); + let _e369 = frag_info.shadow_params[2u]; + let _e370 = texelMetres; + flatOffset = min(_e369, _e370); + let _e372 = v_world_position_1; + let _e374 = (*s_2).n; + let _e375 = flatOffset; + let _e376 = reach; + origin_1 = (_e372 + (_e374 * (_e375 + _e376))); + let _e380 = matrix; + let _e381 = origin_1; + lightSpace = (_e380 * vec4(_e381.x, _e381.y, _e381.z, 1f)); + let _e388 = lightSpace[3u]; + if (_e388 <= 0f) { continue; } - let _e381 = lightSpace; - let _e384 = lightSpace[3u]; - candidate = (_e381.xyz / vec3(_e384)); - let _e388 = candidate[0u]; - let _e392 = candidate[1u]; - inTile = vec2(((_e388 * 0.5f) + 0.5f), (0.5f - (_e392 * 0.5f))); - let _e397 = inTile[0u]; - let _e398 = (_e397 < 0f); - phi_3448_ = _e398; - if !(_e398) { - let _e401 = inTile[0u]; - phi_3448_ = (_e401 > 1f); - } - let _e404 = phi_3448_; - phi_3455_ = _e404; - if !(_e404) { - let _e407 = inTile[1u]; - phi_3455_ = (_e407 < 0f); - } - let _e410 = phi_3455_; - phi_3462_ = _e410; - if !(_e410) { - let _e413 = inTile[1u]; - phi_3462_ = (_e413 > 1f); - } - let _e416 = phi_3462_; - if _e416 { + let _e390 = lightSpace; + let _e393 = lightSpace[3u]; + candidate = (_e390.xyz / vec3(_e393)); + let _e397 = candidate[0u]; + let _e401 = candidate[1u]; + inTile = vec2(((_e397 * 0.5f) + 0.5f), (0.5f - (_e401 * 0.5f))); + let _e406 = inTile[0u]; + let _e407 = (_e406 < 0f); + phi_3618_ = _e407; + if !(_e407) { + let _e410 = inTile[0u]; + phi_3618_ = (_e410 > 1f); + } + let _e413 = phi_3618_; + phi_3625_ = _e413; + if !(_e413) { + let _e416 = inTile[1u]; + phi_3625_ = (_e416 < 0f); + } + let _e419 = phi_3625_; + phi_3632_ = _e419; + if !(_e419) { + let _e422 = inTile[1u]; + phi_3632_ = (_e422 > 1f); + } + let _e425 = phi_3632_; + if _e425 { continue; } - let _e418 = candidate[2u]; - if (_e418 > 1f) { - let _e420 = which; - let _e421 = cascadeCount; - if (_e420 < (_e421 - 1i)) { + let _e427 = candidate[2u]; + if (_e427 > 1f) { + let _e429 = which; + let _e430 = cascadeCount; + if (_e429 < (_e430 - 1i)) { continue; } candidate[2u] = 1f; } - let _e426 = inTile[0u]; - let _e427 = which; - let _e430 = cascadeCount; - let _e434 = inTile[1u]; - uv_4 = vec2(((_e426 + f32(_e427)) / f32(_e430)), _e434); - let _e436 = candidate; - projected = _e436; - let _e437 = which; - cascade = _e437; - let _e438 = texelMetres; - cascadeTexel = _e438; - let _e441 = matrix[0][2u]; - let _e444 = matrix[1][2u]; - let _e447 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e441, _e444, _e447)), 0.000001f)); - let _e453 = (*s_1).n; - let _e456 = matrix[0][2u]; - let _e459 = matrix[1][2u]; - let _e462 = matrix[2][2u]; - let _e466 = cascadeDepth; - cosSlope = clamp((abs(dot(_e453, vec3(_e456, _e459, _e462))) * _e466), 0.1f, 1f); - let _e469 = texelMetres; - let _e470 = cosSlope; - let _e471 = cosSlope; - let _e477 = cosSlope; - let _e479 = cascadeDepth; - receiverSlope = (((_e469 * sqrt(max((1f - (_e470 * _e471)), 0f))) / _e477) / _e479); + let _e435 = inTile[0u]; + let _e436 = which; + let _e439 = cascadeCount; + let _e443 = inTile[1u]; + uv_4 = vec2(((_e435 + f32(_e436)) / f32(_e439)), _e443); + let _e445 = candidate; + projected = _e445; + let _e446 = which; + cascade = _e446; + let _e447 = texelMetres; + cascadeTexel = _e447; + let _e450 = matrix[0][2u]; + let _e453 = matrix[1][2u]; + let _e456 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e450, _e453, _e456)), 0.000001f)); + let _e462 = (*s_2).n; + let _e465 = matrix[0][2u]; + let _e468 = matrix[1][2u]; + let _e471 = matrix[2][2u]; + let _e475 = cascadeDepth; + cosSlope = clamp((abs(dot(_e462, vec3(_e465, _e468, _e471))) * _e475), 0.1f, 1f); + let _e478 = texelMetres; + let _e479 = cosSlope; + let _e480 = cosSlope; + let _e486 = cosSlope; + let _e488 = cascadeDepth; + receiverSlope = (((_e478 * sqrt(max((1f - (_e479 * _e480)), 0f))) / _e486) / _e488); found = true; break; } else { break; } continuing { - let _e481 = attempt; - attempt = (_e481 + 1i); + let _e490 = attempt; + attempt = (_e490 + 1i); } } - let _e483 = found; - if !(_e483) { + let _e492 = found; + if !(_e492) { return 1f; } - let _e485 = cascade; - if (_e485 == 0i) { - let _e489 = frag_info.shadow_bias[0u]; - local_7 = _e489; + let _e494 = cascade; + if (_e494 == 0i) { + let _e498 = frag_info.shadow_bias[0u]; + local_9 = _e498; } else { - let _e490 = cascade; - if (_e490 == 1i) { - let _e494 = frag_info.shadow_bias[1u]; - local_8 = _e494; + let _e499 = cascade; + if (_e499 == 1i) { + let _e503 = frag_info.shadow_bias[1u]; + local_10 = _e503; } else { - let _e497 = frag_info.shadow_bias[2u]; - local_8 = _e497; - } - let _e498 = local_8; - local_7 = _e498; - } - let _e499 = local_7; - bias = _e499; - let _e502 = frag_info.shadow_params[0u]; - let _e505 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e502, _e505); - let _e507 = cascade; - let _e509 = cascadeCount; - let _e513 = texel_1; - tileLo = (vec2((f32(_e507) / f32(_e509)), 0f) + (_e513 * 0.5f)); + let _e506 = frag_info.shadow_bias[2u]; + local_10 = _e506; + } + let _e507 = local_10; + local_9 = _e507; + } + let _e508 = local_9; + bias = _e508; + let _e511 = frag_info.shadow_params[0u]; + let _e514 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e511, _e514); let _e516 = cascade; - let _e519 = cascadeCount; - let _e523 = texel_1; - tileHi = (vec2((f32((_e516 + 1i)) / f32(_e519)), 1f) - (_e523 * 0.5f)); - let _e528 = frag_info.ambient_ground[3u]; - softness = _e528; - lit_1 = 0f; - let _e529 = softness; - if (_e529 < 0f) { - let _e531 = uv_4; - let _e532 = tileLo; - let _e533 = tileHi; - let _e535 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e531, _e532, _e533), 0f); - moments_2 = _e535; - let _e537 = projected[2u]; - let _e538 = bias; - let _e540 = softness; - let _e544 = moments_2; - param_55 = _e544; - param_56 = (_e537 - _e538); - param_57 = clamp((-(_e540) - 1f), 0f, 0.95f); - let _e545 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); - lit_1 = _e545; + let _e518 = cascadeCount; + let _e522 = texel_1; + tileLo = (vec2((f32(_e516) / f32(_e518)), 0f) + (_e522 * 0.5f)); + let _e525 = cascade; + let _e528 = cascadeCount; + let _e532 = texel_1; + tileHi = (vec2((f32((_e525 + 1i)) / f32(_e528)), 1f) - (_e532 * 0.5f)); + let _e537 = frag_info.ambient_ground[3u]; + softness = _e537; + lit_2 = 0f; + let _e538 = softness; + if (_e538 < 0f) { + let _e540 = uv_4; + let _e541 = tileLo; + let _e542 = tileHi; + let _e544 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e540, _e541, _e542), 0f); + moments_2 = _e544; + let _e546 = projected[2u]; + let _e547 = bias; + let _e549 = softness; + let _e553 = moments_2; + param_61 = _e553; + param_62 = (_e546 - _e547); + param_63 = clamp((-(_e549) - 1f), 0f, 0.95f); + let _e554 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); + lit_2 = _e554; } else { - let _e546 = softness; - if (_e546 <= 0f) { + let _e555 = softness; + if (_e555 <= 0f) { y = -1i; loop { - let _e548 = y; - if (_e548 <= 1i) { + let _e557 = y; + if (_e557 <= 1i) { x = -1i; loop { - let _e550 = x; - if (_e550 <= 1i) { - let _e552 = uv_4; - let _e553 = x; - let _e555 = y; - let _e558 = texel_1; - let _e561 = tileLo; - let _e562 = tileHi; - let _e564 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e552 + (vec2(f32(_e553), f32(_e555)) * _e558)), _e561, _e562), 0f); - occluder = _e564.x; - let _e567 = projected[2u]; - let _e568 = bias; - let _e570 = occluder; - let _e573 = lit_1; - lit_1 = (_e573 + select(1f, 0f, ((_e567 - _e568) > _e570))); + let _e559 = x; + if (_e559 <= 1i) { + let _e561 = uv_4; + let _e562 = x; + let _e564 = y; + let _e567 = texel_1; + let _e570 = tileLo; + let _e571 = tileHi; + let _e573 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e561 + (vec2(f32(_e562), f32(_e564)) * _e567)), _e570, _e571), 0f); + occluder = _e573.x; + let _e576 = projected[2u]; + let _e577 = bias; + let _e579 = occluder; + let _e582 = lit_2; + lit_2 = (_e582 + select(1f, 0f, ((_e576 - _e577) > _e579))); continue; } else { break; } continuing { - let _e575 = x; - x = (_e575 + 1i); + let _e584 = x; + x = (_e584 + 1i); } } continue; @@ -6815,145 +7113,145 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e577 = y; - y = (_e577 + 1i); + let _e586 = y; + y = (_e586 + 1i); } } - let _e579 = lit_1; - lit_1 = (_e579 * 0.11111111f); + let _e588 = lit_2; + lit_2 = (_e588 * 0.11111111f); } else { - let _e581 = softness; - spread = tan(min(_e581, 0.5f)); - let _e584 = ShadowNoise_u0028_(); - turn_1 = (_e584 * 6.2831855f); - let _e586 = spread; - let _e588 = projected[2u]; - let _e590 = cascadeDepth; - let _e592 = cascadeTexel; - searchRadius = clamp((((_e586 * _e588) * _e590) / _e592), 1f, 16f); + let _e590 = softness; + spread = tan(min(_e590, 0.5f)); + let _e593 = ShadowNoise_u0028_(); + turn_1 = (_e593 * 6.2831855f); + let _e595 = spread; + let _e597 = projected[2u]; + let _e599 = cascadeDepth; + let _e601 = cascadeTexel; + searchRadius = clamp((((_e595 * _e597) * _e599) / _e601), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e595 = i_5; - if (_e595 < 16i) { - let _e597 = i_5; - param_58 = _e597; - param_59 = 16i; - let _e598 = turn_1; - param_60 = _e598; - let _e599 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); - disc = _e599; - let _e600 = bias; - let _e601 = disc; - let _e603 = searchRadius; - let _e607 = receiverSlope; - tapBias = (_e600 + (max(((length(_e601) * _e603) - 1.5f), 0f) * _e607)); - let _e610 = uv_4; - let _e611 = disc; - let _e612 = texel_1; - let _e614 = searchRadius; - let _e617 = tileLo; - let _e618 = tileHi; - let _e620 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e610 + ((_e611 * _e612) * _e614)), _e617, _e618), 0f); - occluder_1 = _e620.x; - let _e623 = projected[2u]; - let _e624 = tapBias; - let _e626 = occluder_1; - if ((_e623 - _e624) > _e626) { - let _e628 = occluder_1; - let _e629 = blockerSum; - blockerSum = (_e629 + _e628); - let _e631 = blockerCount; - blockerCount = (_e631 + 1f); + let _e604 = i_5; + if (_e604 < 16i) { + let _e606 = i_5; + param_64 = _e606; + param_65 = 16i; + let _e607 = turn_1; + param_66 = _e607; + let _e608 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_64), (¶m_65), (¶m_66)); + disc = _e608; + let _e609 = bias; + let _e610 = disc; + let _e612 = searchRadius; + let _e616 = receiverSlope; + tapBias = (_e609 + (max(((length(_e610) * _e612) - 1.5f), 0f) * _e616)); + let _e619 = uv_4; + let _e620 = disc; + let _e621 = texel_1; + let _e623 = searchRadius; + let _e626 = tileLo; + let _e627 = tileHi; + let _e629 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e619 + ((_e620 * _e621) * _e623)), _e626, _e627), 0f); + occluder_1 = _e629.x; + let _e632 = projected[2u]; + let _e633 = tapBias; + let _e635 = occluder_1; + if ((_e632 - _e633) > _e635) { + let _e637 = occluder_1; + let _e638 = blockerSum; + blockerSum = (_e638 + _e637); + let _e640 = blockerCount; + blockerCount = (_e640 + 1f); } continue; } else { break; } continuing { - let _e633 = i_5; - i_5 = (_e633 + 1i); + let _e642 = i_5; + i_5 = (_e642 + 1i); } } - let _e635 = blockerCount; - if (_e635 <= 0f) { + let _e644 = blockerCount; + if (_e644 <= 0f) { return 1f; } - let _e638 = projected[2u]; - let _e639 = blockerSum; - let _e640 = blockerCount; - let _e644 = cascadeDepth; - gap = (max((_e638 - (_e639 / _e640)), 0f) * _e644); - let _e646 = spread; - let _e647 = gap; - let _e649 = cascadeTexel; - radius_2 = clamp(((_e646 * _e647) / _e649), 1f, 16f); + let _e647 = projected[2u]; + let _e648 = blockerSum; + let _e649 = blockerCount; + let _e653 = cascadeDepth; + gap = (max((_e647 - (_e648 / _e649)), 0f) * _e653); + let _e655 = spread; + let _e656 = gap; + let _e658 = cascadeTexel; + radius_2 = clamp(((_e655 * _e656) / _e658), 1f, 16f); i_6 = 0i; loop { - let _e652 = i_6; - if (_e652 < 16i) { - let _e654 = turn_1; - let _e656 = i_6; - param_61 = _e656; - param_62 = 16i; - param_63 = (_e654 + 1f); - let _e657 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - disc_1 = _e657; - let _e658 = bias; - let _e659 = disc_1; - let _e661 = radius_2; - let _e665 = receiverSlope; - tapBias_1 = (_e658 + (max(((length(_e659) * _e661) - 1.5f), 0f) * _e665)); - let _e668 = uv_4; - let _e669 = disc_1; - let _e670 = texel_1; - let _e672 = radius_2; - let _e675 = tileLo; - let _e676 = tileHi; - let _e678 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e668 + ((_e669 * _e670) * _e672)), _e675, _e676), 0f); - occluder_2 = _e678.x; - let _e681 = projected[2u]; - let _e682 = tapBias_1; - let _e684 = occluder_2; - let _e687 = lit_1; - lit_1 = (_e687 + select(1f, 0f, ((_e681 - _e682) > _e684))); + let _e661 = i_6; + if (_e661 < 16i) { + let _e663 = turn_1; + let _e665 = i_6; + param_67 = _e665; + param_68 = 16i; + param_69 = (_e663 + 1f); + let _e666 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_67), (¶m_68), (¶m_69)); + disc_1 = _e666; + let _e667 = bias; + let _e668 = disc_1; + let _e670 = radius_2; + let _e674 = receiverSlope; + tapBias_1 = (_e667 + (max(((length(_e668) * _e670) - 1.5f), 0f) * _e674)); + let _e677 = uv_4; + let _e678 = disc_1; + let _e679 = texel_1; + let _e681 = radius_2; + let _e684 = tileLo; + let _e685 = tileHi; + let _e687 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e677 + ((_e678 * _e679) * _e681)), _e684, _e685), 0f); + occluder_2 = _e687.x; + let _e690 = projected[2u]; + let _e691 = tapBias_1; + let _e693 = occluder_2; + let _e696 = lit_2; + lit_2 = (_e696 + select(1f, 0f, ((_e690 - _e691) > _e693))); continue; } else { break; } continuing { - let _e689 = i_6; - i_6 = (_e689 + 1i); + let _e698 = i_6; + i_6 = (_e698 + 1i); } } - let _e691 = lit_1; - lit_1 = (_e691 * 0.0625f); + let _e700 = lit_2; + lit_2 = (_e700 * 0.0625f); } } - let _e693 = lit_1; - let _e694 = strength_1; - return mix(1f, _e693, clamp(_e694, 0f, 1f)); + let _e702 = lit_2; + let _e703 = strength_1; + return mix(1f, _e702, clamp(_e703, 0f, 1f)); } -fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_2: ptr, index: ptr) -> f32 { - var param_64: Surface; - var param_65: LightSample; - var param_66: i32; +fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_3: ptr, light_2: ptr, index: ptr) -> f32 { + var param_70: Surface; + var param_71: LightSample; + var param_72: i32; - let _e181 = (*s_2); - param_64 = _e181; - let _e182 = (*light_2); - param_65 = _e182; - let _e183 = (*index); - param_66 = _e183; - let _e184 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); - return _e184; + let _e190 = (*s_3); + param_70 = _e190; + let _e191 = (*light_2); + param_71 = _e191; + let _e192 = (*index); + param_72 = _e192; + let _e193 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_70), (¶m_71), (¶m_72)); + return _e193; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e176 = (*index_1); - return (_e176 < 8i); + let _e185 = (*index_1); + return (_e185 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -6961,26 +7259,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e180 = (*distance_1); - let _e181 = (*distance_1); - attenuation = (1f / max((_e180 * _e181), 0.0001f)); - let _e185 = (*range_4); - if (_e185 > 0f) { - let _e187 = (*distance_1); - let _e188 = (*range_4); - ratio = (_e187 / _e188); - let _e190 = ratio; - let _e191 = ratio; - let _e193 = ratio; - let _e195 = ratio; - window = clamp((1f - (((_e190 * _e191) * _e193) * _e195)), 0f, 1f); - let _e199 = window; - let _e200 = window; - let _e202 = attenuation; - attenuation = (_e202 * (_e199 * _e200)); - } - let _e204 = attenuation; - return _e204; + let _e189 = (*distance_1); + let _e190 = (*distance_1); + attenuation = (1f / max((_e189 * _e190), 0.0001f)); + let _e194 = (*range_4); + if (_e194 > 0f) { + let _e196 = (*distance_1); + let _e197 = (*range_4); + ratio = (_e196 / _e197); + let _e199 = ratio; + let _e200 = ratio; + let _e202 = ratio; + let _e204 = ratio; + window = clamp((1f - (((_e199 * _e200) * _e202) * _e204)), 0f, 1f); + let _e208 = window; + let _e209 = window; + let _e211 = attenuation; + attenuation = (_e211 * (_e208 * _e209)); + } + let _e213 = attenuation; + return _e213; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -6990,82 +7288,82 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_9: vec3; + var local_11: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; var u: f32; var v: f32; - let _e192 = (*halfWidth); - let _e193 = (*halfHeight); - n_2 = cross(_e192, _e193); - let _e195 = n_2; - nLen = length(_e195); - let _e197 = nLen; - if (_e197 < 0.000000000001f) { - let _e199 = (*centre); - return _e199; - } - let _e200 = nLen; - let _e201 = n_2; - n_2 = (_e201 / vec3(_e200)); - let _e204 = (*centre); - let _e205 = (*world_2); - toPlane = (_e204 - _e205); - let _e207 = (*mirror); - let _e208 = n_2; - denom = dot(_e207, _e208); - let _e210 = denom; - if (abs(_e210) < 0.00001f) { - let _e213 = toPlane; - let _e214 = n_2; - let _e215 = toPlane; - let _e216 = n_2; - onPlane = (_e213 - (_e214 * dot(_e215, _e216))); + let _e201 = (*halfWidth); + let _e202 = (*halfHeight); + n_2 = cross(_e201, _e202); + let _e204 = n_2; + nLen = length(_e204); + let _e206 = nLen; + if (_e206 < 0.000000000001f) { + let _e208 = (*centre); + return _e208; + } + let _e209 = nLen; + let _e210 = n_2; + n_2 = (_e210 / vec3(_e209)); + let _e213 = (*centre); + let _e214 = (*world_2); + toPlane = (_e213 - _e214); + let _e216 = (*mirror); + let _e217 = n_2; + denom = dot(_e216, _e217); + let _e219 = denom; + if (abs(_e219) < 0.00001f) { + let _e222 = toPlane; + let _e223 = n_2; + let _e224 = toPlane; + let _e225 = n_2; + onPlane = (_e222 - (_e223 * dot(_e224, _e225))); } else { - let _e220 = toPlane; - let _e221 = n_2; - let _e223 = denom; - t_1 = (dot(_e220, _e221) / _e223); - let _e225 = t_1; - if (_e225 > 0f) { - let _e227 = (*mirror); - let _e228 = t_1; - local_9 = (_e227 * _e228); + let _e229 = toPlane; + let _e230 = n_2; + let _e232 = denom; + t_1 = (dot(_e229, _e230) / _e232); + let _e234 = t_1; + if (_e234 > 0f) { + let _e236 = (*mirror); + let _e237 = t_1; + local_11 = (_e236 * _e237); } else { - let _e230 = toPlane; - let _e231 = n_2; - let _e232 = toPlane; - let _e233 = n_2; - local_9 = (_e230 - (_e231 * dot(_e232, _e233))); - } - let _e237 = local_9; - onPlane = _e237; - } - let _e238 = onPlane; - let _e239 = toPlane; - offset_2 = (_e238 - _e239); - let _e241 = (*halfWidth); - let _e242 = (*halfWidth); - wLen2_ = max(dot(_e241, _e242), 0.000000000001f); - let _e245 = (*halfHeight); - let _e246 = (*halfHeight); - hLen2_ = max(dot(_e245, _e246), 0.000000000001f); - let _e249 = offset_2; + let _e239 = toPlane; + let _e240 = n_2; + let _e241 = toPlane; + let _e242 = n_2; + local_11 = (_e239 - (_e240 * dot(_e241, _e242))); + } + let _e246 = local_11; + onPlane = _e246; + } + let _e247 = onPlane; + let _e248 = toPlane; + offset_2 = (_e247 - _e248); let _e250 = (*halfWidth); - let _e252 = wLen2_; - u = clamp((dot(_e249, _e250) / _e252), -1f, 1f); - let _e255 = offset_2; - let _e256 = (*halfHeight); - let _e258 = hLen2_; - v = clamp((dot(_e255, _e256) / _e258), -1f, 1f); - let _e261 = (*centre); - let _e262 = (*halfWidth); - let _e263 = u; - let _e266 = (*halfHeight); - let _e267 = v; - return ((_e261 + (_e262 * _e263)) + (_e266 * _e267)); + let _e251 = (*halfWidth); + wLen2_ = max(dot(_e250, _e251), 0.000000000001f); + let _e254 = (*halfHeight); + let _e255 = (*halfHeight); + hLen2_ = max(dot(_e254, _e255), 0.000000000001f); + let _e258 = offset_2; + let _e259 = (*halfWidth); + let _e261 = wLen2_; + u = clamp((dot(_e258, _e259) / _e261), -1f, 1f); + let _e264 = offset_2; + let _e265 = (*halfHeight); + let _e267 = hLen2_; + v = clamp((dot(_e264, _e265) / _e267), -1f, 1f); + let _e270 = (*centre); + let _e271 = (*halfWidth); + let _e272 = u; + let _e275 = (*halfHeight); + let _e276 = v; + return ((_e270 + (_e271 * _e272)) + (_e275 * _e276)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>, n_3: ptr>) -> f32 { @@ -7074,34 +7372,34 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_10: f32; + var local_12: f32; - let _e184 = (*a_1); - let _e185 = (*a_1); - ua = (_e184 / vec3(max(length(_e185), 0.000000000001f))); - let _e190 = (*b); - let _e191 = (*b); - ub = (_e190 / vec3(max(length(_e191), 0.000000000001f))); - let _e196 = ua; - let _e197 = ub; - angle_1 = acos(clamp(dot(_e196, _e197), -1f, 1f)); - let _e201 = ua; - let _e202 = ub; - axis = cross(_e201, _e202); - let _e204 = axis; - len = length(_e204); - let _e206 = len; - if (_e206 > 0.000001f) { - let _e208 = angle_1; - let _e209 = axis; - let _e210 = (*n_3); - let _e213 = len; - local_10 = ((_e208 * dot(_e209, _e210)) / _e213); + let _e193 = (*a_1); + let _e194 = (*a_1); + ua = (_e193 / vec3(max(length(_e194), 0.000000000001f))); + let _e199 = (*b); + let _e200 = (*b); + ub = (_e199 / vec3(max(length(_e200), 0.000000000001f))); + let _e205 = ua; + let _e206 = ub; + angle_1 = acos(clamp(dot(_e205, _e206), -1f, 1f)); + let _e210 = ua; + let _e211 = ub; + axis = cross(_e210, _e211); + let _e213 = axis; + len = length(_e213); + let _e215 = len; + if (_e215 > 0.000001f) { + let _e217 = angle_1; + let _e218 = axis; + let _e219 = (*n_3); + let _e222 = len; + local_12 = ((_e217 * dot(_e218, _e219)) / _e222); } else { - local_10 = 0f; + local_12 = 0f; } - let _e215 = local_10; - return _e215; + let _e224 = local_12; + return _e224; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr, 4>>, n_4: ptr>) -> f32 { @@ -7111,212 +7409,212 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_11: i32; + var local_13: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_12: f32; + var local_14: f32; var aAbove: bool; var bAbove: bool; - var local_13: f32; - var param_67: vec3; - var param_68: vec3; - var param_69: vec3; - var param_70: vec3; - var param_71: vec3; - var param_72: vec3; + var local_15: f32; + var param_73: vec3; + var param_74: vec3; + var param_75: vec3; + var param_76: vec3; + var param_77: vec3; + var param_78: vec3; total = 0f; exit = vec3(0f, 0f, 0f); entry = vec3(0f, 0f, 0f); i_7 = 0i; loop { - let _e198 = i_7; - if (_e198 < 4i) { - let _e200 = i_7; - let _e202 = (*corners)[_e200]; - a_2 = _e202; - let _e203 = i_7; - if (_e203 == 3i) { - local_11 = 0i; + let _e207 = i_7; + if (_e207 < 4i) { + let _e209 = i_7; + let _e211 = (*corners)[_e209]; + a_2 = _e211; + let _e212 = i_7; + if (_e212 == 3i) { + local_13 = 0i; } else { - let _e205 = i_7; - local_11 = (_e205 + 1i); - } - let _e207 = local_11; - let _e209 = (*corners)[_e207]; - b_1 = _e209; - let _e210 = a_2; - let _e211 = (*n_4); - ha = dot(_e210, _e211); - let _e213 = b_1; - let _e214 = (*n_4); - hb = dot(_e213, _e214); - let _e216 = ha; - let _e217 = hb; - d_3 = (_e216 - _e217); + let _e214 = i_7; + local_13 = (_e214 + 1i); + } + let _e216 = local_13; + let _e218 = (*corners)[_e216]; + b_1 = _e218; let _e219 = a_2; - let _e220 = b_1; - let _e221 = a_2; - let _e223 = d_3; - if (abs(_e223) > 0.000000000001f) { - let _e226 = ha; - let _e227 = d_3; - local_12 = (_e226 / _e227); + let _e220 = (*n_4); + ha = dot(_e219, _e220); + let _e222 = b_1; + let _e223 = (*n_4); + hb = dot(_e222, _e223); + let _e225 = ha; + let _e226 = hb; + d_3 = (_e225 - _e226); + let _e228 = a_2; + let _e229 = b_1; + let _e230 = a_2; + let _e232 = d_3; + if (abs(_e232) > 0.000000000001f) { + let _e235 = ha; + let _e236 = d_3; + local_14 = (_e235 / _e236); } else { - local_12 = 0f; - } - let _e229 = local_12; - q = (_e219 + ((_e220 - _e221) * _e229)); - let _e232 = ha; - aAbove = (_e232 > 0f); - let _e234 = hb; - bAbove = (_e234 > 0f); - let _e236 = aAbove; - let _e237 = bAbove; - if (_e236 || _e237) { - let _e239 = aAbove; - let _e240 = a_2; - let _e241 = q; - let _e244 = bAbove; - let _e245 = b_1; - let _e246 = q; - param_67 = select(_e241, _e240, vec3(_e239)); - param_68 = select(_e246, _e245, vec3(_e244)); - let _e249 = (*n_4); - param_69 = _e249; - let _e250 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); - local_13 = _e250; + local_14 = 0f; + } + let _e238 = local_14; + q = (_e228 + ((_e229 - _e230) * _e238)); + let _e241 = ha; + aAbove = (_e241 > 0f); + let _e243 = hb; + bAbove = (_e243 > 0f); + let _e245 = aAbove; + let _e246 = bAbove; + if (_e245 || _e246) { + let _e248 = aAbove; + let _e249 = a_2; + let _e250 = q; + let _e253 = bAbove; + let _e254 = b_1; + let _e255 = q; + param_73 = select(_e250, _e249, vec3(_e248)); + param_74 = select(_e255, _e254, vec3(_e253)); + let _e258 = (*n_4); + param_75 = _e258; + let _e259 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_73), (¶m_74), (¶m_75)); + local_15 = _e259; } else { - local_13 = 0f; - } - let _e251 = local_13; - let _e252 = total; - total = (_e252 + _e251); - let _e254 = aAbove; - let _e255 = bAbove; - let _e258 = q; - let _e259 = exit; - exit = select(_e259, _e258, vec3((_e254 && !(_e255)))); - let _e262 = aAbove; + local_15 = 0f; + } + let _e260 = local_15; + let _e261 = total; + total = (_e261 + _e260); + let _e263 = aAbove; let _e264 = bAbove; - let _e266 = q; - let _e267 = entry; - entry = select(_e267, _e266, vec3((!(_e262) && _e264))); + let _e267 = q; + let _e268 = exit; + exit = select(_e268, _e267, vec3((_e263 && !(_e264)))); + let _e271 = aAbove; + let _e273 = bAbove; + let _e275 = q; + let _e276 = entry; + entry = select(_e276, _e275, vec3((!(_e271) && _e273))); continue; } else { break; } continuing { - let _e270 = i_7; - i_7 = (_e270 + 1i); + let _e279 = i_7; + i_7 = (_e279 + 1i); } } - let _e272 = exit; - param_70 = _e272; - let _e273 = entry; - param_71 = _e273; - let _e274 = (*n_4); - param_72 = _e274; - let _e275 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); - let _e276 = total; - total = (_e276 + _e275); - let _e278 = total; - return max((-(_e278) * 0.5f), 0f); + let _e281 = exit; + param_76 = _e281; + let _e282 = entry; + param_77 = _e282; + let _e283 = (*n_4); + param_78 = _e283; + let _e284 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_76), (¶m_77), (¶m_78)); + let _e285 = total; + total = (_e285 + _e284); + let _e287 = total; + return max((-(_e287) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_14: f32; - var local_15: f32; var local_16: f32; + var local_17: f32; + var local_18: f32; - let _e181 = (*slot_1); - let _e182 = (*slot_1); - lane = (_e181 - ((_e182 / 4i) * 4i)); - let _e186 = lane; - if (_e186 == 0i) { - let _e189 = (*four)[0u]; - local_14 = _e189; + let _e190 = (*slot_1); + let _e191 = (*slot_1); + lane = (_e190 - ((_e191 / 4i) * 4i)); + let _e195 = lane; + if (_e195 == 0i) { + let _e198 = (*four)[0u]; + local_16 = _e198; } else { - let _e190 = lane; - if (_e190 == 1i) { - let _e193 = (*four)[1u]; - local_15 = _e193; + let _e199 = lane; + if (_e199 == 1i) { + let _e202 = (*four)[1u]; + local_17 = _e202; } else { - let _e194 = lane; - if (_e194 == 2i) { - let _e197 = (*four)[2u]; - local_16 = _e197; + let _e203 = lane; + if (_e203 == 2i) { + let _e206 = (*four)[2u]; + local_18 = _e206; } else { - let _e199 = (*four)[3u]; - local_16 = _e199; + let _e208 = (*four)[3u]; + local_18 = _e208; } - let _e200 = local_16; - local_15 = _e200; + let _e209 = local_18; + local_17 = _e209; } - let _e201 = local_15; - local_14 = _e201; + let _e210 = local_17; + local_16 = _e210; } - let _e202 = local_14; - return _e202; + let _e211 = local_16; + return _e211; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { - var param_73: vec4; - var param_74: i32; + var param_79: vec4; + var param_80: i32; - let _e178 = (*slot_2); - let _e182 = light_list_info.scales[(_e178 / 4i)]; - param_73 = _e182; - let _e183 = (*slot_2); - param_74 = _e183; - let _e184 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); - return _e184; + let _e187 = (*slot_2); + let _e191 = light_list_info.scales[(_e187 / 4i)]; + param_79 = _e191; + let _e192 = (*slot_2); + param_80 = _e192; + let _e193 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); + return _e193; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_3: vec4; var b_2: vec4; - let _e180 = light_list_info.slot_rows[0i]; - let _e181 = (*row); - a_3 = abs((_e180 - vec4(_e181))); - let _e187 = light_list_info.slot_rows[1i]; - let _e188 = (*row); - b_2 = abs((_e187 - vec4(_e188))); - let _e193 = a_3[0u]; - let _e195 = a_3[1u]; - let _e198 = a_3[2u]; - let _e200 = a_3[3u]; - let _e204 = b_2[0u]; - let _e206 = b_2[1u]; - let _e209 = b_2[2u]; - let _e211 = b_2[3u]; - return (min(min(min(_e193, _e195), min(_e198, _e200)), min(min(_e204, _e206), min(_e209, _e211))) < 0.5f); + let _e189 = light_list_info.slot_rows[0i]; + let _e190 = (*row); + a_3 = abs((_e189 - vec4(_e190))); + let _e196 = light_list_info.slot_rows[1i]; + let _e197 = (*row); + b_2 = abs((_e196 - vec4(_e197))); + let _e202 = a_3[0u]; + let _e204 = a_3[1u]; + let _e207 = a_3[2u]; + let _e209 = a_3[3u]; + let _e213 = b_2[0u]; + let _e215 = b_2[1u]; + let _e218 = b_2[2u]; + let _e220 = b_2[3u]; + return (min(min(min(_e202, _e204), min(_e207, _e209)), min(min(_e213, _e215), min(_e218, _e220))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { - var param_75: vec4; - var param_76: i32; + var param_81: vec4; + var param_82: i32; - let _e178 = (*slot_3); - let _e182 = light_list_info.indices[(_e178 / 4i)]; - param_75 = _e182; - let _e183 = (*slot_3); - param_76 = _e183; - let _e184 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); - return _e184; + let _e187 = (*slot_3); + let _e191 = light_list_info.indices[(_e187 / 4i)]; + param_81 = _e191; + let _e192 = (*slot_3); + param_82 = _e192; + let _e193 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_81), (¶m_82)); + return _e193; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e177 = (*texel_2); - let _e181 = light_list_info.list[1u]; - let _e183 = (*row_1); - let _e187 = light_list_info.list[2u]; - let _e190 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e177 + 0.5f) * _e181), ((_e183 + 0.5f) * _e187)), 0f); - return _e190; + let _e186 = (*texel_2); + let _e190 = light_list_info.list[1u]; + let _e192 = (*row_1); + let _e196 = light_list_info.list[2u]; + let _e199 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e186 + 0.5f) * _e190), ((_e192 + 0.5f) * _e196)), 0f); + return _e199; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -7325,43 +7623,43 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var within: f32; var texel_3: f32; var four_1: vec4; - var param_77: f32; - var param_78: f32; - var param_79: vec4; - var param_80: i32; + var param_83: f32; + var param_84: f32; + var param_85: vec4; + var param_86: i32; - let _e185 = g_cluster_offset; - let _e186 = (*slot_4); - entry_1 = (_e185 + f32(_e186)); - let _e189 = entry_1; - row_2 = floor((_e189 / 16f)); - let _e192 = entry_1; - let _e193 = row_2; - within = (_e192 - (_e193 * 16f)); - let _e196 = within; - texel_3 = floor((_e196 / 4f)); - let _e201 = light_list_info.cluster_depth[3u]; + let _e194 = g_cluster_offset; + let _e195 = (*slot_4); + entry_1 = (_e194 + f32(_e195)); + let _e198 = entry_1; + row_2 = floor((_e198 / 16f)); + let _e201 = entry_1; let _e202 = row_2; - let _e204 = texel_3; - param_77 = _e204; - param_78 = (_e201 + _e202); - let _e205 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); - four_1 = _e205; - let _e206 = within; - let _e207 = texel_3; - let _e212 = four_1; - param_79 = _e212; - param_80 = i32(((_e206 - (_e207 * 4f)) + 0.5f)); - let _e213 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); - return _e213; + within = (_e201 - (_e202 * 16f)); + let _e205 = within; + texel_3 = floor((_e205 / 4f)); + let _e210 = light_list_info.cluster_depth[3u]; + let _e211 = row_2; + let _e213 = texel_3; + param_83 = _e213; + param_84 = (_e210 + _e211); + let _e214 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_83), (¶m_84)); + four_1 = _e214; + let _e215 = within; + let _e216 = texel_3; + let _e221 = four_1; + param_85 = _e221; + param_86 = i32(((_e215 - (_e216 * 4f)) + 0.5f)); + let _e222 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_85), (¶m_86)); + return _e222; } fn Clustered_u0028_() -> bool { - let _e177 = light_list_info.cluster_grid[3u]; - return (_e177 > 0.5f); + let _e186 = light_list_info.cluster_grid[3u]; + return (_e186 > 0.5f); } -fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_3: ptr) -> LightSample { +fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_4: ptr) -> LightSample { var light_3: LightSample; var position: vec4; var color_1: vec4; @@ -7370,14 +7668,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_17: f32; - var param_81: i32; - var param_82: i32; + var local_19: f32; + var param_87: i32; + var param_88: i32; var v_1: f32; var u_1: f32; - var local_18: f32; - var param_83: f32; - var param_84: i32; + var local_20: f32; + var param_89: f32; + var param_90: i32; var type_39: f32; var halfWidth_1: vec3; var halfHeight_1: vec3; @@ -7385,293 +7683,293 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_19: f32; - var param_85: array, 4>; - var param_86: vec3; + var local_21: f32; + var param_91: array, 4>; + var param_92: vec3; var area: f32; var radiance: f32; - var local_20: f32; + var local_22: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; var mirror_1: vec3; var representative: vec3; - var param_87: vec3; - var param_88: vec3; - var param_89: vec3; - var param_90: vec3; - var param_91: vec3; + var param_93: vec3; + var param_94: vec3; + var param_95: vec3; + var param_96: vec3; + var param_97: vec3; var toPoint: vec3; var pointDistance: f32; - var local_21: vec3; + var local_23: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; var distance_3: f32; - var param_92: f32; - var param_93: f32; + var param_98: f32; + var param_99: f32; var cosAngle: f32; light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e228 = (*index_2); - if (_e228 < 8i) { - let _e230 = (*index_2); - let _e233 = frag_info.light_position[_e230]; - position = _e233; - let _e234 = (*index_2); - let _e237 = frag_info.light_color[_e234]; - color_1 = _e237; - let _e238 = (*index_2); - let _e241 = frag_info.light_direction[_e238]; - direction = _e241; - let _e242 = (*index_2); - let _e245 = frag_info.light_cone[_e242]; - cone = _e245; + let _e237 = (*index_2); + if (_e237 < 8i) { + let _e239 = (*index_2); + let _e242 = frag_info.light_position[_e239]; + position = _e242; + let _e243 = (*index_2); + let _e246 = frag_info.light_color[_e243]; + color_1 = _e246; + let _e247 = (*index_2); + let _e250 = frag_info.light_direction[_e247]; + direction = _e250; + let _e251 = (*index_2); + let _e254 = frag_info.light_cone[_e251]; + cone = _e254; } else { - let _e246 = (*index_2); - slot_5 = (_e246 - 8i); - let _e248 = Clustered_u0028_(); - clustered = _e248; - let _e249 = clustered; - if _e249 { - let _e250 = slot_5; - param_81 = _e250; - let _e251 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_17 = _e251; + let _e255 = (*index_2); + slot_5 = (_e255 - 8i); + let _e257 = Clustered_u0028_(); + clustered = _e257; + let _e258 = clustered; + if _e258 { + let _e259 = slot_5; + param_87 = _e259; + let _e260 = ClusterRow_u0028_i1_u003b((¶m_87)); + local_19 = _e260; } else { - let _e252 = slot_5; - param_82 = _e252; - let _e253 = LightListRow_u0028_i1_u003b((¶m_82)); - local_17 = _e253; - } - let _e254 = local_17; - listRow = _e254; - let _e255 = listRow; - let _e259 = light_list_info.list[2u]; - v_1 = ((_e255 + 0.5f) * _e259); - let _e263 = light_list_info.list[1u]; - u_1 = _e263; - let _e264 = u_1; - let _e266 = v_1; - let _e268 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e264), _e266), 0f); - position = _e268; - let _e269 = u_1; - let _e271 = v_1; - let _e273 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e269), _e271), 0f); - color_1 = _e273; - let _e274 = u_1; - let _e276 = v_1; - let _e278 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e274), _e276), 0f); - direction = _e278; - let _e279 = u_1; - let _e281 = v_1; - let _e283 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e279), _e281), 0f); - cone = _e283; - let _e284 = clustered; - if _e284 { - let _e285 = listRow; - param_83 = _e285; - let _e286 = InSlots_u0028_f1_u003b((¶m_83)); - local_18 = select(1f, 0f, _e286); + let _e261 = slot_5; + param_88 = _e261; + let _e262 = LightListRow_u0028_i1_u003b((¶m_88)); + local_19 = _e262; + } + let _e263 = local_19; + listRow = _e263; + let _e264 = listRow; + let _e268 = light_list_info.list[2u]; + v_1 = ((_e264 + 0.5f) * _e268); + let _e272 = light_list_info.list[1u]; + u_1 = _e272; + let _e273 = u_1; + let _e275 = v_1; + let _e277 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e273), _e275), 0f); + position = _e277; + let _e278 = u_1; + let _e280 = v_1; + let _e282 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e278), _e280), 0f); + color_1 = _e282; + let _e283 = u_1; + let _e285 = v_1; + let _e287 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e283), _e285), 0f); + direction = _e287; + let _e288 = u_1; + let _e290 = v_1; + let _e292 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e288), _e290), 0f); + cone = _e292; + let _e293 = clustered; + if _e293 { + let _e294 = listRow; + param_89 = _e294; + let _e295 = InSlots_u0028_f1_u003b((¶m_89)); + local_20 = select(1f, 0f, _e295); } else { - let _e288 = slot_5; - param_84 = _e288; - let _e289 = LightListScale_u0028_i1_u003b((¶m_84)); - local_18 = _e289; - } - let _e290 = local_18; - let _e292 = color_1[3u]; - color_1[3u] = (_e292 * _e290); - } - let _e296 = position[3u]; - type_39 = _e296; - let _e297 = type_39; - if (_e297 > 2.5f) { - let _e299 = direction; - halfWidth_1 = _e299.xyz; - let _e301 = cone; - halfHeight_1 = _e301.xyz; - let _e303 = position; - let _e305 = v_world_position_1; - toCentre = (_e303.xyz - _e305); - let _e307 = toCentre; - let _e308 = halfWidth_1; - let _e310 = halfHeight_1; - corners_1[0i] = ((_e307 - _e308) - _e310); - let _e313 = toCentre; - let _e314 = halfWidth_1; - let _e316 = halfHeight_1; - corners_1[1i] = ((_e313 + _e314) - _e316); - let _e319 = toCentre; - let _e320 = halfWidth_1; - let _e322 = halfHeight_1; - corners_1[2i] = ((_e319 + _e320) + _e322); - let _e325 = toCentre; - let _e326 = halfWidth_1; - let _e328 = halfHeight_1; - corners_1[3i] = ((_e325 - _e326) + _e328); - let _e331 = toCentre; - let _e332 = halfWidth_1; - let _e333 = halfHeight_1; - behind = (dot(_e331, cross(_e332, _e333)) >= 0f); - let _e337 = behind; - if _e337 { - local_19 = 0f; + let _e297 = slot_5; + param_90 = _e297; + let _e298 = LightListScale_u0028_i1_u003b((¶m_90)); + local_20 = _e298; + } + let _e299 = local_20; + let _e301 = color_1[3u]; + color_1[3u] = (_e301 * _e299); + } + let _e305 = position[3u]; + type_39 = _e305; + let _e306 = type_39; + if (_e306 > 2.5f) { + let _e308 = direction; + halfWidth_1 = _e308.xyz; + let _e310 = cone; + halfHeight_1 = _e310.xyz; + let _e312 = position; + let _e314 = v_world_position_1; + toCentre = (_e312.xyz - _e314); + let _e316 = toCentre; + let _e317 = halfWidth_1; + let _e319 = halfHeight_1; + corners_1[0i] = ((_e316 - _e317) - _e319); + let _e322 = toCentre; + let _e323 = halfWidth_1; + let _e325 = halfHeight_1; + corners_1[1i] = ((_e322 + _e323) - _e325); + let _e328 = toCentre; + let _e329 = halfWidth_1; + let _e331 = halfHeight_1; + corners_1[2i] = ((_e328 + _e329) + _e331); + let _e334 = toCentre; + let _e335 = halfWidth_1; + let _e337 = halfHeight_1; + corners_1[3i] = ((_e334 - _e335) + _e337); + let _e340 = toCentre; + let _e341 = halfWidth_1; + let _e342 = halfHeight_1; + behind = (dot(_e340, cross(_e341, _e342)) >= 0f); + let _e346 = behind; + if _e346 { + local_21 = 0f; } else { - let _e338 = corners_1; - param_85 = _e338; - let _e340 = (*s_3).n; - param_86 = _e340; - let _e341 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_19 = _e341; - } - let _e342 = local_19; - formFactor = _e342; - let _e343 = halfWidth_1; - let _e344 = halfHeight_1; - area = (length(cross(_e343, _e344)) * 4f); - let _e348 = area; - if (_e348 > 0.000000001f) { - let _e350 = area; - local_20 = (1f / _e350); + let _e347 = corners_1; + param_91 = _e347; + let _e349 = (*s_4).n; + param_92 = _e349; + let _e350 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_91), (¶m_92)); + local_21 = _e350; + } + let _e351 = local_21; + formFactor = _e351; + let _e352 = halfWidth_1; + let _e353 = halfHeight_1; + area = (length(cross(_e352, _e353)) * 4f); + let _e357 = area; + if (_e357 > 0.000000001f) { + let _e359 = area; + local_22 = (1f / _e359); } else { - local_20 = 0f; - } - let _e352 = local_20; - radiance = _e352; - let _e353 = toCentre; - distance_2 = length(_e353); - let _e356 = direction[3u]; - if (_e356 > 0f) { - let _e358 = distance_2; - let _e360 = direction[3u]; - ratio_1 = (_e358 / _e360); - let _e362 = ratio_1; - let _e363 = ratio_1; - let _e365 = ratio_1; - let _e367 = ratio_1; - window_1 = clamp((1f - (((_e362 * _e363) * _e365) * _e367)), 0f, 1f); - let _e371 = window_1; - let _e372 = window_1; - let _e374 = radiance; - radiance = (_e374 * (_e371 * _e372)); - } - let _e377 = (*s_3).v; - let _e380 = (*s_3).n; - mirror_1 = reflect(-(_e377), _e380); - let _e382 = position; - param_87 = _e382.xyz; - let _e384 = halfWidth_1; - param_88 = _e384; - let _e385 = halfHeight_1; - param_89 = _e385; - let _e386 = v_world_position_1; - param_90 = _e386; - let _e387 = mirror_1; - param_91 = _e387; - let _e388 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); - representative = _e388; - let _e389 = representative; - let _e390 = v_world_position_1; - toPoint = (_e389 - _e390); - let _e392 = toPoint; - pointDistance = length(_e392); - let _e394 = pointDistance; - if (_e394 > 0.000001f) { - let _e396 = toPoint; - let _e397 = pointDistance; - local_21 = (_e396 / vec3(_e397)); + local_22 = 0f; + } + let _e361 = local_22; + radiance = _e361; + let _e362 = toCentre; + distance_2 = length(_e362); + let _e365 = direction[3u]; + if (_e365 > 0f) { + let _e367 = distance_2; + let _e369 = direction[3u]; + ratio_1 = (_e367 / _e369); + let _e371 = ratio_1; + let _e372 = ratio_1; + let _e374 = ratio_1; + let _e376 = ratio_1; + window_1 = clamp((1f - (((_e371 * _e372) * _e374) * _e376)), 0f, 1f); + let _e380 = window_1; + let _e381 = window_1; + let _e383 = radiance; + radiance = (_e383 * (_e380 * _e381)); + } + let _e386 = (*s_4).v; + let _e389 = (*s_4).n; + mirror_1 = reflect(-(_e386), _e389); + let _e391 = position; + param_93 = _e391.xyz; + let _e393 = halfWidth_1; + param_94 = _e393; + let _e394 = halfHeight_1; + param_95 = _e394; + let _e395 = v_world_position_1; + param_96 = _e395; + let _e396 = mirror_1; + param_97 = _e396; + let _e397 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_93), (¶m_94), (¶m_95), (¶m_96), (¶m_97)); + representative = _e397; + let _e398 = representative; + let _e399 = v_world_position_1; + toPoint = (_e398 - _e399); + let _e401 = toPoint; + pointDistance = length(_e401); + let _e403 = pointDistance; + if (_e403 > 0.000001f) { + let _e405 = toPoint; + let _e406 = pointDistance; + local_23 = (_e405 / vec3(_e406)); } else { - let _e401 = (*s_3).n; - local_21 = _e401; - } - let _e402 = local_21; - light_3.l = _e402; - let _e405 = light_3.l; - let _e407 = (*s_3).v; - light_3.h = normalize((_e405 + _e407)); - let _e411 = formFactor; - light_3.n_dot_l = _e411; - let _e414 = (*s_3).n; - let _e416 = light_3.h; - light_3.n_dot_h = max(dot(_e414, _e416), 0f); - let _e421 = (*s_3).v; - let _e423 = light_3.h; - light_3.v_dot_h = max(dot(_e421, _e423), 0f); - let _e427 = color_1; - let _e430 = color_1[3u]; - let _e432 = radiance; - light_3.radiance = ((_e427.xyz * _e430) * _e432); - let _e435 = light_3; - return _e435; - } - let _e436 = direction; - aim = normalize(_e436.xyz); + let _e410 = (*s_4).n; + local_23 = _e410; + } + let _e411 = local_23; + light_3.l = _e411; + let _e414 = light_3.l; + let _e416 = (*s_4).v; + light_3.h = normalize((_e414 + _e416)); + let _e420 = formFactor; + light_3.n_dot_l = _e420; + let _e423 = (*s_4).n; + let _e425 = light_3.h; + light_3.n_dot_h = max(dot(_e423, _e425), 0f); + let _e430 = (*s_4).v; + let _e432 = light_3.h; + light_3.v_dot_h = max(dot(_e430, _e432), 0f); + let _e436 = color_1; + let _e439 = color_1[3u]; + let _e441 = radiance; + light_3.radiance = ((_e436.xyz * _e439) * _e441); + let _e444 = light_3; + return _e444; + } + let _e445 = direction; + aim = normalize(_e445.xyz); attenuation_1 = 1f; - let _e439 = type_39; - if (_e439 < 0.5f) { - let _e441 = aim; - light_3.l = -(_e441); + let _e448 = type_39; + if (_e448 < 0.5f) { + let _e450 = aim; + light_3.l = -(_e450); } else { - let _e444 = position; - let _e446 = v_world_position_1; - toLight_1 = (_e444.xyz - _e446); - let _e448 = toLight_1; - distance_3 = length(_e448); - let _e450 = distance_3; - if (_e450 < 0.000001f) { - let _e453 = (*s_3).n; - light_3.l = _e453; - let _e456 = (*s_3).n; - light_3.h = _e456; + let _e453 = position; + let _e455 = v_world_position_1; + toLight_1 = (_e453.xyz - _e455); + let _e457 = toLight_1; + distance_3 = length(_e457); + let _e459 = distance_3; + if (_e459 < 0.000001f) { + let _e462 = (*s_4).n; + light_3.l = _e462; + let _e465 = (*s_4).n; + light_3.h = _e465; light_3.radiance = vec3(0f, 0f, 0f); light_3.n_dot_l = 0f; light_3.n_dot_h = 0f; light_3.v_dot_h = 0f; - let _e462 = light_3; - return _e462; - } - let _e463 = toLight_1; - let _e464 = distance_3; - light_3.l = (_e463 / vec3(_e464)); - let _e468 = distance_3; - param_92 = _e468; - let _e470 = direction[3u]; - param_93 = _e470; - let _e471 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); - attenuation_1 = _e471; - let _e472 = type_39; - if (_e472 > 1.5f) { - let _e474 = aim; - let _e476 = light_3.l; - cosAngle = dot(_e474, -(_e476)); - let _e479 = cosAngle; - let _e481 = cone[1u]; - let _e484 = cone[0u]; - let _e486 = cone[1u]; - let _e490 = attenuation_1; - attenuation_1 = (_e490 * clamp(((_e479 - _e481) / (_e484 - _e486)), 0f, 1f)); - } - } - let _e493 = light_3.l; - let _e495 = (*s_3).v; - light_3.h = normalize((_e493 + _e495)); - let _e500 = (*s_3).n; + let _e471 = light_3; + return _e471; + } + let _e472 = toLight_1; + let _e473 = distance_3; + light_3.l = (_e472 / vec3(_e473)); + let _e477 = distance_3; + param_98 = _e477; + let _e479 = direction[3u]; + param_99 = _e479; + let _e480 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_98), (¶m_99)); + attenuation_1 = _e480; + let _e481 = type_39; + if (_e481 > 1.5f) { + let _e483 = aim; + let _e485 = light_3.l; + cosAngle = dot(_e483, -(_e485)); + let _e488 = cosAngle; + let _e490 = cone[1u]; + let _e493 = cone[0u]; + let _e495 = cone[1u]; + let _e499 = attenuation_1; + attenuation_1 = (_e499 * clamp(((_e488 - _e490) / (_e493 - _e495)), 0f, 1f)); + } + } let _e502 = light_3.l; - light_3.n_dot_l = max(dot(_e500, _e502), 0f); - let _e507 = (*s_3).n; - let _e509 = light_3.h; - light_3.n_dot_h = max(dot(_e507, _e509), 0f); - let _e514 = (*s_3).v; - let _e516 = light_3.h; - light_3.v_dot_h = max(dot(_e514, _e516), 0f); - let _e520 = color_1; - let _e523 = color_1[3u]; - let _e525 = attenuation_1; - light_3.radiance = ((_e520.xyz * _e523) * _e525); - let _e528 = light_3; - return _e528; + let _e504 = (*s_4).v; + light_3.h = normalize((_e502 + _e504)); + let _e509 = (*s_4).n; + let _e511 = light_3.l; + light_3.n_dot_l = max(dot(_e509, _e511), 0f); + let _e516 = (*s_4).n; + let _e518 = light_3.h; + light_3.n_dot_h = max(dot(_e516, _e518), 0f); + let _e523 = (*s_4).v; + let _e525 = light_3.h; + light_3.v_dot_h = max(dot(_e523, _e525), 0f); + let _e529 = color_1; + let _e532 = color_1[3u]; + let _e534 = attenuation_1; + light_3.radiance = ((_e529.xyz * _e532) * _e534); + let _e537 = light_3; + return _e537; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -7682,296 +7980,289 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_22: f32; + var local_24: f32; var cell: f32; var row_3: f32; var header: vec4; - var param_94: f32; - var param_95: f32; + var param_100: f32; + var param_101: f32; - let _e190 = light_list_info.cluster_view_projection; - let _e191 = (*world_3); - clip_1 = (_e190 * vec4(_e191.x, _e191.y, _e191.z, 1f)); - let _e197 = clip_1; - let _e200 = clip_1[3u]; - ndc_1 = (_e197.xy / vec2(max(_e200, 0.000001f))); - let _e205 = light_list_info.cluster_grid; - grid = _e205.xyz; - let _e209 = light_list_info.cluster_depth[0u]; - near_1 = _e209; - let _e211 = ndc_1[0u]; - let _e215 = grid[0u]; - let _e219 = grid[0u]; - tx = clamp(floor((((_e211 * 0.5f) + 0.5f) * _e215)), 0f, (_e219 - 1f)); - let _e223 = ndc_1[1u]; - let _e227 = grid[1u]; - let _e231 = grid[1u]; - ty = clamp(floor((((_e223 * 0.5f) + 0.5f) * _e227)), 0f, (_e231 - 1f)); - let _e235 = clip_1[3u]; - let _e236 = near_1; - if (_e235 <= _e236) { - local_22 = 0f; + let _e199 = light_list_info.cluster_view_projection; + let _e200 = (*world_3); + clip_1 = (_e199 * vec4(_e200.x, _e200.y, _e200.z, 1f)); + let _e206 = clip_1; + let _e209 = clip_1[3u]; + ndc_1 = (_e206.xy / vec2(max(_e209, 0.000001f))); + let _e214 = light_list_info.cluster_grid; + grid = _e214.xyz; + let _e218 = light_list_info.cluster_depth[0u]; + near_1 = _e218; + let _e220 = ndc_1[0u]; + let _e224 = grid[0u]; + let _e228 = grid[0u]; + tx = clamp(floor((((_e220 * 0.5f) + 0.5f) * _e224)), 0f, (_e228 - 1f)); + let _e232 = ndc_1[1u]; + let _e236 = grid[1u]; + let _e240 = grid[1u]; + ty = clamp(floor((((_e232 * 0.5f) + 0.5f) * _e236)), 0f, (_e240 - 1f)); + let _e244 = clip_1[3u]; + let _e245 = near_1; + if (_e244 <= _e245) { + local_24 = 0f; } else { - let _e239 = clip_1[3u]; - let _e240 = near_1; - let _e245 = light_list_info.cluster_depth[1u]; - let _e249 = grid[2u]; - local_22 = clamp(floor((log((_e239 / _e240)) * _e245)), 0f, (_e249 - 1f)); - } - let _e252 = local_22; - tz = _e252; - let _e253 = tx; - let _e254 = ty; - let _e256 = grid[0u]; - let _e259 = tz; - let _e261 = grid[0u]; - let _e264 = grid[1u]; - cell = ((_e253 + (_e254 * _e256)) + ((_e259 * _e261) * _e264)); - let _e267 = cell; - row_3 = floor((_e267 / 4f)); - let _e270 = cell; - let _e271 = row_3; - let _e276 = light_list_info.cluster_depth[2u]; - let _e277 = row_3; - param_94 = (_e270 - (_e271 * 4f)); - param_95 = (_e276 + _e277); - let _e279 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); - header = _e279; - let _e281 = header[0u]; - g_cluster_offset = _e281; - let _e283 = header[1u]; - g_cluster_count = _e283; + let _e248 = clip_1[3u]; + let _e249 = near_1; + let _e254 = light_list_info.cluster_depth[1u]; + let _e258 = grid[2u]; + local_24 = clamp(floor((log((_e248 / _e249)) * _e254)), 0f, (_e258 - 1f)); + } + let _e261 = local_24; + tz = _e261; + let _e262 = tx; + let _e263 = ty; + let _e265 = grid[0u]; + let _e268 = tz; + let _e270 = grid[0u]; + let _e273 = grid[1u]; + cell = ((_e262 + (_e263 * _e265)) + ((_e268 * _e270) * _e273)); + let _e276 = cell; + row_3 = floor((_e276 / 4f)); + let _e279 = cell; + let _e280 = row_3; + let _e285 = light_list_info.cluster_depth[2u]; + let _e286 = row_3; + param_100 = (_e279 - (_e280 * 4f)); + param_101 = (_e285 + _e286); + let _e288 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_100), (¶m_101)); + header = _e288; + let _e290 = header[0u]; + g_cluster_offset = _e290; + let _e292 = header[1u]; + g_cluster_count = _e292; return; } fn LightCount_u0028_() -> i32 { var tail: f32; - var param_96: vec3; + var param_102: vec3; - let _e179 = light_list_info.list[0u]; - tail = _e179; - let _e180 = Clustered_u0028_(); - if _e180 { - let _e181 = v_world_position_1; - param_96 = _e181; - FindCluster_u0028_vf3_u003b((¶m_96)); - let _e182 = g_cluster_count; - tail = _e182; + let _e188 = light_list_info.list[0u]; + tail = _e188; + let _e189 = Clustered_u0028_(); + if _e189 { + let _e190 = v_world_position_1; + param_102 = _e190; + FindCluster_u0028_vf3_u003b((¶m_102)); + let _e191 = g_cluster_count; + tail = _e191; } - let _e185 = frag_info.frame_params[1u]; - let _e189 = tail; - return (clamp(i32((_e185 + 0.5f)), 0i, 8i) + clamp(i32((_e189 + 0.5f)), 0i, 24i)); + let _e194 = frag_info.frame_params[1u]; + let _e198 = tail; + return (clamp(i32((_e194 + 0.5f)), 0i, 8i) + clamp(i32((_e198 + 0.5f)), 0i, 24i)); } -fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_4: ptr) -> vec3 { +fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_5: ptr) -> vec3 { var total_1: vec3; var count_1: i32; var i_8: i32; var light_4: LightSample; - var param_97: i32; - var param_98: Surface; + var param_103: i32; + var param_104: Surface; var visibility: f32; - var param_99: i32; - var local_23: f32; - var param_100: Surface; - var param_101: LightSample; - var param_102: i32; - var param_103: vec3; - var param_104: vec3; var param_105: i32; + var local_25: f32; var param_106: Surface; var param_107: LightSample; + var param_108: i32; + var param_109: vec3; + var param_110: vec3; + var param_111: i32; + var param_112: Surface; + var param_113: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e193 = LightCount_u0028_(); - count_1 = _e193; + let _e202 = LightCount_u0028_(); + count_1 = _e202; i_8 = 0i; loop { - let _e194 = i_8; - if (_e194 < 32i) { - let _e196 = i_8; - let _e197 = count_1; - if (_e196 >= _e197) { + let _e203 = i_8; + if (_e203 < 32i) { + let _e205 = i_8; + let _e206 = count_1; + if (_e205 >= _e206) { break; } - let _e199 = i_8; - param_97 = _e199; - let _e200 = (*s_4); - param_98 = _e200; - let _e201 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); - light_4 = _e201; - let _e203 = light_4.n_dot_l; - if (_e203 <= 0f) { + let _e208 = i_8; + param_103 = _e208; + let _e209 = (*s_5); + param_104 = _e209; + let _e210 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_103), (¶m_104)); + light_4 = _e210; + let _e212 = light_4.n_dot_l; + if (_e212 <= 0f) { continue; } - let _e205 = i_8; - param_99 = _e205; - let _e206 = LightHasShadow_u0028_i1_u003b((¶m_99)); - if _e206 { - let _e207 = (*s_4); - param_100 = _e207; - let _e208 = light_4; - param_101 = _e208; - let _e209 = i_8; - param_102 = _e209; - let _e210 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); - let _e211 = v_world_position_1; - param_103 = _e211; - let _e213 = (*s_4).n; - param_104 = _e213; - let _e214 = i_8; - param_105 = _e214; - let _e215 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_23 = (_e210 * _e215); + let _e214 = i_8; + param_105 = _e214; + let _e215 = LightHasShadow_u0028_i1_u003b((¶m_105)); + if _e215 { + let _e216 = (*s_5); + param_106 = _e216; + let _e217 = light_4; + param_107 = _e217; + let _e218 = i_8; + param_108 = _e218; + let _e219 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_106), (¶m_107), (¶m_108)); + let _e220 = v_world_position_1; + param_109 = _e220; + let _e222 = (*s_5).n; + param_110 = _e222; + let _e223 = i_8; + param_111 = _e223; + let _e224 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_109), (¶m_110), (¶m_111)); + local_25 = (_e219 * _e224); } else { - local_23 = 1f; + local_25 = 1f; } - let _e217 = local_23; - visibility = _e217; - let _e218 = visibility; - if (_e218 <= 0f) { + let _e226 = local_25; + visibility = _e226; + let _e227 = visibility; + if (_e227 <= 0f) { continue; } - let _e220 = (*s_4); - param_106 = _e220; - let _e221 = light_4; - param_107 = _e221; - let _e222 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); - let _e224 = light_4.radiance; - let _e227 = light_4.n_dot_l; - let _e229 = visibility; - let _e231 = total_1; - total_1 = (_e231 + (((_e222 * _e224) * _e227) * _e229)); + let _e229 = (*s_5); + param_112 = _e229; + let _e230 = light_4; + param_113 = _e230; + let _e231 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_112), (¶m_113)); + let _e233 = light_4.radiance; + let _e236 = light_4.n_dot_l; + let _e238 = visibility; + let _e240 = total_1; + total_1 = (_e240 + (((_e231 * _e233) * _e236) * _e238)); continue; } else { break; } continuing { - let _e233 = i_8; - i_8 = (_e233 + 1i); + let _e242 = i_8; + i_8 = (_e242 + 1i); } } - let _e235 = total_1; - return _e235; + let _e244 = total_1; + return _e244; } fn SampleLightmap_u0028_() -> vec3 { var texel_4: vec4; - let _e176 = v_lightmap_uv_1; - let _e177 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e176); - texel_4 = _e177; - let _e178 = texel_4; - let _e181 = texel_4[3u]; - return ((_e178.xyz * _e181) * 8f); -} - -fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e176 = (*srgb); - let _e179 = (*srgb); - let _e184 = (*srgb); - return mix((_e176 / vec3(12.92f)), pow(((_e179 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e184)); + let _e185 = v_lightmap_uv_1; + let _e186 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e185); + texel_4 = _e186; + let _e187 = texel_4; + let _e190 = texel_4[3u]; + return ((_e187.xyz * _e190) * 8f); } fn MaterialLodBias_u0028_() -> f32 { - let _e177 = frag_info.target_origin[1u]; - return _e177; + let _e186 = frag_info.target_origin[1u]; + return _e186; } -fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_5: ptr) { +fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) { var emissive: vec3; - var param_108: vec3; + var param_114: vec3; - let _e178 = v_texcoord_1; - let _e179 = MaterialLodBias_u0028_(); - let _e180 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e178, _e179); - param_108 = _e180.xyz; - let _e182 = SrgbToLinear_u0028_vf3_u003b((¶m_108)); - emissive = _e182; - let _e183 = emissive; - let _e185 = frag_info.emissive; - let _e190 = frag_info.material2_[3u]; - (*s_5).emissive = ((_e183 * _e185.xyz) * _e190); + let _e187 = v_texcoord_1; + let _e188 = MaterialLodBias_u0028_(); + let _e189 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e187, _e188); + param_114 = _e189.xyz; + let _e191 = SrgbToLinear_u0028_vf3_u003b((¶m_114)); + emissive = _e191; + let _e192 = emissive; + let _e194 = frag_info.emissive; + let _e199 = frag_info.material2_[3u]; + (*s_6).emissive = ((_e192 * _e194.xyz) * _e199); return; } -fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) { +fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) { var occlusion: f32; - let _e177 = v_texcoord_1; - let _e178 = MaterialLodBias_u0028_(); - let _e179 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e177, _e178); - occlusion = _e179.x; - let _e181 = occlusion; - let _e184 = frag_info.material2_[2u]; - (*s_6).occlusion = mix(1f, _e181, clamp(_e184, 0f, 1f)); + let _e186 = v_texcoord_1; + let _e187 = MaterialLodBias_u0028_(); + let _e188 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e186, _e187); + occlusion = _e188.x; + let _e190 = occlusion; + let _e193 = frag_info.material2_[2u]; + (*s_7).occlusion = mix(1f, _e190, clamp(_e193, 0f, 1f)); return; } -fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) { +fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { var sampledTexel: vec4; var t_2: vec3; var b_3: vec3; var sampled: vec3; - let _e180 = v_texcoord_1; - let _e181 = MaterialLodBias_u0028_(); - let _e182 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e180, _e181); - sampledTexel = _e182; - let _e183 = v_tangent_1; - t_2 = _e183.xyz; - let _e185 = t_2; - let _e187 = (*s_7).n; - let _e189 = (*s_7).n; - let _e190 = t_2; - t_2 = (_e185 - (_e187 * dot(_e189, _e190))); + let _e189 = v_texcoord_1; + let _e190 = MaterialLodBias_u0028_(); + let _e191 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e189, _e190); + sampledTexel = _e191; + let _e192 = v_tangent_1; + t_2 = _e192.xyz; let _e194 = t_2; - let _e195 = t_2; - if (dot(_e194, _e195) < 0.000000000001f) { + let _e196 = (*s_8).n; + let _e198 = (*s_8).n; + let _e199 = t_2; + t_2 = (_e194 - (_e196 * dot(_e198, _e199))); + let _e203 = t_2; + let _e204 = t_2; + if (dot(_e203, _e204) < 0.000000000001f) { return; } - let _e198 = t_2; - t_2 = normalize(_e198); - let _e201 = (*s_7).n; - let _e202 = t_2; - let _e205 = v_tangent_1[3u]; - b_3 = (cross(_e201, _e202) * _e205); - let _e207 = gl_FrontFacing_1; - if !(_e207) { - let _e209 = t_2; - t_2 = -(_e209); - } - let _e211 = sampledTexel; - sampled = ((_e211.xyz * 2f) - vec3(1f)); - let _e218 = frag_info.emissive[3u]; - if (_e218 > 0.5f) { - let _e220 = sampled; - let _e222 = sampled; - sampled[2u] = sqrt(max((1f - dot(_e220.xy, _e222.xy)), 0f)); - } - let _e231 = frag_info.material2_[1u]; - let _e232 = sampled; - let _e234 = (_e232.xy * _e231); - sampled[0u] = _e234.x; - sampled[1u] = _e234.y; - let _e239 = t_2; - let _e241 = sampled[0u]; - let _e243 = b_3; - let _e245 = sampled[1u]; - let _e249 = (*s_7).n; - let _e251 = sampled[2u]; - (*s_7).n = normalize((((_e239 * _e241) + (_e243 * _e245)) + (_e249 * _e251))); - let _e257 = (*s_7).n; - let _e259 = (*s_7).v; - (*s_7).n_dot_v = max(dot(_e257, _e259), 0.0001f); + let _e207 = t_2; + t_2 = normalize(_e207); + let _e210 = (*s_8).n; + let _e211 = t_2; + let _e214 = v_tangent_1[3u]; + b_3 = (cross(_e210, _e211) * _e214); + let _e216 = gl_FrontFacing_1; + if !(_e216) { + let _e218 = t_2; + t_2 = -(_e218); + } + let _e220 = sampledTexel; + sampled = ((_e220.xyz * 2f) - vec3(1f)); + let _e227 = frag_info.emissive[3u]; + if (_e227 > 0.5f) { + let _e229 = sampled; + let _e231 = sampled; + sampled[2u] = sqrt(max((1f - dot(_e229.xy, _e231.xy)), 0f)); + } + let _e240 = frag_info.material2_[1u]; + let _e241 = sampled; + let _e243 = (_e241.xy * _e240); + sampled[0u] = _e243.x; + sampled[1u] = _e243.y; + let _e248 = t_2; + let _e250 = sampled[0u]; + let _e252 = b_3; + let _e254 = sampled[1u]; + let _e258 = (*s_8).n; + let _e260 = sampled[2u]; + (*s_8).n = normalize((((_e248 * _e250) + (_e252 * _e254)) + (_e258 * _e260))); + let _e266 = (*s_8).n; + let _e268 = (*s_8).v; + (*s_8).n_dot_v = max(dot(_e266, _e268), 0.0001f); return; } fn AtlasTexel_u0028_vf2_u003b(texel_5: ptr>) -> vec4 { - let _e176 = (*texel_5); - let _e180 = irradiance_info.atlas; - let _e183 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e176 + vec2(0.5f)) * _e180.xy), 0f); - return _e183; + let _e185 = (*texel_5); + let _e189 = irradiance_info.atlas; + let _e192 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e185 + vec2(0.5f)) * _e189.xy), 0f); + return _e192; } fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr>, interior: ptr, uv_5: ptr>) -> vec4 { @@ -7979,50 +8270,50 @@ fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr; var f: vec2; var a_4: vec4; - var param_109: vec2; + var param_115: vec2; var b_4: vec4; - var param_110: vec2; - var c: vec4; - var param_111: vec2; + var param_116: vec2; + var c_1: vec4; + var param_117: vec2; var d_4: vec4; - var param_112: vec2; + var param_118: vec2; - let _e189 = (*uv_5); - let _e190 = (*interior); - at_1 = ((vec2(1f) + (_e189 * _e190)) - vec2(0.5f)); - let _e196 = at_1; - low = floor(_e196); - let _e198 = at_1; - let _e199 = low; - f = (_e198 - _e199); - let _e201 = (*corner); - let _e202 = low; - param_109 = (_e201 + _e202); - let _e204 = AtlasTexel_u0028_vf2_u003b((¶m_109)); - a_4 = _e204; - let _e205 = (*corner); - let _e206 = low; - param_110 = ((_e205 + _e206) + vec2(1f, 0f)); - let _e209 = AtlasTexel_u0028_vf2_u003b((¶m_110)); - b_4 = _e209; + let _e198 = (*uv_5); + let _e199 = (*interior); + at_1 = ((vec2(1f) + (_e198 * _e199)) - vec2(0.5f)); + let _e205 = at_1; + low = floor(_e205); + let _e207 = at_1; + let _e208 = low; + f = (_e207 - _e208); let _e210 = (*corner); let _e211 = low; - param_111 = ((_e210 + _e211) + vec2(0f, 1f)); - let _e214 = AtlasTexel_u0028_vf2_u003b((¶m_111)); - c = _e214; - let _e215 = (*corner); - let _e216 = low; - param_112 = ((_e215 + _e216) + vec2(1f, 1f)); - let _e219 = AtlasTexel_u0028_vf2_u003b((¶m_112)); - d_4 = _e219; - let _e220 = a_4; - let _e221 = b_4; - let _e223 = f[0u]; - let _e226 = c; - let _e227 = d_4; - let _e229 = f[0u]; - let _e233 = f[1u]; - return mix(mix(_e220, _e221, vec4(_e223)), mix(_e226, _e227, vec4(_e229)), vec4(_e233)); + param_115 = (_e210 + _e211); + let _e213 = AtlasTexel_u0028_vf2_u003b((¶m_115)); + a_4 = _e213; + let _e214 = (*corner); + let _e215 = low; + param_116 = ((_e214 + _e215) + vec2(1f, 0f)); + let _e218 = AtlasTexel_u0028_vf2_u003b((¶m_116)); + b_4 = _e218; + let _e219 = (*corner); + let _e220 = low; + param_117 = ((_e219 + _e220) + vec2(0f, 1f)); + let _e223 = AtlasTexel_u0028_vf2_u003b((¶m_117)); + c_1 = _e223; + let _e224 = (*corner); + let _e225 = low; + param_118 = ((_e224 + _e225) + vec2(1f, 1f)); + let _e228 = AtlasTexel_u0028_vf2_u003b((¶m_118)); + d_4 = _e228; + let _e229 = a_4; + let _e230 = b_4; + let _e232 = f[0u]; + let _e235 = c_1; + let _e236 = d_4; + let _e238 = f[0u]; + let _e242 = f[1u]; + return mix(mix(_e229, _e230, vec4(_e232)), mix(_e235, _e236, vec4(_e238)), vec4(_e242)); } fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec2 { @@ -8030,32 +8321,32 @@ fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec var n_5: vec3; var xy: vec2; - let _e180 = (*direction_1)[0u]; - let _e183 = (*direction_1)[1u]; - let _e187 = (*direction_1)[2u]; - sum_1 = ((abs(_e180) + abs(_e183)) + abs(_e187)); - let _e190 = sum_1; - if (_e190 <= 0f) { + let _e189 = (*direction_1)[0u]; + let _e192 = (*direction_1)[1u]; + let _e196 = (*direction_1)[2u]; + sum_1 = ((abs(_e189) + abs(_e192)) + abs(_e196)); + let _e199 = sum_1; + if (_e199 <= 0f) { return vec2(0.5f, 0.5f); } - let _e192 = (*direction_1); - let _e193 = sum_1; - n_5 = (_e192 / vec3(_e193)); - let _e196 = n_5; - xy = _e196.xy; - let _e199 = n_5[2u]; - if (_e199 < 0f) { - let _e202 = n_5[1u]; - let _e206 = n_5[0u]; - let _e211 = n_5[0u]; - let _e215 = n_5[1u]; - xy = vec2(((1f - abs(_e202)) * select(-1f, 1f, (_e206 >= 0f))), ((1f - abs(_e211)) * select(-1f, 1f, (_e215 >= 0f)))); - } - let _e220 = xy; - return ((_e220 * 0.5f) + vec2(0.5f)); + let _e201 = (*direction_1); + let _e202 = sum_1; + n_5 = (_e201 / vec3(_e202)); + let _e205 = n_5; + xy = _e205.xy; + let _e208 = n_5[2u]; + if (_e208 < 0f) { + let _e211 = n_5[1u]; + let _e215 = n_5[0u]; + let _e220 = n_5[0u]; + let _e224 = n_5[1u]; + xy = vec2(((1f - abs(_e211)) * select(-1f, 1f, (_e215 >= 0f))), ((1f - abs(_e220)) * select(-1f, 1f, (_e224 >= 0f)))); + } + let _e229 = xy; + return ((_e229 * 0.5f) + vec2(0.5f)); } -fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view: ptr>) -> vec3 { +fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view_1: ptr>) -> vec3 { var origin_2: vec3; var spacing: vec3; var counts: vec3; @@ -8077,9 +8368,9 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var irradianceCorner: vec2; var momentCorner: vec2; - var param_113: vec2; + var param_119: vec2; var trilinear: vec3; - var weight_2: f32; + var weight_3: f32; var probePosition: vec3; var toProbe: vec3; var distance_4: f32; @@ -8087,334 +8378,337 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; - var param_114: vec3; - var param_115: vec2; - var param_116: f32; - var param_117: vec2; + var param_120: vec3; + var param_121: vec2; + var param_122: f32; + var param_123: vec2; var chebyshev: f32; var variance_1: f32; var difference: f32; - var param_118: vec3; - var param_119: vec2; - var param_120: f32; - var param_121: vec2; - var local_24: vec3; - - let _e222 = irradiance_info.origin; - origin_2 = _e222.xyz; - let _e225 = irradiance_info.spacing; - spacing = _e225.xyz; - let _e228 = irradiance_info.counts; - counts = _e228.xyz; - let _e232 = irradiance_info.tiles[0u]; - irradianceTile = _e232; - let _e235 = irradiance_info.tiles[1u]; - depthTile = _e235; - let _e238 = irradiance_info.tiles[2u]; - columns = _e238; - let _e241 = irradiance_info.tiles[3u]; - momentsTop = _e241; - let _e242 = (*normal_1); - unit = normalize(_e242); - let _e244 = (*world_4); - let _e245 = unit; - let _e248 = irradiance_info.spacing[3u]; - let _e251 = (*view); - let _e254 = irradiance_info.counts[3u]; - biased = ((_e244 + (_e245 * _e248)) + (_e251 * _e254)); - let _e257 = biased; - let _e258 = origin_2; - let _e260 = spacing; - grid_1 = ((_e257 - _e258) / _e260); - let _e262 = grid_1; - let _e264 = counts; - base = clamp(floor(_e262), vec3(0f, 0f, 0f), (_e264 - vec3(2f))); - let _e268 = grid_1; - let _e269 = base; - f_1 = clamp((_e268 - _e269), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + var param_124: vec3; + var param_125: vec2; + var param_126: f32; + var param_127: vec2; + var local_26: vec3; + + let _e231 = irradiance_info.origin; + origin_2 = _e231.xyz; + let _e234 = irradiance_info.spacing; + spacing = _e234.xyz; + let _e237 = irradiance_info.counts; + counts = _e237.xyz; + let _e241 = irradiance_info.tiles[0u]; + irradianceTile = _e241; + let _e244 = irradiance_info.tiles[1u]; + depthTile = _e244; + let _e247 = irradiance_info.tiles[2u]; + columns = _e247; + let _e250 = irradiance_info.tiles[3u]; + momentsTop = _e250; + let _e251 = (*normal_1); + unit = normalize(_e251); + let _e253 = (*world_4); + let _e254 = unit; + let _e257 = irradiance_info.spacing[3u]; + let _e260 = (*view_1); + let _e263 = irradiance_info.counts[3u]; + biased = ((_e253 + (_e254 * _e257)) + (_e260 * _e263)); + let _e266 = biased; + let _e267 = origin_2; + let _e269 = spacing; + grid_1 = ((_e266 - _e267) / _e269); + let _e271 = grid_1; + let _e273 = counts; + base = clamp(floor(_e271), vec3(0f, 0f, 0f), (_e273 - vec3(2f))); + let _e277 = grid_1; + let _e278 = base; + f_1 = clamp((_e277 - _e278), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); total_2 = vec3(0f, 0f, 0f); weights = 0f; corner_1 = 0i; loop { - let _e272 = corner_1; - if (_e272 < 8i) { - let _e274 = corner_1; - let _e277 = corner_1; - let _e282 = corner_1; - offset_3 = vec3(f32((_e274 & 1i)), f32(((_e277 >> bitcast(1i)) & 1i)), f32(((_e282 >> bitcast(2i)) & 1i))); - let _e288 = base; - let _e289 = offset_3; - cell_1 = (_e288 + _e289); - let _e292 = cell_1[2u]; - let _e294 = counts[1u]; - let _e297 = cell_1[1u]; - let _e300 = counts[0u]; - let _e303 = cell_1[0u]; - probe = ((((_e292 * _e294) + _e297) * _e300) + _e303); - let _e305 = probe; - let _e306 = columns; - let _e311 = probe; - let _e312 = columns; - tile_4 = vec2((_e305 - (floor((_e305 / _e306)) * _e306)), floor((_e311 / _e312))); - let _e316 = tile_4; - let _e317 = irradianceTile; - irradianceCorner = (_e316 * (_e317 + 2f)); - let _e321 = tile_4[0u]; - let _e322 = depthTile; - let _e325 = momentsTop; - let _e327 = tile_4[1u]; - let _e328 = depthTile; - momentCorner = vec2((_e321 * (_e322 + 2f)), (_e325 + (_e327 * (_e328 + 2f)))); - let _e333 = irradianceCorner; - param_113 = (_e333 + vec2(1f)); - let _e336 = AtlasTexel_u0028_vf2_u003b((¶m_113)); - if (_e336.w < 0.5f) { + let _e281 = corner_1; + if (_e281 < 8i) { + let _e283 = corner_1; + let _e286 = corner_1; + let _e291 = corner_1; + offset_3 = vec3(f32((_e283 & 1i)), f32(((_e286 >> bitcast(1i)) & 1i)), f32(((_e291 >> bitcast(2i)) & 1i))); + let _e297 = base; + let _e298 = offset_3; + cell_1 = (_e297 + _e298); + let _e301 = cell_1[2u]; + let _e303 = counts[1u]; + let _e306 = cell_1[1u]; + let _e309 = counts[0u]; + let _e312 = cell_1[0u]; + probe = ((((_e301 * _e303) + _e306) * _e309) + _e312); + let _e314 = probe; + let _e315 = columns; + let _e320 = probe; + let _e321 = columns; + tile_4 = vec2((_e314 - (floor((_e314 / _e315)) * _e315)), floor((_e320 / _e321))); + let _e325 = tile_4; + let _e326 = irradianceTile; + irradianceCorner = (_e325 * (_e326 + 2f)); + let _e330 = tile_4[0u]; + let _e331 = depthTile; + let _e334 = momentsTop; + let _e336 = tile_4[1u]; + let _e337 = depthTile; + momentCorner = vec2((_e330 * (_e331 + 2f)), (_e334 + (_e336 * (_e337 + 2f)))); + let _e342 = irradianceCorner; + param_119 = (_e342 + vec2(1f)); + let _e345 = AtlasTexel_u0028_vf2_u003b((¶m_119)); + if (_e345.w < 0.5f) { continue; } - let _e339 = f_1; - let _e341 = f_1; - let _e342 = offset_3; - trilinear = mix((vec3(1f, 1f, 1f) - _e339), _e341, _e342); - let _e345 = trilinear[0u]; - let _e347 = trilinear[1u]; - let _e350 = trilinear[2u]; - weight_2 = max(((_e345 * _e347) * _e350), 0.001f); - let _e353 = origin_2; - let _e354 = spacing; - let _e355 = cell_1; - probePosition = (_e353 + (_e354 * _e355)); - let _e358 = probePosition; - let _e359 = biased; - toProbe = (_e358 - _e359); - let _e361 = toProbe; - distance_4 = length(_e361); - let _e363 = distance_4; - if (_e363 > 0.000001f) { - let _e365 = toProbe; - let _e366 = distance_4; - direction_2 = (_e365 / vec3(_e366)); - let _e369 = unit; - let _e370 = probePosition; - let _e371 = (*world_4); - facing = ((dot(_e369, normalize((_e370 - _e371))) * 0.5f) + 0.5f); - let _e377 = facing; - let _e378 = facing; - probeWeight = ((_e377 * _e378) + 0.2f); - let _e381 = direction_2; - param_114 = -(_e381); - let _e383 = ProbeOctahedral_u0028_vf3_u003b((¶m_114)); - let _e384 = momentCorner; - param_115 = _e384; - let _e385 = depthTile; - param_116 = _e385; - param_117 = _e383; - let _e386 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_115), (¶m_116), (¶m_117)); - moments_3 = _e386.xy; + let _e348 = f_1; + let _e350 = f_1; + let _e351 = offset_3; + trilinear = mix((vec3(1f, 1f, 1f) - _e348), _e350, _e351); + let _e354 = trilinear[0u]; + let _e356 = trilinear[1u]; + let _e359 = trilinear[2u]; + weight_3 = max(((_e354 * _e356) * _e359), 0.001f); + let _e362 = origin_2; + let _e363 = spacing; + let _e364 = cell_1; + probePosition = (_e362 + (_e363 * _e364)); + let _e367 = probePosition; + let _e368 = biased; + toProbe = (_e367 - _e368); + let _e370 = toProbe; + distance_4 = length(_e370); + let _e372 = distance_4; + if (_e372 > 0.000001f) { + let _e374 = toProbe; + let _e375 = distance_4; + direction_2 = (_e374 / vec3(_e375)); + let _e378 = unit; + let _e379 = probePosition; + let _e380 = (*world_4); + facing = ((dot(_e378, normalize((_e379 - _e380))) * 0.5f) + 0.5f); + let _e386 = facing; + let _e387 = facing; + probeWeight = ((_e386 * _e387) + 0.2f); + let _e390 = direction_2; + param_120 = -(_e390); + let _e392 = ProbeOctahedral_u0028_vf3_u003b((¶m_120)); + let _e393 = momentCorner; + param_121 = _e393; + let _e394 = depthTile; + param_122 = _e394; + param_123 = _e392; + let _e395 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_121), (¶m_122), (¶m_123)); + moments_3 = _e395.xy; chebyshev = 1f; - let _e388 = distance_4; - let _e390 = moments_3[0u]; - if (_e388 > _e390) { - let _e393 = moments_3[1u]; - let _e395 = moments_3[0u]; - let _e397 = moments_3[0u]; - variance_1 = max((_e393 - (_e395 * _e397)), 0.000001f); - let _e401 = distance_4; - let _e403 = moments_3[0u]; - difference = (_e401 - _e403); - let _e405 = variance_1; - let _e406 = variance_1; - let _e407 = difference; - let _e408 = difference; - chebyshev = (_e405 / (_e406 + (_e407 * _e408))); - let _e412 = chebyshev; - let _e413 = chebyshev; - let _e415 = chebyshev; - chebyshev = ((_e412 * _e413) * _e415); + let _e397 = distance_4; + let _e399 = moments_3[0u]; + if (_e397 > _e399) { + let _e402 = moments_3[1u]; + let _e404 = moments_3[0u]; + let _e406 = moments_3[0u]; + variance_1 = max((_e402 - (_e404 * _e406)), 0.000001f); + let _e410 = distance_4; + let _e412 = moments_3[0u]; + difference = (_e410 - _e412); + let _e414 = variance_1; + let _e415 = variance_1; + let _e416 = difference; + let _e417 = difference; + chebyshev = (_e414 / (_e415 + (_e416 * _e417))); + let _e421 = chebyshev; + let _e422 = chebyshev; + let _e424 = chebyshev; + chebyshev = ((_e421 * _e422) * _e424); } - let _e417 = probeWeight; - let _e418 = chebyshev; - probeWeight = max((_e417 * max(_e418, 0.05f)), 0.000001f); - let _e422 = probeWeight; - if (_e422 < 0.2f) { - let _e424 = probeWeight; - let _e425 = probeWeight; - let _e428 = probeWeight; - probeWeight = (_e428 * ((_e424 * _e425) * 25f)); + let _e426 = probeWeight; + let _e427 = chebyshev; + probeWeight = max((_e426 * max(_e427, 0.05f)), 0.000001f); + let _e431 = probeWeight; + if (_e431 < 0.2f) { + let _e433 = probeWeight; + let _e434 = probeWeight; + let _e437 = probeWeight; + probeWeight = (_e437 * ((_e433 * _e434) * 25f)); } - let _e430 = probeWeight; - let _e431 = weight_2; - weight_2 = (_e431 * _e430); - } - let _e433 = unit; - param_118 = _e433; - let _e434 = ProbeOctahedral_u0028_vf3_u003b((¶m_118)); - let _e435 = irradianceCorner; - param_119 = _e435; - let _e436 = irradianceTile; - param_120 = _e436; - param_121 = _e434; - let _e437 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_119), (¶m_120), (¶m_121)); - let _e439 = weight_2; - let _e441 = total_2; - total_2 = (_e441 + (_e437.xyz * _e439)); - let _e443 = weight_2; - let _e444 = weights; - weights = (_e444 + _e443); + let _e439 = probeWeight; + let _e440 = weight_3; + weight_3 = (_e440 * _e439); + } + let _e442 = unit; + param_124 = _e442; + let _e443 = ProbeOctahedral_u0028_vf3_u003b((¶m_124)); + let _e444 = irradianceCorner; + param_125 = _e444; + let _e445 = irradianceTile; + param_126 = _e445; + param_127 = _e443; + let _e446 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_125), (¶m_126), (¶m_127)); + let _e448 = weight_3; + let _e450 = total_2; + total_2 = (_e450 + (_e446.xyz * _e448)); + let _e452 = weight_3; + let _e453 = weights; + weights = (_e453 + _e452); continue; } else { break; } continuing { - let _e446 = corner_1; - corner_1 = (_e446 + 1i); + let _e455 = corner_1; + corner_1 = (_e455 + 1i); } } - let _e448 = weights; - if (_e448 > 0f) { - let _e450 = total_2; - let _e451 = weights; - local_24 = (_e450 / vec3(_e451)); + let _e457 = weights; + if (_e457 > 0f) { + let _e459 = total_2; + let _e460 = weights; + local_26 = (_e459 / vec3(_e460)); } else { - local_24 = vec3(0f, 0f, 0f); + local_26 = vec3(0f, 0f, 0f); } - let _e454 = local_24; - return _e454; + let _e463 = local_26; + return _e463; } fn IrradianceEnabled_u0028_() -> bool { - let _e177 = irradiance_info.origin[3u]; - return (_e177 > 0.5f); + let _e186 = irradiance_info.origin[3u]; + return (_e186 > 0.5f); } -fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { - var param_122: vec3; - var param_123: vec3; - var param_124: vec3; - var param_125: Surface; - var param_126: Surface; - var param_127: Surface; - - let _e182 = IrradianceEnabled_u0028_(); - if _e182 { - let _e183 = v_world_position_1; - param_122 = _e183; - let _e185 = (*s_8).n; - param_123 = _e185; - let _e187 = (*s_8).v; - param_124 = _e187; - let _e188 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_122), (¶m_123), (¶m_124)); - let _e191 = frag_info.material[2u]; - (*s_8).ambient = (_e188 * _e191); - } - let _e194 = (*s_8); - param_125 = _e194; - ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_125)); - let _e195 = param_125; - (*s_8) = _e195; - let _e196 = (*s_8); - param_126 = _e196; - ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_126)); - let _e197 = param_126; - (*s_8) = _e197; - let _e198 = (*s_8); - param_127 = _e198; - ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_127)); - let _e199 = param_127; - (*s_8) = _e199; +fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) { + var param_128: vec3; + var param_129: vec3; + var param_130: vec3; + var param_131: Surface; + var param_132: Surface; + var param_133: Surface; + + let _e191 = IrradianceEnabled_u0028_(); + if _e191 { + let _e192 = v_world_position_1; + param_128 = _e192; + let _e194 = (*s_9).n; + param_129 = _e194; + let _e196 = (*s_9).v; + param_130 = _e196; + let _e197 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_128), (¶m_129), (¶m_130)); + let _e200 = frag_info.material[2u]; + (*s_9).ambient = (_e197 * _e200); + } + let _e203 = (*s_9); + param_131 = _e203; + ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_131)); + let _e204 = param_131; + (*s_9) = _e204; + let _e205 = (*s_9); + param_132 = _e205; + ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_132)); + let _e206 = param_132; + (*s_9) = _e206; + let _e207 = (*s_9); + param_133 = _e207; + ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_133)); + let _e208 = param_133; + (*s_9) = _e208; return; } fn ReadSurface_u0028_() -> Surface { var texel_6: vec4; - var s_9: Surface; - var param_128: vec3; - var param_129: vec3; + var s_10: Surface; + var param_134: vec3; + var param_135: vec3; var cutoff: f32; var anchored: vec3; var noise_1: f32; - let _e182 = v_texcoord_1; - let _e183 = MaterialLodBias_u0028_(); - let _e184 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e182, _e183); - texel_6 = _e184; - let _e185 = texel_6; - param_128 = _e185.xyz; - let _e187 = SrgbToLinear_u0028_vf3_u003b((¶m_128)); - let _e189 = frag_info.base_color; - param_129 = _e189.xyz; - let _e191 = SrgbToLinear_u0028_vf3_u003b((¶m_129)); - let _e193 = v_color_1; - s_9.albedo = ((_e187 * _e191) * _e193.xyz); - let _e198 = texel_6[3u]; - let _e201 = frag_info.base_color[3u]; - let _e204 = v_color_1[3u]; - s_9.alpha = ((_e198 * _e201) * _e204); - let _e208 = s_9.albedo; - g_albedo = _e208; - let _e211 = frag_info.material2_[0u]; - cutoff = _e211; - let _e212 = cutoff; - if (_e212 >= 0f) { - let _e215 = s_9.alpha; - let _e216 = cutoff; - if (_e215 < _e216) { + let _e191 = v_texcoord_1; + let _e192 = MaterialLodBias_u0028_(); + let _e193 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e191, _e192); + texel_6 = _e193; + let _e194 = texel_6; + param_134 = _e194.xyz; + let _e196 = SrgbToLinear_u0028_vf3_u003b((¶m_134)); + let _e198 = frag_info.base_color; + param_135 = _e198.xyz; + let _e200 = SrgbToLinear_u0028_vf3_u003b((¶m_135)); + let _e202 = v_color_1; + s_10.albedo = ((_e196 * _e200) * _e202.xyz); + let _e207 = texel_6[3u]; + let _e210 = frag_info.base_color[3u]; + let _e213 = v_color_1[3u]; + s_10.alpha = ((_e207 * _e210) * _e213); + let _e217 = s_10.albedo; + g_albedo = _e217; + let _e220 = frag_info.material2_[0u]; + cutoff = _e220; + let _e221 = cutoff; + if (_e221 >= 0f) { + let _e224 = s_10.alpha; + let _e225 = cutoff; + if (_e224 < _e225) { discard; } } else { - let _e218 = cutoff; - if (_e218 < -1.5f) { - let _e220 = v_world_position_1; - anchored = floor((_e220 * 16f)); - let _e223 = anchored; - noise_1 = fract((sin(dot(_e223, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e229 = s_9.alpha; - let _e230 = noise_1; - if (_e229 < _e230) { + let _e227 = cutoff; + if (_e227 < -1.5f) { + let _e229 = v_world_position_1; + anchored = floor((_e229 * 16f)); + let _e232 = anchored; + noise_1 = fract((sin(dot(_e232, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e238 = s_10.alpha; + let _e239 = noise_1; + if (_e238 < _e239) { discard; } } } - let _e232 = cutoff; - let _e234 = cutoff; - g_premultiply = ((_e232 < 0f) && (_e234 > -0.75f)); - let _e237 = v_normal_1; - s_9.n = normalize(_e237); - let _e240 = gl_FrontFacing_1; - if !(_e240) { - let _e243 = s_9.n; - s_9.n = -(_e243); - } - let _e247 = frag_info.camera_position; - let _e249 = v_world_position_1; - s_9.v = normalize((_e247.xyz - _e249)); - let _e254 = s_9.n; - let _e256 = s_9.v; - s_9.n_dot_v = max(dot(_e254, _e256), 0.0001f); - let _e262 = frag_info.material[0u]; - s_9.metallic = clamp(_e262, 0f, 1f); - let _e267 = frag_info.material[1u]; - s_9.roughness = clamp(_e267, 0.02f, 1f); - let _e271 = frag_info.ambient_ground; - let _e274 = frag_info.ambient_sky; - let _e278 = s_9.n[1u]; - let _e285 = frag_info.material[2u]; - s_9.ambient = (mix(_e271.xyz, _e274.xyz, vec3(((_e278 * 0.5f) + 0.5f))) * _e285); - let _e290 = frag_info.frame_params[0u]; - s_9.exposure = max(_e290, 0f); - s_9.occlusion = 1f; - s_9.emissive = vec3(0f, 0f, 0f); - let _e295 = s_9; - return _e295; + let _e241 = cutoff; + let _e243 = cutoff; + g_premultiply = ((_e241 < 0f) && (_e243 > -0.75f)); + let _e246 = v_normal_1; + s_10.n = normalize(_e246); + let _e249 = gl_FrontFacing_1; + if !(_e249) { + let _e252 = s_10.n; + s_10.n = -(_e252); + } + let _e256 = frag_info.camera_position; + let _e258 = v_world_position_1; + s_10.v = normalize((_e256.xyz - _e258)); + let _e263 = s_10.n; + let _e265 = s_10.v; + s_10.n_dot_v = max(dot(_e263, _e265), 0.0001f); + let _e271 = frag_info.material[0u]; + s_10.metallic = clamp(_e271, 0f, 1f); + let _e276 = frag_info.material[1u]; + s_10.roughness = clamp(_e276, 0.02f, 1f); + let _e280 = frag_info.ambient_ground; + let _e283 = frag_info.ambient_sky; + let _e287 = s_10.n[1u]; + let _e294 = frag_info.material[2u]; + s_10.ambient = (mix(_e280.xyz, _e283.xyz, vec3(((_e287 * 0.5f) + 0.5f))) * _e294); + let _e299 = frag_info.frame_params[0u]; + s_10.exposure = max(_e299, 0f); + s_10.occlusion = 1f; + s_10.emissive = vec3(0f, 0f, 0f); + let _e304 = s_10; + return _e304; } fn main_1() { - var s_10: Surface; - var param_130: Surface; + var s_11: Surface; + var param_136: Surface; var ambient: vec3; - var param_131: Surface; - var param_132: vec3; - var param_133: f32; - var param_134: f32; + var lit_3: vec3; + var param_137: Surface; + var param_138: Surface; + var param_139: vec3; + var param_140: vec3; + var param_141: f32; + var param_142: f32; g_albedo = vec3(0f, 0f, 0f); g_debug_surface = vec3(0f, 0f, 0f); @@ -8422,30 +8716,40 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e182 = ReadSurface_u0028_(); - s_10 = _e182; - let _e183 = s_10; - param_130 = _e183; - ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_130)); - let _e184 = param_130; - s_10 = _e184; - let _e186 = s_10.albedo; - let _e188 = s_10.ambient; - let _e189 = SampleLightmap_u0028_(); - let _e193 = s_10.occlusion; - ambient = ((_e186 * (_e188 + _e189)) * _e193); - let _e195 = s_10; - param_131 = _e195; - let _e196 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_131)); - let _e198 = s_10.occlusion; - let _e200 = ambient; - let _e203 = s_10.emissive; - param_132 = (((_e196 * _e198) + _e200) + _e203); - let _e206 = s_10.alpha; - param_133 = _e206; - let _e208 = s_10.roughness; - param_134 = _e208; - WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_132), (¶m_133), (¶m_134)); + let _e194 = ReadSurface_u0028_(); + s_11 = _e194; + let _e195 = s_11; + param_136 = _e195; + ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_136)); + let _e196 = param_136; + s_11 = _e196; + let _e198 = s_11.albedo; + let _e200 = s_11.ambient; + let _e201 = SampleLightmap_u0028_(); + let _e205 = s_11.occlusion; + ambient = ((_e198 * (_e200 + _e201)) * _e205); + let _e207 = s_11; + param_137 = _e207; + let _e208 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_137)); + let _e210 = s_11.occlusion; + let _e212 = ambient; + let _e215 = s_11.emissive; + lit_3 = (((_e208 * _e210) + _e212) + _e215); + let _e217 = s_11; + param_138 = _e217; + let _e218 = lit_3; + param_139 = _e218; + let _e219 = WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b((¶m_138), (¶m_139)); + if _e219 { + return; + } + let _e220 = lit_3; + param_140 = _e220; + let _e222 = s_11.alpha; + param_141 = _e222; + let _e224 = s_11.roughness; + param_142 = _e224; + WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_140), (¶m_141), (¶m_142)); return; } @@ -8487,7 +8791,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember( name: 'light_position', @@ -8584,6 +8888,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, shadow_bias: vec4, target_origin: vec4, + debug_view: vec4, } struct PointShadow { @@ -8938,10 +9248,10 @@ var shadow_texture_tex: texture_2d; var shadow_texture_smp: sampler; fn ViewDepth_u0028_() -> f32 { - let _e176 = v_world_position_1; - let _e178 = fog_info.eye; - let _e182 = fog_info.forward; - return dot((_e176 - _e178.xyz), _e182.xyz); + let _e185 = v_world_position_1; + let _e187 = fog_info.eye; + let _e191 = fog_info.forward; + return dot((_e185 - _e187.xyz), _e191.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -8950,22 +9260,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e181 = ViewDepth_u0028_(); - z = abs(_e181); - let _e183 = z; - near = (_e183 / 5f); - let _e185 = z; - far = (_e185 / 200f); - let _e187 = far; - let _e188 = far; - let _e190 = far; - far3_ = ((_e187 * _e188) * _e190); - let _e192 = (*alpha); - let _e193 = near; - let _e194 = near; - let _e197 = far3_; - let _e198 = far3_; - return (_e192 * clamp((10f / ((0.00001f + (_e193 * _e194)) + (_e197 * _e198))), 0.01f, 3000f)); + let _e190 = ViewDepth_u0028_(); + z = abs(_e190); + let _e192 = z; + near = (_e192 / 5f); + let _e194 = z; + far = (_e194 / 200f); + let _e196 = far; + let _e197 = far; + let _e199 = far; + far3_ = ((_e196 * _e197) * _e199); + let _e201 = (*alpha); + let _e202 = near; + let _e203 = near; + let _e206 = far3_; + let _e207 = far3_; + return (_e201 * clamp((10f / ((0.00001f + (_e202 * _e203)) + (_e206 * _e207))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -8977,39 +9287,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e185 = fog_info.forward[3u]; - mode = _e185; - let _e186 = mode; - if (_e186 > 0.5f) { - let _e189 = frag_color[3u]; - alpha_1 = _e189; - let _e190 = alpha_1; - param = _e190; - let _e191 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e191; - let _e192 = frag_color; - let _e194 = weight; - let _e195 = (_e192.xyz * _e194); - let _e196 = alpha_1; - let _e197 = weight; - accumulate = vec4(_e195.x, _e195.y, _e195.z, (_e196 * _e197)); - let _e203 = mode; - let _e205 = mode; - revealage = ((_e203 > 1.5f) && (_e205 < 2.5f)); - let _e208 = revealage; - if _e208 { - let _e209 = alpha_1; - local = vec4(_e209); + let _e194 = fog_info.forward[3u]; + mode = _e194; + let _e195 = mode; + if (_e195 > 0.5f) { + let _e198 = frag_color[3u]; + alpha_1 = _e198; + let _e199 = alpha_1; + param = _e199; + let _e200 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e200; + let _e201 = frag_color; + let _e203 = weight; + let _e204 = (_e201.xyz * _e203); + let _e205 = alpha_1; + let _e206 = weight; + accumulate = vec4(_e204.x, _e204.y, _e204.z, (_e205 * _e206)); + let _e212 = mode; + let _e214 = mode; + revealage = ((_e212 > 1.5f) && (_e214 < 2.5f)); + let _e217 = revealage; + if _e217 { + let _e218 = alpha_1; + local = vec4(_e218); } else { - let _e211 = accumulate; - local = _e211; + let _e220 = accumulate; + local = _e220; } - let _e212 = local; - frag_color = _e212; - let _e213 = mode; - if (_e213 > 2.5f) { - let _e215 = alpha_1; - frag_surface = vec4(_e215); + let _e221 = local; + frag_color = _e221; + let _e222 = mode; + if (_e222 > 2.5f) { + let _e224 = alpha_1; + frag_surface = vec4(_e224); } } return; @@ -9018,29 +9328,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e179 = (*n)[0u]; - let _e182 = (*n)[1u]; - let _e186 = (*n)[2u]; - let _e189 = (*n); - (*n) = (_e189 / vec3(((abs(_e179) + abs(_e182)) + abs(_e186)))); - let _e192 = (*n); - e = _e192.xy; + let _e188 = (*n)[0u]; + let _e191 = (*n)[1u]; let _e195 = (*n)[2u]; - if (_e195 < 0f) { - let _e197 = (*n); - let _e203 = (*n)[0u]; - let _e207 = (*n)[1u]; - e = ((vec2(1f) - abs(_e197.yx)) * vec2(select(-1f, 1f, (_e203 >= 0f)), select(-1f, 1f, (_e207 >= 0f)))); - } - let _e212 = e; - return ((_e212 * 0.5f) + vec2(0.5f)); + let _e198 = (*n); + (*n) = (_e198 / vec3(((abs(_e188) + abs(_e191)) + abs(_e195)))); + let _e201 = (*n); + e = _e201.xy; + let _e204 = (*n)[2u]; + if (_e204 < 0f) { + let _e206 = (*n); + let _e212 = (*n)[0u]; + let _e216 = (*n)[1u]; + e = ((vec2(1f) - abs(_e206.yx)) * vec2(select(-1f, 1f, (_e212 >= 0f)), select(-1f, 1f, (_e216 >= 0f)))); + } + let _e221 = e; + return ((_e221 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e177 = (*linear); - let _e179 = (*linear); - let _e183 = (*linear); - return mix((_e177 * 12.92f), ((pow(_e179, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e183)); + let _e186 = (*linear); + let _e188 = (*linear); + let _e192 = (*linear); + return mix((_e186 * 12.92f), ((pow(_e188, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e192)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -9048,37 +9358,37 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e180 = g_albedo; - param_1 = clamp(_e180, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e182 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e182.x, _e182.y, _e182.z, 1f); - let _e187 = g_debug_surface_on; - if _e187 { - let _e188 = g_debug_surface; - let _e189 = ViewDepth_u0028_(); - frag_surface = vec4(_e188.x, _e188.y, _e188.z, _e189); + let _e189 = g_albedo; + param_1 = clamp(_e189, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e191 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e191.x, _e191.y, _e191.z, 1f); + let _e196 = g_debug_surface_on; + if _e196 { + let _e197 = g_debug_surface; + let _e198 = ViewDepth_u0028_(); + frag_surface = vec4(_e197.x, _e197.y, _e197.z, _e198); return; } - let _e194 = v_normal_1; - geometric = normalize(_e194); - let _e196 = gl_FrontFacing_1; - if !(_e196) { - let _e198 = geometric; - geometric = -(_e198); - } - let _e200 = geometric; - param_2 = _e200; - let _e201 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e202 = (*roughness); - let _e204 = ViewDepth_u0028_(); - frag_surface = vec4(_e201.x, _e201.y, clamp(_e202, 0f, 1f), _e204); + let _e203 = v_normal_1; + geometric = normalize(_e203); + let _e205 = gl_FrontFacing_1; + if !(_e205) { + let _e207 = geometric; + geometric = -(_e207); + } + let _e209 = geometric; + param_2 = _e209; + let _e210 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e211 = (*roughness); + let _e213 = ViewDepth_u0028_(); + frag_surface = vec4(_e210.x, _e210.y, clamp(_e211, 0f, 1f), _e213); return; } fn EyeDistance_u0028_() -> f32 { - let _e176 = v_world_position_1; - let _e178 = fog_info.eye; - return distance(_e176, _e178.xyz); + let _e185 = v_world_position_1; + let _e187 = fog_info.eye; + return distance(_e185, _e187.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -9088,41 +9398,41 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var k: f32; var local_1: f32; - let _e184 = fog_info.fog[3u]; - density = _e184; - let _e185 = density; - if (_e185 <= 0f) { - let _e187 = (*color); - return _e187; - } - let _e188 = EyeDistance_u0028_(); - d = _e188; - let _e191 = fog_info.eye[3u]; - falloff = _e191; - let _e192 = falloff; - if (_e192 > 0f) { - let _e194 = falloff; - let _e196 = v_world_position_1[1u]; - let _e199 = fog_info.eye[1u]; - k = (_e194 * (_e196 - _e199)); - let _e202 = k; - if (abs(_e202) > 0.001f) { - let _e205 = k; - let _e209 = k; - local_1 = ((1f - exp(-(_e205))) / _e209); + let _e193 = fog_info.fog[3u]; + density = _e193; + let _e194 = density; + if (_e194 <= 0f) { + let _e196 = (*color); + return _e196; + } + let _e197 = EyeDistance_u0028_(); + d = _e197; + let _e200 = fog_info.eye[3u]; + falloff = _e200; + let _e201 = falloff; + if (_e201 > 0f) { + let _e203 = falloff; + let _e205 = v_world_position_1[1u]; + let _e208 = fog_info.eye[1u]; + k = (_e203 * (_e205 - _e208)); + let _e211 = k; + if (abs(_e211) > 0.001f) { + let _e214 = k; + let _e218 = k; + local_1 = ((1f - exp(-(_e214))) / _e218); } else { - let _e211 = k; - local_1 = (1f - (0.5f * _e211)); + let _e220 = k; + local_1 = (1f - (0.5f * _e220)); } - let _e214 = local_1; - let _e215 = density; - density = (_e215 * _e214); + let _e223 = local_1; + let _e224 = density; + density = (_e224 * _e223); } - let _e218 = fog_info.fog; - let _e220 = (*color); - let _e221 = density; - let _e223 = d; - return mix(_e218.xyz, _e220, vec3(clamp(exp((-(_e221) * _e223)), 0f, 1f))); + let _e227 = fog_info.fog; + let _e229 = (*color); + let _e230 = density; + let _e232 = d; + return mix(_e227.xyz, _e229, vec3(clamp(exp((-(_e230) * _e232)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -9130,65 +9440,205 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e182 = g_premultiply; - let _e183 = (*alpha_2); - weight_1 = select(1f, _e183, _e182); - let _e185 = (*linearColor); - param_3 = _e185; - let _e186 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e187 = weight_1; - let _e188 = (_e186 * _e187); - let _e189 = (*alpha_2); - frag_color = vec4(_e188.x, _e188.y, _e188.z, _e189); - let _e194 = (*roughness_1); - param_4 = _e194; + let _e191 = g_premultiply; + let _e192 = (*alpha_2); + weight_1 = select(1f, _e192, _e191); + let _e194 = (*linearColor); + param_3 = _e194; + let _e195 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e196 = weight_1; + let _e197 = (_e195 * _e196); + let _e198 = (*alpha_2); + frag_color = vec4(_e197.x, _e197.y, _e197.z, _e198); + let _e203 = (*roughness_1); + param_4 = _e203; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; } -fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s: ptr, light: ptr) -> vec3 { +fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { + let _e186 = (*srgb); + let _e189 = (*srgb); + let _e194 = (*srgb); + return mix((_e186 / vec3(12.92f)), pow(((_e189 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e194)); +} + +fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { + var phi_2504_: bool; + + let _e186 = (*c); + let _e187 = (*c); + let _e189 = any((_e186 != _e187)); + phi_2504_ = _e189; + if !(_e189) { + let _e191 = (*c); + phi_2504_ = any((abs(_e191) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + } + let _e196 = phi_2504_; + return _e196; +} + +fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b(s: ptr, lit: ptr>) -> bool { + var view: f32; + var code: i32; + var shown: vec3; + var param_5: vec3; + var param_6: vec3; + var grey: f32; + var param_7: vec3; + var param_8: vec3; + var local_2: vec3; + var weight_2: f32; + var local_3: f32; + var param_9: vec3; + var param_10: f32; + + let _e202 = frag_info.debug_view[0u]; + view = _e202; + let _e203 = view; + if (_e203 < 0.5f) { + return false; + } + let _e206 = gl_FragCoord_1[0u]; + let _e209 = frag_info.debug_view[1u]; + let _e212 = frag_info.debug_view[2u]; + if (_e206 < (_e209 * _e212)) { + return false; + } + let _e215 = view; + code = i32((_e215 + 0.5f)); + shown = vec3(0f, 0f, 0f); + let _e218 = code; + if (_e218 == 1i) { + let _e221 = (*s).albedo; + param_5 = clamp(_e221, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e223 = LinearToSrgb_u0028_vf3_u003b((¶m_5)); + shown = _e223; + } else { + let _e224 = code; + if (_e224 == 2i) { + let _e227 = (*s).n; + shown = ((_e227 * 0.5f) + vec3(0.5f, 0.5f, 0.5f)); + } else { + let _e230 = code; + if (_e230 == 3i) { + let _e233 = (*s).roughness; + shown = vec3(clamp(_e233, 0f, 1f)); + } else { + let _e236 = code; + if (_e236 == 4i) { + let _e239 = (*s).metallic; + shown = vec3(clamp(_e239, 0f, 1f)); + } else { + let _e242 = code; + if (_e242 == 5i) { + let _e245 = (*s).occlusion; + shown = vec3(clamp(_e245, 0f, 1f)); + } else { + let _e248 = code; + if (_e248 == 6i) { + let _e251 = (*s).emissive; + param_6 = clamp(_e251, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e253 = LinearToSrgb_u0028_vf3_u003b((¶m_6)); + shown = _e253; + } else { + let _e254 = code; + if (_e254 == 7i) { + let _e256 = v_texcoord_1; + let _e257 = fract(_e256); + shown = vec3(_e257.x, _e257.y, 0f); + } else { + let _e261 = code; + if (_e261 == 8i) { + let _e263 = (*lit); + param_7 = clamp(_e263, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e265 = LinearToSrgb_u0028_vf3_u003b((¶m_7)); + grey = dot(_e265, vec3(0.2126f, 0.7152f, 0.0722f)); + let _e267 = (*lit); + param_8 = _e267; + let _e268 = NonFinite_u0028_vf3_u003b((¶m_8)); + if _e268 { + local_2 = vec3(1f, 0f, 1f); + } else { + let _e269 = grey; + local_2 = vec3((_e269 * 0.5f)); + } + let _e272 = local_2; + shown = _e272; + } + } + } + } + } + } + } + } + let _e273 = g_premultiply; + if _e273 { + let _e275 = (*s).alpha; + local_3 = _e275; + } else { + local_3 = 1f; + } + let _e276 = local_3; + weight_2 = _e276; + let _e277 = shown; + param_9 = _e277; + let _e278 = SrgbToLinear_u0028_vf3_u003b((¶m_9)); + let _e279 = weight_2; + let _e280 = (_e278 * _e279); + let _e282 = (*s).alpha; + frag_color = vec4(_e280.x, _e280.y, _e280.z, _e282); + let _e288 = (*s).roughness; + param_10 = _e288; + WriteSurfaceGeometry_u0028_f1_u003b((¶m_10)); + WriteWeightedBlended_u0028_(); + return true; +} + +fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_1: ptr, light: ptr) -> vec3 { var shininess: f32; var specular: f32; - let _e181 = (*s).roughness; - shininess = mix(256f, 4f, _e181); - let _e184 = (*light).n_dot_h; - let _e185 = shininess; - let _e189 = frag_info.material[3u]; - specular = (pow(_e184, _e185) * _e189); - let _e192 = (*s).albedo; - let _e193 = specular; - return (_e192 + vec3(_e193)); + let _e190 = (*s_1).roughness; + shininess = mix(256f, 4f, _e190); + let _e193 = (*light).n_dot_h; + let _e194 = shininess; + let _e198 = frag_info.material[3u]; + specular = (pow(_e193, _e194) * _e198); + let _e201 = (*s_1).albedo; + let _e202 = specular; + return (_e201 + vec3(_e202)); } fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e177 = (*i); - if (_e177 == 0i) { + let _e186 = (*i); + if (_e186 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e179 = (*i); - if (_e179 == 1i) { + let _e188 = (*i); + if (_e188 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e181 = (*i); - if (_e181 == 2i) { + let _e190 = (*i); + if (_e190 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e183 = (*i); - if (_e183 == 3i) { + let _e192 = (*i); + if (_e192 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e185 = (*i); - if (_e185 == 4i) { + let _e194 = (*i); + if (_e194 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e187 = (*i); - if (_e187 == 5i) { + let _e196 = (*i); + if (_e196 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e189 = (*i); - if (_e189 == 6i) { + let _e198 = (*i); + if (_e198 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -9196,208 +9646,208 @@ fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr, ca: ptr, sa: ptr, radius: ptr) -> vec2 { var p: vec2; - var param_5: i32; + var param_11: i32; - let _e182 = (*i_1); - param_5 = _e182; - let _e183 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); - p = _e183; - let _e185 = p[0u]; - let _e186 = (*ca); - let _e189 = p[1u]; - let _e190 = (*sa); + let _e191 = (*i_1); + param_11 = _e191; + let _e192 = PointShadowDiskTap_u0028_i1_u003b((¶m_11)); + p = _e192; let _e194 = p[0u]; - let _e195 = (*sa); + let _e195 = (*ca); let _e198 = p[1u]; - let _e199 = (*ca); - let _e203 = (*radius); - return (vec2(((_e185 * _e186) - (_e189 * _e190)), ((_e194 * _e195) + (_e198 * _e199))) * _e203); + let _e199 = (*sa); + let _e203 = p[0u]; + let _e204 = (*sa); + let _e207 = p[1u]; + let _e208 = (*ca); + let _e212 = (*radius); + return (vec2(((_e194 * _e195) - (_e198 * _e199)), ((_e203 * _e204) + (_e207 * _e208))) * _e212); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_2: vec2; + var local_4: vec2; var atlas: vec2; - let _e185 = point_shadow.params[0u]; - inset = _e185; - let _e186 = (*uv); - let _e187 = (*offset); - let _e189 = inset; - let _e190 = inset; - local_2 = clamp((_e186 + _e187), vec2(_e189), vec2((1f - _e190))); - let _e195 = local_2; - let _e196 = (*tile); - atlas = ((_e195 + _e196) * vec2(0.16666667f, 0.16666667f)); - let _e201 = point_shadow.params3_[0u]; - if (_e201 > 0.5f) { - let _e204 = atlas[1u]; - atlas[1u] = (1f - _e204); - } - let _e207 = atlas; - let _e208 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e207, 0f); - let _e210 = atlas; - let _e211 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e210, 0f); - let _e214 = (*range); - return (min(_e208.x, _e211.x) * _e214); + let _e194 = point_shadow.params[0u]; + inset = _e194; + let _e195 = (*uv); + let _e196 = (*offset); + let _e198 = inset; + let _e199 = inset; + local_4 = clamp((_e195 + _e196), vec2(_e198), vec2((1f - _e199))); + let _e204 = local_4; + let _e205 = (*tile); + atlas = ((_e204 + _e205) * vec2(0.16666667f, 0.16666667f)); + let _e210 = point_shadow.params3_[0u]; + if (_e210 > 0.5f) { + let _e213 = atlas[1u]; + atlas[1u] = (1f - _e213); + } + let _e216 = atlas; + let _e217 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e216, 0f); + let _e219 = atlas; + let _e220 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e219, 0f); + let _e223 = (*range); + return (min(_e217.x, _e220.x) * _e223); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { var stored: f32; - var param_6: vec2; - var param_7: vec2; - var param_8: vec2; - var param_9: f32; + var param_12: vec2; + var param_13: vec2; + var param_14: vec2; + var param_15: f32; - let _e186 = (*uv_1); - param_6 = _e186; - let _e187 = (*offset_1); - param_7 = _e187; - let _e188 = (*tile_1); - param_8 = _e188; - let _e189 = (*range_1); - param_9 = _e189; - let _e190 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); - stored = _e190; - let _e191 = stored; - let _e192 = (*range_1); - if (_e191 >= (_e192 * 0.999f)) { + let _e195 = (*uv_1); + param_12 = _e195; + let _e196 = (*offset_1); + param_13 = _e196; + let _e197 = (*tile_1); + param_14 = _e197; + let _e198 = (*range_1); + param_15 = _e198; + let _e199 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_12), (¶m_13), (¶m_14), (¶m_15)); + stored = _e199; + let _e200 = stored; + let _e201 = (*range_1); + if (_e200 >= (_e201 * 0.999f)) { return 1f; } - let _e195 = (*receiver); - let _e196 = stored; - return select(1f, 0f, (_e195 > _e196)); + let _e204 = (*receiver); + let _e205 = stored; + return select(1f, 0f, (_e204 > _e205)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { var lightRadius: f32; var minRadius: f32; var maxRadius: f32; - var param_10: vec2; - var param_11: vec2; - var param_12: vec2; - var param_13: f32; + var param_16: vec2; + var param_17: vec2; + var param_18: vec2; + var param_19: f32; var sum: f32; var count: f32; var i_2: i32; var stored_1: f32; - var param_14: i32; - var param_15: f32; - var param_16: f32; - var param_17: f32; - var param_18: vec2; - var param_19: vec2; - var param_20: vec2; + var param_20: i32; var param_21: f32; - var blocker: f32; - var world: f32; + var param_22: f32; + var param_23: f32; + var param_24: vec2; + var param_25: vec2; + var param_26: vec2; + var param_27: f32; + var blocker: f32; + var world: f32; (*blockerOut) = -1f; - let _e207 = point_shadow.params2_[1u]; - lightRadius = _e207; - let _e210 = point_shadow.params2_[0u]; - minRadius = _e210; - let _e213 = point_shadow.params2_[2u]; - maxRadius = _e213; - let _e214 = lightRadius; - if (_e214 <= 0f) { - let _e216 = (*uv_2); - param_10 = _e216; - param_11 = vec2(0f, 0f); - let _e217 = (*tile_2); - param_12 = _e217; - let _e218 = (*range_2); - param_13 = _e218; - let _e219 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); - (*blockerOut) = _e219; - let _e220 = minRadius; - return _e220; + let _e216 = point_shadow.params2_[1u]; + lightRadius = _e216; + let _e219 = point_shadow.params2_[0u]; + minRadius = _e219; + let _e222 = point_shadow.params2_[2u]; + maxRadius = _e222; + let _e223 = lightRadius; + if (_e223 <= 0f) { + let _e225 = (*uv_2); + param_16 = _e225; + param_17 = vec2(0f, 0f); + let _e226 = (*tile_2); + param_18 = _e226; + let _e227 = (*range_2); + param_19 = _e227; + let _e228 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_16), (¶m_17), (¶m_18), (¶m_19)); + (*blockerOut) = _e228; + let _e229 = minRadius; + return _e229; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e221 = i_2; - if (_e221 < 8i) { - let _e223 = i_2; - param_14 = _e223; - let _e224 = (*ca_1); - param_15 = _e224; - let _e225 = (*sa_1); - param_16 = _e225; - let _e226 = maxRadius; - param_17 = _e226; - let _e227 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); - let _e228 = (*uv_2); - param_18 = _e228; - param_19 = _e227; - let _e229 = (*tile_2); - param_20 = _e229; - let _e230 = (*range_2); - param_21 = _e230; - let _e231 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); - stored_1 = _e231; - let _e232 = stored_1; - let _e233 = (*range_2); - if (_e232 >= (_e233 * 0.999f)) { + let _e230 = i_2; + if (_e230 < 8i) { + let _e232 = i_2; + param_20 = _e232; + let _e233 = (*ca_1); + param_21 = _e233; + let _e234 = (*sa_1); + param_22 = _e234; + let _e235 = maxRadius; + param_23 = _e235; + let _e236 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_20), (¶m_21), (¶m_22), (¶m_23)); + let _e237 = (*uv_2); + param_24 = _e237; + param_25 = _e236; + let _e238 = (*tile_2); + param_26 = _e238; + let _e239 = (*range_2); + param_27 = _e239; + let _e240 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_24), (¶m_25), (¶m_26), (¶m_27)); + stored_1 = _e240; + let _e241 = stored_1; + let _e242 = (*range_2); + if (_e241 >= (_e242 * 0.999f)) { continue; } - let _e236 = stored_1; - let _e237 = (*receiver_1); - if (_e236 >= _e237) { + let _e245 = stored_1; + let _e246 = (*receiver_1); + if (_e245 >= _e246) { continue; } - let _e239 = stored_1; - let _e240 = sum; - sum = (_e240 + _e239); - let _e242 = count; - count = (_e242 + 1f); + let _e248 = stored_1; + let _e249 = sum; + sum = (_e249 + _e248); + let _e251 = count; + count = (_e251 + 1f); continue; } else { break; } continuing { - let _e244 = i_2; - i_2 = (_e244 + 1i); + let _e253 = i_2; + i_2 = (_e253 + 1i); } } - let _e246 = count; - if (_e246 < 0.5f) { + let _e255 = count; + if (_e255 < 0.5f) { return -1f; } - let _e248 = sum; - let _e249 = count; - blocker = max((_e248 / _e249), 0.0001f); - let _e252 = blocker; - (*blockerOut) = _e252; - let _e253 = lightRadius; - let _e254 = (*receiver_1); - let _e255 = blocker; - let _e259 = blocker; - world = ((_e253 * max((_e254 - _e255), 0f)) / _e259); - let _e261 = world; - let _e262 = (*receiver_1); - let _e264 = (*tanHalf); - let _e267 = minRadius; - let _e268 = maxRadius; - return clamp((_e261 / ((2f * _e262) * _e264)), _e267, _e268); + let _e257 = sum; + let _e258 = count; + blocker = max((_e257 / _e258), 0.0001f); + let _e261 = blocker; + (*blockerOut) = _e261; + let _e262 = lightRadius; + let _e263 = (*receiver_1); + let _e264 = blocker; + let _e268 = blocker; + world = ((_e262 * max((_e263 - _e264), 0f)) / _e268); + let _e270 = world; + let _e271 = (*receiver_1); + let _e273 = (*tanHalf); + let _e276 = minRadius; + let _e277 = maxRadius; + return clamp((_e270 / ((2f * _e271) * _e273)), _e276, _e277); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_3: vec2; + var local_5: vec2; - let _e178 = (*rows); - if (_e178 > 0f) { - let _e181 = gl_FragCoord_1[0u]; - let _e182 = (*rows); - let _e184 = gl_FragCoord_1[1u]; - local_3 = vec2(_e181, (_e182 - _e184)); + let _e187 = (*rows); + if (_e187 > 0f) { + let _e190 = gl_FragCoord_1[0u]; + let _e191 = (*rows); + let _e193 = gl_FragCoord_1[1u]; + local_5 = vec2(_e190, (_e191 - _e193)); } else { - let _e187 = gl_FragCoord_1; - local_3 = _e187.xy; + let _e196 = gl_FragCoord_1; + local_5 = _e196.xy; } - let _e189 = local_3; - return _e189; + let _e198 = local_5; + return _e198; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -9420,292 +9870,292 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var receiver_2: f32; var noise: f32; - var param_22: f32; + var param_28: f32; var angle: f32; var ca_2: f32; var sa_2: f32; var tanHalf_1: f32; var blocker_1: f32; var radius_1: f32; - var param_23: vec2; - var param_24: vec2; - var param_25: f32; - var param_26: f32; - var param_27: f32; - var param_28: f32; - var param_29: f32; - var param_30: f32; - var local_4: vec3; - var lit: f32; - var param_31: vec2; - var param_32: vec2; - var param_33: vec2; + var param_29: vec2; + var param_30: vec2; + var param_31: f32; + var param_32: f32; + var param_33: f32; var param_34: f32; var param_35: f32; + var param_36: f32; + var local_6: vec3; + var lit_1: f32; + var param_37: vec2; + var param_38: vec2; + var param_39: vec2; + var param_40: f32; + var param_41: f32; var i_3: i32; - var param_36: i32; - var param_37: f32; - var param_38: f32; - var param_39: f32; - var param_40: vec2; - var param_41: vec2; - var param_42: vec2; + var param_42: i32; var param_43: f32; var param_44: f32; - var phi_2184_: bool; - var phi_2245_: bool; - - let _e230 = (*lightIndex); - let _e234 = point_shadow.slots[_e230][0u]; - slot = i32((_e234 + 0.5f)); - let _e237 = (*lightIndex); - let _e241 = point_shadow.slots[_e237][0u]; - if (_e241 < 0f) { + var param_45: f32; + var param_46: vec2; + var param_47: vec2; + var param_48: vec2; + var param_49: f32; + var param_50: f32; + var phi_2193_: bool; + var phi_2254_: bool; + + let _e239 = (*lightIndex); + let _e243 = point_shadow.slots[_e239][0u]; + slot = i32((_e243 + 0.5f)); + let _e246 = (*lightIndex); + let _e250 = point_shadow.slots[_e246][0u]; + if (_e250 < 0f) { return 1f; } - let _e245 = point_shadow.params[2u]; - strength = _e245; - let _e246 = strength; - if (_e246 <= 0f) { + let _e254 = point_shadow.params[2u]; + strength = _e254; + let _e255 = strength; + if (_e255 <= 0f) { return 1f; } - let _e248 = slot; - let _e251 = point_shadow.lights[_e248]; - let _e253 = (*world_1); - toLight = (_e251.xyz - _e253); - let _e255 = toLight; - toLightLength = max(length(_e255), 0.000001f); - let _e258 = (*normal); - let _e259 = toLight; - let _e260 = toLightLength; - nDotL = max(dot(_e258, (_e259 / vec3(_e260))), 0.15f); - let _e265 = nDotL; - let _e266 = nDotL; - let _e271 = nDotL; - let _e272 = nDotL; - slope = min((sqrt(max((1f - (_e265 * _e266)), 0f)) / (_e271 * _e272)), 8f); - let _e276 = toLightLength; - let _e278 = (*lightIndex); - let _e282 = point_shadow.slots[_e278][2u]; - let _e287 = point_shadow.params3_[1u]; - texel = (((2f * _e276) * max(_e282, 0.0001f)) * _e287); - let _e289 = (*world_1); - let _e290 = (*normal); - let _e291 = texel; - let _e295 = point_shadow.params[3u]; - let _e297 = slope; - origin = (_e289 + (((_e290 * _e291) * _e295) * (1f + _e297))); - let _e301 = origin; - let _e302 = slot; - let _e305 = point_shadow.lights[_e302]; - toFragment = (_e301 - _e305.xyz); - let _e308 = toFragment; - distance_ = length(_e308); - let _e310 = slot; - let _e314 = point_shadow.lights[_e310][3u]; - range_3 = max(_e314, 0.0001f); - let _e316 = distance_; - let _e317 = range_3; - if (_e316 >= _e317) { + let _e257 = slot; + let _e260 = point_shadow.lights[_e257]; + let _e262 = (*world_1); + toLight = (_e260.xyz - _e262); + let _e264 = toLight; + toLightLength = max(length(_e264), 0.000001f); + let _e267 = (*normal); + let _e268 = toLight; + let _e269 = toLightLength; + nDotL = max(dot(_e267, (_e268 / vec3(_e269))), 0.15f); + let _e274 = nDotL; + let _e275 = nDotL; + let _e280 = nDotL; + let _e281 = nDotL; + slope = min((sqrt(max((1f - (_e274 * _e275)), 0f)) / (_e280 * _e281)), 8f); + let _e285 = toLightLength; + let _e287 = (*lightIndex); + let _e291 = point_shadow.slots[_e287][2u]; + let _e296 = point_shadow.params3_[1u]; + texel = (((2f * _e285) * max(_e291, 0.0001f)) * _e296); + let _e298 = (*world_1); + let _e299 = (*normal); + let _e300 = texel; + let _e304 = point_shadow.params[3u]; + let _e306 = slope; + origin = (_e298 + (((_e299 * _e300) * _e304) * (1f + _e306))); + let _e310 = origin; + let _e311 = slot; + let _e314 = point_shadow.lights[_e311]; + toFragment = (_e310 - _e314.xyz); + let _e317 = toFragment; + distance_ = length(_e317); + let _e319 = slot; + let _e323 = point_shadow.lights[_e319][3u]; + range_3 = max(_e323, 0.0001f); + let _e325 = distance_; + let _e326 = range_3; + if (_e325 >= _e326) { return 1f; } face = 0i; - let _e319 = (*lightIndex); - let _e323 = point_shadow.slots[_e319][1u]; - if (_e323 < 0.5f) { - let _e325 = toFragment; - a = abs(_e325); - let _e328 = a[0u]; - let _e330 = a[1u]; - let _e331 = (_e328 >= _e330); - phi_2184_ = _e331; - if _e331 { - let _e333 = a[0u]; - let _e335 = a[2u]; - phi_2184_ = (_e333 >= _e335); - } - let _e338 = phi_2184_; - if _e338 { - let _e340 = toFragment[0u]; - face = select(1i, 0i, (_e340 > 0f)); + let _e328 = (*lightIndex); + let _e332 = point_shadow.slots[_e328][1u]; + if (_e332 < 0.5f) { + let _e334 = toFragment; + a = abs(_e334); + let _e337 = a[0u]; + let _e339 = a[1u]; + let _e340 = (_e337 >= _e339); + phi_2193_ = _e340; + if _e340 { + let _e342 = a[0u]; + let _e344 = a[2u]; + phi_2193_ = (_e342 >= _e344); + } + let _e347 = phi_2193_; + if _e347 { + let _e349 = toFragment[0u]; + face = select(1i, 0i, (_e349 > 0f)); } else { - let _e344 = a[1u]; - let _e346 = a[2u]; - if (_e344 >= _e346) { - let _e349 = toFragment[1u]; - face = select(3i, 2i, (_e349 > 0f)); + let _e353 = a[1u]; + let _e355 = a[2u]; + if (_e353 >= _e355) { + let _e358 = toFragment[1u]; + face = select(3i, 2i, (_e358 > 0f)); } else { - let _e353 = toFragment[2u]; - face = select(5i, 4i, (_e353 > 0f)); + let _e362 = toFragment[2u]; + face = select(5i, 4i, (_e362 > 0f)); } } } - let _e356 = slot; - let _e358 = face; - let _e362 = point_shadow.faces[((_e356 * 6i) + _e358)]; - let _e363 = origin; - clip = (_e362 * vec4(_e363.x, _e363.y, _e363.z, 1f)); - let _e370 = clip[3u]; - if (_e370 <= 0f) { + let _e365 = slot; + let _e367 = face; + let _e371 = point_shadow.faces[((_e365 * 6i) + _e367)]; + let _e372 = origin; + clip = (_e371 * vec4(_e372.x, _e372.y, _e372.z, 1f)); + let _e379 = clip[3u]; + if (_e379 <= 0f) { return 1f; } - let _e372 = clip; - let _e375 = clip[3u]; - ndc = (_e372.xy / vec2(_e375)); - let _e379 = ndc[0u]; - let _e381 = (abs(_e379) > 1f); - phi_2245_ = _e381; - if !(_e381) { - let _e384 = ndc[1u]; - phi_2245_ = (abs(_e384) > 1f); - } - let _e388 = phi_2245_; - if _e388 { + let _e381 = clip; + let _e384 = clip[3u]; + ndc = (_e381.xy / vec2(_e384)); + let _e388 = ndc[0u]; + let _e390 = (abs(_e388) > 1f); + phi_2254_ = _e390; + if !(_e390) { + let _e393 = ndc[1u]; + phi_2254_ = (abs(_e393) > 1f); + } + let _e397 = phi_2254_; + if _e397 { return 1f; } - let _e390 = ndc[0u]; - let _e394 = ndc[1u]; - uv_3 = vec2(((_e390 * 0.5f) + 0.5f), (0.5f - (_e394 * 0.5f))); - let _e398 = face; - let _e400 = slot; - tile_3 = vec2(f32(_e398), f32(_e400)); - let _e403 = distance_; - let _e406 = point_shadow.params[1u]; - receiver_2 = (_e403 - _e406); - let _e410 = frag_info.target_origin[0u]; - param_22 = _e410; - let _e411 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); - noise = fract((52.982918f * fract(dot(_e411, vec2(0.06711056f, 0.00583715f))))); - let _e416 = noise; - angle = (_e416 * 6.2831855f); - let _e418 = angle; - ca_2 = cos(_e418); - let _e420 = angle; - sa_2 = sin(_e420); - let _e422 = (*lightIndex); - let _e426 = point_shadow.slots[_e422][2u]; - tanHalf_1 = max(_e426, 0.0001f); + let _e399 = ndc[0u]; + let _e403 = ndc[1u]; + uv_3 = vec2(((_e399 * 0.5f) + 0.5f), (0.5f - (_e403 * 0.5f))); + let _e407 = face; + let _e409 = slot; + tile_3 = vec2(f32(_e407), f32(_e409)); + let _e412 = distance_; + let _e415 = point_shadow.params[1u]; + receiver_2 = (_e412 - _e415); + let _e419 = frag_info.target_origin[0u]; + param_28 = _e419; + let _e420 = FragCoordFromTop_u0028_f1_u003b((¶m_28)); + noise = fract((52.982918f * fract(dot(_e420, vec2(0.06711056f, 0.00583715f))))); + let _e425 = noise; + angle = (_e425 * 6.2831855f); + let _e427 = angle; + ca_2 = cos(_e427); + let _e429 = angle; + sa_2 = sin(_e429); + let _e431 = (*lightIndex); + let _e435 = point_shadow.slots[_e431][2u]; + tanHalf_1 = max(_e435, 0.0001f); blocker_1 = -1f; - let _e428 = uv_3; - param_23 = _e428; - let _e429 = tile_3; - param_24 = _e429; - let _e430 = range_3; - param_25 = _e430; - let _e431 = receiver_2; - param_26 = _e431; - let _e432 = ca_2; - param_27 = _e432; - let _e433 = sa_2; - param_28 = _e433; - let _e434 = tanHalf_1; - param_29 = _e434; - let _e435 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e436 = param_30; - blocker_1 = _e436; - radius_1 = _e435; - let _e439 = point_shadow.params2_[3u]; - if (_e439 > 0.5f) { + let _e437 = uv_3; + param_29 = _e437; + let _e438 = tile_3; + param_30 = _e438; + let _e439 = range_3; + param_31 = _e439; + let _e440 = receiver_2; + param_32 = _e440; + let _e441 = ca_2; + param_33 = _e441; + let _e442 = sa_2; + param_34 = _e442; + let _e443 = tanHalf_1; + param_35 = _e443; + let _e444 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_29), (¶m_30), (¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35), (¶m_36)); + let _e445 = param_36; + blocker_1 = _e445; + radius_1 = _e444; + let _e448 = point_shadow.params2_[3u]; + if (_e448 > 0.5f) { g_debug_surface_on = true; - let _e441 = radius_1; - if (_e441 < 0f) { - local_4 = vec3(0f, 0f, 1f); + let _e450 = radius_1; + if (_e450 < 0f) { + local_6 = vec3(0f, 0f, 1f); } else { - let _e443 = radius_1; - let _e446 = point_shadow.params2_[2u]; - let _e450 = blocker_1; - let _e451 = range_3; - local_4 = vec3(clamp((_e443 / max(_e446, 0.000001f)), 0f, 1f), clamp((_e450 / _e451), 0f, 1f), 0f); + let _e452 = radius_1; + let _e455 = point_shadow.params2_[2u]; + let _e459 = blocker_1; + let _e460 = range_3; + local_6 = vec3(clamp((_e452 / max(_e455, 0.000001f)), 0f, 1f), clamp((_e459 / _e460), 0f, 1f), 0f); } - let _e455 = local_4; - g_debug_surface = _e455; + let _e464 = local_6; + g_debug_surface = _e464; } - let _e456 = radius_1; - if (_e456 < 0f) { + let _e465 = radius_1; + if (_e465 < 0f) { return 1f; } - let _e458 = uv_3; - param_31 = _e458; - param_32 = vec2(0f, 0f); - let _e459 = tile_3; - param_33 = _e459; - let _e460 = range_3; - param_34 = _e460; - let _e461 = receiver_2; - param_35 = _e461; - let _e462 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); - lit = _e462; - let _e463 = radius_1; - if (_e463 > 0f) { + let _e467 = uv_3; + param_37 = _e467; + param_38 = vec2(0f, 0f); + let _e468 = tile_3; + param_39 = _e468; + let _e469 = range_3; + param_40 = _e469; + let _e470 = receiver_2; + param_41 = _e470; + let _e471 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_37), (¶m_38), (¶m_39), (¶m_40), (¶m_41)); + lit_1 = _e471; + let _e472 = radius_1; + if (_e472 > 0f) { i_3 = 0i; loop { - let _e465 = i_3; - if (_e465 < 8i) { - let _e467 = i_3; - param_36 = _e467; - let _e468 = ca_2; - param_37 = _e468; - let _e469 = sa_2; - param_38 = _e469; - let _e470 = radius_1; - param_39 = _e470; - let _e471 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - let _e472 = uv_3; - param_40 = _e472; - param_41 = _e471; - let _e473 = tile_3; - param_42 = _e473; - let _e474 = range_3; - param_43 = _e474; - let _e475 = receiver_2; - param_44 = _e475; - let _e476 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); - let _e477 = lit; - lit = (_e477 + _e476); + let _e474 = i_3; + if (_e474 < 8i) { + let _e476 = i_3; + param_42 = _e476; + let _e477 = ca_2; + param_43 = _e477; + let _e478 = sa_2; + param_44 = _e478; + let _e479 = radius_1; + param_45 = _e479; + let _e480 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_42), (¶m_43), (¶m_44), (¶m_45)); + let _e481 = uv_3; + param_46 = _e481; + param_47 = _e480; + let _e482 = tile_3; + param_48 = _e482; + let _e483 = range_3; + param_49 = _e483; + let _e484 = receiver_2; + param_50 = _e484; + let _e485 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_46), (¶m_47), (¶m_48), (¶m_49), (¶m_50)); + let _e486 = lit_1; + lit_1 = (_e486 + _e485); continue; } else { break; } continuing { - let _e479 = i_3; - i_3 = (_e479 + 1i); + let _e488 = i_3; + i_3 = (_e488 + 1i); } } - let _e481 = lit; - lit = (_e481 * 0.11111111f); + let _e490 = lit_1; + lit_1 = (_e490 * 0.11111111f); } - let _e483 = lit; - let _e484 = strength; - return mix(1f, _e483, clamp(_e484, 0f, 1f)); + let _e492 = lit_1; + let _e493 = strength; + return mix(1f, _e492, clamp(_e493, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r: f32; var theta: f32; - let _e181 = (*i_4); - let _e184 = (*n_1); - r = sqrt(((f32(_e181) + 0.5f) / f32(_e184))); - let _e188 = (*i_4); - let _e191 = (*turn); - theta = ((f32(_e188) * 2.3999631f) + _e191); - let _e193 = r; - let _e194 = theta; - let _e196 = theta; - return (vec2(cos(_e194), sin(_e196)) * _e193); + let _e190 = (*i_4); + let _e193 = (*n_1); + r = sqrt(((f32(_e190) + 0.5f) / f32(_e193))); + let _e197 = (*i_4); + let _e200 = (*turn); + theta = ((f32(_e197) * 2.3999631f) + _e200); + let _e202 = r; + let _e203 = theta; + let _e205 = theta; + return (vec2(cos(_e203), sin(_e205)) * _e202); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; - var param_45: f32; + var param_51: f32; - let _e180 = frag_info.target_origin[0u]; - param_45 = _e180; - let _e181 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); - let _e184 = frag_info.target_origin[3u]; - at = (_e181 + vec2((5.588238f * max(_e184, 0f)))); - let _e189 = at; - return fract((52.982918f * fract(dot(_e189, vec2(0.06711056f, 0.00583715f))))); + let _e189 = frag_info.target_origin[0u]; + param_51 = _e189; + let _e190 = FragCoordFromTop_u0028_f1_u003b((¶m_51)); + let _e193 = frag_info.target_origin[3u]; + at = (_e190 + vec2((5.588238f * max(_e193, 0f)))); + let _e198 = at; + return fract((52.982918f * fract(dot(_e198, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -9714,88 +10164,88 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_2: f32; - let _e178 = (*depth); - d_2 = ((2f * clamp(_e178, 0f, 1f)) - 1f); - let _e182 = d_2; - let _e185 = d_2; - return vec2(exp((40f * _e182)), -(exp((-5f * _e185)))); + let _e187 = (*depth); + d_2 = ((2f * clamp(_e187, 0f, 1f)) - 1f); + let _e191 = d_2; + let _e194 = d_2; + return vec2(exp((40f * _e191)), -(exp((-5f * _e194)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { var warped: vec2; - var param_46: f32; + var param_52: f32; var scale: vec2; var positive: f32; - var param_47: vec2; - var param_48: f32; - var param_49: f32; - var param_50: f32; - var negative: f32; - var param_51: vec2; - var param_52: f32; - var param_53: f32; + var param_53: vec2; var param_54: f32; + var param_55: f32; + var param_56: f32; + var negative: f32; + var param_57: vec2; + var param_58: f32; + var param_59: f32; + var param_60: f32; - let _e192 = (*depth_1); - param_46 = _e192; - let _e193 = EvsmWarp_u0028_f1_u003b((¶m_46)); - warped = _e193; - let _e194 = warped; - scale = (vec2(0.004f, 0.0005f) * _e194); - let _e197 = scale[0u]; - let _e199 = scale[0u]; - let _e201 = (*moments_1); - param_47 = _e201.xy; - let _e204 = warped[0u]; - param_48 = _e204; - param_49 = (_e197 * _e199); - let _e205 = (*bleed_1); - param_50 = _e205; - let _e206 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); - positive = _e206; - let _e208 = scale[1u]; - let _e210 = scale[1u]; - let _e212 = (*moments_1); - param_51 = _e212.zw; - let _e215 = warped[1u]; - param_52 = _e215; - param_53 = (_e208 * _e210); - let _e216 = (*bleed_1); - param_54 = _e216; - let _e217 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); - negative = _e217; - let _e218 = positive; - let _e219 = negative; - return min(_e218, _e219); -} - -fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_1: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { + let _e201 = (*depth_1); + param_52 = _e201; + let _e202 = EvsmWarp_u0028_f1_u003b((¶m_52)); + warped = _e202; + let _e203 = warped; + scale = (vec2(0.004f, 0.0005f) * _e203); + let _e206 = scale[0u]; + let _e208 = scale[0u]; + let _e210 = (*moments_1); + param_53 = _e210.xy; + let _e213 = warped[0u]; + param_54 = _e213; + param_55 = (_e206 * _e208); + let _e214 = (*bleed_1); + param_56 = _e214; + let _e215 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_53), (¶m_54), (¶m_55), (¶m_56)); + positive = _e215; + let _e217 = scale[1u]; + let _e219 = scale[1u]; + let _e221 = (*moments_1); + param_57 = _e221.zw; + let _e224 = warped[1u]; + param_58 = _e224; + param_59 = (_e217 * _e219); + let _e225 = (*bleed_1); + param_60 = _e225; + let _e226 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_57), (¶m_58), (¶m_59), (¶m_60)); + negative = _e226; + let _e227 = positive; + let _e228 = negative; + return min(_e227, _e228); +} + +fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { var strength_1: f32; var cascadeCount: i32; var viewDistance: f32; @@ -9809,8 +10259,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_5: mat4x4; - var local_6: mat4x4; + var local_7: mat4x4; + var local_8: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -9824,17 +10274,17 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_7: f32; - var local_8: f32; + var local_9: f32; + var local_10: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; var softness: f32; - var lit_1: f32; + var lit_2: f32; var moments_2: vec4; - var param_55: vec4; - var param_56: f32; - var param_57: f32; + var param_61: vec4; + var param_62: f32; + var param_63: f32; var y: i32; var x: i32; var occluder: f32; @@ -9845,65 +10295,65 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var blockerCount: f32; var i_5: i32; var disc: vec2; - var param_58: i32; - var param_59: i32; - var param_60: f32; + var param_64: i32; + var param_65: i32; + var param_66: f32; var tapBias: f32; var occluder_1: f32; var gap: f32; var radius_2: f32; var i_6: i32; var disc_1: vec2; - var param_61: i32; - var param_62: i32; - var param_63: f32; + var param_67: i32; + var param_68: i32; + var param_69: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3303_: bool; - var phi_3314_: bool; - var phi_3475_: bool; - var phi_3482_: bool; - var phi_3489_: bool; - - let _e244 = frag_info.shadow_params[3u]; - strength_1 = _e244; - let _e245 = strength_1; - if (_e245 <= 0f) { + var phi_3473_: bool; + var phi_3484_: bool; + var phi_3645_: bool; + var phi_3652_: bool; + var phi_3659_: bool; + + let _e253 = frag_info.shadow_params[3u]; + strength_1 = _e253; + let _e254 = strength_1; + if (_e254 <= 0f) { return 1f; } - let _e247 = (*lightIndex_1); - let _e250 = frag_info.frame_params[2u]; - if (_e247 != i32((_e250 + 0.5f))) { + let _e256 = (*lightIndex_1); + let _e259 = frag_info.frame_params[2u]; + if (_e256 != i32((_e259 + 0.5f))) { return 1f; } - let _e256 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e256 + 0.5f)); - let _e259 = v_world_position_1; - let _e261 = frag_info.camera_position; - viewDistance = length((_e259 - _e261.xyz)); + let _e265 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e265 + 0.5f)); + let _e268 = v_world_position_1; + let _e270 = frag_info.camera_position; + viewDistance = length((_e268 - _e270.xyz)); cascade = 0i; - let _e265 = cascadeCount; - let _e266 = (_e265 > 1i); - phi_3303_ = _e266; - if _e266 { - let _e267 = viewDistance; - let _e270 = frag_info.shadow_cascades[0u]; - phi_3303_ = (_e267 > _e270); - } - let _e273 = phi_3303_; - if _e273 { - cascade = 1i; - } let _e274 = cascadeCount; - let _e275 = (_e274 > 2i); - phi_3314_ = _e275; + let _e275 = (_e274 > 1i); + phi_3473_ = _e275; if _e275 { let _e276 = viewDistance; - let _e279 = frag_info.shadow_cascades[1u]; - phi_3314_ = (_e276 > _e279); + let _e279 = frag_info.shadow_cascades[0u]; + phi_3473_ = (_e276 > _e279); } - let _e282 = phi_3314_; + let _e282 = phi_3473_; if _e282 { + cascade = 1i; + } + let _e283 = cascadeCount; + let _e284 = (_e283 > 2i); + phi_3484_ = _e284; + if _e284 { + let _e285 = viewDistance; + let _e288 = frag_info.shadow_cascades[1u]; + phi_3484_ = (_e285 > _e288); + } + let _e291 = phi_3484_; + if _e291 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -9914,232 +10364,232 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e283 = attempt; - if (_e283 < 3i) { - let _e285 = cascade; - let _e286 = attempt; - which = (_e285 + _e286); - let _e288 = which; - let _e289 = cascadeCount; - if (_e288 >= _e289) { + let _e292 = attempt; + if (_e292 < 3i) { + let _e294 = cascade; + let _e295 = attempt; + which = (_e294 + _e295); + let _e297 = which; + let _e298 = cascadeCount; + if (_e297 >= _e298) { break; } - let _e291 = which; - if (_e291 == 0i) { - let _e294 = frag_info.shadow_matrix; - local_5 = _e294; + let _e300 = which; + if (_e300 == 0i) { + let _e303 = frag_info.shadow_matrix; + local_7 = _e303; } else { - let _e295 = which; - if (_e295 == 1i) { - let _e298 = frag_info.shadow_matrix_far; - local_6 = _e298; + let _e304 = which; + if (_e304 == 1i) { + let _e307 = frag_info.shadow_matrix_far; + local_8 = _e307; } else { - let _e300 = frag_info.shadow_matrix_farthest; - local_6 = _e300; + let _e309 = frag_info.shadow_matrix_farthest; + local_8 = _e309; } - let _e301 = local_6; - local_5 = _e301; - } - let _e302 = local_5; - matrix = _e302; - let _e305 = matrix[0][0u]; - let _e308 = matrix[1][0u]; - let _e311 = matrix[2][0u]; - axisX = vec3(_e305, _e308, _e311); - let _e315 = matrix[0][1u]; - let _e318 = matrix[1][1u]; - let _e321 = matrix[2][1u]; - axisY = vec3(_e315, _e318, _e321); - let _e323 = axisX; - rowX = max(length(_e323), 0.000001f); - let _e326 = axisY; - rowY = max(length(_e326), 0.000001f); - let _e331 = frag_info.shadow_cascades[3u]; - let _e333 = rowX; - texelMetres = ((2f * _e331) / _e333); - let _e337 = frag_info.shadow_cascades[3u]; - let _e340 = (*s_1).n; - let _e341 = axisX; - let _e344 = rowX; - let _e345 = rowX; - let _e349 = (*s_1).n; - let _e350 = axisY; - let _e353 = rowY; - let _e354 = rowY; - reach = ((3f * _e337) * ((abs(dot(_e340, _e341)) / (_e344 * _e345)) + (abs(dot(_e349, _e350)) / (_e353 * _e354)))); - let _e361 = frag_info.shadow_params[2u]; - let _e362 = texelMetres; - flatOffset = min(_e361, _e362); - let _e364 = v_world_position_1; - let _e366 = (*s_1).n; - let _e367 = flatOffset; - let _e368 = reach; - origin_1 = (_e364 + (_e366 * (_e367 + _e368))); - let _e372 = matrix; - let _e373 = origin_1; - lightSpace = (_e372 * vec4(_e373.x, _e373.y, _e373.z, 1f)); - let _e380 = lightSpace[3u]; - if (_e380 <= 0f) { + let _e310 = local_8; + local_7 = _e310; + } + let _e311 = local_7; + matrix = _e311; + let _e314 = matrix[0][0u]; + let _e317 = matrix[1][0u]; + let _e320 = matrix[2][0u]; + axisX = vec3(_e314, _e317, _e320); + let _e324 = matrix[0][1u]; + let _e327 = matrix[1][1u]; + let _e330 = matrix[2][1u]; + axisY = vec3(_e324, _e327, _e330); + let _e332 = axisX; + rowX = max(length(_e332), 0.000001f); + let _e335 = axisY; + rowY = max(length(_e335), 0.000001f); + let _e340 = frag_info.shadow_cascades[3u]; + let _e342 = rowX; + texelMetres = ((2f * _e340) / _e342); + let _e346 = frag_info.shadow_cascades[3u]; + let _e349 = (*s_2).n; + let _e350 = axisX; + let _e353 = rowX; + let _e354 = rowX; + let _e358 = (*s_2).n; + let _e359 = axisY; + let _e362 = rowY; + let _e363 = rowY; + reach = ((3f * _e346) * ((abs(dot(_e349, _e350)) / (_e353 * _e354)) + (abs(dot(_e358, _e359)) / (_e362 * _e363)))); + let _e370 = frag_info.shadow_params[2u]; + let _e371 = texelMetres; + flatOffset = min(_e370, _e371); + let _e373 = v_world_position_1; + let _e375 = (*s_2).n; + let _e376 = flatOffset; + let _e377 = reach; + origin_1 = (_e373 + (_e375 * (_e376 + _e377))); + let _e381 = matrix; + let _e382 = origin_1; + lightSpace = (_e381 * vec4(_e382.x, _e382.y, _e382.z, 1f)); + let _e389 = lightSpace[3u]; + if (_e389 <= 0f) { continue; } - let _e382 = lightSpace; - let _e385 = lightSpace[3u]; - candidate = (_e382.xyz / vec3(_e385)); - let _e389 = candidate[0u]; - let _e393 = candidate[1u]; - inTile = vec2(((_e389 * 0.5f) + 0.5f), (0.5f - (_e393 * 0.5f))); - let _e398 = inTile[0u]; - let _e399 = (_e398 < 0f); - phi_3475_ = _e399; - if !(_e399) { - let _e402 = inTile[0u]; - phi_3475_ = (_e402 > 1f); - } - let _e405 = phi_3475_; - phi_3482_ = _e405; - if !(_e405) { - let _e408 = inTile[1u]; - phi_3482_ = (_e408 < 0f); - } - let _e411 = phi_3482_; - phi_3489_ = _e411; - if !(_e411) { - let _e414 = inTile[1u]; - phi_3489_ = (_e414 > 1f); - } - let _e417 = phi_3489_; - if _e417 { + let _e391 = lightSpace; + let _e394 = lightSpace[3u]; + candidate = (_e391.xyz / vec3(_e394)); + let _e398 = candidate[0u]; + let _e402 = candidate[1u]; + inTile = vec2(((_e398 * 0.5f) + 0.5f), (0.5f - (_e402 * 0.5f))); + let _e407 = inTile[0u]; + let _e408 = (_e407 < 0f); + phi_3645_ = _e408; + if !(_e408) { + let _e411 = inTile[0u]; + phi_3645_ = (_e411 > 1f); + } + let _e414 = phi_3645_; + phi_3652_ = _e414; + if !(_e414) { + let _e417 = inTile[1u]; + phi_3652_ = (_e417 < 0f); + } + let _e420 = phi_3652_; + phi_3659_ = _e420; + if !(_e420) { + let _e423 = inTile[1u]; + phi_3659_ = (_e423 > 1f); + } + let _e426 = phi_3659_; + if _e426 { continue; } - let _e419 = candidate[2u]; - if (_e419 > 1f) { - let _e421 = which; - let _e422 = cascadeCount; - if (_e421 < (_e422 - 1i)) { + let _e428 = candidate[2u]; + if (_e428 > 1f) { + let _e430 = which; + let _e431 = cascadeCount; + if (_e430 < (_e431 - 1i)) { continue; } candidate[2u] = 1f; } - let _e427 = inTile[0u]; - let _e428 = which; - let _e431 = cascadeCount; - let _e435 = inTile[1u]; - uv_4 = vec2(((_e427 + f32(_e428)) / f32(_e431)), _e435); - let _e437 = candidate; - projected = _e437; - let _e438 = which; - cascade = _e438; - let _e439 = texelMetres; - cascadeTexel = _e439; - let _e442 = matrix[0][2u]; - let _e445 = matrix[1][2u]; - let _e448 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e442, _e445, _e448)), 0.000001f)); - let _e454 = (*s_1).n; - let _e457 = matrix[0][2u]; - let _e460 = matrix[1][2u]; - let _e463 = matrix[2][2u]; - let _e467 = cascadeDepth; - cosSlope = clamp((abs(dot(_e454, vec3(_e457, _e460, _e463))) * _e467), 0.1f, 1f); - let _e470 = texelMetres; - let _e471 = cosSlope; - let _e472 = cosSlope; - let _e478 = cosSlope; - let _e480 = cascadeDepth; - receiverSlope = (((_e470 * sqrt(max((1f - (_e471 * _e472)), 0f))) / _e478) / _e480); + let _e436 = inTile[0u]; + let _e437 = which; + let _e440 = cascadeCount; + let _e444 = inTile[1u]; + uv_4 = vec2(((_e436 + f32(_e437)) / f32(_e440)), _e444); + let _e446 = candidate; + projected = _e446; + let _e447 = which; + cascade = _e447; + let _e448 = texelMetres; + cascadeTexel = _e448; + let _e451 = matrix[0][2u]; + let _e454 = matrix[1][2u]; + let _e457 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e451, _e454, _e457)), 0.000001f)); + let _e463 = (*s_2).n; + let _e466 = matrix[0][2u]; + let _e469 = matrix[1][2u]; + let _e472 = matrix[2][2u]; + let _e476 = cascadeDepth; + cosSlope = clamp((abs(dot(_e463, vec3(_e466, _e469, _e472))) * _e476), 0.1f, 1f); + let _e479 = texelMetres; + let _e480 = cosSlope; + let _e481 = cosSlope; + let _e487 = cosSlope; + let _e489 = cascadeDepth; + receiverSlope = (((_e479 * sqrt(max((1f - (_e480 * _e481)), 0f))) / _e487) / _e489); found = true; break; } else { break; } continuing { - let _e482 = attempt; - attempt = (_e482 + 1i); + let _e491 = attempt; + attempt = (_e491 + 1i); } } - let _e484 = found; - if !(_e484) { + let _e493 = found; + if !(_e493) { return 1f; } - let _e486 = cascade; - if (_e486 == 0i) { - let _e490 = frag_info.shadow_bias[0u]; - local_7 = _e490; + let _e495 = cascade; + if (_e495 == 0i) { + let _e499 = frag_info.shadow_bias[0u]; + local_9 = _e499; } else { - let _e491 = cascade; - if (_e491 == 1i) { - let _e495 = frag_info.shadow_bias[1u]; - local_8 = _e495; + let _e500 = cascade; + if (_e500 == 1i) { + let _e504 = frag_info.shadow_bias[1u]; + local_10 = _e504; } else { - let _e498 = frag_info.shadow_bias[2u]; - local_8 = _e498; + let _e507 = frag_info.shadow_bias[2u]; + local_10 = _e507; } - let _e499 = local_8; - local_7 = _e499; + let _e508 = local_10; + local_9 = _e508; } - let _e500 = local_7; - bias = _e500; - let _e503 = frag_info.shadow_params[0u]; - let _e506 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e503, _e506); - let _e508 = cascade; - let _e510 = cascadeCount; - let _e514 = texel_1; - tileLo = (vec2((f32(_e508) / f32(_e510)), 0f) + (_e514 * 0.5f)); + let _e509 = local_9; + bias = _e509; + let _e512 = frag_info.shadow_params[0u]; + let _e515 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e512, _e515); let _e517 = cascade; - let _e520 = cascadeCount; - let _e524 = texel_1; - tileHi = (vec2((f32((_e517 + 1i)) / f32(_e520)), 1f) - (_e524 * 0.5f)); - let _e529 = frag_info.ambient_ground[3u]; - softness = _e529; - lit_1 = 0f; - let _e530 = softness; - if (_e530 < 0f) { - let _e532 = uv_4; - let _e533 = tileLo; - let _e534 = tileHi; - let _e536 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e532, _e533, _e534), 0f); - moments_2 = _e536; - let _e538 = projected[2u]; - let _e539 = bias; - let _e541 = softness; - let _e545 = moments_2; - param_55 = _e545; - param_56 = (_e538 - _e539); - param_57 = clamp((-(_e541) - 1f), 0f, 0.95f); - let _e546 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); - lit_1 = _e546; + let _e519 = cascadeCount; + let _e523 = texel_1; + tileLo = (vec2((f32(_e517) / f32(_e519)), 0f) + (_e523 * 0.5f)); + let _e526 = cascade; + let _e529 = cascadeCount; + let _e533 = texel_1; + tileHi = (vec2((f32((_e526 + 1i)) / f32(_e529)), 1f) - (_e533 * 0.5f)); + let _e538 = frag_info.ambient_ground[3u]; + softness = _e538; + lit_2 = 0f; + let _e539 = softness; + if (_e539 < 0f) { + let _e541 = uv_4; + let _e542 = tileLo; + let _e543 = tileHi; + let _e545 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e541, _e542, _e543), 0f); + moments_2 = _e545; + let _e547 = projected[2u]; + let _e548 = bias; + let _e550 = softness; + let _e554 = moments_2; + param_61 = _e554; + param_62 = (_e547 - _e548); + param_63 = clamp((-(_e550) - 1f), 0f, 0.95f); + let _e555 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); + lit_2 = _e555; } else { - let _e547 = softness; - if (_e547 <= 0f) { + let _e556 = softness; + if (_e556 <= 0f) { y = -1i; loop { - let _e549 = y; - if (_e549 <= 1i) { + let _e558 = y; + if (_e558 <= 1i) { x = -1i; loop { - let _e551 = x; - if (_e551 <= 1i) { - let _e553 = uv_4; - let _e554 = x; - let _e556 = y; - let _e559 = texel_1; - let _e562 = tileLo; - let _e563 = tileHi; - let _e565 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e553 + (vec2(f32(_e554), f32(_e556)) * _e559)), _e562, _e563), 0f); - occluder = _e565.x; - let _e568 = projected[2u]; - let _e569 = bias; - let _e571 = occluder; - let _e574 = lit_1; - lit_1 = (_e574 + select(1f, 0f, ((_e568 - _e569) > _e571))); + let _e560 = x; + if (_e560 <= 1i) { + let _e562 = uv_4; + let _e563 = x; + let _e565 = y; + let _e568 = texel_1; + let _e571 = tileLo; + let _e572 = tileHi; + let _e574 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e562 + (vec2(f32(_e563), f32(_e565)) * _e568)), _e571, _e572), 0f); + occluder = _e574.x; + let _e577 = projected[2u]; + let _e578 = bias; + let _e580 = occluder; + let _e583 = lit_2; + lit_2 = (_e583 + select(1f, 0f, ((_e577 - _e578) > _e580))); continue; } else { break; } continuing { - let _e576 = x; - x = (_e576 + 1i); + let _e585 = x; + x = (_e585 + 1i); } } continue; @@ -10147,145 +10597,145 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e578 = y; - y = (_e578 + 1i); + let _e587 = y; + y = (_e587 + 1i); } } - let _e580 = lit_1; - lit_1 = (_e580 * 0.11111111f); + let _e589 = lit_2; + lit_2 = (_e589 * 0.11111111f); } else { - let _e582 = softness; - spread = tan(min(_e582, 0.5f)); - let _e585 = ShadowNoise_u0028_(); - turn_1 = (_e585 * 6.2831855f); - let _e587 = spread; - let _e589 = projected[2u]; - let _e591 = cascadeDepth; - let _e593 = cascadeTexel; - searchRadius = clamp((((_e587 * _e589) * _e591) / _e593), 1f, 16f); + let _e591 = softness; + spread = tan(min(_e591, 0.5f)); + let _e594 = ShadowNoise_u0028_(); + turn_1 = (_e594 * 6.2831855f); + let _e596 = spread; + let _e598 = projected[2u]; + let _e600 = cascadeDepth; + let _e602 = cascadeTexel; + searchRadius = clamp((((_e596 * _e598) * _e600) / _e602), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e596 = i_5; - if (_e596 < 16i) { - let _e598 = i_5; - param_58 = _e598; - param_59 = 16i; - let _e599 = turn_1; - param_60 = _e599; - let _e600 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); - disc = _e600; - let _e601 = bias; - let _e602 = disc; - let _e604 = searchRadius; - let _e608 = receiverSlope; - tapBias = (_e601 + (max(((length(_e602) * _e604) - 1.5f), 0f) * _e608)); - let _e611 = uv_4; - let _e612 = disc; - let _e613 = texel_1; - let _e615 = searchRadius; - let _e618 = tileLo; - let _e619 = tileHi; - let _e621 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e611 + ((_e612 * _e613) * _e615)), _e618, _e619), 0f); - occluder_1 = _e621.x; - let _e624 = projected[2u]; - let _e625 = tapBias; - let _e627 = occluder_1; - if ((_e624 - _e625) > _e627) { - let _e629 = occluder_1; - let _e630 = blockerSum; - blockerSum = (_e630 + _e629); - let _e632 = blockerCount; - blockerCount = (_e632 + 1f); + let _e605 = i_5; + if (_e605 < 16i) { + let _e607 = i_5; + param_64 = _e607; + param_65 = 16i; + let _e608 = turn_1; + param_66 = _e608; + let _e609 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_64), (¶m_65), (¶m_66)); + disc = _e609; + let _e610 = bias; + let _e611 = disc; + let _e613 = searchRadius; + let _e617 = receiverSlope; + tapBias = (_e610 + (max(((length(_e611) * _e613) - 1.5f), 0f) * _e617)); + let _e620 = uv_4; + let _e621 = disc; + let _e622 = texel_1; + let _e624 = searchRadius; + let _e627 = tileLo; + let _e628 = tileHi; + let _e630 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e620 + ((_e621 * _e622) * _e624)), _e627, _e628), 0f); + occluder_1 = _e630.x; + let _e633 = projected[2u]; + let _e634 = tapBias; + let _e636 = occluder_1; + if ((_e633 - _e634) > _e636) { + let _e638 = occluder_1; + let _e639 = blockerSum; + blockerSum = (_e639 + _e638); + let _e641 = blockerCount; + blockerCount = (_e641 + 1f); } continue; } else { break; } continuing { - let _e634 = i_5; - i_5 = (_e634 + 1i); + let _e643 = i_5; + i_5 = (_e643 + 1i); } } - let _e636 = blockerCount; - if (_e636 <= 0f) { + let _e645 = blockerCount; + if (_e645 <= 0f) { return 1f; } - let _e639 = projected[2u]; - let _e640 = blockerSum; - let _e641 = blockerCount; - let _e645 = cascadeDepth; - gap = (max((_e639 - (_e640 / _e641)), 0f) * _e645); - let _e647 = spread; - let _e648 = gap; - let _e650 = cascadeTexel; - radius_2 = clamp(((_e647 * _e648) / _e650), 1f, 16f); + let _e648 = projected[2u]; + let _e649 = blockerSum; + let _e650 = blockerCount; + let _e654 = cascadeDepth; + gap = (max((_e648 - (_e649 / _e650)), 0f) * _e654); + let _e656 = spread; + let _e657 = gap; + let _e659 = cascadeTexel; + radius_2 = clamp(((_e656 * _e657) / _e659), 1f, 16f); i_6 = 0i; loop { - let _e653 = i_6; - if (_e653 < 16i) { - let _e655 = turn_1; - let _e657 = i_6; - param_61 = _e657; - param_62 = 16i; - param_63 = (_e655 + 1f); - let _e658 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - disc_1 = _e658; - let _e659 = bias; - let _e660 = disc_1; - let _e662 = radius_2; - let _e666 = receiverSlope; - tapBias_1 = (_e659 + (max(((length(_e660) * _e662) - 1.5f), 0f) * _e666)); - let _e669 = uv_4; - let _e670 = disc_1; - let _e671 = texel_1; - let _e673 = radius_2; - let _e676 = tileLo; - let _e677 = tileHi; - let _e679 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e669 + ((_e670 * _e671) * _e673)), _e676, _e677), 0f); - occluder_2 = _e679.x; - let _e682 = projected[2u]; - let _e683 = tapBias_1; - let _e685 = occluder_2; - let _e688 = lit_1; - lit_1 = (_e688 + select(1f, 0f, ((_e682 - _e683) > _e685))); + let _e662 = i_6; + if (_e662 < 16i) { + let _e664 = turn_1; + let _e666 = i_6; + param_67 = _e666; + param_68 = 16i; + param_69 = (_e664 + 1f); + let _e667 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_67), (¶m_68), (¶m_69)); + disc_1 = _e667; + let _e668 = bias; + let _e669 = disc_1; + let _e671 = radius_2; + let _e675 = receiverSlope; + tapBias_1 = (_e668 + (max(((length(_e669) * _e671) - 1.5f), 0f) * _e675)); + let _e678 = uv_4; + let _e679 = disc_1; + let _e680 = texel_1; + let _e682 = radius_2; + let _e685 = tileLo; + let _e686 = tileHi; + let _e688 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e678 + ((_e679 * _e680) * _e682)), _e685, _e686), 0f); + occluder_2 = _e688.x; + let _e691 = projected[2u]; + let _e692 = tapBias_1; + let _e694 = occluder_2; + let _e697 = lit_2; + lit_2 = (_e697 + select(1f, 0f, ((_e691 - _e692) > _e694))); continue; } else { break; } continuing { - let _e690 = i_6; - i_6 = (_e690 + 1i); + let _e699 = i_6; + i_6 = (_e699 + 1i); } } - let _e692 = lit_1; - lit_1 = (_e692 * 0.0625f); + let _e701 = lit_2; + lit_2 = (_e701 * 0.0625f); } } - let _e694 = lit_1; - let _e695 = strength_1; - return mix(1f, _e694, clamp(_e695, 0f, 1f)); + let _e703 = lit_2; + let _e704 = strength_1; + return mix(1f, _e703, clamp(_e704, 0f, 1f)); } -fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_2: ptr, index: ptr) -> f32 { - var param_64: Surface; - var param_65: LightSample; - var param_66: i32; +fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_3: ptr, light_2: ptr, index: ptr) -> f32 { + var param_70: Surface; + var param_71: LightSample; + var param_72: i32; - let _e182 = (*s_2); - param_64 = _e182; - let _e183 = (*light_2); - param_65 = _e183; - let _e184 = (*index); - param_66 = _e184; - let _e185 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); - return _e185; + let _e191 = (*s_3); + param_70 = _e191; + let _e192 = (*light_2); + param_71 = _e192; + let _e193 = (*index); + param_72 = _e193; + let _e194 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_70), (¶m_71), (¶m_72)); + return _e194; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e177 = (*index_1); - return (_e177 < 8i); + let _e186 = (*index_1); + return (_e186 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -10293,26 +10743,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e181 = (*distance_1); - let _e182 = (*distance_1); - attenuation = (1f / max((_e181 * _e182), 0.0001f)); - let _e186 = (*range_4); - if (_e186 > 0f) { - let _e188 = (*distance_1); - let _e189 = (*range_4); - ratio = (_e188 / _e189); - let _e191 = ratio; - let _e192 = ratio; - let _e194 = ratio; - let _e196 = ratio; - window = clamp((1f - (((_e191 * _e192) * _e194) * _e196)), 0f, 1f); - let _e200 = window; - let _e201 = window; - let _e203 = attenuation; - attenuation = (_e203 * (_e200 * _e201)); - } - let _e205 = attenuation; - return _e205; + let _e190 = (*distance_1); + let _e191 = (*distance_1); + attenuation = (1f / max((_e190 * _e191), 0.0001f)); + let _e195 = (*range_4); + if (_e195 > 0f) { + let _e197 = (*distance_1); + let _e198 = (*range_4); + ratio = (_e197 / _e198); + let _e200 = ratio; + let _e201 = ratio; + let _e203 = ratio; + let _e205 = ratio; + window = clamp((1f - (((_e200 * _e201) * _e203) * _e205)), 0f, 1f); + let _e209 = window; + let _e210 = window; + let _e212 = attenuation; + attenuation = (_e212 * (_e209 * _e210)); + } + let _e214 = attenuation; + return _e214; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -10322,82 +10772,82 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_9: vec3; + var local_11: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; var u: f32; var v: f32; - let _e193 = (*halfWidth); - let _e194 = (*halfHeight); - n_2 = cross(_e193, _e194); - let _e196 = n_2; - nLen = length(_e196); - let _e198 = nLen; - if (_e198 < 0.000000000001f) { - let _e200 = (*centre); - return _e200; - } - let _e201 = nLen; - let _e202 = n_2; - n_2 = (_e202 / vec3(_e201)); - let _e205 = (*centre); - let _e206 = (*world_2); - toPlane = (_e205 - _e206); - let _e208 = (*mirror); - let _e209 = n_2; - denom = dot(_e208, _e209); - let _e211 = denom; - if (abs(_e211) < 0.00001f) { - let _e214 = toPlane; - let _e215 = n_2; - let _e216 = toPlane; - let _e217 = n_2; - onPlane = (_e214 - (_e215 * dot(_e216, _e217))); + let _e202 = (*halfWidth); + let _e203 = (*halfHeight); + n_2 = cross(_e202, _e203); + let _e205 = n_2; + nLen = length(_e205); + let _e207 = nLen; + if (_e207 < 0.000000000001f) { + let _e209 = (*centre); + return _e209; + } + let _e210 = nLen; + let _e211 = n_2; + n_2 = (_e211 / vec3(_e210)); + let _e214 = (*centre); + let _e215 = (*world_2); + toPlane = (_e214 - _e215); + let _e217 = (*mirror); + let _e218 = n_2; + denom = dot(_e217, _e218); + let _e220 = denom; + if (abs(_e220) < 0.00001f) { + let _e223 = toPlane; + let _e224 = n_2; + let _e225 = toPlane; + let _e226 = n_2; + onPlane = (_e223 - (_e224 * dot(_e225, _e226))); } else { - let _e221 = toPlane; - let _e222 = n_2; - let _e224 = denom; - t_1 = (dot(_e221, _e222) / _e224); - let _e226 = t_1; - if (_e226 > 0f) { - let _e228 = (*mirror); - let _e229 = t_1; - local_9 = (_e228 * _e229); + let _e230 = toPlane; + let _e231 = n_2; + let _e233 = denom; + t_1 = (dot(_e230, _e231) / _e233); + let _e235 = t_1; + if (_e235 > 0f) { + let _e237 = (*mirror); + let _e238 = t_1; + local_11 = (_e237 * _e238); } else { - let _e231 = toPlane; - let _e232 = n_2; - let _e233 = toPlane; - let _e234 = n_2; - local_9 = (_e231 - (_e232 * dot(_e233, _e234))); - } - let _e238 = local_9; - onPlane = _e238; - } - let _e239 = onPlane; - let _e240 = toPlane; - offset_2 = (_e239 - _e240); - let _e242 = (*halfWidth); - let _e243 = (*halfWidth); - wLen2_ = max(dot(_e242, _e243), 0.000000000001f); - let _e246 = (*halfHeight); - let _e247 = (*halfHeight); - hLen2_ = max(dot(_e246, _e247), 0.000000000001f); - let _e250 = offset_2; + let _e240 = toPlane; + let _e241 = n_2; + let _e242 = toPlane; + let _e243 = n_2; + local_11 = (_e240 - (_e241 * dot(_e242, _e243))); + } + let _e247 = local_11; + onPlane = _e247; + } + let _e248 = onPlane; + let _e249 = toPlane; + offset_2 = (_e248 - _e249); let _e251 = (*halfWidth); - let _e253 = wLen2_; - u = clamp((dot(_e250, _e251) / _e253), -1f, 1f); - let _e256 = offset_2; - let _e257 = (*halfHeight); - let _e259 = hLen2_; - v = clamp((dot(_e256, _e257) / _e259), -1f, 1f); - let _e262 = (*centre); - let _e263 = (*halfWidth); - let _e264 = u; - let _e267 = (*halfHeight); - let _e268 = v; - return ((_e262 + (_e263 * _e264)) + (_e267 * _e268)); + let _e252 = (*halfWidth); + wLen2_ = max(dot(_e251, _e252), 0.000000000001f); + let _e255 = (*halfHeight); + let _e256 = (*halfHeight); + hLen2_ = max(dot(_e255, _e256), 0.000000000001f); + let _e259 = offset_2; + let _e260 = (*halfWidth); + let _e262 = wLen2_; + u = clamp((dot(_e259, _e260) / _e262), -1f, 1f); + let _e265 = offset_2; + let _e266 = (*halfHeight); + let _e268 = hLen2_; + v = clamp((dot(_e265, _e266) / _e268), -1f, 1f); + let _e271 = (*centre); + let _e272 = (*halfWidth); + let _e273 = u; + let _e276 = (*halfHeight); + let _e277 = v; + return ((_e271 + (_e272 * _e273)) + (_e276 * _e277)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>, n_3: ptr>) -> f32 { @@ -10406,34 +10856,34 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_10: f32; + var local_12: f32; - let _e185 = (*a_1); - let _e186 = (*a_1); - ua = (_e185 / vec3(max(length(_e186), 0.000000000001f))); - let _e191 = (*b); - let _e192 = (*b); - ub = (_e191 / vec3(max(length(_e192), 0.000000000001f))); - let _e197 = ua; - let _e198 = ub; - angle_1 = acos(clamp(dot(_e197, _e198), -1f, 1f)); - let _e202 = ua; - let _e203 = ub; - axis = cross(_e202, _e203); - let _e205 = axis; - len = length(_e205); - let _e207 = len; - if (_e207 > 0.000001f) { - let _e209 = angle_1; - let _e210 = axis; - let _e211 = (*n_3); - let _e214 = len; - local_10 = ((_e209 * dot(_e210, _e211)) / _e214); + let _e194 = (*a_1); + let _e195 = (*a_1); + ua = (_e194 / vec3(max(length(_e195), 0.000000000001f))); + let _e200 = (*b); + let _e201 = (*b); + ub = (_e200 / vec3(max(length(_e201), 0.000000000001f))); + let _e206 = ua; + let _e207 = ub; + angle_1 = acos(clamp(dot(_e206, _e207), -1f, 1f)); + let _e211 = ua; + let _e212 = ub; + axis = cross(_e211, _e212); + let _e214 = axis; + len = length(_e214); + let _e216 = len; + if (_e216 > 0.000001f) { + let _e218 = angle_1; + let _e219 = axis; + let _e220 = (*n_3); + let _e223 = len; + local_12 = ((_e218 * dot(_e219, _e220)) / _e223); } else { - local_10 = 0f; + local_12 = 0f; } - let _e216 = local_10; - return _e216; + let _e225 = local_12; + return _e225; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr, 4>>, n_4: ptr>) -> f32 { @@ -10443,212 +10893,212 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_11: i32; + var local_13: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_12: f32; + var local_14: f32; var aAbove: bool; var bAbove: bool; - var local_13: f32; - var param_67: vec3; - var param_68: vec3; - var param_69: vec3; - var param_70: vec3; - var param_71: vec3; - var param_72: vec3; + var local_15: f32; + var param_73: vec3; + var param_74: vec3; + var param_75: vec3; + var param_76: vec3; + var param_77: vec3; + var param_78: vec3; total = 0f; exit = vec3(0f, 0f, 0f); entry = vec3(0f, 0f, 0f); i_7 = 0i; loop { - let _e199 = i_7; - if (_e199 < 4i) { - let _e201 = i_7; - let _e203 = (*corners)[_e201]; - a_2 = _e203; - let _e204 = i_7; - if (_e204 == 3i) { - local_11 = 0i; + let _e208 = i_7; + if (_e208 < 4i) { + let _e210 = i_7; + let _e212 = (*corners)[_e210]; + a_2 = _e212; + let _e213 = i_7; + if (_e213 == 3i) { + local_13 = 0i; } else { - let _e206 = i_7; - local_11 = (_e206 + 1i); + let _e215 = i_7; + local_13 = (_e215 + 1i); } - let _e208 = local_11; - let _e210 = (*corners)[_e208]; - b_1 = _e210; - let _e211 = a_2; - let _e212 = (*n_4); - ha = dot(_e211, _e212); - let _e214 = b_1; - let _e215 = (*n_4); - hb = dot(_e214, _e215); - let _e217 = ha; - let _e218 = hb; - d_3 = (_e217 - _e218); + let _e217 = local_13; + let _e219 = (*corners)[_e217]; + b_1 = _e219; let _e220 = a_2; - let _e221 = b_1; - let _e222 = a_2; - let _e224 = d_3; - if (abs(_e224) > 0.000000000001f) { - let _e227 = ha; - let _e228 = d_3; - local_12 = (_e227 / _e228); + let _e221 = (*n_4); + ha = dot(_e220, _e221); + let _e223 = b_1; + let _e224 = (*n_4); + hb = dot(_e223, _e224); + let _e226 = ha; + let _e227 = hb; + d_3 = (_e226 - _e227); + let _e229 = a_2; + let _e230 = b_1; + let _e231 = a_2; + let _e233 = d_3; + if (abs(_e233) > 0.000000000001f) { + let _e236 = ha; + let _e237 = d_3; + local_14 = (_e236 / _e237); } else { - local_12 = 0f; - } - let _e230 = local_12; - q = (_e220 + ((_e221 - _e222) * _e230)); - let _e233 = ha; - aAbove = (_e233 > 0f); - let _e235 = hb; - bAbove = (_e235 > 0f); - let _e237 = aAbove; - let _e238 = bAbove; - if (_e237 || _e238) { - let _e240 = aAbove; - let _e241 = a_2; - let _e242 = q; - let _e245 = bAbove; - let _e246 = b_1; - let _e247 = q; - param_67 = select(_e242, _e241, vec3(_e240)); - param_68 = select(_e247, _e246, vec3(_e245)); - let _e250 = (*n_4); - param_69 = _e250; - let _e251 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); - local_13 = _e251; + local_14 = 0f; + } + let _e239 = local_14; + q = (_e229 + ((_e230 - _e231) * _e239)); + let _e242 = ha; + aAbove = (_e242 > 0f); + let _e244 = hb; + bAbove = (_e244 > 0f); + let _e246 = aAbove; + let _e247 = bAbove; + if (_e246 || _e247) { + let _e249 = aAbove; + let _e250 = a_2; + let _e251 = q; + let _e254 = bAbove; + let _e255 = b_1; + let _e256 = q; + param_73 = select(_e251, _e250, vec3(_e249)); + param_74 = select(_e256, _e255, vec3(_e254)); + let _e259 = (*n_4); + param_75 = _e259; + let _e260 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_73), (¶m_74), (¶m_75)); + local_15 = _e260; } else { - local_13 = 0f; + local_15 = 0f; } - let _e252 = local_13; - let _e253 = total; - total = (_e253 + _e252); - let _e255 = aAbove; - let _e256 = bAbove; - let _e259 = q; - let _e260 = exit; - exit = select(_e260, _e259, vec3((_e255 && !(_e256)))); - let _e263 = aAbove; + let _e261 = local_15; + let _e262 = total; + total = (_e262 + _e261); + let _e264 = aAbove; let _e265 = bAbove; - let _e267 = q; - let _e268 = entry; - entry = select(_e268, _e267, vec3((!(_e263) && _e265))); + let _e268 = q; + let _e269 = exit; + exit = select(_e269, _e268, vec3((_e264 && !(_e265)))); + let _e272 = aAbove; + let _e274 = bAbove; + let _e276 = q; + let _e277 = entry; + entry = select(_e277, _e276, vec3((!(_e272) && _e274))); continue; } else { break; } continuing { - let _e271 = i_7; - i_7 = (_e271 + 1i); + let _e280 = i_7; + i_7 = (_e280 + 1i); } } - let _e273 = exit; - param_70 = _e273; - let _e274 = entry; - param_71 = _e274; - let _e275 = (*n_4); - param_72 = _e275; - let _e276 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); - let _e277 = total; - total = (_e277 + _e276); - let _e279 = total; - return max((-(_e279) * 0.5f), 0f); + let _e282 = exit; + param_76 = _e282; + let _e283 = entry; + param_77 = _e283; + let _e284 = (*n_4); + param_78 = _e284; + let _e285 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_76), (¶m_77), (¶m_78)); + let _e286 = total; + total = (_e286 + _e285); + let _e288 = total; + return max((-(_e288) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_14: f32; - var local_15: f32; var local_16: f32; + var local_17: f32; + var local_18: f32; - let _e182 = (*slot_1); - let _e183 = (*slot_1); - lane = (_e182 - ((_e183 / 4i) * 4i)); - let _e187 = lane; - if (_e187 == 0i) { - let _e190 = (*four)[0u]; - local_14 = _e190; + let _e191 = (*slot_1); + let _e192 = (*slot_1); + lane = (_e191 - ((_e192 / 4i) * 4i)); + let _e196 = lane; + if (_e196 == 0i) { + let _e199 = (*four)[0u]; + local_16 = _e199; } else { - let _e191 = lane; - if (_e191 == 1i) { - let _e194 = (*four)[1u]; - local_15 = _e194; + let _e200 = lane; + if (_e200 == 1i) { + let _e203 = (*four)[1u]; + local_17 = _e203; } else { - let _e195 = lane; - if (_e195 == 2i) { - let _e198 = (*four)[2u]; - local_16 = _e198; + let _e204 = lane; + if (_e204 == 2i) { + let _e207 = (*four)[2u]; + local_18 = _e207; } else { - let _e200 = (*four)[3u]; - local_16 = _e200; + let _e209 = (*four)[3u]; + local_18 = _e209; } - let _e201 = local_16; - local_15 = _e201; + let _e210 = local_18; + local_17 = _e210; } - let _e202 = local_15; - local_14 = _e202; + let _e211 = local_17; + local_16 = _e211; } - let _e203 = local_14; - return _e203; + let _e212 = local_16; + return _e212; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { - var param_73: vec4; - var param_74: i32; + var param_79: vec4; + var param_80: i32; - let _e179 = (*slot_2); - let _e183 = light_list_info.scales[(_e179 / 4i)]; - param_73 = _e183; - let _e184 = (*slot_2); - param_74 = _e184; - let _e185 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); - return _e185; + let _e188 = (*slot_2); + let _e192 = light_list_info.scales[(_e188 / 4i)]; + param_79 = _e192; + let _e193 = (*slot_2); + param_80 = _e193; + let _e194 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); + return _e194; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_3: vec4; var b_2: vec4; - let _e181 = light_list_info.slot_rows[0i]; - let _e182 = (*row); - a_3 = abs((_e181 - vec4(_e182))); - let _e188 = light_list_info.slot_rows[1i]; - let _e189 = (*row); - b_2 = abs((_e188 - vec4(_e189))); - let _e194 = a_3[0u]; - let _e196 = a_3[1u]; - let _e199 = a_3[2u]; - let _e201 = a_3[3u]; - let _e205 = b_2[0u]; - let _e207 = b_2[1u]; - let _e210 = b_2[2u]; - let _e212 = b_2[3u]; - return (min(min(min(_e194, _e196), min(_e199, _e201)), min(min(_e205, _e207), min(_e210, _e212))) < 0.5f); + let _e190 = light_list_info.slot_rows[0i]; + let _e191 = (*row); + a_3 = abs((_e190 - vec4(_e191))); + let _e197 = light_list_info.slot_rows[1i]; + let _e198 = (*row); + b_2 = abs((_e197 - vec4(_e198))); + let _e203 = a_3[0u]; + let _e205 = a_3[1u]; + let _e208 = a_3[2u]; + let _e210 = a_3[3u]; + let _e214 = b_2[0u]; + let _e216 = b_2[1u]; + let _e219 = b_2[2u]; + let _e221 = b_2[3u]; + return (min(min(min(_e203, _e205), min(_e208, _e210)), min(min(_e214, _e216), min(_e219, _e221))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { - var param_75: vec4; - var param_76: i32; + var param_81: vec4; + var param_82: i32; - let _e179 = (*slot_3); - let _e183 = light_list_info.indices[(_e179 / 4i)]; - param_75 = _e183; - let _e184 = (*slot_3); - param_76 = _e184; - let _e185 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); - return _e185; + let _e188 = (*slot_3); + let _e192 = light_list_info.indices[(_e188 / 4i)]; + param_81 = _e192; + let _e193 = (*slot_3); + param_82 = _e193; + let _e194 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_81), (¶m_82)); + return _e194; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e178 = (*texel_2); - let _e182 = light_list_info.list[1u]; - let _e184 = (*row_1); - let _e188 = light_list_info.list[2u]; - let _e191 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e178 + 0.5f) * _e182), ((_e184 + 0.5f) * _e188)), 0f); - return _e191; + let _e187 = (*texel_2); + let _e191 = light_list_info.list[1u]; + let _e193 = (*row_1); + let _e197 = light_list_info.list[2u]; + let _e200 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e187 + 0.5f) * _e191), ((_e193 + 0.5f) * _e197)), 0f); + return _e200; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -10657,43 +11107,43 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var within: f32; var texel_3: f32; var four_1: vec4; - var param_77: f32; - var param_78: f32; - var param_79: vec4; - var param_80: i32; + var param_83: f32; + var param_84: f32; + var param_85: vec4; + var param_86: i32; - let _e186 = g_cluster_offset; - let _e187 = (*slot_4); - entry_1 = (_e186 + f32(_e187)); - let _e190 = entry_1; - row_2 = floor((_e190 / 16f)); - let _e193 = entry_1; - let _e194 = row_2; - within = (_e193 - (_e194 * 16f)); - let _e197 = within; - texel_3 = floor((_e197 / 4f)); - let _e202 = light_list_info.cluster_depth[3u]; + let _e195 = g_cluster_offset; + let _e196 = (*slot_4); + entry_1 = (_e195 + f32(_e196)); + let _e199 = entry_1; + row_2 = floor((_e199 / 16f)); + let _e202 = entry_1; let _e203 = row_2; - let _e205 = texel_3; - param_77 = _e205; - param_78 = (_e202 + _e203); - let _e206 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); - four_1 = _e206; - let _e207 = within; - let _e208 = texel_3; - let _e213 = four_1; - param_79 = _e213; - param_80 = i32(((_e207 - (_e208 * 4f)) + 0.5f)); - let _e214 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); - return _e214; + within = (_e202 - (_e203 * 16f)); + let _e206 = within; + texel_3 = floor((_e206 / 4f)); + let _e211 = light_list_info.cluster_depth[3u]; + let _e212 = row_2; + let _e214 = texel_3; + param_83 = _e214; + param_84 = (_e211 + _e212); + let _e215 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_83), (¶m_84)); + four_1 = _e215; + let _e216 = within; + let _e217 = texel_3; + let _e222 = four_1; + param_85 = _e222; + param_86 = i32(((_e216 - (_e217 * 4f)) + 0.5f)); + let _e223 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_85), (¶m_86)); + return _e223; } fn Clustered_u0028_() -> bool { - let _e178 = light_list_info.cluster_grid[3u]; - return (_e178 > 0.5f); + let _e187 = light_list_info.cluster_grid[3u]; + return (_e187 > 0.5f); } -fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_3: ptr) -> LightSample { +fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_4: ptr) -> LightSample { var light_3: LightSample; var position: vec4; var color_1: vec4; @@ -10702,14 +11152,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_17: f32; - var param_81: i32; - var param_82: i32; + var local_19: f32; + var param_87: i32; + var param_88: i32; var v_1: f32; var u_1: f32; - var local_18: f32; - var param_83: f32; - var param_84: i32; + var local_20: f32; + var param_89: f32; + var param_90: i32; var type_39: f32; var halfWidth_1: vec3; var halfHeight_1: vec3; @@ -10717,293 +11167,293 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_19: f32; - var param_85: array, 4>; - var param_86: vec3; + var local_21: f32; + var param_91: array, 4>; + var param_92: vec3; var area: f32; var radiance: f32; - var local_20: f32; + var local_22: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; var mirror_1: vec3; var representative: vec3; - var param_87: vec3; - var param_88: vec3; - var param_89: vec3; - var param_90: vec3; - var param_91: vec3; + var param_93: vec3; + var param_94: vec3; + var param_95: vec3; + var param_96: vec3; + var param_97: vec3; var toPoint: vec3; var pointDistance: f32; - var local_21: vec3; + var local_23: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; var distance_3: f32; - var param_92: f32; - var param_93: f32; + var param_98: f32; + var param_99: f32; var cosAngle: f32; light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e229 = (*index_2); - if (_e229 < 8i) { - let _e231 = (*index_2); - let _e234 = frag_info.light_position[_e231]; - position = _e234; - let _e235 = (*index_2); - let _e238 = frag_info.light_color[_e235]; - color_1 = _e238; - let _e239 = (*index_2); - let _e242 = frag_info.light_direction[_e239]; - direction = _e242; - let _e243 = (*index_2); - let _e246 = frag_info.light_cone[_e243]; - cone = _e246; + let _e238 = (*index_2); + if (_e238 < 8i) { + let _e240 = (*index_2); + let _e243 = frag_info.light_position[_e240]; + position = _e243; + let _e244 = (*index_2); + let _e247 = frag_info.light_color[_e244]; + color_1 = _e247; + let _e248 = (*index_2); + let _e251 = frag_info.light_direction[_e248]; + direction = _e251; + let _e252 = (*index_2); + let _e255 = frag_info.light_cone[_e252]; + cone = _e255; } else { - let _e247 = (*index_2); - slot_5 = (_e247 - 8i); - let _e249 = Clustered_u0028_(); - clustered = _e249; - let _e250 = clustered; - if _e250 { - let _e251 = slot_5; - param_81 = _e251; - let _e252 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_17 = _e252; + let _e256 = (*index_2); + slot_5 = (_e256 - 8i); + let _e258 = Clustered_u0028_(); + clustered = _e258; + let _e259 = clustered; + if _e259 { + let _e260 = slot_5; + param_87 = _e260; + let _e261 = ClusterRow_u0028_i1_u003b((¶m_87)); + local_19 = _e261; } else { - let _e253 = slot_5; - param_82 = _e253; - let _e254 = LightListRow_u0028_i1_u003b((¶m_82)); - local_17 = _e254; - } - let _e255 = local_17; - listRow = _e255; - let _e256 = listRow; - let _e260 = light_list_info.list[2u]; - v_1 = ((_e256 + 0.5f) * _e260); - let _e264 = light_list_info.list[1u]; - u_1 = _e264; - let _e265 = u_1; - let _e267 = v_1; - let _e269 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e265), _e267), 0f); - position = _e269; - let _e270 = u_1; - let _e272 = v_1; - let _e274 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e270), _e272), 0f); - color_1 = _e274; - let _e275 = u_1; - let _e277 = v_1; - let _e279 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e275), _e277), 0f); - direction = _e279; - let _e280 = u_1; - let _e282 = v_1; - let _e284 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e280), _e282), 0f); - cone = _e284; - let _e285 = clustered; - if _e285 { - let _e286 = listRow; - param_83 = _e286; - let _e287 = InSlots_u0028_f1_u003b((¶m_83)); - local_18 = select(1f, 0f, _e287); + let _e262 = slot_5; + param_88 = _e262; + let _e263 = LightListRow_u0028_i1_u003b((¶m_88)); + local_19 = _e263; + } + let _e264 = local_19; + listRow = _e264; + let _e265 = listRow; + let _e269 = light_list_info.list[2u]; + v_1 = ((_e265 + 0.5f) * _e269); + let _e273 = light_list_info.list[1u]; + u_1 = _e273; + let _e274 = u_1; + let _e276 = v_1; + let _e278 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e274), _e276), 0f); + position = _e278; + let _e279 = u_1; + let _e281 = v_1; + let _e283 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e279), _e281), 0f); + color_1 = _e283; + let _e284 = u_1; + let _e286 = v_1; + let _e288 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e284), _e286), 0f); + direction = _e288; + let _e289 = u_1; + let _e291 = v_1; + let _e293 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e289), _e291), 0f); + cone = _e293; + let _e294 = clustered; + if _e294 { + let _e295 = listRow; + param_89 = _e295; + let _e296 = InSlots_u0028_f1_u003b((¶m_89)); + local_20 = select(1f, 0f, _e296); } else { - let _e289 = slot_5; - param_84 = _e289; - let _e290 = LightListScale_u0028_i1_u003b((¶m_84)); - local_18 = _e290; - } - let _e291 = local_18; - let _e293 = color_1[3u]; - color_1[3u] = (_e293 * _e291); - } - let _e297 = position[3u]; - type_39 = _e297; - let _e298 = type_39; - if (_e298 > 2.5f) { - let _e300 = direction; - halfWidth_1 = _e300.xyz; - let _e302 = cone; - halfHeight_1 = _e302.xyz; - let _e304 = position; - let _e306 = v_world_position_1; - toCentre = (_e304.xyz - _e306); - let _e308 = toCentre; - let _e309 = halfWidth_1; - let _e311 = halfHeight_1; - corners_1[0i] = ((_e308 - _e309) - _e311); - let _e314 = toCentre; - let _e315 = halfWidth_1; - let _e317 = halfHeight_1; - corners_1[1i] = ((_e314 + _e315) - _e317); - let _e320 = toCentre; - let _e321 = halfWidth_1; - let _e323 = halfHeight_1; - corners_1[2i] = ((_e320 + _e321) + _e323); - let _e326 = toCentre; - let _e327 = halfWidth_1; - let _e329 = halfHeight_1; - corners_1[3i] = ((_e326 - _e327) + _e329); - let _e332 = toCentre; - let _e333 = halfWidth_1; - let _e334 = halfHeight_1; - behind = (dot(_e332, cross(_e333, _e334)) >= 0f); - let _e338 = behind; - if _e338 { - local_19 = 0f; + let _e298 = slot_5; + param_90 = _e298; + let _e299 = LightListScale_u0028_i1_u003b((¶m_90)); + local_20 = _e299; + } + let _e300 = local_20; + let _e302 = color_1[3u]; + color_1[3u] = (_e302 * _e300); + } + let _e306 = position[3u]; + type_39 = _e306; + let _e307 = type_39; + if (_e307 > 2.5f) { + let _e309 = direction; + halfWidth_1 = _e309.xyz; + let _e311 = cone; + halfHeight_1 = _e311.xyz; + let _e313 = position; + let _e315 = v_world_position_1; + toCentre = (_e313.xyz - _e315); + let _e317 = toCentre; + let _e318 = halfWidth_1; + let _e320 = halfHeight_1; + corners_1[0i] = ((_e317 - _e318) - _e320); + let _e323 = toCentre; + let _e324 = halfWidth_1; + let _e326 = halfHeight_1; + corners_1[1i] = ((_e323 + _e324) - _e326); + let _e329 = toCentre; + let _e330 = halfWidth_1; + let _e332 = halfHeight_1; + corners_1[2i] = ((_e329 + _e330) + _e332); + let _e335 = toCentre; + let _e336 = halfWidth_1; + let _e338 = halfHeight_1; + corners_1[3i] = ((_e335 - _e336) + _e338); + let _e341 = toCentre; + let _e342 = halfWidth_1; + let _e343 = halfHeight_1; + behind = (dot(_e341, cross(_e342, _e343)) >= 0f); + let _e347 = behind; + if _e347 { + local_21 = 0f; } else { - let _e339 = corners_1; - param_85 = _e339; - let _e341 = (*s_3).n; - param_86 = _e341; - let _e342 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_19 = _e342; - } - let _e343 = local_19; - formFactor = _e343; - let _e344 = halfWidth_1; - let _e345 = halfHeight_1; - area = (length(cross(_e344, _e345)) * 4f); - let _e349 = area; - if (_e349 > 0.000000001f) { - let _e351 = area; - local_20 = (1f / _e351); + let _e348 = corners_1; + param_91 = _e348; + let _e350 = (*s_4).n; + param_92 = _e350; + let _e351 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_91), (¶m_92)); + local_21 = _e351; + } + let _e352 = local_21; + formFactor = _e352; + let _e353 = halfWidth_1; + let _e354 = halfHeight_1; + area = (length(cross(_e353, _e354)) * 4f); + let _e358 = area; + if (_e358 > 0.000000001f) { + let _e360 = area; + local_22 = (1f / _e360); } else { - local_20 = 0f; - } - let _e353 = local_20; - radiance = _e353; - let _e354 = toCentre; - distance_2 = length(_e354); - let _e357 = direction[3u]; - if (_e357 > 0f) { - let _e359 = distance_2; - let _e361 = direction[3u]; - ratio_1 = (_e359 / _e361); - let _e363 = ratio_1; - let _e364 = ratio_1; - let _e366 = ratio_1; - let _e368 = ratio_1; - window_1 = clamp((1f - (((_e363 * _e364) * _e366) * _e368)), 0f, 1f); - let _e372 = window_1; - let _e373 = window_1; - let _e375 = radiance; - radiance = (_e375 * (_e372 * _e373)); - } - let _e378 = (*s_3).v; - let _e381 = (*s_3).n; - mirror_1 = reflect(-(_e378), _e381); - let _e383 = position; - param_87 = _e383.xyz; - let _e385 = halfWidth_1; - param_88 = _e385; - let _e386 = halfHeight_1; - param_89 = _e386; - let _e387 = v_world_position_1; - param_90 = _e387; - let _e388 = mirror_1; - param_91 = _e388; - let _e389 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); - representative = _e389; - let _e390 = representative; - let _e391 = v_world_position_1; - toPoint = (_e390 - _e391); - let _e393 = toPoint; - pointDistance = length(_e393); - let _e395 = pointDistance; - if (_e395 > 0.000001f) { - let _e397 = toPoint; - let _e398 = pointDistance; - local_21 = (_e397 / vec3(_e398)); + local_22 = 0f; + } + let _e362 = local_22; + radiance = _e362; + let _e363 = toCentre; + distance_2 = length(_e363); + let _e366 = direction[3u]; + if (_e366 > 0f) { + let _e368 = distance_2; + let _e370 = direction[3u]; + ratio_1 = (_e368 / _e370); + let _e372 = ratio_1; + let _e373 = ratio_1; + let _e375 = ratio_1; + let _e377 = ratio_1; + window_1 = clamp((1f - (((_e372 * _e373) * _e375) * _e377)), 0f, 1f); + let _e381 = window_1; + let _e382 = window_1; + let _e384 = radiance; + radiance = (_e384 * (_e381 * _e382)); + } + let _e387 = (*s_4).v; + let _e390 = (*s_4).n; + mirror_1 = reflect(-(_e387), _e390); + let _e392 = position; + param_93 = _e392.xyz; + let _e394 = halfWidth_1; + param_94 = _e394; + let _e395 = halfHeight_1; + param_95 = _e395; + let _e396 = v_world_position_1; + param_96 = _e396; + let _e397 = mirror_1; + param_97 = _e397; + let _e398 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_93), (¶m_94), (¶m_95), (¶m_96), (¶m_97)); + representative = _e398; + let _e399 = representative; + let _e400 = v_world_position_1; + toPoint = (_e399 - _e400); + let _e402 = toPoint; + pointDistance = length(_e402); + let _e404 = pointDistance; + if (_e404 > 0.000001f) { + let _e406 = toPoint; + let _e407 = pointDistance; + local_23 = (_e406 / vec3(_e407)); } else { - let _e402 = (*s_3).n; - local_21 = _e402; - } - let _e403 = local_21; - light_3.l = _e403; - let _e406 = light_3.l; - let _e408 = (*s_3).v; - light_3.h = normalize((_e406 + _e408)); - let _e412 = formFactor; - light_3.n_dot_l = _e412; - let _e415 = (*s_3).n; - let _e417 = light_3.h; - light_3.n_dot_h = max(dot(_e415, _e417), 0f); - let _e422 = (*s_3).v; - let _e424 = light_3.h; - light_3.v_dot_h = max(dot(_e422, _e424), 0f); - let _e428 = color_1; - let _e431 = color_1[3u]; - let _e433 = radiance; - light_3.radiance = ((_e428.xyz * _e431) * _e433); - let _e436 = light_3; - return _e436; - } - let _e437 = direction; - aim = normalize(_e437.xyz); + let _e411 = (*s_4).n; + local_23 = _e411; + } + let _e412 = local_23; + light_3.l = _e412; + let _e415 = light_3.l; + let _e417 = (*s_4).v; + light_3.h = normalize((_e415 + _e417)); + let _e421 = formFactor; + light_3.n_dot_l = _e421; + let _e424 = (*s_4).n; + let _e426 = light_3.h; + light_3.n_dot_h = max(dot(_e424, _e426), 0f); + let _e431 = (*s_4).v; + let _e433 = light_3.h; + light_3.v_dot_h = max(dot(_e431, _e433), 0f); + let _e437 = color_1; + let _e440 = color_1[3u]; + let _e442 = radiance; + light_3.radiance = ((_e437.xyz * _e440) * _e442); + let _e445 = light_3; + return _e445; + } + let _e446 = direction; + aim = normalize(_e446.xyz); attenuation_1 = 1f; - let _e440 = type_39; - if (_e440 < 0.5f) { - let _e442 = aim; - light_3.l = -(_e442); + let _e449 = type_39; + if (_e449 < 0.5f) { + let _e451 = aim; + light_3.l = -(_e451); } else { - let _e445 = position; - let _e447 = v_world_position_1; - toLight_1 = (_e445.xyz - _e447); - let _e449 = toLight_1; - distance_3 = length(_e449); - let _e451 = distance_3; - if (_e451 < 0.000001f) { - let _e454 = (*s_3).n; - light_3.l = _e454; - let _e457 = (*s_3).n; - light_3.h = _e457; + let _e454 = position; + let _e456 = v_world_position_1; + toLight_1 = (_e454.xyz - _e456); + let _e458 = toLight_1; + distance_3 = length(_e458); + let _e460 = distance_3; + if (_e460 < 0.000001f) { + let _e463 = (*s_4).n; + light_3.l = _e463; + let _e466 = (*s_4).n; + light_3.h = _e466; light_3.radiance = vec3(0f, 0f, 0f); light_3.n_dot_l = 0f; light_3.n_dot_h = 0f; light_3.v_dot_h = 0f; - let _e463 = light_3; - return _e463; - } - let _e464 = toLight_1; - let _e465 = distance_3; - light_3.l = (_e464 / vec3(_e465)); - let _e469 = distance_3; - param_92 = _e469; - let _e471 = direction[3u]; - param_93 = _e471; - let _e472 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); - attenuation_1 = _e472; - let _e473 = type_39; - if (_e473 > 1.5f) { - let _e475 = aim; - let _e477 = light_3.l; - cosAngle = dot(_e475, -(_e477)); - let _e480 = cosAngle; - let _e482 = cone[1u]; - let _e485 = cone[0u]; - let _e487 = cone[1u]; - let _e491 = attenuation_1; - attenuation_1 = (_e491 * clamp(((_e480 - _e482) / (_e485 - _e487)), 0f, 1f)); + let _e472 = light_3; + return _e472; + } + let _e473 = toLight_1; + let _e474 = distance_3; + light_3.l = (_e473 / vec3(_e474)); + let _e478 = distance_3; + param_98 = _e478; + let _e480 = direction[3u]; + param_99 = _e480; + let _e481 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_98), (¶m_99)); + attenuation_1 = _e481; + let _e482 = type_39; + if (_e482 > 1.5f) { + let _e484 = aim; + let _e486 = light_3.l; + cosAngle = dot(_e484, -(_e486)); + let _e489 = cosAngle; + let _e491 = cone[1u]; + let _e494 = cone[0u]; + let _e496 = cone[1u]; + let _e500 = attenuation_1; + attenuation_1 = (_e500 * clamp(((_e489 - _e491) / (_e494 - _e496)), 0f, 1f)); } } - let _e494 = light_3.l; - let _e496 = (*s_3).v; - light_3.h = normalize((_e494 + _e496)); - let _e501 = (*s_3).n; let _e503 = light_3.l; - light_3.n_dot_l = max(dot(_e501, _e503), 0f); - let _e508 = (*s_3).n; - let _e510 = light_3.h; - light_3.n_dot_h = max(dot(_e508, _e510), 0f); - let _e515 = (*s_3).v; - let _e517 = light_3.h; - light_3.v_dot_h = max(dot(_e515, _e517), 0f); - let _e521 = color_1; - let _e524 = color_1[3u]; - let _e526 = attenuation_1; - light_3.radiance = ((_e521.xyz * _e524) * _e526); - let _e529 = light_3; - return _e529; + let _e505 = (*s_4).v; + light_3.h = normalize((_e503 + _e505)); + let _e510 = (*s_4).n; + let _e512 = light_3.l; + light_3.n_dot_l = max(dot(_e510, _e512), 0f); + let _e517 = (*s_4).n; + let _e519 = light_3.h; + light_3.n_dot_h = max(dot(_e517, _e519), 0f); + let _e524 = (*s_4).v; + let _e526 = light_3.h; + light_3.v_dot_h = max(dot(_e524, _e526), 0f); + let _e530 = color_1; + let _e533 = color_1[3u]; + let _e535 = attenuation_1; + light_3.radiance = ((_e530.xyz * _e533) * _e535); + let _e538 = light_3; + return _e538; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -11014,312 +11464,305 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_22: f32; + var local_24: f32; var cell: f32; var row_3: f32; var header: vec4; - var param_94: f32; - var param_95: f32; + var param_100: f32; + var param_101: f32; - let _e191 = light_list_info.cluster_view_projection; - let _e192 = (*world_3); - clip_1 = (_e191 * vec4(_e192.x, _e192.y, _e192.z, 1f)); - let _e198 = clip_1; - let _e201 = clip_1[3u]; - ndc_1 = (_e198.xy / vec2(max(_e201, 0.000001f))); - let _e206 = light_list_info.cluster_grid; - grid = _e206.xyz; - let _e210 = light_list_info.cluster_depth[0u]; - near_1 = _e210; - let _e212 = ndc_1[0u]; - let _e216 = grid[0u]; - let _e220 = grid[0u]; - tx = clamp(floor((((_e212 * 0.5f) + 0.5f) * _e216)), 0f, (_e220 - 1f)); - let _e224 = ndc_1[1u]; - let _e228 = grid[1u]; - let _e232 = grid[1u]; - ty = clamp(floor((((_e224 * 0.5f) + 0.5f) * _e228)), 0f, (_e232 - 1f)); - let _e236 = clip_1[3u]; - let _e237 = near_1; - if (_e236 <= _e237) { - local_22 = 0f; + let _e200 = light_list_info.cluster_view_projection; + let _e201 = (*world_3); + clip_1 = (_e200 * vec4(_e201.x, _e201.y, _e201.z, 1f)); + let _e207 = clip_1; + let _e210 = clip_1[3u]; + ndc_1 = (_e207.xy / vec2(max(_e210, 0.000001f))); + let _e215 = light_list_info.cluster_grid; + grid = _e215.xyz; + let _e219 = light_list_info.cluster_depth[0u]; + near_1 = _e219; + let _e221 = ndc_1[0u]; + let _e225 = grid[0u]; + let _e229 = grid[0u]; + tx = clamp(floor((((_e221 * 0.5f) + 0.5f) * _e225)), 0f, (_e229 - 1f)); + let _e233 = ndc_1[1u]; + let _e237 = grid[1u]; + let _e241 = grid[1u]; + ty = clamp(floor((((_e233 * 0.5f) + 0.5f) * _e237)), 0f, (_e241 - 1f)); + let _e245 = clip_1[3u]; + let _e246 = near_1; + if (_e245 <= _e246) { + local_24 = 0f; } else { - let _e240 = clip_1[3u]; - let _e241 = near_1; - let _e246 = light_list_info.cluster_depth[1u]; - let _e250 = grid[2u]; - local_22 = clamp(floor((log((_e240 / _e241)) * _e246)), 0f, (_e250 - 1f)); - } - let _e253 = local_22; - tz = _e253; - let _e254 = tx; - let _e255 = ty; - let _e257 = grid[0u]; - let _e260 = tz; - let _e262 = grid[0u]; - let _e265 = grid[1u]; - cell = ((_e254 + (_e255 * _e257)) + ((_e260 * _e262) * _e265)); - let _e268 = cell; - row_3 = floor((_e268 / 4f)); - let _e271 = cell; - let _e272 = row_3; - let _e277 = light_list_info.cluster_depth[2u]; - let _e278 = row_3; - param_94 = (_e271 - (_e272 * 4f)); - param_95 = (_e277 + _e278); - let _e280 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); - header = _e280; - let _e282 = header[0u]; - g_cluster_offset = _e282; - let _e284 = header[1u]; - g_cluster_count = _e284; + let _e249 = clip_1[3u]; + let _e250 = near_1; + let _e255 = light_list_info.cluster_depth[1u]; + let _e259 = grid[2u]; + local_24 = clamp(floor((log((_e249 / _e250)) * _e255)), 0f, (_e259 - 1f)); + } + let _e262 = local_24; + tz = _e262; + let _e263 = tx; + let _e264 = ty; + let _e266 = grid[0u]; + let _e269 = tz; + let _e271 = grid[0u]; + let _e274 = grid[1u]; + cell = ((_e263 + (_e264 * _e266)) + ((_e269 * _e271) * _e274)); + let _e277 = cell; + row_3 = floor((_e277 / 4f)); + let _e280 = cell; + let _e281 = row_3; + let _e286 = light_list_info.cluster_depth[2u]; + let _e287 = row_3; + param_100 = (_e280 - (_e281 * 4f)); + param_101 = (_e286 + _e287); + let _e289 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_100), (¶m_101)); + header = _e289; + let _e291 = header[0u]; + g_cluster_offset = _e291; + let _e293 = header[1u]; + g_cluster_count = _e293; return; } fn LightCount_u0028_() -> i32 { var tail: f32; - var param_96: vec3; + var param_102: vec3; - let _e180 = light_list_info.list[0u]; - tail = _e180; - let _e181 = Clustered_u0028_(); - if _e181 { - let _e182 = v_world_position_1; - param_96 = _e182; - FindCluster_u0028_vf3_u003b((¶m_96)); - let _e183 = g_cluster_count; - tail = _e183; + let _e189 = light_list_info.list[0u]; + tail = _e189; + let _e190 = Clustered_u0028_(); + if _e190 { + let _e191 = v_world_position_1; + param_102 = _e191; + FindCluster_u0028_vf3_u003b((¶m_102)); + let _e192 = g_cluster_count; + tail = _e192; } - let _e186 = frag_info.frame_params[1u]; - let _e190 = tail; - return (clamp(i32((_e186 + 0.5f)), 0i, 8i) + clamp(i32((_e190 + 0.5f)), 0i, 24i)); + let _e195 = frag_info.frame_params[1u]; + let _e199 = tail; + return (clamp(i32((_e195 + 0.5f)), 0i, 8i) + clamp(i32((_e199 + 0.5f)), 0i, 24i)); } -fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_4: ptr) -> vec3 { +fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_5: ptr) -> vec3 { var total_1: vec3; var count_1: i32; var i_8: i32; var light_4: LightSample; - var param_97: i32; - var param_98: Surface; + var param_103: i32; + var param_104: Surface; var visibility: f32; - var param_99: i32; - var local_23: f32; - var param_100: Surface; - var param_101: LightSample; - var param_102: i32; - var param_103: vec3; - var param_104: vec3; var param_105: i32; + var local_25: f32; var param_106: Surface; var param_107: LightSample; + var param_108: i32; + var param_109: vec3; + var param_110: vec3; + var param_111: i32; + var param_112: Surface; + var param_113: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e194 = LightCount_u0028_(); - count_1 = _e194; + let _e203 = LightCount_u0028_(); + count_1 = _e203; i_8 = 0i; loop { - let _e195 = i_8; - if (_e195 < 32i) { - let _e197 = i_8; - let _e198 = count_1; - if (_e197 >= _e198) { + let _e204 = i_8; + if (_e204 < 32i) { + let _e206 = i_8; + let _e207 = count_1; + if (_e206 >= _e207) { break; } - let _e200 = i_8; - param_97 = _e200; - let _e201 = (*s_4); - param_98 = _e201; - let _e202 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); - light_4 = _e202; - let _e204 = light_4.n_dot_l; - if (_e204 <= 0f) { + let _e209 = i_8; + param_103 = _e209; + let _e210 = (*s_5); + param_104 = _e210; + let _e211 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_103), (¶m_104)); + light_4 = _e211; + let _e213 = light_4.n_dot_l; + if (_e213 <= 0f) { continue; } - let _e206 = i_8; - param_99 = _e206; - let _e207 = LightHasShadow_u0028_i1_u003b((¶m_99)); - if _e207 { - let _e208 = (*s_4); - param_100 = _e208; - let _e209 = light_4; - param_101 = _e209; - let _e210 = i_8; - param_102 = _e210; - let _e211 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); - let _e212 = v_world_position_1; - param_103 = _e212; - let _e214 = (*s_4).n; - param_104 = _e214; - let _e215 = i_8; - param_105 = _e215; - let _e216 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_23 = (_e211 * _e216); + let _e215 = i_8; + param_105 = _e215; + let _e216 = LightHasShadow_u0028_i1_u003b((¶m_105)); + if _e216 { + let _e217 = (*s_5); + param_106 = _e217; + let _e218 = light_4; + param_107 = _e218; + let _e219 = i_8; + param_108 = _e219; + let _e220 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_106), (¶m_107), (¶m_108)); + let _e221 = v_world_position_1; + param_109 = _e221; + let _e223 = (*s_5).n; + param_110 = _e223; + let _e224 = i_8; + param_111 = _e224; + let _e225 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_109), (¶m_110), (¶m_111)); + local_25 = (_e220 * _e225); } else { - local_23 = 1f; + local_25 = 1f; } - let _e218 = local_23; - visibility = _e218; - let _e219 = visibility; - if (_e219 <= 0f) { + let _e227 = local_25; + visibility = _e227; + let _e228 = visibility; + if (_e228 <= 0f) { continue; } - let _e221 = (*s_4); - param_106 = _e221; - let _e222 = light_4; - param_107 = _e222; - let _e223 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); - let _e225 = light_4.radiance; - let _e228 = light_4.n_dot_l; - let _e230 = visibility; - let _e232 = total_1; - total_1 = (_e232 + (((_e223 * _e225) * _e228) * _e230)); + let _e230 = (*s_5); + param_112 = _e230; + let _e231 = light_4; + param_113 = _e231; + let _e232 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_112), (¶m_113)); + let _e234 = light_4.radiance; + let _e237 = light_4.n_dot_l; + let _e239 = visibility; + let _e241 = total_1; + total_1 = (_e241 + (((_e232 * _e234) * _e237) * _e239)); continue; } else { break; } continuing { - let _e234 = i_8; - i_8 = (_e234 + 1i); + let _e243 = i_8; + i_8 = (_e243 + 1i); } } - let _e236 = total_1; - return _e236; + let _e245 = total_1; + return _e245; } fn SampleLightmap_u0028_() -> vec3 { var texel_4: vec4; - let _e177 = v_lightmap_uv_1; - let _e178 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e177); - texel_4 = _e178; - let _e179 = texel_4; - let _e182 = texel_4[3u]; - return ((_e179.xyz * _e182) * 8f); + let _e186 = v_lightmap_uv_1; + let _e187 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e186); + texel_4 = _e187; + let _e188 = texel_4; + let _e191 = texel_4[3u]; + return ((_e188.xyz * _e191) * 8f); } fn MaterialLodBias_u0028_() -> f32 { - let _e178 = frag_info.target_origin[1u]; - return _e178; + let _e187 = frag_info.target_origin[1u]; + return _e187; } -fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_5: ptr) { +fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) { var orm: vec3; - let _e178 = v_texcoord_1; - let _e179 = MaterialLodBias_u0028_(); - let _e180 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e178, _e179); - orm = _e180.xyz; - let _e183 = (*s_5).metallic; - let _e185 = orm[2u]; - (*s_5).metallic = clamp((_e183 * _e185), 0f, 1f); - let _e190 = (*s_5).roughness; - let _e192 = orm[1u]; - (*s_5).roughness = clamp((_e190 * _e192), 0.02f, 1f); + let _e187 = v_texcoord_1; + let _e188 = MaterialLodBias_u0028_(); + let _e189 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e187, _e188); + orm = _e189.xyz; + let _e192 = (*s_6).metallic; + let _e194 = orm[2u]; + (*s_6).metallic = clamp((_e192 * _e194), 0f, 1f); + let _e199 = (*s_6).roughness; + let _e201 = orm[1u]; + (*s_6).roughness = clamp((_e199 * _e201), 0.02f, 1f); return; } -fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e177 = (*srgb); - let _e180 = (*srgb); - let _e185 = (*srgb); - return mix((_e177 / vec3(12.92f)), pow(((_e180 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e185)); -} - -fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) { +fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) { var emissive: vec3; - var param_108: vec3; + var param_114: vec3; - let _e179 = v_texcoord_1; - let _e180 = MaterialLodBias_u0028_(); - let _e181 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e179, _e180); - param_108 = _e181.xyz; - let _e183 = SrgbToLinear_u0028_vf3_u003b((¶m_108)); - emissive = _e183; - let _e184 = emissive; - let _e186 = frag_info.emissive; - let _e191 = frag_info.material2_[3u]; - (*s_6).emissive = ((_e184 * _e186.xyz) * _e191); + let _e188 = v_texcoord_1; + let _e189 = MaterialLodBias_u0028_(); + let _e190 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e188, _e189); + param_114 = _e190.xyz; + let _e192 = SrgbToLinear_u0028_vf3_u003b((¶m_114)); + emissive = _e192; + let _e193 = emissive; + let _e195 = frag_info.emissive; + let _e200 = frag_info.material2_[3u]; + (*s_7).emissive = ((_e193 * _e195.xyz) * _e200); return; } -fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) { +fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { var occlusion: f32; - let _e178 = v_texcoord_1; - let _e179 = MaterialLodBias_u0028_(); - let _e180 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e178, _e179); - occlusion = _e180.x; - let _e182 = occlusion; - let _e185 = frag_info.material2_[2u]; - (*s_7).occlusion = mix(1f, _e182, clamp(_e185, 0f, 1f)); + let _e187 = v_texcoord_1; + let _e188 = MaterialLodBias_u0028_(); + let _e189 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e187, _e188); + occlusion = _e189.x; + let _e191 = occlusion; + let _e194 = frag_info.material2_[2u]; + (*s_8).occlusion = mix(1f, _e191, clamp(_e194, 0f, 1f)); return; } -fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { +fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) { var sampledTexel: vec4; var t_2: vec3; var b_3: vec3; var sampled: vec3; - let _e181 = v_texcoord_1; - let _e182 = MaterialLodBias_u0028_(); - let _e183 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e181, _e182); - sampledTexel = _e183; - let _e184 = v_tangent_1; - t_2 = _e184.xyz; - let _e186 = t_2; - let _e188 = (*s_8).n; - let _e190 = (*s_8).n; - let _e191 = t_2; - t_2 = (_e186 - (_e188 * dot(_e190, _e191))); + let _e190 = v_texcoord_1; + let _e191 = MaterialLodBias_u0028_(); + let _e192 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e190, _e191); + sampledTexel = _e192; + let _e193 = v_tangent_1; + t_2 = _e193.xyz; let _e195 = t_2; - let _e196 = t_2; - if (dot(_e195, _e196) < 0.000000000001f) { + let _e197 = (*s_9).n; + let _e199 = (*s_9).n; + let _e200 = t_2; + t_2 = (_e195 - (_e197 * dot(_e199, _e200))); + let _e204 = t_2; + let _e205 = t_2; + if (dot(_e204, _e205) < 0.000000000001f) { return; } - let _e199 = t_2; - t_2 = normalize(_e199); - let _e202 = (*s_8).n; - let _e203 = t_2; - let _e206 = v_tangent_1[3u]; - b_3 = (cross(_e202, _e203) * _e206); - let _e208 = gl_FrontFacing_1; - if !(_e208) { - let _e210 = t_2; - t_2 = -(_e210); - } - let _e212 = sampledTexel; - sampled = ((_e212.xyz * 2f) - vec3(1f)); - let _e219 = frag_info.emissive[3u]; - if (_e219 > 0.5f) { - let _e221 = sampled; - let _e223 = sampled; - sampled[2u] = sqrt(max((1f - dot(_e221.xy, _e223.xy)), 0f)); - } - let _e232 = frag_info.material2_[1u]; - let _e233 = sampled; - let _e235 = (_e233.xy * _e232); - sampled[0u] = _e235.x; - sampled[1u] = _e235.y; - let _e240 = t_2; - let _e242 = sampled[0u]; - let _e244 = b_3; - let _e246 = sampled[1u]; - let _e250 = (*s_8).n; - let _e252 = sampled[2u]; - (*s_8).n = normalize((((_e240 * _e242) + (_e244 * _e246)) + (_e250 * _e252))); - let _e258 = (*s_8).n; - let _e260 = (*s_8).v; - (*s_8).n_dot_v = max(dot(_e258, _e260), 0.0001f); + let _e208 = t_2; + t_2 = normalize(_e208); + let _e211 = (*s_9).n; + let _e212 = t_2; + let _e215 = v_tangent_1[3u]; + b_3 = (cross(_e211, _e212) * _e215); + let _e217 = gl_FrontFacing_1; + if !(_e217) { + let _e219 = t_2; + t_2 = -(_e219); + } + let _e221 = sampledTexel; + sampled = ((_e221.xyz * 2f) - vec3(1f)); + let _e228 = frag_info.emissive[3u]; + if (_e228 > 0.5f) { + let _e230 = sampled; + let _e232 = sampled; + sampled[2u] = sqrt(max((1f - dot(_e230.xy, _e232.xy)), 0f)); + } + let _e241 = frag_info.material2_[1u]; + let _e242 = sampled; + let _e244 = (_e242.xy * _e241); + sampled[0u] = _e244.x; + sampled[1u] = _e244.y; + let _e249 = t_2; + let _e251 = sampled[0u]; + let _e253 = b_3; + let _e255 = sampled[1u]; + let _e259 = (*s_9).n; + let _e261 = sampled[2u]; + (*s_9).n = normalize((((_e249 * _e251) + (_e253 * _e255)) + (_e259 * _e261))); + let _e267 = (*s_9).n; + let _e269 = (*s_9).v; + (*s_9).n_dot_v = max(dot(_e267, _e269), 0.0001f); return; } fn AtlasTexel_u0028_vf2_u003b(texel_5: ptr>) -> vec4 { - let _e177 = (*texel_5); - let _e181 = irradiance_info.atlas; - let _e184 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e177 + vec2(0.5f)) * _e181.xy), 0f); - return _e184; + let _e186 = (*texel_5); + let _e190 = irradiance_info.atlas; + let _e193 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e186 + vec2(0.5f)) * _e190.xy), 0f); + return _e193; } fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr>, interior: ptr, uv_5: ptr>) -> vec4 { @@ -11327,50 +11770,50 @@ fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr; var f: vec2; var a_4: vec4; - var param_109: vec2; + var param_115: vec2; var b_4: vec4; - var param_110: vec2; - var c: vec4; - var param_111: vec2; + var param_116: vec2; + var c_1: vec4; + var param_117: vec2; var d_4: vec4; - var param_112: vec2; + var param_118: vec2; - let _e190 = (*uv_5); - let _e191 = (*interior); - at_1 = ((vec2(1f) + (_e190 * _e191)) - vec2(0.5f)); - let _e197 = at_1; - low = floor(_e197); - let _e199 = at_1; - let _e200 = low; - f = (_e199 - _e200); - let _e202 = (*corner); - let _e203 = low; - param_109 = (_e202 + _e203); - let _e205 = AtlasTexel_u0028_vf2_u003b((¶m_109)); - a_4 = _e205; - let _e206 = (*corner); - let _e207 = low; - param_110 = ((_e206 + _e207) + vec2(1f, 0f)); - let _e210 = AtlasTexel_u0028_vf2_u003b((¶m_110)); - b_4 = _e210; + let _e199 = (*uv_5); + let _e200 = (*interior); + at_1 = ((vec2(1f) + (_e199 * _e200)) - vec2(0.5f)); + let _e206 = at_1; + low = floor(_e206); + let _e208 = at_1; + let _e209 = low; + f = (_e208 - _e209); let _e211 = (*corner); let _e212 = low; - param_111 = ((_e211 + _e212) + vec2(0f, 1f)); - let _e215 = AtlasTexel_u0028_vf2_u003b((¶m_111)); - c = _e215; - let _e216 = (*corner); - let _e217 = low; - param_112 = ((_e216 + _e217) + vec2(1f, 1f)); - let _e220 = AtlasTexel_u0028_vf2_u003b((¶m_112)); - d_4 = _e220; - let _e221 = a_4; - let _e222 = b_4; - let _e224 = f[0u]; - let _e227 = c; - let _e228 = d_4; - let _e230 = f[0u]; - let _e234 = f[1u]; - return mix(mix(_e221, _e222, vec4(_e224)), mix(_e227, _e228, vec4(_e230)), vec4(_e234)); + param_115 = (_e211 + _e212); + let _e214 = AtlasTexel_u0028_vf2_u003b((¶m_115)); + a_4 = _e214; + let _e215 = (*corner); + let _e216 = low; + param_116 = ((_e215 + _e216) + vec2(1f, 0f)); + let _e219 = AtlasTexel_u0028_vf2_u003b((¶m_116)); + b_4 = _e219; + let _e220 = (*corner); + let _e221 = low; + param_117 = ((_e220 + _e221) + vec2(0f, 1f)); + let _e224 = AtlasTexel_u0028_vf2_u003b((¶m_117)); + c_1 = _e224; + let _e225 = (*corner); + let _e226 = low; + param_118 = ((_e225 + _e226) + vec2(1f, 1f)); + let _e229 = AtlasTexel_u0028_vf2_u003b((¶m_118)); + d_4 = _e229; + let _e230 = a_4; + let _e231 = b_4; + let _e233 = f[0u]; + let _e236 = c_1; + let _e237 = d_4; + let _e239 = f[0u]; + let _e243 = f[1u]; + return mix(mix(_e230, _e231, vec4(_e233)), mix(_e236, _e237, vec4(_e239)), vec4(_e243)); } fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec2 { @@ -11378,32 +11821,32 @@ fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec var n_5: vec3; var xy: vec2; - let _e181 = (*direction_1)[0u]; - let _e184 = (*direction_1)[1u]; - let _e188 = (*direction_1)[2u]; - sum_1 = ((abs(_e181) + abs(_e184)) + abs(_e188)); - let _e191 = sum_1; - if (_e191 <= 0f) { + let _e190 = (*direction_1)[0u]; + let _e193 = (*direction_1)[1u]; + let _e197 = (*direction_1)[2u]; + sum_1 = ((abs(_e190) + abs(_e193)) + abs(_e197)); + let _e200 = sum_1; + if (_e200 <= 0f) { return vec2(0.5f, 0.5f); } - let _e193 = (*direction_1); - let _e194 = sum_1; - n_5 = (_e193 / vec3(_e194)); - let _e197 = n_5; - xy = _e197.xy; - let _e200 = n_5[2u]; - if (_e200 < 0f) { - let _e203 = n_5[1u]; - let _e207 = n_5[0u]; - let _e212 = n_5[0u]; - let _e216 = n_5[1u]; - xy = vec2(((1f - abs(_e203)) * select(-1f, 1f, (_e207 >= 0f))), ((1f - abs(_e212)) * select(-1f, 1f, (_e216 >= 0f)))); - } - let _e221 = xy; - return ((_e221 * 0.5f) + vec2(0.5f)); + let _e202 = (*direction_1); + let _e203 = sum_1; + n_5 = (_e202 / vec3(_e203)); + let _e206 = n_5; + xy = _e206.xy; + let _e209 = n_5[2u]; + if (_e209 < 0f) { + let _e212 = n_5[1u]; + let _e216 = n_5[0u]; + let _e221 = n_5[0u]; + let _e225 = n_5[1u]; + xy = vec2(((1f - abs(_e212)) * select(-1f, 1f, (_e216 >= 0f))), ((1f - abs(_e221)) * select(-1f, 1f, (_e225 >= 0f)))); + } + let _e230 = xy; + return ((_e230 * 0.5f) + vec2(0.5f)); } -fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view: ptr>) -> vec3 { +fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view_1: ptr>) -> vec3 { var origin_2: vec3; var spacing: vec3; var counts: vec3; @@ -11425,9 +11868,9 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var irradianceCorner: vec2; var momentCorner: vec2; - var param_113: vec2; + var param_119: vec2; var trilinear: vec3; - var weight_2: f32; + var weight_3: f32; var probePosition: vec3; var toProbe: vec3; var distance_4: f32; @@ -11435,335 +11878,338 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; - var param_114: vec3; - var param_115: vec2; - var param_116: f32; - var param_117: vec2; + var param_120: vec3; + var param_121: vec2; + var param_122: f32; + var param_123: vec2; var chebyshev: f32; var variance_1: f32; var difference: f32; - var param_118: vec3; - var param_119: vec2; - var param_120: f32; - var param_121: vec2; - var local_24: vec3; - - let _e223 = irradiance_info.origin; - origin_2 = _e223.xyz; - let _e226 = irradiance_info.spacing; - spacing = _e226.xyz; - let _e229 = irradiance_info.counts; - counts = _e229.xyz; - let _e233 = irradiance_info.tiles[0u]; - irradianceTile = _e233; - let _e236 = irradiance_info.tiles[1u]; - depthTile = _e236; - let _e239 = irradiance_info.tiles[2u]; - columns = _e239; - let _e242 = irradiance_info.tiles[3u]; - momentsTop = _e242; - let _e243 = (*normal_1); - unit = normalize(_e243); - let _e245 = (*world_4); - let _e246 = unit; - let _e249 = irradiance_info.spacing[3u]; - let _e252 = (*view); - let _e255 = irradiance_info.counts[3u]; - biased = ((_e245 + (_e246 * _e249)) + (_e252 * _e255)); - let _e258 = biased; - let _e259 = origin_2; - let _e261 = spacing; - grid_1 = ((_e258 - _e259) / _e261); - let _e263 = grid_1; - let _e265 = counts; - base = clamp(floor(_e263), vec3(0f, 0f, 0f), (_e265 - vec3(2f))); - let _e269 = grid_1; - let _e270 = base; - f_1 = clamp((_e269 - _e270), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + var param_124: vec3; + var param_125: vec2; + var param_126: f32; + var param_127: vec2; + var local_26: vec3; + + let _e232 = irradiance_info.origin; + origin_2 = _e232.xyz; + let _e235 = irradiance_info.spacing; + spacing = _e235.xyz; + let _e238 = irradiance_info.counts; + counts = _e238.xyz; + let _e242 = irradiance_info.tiles[0u]; + irradianceTile = _e242; + let _e245 = irradiance_info.tiles[1u]; + depthTile = _e245; + let _e248 = irradiance_info.tiles[2u]; + columns = _e248; + let _e251 = irradiance_info.tiles[3u]; + momentsTop = _e251; + let _e252 = (*normal_1); + unit = normalize(_e252); + let _e254 = (*world_4); + let _e255 = unit; + let _e258 = irradiance_info.spacing[3u]; + let _e261 = (*view_1); + let _e264 = irradiance_info.counts[3u]; + biased = ((_e254 + (_e255 * _e258)) + (_e261 * _e264)); + let _e267 = biased; + let _e268 = origin_2; + let _e270 = spacing; + grid_1 = ((_e267 - _e268) / _e270); + let _e272 = grid_1; + let _e274 = counts; + base = clamp(floor(_e272), vec3(0f, 0f, 0f), (_e274 - vec3(2f))); + let _e278 = grid_1; + let _e279 = base; + f_1 = clamp((_e278 - _e279), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); total_2 = vec3(0f, 0f, 0f); weights = 0f; corner_1 = 0i; loop { - let _e273 = corner_1; - if (_e273 < 8i) { - let _e275 = corner_1; - let _e278 = corner_1; - let _e283 = corner_1; - offset_3 = vec3(f32((_e275 & 1i)), f32(((_e278 >> bitcast(1i)) & 1i)), f32(((_e283 >> bitcast(2i)) & 1i))); - let _e289 = base; - let _e290 = offset_3; - cell_1 = (_e289 + _e290); - let _e293 = cell_1[2u]; - let _e295 = counts[1u]; - let _e298 = cell_1[1u]; - let _e301 = counts[0u]; - let _e304 = cell_1[0u]; - probe = ((((_e293 * _e295) + _e298) * _e301) + _e304); - let _e306 = probe; - let _e307 = columns; - let _e312 = probe; - let _e313 = columns; - tile_4 = vec2((_e306 - (floor((_e306 / _e307)) * _e307)), floor((_e312 / _e313))); - let _e317 = tile_4; - let _e318 = irradianceTile; - irradianceCorner = (_e317 * (_e318 + 2f)); - let _e322 = tile_4[0u]; - let _e323 = depthTile; - let _e326 = momentsTop; - let _e328 = tile_4[1u]; - let _e329 = depthTile; - momentCorner = vec2((_e322 * (_e323 + 2f)), (_e326 + (_e328 * (_e329 + 2f)))); - let _e334 = irradianceCorner; - param_113 = (_e334 + vec2(1f)); - let _e337 = AtlasTexel_u0028_vf2_u003b((¶m_113)); - if (_e337.w < 0.5f) { + let _e282 = corner_1; + if (_e282 < 8i) { + let _e284 = corner_1; + let _e287 = corner_1; + let _e292 = corner_1; + offset_3 = vec3(f32((_e284 & 1i)), f32(((_e287 >> bitcast(1i)) & 1i)), f32(((_e292 >> bitcast(2i)) & 1i))); + let _e298 = base; + let _e299 = offset_3; + cell_1 = (_e298 + _e299); + let _e302 = cell_1[2u]; + let _e304 = counts[1u]; + let _e307 = cell_1[1u]; + let _e310 = counts[0u]; + let _e313 = cell_1[0u]; + probe = ((((_e302 * _e304) + _e307) * _e310) + _e313); + let _e315 = probe; + let _e316 = columns; + let _e321 = probe; + let _e322 = columns; + tile_4 = vec2((_e315 - (floor((_e315 / _e316)) * _e316)), floor((_e321 / _e322))); + let _e326 = tile_4; + let _e327 = irradianceTile; + irradianceCorner = (_e326 * (_e327 + 2f)); + let _e331 = tile_4[0u]; + let _e332 = depthTile; + let _e335 = momentsTop; + let _e337 = tile_4[1u]; + let _e338 = depthTile; + momentCorner = vec2((_e331 * (_e332 + 2f)), (_e335 + (_e337 * (_e338 + 2f)))); + let _e343 = irradianceCorner; + param_119 = (_e343 + vec2(1f)); + let _e346 = AtlasTexel_u0028_vf2_u003b((¶m_119)); + if (_e346.w < 0.5f) { continue; } - let _e340 = f_1; - let _e342 = f_1; - let _e343 = offset_3; - trilinear = mix((vec3(1f, 1f, 1f) - _e340), _e342, _e343); - let _e346 = trilinear[0u]; - let _e348 = trilinear[1u]; - let _e351 = trilinear[2u]; - weight_2 = max(((_e346 * _e348) * _e351), 0.001f); - let _e354 = origin_2; - let _e355 = spacing; - let _e356 = cell_1; - probePosition = (_e354 + (_e355 * _e356)); - let _e359 = probePosition; - let _e360 = biased; - toProbe = (_e359 - _e360); - let _e362 = toProbe; - distance_4 = length(_e362); - let _e364 = distance_4; - if (_e364 > 0.000001f) { - let _e366 = toProbe; - let _e367 = distance_4; - direction_2 = (_e366 / vec3(_e367)); - let _e370 = unit; - let _e371 = probePosition; - let _e372 = (*world_4); - facing = ((dot(_e370, normalize((_e371 - _e372))) * 0.5f) + 0.5f); - let _e378 = facing; - let _e379 = facing; - probeWeight = ((_e378 * _e379) + 0.2f); - let _e382 = direction_2; - param_114 = -(_e382); - let _e384 = ProbeOctahedral_u0028_vf3_u003b((¶m_114)); - let _e385 = momentCorner; - param_115 = _e385; - let _e386 = depthTile; - param_116 = _e386; - param_117 = _e384; - let _e387 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_115), (¶m_116), (¶m_117)); - moments_3 = _e387.xy; + let _e349 = f_1; + let _e351 = f_1; + let _e352 = offset_3; + trilinear = mix((vec3(1f, 1f, 1f) - _e349), _e351, _e352); + let _e355 = trilinear[0u]; + let _e357 = trilinear[1u]; + let _e360 = trilinear[2u]; + weight_3 = max(((_e355 * _e357) * _e360), 0.001f); + let _e363 = origin_2; + let _e364 = spacing; + let _e365 = cell_1; + probePosition = (_e363 + (_e364 * _e365)); + let _e368 = probePosition; + let _e369 = biased; + toProbe = (_e368 - _e369); + let _e371 = toProbe; + distance_4 = length(_e371); + let _e373 = distance_4; + if (_e373 > 0.000001f) { + let _e375 = toProbe; + let _e376 = distance_4; + direction_2 = (_e375 / vec3(_e376)); + let _e379 = unit; + let _e380 = probePosition; + let _e381 = (*world_4); + facing = ((dot(_e379, normalize((_e380 - _e381))) * 0.5f) + 0.5f); + let _e387 = facing; + let _e388 = facing; + probeWeight = ((_e387 * _e388) + 0.2f); + let _e391 = direction_2; + param_120 = -(_e391); + let _e393 = ProbeOctahedral_u0028_vf3_u003b((¶m_120)); + let _e394 = momentCorner; + param_121 = _e394; + let _e395 = depthTile; + param_122 = _e395; + param_123 = _e393; + let _e396 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_121), (¶m_122), (¶m_123)); + moments_3 = _e396.xy; chebyshev = 1f; - let _e389 = distance_4; - let _e391 = moments_3[0u]; - if (_e389 > _e391) { - let _e394 = moments_3[1u]; - let _e396 = moments_3[0u]; - let _e398 = moments_3[0u]; - variance_1 = max((_e394 - (_e396 * _e398)), 0.000001f); - let _e402 = distance_4; - let _e404 = moments_3[0u]; - difference = (_e402 - _e404); - let _e406 = variance_1; - let _e407 = variance_1; - let _e408 = difference; - let _e409 = difference; - chebyshev = (_e406 / (_e407 + (_e408 * _e409))); - let _e413 = chebyshev; - let _e414 = chebyshev; - let _e416 = chebyshev; - chebyshev = ((_e413 * _e414) * _e416); + let _e398 = distance_4; + let _e400 = moments_3[0u]; + if (_e398 > _e400) { + let _e403 = moments_3[1u]; + let _e405 = moments_3[0u]; + let _e407 = moments_3[0u]; + variance_1 = max((_e403 - (_e405 * _e407)), 0.000001f); + let _e411 = distance_4; + let _e413 = moments_3[0u]; + difference = (_e411 - _e413); + let _e415 = variance_1; + let _e416 = variance_1; + let _e417 = difference; + let _e418 = difference; + chebyshev = (_e415 / (_e416 + (_e417 * _e418))); + let _e422 = chebyshev; + let _e423 = chebyshev; + let _e425 = chebyshev; + chebyshev = ((_e422 * _e423) * _e425); } - let _e418 = probeWeight; - let _e419 = chebyshev; - probeWeight = max((_e418 * max(_e419, 0.05f)), 0.000001f); - let _e423 = probeWeight; - if (_e423 < 0.2f) { - let _e425 = probeWeight; - let _e426 = probeWeight; - let _e429 = probeWeight; - probeWeight = (_e429 * ((_e425 * _e426) * 25f)); + let _e427 = probeWeight; + let _e428 = chebyshev; + probeWeight = max((_e427 * max(_e428, 0.05f)), 0.000001f); + let _e432 = probeWeight; + if (_e432 < 0.2f) { + let _e434 = probeWeight; + let _e435 = probeWeight; + let _e438 = probeWeight; + probeWeight = (_e438 * ((_e434 * _e435) * 25f)); } - let _e431 = probeWeight; - let _e432 = weight_2; - weight_2 = (_e432 * _e431); - } - let _e434 = unit; - param_118 = _e434; - let _e435 = ProbeOctahedral_u0028_vf3_u003b((¶m_118)); - let _e436 = irradianceCorner; - param_119 = _e436; - let _e437 = irradianceTile; - param_120 = _e437; - param_121 = _e435; - let _e438 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_119), (¶m_120), (¶m_121)); - let _e440 = weight_2; - let _e442 = total_2; - total_2 = (_e442 + (_e438.xyz * _e440)); - let _e444 = weight_2; - let _e445 = weights; - weights = (_e445 + _e444); + let _e440 = probeWeight; + let _e441 = weight_3; + weight_3 = (_e441 * _e440); + } + let _e443 = unit; + param_124 = _e443; + let _e444 = ProbeOctahedral_u0028_vf3_u003b((¶m_124)); + let _e445 = irradianceCorner; + param_125 = _e445; + let _e446 = irradianceTile; + param_126 = _e446; + param_127 = _e444; + let _e447 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_125), (¶m_126), (¶m_127)); + let _e449 = weight_3; + let _e451 = total_2; + total_2 = (_e451 + (_e447.xyz * _e449)); + let _e453 = weight_3; + let _e454 = weights; + weights = (_e454 + _e453); continue; } else { break; } continuing { - let _e447 = corner_1; - corner_1 = (_e447 + 1i); + let _e456 = corner_1; + corner_1 = (_e456 + 1i); } } - let _e449 = weights; - if (_e449 > 0f) { - let _e451 = total_2; - let _e452 = weights; - local_24 = (_e451 / vec3(_e452)); + let _e458 = weights; + if (_e458 > 0f) { + let _e460 = total_2; + let _e461 = weights; + local_26 = (_e460 / vec3(_e461)); } else { - local_24 = vec3(0f, 0f, 0f); + local_26 = vec3(0f, 0f, 0f); } - let _e455 = local_24; - return _e455; + let _e464 = local_26; + return _e464; } fn IrradianceEnabled_u0028_() -> bool { - let _e178 = irradiance_info.origin[3u]; - return (_e178 > 0.5f); + let _e187 = irradiance_info.origin[3u]; + return (_e187 > 0.5f); } -fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) { - var param_122: vec3; - var param_123: vec3; - var param_124: vec3; - var param_125: Surface; - var param_126: Surface; - var param_127: Surface; - - let _e183 = IrradianceEnabled_u0028_(); - if _e183 { - let _e184 = v_world_position_1; - param_122 = _e184; - let _e186 = (*s_9).n; - param_123 = _e186; - let _e188 = (*s_9).v; - param_124 = _e188; - let _e189 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_122), (¶m_123), (¶m_124)); - let _e192 = frag_info.material[2u]; - (*s_9).ambient = (_e189 * _e192); - } - let _e195 = (*s_9); - param_125 = _e195; - ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_125)); - let _e196 = param_125; - (*s_9) = _e196; - let _e197 = (*s_9); - param_126 = _e197; - ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_126)); - let _e198 = param_126; - (*s_9) = _e198; - let _e199 = (*s_9); - param_127 = _e199; - ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_127)); - let _e200 = param_127; - (*s_9) = _e200; +fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_10: ptr) { + var param_128: vec3; + var param_129: vec3; + var param_130: vec3; + var param_131: Surface; + var param_132: Surface; + var param_133: Surface; + + let _e192 = IrradianceEnabled_u0028_(); + if _e192 { + let _e193 = v_world_position_1; + param_128 = _e193; + let _e195 = (*s_10).n; + param_129 = _e195; + let _e197 = (*s_10).v; + param_130 = _e197; + let _e198 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_128), (¶m_129), (¶m_130)); + let _e201 = frag_info.material[2u]; + (*s_10).ambient = (_e198 * _e201); + } + let _e204 = (*s_10); + param_131 = _e204; + ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_131)); + let _e205 = param_131; + (*s_10) = _e205; + let _e206 = (*s_10); + param_132 = _e206; + ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_132)); + let _e207 = param_132; + (*s_10) = _e207; + let _e208 = (*s_10); + param_133 = _e208; + ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_133)); + let _e209 = param_133; + (*s_10) = _e209; return; } fn ReadSurface_u0028_() -> Surface { var texel_6: vec4; - var s_10: Surface; - var param_128: vec3; - var param_129: vec3; + var s_11: Surface; + var param_134: vec3; + var param_135: vec3; var cutoff: f32; var anchored: vec3; var noise_1: f32; - let _e183 = v_texcoord_1; - let _e184 = MaterialLodBias_u0028_(); - let _e185 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e183, _e184); - texel_6 = _e185; - let _e186 = texel_6; - param_128 = _e186.xyz; - let _e188 = SrgbToLinear_u0028_vf3_u003b((¶m_128)); - let _e190 = frag_info.base_color; - param_129 = _e190.xyz; - let _e192 = SrgbToLinear_u0028_vf3_u003b((¶m_129)); - let _e194 = v_color_1; - s_10.albedo = ((_e188 * _e192) * _e194.xyz); - let _e199 = texel_6[3u]; - let _e202 = frag_info.base_color[3u]; - let _e205 = v_color_1[3u]; - s_10.alpha = ((_e199 * _e202) * _e205); - let _e209 = s_10.albedo; - g_albedo = _e209; - let _e212 = frag_info.material2_[0u]; - cutoff = _e212; - let _e213 = cutoff; - if (_e213 >= 0f) { - let _e216 = s_10.alpha; - let _e217 = cutoff; - if (_e216 < _e217) { + let _e192 = v_texcoord_1; + let _e193 = MaterialLodBias_u0028_(); + let _e194 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e192, _e193); + texel_6 = _e194; + let _e195 = texel_6; + param_134 = _e195.xyz; + let _e197 = SrgbToLinear_u0028_vf3_u003b((¶m_134)); + let _e199 = frag_info.base_color; + param_135 = _e199.xyz; + let _e201 = SrgbToLinear_u0028_vf3_u003b((¶m_135)); + let _e203 = v_color_1; + s_11.albedo = ((_e197 * _e201) * _e203.xyz); + let _e208 = texel_6[3u]; + let _e211 = frag_info.base_color[3u]; + let _e214 = v_color_1[3u]; + s_11.alpha = ((_e208 * _e211) * _e214); + let _e218 = s_11.albedo; + g_albedo = _e218; + let _e221 = frag_info.material2_[0u]; + cutoff = _e221; + let _e222 = cutoff; + if (_e222 >= 0f) { + let _e225 = s_11.alpha; + let _e226 = cutoff; + if (_e225 < _e226) { discard; } } else { - let _e219 = cutoff; - if (_e219 < -1.5f) { - let _e221 = v_world_position_1; - anchored = floor((_e221 * 16f)); - let _e224 = anchored; - noise_1 = fract((sin(dot(_e224, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e230 = s_10.alpha; - let _e231 = noise_1; - if (_e230 < _e231) { + let _e228 = cutoff; + if (_e228 < -1.5f) { + let _e230 = v_world_position_1; + anchored = floor((_e230 * 16f)); + let _e233 = anchored; + noise_1 = fract((sin(dot(_e233, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e239 = s_11.alpha; + let _e240 = noise_1; + if (_e239 < _e240) { discard; } } } - let _e233 = cutoff; - let _e235 = cutoff; - g_premultiply = ((_e233 < 0f) && (_e235 > -0.75f)); - let _e238 = v_normal_1; - s_10.n = normalize(_e238); - let _e241 = gl_FrontFacing_1; - if !(_e241) { - let _e244 = s_10.n; - s_10.n = -(_e244); + let _e242 = cutoff; + let _e244 = cutoff; + g_premultiply = ((_e242 < 0f) && (_e244 > -0.75f)); + let _e247 = v_normal_1; + s_11.n = normalize(_e247); + let _e250 = gl_FrontFacing_1; + if !(_e250) { + let _e253 = s_11.n; + s_11.n = -(_e253); } - let _e248 = frag_info.camera_position; - let _e250 = v_world_position_1; - s_10.v = normalize((_e248.xyz - _e250)); - let _e255 = s_10.n; - let _e257 = s_10.v; - s_10.n_dot_v = max(dot(_e255, _e257), 0.0001f); - let _e263 = frag_info.material[0u]; - s_10.metallic = clamp(_e263, 0f, 1f); - let _e268 = frag_info.material[1u]; - s_10.roughness = clamp(_e268, 0.02f, 1f); - let _e272 = frag_info.ambient_ground; - let _e275 = frag_info.ambient_sky; - let _e279 = s_10.n[1u]; - let _e286 = frag_info.material[2u]; - s_10.ambient = (mix(_e272.xyz, _e275.xyz, vec3(((_e279 * 0.5f) + 0.5f))) * _e286); - let _e291 = frag_info.frame_params[0u]; - s_10.exposure = max(_e291, 0f); - s_10.occlusion = 1f; - s_10.emissive = vec3(0f, 0f, 0f); - let _e296 = s_10; - return _e296; + let _e257 = frag_info.camera_position; + let _e259 = v_world_position_1; + s_11.v = normalize((_e257.xyz - _e259)); + let _e264 = s_11.n; + let _e266 = s_11.v; + s_11.n_dot_v = max(dot(_e264, _e266), 0.0001f); + let _e272 = frag_info.material[0u]; + s_11.metallic = clamp(_e272, 0f, 1f); + let _e277 = frag_info.material[1u]; + s_11.roughness = clamp(_e277, 0.02f, 1f); + let _e281 = frag_info.ambient_ground; + let _e284 = frag_info.ambient_sky; + let _e288 = s_11.n[1u]; + let _e295 = frag_info.material[2u]; + s_11.ambient = (mix(_e281.xyz, _e284.xyz, vec3(((_e288 * 0.5f) + 0.5f))) * _e295); + let _e300 = frag_info.frame_params[0u]; + s_11.exposure = max(_e300, 0f); + s_11.occlusion = 1f; + s_11.emissive = vec3(0f, 0f, 0f); + let _e305 = s_11; + return _e305; } fn main_1() { - var s_11: Surface; - var param_130: Surface; - var param_131: Surface; + var s_12: Surface; + var param_136: Surface; + var param_137: Surface; var ambient: vec3; - var param_132: Surface; - var param_133: vec3; - var param_134: f32; - var param_135: f32; + var lit_3: vec3; + var param_138: Surface; + var param_139: Surface; + var param_140: vec3; + var param_141: vec3; + var param_142: f32; + var param_143: f32; g_albedo = vec3(0f, 0f, 0f); g_debug_surface = vec3(0f, 0f, 0f); @@ -11771,35 +12217,45 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e184 = ReadSurface_u0028_(); - s_11 = _e184; - let _e185 = s_11; - param_130 = _e185; - ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_130)); - let _e186 = param_130; - s_11 = _e186; - let _e187 = s_11; - param_131 = _e187; - ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_131)); - let _e188 = param_131; - s_11 = _e188; - let _e190 = s_11.albedo; - let _e192 = s_11.ambient; - let _e193 = SampleLightmap_u0028_(); - let _e197 = s_11.occlusion; - ambient = ((_e190 * (_e192 + _e193)) * _e197); - let _e199 = s_11; - param_132 = _e199; - let _e200 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_132)); - let _e202 = s_11.occlusion; - let _e204 = ambient; - let _e207 = s_11.emissive; - param_133 = (((_e200 * _e202) + _e204) + _e207); - let _e210 = s_11.alpha; - param_134 = _e210; - let _e212 = s_11.roughness; - param_135 = _e212; - WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_133), (¶m_134), (¶m_135)); + let _e196 = ReadSurface_u0028_(); + s_12 = _e196; + let _e197 = s_12; + param_136 = _e197; + ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_136)); + let _e198 = param_136; + s_12 = _e198; + let _e199 = s_12; + param_137 = _e199; + ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_137)); + let _e200 = param_137; + s_12 = _e200; + let _e202 = s_12.albedo; + let _e204 = s_12.ambient; + let _e205 = SampleLightmap_u0028_(); + let _e209 = s_12.occlusion; + ambient = ((_e202 * (_e204 + _e205)) * _e209); + let _e211 = s_12; + param_138 = _e211; + let _e212 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_138)); + let _e214 = s_12.occlusion; + let _e216 = ambient; + let _e219 = s_12.emissive; + lit_3 = (((_e212 * _e214) + _e216) + _e219); + let _e221 = s_12; + param_139 = _e221; + let _e222 = lit_3; + param_140 = _e222; + let _e223 = WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b((¶m_139), (¶m_140)); + if _e223 { + return; + } + let _e224 = lit_3; + param_141 = _e224; + let _e226 = s_12.alpha; + param_142 = _e226; + let _e228 = s_12.roughness; + param_143 = _e228; + WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_141), (¶m_142), (¶m_143)); return; } @@ -11841,7 +12297,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember( name: 'light_position', @@ -11938,6 +12394,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, shadow_bias: vec4, target_origin: vec4, + debug_view: vec4, } struct PointShadow { @@ -12300,10 +12762,10 @@ var environment_texture_tex: texture_cube; var environment_texture_smp: sampler; fn ViewDepth_u0028_() -> f32 { - let _e195 = v_world_position_1; - let _e197 = fog_info.eye; - let _e201 = fog_info.forward; - return dot((_e195 - _e197.xyz), _e201.xyz); + let _e204 = v_world_position_1; + let _e206 = fog_info.eye; + let _e210 = fog_info.forward; + return dot((_e204 - _e206.xyz), _e210.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -12312,22 +12774,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e200 = ViewDepth_u0028_(); - z = abs(_e200); - let _e202 = z; - near = (_e202 / 5f); - let _e204 = z; - far = (_e204 / 200f); - let _e206 = far; - let _e207 = far; - let _e209 = far; - far3_ = ((_e206 * _e207) * _e209); - let _e211 = (*alpha); - let _e212 = near; - let _e213 = near; - let _e216 = far3_; - let _e217 = far3_; - return (_e211 * clamp((10f / ((0.00001f + (_e212 * _e213)) + (_e216 * _e217))), 0.01f, 3000f)); + let _e209 = ViewDepth_u0028_(); + z = abs(_e209); + let _e211 = z; + near = (_e211 / 5f); + let _e213 = z; + far = (_e213 / 200f); + let _e215 = far; + let _e216 = far; + let _e218 = far; + far3_ = ((_e215 * _e216) * _e218); + let _e220 = (*alpha); + let _e221 = near; + let _e222 = near; + let _e225 = far3_; + let _e226 = far3_; + return (_e220 * clamp((10f / ((0.00001f + (_e221 * _e222)) + (_e225 * _e226))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -12339,39 +12801,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e204 = fog_info.forward[3u]; - mode = _e204; - let _e205 = mode; - if (_e205 > 0.5f) { - let _e208 = frag_color[3u]; - alpha_1 = _e208; - let _e209 = alpha_1; - param = _e209; - let _e210 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e210; - let _e211 = frag_color; - let _e213 = weight; - let _e214 = (_e211.xyz * _e213); - let _e215 = alpha_1; - let _e216 = weight; - accumulate = vec4(_e214.x, _e214.y, _e214.z, (_e215 * _e216)); - let _e222 = mode; - let _e224 = mode; - revealage = ((_e222 > 1.5f) && (_e224 < 2.5f)); - let _e227 = revealage; - if _e227 { - let _e228 = alpha_1; - local = vec4(_e228); + let _e213 = fog_info.forward[3u]; + mode = _e213; + let _e214 = mode; + if (_e214 > 0.5f) { + let _e217 = frag_color[3u]; + alpha_1 = _e217; + let _e218 = alpha_1; + param = _e218; + let _e219 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e219; + let _e220 = frag_color; + let _e222 = weight; + let _e223 = (_e220.xyz * _e222); + let _e224 = alpha_1; + let _e225 = weight; + accumulate = vec4(_e223.x, _e223.y, _e223.z, (_e224 * _e225)); + let _e231 = mode; + let _e233 = mode; + revealage = ((_e231 > 1.5f) && (_e233 < 2.5f)); + let _e236 = revealage; + if _e236 { + let _e237 = alpha_1; + local = vec4(_e237); } else { - let _e230 = accumulate; - local = _e230; + let _e239 = accumulate; + local = _e239; } - let _e231 = local; - frag_color = _e231; - let _e232 = mode; - if (_e232 > 2.5f) { - let _e234 = alpha_1; - frag_surface = vec4(_e234); + let _e240 = local; + frag_color = _e240; + let _e241 = mode; + if (_e241 > 2.5f) { + let _e243 = alpha_1; + frag_surface = vec4(_e243); } } return; @@ -12380,29 +12842,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e198 = (*n)[0u]; - let _e201 = (*n)[1u]; - let _e205 = (*n)[2u]; - let _e208 = (*n); - (*n) = (_e208 / vec3(((abs(_e198) + abs(_e201)) + abs(_e205)))); - let _e211 = (*n); - e = _e211.xy; + let _e207 = (*n)[0u]; + let _e210 = (*n)[1u]; let _e214 = (*n)[2u]; - if (_e214 < 0f) { - let _e216 = (*n); - let _e222 = (*n)[0u]; - let _e226 = (*n)[1u]; - e = ((vec2(1f) - abs(_e216.yx)) * vec2(select(-1f, 1f, (_e222 >= 0f)), select(-1f, 1f, (_e226 >= 0f)))); - } - let _e231 = e; - return ((_e231 * 0.5f) + vec2(0.5f)); + let _e217 = (*n); + (*n) = (_e217 / vec3(((abs(_e207) + abs(_e210)) + abs(_e214)))); + let _e220 = (*n); + e = _e220.xy; + let _e223 = (*n)[2u]; + if (_e223 < 0f) { + let _e225 = (*n); + let _e231 = (*n)[0u]; + let _e235 = (*n)[1u]; + e = ((vec2(1f) - abs(_e225.yx)) * vec2(select(-1f, 1f, (_e231 >= 0f)), select(-1f, 1f, (_e235 >= 0f)))); + } + let _e240 = e; + return ((_e240 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e196 = (*linear); - let _e198 = (*linear); - let _e202 = (*linear); - return mix((_e196 * 12.92f), ((pow(_e198, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e202)); + let _e205 = (*linear); + let _e207 = (*linear); + let _e211 = (*linear); + return mix((_e205 * 12.92f), ((pow(_e207, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e211)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -12410,37 +12872,37 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e199 = g_albedo; - param_1 = clamp(_e199, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e201 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e201.x, _e201.y, _e201.z, 1f); - let _e206 = g_debug_surface_on; - if _e206 { - let _e207 = g_debug_surface; - let _e208 = ViewDepth_u0028_(); - frag_surface = vec4(_e207.x, _e207.y, _e207.z, _e208); + let _e208 = g_albedo; + param_1 = clamp(_e208, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e210 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e210.x, _e210.y, _e210.z, 1f); + let _e215 = g_debug_surface_on; + if _e215 { + let _e216 = g_debug_surface; + let _e217 = ViewDepth_u0028_(); + frag_surface = vec4(_e216.x, _e216.y, _e216.z, _e217); return; } - let _e213 = v_normal_1; - geometric = normalize(_e213); - let _e215 = gl_FrontFacing_1; - if !(_e215) { - let _e217 = geometric; - geometric = -(_e217); - } - let _e219 = geometric; - param_2 = _e219; - let _e220 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e221 = (*roughness); - let _e223 = ViewDepth_u0028_(); - frag_surface = vec4(_e220.x, _e220.y, clamp(_e221, 0f, 1f), _e223); + let _e222 = v_normal_1; + geometric = normalize(_e222); + let _e224 = gl_FrontFacing_1; + if !(_e224) { + let _e226 = geometric; + geometric = -(_e226); + } + let _e228 = geometric; + param_2 = _e228; + let _e229 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e230 = (*roughness); + let _e232 = ViewDepth_u0028_(); + frag_surface = vec4(_e229.x, _e229.y, clamp(_e230, 0f, 1f), _e232); return; } fn EyeDistance_u0028_() -> f32 { - let _e195 = v_world_position_1; - let _e197 = fog_info.eye; - return distance(_e195, _e197.xyz); + let _e204 = v_world_position_1; + let _e206 = fog_info.eye; + return distance(_e204, _e206.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -12450,41 +12912,41 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var k: f32; var local_1: f32; - let _e203 = fog_info.fog[3u]; - density = _e203; - let _e204 = density; - if (_e204 <= 0f) { - let _e206 = (*color); - return _e206; - } - let _e207 = EyeDistance_u0028_(); - d = _e207; - let _e210 = fog_info.eye[3u]; - falloff = _e210; - let _e211 = falloff; - if (_e211 > 0f) { - let _e213 = falloff; - let _e215 = v_world_position_1[1u]; - let _e218 = fog_info.eye[1u]; - k = (_e213 * (_e215 - _e218)); - let _e221 = k; - if (abs(_e221) > 0.001f) { - let _e224 = k; - let _e228 = k; - local_1 = ((1f - exp(-(_e224))) / _e228); + let _e212 = fog_info.fog[3u]; + density = _e212; + let _e213 = density; + if (_e213 <= 0f) { + let _e215 = (*color); + return _e215; + } + let _e216 = EyeDistance_u0028_(); + d = _e216; + let _e219 = fog_info.eye[3u]; + falloff = _e219; + let _e220 = falloff; + if (_e220 > 0f) { + let _e222 = falloff; + let _e224 = v_world_position_1[1u]; + let _e227 = fog_info.eye[1u]; + k = (_e222 * (_e224 - _e227)); + let _e230 = k; + if (abs(_e230) > 0.001f) { + let _e233 = k; + let _e237 = k; + local_1 = ((1f - exp(-(_e233))) / _e237); } else { - let _e230 = k; - local_1 = (1f - (0.5f * _e230)); + let _e239 = k; + local_1 = (1f - (0.5f * _e239)); } - let _e233 = local_1; - let _e234 = density; - density = (_e234 * _e233); + let _e242 = local_1; + let _e243 = density; + density = (_e243 * _e242); } - let _e237 = fog_info.fog; - let _e239 = (*color); - let _e240 = density; - let _e242 = d; - return mix(_e237.xyz, _e239, vec3(clamp(exp((-(_e240) * _e242)), 0f, 1f))); + let _e246 = fog_info.fog; + let _e248 = (*color); + let _e249 = density; + let _e251 = d; + return mix(_e246.xyz, _e248, vec3(clamp(exp((-(_e249) * _e251)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -12492,191 +12954,331 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e201 = g_premultiply; - let _e202 = (*alpha_2); - weight_1 = select(1f, _e202, _e201); - let _e204 = (*linearColor); - param_3 = _e204; - let _e205 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e206 = weight_1; - let _e207 = (_e205 * _e206); - let _e208 = (*alpha_2); - frag_color = vec4(_e207.x, _e207.y, _e207.z, _e208); - let _e213 = (*roughness_1); - param_4 = _e213; + let _e210 = g_premultiply; + let _e211 = (*alpha_2); + weight_1 = select(1f, _e211, _e210); + let _e213 = (*linearColor); + param_3 = _e213; + let _e214 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e215 = weight_1; + let _e216 = (_e214 * _e215); + let _e217 = (*alpha_2); + frag_color = vec4(_e216.x, _e216.y, _e216.z, _e217); + let _e222 = (*roughness_1); + param_4 = _e222; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; } +fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { + let _e205 = (*srgb); + let _e208 = (*srgb); + let _e213 = (*srgb); + return mix((_e205 / vec3(12.92f)), pow(((_e208 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e213)); +} + +fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { + var phi_2843_: bool; + + let _e205 = (*c); + let _e206 = (*c); + let _e208 = any((_e205 != _e206)); + phi_2843_ = _e208; + if !(_e208) { + let _e210 = (*c); + phi_2843_ = any((abs(_e210) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + } + let _e215 = phi_2843_; + return _e215; +} + +fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b(s: ptr, lit: ptr>) -> bool { + var view: f32; + var code: i32; + var shown: vec3; + var param_5: vec3; + var param_6: vec3; + var grey: f32; + var param_7: vec3; + var param_8: vec3; + var local_2: vec3; + var weight_2: f32; + var local_3: f32; + var param_9: vec3; + var param_10: f32; + + let _e221 = frag_info.debug_view[0u]; + view = _e221; + let _e222 = view; + if (_e222 < 0.5f) { + return false; + } + let _e225 = gl_FragCoord_1[0u]; + let _e228 = frag_info.debug_view[1u]; + let _e231 = frag_info.debug_view[2u]; + if (_e225 < (_e228 * _e231)) { + return false; + } + let _e234 = view; + code = i32((_e234 + 0.5f)); + shown = vec3(0f, 0f, 0f); + let _e237 = code; + if (_e237 == 1i) { + let _e240 = (*s).albedo; + param_5 = clamp(_e240, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e242 = LinearToSrgb_u0028_vf3_u003b((¶m_5)); + shown = _e242; + } else { + let _e243 = code; + if (_e243 == 2i) { + let _e246 = (*s).n; + shown = ((_e246 * 0.5f) + vec3(0.5f, 0.5f, 0.5f)); + } else { + let _e249 = code; + if (_e249 == 3i) { + let _e252 = (*s).roughness; + shown = vec3(clamp(_e252, 0f, 1f)); + } else { + let _e255 = code; + if (_e255 == 4i) { + let _e258 = (*s).metallic; + shown = vec3(clamp(_e258, 0f, 1f)); + } else { + let _e261 = code; + if (_e261 == 5i) { + let _e264 = (*s).occlusion; + shown = vec3(clamp(_e264, 0f, 1f)); + } else { + let _e267 = code; + if (_e267 == 6i) { + let _e270 = (*s).emissive; + param_6 = clamp(_e270, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e272 = LinearToSrgb_u0028_vf3_u003b((¶m_6)); + shown = _e272; + } else { + let _e273 = code; + if (_e273 == 7i) { + let _e275 = v_texcoord_1; + let _e276 = fract(_e275); + shown = vec3(_e276.x, _e276.y, 0f); + } else { + let _e280 = code; + if (_e280 == 8i) { + let _e282 = (*lit); + param_7 = clamp(_e282, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e284 = LinearToSrgb_u0028_vf3_u003b((¶m_7)); + grey = dot(_e284, vec3(0.2126f, 0.7152f, 0.0722f)); + let _e286 = (*lit); + param_8 = _e286; + let _e287 = NonFinite_u0028_vf3_u003b((¶m_8)); + if _e287 { + local_2 = vec3(1f, 0f, 1f); + } else { + let _e288 = grey; + local_2 = vec3((_e288 * 0.5f)); + } + let _e291 = local_2; + shown = _e291; + } + } + } + } + } + } + } + } + let _e292 = g_premultiply; + if _e292 { + let _e294 = (*s).alpha; + local_3 = _e294; + } else { + local_3 = 1f; + } + let _e295 = local_3; + weight_2 = _e295; + let _e296 = shown; + param_9 = _e296; + let _e297 = SrgbToLinear_u0028_vf3_u003b((¶m_9)); + let _e298 = weight_2; + let _e299 = (_e297 * _e298); + let _e301 = (*s).alpha; + frag_color = vec4(_e299.x, _e299.y, _e299.z, _e301); + let _e307 = (*s).roughness; + param_10 = _e307; + WriteSurfaceGeometry_u0028_f1_u003b((¶m_10)); + WriteWeightedBlended_u0028_(); + return true; +} + fn FonAlbedo_u0028_f1_u003b_f1_u003b(mu: ptr, r: ptr) -> f32 { var m: f32; var g: f32; - let _e199 = (*mu); - m = (1f - _e199); - let _e201 = m; - let _e202 = m; - let _e203 = m; - let _e204 = m; - g = (_e201 * (0.05710853f + (_e202 * (0.49188188f + (_e203 * (-0.33218145f + (_e204 * 0.071443f))))))); - let _e212 = (*r); - let _e213 = g; - let _e216 = (*r); - return ((1f + (_e212 * _e213)) / (1f + (0.2877934f * _e216))); + let _e208 = (*mu); + m = (1f - _e208); + let _e210 = m; + let _e211 = m; + let _e212 = m; + let _e213 = m; + g = (_e210 * (0.05710853f + (_e211 * (0.49188188f + (_e212 * (-0.33218145f + (_e213 * 0.071443f))))))); + let _e221 = (*r); + let _e222 = g; + let _e225 = (*r); + return ((1f + (_e221 * _e222)) / (1f + (0.2877934f * _e225))); } fn EonMultiAlbedo_u0028_vf3_u003b_f1_u003b(rho: ptr>, average: ptr) -> vec3 { - let _e197 = (*rho); - let _e198 = (*rho); - let _e200 = (*average); - let _e202 = (*rho); - let _e203 = (*average); - return (((_e197 * _e198) * _e200) / (vec3(1f, 1f, 1f) - (_e202 * (1f - _e203)))); + let _e206 = (*rho); + let _e207 = (*rho); + let _e209 = (*average); + let _e211 = (*rho); + let _e212 = (*average); + return (((_e206 * _e207) * _e209) / (vec3(1f, 1f, 1f) - (_e211 * (1f - _e212)))); } fn FonAverage_u0028_f1_u003b(r_1: ptr) -> f32 { - let _e196 = (*r_1); - let _e199 = (*r_1); - return ((1f + (0.07248821f * _e196)) / (1f + (0.2877934f * _e199))); + let _e205 = (*r_1); + let _e208 = (*r_1); + return ((1f + (0.07248821f * _e205)) / (1f + (0.2877934f * _e208))); } fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, r_2: ptr, mu_i: ptr, mu_o: ptr, l_dot_v: ptr) -> vec3 { - var s: f32; + var s_1: f32; var s_over_t: f32; - var local_2: f32; + var local_4: f32; var af: f32; var single: vec3; var average_1: f32; - var param_5: f32; - var multi: vec3; - var param_6: vec3; - var param_7: f32; - var param_8: f32; - var param_9: f32; - var param_10: f32; var param_11: f32; + var multi: vec3; + var param_12: vec3; + var param_13: f32; + var param_14: f32; + var param_15: f32; + var param_16: f32; + var param_17: f32; - let _e214 = (*l_dot_v); - let _e215 = (*mu_i); - let _e216 = (*mu_o); - s = (_e214 - (_e215 * _e216)); - let _e219 = s; - if (_e219 > 0f) { - let _e221 = s; - let _e222 = (*mu_i); - let _e223 = (*mu_o); - local_2 = (_e221 / max(_e222, _e223)); + let _e223 = (*l_dot_v); + let _e224 = (*mu_i); + let _e225 = (*mu_o); + s_1 = (_e223 - (_e224 * _e225)); + let _e228 = s_1; + if (_e228 > 0f) { + let _e230 = s_1; + let _e231 = (*mu_i); + let _e232 = (*mu_o); + local_4 = (_e230 / max(_e231, _e232)); } else { - let _e226 = s; - local_2 = _e226; - } - let _e227 = local_2; - s_over_t = _e227; - let _e228 = (*r_2); - af = (1f / (1f + (0.2877934f * _e228))); - let _e232 = (*rho_1); - let _e233 = af; - let _e234 = (*r_2); - let _e235 = s_over_t; - single = (_e232 * (_e233 * (1f + (_e234 * _e235)))); - let _e240 = (*r_2); - param_5 = _e240; - let _e241 = FonAverage_u0028_f1_u003b((¶m_5)); - average_1 = _e241; - let _e242 = (*rho_1); - param_6 = _e242; - let _e243 = average_1; - param_7 = _e243; - let _e244 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_6), (¶m_7)); - let _e245 = (*mu_o); - param_8 = _e245; - let _e246 = (*r_2); - param_9 = _e246; - let _e247 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_8), (¶m_9)); - let _e250 = (*mu_i); - param_10 = _e250; - let _e251 = (*r_2); - param_11 = _e251; - let _e252 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_10), (¶m_11)); - let _e256 = average_1; - multi = (_e244 * ((max((1f - _e247), 0.0000001f) * max((1f - _e252), 0.0000001f)) / max((1f - _e256), 0.0000001f))); - let _e261 = single; - let _e262 = multi; - return ((_e261 + _e262) / vec3(3.1415927f)); + let _e235 = s_1; + local_4 = _e235; + } + let _e236 = local_4; + s_over_t = _e236; + let _e237 = (*r_2); + af = (1f / (1f + (0.2877934f * _e237))); + let _e241 = (*rho_1); + let _e242 = af; + let _e243 = (*r_2); + let _e244 = s_over_t; + single = (_e241 * (_e242 * (1f + (_e243 * _e244)))); + let _e249 = (*r_2); + param_11 = _e249; + let _e250 = FonAverage_u0028_f1_u003b((¶m_11)); + average_1 = _e250; + let _e251 = (*rho_1); + param_12 = _e251; + let _e252 = average_1; + param_13 = _e252; + let _e253 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_12), (¶m_13)); + let _e254 = (*mu_o); + param_14 = _e254; + let _e255 = (*r_2); + param_15 = _e255; + let _e256 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_14), (¶m_15)); + let _e259 = (*mu_i); + param_16 = _e259; + let _e260 = (*r_2); + param_17 = _e260; + let _e261 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_16), (¶m_17)); + let _e265 = average_1; + multi = (_e253 * ((max((1f - _e256), 0.0000001f) * max((1f - _e261), 0.0000001f)) / max((1f - _e265), 0.0000001f))); + let _e270 = single; + let _e271 = multi; + return ((_e270 + _e271) / vec3(3.1415927f)); } fn EonDiffuse_u0028_() -> bool { - let _e197 = frag_info.ambient_sky[3u]; - return (_e197 > 0.5f); + let _e206 = frag_info.ambient_sky[3u]; + return (_e206 > 0.5f); } fn LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b(x: ptr, y: ptr, table: ptr) -> vec2 { var inTable: vec2; - let _e199 = (*x); - let _e200 = (*y); - inTable = ((vec2(_e199, _e200) * 0.984375f) + vec2(0.0078125f)); - let _e206 = inTable[0u]; - let _e208 = inTable[1u]; - let _e209 = (*table); - return vec2(_e206, ((_e208 + _e209) * 0.5f)); + let _e208 = (*x); + let _e209 = (*y); + inTable = ((vec2(_e208, _e209) * 0.984375f) + vec2(0.0078125f)); + let _e215 = inTable[0u]; + let _e217 = inTable[1u]; + let _e218 = (*table); + return vec2(_e215, ((_e217 + _e218) * 0.5f)); } fn EnvBrdf_u0028_f1_u003b_f1_u003b(roughness_2: ptr, n_dot_v: ptr) -> vec2 { var dfg: vec2; - var param_12: f32; - var param_13: f32; - var param_14: f32; + var param_18: f32; + var param_19: f32; + var param_20: f32; + + let _e210 = (*roughness_2); + let _e212 = (*n_dot_v); + param_18 = clamp(_e210, 0f, 1f); + param_19 = sqrt(clamp((1f - _e212), 0f, 1f)); + param_20 = 1f; + let _e216 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20)); + let _e217 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e216, 0f); + dfg = _e217.xy; + let _e220 = dfg[0u]; + let _e222 = dfg[1u]; + let _e225 = dfg[1u]; + return vec2((_e220 - _e222), _e225); +} - let _e201 = (*roughness_2); - let _e203 = (*n_dot_v); - param_12 = clamp(_e201, 0f, 1f); - param_13 = sqrt(clamp((1f - _e203), 0f, 1f)); - param_14 = 1f; - let _e207 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_12), (¶m_13), (¶m_14)); - let _e208 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e207, 0f); - dfg = _e208.xy; - let _e211 = dfg[0u]; - let _e213 = dfg[1u]; - let _e216 = dfg[1u]; - return vec2((_e211 - _e213), _e216); -} - -fn MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(f0_: ptr>, s_1: ptr) -> vec3 { +fn MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(f0_: ptr>, s_2: ptr) -> vec3 { var ab: vec2; - var param_15: f32; - var param_16: f32; + var param_21: f32; + var param_22: f32; var ess: f32; - let _e202 = (*s_1).roughness; - param_15 = max(_e202, 0.045f); - let _e205 = (*s_1).n_dot_v; - param_16 = _e205; - let _e206 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_15), (¶m_16)); - ab = _e206; - let _e208 = ab[0u]; - let _e210 = ab[1u]; - ess = max((_e208 + _e210), 0.0001f); - let _e213 = (*f0_); - let _e214 = ess; - return (vec3(1f, 1f, 1f) + (_e213 * ((1f / _e214) - 1f))); + let _e211 = (*s_2).roughness; + param_21 = max(_e211, 0.045f); + let _e214 = (*s_2).n_dot_v; + param_22 = _e214; + let _e215 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_21), (¶m_22)); + ab = _e215; + let _e217 = ab[0u]; + let _e219 = ab[1u]; + ess = max((_e217 + _e219), 0.0001f); + let _e222 = (*f0_); + let _e223 = ess; + return (vec3(1f, 1f, 1f) + (_e222 * ((1f / _e223) - 1f))); } fn EnergyCompensation_u0028_() -> bool { - let _e197 = frag_info.target_origin[2u]; - return (_e197 > 0.5f); + let _e206 = frag_info.target_origin[2u]; + return (_e206 > 0.5f); } fn F_Schlick_u0028_vf3_u003b_f1_u003b(f0_1: ptr>, v_dot_h: ptr) -> vec3 { var f: f32; - let _e198 = (*v_dot_h); - f = pow((1f - _e198), 5f); - let _e201 = (*f0_1); - let _e202 = (*f0_1); - let _e204 = f; - return (_e201 + ((vec3(1f, 1f, 1f) - _e202) * _e204)); + let _e207 = (*v_dot_h); + f = pow((1f - _e207), 5f); + let _e210 = (*f0_1); + let _e211 = (*f0_1); + let _e213 = f; + return (_e210 + ((vec3(1f, 1f, 1f) - _e211) * _e213)); } fn V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b(n_dot_v_1: ptr, n_dot_l: ptr, alpha_3: ptr) -> f32 { @@ -12684,179 +13286,179 @@ fn V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b(n_dot_v_1: ptr, alpha_4: ptr) -> f32 { var a: f32; var k_1: f32; - let _e199 = (*n_dot_h); - let _e200 = (*alpha_4); - a = (_e199 * _e200); - let _e202 = (*alpha_4); - let _e203 = (*n_dot_h); - let _e204 = (*n_dot_h); - let _e207 = a; - let _e208 = a; - k_1 = (_e202 / max(((1f - (_e203 * _e204)) + (_e207 * _e208)), 0.0000001f)); - let _e213 = k_1; - let _e214 = k_1; - return ((_e213 * _e214) * 0.31830987f); -} - -fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_2: ptr, light: ptr) -> vec3 { + let _e208 = (*n_dot_h); + let _e209 = (*alpha_4); + a = (_e208 * _e209); + let _e211 = (*alpha_4); + let _e212 = (*n_dot_h); + let _e213 = (*n_dot_h); + let _e216 = a; + let _e217 = a; + k_1 = (_e211 / max(((1f - (_e212 * _e213)) + (_e216 * _e217)), 0.0000001f)); + let _e222 = k_1; + let _e223 = k_1; + return ((_e222 * _e223) * 0.31830987f); +} + +fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_3: ptr, light: ptr) -> vec3 { var lobe: f32; var alpha_5: f32; var f0_2: vec3; var diffuseColor: vec3; var d_1: f32; - var param_17: f32; - var param_18: f32; + var param_23: f32; + var param_24: f32; var vis: f32; - var param_19: f32; - var param_20: f32; - var param_21: f32; + var param_25: f32; + var param_26: f32; + var param_27: f32; var f_1: vec3; - var param_22: vec3; - var param_23: f32; + var param_28: vec3; + var param_29: f32; var specular: vec3; - var param_24: vec3; - var param_25: Surface; + var param_30: vec3; + var param_31: Surface; var diffuse: vec3; - var param_26: vec3; - var param_27: f32; - var param_28: f32; - var param_29: f32; - var param_30: f32; + var param_32: vec3; + var param_33: f32; + var param_34: f32; + var param_35: f32; + var param_36: f32; - let _e221 = (*s_2).roughness; - lobe = max(_e221, 0.045f); - let _e223 = lobe; - let _e224 = lobe; - alpha_5 = (_e223 * _e224); - let _e227 = (*s_2).albedo; - let _e229 = (*s_2).metallic; - f0_2 = mix(vec3(0.04f, 0.04f, 0.04f), _e227, vec3(_e229)); - let _e233 = (*s_2).albedo; - let _e235 = (*s_2).metallic; - diffuseColor = (_e233 * (1f - _e235)); - let _e239 = (*light).n_dot_h; - param_17 = _e239; - let _e240 = alpha_5; - param_18 = _e240; - let _e241 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_17), (¶m_18)); - d_1 = _e241; - let _e243 = (*s_2).n_dot_v; - param_19 = _e243; - let _e245 = (*light).n_dot_l; - param_20 = _e245; - let _e246 = alpha_5; - param_21 = _e246; - let _e247 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_19), (¶m_20), (¶m_21)); - vis = _e247; - let _e248 = f0_2; - param_22 = _e248; - let _e250 = (*light).v_dot_h; - param_23 = _e250; - let _e251 = F_Schlick_u0028_vf3_u003b_f1_u003b((¶m_22), (¶m_23)); - f_1 = _e251; - let _e252 = d_1; - let _e253 = vis; - let _e255 = f_1; - let _e259 = frag_info.material[3u]; - specular = ((_e255 * (_e252 * _e253)) * _e259); - let _e262 = (*light).integrated; - if (_e262 > 0.5f) { - let _e266 = (*light).ltc[0u]; - let _e267 = f0_2; - let _e270 = (*light).ltc[1u]; - let _e272 = f0_2; - let _e277 = (*light).ltc[2u]; - let _e283 = frag_info.material[3u]; - let _e286 = (*light).n_dot_l; - specular = (((((_e267 * _e270) + ((vec3(1f) - _e272) * _e277)) * _e266) * _e283) / vec3(max(_e286, 0.000001f))); - } - let _e290 = EnergyCompensation_u0028_(); - if _e290 { - let _e291 = f0_2; - param_24 = _e291; - let _e292 = (*s_2); - param_25 = _e292; - let _e293 = MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_24), (¶m_25)); - let _e294 = specular; - specular = (_e294 * _e293); - } - let _e296 = diffuseColor; - let _e297 = f_1; - diffuse = ((_e296 * (vec3(1f, 1f, 1f) - _e297)) / vec3(3.1415927f)); - let _e302 = EonDiffuse_u0028_(); - if _e302 { - let _e304 = (*s_2).n; - let _e306 = (*light).l; - let _e310 = (*light).l; - let _e312 = (*s_2).v; - let _e314 = diffuseColor; - param_26 = _e314; - let _e316 = (*s_2).roughness; - param_27 = _e316; - param_28 = clamp(dot(_e304, _e306), 0.0001f, 1f); - let _e318 = (*s_2).n_dot_v; - param_29 = _e318; - param_30 = dot(_e310, _e312); - let _e319 = EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e320 = f_1; - diffuse = (_e319 * (vec3(1f, 1f, 1f) - _e320)); - } - let _e323 = diffuse; - let _e324 = specular; - return ((_e323 + _e324) * 3.1415927f); + let _e230 = (*s_3).roughness; + lobe = max(_e230, 0.045f); + let _e232 = lobe; + let _e233 = lobe; + alpha_5 = (_e232 * _e233); + let _e236 = (*s_3).albedo; + let _e238 = (*s_3).metallic; + f0_2 = mix(vec3(0.04f, 0.04f, 0.04f), _e236, vec3(_e238)); + let _e242 = (*s_3).albedo; + let _e244 = (*s_3).metallic; + diffuseColor = (_e242 * (1f - _e244)); + let _e248 = (*light).n_dot_h; + param_23 = _e248; + let _e249 = alpha_5; + param_24 = _e249; + let _e250 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_23), (¶m_24)); + d_1 = _e250; + let _e252 = (*s_3).n_dot_v; + param_25 = _e252; + let _e254 = (*light).n_dot_l; + param_26 = _e254; + let _e255 = alpha_5; + param_27 = _e255; + let _e256 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_25), (¶m_26), (¶m_27)); + vis = _e256; + let _e257 = f0_2; + param_28 = _e257; + let _e259 = (*light).v_dot_h; + param_29 = _e259; + let _e260 = F_Schlick_u0028_vf3_u003b_f1_u003b((¶m_28), (¶m_29)); + f_1 = _e260; + let _e261 = d_1; + let _e262 = vis; + let _e264 = f_1; + let _e268 = frag_info.material[3u]; + specular = ((_e264 * (_e261 * _e262)) * _e268); + let _e271 = (*light).integrated; + if (_e271 > 0.5f) { + let _e275 = (*light).ltc[0u]; + let _e276 = f0_2; + let _e279 = (*light).ltc[1u]; + let _e281 = f0_2; + let _e286 = (*light).ltc[2u]; + let _e292 = frag_info.material[3u]; + let _e295 = (*light).n_dot_l; + specular = (((((_e276 * _e279) + ((vec3(1f) - _e281) * _e286)) * _e275) * _e292) / vec3(max(_e295, 0.000001f))); + } + let _e299 = EnergyCompensation_u0028_(); + if _e299 { + let _e300 = f0_2; + param_30 = _e300; + let _e301 = (*s_3); + param_31 = _e301; + let _e302 = MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_30), (¶m_31)); + let _e303 = specular; + specular = (_e303 * _e302); + } + let _e305 = diffuseColor; + let _e306 = f_1; + diffuse = ((_e305 * (vec3(1f, 1f, 1f) - _e306)) / vec3(3.1415927f)); + let _e311 = EonDiffuse_u0028_(); + if _e311 { + let _e313 = (*s_3).n; + let _e315 = (*light).l; + let _e319 = (*light).l; + let _e321 = (*s_3).v; + let _e323 = diffuseColor; + param_32 = _e323; + let _e325 = (*s_3).roughness; + param_33 = _e325; + param_34 = clamp(dot(_e313, _e315), 0.0001f, 1f); + let _e327 = (*s_3).n_dot_v; + param_35 = _e327; + param_36 = dot(_e319, _e321); + let _e328 = EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_32), (¶m_33), (¶m_34), (¶m_35), (¶m_36)); + let _e329 = f_1; + diffuse = (_e328 * (vec3(1f, 1f, 1f) - _e329)); + } + let _e332 = diffuse; + let _e333 = specular; + return ((_e332 + _e333) * 3.1415927f); } fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e196 = (*i); - if (_e196 == 0i) { + let _e205 = (*i); + if (_e205 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e198 = (*i); - if (_e198 == 1i) { + let _e207 = (*i); + if (_e207 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e200 = (*i); - if (_e200 == 2i) { + let _e209 = (*i); + if (_e209 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e202 = (*i); - if (_e202 == 3i) { + let _e211 = (*i); + if (_e211 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e204 = (*i); - if (_e204 == 4i) { + let _e213 = (*i); + if (_e213 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e206 = (*i); - if (_e206 == 5i) { + let _e215 = (*i); + if (_e215 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e208 = (*i); - if (_e208 == 6i) { + let _e217 = (*i); + if (_e217 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -12864,208 +13466,208 @@ fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr, ca: ptr, sa: ptr, radius: ptr) -> vec2 { var p: vec2; - var param_31: i32; + var param_37: i32; - let _e201 = (*i_1); - param_31 = _e201; - let _e202 = PointShadowDiskTap_u0028_i1_u003b((¶m_31)); - p = _e202; - let _e204 = p[0u]; - let _e205 = (*ca); - let _e208 = p[1u]; - let _e209 = (*sa); + let _e210 = (*i_1); + param_37 = _e210; + let _e211 = PointShadowDiskTap_u0028_i1_u003b((¶m_37)); + p = _e211; let _e213 = p[0u]; - let _e214 = (*sa); + let _e214 = (*ca); let _e217 = p[1u]; - let _e218 = (*ca); - let _e222 = (*radius); - return (vec2(((_e204 * _e205) - (_e208 * _e209)), ((_e213 * _e214) + (_e217 * _e218))) * _e222); + let _e218 = (*sa); + let _e222 = p[0u]; + let _e223 = (*sa); + let _e226 = p[1u]; + let _e227 = (*ca); + let _e231 = (*radius); + return (vec2(((_e213 * _e214) - (_e217 * _e218)), ((_e222 * _e223) + (_e226 * _e227))) * _e231); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_3: vec2; + var local_5: vec2; var atlas: vec2; - let _e204 = point_shadow.params[0u]; - inset = _e204; - let _e205 = (*uv); - let _e206 = (*offset); - let _e208 = inset; - let _e209 = inset; - local_3 = clamp((_e205 + _e206), vec2(_e208), vec2((1f - _e209))); - let _e214 = local_3; - let _e215 = (*tile); - atlas = ((_e214 + _e215) * vec2(0.16666667f, 0.16666667f)); - let _e220 = point_shadow.params3_[0u]; - if (_e220 > 0.5f) { - let _e223 = atlas[1u]; - atlas[1u] = (1f - _e223); - } - let _e226 = atlas; - let _e227 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e226, 0f); - let _e229 = atlas; - let _e230 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e229, 0f); - let _e233 = (*range); - return (min(_e227.x, _e230.x) * _e233); + let _e213 = point_shadow.params[0u]; + inset = _e213; + let _e214 = (*uv); + let _e215 = (*offset); + let _e217 = inset; + let _e218 = inset; + local_5 = clamp((_e214 + _e215), vec2(_e217), vec2((1f - _e218))); + let _e223 = local_5; + let _e224 = (*tile); + atlas = ((_e223 + _e224) * vec2(0.16666667f, 0.16666667f)); + let _e229 = point_shadow.params3_[0u]; + if (_e229 > 0.5f) { + let _e232 = atlas[1u]; + atlas[1u] = (1f - _e232); + } + let _e235 = atlas; + let _e236 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e235, 0f); + let _e238 = atlas; + let _e239 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e238, 0f); + let _e242 = (*range); + return (min(_e236.x, _e239.x) * _e242); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { var stored: f32; - var param_32: vec2; - var param_33: vec2; - var param_34: vec2; - var param_35: f32; + var param_38: vec2; + var param_39: vec2; + var param_40: vec2; + var param_41: f32; - let _e205 = (*uv_1); - param_32 = _e205; - let _e206 = (*offset_1); - param_33 = _e206; - let _e207 = (*tile_1); - param_34 = _e207; - let _e208 = (*range_1); - param_35 = _e208; - let _e209 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_32), (¶m_33), (¶m_34), (¶m_35)); - stored = _e209; - let _e210 = stored; - let _e211 = (*range_1); - if (_e210 >= (_e211 * 0.999f)) { + let _e214 = (*uv_1); + param_38 = _e214; + let _e215 = (*offset_1); + param_39 = _e215; + let _e216 = (*tile_1); + param_40 = _e216; + let _e217 = (*range_1); + param_41 = _e217; + let _e218 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_38), (¶m_39), (¶m_40), (¶m_41)); + stored = _e218; + let _e219 = stored; + let _e220 = (*range_1); + if (_e219 >= (_e220 * 0.999f)) { return 1f; } - let _e214 = (*receiver); - let _e215 = stored; - return select(1f, 0f, (_e214 > _e215)); + let _e223 = (*receiver); + let _e224 = stored; + return select(1f, 0f, (_e223 > _e224)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { var lightRadius: f32; var minRadius: f32; var maxRadius: f32; - var param_36: vec2; - var param_37: vec2; - var param_38: vec2; - var param_39: f32; + var param_42: vec2; + var param_43: vec2; + var param_44: vec2; + var param_45: f32; var sum: f32; var count: f32; var i_2: i32; var stored_1: f32; - var param_40: i32; - var param_41: f32; - var param_42: f32; - var param_43: f32; - var param_44: vec2; - var param_45: vec2; - var param_46: vec2; + var param_46: i32; var param_47: f32; + var param_48: f32; + var param_49: f32; + var param_50: vec2; + var param_51: vec2; + var param_52: vec2; + var param_53: f32; var blocker: f32; var world: f32; (*blockerOut) = -1f; - let _e226 = point_shadow.params2_[1u]; - lightRadius = _e226; - let _e229 = point_shadow.params2_[0u]; - minRadius = _e229; - let _e232 = point_shadow.params2_[2u]; - maxRadius = _e232; - let _e233 = lightRadius; - if (_e233 <= 0f) { - let _e235 = (*uv_2); - param_36 = _e235; - param_37 = vec2(0f, 0f); - let _e236 = (*tile_2); - param_38 = _e236; - let _e237 = (*range_2); - param_39 = _e237; - let _e238 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - (*blockerOut) = _e238; - let _e239 = minRadius; - return _e239; + let _e235 = point_shadow.params2_[1u]; + lightRadius = _e235; + let _e238 = point_shadow.params2_[0u]; + minRadius = _e238; + let _e241 = point_shadow.params2_[2u]; + maxRadius = _e241; + let _e242 = lightRadius; + if (_e242 <= 0f) { + let _e244 = (*uv_2); + param_42 = _e244; + param_43 = vec2(0f, 0f); + let _e245 = (*tile_2); + param_44 = _e245; + let _e246 = (*range_2); + param_45 = _e246; + let _e247 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_42), (¶m_43), (¶m_44), (¶m_45)); + (*blockerOut) = _e247; + let _e248 = minRadius; + return _e248; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e240 = i_2; - if (_e240 < 8i) { - let _e242 = i_2; - param_40 = _e242; - let _e243 = (*ca_1); - param_41 = _e243; - let _e244 = (*sa_1); - param_42 = _e244; - let _e245 = maxRadius; - param_43 = _e245; - let _e246 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43)); - let _e247 = (*uv_2); - param_44 = _e247; - param_45 = _e246; - let _e248 = (*tile_2); - param_46 = _e248; - let _e249 = (*range_2); - param_47 = _e249; - let _e250 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_44), (¶m_45), (¶m_46), (¶m_47)); - stored_1 = _e250; - let _e251 = stored_1; - let _e252 = (*range_2); - if (_e251 >= (_e252 * 0.999f)) { + let _e249 = i_2; + if (_e249 < 8i) { + let _e251 = i_2; + param_46 = _e251; + let _e252 = (*ca_1); + param_47 = _e252; + let _e253 = (*sa_1); + param_48 = _e253; + let _e254 = maxRadius; + param_49 = _e254; + let _e255 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_46), (¶m_47), (¶m_48), (¶m_49)); + let _e256 = (*uv_2); + param_50 = _e256; + param_51 = _e255; + let _e257 = (*tile_2); + param_52 = _e257; + let _e258 = (*range_2); + param_53 = _e258; + let _e259 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_50), (¶m_51), (¶m_52), (¶m_53)); + stored_1 = _e259; + let _e260 = stored_1; + let _e261 = (*range_2); + if (_e260 >= (_e261 * 0.999f)) { continue; } - let _e255 = stored_1; - let _e256 = (*receiver_1); - if (_e255 >= _e256) { + let _e264 = stored_1; + let _e265 = (*receiver_1); + if (_e264 >= _e265) { continue; } - let _e258 = stored_1; - let _e259 = sum; - sum = (_e259 + _e258); - let _e261 = count; - count = (_e261 + 1f); + let _e267 = stored_1; + let _e268 = sum; + sum = (_e268 + _e267); + let _e270 = count; + count = (_e270 + 1f); continue; } else { break; } continuing { - let _e263 = i_2; - i_2 = (_e263 + 1i); + let _e272 = i_2; + i_2 = (_e272 + 1i); } } - let _e265 = count; - if (_e265 < 0.5f) { + let _e274 = count; + if (_e274 < 0.5f) { return -1f; } - let _e267 = sum; - let _e268 = count; - blocker = max((_e267 / _e268), 0.0001f); - let _e271 = blocker; - (*blockerOut) = _e271; - let _e272 = lightRadius; - let _e273 = (*receiver_1); - let _e274 = blocker; - let _e278 = blocker; - world = ((_e272 * max((_e273 - _e274), 0f)) / _e278); - let _e280 = world; - let _e281 = (*receiver_1); - let _e283 = (*tanHalf); - let _e286 = minRadius; - let _e287 = maxRadius; - return clamp((_e280 / ((2f * _e281) * _e283)), _e286, _e287); + let _e276 = sum; + let _e277 = count; + blocker = max((_e276 / _e277), 0.0001f); + let _e280 = blocker; + (*blockerOut) = _e280; + let _e281 = lightRadius; + let _e282 = (*receiver_1); + let _e283 = blocker; + let _e287 = blocker; + world = ((_e281 * max((_e282 - _e283), 0f)) / _e287); + let _e289 = world; + let _e290 = (*receiver_1); + let _e292 = (*tanHalf); + let _e295 = minRadius; + let _e296 = maxRadius; + return clamp((_e289 / ((2f * _e290) * _e292)), _e295, _e296); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_4: vec2; + var local_6: vec2; - let _e197 = (*rows); - if (_e197 > 0f) { - let _e200 = gl_FragCoord_1[0u]; - let _e201 = (*rows); - let _e203 = gl_FragCoord_1[1u]; - local_4 = vec2(_e200, (_e201 - _e203)); + let _e206 = (*rows); + if (_e206 > 0f) { + let _e209 = gl_FragCoord_1[0u]; + let _e210 = (*rows); + let _e212 = gl_FragCoord_1[1u]; + local_6 = vec2(_e209, (_e210 - _e212)); } else { - let _e206 = gl_FragCoord_1; - local_4 = _e206.xy; + let _e215 = gl_FragCoord_1; + local_6 = _e215.xy; } - let _e208 = local_4; - return _e208; + let _e217 = local_6; + return _e217; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -13088,292 +13690,292 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var receiver_2: f32; var noise: f32; - var param_48: f32; + var param_54: f32; var angle: f32; var ca_2: f32; var sa_2: f32; var tanHalf_1: f32; var blocker_1: f32; var radius_1: f32; - var param_49: vec2; - var param_50: vec2; - var param_51: f32; - var param_52: f32; - var param_53: f32; - var param_54: f32; - var param_55: f32; - var param_56: f32; - var local_5: vec3; - var lit: f32; - var param_57: vec2; - var param_58: vec2; - var param_59: vec2; + var param_55: vec2; + var param_56: vec2; + var param_57: f32; + var param_58: f32; + var param_59: f32; var param_60: f32; var param_61: f32; + var param_62: f32; + var local_7: vec3; + var lit_1: f32; + var param_63: vec2; + var param_64: vec2; + var param_65: vec2; + var param_66: f32; + var param_67: f32; var i_3: i32; - var param_62: i32; - var param_63: f32; - var param_64: f32; - var param_65: f32; - var param_66: vec2; - var param_67: vec2; - var param_68: vec2; + var param_68: i32; var param_69: f32; var param_70: f32; - var phi_2525_: bool; - var phi_2586_: bool; - - let _e249 = (*lightIndex); - let _e253 = point_shadow.slots[_e249][0u]; - slot = i32((_e253 + 0.5f)); - let _e256 = (*lightIndex); - let _e260 = point_shadow.slots[_e256][0u]; - if (_e260 < 0f) { + var param_71: f32; + var param_72: vec2; + var param_73: vec2; + var param_74: vec2; + var param_75: f32; + var param_76: f32; + var phi_2534_: bool; + var phi_2595_: bool; + + let _e258 = (*lightIndex); + let _e262 = point_shadow.slots[_e258][0u]; + slot = i32((_e262 + 0.5f)); + let _e265 = (*lightIndex); + let _e269 = point_shadow.slots[_e265][0u]; + if (_e269 < 0f) { return 1f; } - let _e264 = point_shadow.params[2u]; - strength = _e264; - let _e265 = strength; - if (_e265 <= 0f) { + let _e273 = point_shadow.params[2u]; + strength = _e273; + let _e274 = strength; + if (_e274 <= 0f) { return 1f; } - let _e267 = slot; - let _e270 = point_shadow.lights[_e267]; - let _e272 = (*world_1); - toLight = (_e270.xyz - _e272); - let _e274 = toLight; - toLightLength = max(length(_e274), 0.000001f); - let _e277 = (*normal); - let _e278 = toLight; - let _e279 = toLightLength; - nDotL = max(dot(_e277, (_e278 / vec3(_e279))), 0.15f); - let _e284 = nDotL; - let _e285 = nDotL; - let _e290 = nDotL; - let _e291 = nDotL; - slope = min((sqrt(max((1f - (_e284 * _e285)), 0f)) / (_e290 * _e291)), 8f); - let _e295 = toLightLength; - let _e297 = (*lightIndex); - let _e301 = point_shadow.slots[_e297][2u]; - let _e306 = point_shadow.params3_[1u]; - texel = (((2f * _e295) * max(_e301, 0.0001f)) * _e306); - let _e308 = (*world_1); - let _e309 = (*normal); - let _e310 = texel; - let _e314 = point_shadow.params[3u]; - let _e316 = slope; - origin = (_e308 + (((_e309 * _e310) * _e314) * (1f + _e316))); - let _e320 = origin; - let _e321 = slot; - let _e324 = point_shadow.lights[_e321]; - toFragment = (_e320 - _e324.xyz); - let _e327 = toFragment; - distance_ = length(_e327); - let _e329 = slot; - let _e333 = point_shadow.lights[_e329][3u]; - range_3 = max(_e333, 0.0001f); - let _e335 = distance_; - let _e336 = range_3; - if (_e335 >= _e336) { + let _e276 = slot; + let _e279 = point_shadow.lights[_e276]; + let _e281 = (*world_1); + toLight = (_e279.xyz - _e281); + let _e283 = toLight; + toLightLength = max(length(_e283), 0.000001f); + let _e286 = (*normal); + let _e287 = toLight; + let _e288 = toLightLength; + nDotL = max(dot(_e286, (_e287 / vec3(_e288))), 0.15f); + let _e293 = nDotL; + let _e294 = nDotL; + let _e299 = nDotL; + let _e300 = nDotL; + slope = min((sqrt(max((1f - (_e293 * _e294)), 0f)) / (_e299 * _e300)), 8f); + let _e304 = toLightLength; + let _e306 = (*lightIndex); + let _e310 = point_shadow.slots[_e306][2u]; + let _e315 = point_shadow.params3_[1u]; + texel = (((2f * _e304) * max(_e310, 0.0001f)) * _e315); + let _e317 = (*world_1); + let _e318 = (*normal); + let _e319 = texel; + let _e323 = point_shadow.params[3u]; + let _e325 = slope; + origin = (_e317 + (((_e318 * _e319) * _e323) * (1f + _e325))); + let _e329 = origin; + let _e330 = slot; + let _e333 = point_shadow.lights[_e330]; + toFragment = (_e329 - _e333.xyz); + let _e336 = toFragment; + distance_ = length(_e336); + let _e338 = slot; + let _e342 = point_shadow.lights[_e338][3u]; + range_3 = max(_e342, 0.0001f); + let _e344 = distance_; + let _e345 = range_3; + if (_e344 >= _e345) { return 1f; } face = 0i; - let _e338 = (*lightIndex); - let _e342 = point_shadow.slots[_e338][1u]; - if (_e342 < 0.5f) { - let _e344 = toFragment; - a_1 = abs(_e344); - let _e347 = a_1[0u]; - let _e349 = a_1[1u]; - let _e350 = (_e347 >= _e349); - phi_2525_ = _e350; - if _e350 { - let _e352 = a_1[0u]; - let _e354 = a_1[2u]; - phi_2525_ = (_e352 >= _e354); - } - let _e357 = phi_2525_; - if _e357 { - let _e359 = toFragment[0u]; - face = select(1i, 0i, (_e359 > 0f)); + let _e347 = (*lightIndex); + let _e351 = point_shadow.slots[_e347][1u]; + if (_e351 < 0.5f) { + let _e353 = toFragment; + a_1 = abs(_e353); + let _e356 = a_1[0u]; + let _e358 = a_1[1u]; + let _e359 = (_e356 >= _e358); + phi_2534_ = _e359; + if _e359 { + let _e361 = a_1[0u]; + let _e363 = a_1[2u]; + phi_2534_ = (_e361 >= _e363); + } + let _e366 = phi_2534_; + if _e366 { + let _e368 = toFragment[0u]; + face = select(1i, 0i, (_e368 > 0f)); } else { - let _e363 = a_1[1u]; - let _e365 = a_1[2u]; - if (_e363 >= _e365) { - let _e368 = toFragment[1u]; - face = select(3i, 2i, (_e368 > 0f)); + let _e372 = a_1[1u]; + let _e374 = a_1[2u]; + if (_e372 >= _e374) { + let _e377 = toFragment[1u]; + face = select(3i, 2i, (_e377 > 0f)); } else { - let _e372 = toFragment[2u]; - face = select(5i, 4i, (_e372 > 0f)); + let _e381 = toFragment[2u]; + face = select(5i, 4i, (_e381 > 0f)); } } } - let _e375 = slot; - let _e377 = face; - let _e381 = point_shadow.faces[((_e375 * 6i) + _e377)]; - let _e382 = origin; - clip = (_e381 * vec4(_e382.x, _e382.y, _e382.z, 1f)); - let _e389 = clip[3u]; - if (_e389 <= 0f) { + let _e384 = slot; + let _e386 = face; + let _e390 = point_shadow.faces[((_e384 * 6i) + _e386)]; + let _e391 = origin; + clip = (_e390 * vec4(_e391.x, _e391.y, _e391.z, 1f)); + let _e398 = clip[3u]; + if (_e398 <= 0f) { return 1f; } - let _e391 = clip; - let _e394 = clip[3u]; - ndc = (_e391.xy / vec2(_e394)); - let _e398 = ndc[0u]; - let _e400 = (abs(_e398) > 1f); - phi_2586_ = _e400; - if !(_e400) { - let _e403 = ndc[1u]; - phi_2586_ = (abs(_e403) > 1f); - } - let _e407 = phi_2586_; - if _e407 { + let _e400 = clip; + let _e403 = clip[3u]; + ndc = (_e400.xy / vec2(_e403)); + let _e407 = ndc[0u]; + let _e409 = (abs(_e407) > 1f); + phi_2595_ = _e409; + if !(_e409) { + let _e412 = ndc[1u]; + phi_2595_ = (abs(_e412) > 1f); + } + let _e416 = phi_2595_; + if _e416 { return 1f; } - let _e409 = ndc[0u]; - let _e413 = ndc[1u]; - uv_3 = vec2(((_e409 * 0.5f) + 0.5f), (0.5f - (_e413 * 0.5f))); - let _e417 = face; - let _e419 = slot; - tile_3 = vec2(f32(_e417), f32(_e419)); - let _e422 = distance_; - let _e425 = point_shadow.params[1u]; - receiver_2 = (_e422 - _e425); - let _e429 = frag_info.target_origin[0u]; - param_48 = _e429; - let _e430 = FragCoordFromTop_u0028_f1_u003b((¶m_48)); - noise = fract((52.982918f * fract(dot(_e430, vec2(0.06711056f, 0.00583715f))))); - let _e435 = noise; - angle = (_e435 * 6.2831855f); - let _e437 = angle; - ca_2 = cos(_e437); - let _e439 = angle; - sa_2 = sin(_e439); - let _e441 = (*lightIndex); - let _e445 = point_shadow.slots[_e441][2u]; - tanHalf_1 = max(_e445, 0.0001f); + let _e418 = ndc[0u]; + let _e422 = ndc[1u]; + uv_3 = vec2(((_e418 * 0.5f) + 0.5f), (0.5f - (_e422 * 0.5f))); + let _e426 = face; + let _e428 = slot; + tile_3 = vec2(f32(_e426), f32(_e428)); + let _e431 = distance_; + let _e434 = point_shadow.params[1u]; + receiver_2 = (_e431 - _e434); + let _e438 = frag_info.target_origin[0u]; + param_54 = _e438; + let _e439 = FragCoordFromTop_u0028_f1_u003b((¶m_54)); + noise = fract((52.982918f * fract(dot(_e439, vec2(0.06711056f, 0.00583715f))))); + let _e444 = noise; + angle = (_e444 * 6.2831855f); + let _e446 = angle; + ca_2 = cos(_e446); + let _e448 = angle; + sa_2 = sin(_e448); + let _e450 = (*lightIndex); + let _e454 = point_shadow.slots[_e450][2u]; + tanHalf_1 = max(_e454, 0.0001f); blocker_1 = -1f; - let _e447 = uv_3; - param_49 = _e447; - let _e448 = tile_3; - param_50 = _e448; - let _e449 = range_3; - param_51 = _e449; - let _e450 = receiver_2; - param_52 = _e450; - let _e451 = ca_2; - param_53 = _e451; - let _e452 = sa_2; - param_54 = _e452; - let _e453 = tanHalf_1; - param_55 = _e453; - let _e454 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_49), (¶m_50), (¶m_51), (¶m_52), (¶m_53), (¶m_54), (¶m_55), (¶m_56)); - let _e455 = param_56; - blocker_1 = _e455; - radius_1 = _e454; - let _e458 = point_shadow.params2_[3u]; - if (_e458 > 0.5f) { + let _e456 = uv_3; + param_55 = _e456; + let _e457 = tile_3; + param_56 = _e457; + let _e458 = range_3; + param_57 = _e458; + let _e459 = receiver_2; + param_58 = _e459; + let _e460 = ca_2; + param_59 = _e460; + let _e461 = sa_2; + param_60 = _e461; + let _e462 = tanHalf_1; + param_61 = _e462; + let _e463 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57), (¶m_58), (¶m_59), (¶m_60), (¶m_61), (¶m_62)); + let _e464 = param_62; + blocker_1 = _e464; + radius_1 = _e463; + let _e467 = point_shadow.params2_[3u]; + if (_e467 > 0.5f) { g_debug_surface_on = true; - let _e460 = radius_1; - if (_e460 < 0f) { - local_5 = vec3(0f, 0f, 1f); + let _e469 = radius_1; + if (_e469 < 0f) { + local_7 = vec3(0f, 0f, 1f); } else { - let _e462 = radius_1; - let _e465 = point_shadow.params2_[2u]; - let _e469 = blocker_1; - let _e470 = range_3; - local_5 = vec3(clamp((_e462 / max(_e465, 0.000001f)), 0f, 1f), clamp((_e469 / _e470), 0f, 1f), 0f); + let _e471 = radius_1; + let _e474 = point_shadow.params2_[2u]; + let _e478 = blocker_1; + let _e479 = range_3; + local_7 = vec3(clamp((_e471 / max(_e474, 0.000001f)), 0f, 1f), clamp((_e478 / _e479), 0f, 1f), 0f); } - let _e474 = local_5; - g_debug_surface = _e474; + let _e483 = local_7; + g_debug_surface = _e483; } - let _e475 = radius_1; - if (_e475 < 0f) { + let _e484 = radius_1; + if (_e484 < 0f) { return 1f; } - let _e477 = uv_3; - param_57 = _e477; - param_58 = vec2(0f, 0f); - let _e478 = tile_3; - param_59 = _e478; - let _e479 = range_3; - param_60 = _e479; - let _e480 = receiver_2; - param_61 = _e480; - let _e481 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_57), (¶m_58), (¶m_59), (¶m_60), (¶m_61)); - lit = _e481; - let _e482 = radius_1; - if (_e482 > 0f) { + let _e486 = uv_3; + param_63 = _e486; + param_64 = vec2(0f, 0f); + let _e487 = tile_3; + param_65 = _e487; + let _e488 = range_3; + param_66 = _e488; + let _e489 = receiver_2; + param_67 = _e489; + let _e490 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_63), (¶m_64), (¶m_65), (¶m_66), (¶m_67)); + lit_1 = _e490; + let _e491 = radius_1; + if (_e491 > 0f) { i_3 = 0i; loop { - let _e484 = i_3; - if (_e484 < 8i) { - let _e486 = i_3; - param_62 = _e486; - let _e487 = ca_2; - param_63 = _e487; - let _e488 = sa_2; - param_64 = _e488; - let _e489 = radius_1; - param_65 = _e489; - let _e490 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_62), (¶m_63), (¶m_64), (¶m_65)); - let _e491 = uv_3; - param_66 = _e491; - param_67 = _e490; - let _e492 = tile_3; - param_68 = _e492; - let _e493 = range_3; - param_69 = _e493; - let _e494 = receiver_2; - param_70 = _e494; - let _e495 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_66), (¶m_67), (¶m_68), (¶m_69), (¶m_70)); - let _e496 = lit; - lit = (_e496 + _e495); + let _e493 = i_3; + if (_e493 < 8i) { + let _e495 = i_3; + param_68 = _e495; + let _e496 = ca_2; + param_69 = _e496; + let _e497 = sa_2; + param_70 = _e497; + let _e498 = radius_1; + param_71 = _e498; + let _e499 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_68), (¶m_69), (¶m_70), (¶m_71)); + let _e500 = uv_3; + param_72 = _e500; + param_73 = _e499; + let _e501 = tile_3; + param_74 = _e501; + let _e502 = range_3; + param_75 = _e502; + let _e503 = receiver_2; + param_76 = _e503; + let _e504 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_72), (¶m_73), (¶m_74), (¶m_75), (¶m_76)); + let _e505 = lit_1; + lit_1 = (_e505 + _e504); continue; } else { break; } continuing { - let _e498 = i_3; - i_3 = (_e498 + 1i); + let _e507 = i_3; + i_3 = (_e507 + 1i); } } - let _e500 = lit; - lit = (_e500 * 0.11111111f); + let _e509 = lit_1; + lit_1 = (_e509 * 0.11111111f); } - let _e502 = lit; - let _e503 = strength; - return mix(1f, _e502, clamp(_e503, 0f, 1f)); + let _e511 = lit_1; + let _e512 = strength; + return mix(1f, _e511, clamp(_e512, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r_3: f32; var theta: f32; - let _e200 = (*i_4); - let _e203 = (*n_1); - r_3 = sqrt(((f32(_e200) + 0.5f) / f32(_e203))); - let _e207 = (*i_4); - let _e210 = (*turn); - theta = ((f32(_e207) * 2.3999631f) + _e210); - let _e212 = r_3; - let _e213 = theta; - let _e215 = theta; - return (vec2(cos(_e213), sin(_e215)) * _e212); + let _e209 = (*i_4); + let _e212 = (*n_1); + r_3 = sqrt(((f32(_e209) + 0.5f) / f32(_e212))); + let _e216 = (*i_4); + let _e219 = (*turn); + theta = ((f32(_e216) * 2.3999631f) + _e219); + let _e221 = r_3; + let _e222 = theta; + let _e224 = theta; + return (vec2(cos(_e222), sin(_e224)) * _e221); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; - var param_71: f32; + var param_77: f32; - let _e199 = frag_info.target_origin[0u]; - param_71 = _e199; - let _e200 = FragCoordFromTop_u0028_f1_u003b((¶m_71)); - let _e203 = frag_info.target_origin[3u]; - at = (_e200 + vec2((5.588238f * max(_e203, 0f)))); - let _e208 = at; - return fract((52.982918f * fract(dot(_e208, vec2(0.06711056f, 0.00583715f))))); + let _e208 = frag_info.target_origin[0u]; + param_77 = _e208; + let _e209 = FragCoordFromTop_u0028_f1_u003b((¶m_77)); + let _e212 = frag_info.target_origin[3u]; + at = (_e209 + vec2((5.588238f * max(_e212, 0f)))); + let _e217 = at; + return fract((52.982918f * fract(dot(_e217, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -13382,88 +13984,88 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_3: f32; - let _e197 = (*depth); - d_3 = ((2f * clamp(_e197, 0f, 1f)) - 1f); - let _e201 = d_3; - let _e204 = d_3; - return vec2(exp((40f * _e201)), -(exp((-5f * _e204)))); + let _e206 = (*depth); + d_3 = ((2f * clamp(_e206, 0f, 1f)) - 1f); + let _e210 = d_3; + let _e213 = d_3; + return vec2(exp((40f * _e210)), -(exp((-5f * _e213)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { var warped: vec2; - var param_72: f32; + var param_78: f32; var scale: vec2; var positive: f32; - var param_73: vec2; - var param_74: f32; - var param_75: f32; - var param_76: f32; - var negative: f32; - var param_77: vec2; - var param_78: f32; - var param_79: f32; + var param_79: vec2; var param_80: f32; + var param_81: f32; + var param_82: f32; + var negative: f32; + var param_83: vec2; + var param_84: f32; + var param_85: f32; + var param_86: f32; - let _e211 = (*depth_1); - param_72 = _e211; - let _e212 = EvsmWarp_u0028_f1_u003b((¶m_72)); - warped = _e212; - let _e213 = warped; - scale = (vec2(0.004f, 0.0005f) * _e213); - let _e216 = scale[0u]; - let _e218 = scale[0u]; - let _e220 = (*moments_1); - param_73 = _e220.xy; - let _e223 = warped[0u]; - param_74 = _e223; - param_75 = (_e216 * _e218); - let _e224 = (*bleed_1); - param_76 = _e224; - let _e225 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_73), (¶m_74), (¶m_75), (¶m_76)); - positive = _e225; - let _e227 = scale[1u]; - let _e229 = scale[1u]; - let _e231 = (*moments_1); - param_77 = _e231.zw; - let _e234 = warped[1u]; - param_78 = _e234; - param_79 = (_e227 * _e229); - let _e235 = (*bleed_1); - param_80 = _e235; - let _e236 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_77), (¶m_78), (¶m_79), (¶m_80)); - negative = _e236; - let _e237 = positive; - let _e238 = negative; - return min(_e237, _e238); -} - -fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_3: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { + let _e220 = (*depth_1); + param_78 = _e220; + let _e221 = EvsmWarp_u0028_f1_u003b((¶m_78)); + warped = _e221; + let _e222 = warped; + scale = (vec2(0.004f, 0.0005f) * _e222); + let _e225 = scale[0u]; + let _e227 = scale[0u]; + let _e229 = (*moments_1); + param_79 = _e229.xy; + let _e232 = warped[0u]; + param_80 = _e232; + param_81 = (_e225 * _e227); + let _e233 = (*bleed_1); + param_82 = _e233; + let _e234 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_79), (¶m_80), (¶m_81), (¶m_82)); + positive = _e234; + let _e236 = scale[1u]; + let _e238 = scale[1u]; + let _e240 = (*moments_1); + param_83 = _e240.zw; + let _e243 = warped[1u]; + param_84 = _e243; + param_85 = (_e236 * _e238); + let _e244 = (*bleed_1); + param_86 = _e244; + let _e245 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_83), (¶m_84), (¶m_85), (¶m_86)); + negative = _e245; + let _e246 = positive; + let _e247 = negative; + return min(_e246, _e247); +} + +fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_4: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { var strength_1: f32; var cascadeCount: i32; var viewDistance: f32; @@ -13477,8 +14079,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_6: mat4x4; - var local_7: mat4x4; + var local_8: mat4x4; + var local_9: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -13492,17 +14094,17 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_8: f32; - var local_9: f32; + var local_10: f32; + var local_11: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; var softness: f32; - var lit_1: f32; + var lit_2: f32; var moments_2: vec4; - var param_81: vec4; - var param_82: f32; - var param_83: f32; + var param_87: vec4; + var param_88: f32; + var param_89: f32; var y_1: i32; var x_1: i32; var occluder: f32; @@ -13513,65 +14115,65 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var blockerCount: f32; var i_5: i32; var disc: vec2; - var param_84: i32; - var param_85: i32; - var param_86: f32; + var param_90: i32; + var param_91: i32; + var param_92: f32; var tapBias: f32; var occluder_1: f32; var gap: f32; var radius_2: f32; var i_6: i32; var disc_1: vec2; - var param_87: i32; - var param_88: i32; - var param_89: f32; + var param_93: i32; + var param_94: i32; + var param_95: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3642_: bool; - var phi_3653_: bool; - var phi_3814_: bool; - var phi_3821_: bool; - var phi_3828_: bool; - - let _e263 = frag_info.shadow_params[3u]; - strength_1 = _e263; - let _e264 = strength_1; - if (_e264 <= 0f) { + var phi_3812_: bool; + var phi_3823_: bool; + var phi_3984_: bool; + var phi_3991_: bool; + var phi_3998_: bool; + + let _e272 = frag_info.shadow_params[3u]; + strength_1 = _e272; + let _e273 = strength_1; + if (_e273 <= 0f) { return 1f; } - let _e266 = (*lightIndex_1); - let _e269 = frag_info.frame_params[2u]; - if (_e266 != i32((_e269 + 0.5f))) { + let _e275 = (*lightIndex_1); + let _e278 = frag_info.frame_params[2u]; + if (_e275 != i32((_e278 + 0.5f))) { return 1f; } - let _e275 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e275 + 0.5f)); - let _e278 = v_world_position_1; - let _e280 = frag_info.camera_position; - viewDistance = length((_e278 - _e280.xyz)); + let _e284 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e284 + 0.5f)); + let _e287 = v_world_position_1; + let _e289 = frag_info.camera_position; + viewDistance = length((_e287 - _e289.xyz)); cascade = 0i; - let _e284 = cascadeCount; - let _e285 = (_e284 > 1i); - phi_3642_ = _e285; - if _e285 { - let _e286 = viewDistance; - let _e289 = frag_info.shadow_cascades[0u]; - phi_3642_ = (_e286 > _e289); - } - let _e292 = phi_3642_; - if _e292 { - cascade = 1i; - } let _e293 = cascadeCount; - let _e294 = (_e293 > 2i); - phi_3653_ = _e294; + let _e294 = (_e293 > 1i); + phi_3812_ = _e294; if _e294 { let _e295 = viewDistance; - let _e298 = frag_info.shadow_cascades[1u]; - phi_3653_ = (_e295 > _e298); + let _e298 = frag_info.shadow_cascades[0u]; + phi_3812_ = (_e295 > _e298); } - let _e301 = phi_3653_; + let _e301 = phi_3812_; if _e301 { + cascade = 1i; + } + let _e302 = cascadeCount; + let _e303 = (_e302 > 2i); + phi_3823_ = _e303; + if _e303 { + let _e304 = viewDistance; + let _e307 = frag_info.shadow_cascades[1u]; + phi_3823_ = (_e304 > _e307); + } + let _e310 = phi_3823_; + if _e310 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -13582,232 +14184,232 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e302 = attempt; - if (_e302 < 3i) { - let _e304 = cascade; - let _e305 = attempt; - which = (_e304 + _e305); - let _e307 = which; - let _e308 = cascadeCount; - if (_e307 >= _e308) { + let _e311 = attempt; + if (_e311 < 3i) { + let _e313 = cascade; + let _e314 = attempt; + which = (_e313 + _e314); + let _e316 = which; + let _e317 = cascadeCount; + if (_e316 >= _e317) { break; } - let _e310 = which; - if (_e310 == 0i) { - let _e313 = frag_info.shadow_matrix; - local_6 = _e313; + let _e319 = which; + if (_e319 == 0i) { + let _e322 = frag_info.shadow_matrix; + local_8 = _e322; } else { - let _e314 = which; - if (_e314 == 1i) { - let _e317 = frag_info.shadow_matrix_far; - local_7 = _e317; + let _e323 = which; + if (_e323 == 1i) { + let _e326 = frag_info.shadow_matrix_far; + local_9 = _e326; } else { - let _e319 = frag_info.shadow_matrix_farthest; - local_7 = _e319; + let _e328 = frag_info.shadow_matrix_farthest; + local_9 = _e328; } - let _e320 = local_7; - local_6 = _e320; - } - let _e321 = local_6; - matrix = _e321; - let _e324 = matrix[0][0u]; - let _e327 = matrix[1][0u]; - let _e330 = matrix[2][0u]; - axisX = vec3(_e324, _e327, _e330); - let _e334 = matrix[0][1u]; - let _e337 = matrix[1][1u]; - let _e340 = matrix[2][1u]; - axisY = vec3(_e334, _e337, _e340); - let _e342 = axisX; - rowX = max(length(_e342), 0.000001f); - let _e345 = axisY; - rowY = max(length(_e345), 0.000001f); - let _e350 = frag_info.shadow_cascades[3u]; - let _e352 = rowX; - texelMetres = ((2f * _e350) / _e352); - let _e356 = frag_info.shadow_cascades[3u]; - let _e359 = (*s_3).n; - let _e360 = axisX; - let _e363 = rowX; - let _e364 = rowX; - let _e368 = (*s_3).n; - let _e369 = axisY; - let _e372 = rowY; - let _e373 = rowY; - reach = ((3f * _e356) * ((abs(dot(_e359, _e360)) / (_e363 * _e364)) + (abs(dot(_e368, _e369)) / (_e372 * _e373)))); - let _e380 = frag_info.shadow_params[2u]; - let _e381 = texelMetres; - flatOffset = min(_e380, _e381); - let _e383 = v_world_position_1; - let _e385 = (*s_3).n; - let _e386 = flatOffset; - let _e387 = reach; - origin_1 = (_e383 + (_e385 * (_e386 + _e387))); - let _e391 = matrix; - let _e392 = origin_1; - lightSpace = (_e391 * vec4(_e392.x, _e392.y, _e392.z, 1f)); - let _e399 = lightSpace[3u]; - if (_e399 <= 0f) { + let _e329 = local_9; + local_8 = _e329; + } + let _e330 = local_8; + matrix = _e330; + let _e333 = matrix[0][0u]; + let _e336 = matrix[1][0u]; + let _e339 = matrix[2][0u]; + axisX = vec3(_e333, _e336, _e339); + let _e343 = matrix[0][1u]; + let _e346 = matrix[1][1u]; + let _e349 = matrix[2][1u]; + axisY = vec3(_e343, _e346, _e349); + let _e351 = axisX; + rowX = max(length(_e351), 0.000001f); + let _e354 = axisY; + rowY = max(length(_e354), 0.000001f); + let _e359 = frag_info.shadow_cascades[3u]; + let _e361 = rowX; + texelMetres = ((2f * _e359) / _e361); + let _e365 = frag_info.shadow_cascades[3u]; + let _e368 = (*s_4).n; + let _e369 = axisX; + let _e372 = rowX; + let _e373 = rowX; + let _e377 = (*s_4).n; + let _e378 = axisY; + let _e381 = rowY; + let _e382 = rowY; + reach = ((3f * _e365) * ((abs(dot(_e368, _e369)) / (_e372 * _e373)) + (abs(dot(_e377, _e378)) / (_e381 * _e382)))); + let _e389 = frag_info.shadow_params[2u]; + let _e390 = texelMetres; + flatOffset = min(_e389, _e390); + let _e392 = v_world_position_1; + let _e394 = (*s_4).n; + let _e395 = flatOffset; + let _e396 = reach; + origin_1 = (_e392 + (_e394 * (_e395 + _e396))); + let _e400 = matrix; + let _e401 = origin_1; + lightSpace = (_e400 * vec4(_e401.x, _e401.y, _e401.z, 1f)); + let _e408 = lightSpace[3u]; + if (_e408 <= 0f) { continue; } - let _e401 = lightSpace; - let _e404 = lightSpace[3u]; - candidate = (_e401.xyz / vec3(_e404)); - let _e408 = candidate[0u]; - let _e412 = candidate[1u]; - inTile = vec2(((_e408 * 0.5f) + 0.5f), (0.5f - (_e412 * 0.5f))); - let _e417 = inTile[0u]; - let _e418 = (_e417 < 0f); - phi_3814_ = _e418; - if !(_e418) { - let _e421 = inTile[0u]; - phi_3814_ = (_e421 > 1f); - } - let _e424 = phi_3814_; - phi_3821_ = _e424; - if !(_e424) { - let _e427 = inTile[1u]; - phi_3821_ = (_e427 < 0f); - } - let _e430 = phi_3821_; - phi_3828_ = _e430; - if !(_e430) { - let _e433 = inTile[1u]; - phi_3828_ = (_e433 > 1f); - } - let _e436 = phi_3828_; - if _e436 { + let _e410 = lightSpace; + let _e413 = lightSpace[3u]; + candidate = (_e410.xyz / vec3(_e413)); + let _e417 = candidate[0u]; + let _e421 = candidate[1u]; + inTile = vec2(((_e417 * 0.5f) + 0.5f), (0.5f - (_e421 * 0.5f))); + let _e426 = inTile[0u]; + let _e427 = (_e426 < 0f); + phi_3984_ = _e427; + if !(_e427) { + let _e430 = inTile[0u]; + phi_3984_ = (_e430 > 1f); + } + let _e433 = phi_3984_; + phi_3991_ = _e433; + if !(_e433) { + let _e436 = inTile[1u]; + phi_3991_ = (_e436 < 0f); + } + let _e439 = phi_3991_; + phi_3998_ = _e439; + if !(_e439) { + let _e442 = inTile[1u]; + phi_3998_ = (_e442 > 1f); + } + let _e445 = phi_3998_; + if _e445 { continue; } - let _e438 = candidate[2u]; - if (_e438 > 1f) { - let _e440 = which; - let _e441 = cascadeCount; - if (_e440 < (_e441 - 1i)) { + let _e447 = candidate[2u]; + if (_e447 > 1f) { + let _e449 = which; + let _e450 = cascadeCount; + if (_e449 < (_e450 - 1i)) { continue; } candidate[2u] = 1f; } - let _e446 = inTile[0u]; - let _e447 = which; - let _e450 = cascadeCount; - let _e454 = inTile[1u]; - uv_4 = vec2(((_e446 + f32(_e447)) / f32(_e450)), _e454); - let _e456 = candidate; - projected = _e456; - let _e457 = which; - cascade = _e457; - let _e458 = texelMetres; - cascadeTexel = _e458; - let _e461 = matrix[0][2u]; - let _e464 = matrix[1][2u]; - let _e467 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e461, _e464, _e467)), 0.000001f)); - let _e473 = (*s_3).n; - let _e476 = matrix[0][2u]; - let _e479 = matrix[1][2u]; - let _e482 = matrix[2][2u]; - let _e486 = cascadeDepth; - cosSlope = clamp((abs(dot(_e473, vec3(_e476, _e479, _e482))) * _e486), 0.1f, 1f); - let _e489 = texelMetres; - let _e490 = cosSlope; - let _e491 = cosSlope; - let _e497 = cosSlope; - let _e499 = cascadeDepth; - receiverSlope = (((_e489 * sqrt(max((1f - (_e490 * _e491)), 0f))) / _e497) / _e499); + let _e455 = inTile[0u]; + let _e456 = which; + let _e459 = cascadeCount; + let _e463 = inTile[1u]; + uv_4 = vec2(((_e455 + f32(_e456)) / f32(_e459)), _e463); + let _e465 = candidate; + projected = _e465; + let _e466 = which; + cascade = _e466; + let _e467 = texelMetres; + cascadeTexel = _e467; + let _e470 = matrix[0][2u]; + let _e473 = matrix[1][2u]; + let _e476 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e470, _e473, _e476)), 0.000001f)); + let _e482 = (*s_4).n; + let _e485 = matrix[0][2u]; + let _e488 = matrix[1][2u]; + let _e491 = matrix[2][2u]; + let _e495 = cascadeDepth; + cosSlope = clamp((abs(dot(_e482, vec3(_e485, _e488, _e491))) * _e495), 0.1f, 1f); + let _e498 = texelMetres; + let _e499 = cosSlope; + let _e500 = cosSlope; + let _e506 = cosSlope; + let _e508 = cascadeDepth; + receiverSlope = (((_e498 * sqrt(max((1f - (_e499 * _e500)), 0f))) / _e506) / _e508); found = true; break; } else { break; } continuing { - let _e501 = attempt; - attempt = (_e501 + 1i); + let _e510 = attempt; + attempt = (_e510 + 1i); } } - let _e503 = found; - if !(_e503) { + let _e512 = found; + if !(_e512) { return 1f; } - let _e505 = cascade; - if (_e505 == 0i) { - let _e509 = frag_info.shadow_bias[0u]; - local_8 = _e509; + let _e514 = cascade; + if (_e514 == 0i) { + let _e518 = frag_info.shadow_bias[0u]; + local_10 = _e518; } else { - let _e510 = cascade; - if (_e510 == 1i) { - let _e514 = frag_info.shadow_bias[1u]; - local_9 = _e514; + let _e519 = cascade; + if (_e519 == 1i) { + let _e523 = frag_info.shadow_bias[1u]; + local_11 = _e523; } else { - let _e517 = frag_info.shadow_bias[2u]; - local_9 = _e517; + let _e526 = frag_info.shadow_bias[2u]; + local_11 = _e526; } - let _e518 = local_9; - local_8 = _e518; + let _e527 = local_11; + local_10 = _e527; } - let _e519 = local_8; - bias = _e519; - let _e522 = frag_info.shadow_params[0u]; - let _e525 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e522, _e525); - let _e527 = cascade; - let _e529 = cascadeCount; - let _e533 = texel_1; - tileLo = (vec2((f32(_e527) / f32(_e529)), 0f) + (_e533 * 0.5f)); + let _e528 = local_10; + bias = _e528; + let _e531 = frag_info.shadow_params[0u]; + let _e534 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e531, _e534); let _e536 = cascade; - let _e539 = cascadeCount; - let _e543 = texel_1; - tileHi = (vec2((f32((_e536 + 1i)) / f32(_e539)), 1f) - (_e543 * 0.5f)); - let _e548 = frag_info.ambient_ground[3u]; - softness = _e548; - lit_1 = 0f; - let _e549 = softness; - if (_e549 < 0f) { - let _e551 = uv_4; - let _e552 = tileLo; - let _e553 = tileHi; - let _e555 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e551, _e552, _e553), 0f); - moments_2 = _e555; - let _e557 = projected[2u]; - let _e558 = bias; - let _e560 = softness; - let _e564 = moments_2; - param_81 = _e564; - param_82 = (_e557 - _e558); - param_83 = clamp((-(_e560) - 1f), 0f, 0.95f); - let _e565 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_81), (¶m_82), (¶m_83)); - lit_1 = _e565; + let _e538 = cascadeCount; + let _e542 = texel_1; + tileLo = (vec2((f32(_e536) / f32(_e538)), 0f) + (_e542 * 0.5f)); + let _e545 = cascade; + let _e548 = cascadeCount; + let _e552 = texel_1; + tileHi = (vec2((f32((_e545 + 1i)) / f32(_e548)), 1f) - (_e552 * 0.5f)); + let _e557 = frag_info.ambient_ground[3u]; + softness = _e557; + lit_2 = 0f; + let _e558 = softness; + if (_e558 < 0f) { + let _e560 = uv_4; + let _e561 = tileLo; + let _e562 = tileHi; + let _e564 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e560, _e561, _e562), 0f); + moments_2 = _e564; + let _e566 = projected[2u]; + let _e567 = bias; + let _e569 = softness; + let _e573 = moments_2; + param_87 = _e573; + param_88 = (_e566 - _e567); + param_89 = clamp((-(_e569) - 1f), 0f, 0.95f); + let _e574 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_87), (¶m_88), (¶m_89)); + lit_2 = _e574; } else { - let _e566 = softness; - if (_e566 <= 0f) { + let _e575 = softness; + if (_e575 <= 0f) { y_1 = -1i; loop { - let _e568 = y_1; - if (_e568 <= 1i) { + let _e577 = y_1; + if (_e577 <= 1i) { x_1 = -1i; loop { - let _e570 = x_1; - if (_e570 <= 1i) { - let _e572 = uv_4; - let _e573 = x_1; - let _e575 = y_1; - let _e578 = texel_1; - let _e581 = tileLo; - let _e582 = tileHi; - let _e584 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e572 + (vec2(f32(_e573), f32(_e575)) * _e578)), _e581, _e582), 0f); - occluder = _e584.x; - let _e587 = projected[2u]; - let _e588 = bias; - let _e590 = occluder; - let _e593 = lit_1; - lit_1 = (_e593 + select(1f, 0f, ((_e587 - _e588) > _e590))); + let _e579 = x_1; + if (_e579 <= 1i) { + let _e581 = uv_4; + let _e582 = x_1; + let _e584 = y_1; + let _e587 = texel_1; + let _e590 = tileLo; + let _e591 = tileHi; + let _e593 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e581 + (vec2(f32(_e582), f32(_e584)) * _e587)), _e590, _e591), 0f); + occluder = _e593.x; + let _e596 = projected[2u]; + let _e597 = bias; + let _e599 = occluder; + let _e602 = lit_2; + lit_2 = (_e602 + select(1f, 0f, ((_e596 - _e597) > _e599))); continue; } else { break; } continuing { - let _e595 = x_1; - x_1 = (_e595 + 1i); + let _e604 = x_1; + x_1 = (_e604 + 1i); } } continue; @@ -13815,145 +14417,145 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e597 = y_1; - y_1 = (_e597 + 1i); + let _e606 = y_1; + y_1 = (_e606 + 1i); } } - let _e599 = lit_1; - lit_1 = (_e599 * 0.11111111f); + let _e608 = lit_2; + lit_2 = (_e608 * 0.11111111f); } else { - let _e601 = softness; - spread = tan(min(_e601, 0.5f)); - let _e604 = ShadowNoise_u0028_(); - turn_1 = (_e604 * 6.2831855f); - let _e606 = spread; - let _e608 = projected[2u]; - let _e610 = cascadeDepth; - let _e612 = cascadeTexel; - searchRadius = clamp((((_e606 * _e608) * _e610) / _e612), 1f, 16f); + let _e610 = softness; + spread = tan(min(_e610, 0.5f)); + let _e613 = ShadowNoise_u0028_(); + turn_1 = (_e613 * 6.2831855f); + let _e615 = spread; + let _e617 = projected[2u]; + let _e619 = cascadeDepth; + let _e621 = cascadeTexel; + searchRadius = clamp((((_e615 * _e617) * _e619) / _e621), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e615 = i_5; - if (_e615 < 16i) { - let _e617 = i_5; - param_84 = _e617; - param_85 = 16i; - let _e618 = turn_1; - param_86 = _e618; - let _e619 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_84), (¶m_85), (¶m_86)); - disc = _e619; - let _e620 = bias; - let _e621 = disc; - let _e623 = searchRadius; - let _e627 = receiverSlope; - tapBias = (_e620 + (max(((length(_e621) * _e623) - 1.5f), 0f) * _e627)); - let _e630 = uv_4; - let _e631 = disc; - let _e632 = texel_1; - let _e634 = searchRadius; - let _e637 = tileLo; - let _e638 = tileHi; - let _e640 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e630 + ((_e631 * _e632) * _e634)), _e637, _e638), 0f); - occluder_1 = _e640.x; - let _e643 = projected[2u]; - let _e644 = tapBias; - let _e646 = occluder_1; - if ((_e643 - _e644) > _e646) { - let _e648 = occluder_1; - let _e649 = blockerSum; - blockerSum = (_e649 + _e648); - let _e651 = blockerCount; - blockerCount = (_e651 + 1f); + let _e624 = i_5; + if (_e624 < 16i) { + let _e626 = i_5; + param_90 = _e626; + param_91 = 16i; + let _e627 = turn_1; + param_92 = _e627; + let _e628 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_90), (¶m_91), (¶m_92)); + disc = _e628; + let _e629 = bias; + let _e630 = disc; + let _e632 = searchRadius; + let _e636 = receiverSlope; + tapBias = (_e629 + (max(((length(_e630) * _e632) - 1.5f), 0f) * _e636)); + let _e639 = uv_4; + let _e640 = disc; + let _e641 = texel_1; + let _e643 = searchRadius; + let _e646 = tileLo; + let _e647 = tileHi; + let _e649 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e639 + ((_e640 * _e641) * _e643)), _e646, _e647), 0f); + occluder_1 = _e649.x; + let _e652 = projected[2u]; + let _e653 = tapBias; + let _e655 = occluder_1; + if ((_e652 - _e653) > _e655) { + let _e657 = occluder_1; + let _e658 = blockerSum; + blockerSum = (_e658 + _e657); + let _e660 = blockerCount; + blockerCount = (_e660 + 1f); } continue; } else { break; } continuing { - let _e653 = i_5; - i_5 = (_e653 + 1i); + let _e662 = i_5; + i_5 = (_e662 + 1i); } } - let _e655 = blockerCount; - if (_e655 <= 0f) { + let _e664 = blockerCount; + if (_e664 <= 0f) { return 1f; } - let _e658 = projected[2u]; - let _e659 = blockerSum; - let _e660 = blockerCount; - let _e664 = cascadeDepth; - gap = (max((_e658 - (_e659 / _e660)), 0f) * _e664); - let _e666 = spread; - let _e667 = gap; - let _e669 = cascadeTexel; - radius_2 = clamp(((_e666 * _e667) / _e669), 1f, 16f); + let _e667 = projected[2u]; + let _e668 = blockerSum; + let _e669 = blockerCount; + let _e673 = cascadeDepth; + gap = (max((_e667 - (_e668 / _e669)), 0f) * _e673); + let _e675 = spread; + let _e676 = gap; + let _e678 = cascadeTexel; + radius_2 = clamp(((_e675 * _e676) / _e678), 1f, 16f); i_6 = 0i; loop { - let _e672 = i_6; - if (_e672 < 16i) { - let _e674 = turn_1; - let _e676 = i_6; - param_87 = _e676; - param_88 = 16i; - param_89 = (_e674 + 1f); - let _e677 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_87), (¶m_88), (¶m_89)); - disc_1 = _e677; - let _e678 = bias; - let _e679 = disc_1; - let _e681 = radius_2; - let _e685 = receiverSlope; - tapBias_1 = (_e678 + (max(((length(_e679) * _e681) - 1.5f), 0f) * _e685)); - let _e688 = uv_4; - let _e689 = disc_1; - let _e690 = texel_1; - let _e692 = radius_2; - let _e695 = tileLo; - let _e696 = tileHi; - let _e698 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e688 + ((_e689 * _e690) * _e692)), _e695, _e696), 0f); - occluder_2 = _e698.x; - let _e701 = projected[2u]; - let _e702 = tapBias_1; - let _e704 = occluder_2; - let _e707 = lit_1; - lit_1 = (_e707 + select(1f, 0f, ((_e701 - _e702) > _e704))); + let _e681 = i_6; + if (_e681 < 16i) { + let _e683 = turn_1; + let _e685 = i_6; + param_93 = _e685; + param_94 = 16i; + param_95 = (_e683 + 1f); + let _e686 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_93), (¶m_94), (¶m_95)); + disc_1 = _e686; + let _e687 = bias; + let _e688 = disc_1; + let _e690 = radius_2; + let _e694 = receiverSlope; + tapBias_1 = (_e687 + (max(((length(_e688) * _e690) - 1.5f), 0f) * _e694)); + let _e697 = uv_4; + let _e698 = disc_1; + let _e699 = texel_1; + let _e701 = radius_2; + let _e704 = tileLo; + let _e705 = tileHi; + let _e707 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e697 + ((_e698 * _e699) * _e701)), _e704, _e705), 0f); + occluder_2 = _e707.x; + let _e710 = projected[2u]; + let _e711 = tapBias_1; + let _e713 = occluder_2; + let _e716 = lit_2; + lit_2 = (_e716 + select(1f, 0f, ((_e710 - _e711) > _e713))); continue; } else { break; } continuing { - let _e709 = i_6; - i_6 = (_e709 + 1i); + let _e718 = i_6; + i_6 = (_e718 + 1i); } } - let _e711 = lit_1; - lit_1 = (_e711 * 0.0625f); + let _e720 = lit_2; + lit_2 = (_e720 * 0.0625f); } } - let _e713 = lit_1; - let _e714 = strength_1; - return mix(1f, _e713, clamp(_e714, 0f, 1f)); + let _e722 = lit_2; + let _e723 = strength_1; + return mix(1f, _e722, clamp(_e723, 0f, 1f)); } -fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_4: ptr, light_2: ptr, index: ptr) -> f32 { - var param_90: Surface; - var param_91: LightSample; - var param_92: i32; +fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_5: ptr, light_2: ptr, index: ptr) -> f32 { + var param_96: Surface; + var param_97: LightSample; + var param_98: i32; - let _e201 = (*s_4); - param_90 = _e201; - let _e202 = (*light_2); - param_91 = _e202; - let _e203 = (*index); - param_92 = _e203; - let _e204 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_90), (¶m_91), (¶m_92)); - return _e204; + let _e210 = (*s_5); + param_96 = _e210; + let _e211 = (*light_2); + param_97 = _e211; + let _e212 = (*index); + param_98 = _e212; + let _e213 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_96), (¶m_97), (¶m_98)); + return _e213; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e196 = (*index_1); - return (_e196 < 8i); + let _e205 = (*index_1); + return (_e205 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -13961,211 +14563,211 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e200 = (*distance_1); - let _e201 = (*distance_1); - attenuation = (1f / max((_e200 * _e201), 0.0001f)); - let _e205 = (*range_4); - if (_e205 > 0f) { - let _e207 = (*distance_1); - let _e208 = (*range_4); - ratio = (_e207 / _e208); - let _e210 = ratio; - let _e211 = ratio; - let _e213 = ratio; - let _e215 = ratio; - window = clamp((1f - (((_e210 * _e211) * _e213) * _e215)), 0f, 1f); - let _e219 = window; - let _e220 = window; - let _e222 = attenuation; - attenuation = (_e222 * (_e219 * _e220)); - } - let _e224 = attenuation; - return _e224; + let _e209 = (*distance_1); + let _e210 = (*distance_1); + attenuation = (1f / max((_e209 * _e210), 0.0001f)); + let _e214 = (*range_4); + if (_e214 > 0f) { + let _e216 = (*distance_1); + let _e217 = (*range_4); + ratio = (_e216 / _e217); + let _e219 = ratio; + let _e220 = ratio; + let _e222 = ratio; + let _e224 = ratio; + window = clamp((1f - (((_e219 * _e220) * _e222) * _e224)), 0f, 1f); + let _e228 = window; + let _e229 = window; + let _e231 = attenuation; + attenuation = (_e231 * (_e228 * _e229)); + } + let _e233 = attenuation; + return _e233; } fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_2: ptr>, b: ptr>) -> vec3 { var axis: vec3; var len: f32; var angle_1: f32; - var local_10: vec3; - - let _e201 = (*a_2); - let _e202 = (*b); - axis = cross(_e201, _e202); - let _e204 = axis; - len = length(_e204); - let _e206 = (*a_2); - let _e207 = (*b); - angle_1 = acos(clamp(dot(_e206, _e207), -1f, 1f)); - let _e211 = len; - if (_e211 > 0.000001f) { - let _e213 = axis; - let _e214 = angle_1; - let _e215 = len; - local_10 = (_e213 * (_e214 / (_e215 * 6.2831855f))); + var local_12: vec3; + + let _e210 = (*a_2); + let _e211 = (*b); + axis = cross(_e210, _e211); + let _e213 = axis; + len = length(_e213); + let _e215 = (*a_2); + let _e216 = (*b); + angle_1 = acos(clamp(dot(_e215, _e216), -1f, 1f)); + let _e220 = len; + if (_e220 > 0.000001f) { + let _e222 = axis; + let _e223 = angle_1; + let _e224 = len; + local_12 = (_e222 * (_e223 / (_e224 * 6.2831855f))); } else { - local_10 = vec3(0f, 0f, 0f); + local_12 = vec3(0f, 0f, 0f); } - let _e219 = local_10; - return _e219; + let _e228 = local_12; + return _e228; } fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: ptr>, v: ptr>, roughness_3: ptr, corners: ptr, 4>>) -> vec3 { var uv_5: vec2; var inverse: vec4; - var param_93: f32; - var param_94: f32; - var param_95: f32; + var param_99: f32; + var param_100: f32; + var param_101: f32; var fit: vec4; - var param_96: f32; - var param_97: f32; - var param_98: f32; + var param_102: f32; + var param_103: f32; + var param_104: f32; var along: vec3; var alongLength: f32; var t1_: vec3; - var local_11: vec3; + var local_13: vec3; var t2_: vec3; var minv: mat3x3; var i_7: i32; var p_1: vec3; var l: array, 4>; var f_2: vec3; - var param_99: vec3; - var param_100: vec3; - var param_101: vec3; - var param_102: vec3; - var param_103: vec3; - var param_104: vec3; var param_105: vec3; var param_106: vec3; + var param_107: vec3; + var param_108: vec3; + var param_109: vec3; + var param_110: vec3; + var param_111: vec3; + var param_112: vec3; var len_1: f32; var z_1: f32; - var local_12: f32; + var local_14: f32; var sphere: f32; - var param_107: f32; - var param_108: f32; - var param_109: f32; + var param_113: f32; + var param_114: f32; + var param_115: f32; - let _e233 = (*roughness_3); - let _e235 = (*n_2); - let _e236 = (*v); - uv_5 = vec2(clamp(_e233, 0f, 1f), sqrt(clamp((1f - dot(_e235, _e236)), 0f, 1f))); - let _e243 = uv_5[0u]; - param_93 = _e243; - let _e245 = uv_5[1u]; - param_94 = _e245; - param_95 = 0f; - let _e246 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_93), (¶m_94), (¶m_95)); - let _e247 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e246, 0f); - inverse = _e247; - let _e249 = uv_5[0u]; - param_96 = _e249; - let _e251 = uv_5[1u]; - param_97 = _e251; - param_98 = 1f; - let _e252 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_96), (¶m_97), (¶m_98)); - let _e253 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e252, 0f); - fit = _e253; - let _e254 = (*v); - let _e255 = (*n_2); - let _e256 = (*v); - let _e257 = (*n_2); - along = (_e254 - (_e255 * dot(_e256, _e257))); - let _e261 = along; - alongLength = length(_e261); - let _e263 = alongLength; - if (_e263 > 0.00001f) { - let _e265 = along; - let _e266 = alongLength; - local_11 = (_e265 / vec3(_e266)); + let _e242 = (*roughness_3); + let _e244 = (*n_2); + let _e245 = (*v); + uv_5 = vec2(clamp(_e242, 0f, 1f), sqrt(clamp((1f - dot(_e244, _e245)), 0f, 1f))); + let _e252 = uv_5[0u]; + param_99 = _e252; + let _e254 = uv_5[1u]; + param_100 = _e254; + param_101 = 0f; + let _e255 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_99), (¶m_100), (¶m_101)); + let _e256 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e255, 0f); + inverse = _e256; + let _e258 = uv_5[0u]; + param_102 = _e258; + let _e260 = uv_5[1u]; + param_103 = _e260; + param_104 = 1f; + let _e261 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_102), (¶m_103), (¶m_104)); + let _e262 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e261, 0f); + fit = _e262; + let _e263 = (*v); + let _e264 = (*n_2); + let _e265 = (*v); + let _e266 = (*n_2); + along = (_e263 - (_e264 * dot(_e265, _e266))); + let _e270 = along; + alongLength = length(_e270); + let _e272 = alongLength; + if (_e272 > 0.00001f) { + let _e274 = along; + let _e275 = alongLength; + local_13 = (_e274 / vec3(_e275)); } else { - let _e269 = (*n_2); - let _e271 = (*n_2)[2u]; - local_11 = normalize(cross(_e269, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e271) < 0.999f))))); - } - let _e278 = local_11; - t1_ = _e278; - let _e279 = (*n_2); - let _e280 = t1_; - t2_ = cross(_e279, _e280); - let _e283 = inverse[0u]; - let _e285 = inverse[1u]; - let _e286 = vec3(_e283, 0f, _e285); - let _e288 = inverse[2u]; - let _e290 = inverse[3u]; - let _e291 = vec3(_e288, 0f, _e290); - minv = mat3x3(vec3(_e286.x, _e286.y, _e286.z), vec3(vec3(0f, 1f, 0f).x, vec3(0f, 1f, 0f).y, vec3(0f, 1f, 0f).z), vec3(_e291.x, _e291.y, _e291.z)); + let _e278 = (*n_2); + let _e280 = (*n_2)[2u]; + local_13 = normalize(cross(_e278, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e280) < 0.999f))))); + } + let _e287 = local_13; + t1_ = _e287; + let _e288 = (*n_2); + let _e289 = t1_; + t2_ = cross(_e288, _e289); + let _e292 = inverse[0u]; + let _e294 = inverse[1u]; + let _e295 = vec3(_e292, 0f, _e294); + let _e297 = inverse[2u]; + let _e299 = inverse[3u]; + let _e300 = vec3(_e297, 0f, _e299); + minv = mat3x3(vec3(_e295.x, _e295.y, _e295.z), vec3(vec3(0f, 1f, 0f).x, vec3(0f, 1f, 0f).y, vec3(0f, 1f, 0f).z), vec3(_e300.x, _e300.y, _e300.z)); i_7 = 0i; loop { - let _e305 = i_7; - if (_e305 < 4i) { - let _e307 = i_7; - let _e309 = (*corners)[_e307]; - p_1 = _e309; - let _e310 = i_7; - let _e311 = minv; - let _e312 = p_1; - let _e313 = t1_; - let _e315 = p_1; - let _e316 = t2_; - let _e318 = p_1; - let _e319 = (*n_2); - l[_e310] = normalize((_e311 * vec3(dot(_e312, _e313), dot(_e315, _e316), dot(_e318, _e319)))); + let _e314 = i_7; + if (_e314 < 4i) { + let _e316 = i_7; + let _e318 = (*corners)[_e316]; + p_1 = _e318; + let _e319 = i_7; + let _e320 = minv; + let _e321 = p_1; + let _e322 = t1_; + let _e324 = p_1; + let _e325 = t2_; + let _e327 = p_1; + let _e328 = (*n_2); + l[_e319] = normalize((_e320 * vec3(dot(_e321, _e322), dot(_e324, _e325), dot(_e327, _e328)))); continue; } else { break; } continuing { - let _e325 = i_7; - i_7 = (_e325 + 1i); - } - } - let _e328 = l[0i]; - param_99 = _e328; - let _e330 = l[1i]; - param_100 = _e330; - let _e331 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_99), (¶m_100)); - let _e333 = l[1i]; - param_101 = _e333; - let _e335 = l[2i]; - param_102 = _e335; - let _e336 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_101), (¶m_102)); - let _e339 = l[2i]; - param_103 = _e339; - let _e341 = l[3i]; - param_104 = _e341; - let _e342 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_103), (¶m_104)); - let _e345 = l[3i]; - param_105 = _e345; - let _e347 = l[0i]; - param_106 = _e347; - let _e348 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_105), (¶m_106)); - f_2 = -((((_e331 + _e336) + _e342) + _e348)); - let _e351 = f_2; - len_1 = length(_e351); - let _e353 = len_1; - if (_e353 > 0.000000001f) { - let _e356 = f_2[2u]; - let _e357 = len_1; - local_12 = (_e356 / _e357); + let _e334 = i_7; + i_7 = (_e334 + 1i); + } + } + let _e337 = l[0i]; + param_105 = _e337; + let _e339 = l[1i]; + param_106 = _e339; + let _e340 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_105), (¶m_106)); + let _e342 = l[1i]; + param_107 = _e342; + let _e344 = l[2i]; + param_108 = _e344; + let _e345 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_107), (¶m_108)); + let _e348 = l[2i]; + param_109 = _e348; + let _e350 = l[3i]; + param_110 = _e350; + let _e351 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_109), (¶m_110)); + let _e354 = l[3i]; + param_111 = _e354; + let _e356 = l[0i]; + param_112 = _e356; + let _e357 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_111), (¶m_112)); + f_2 = -((((_e340 + _e345) + _e351) + _e357)); + let _e360 = f_2; + len_1 = length(_e360); + let _e362 = len_1; + if (_e362 > 0.000000001f) { + let _e365 = f_2[2u]; + let _e366 = len_1; + local_14 = (_e365 / _e366); } else { - local_12 = 0f; + local_14 = 0f; } - let _e359 = local_12; - z_1 = _e359; - let _e360 = z_1; - let _e363 = len_1; - param_107 = ((_e360 * 0.5f) + 0.5f); - param_108 = clamp(_e363, 0f, 1f); - param_109 = 1f; - let _e365 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_107), (¶m_108), (¶m_109)); - let _e366 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e365, 0f); - sphere = _e366.w; - let _e368 = len_1; - let _e369 = sphere; - let _e373 = fit[0u]; - let _e375 = fit[1u]; - return vec3(max((_e368 * _e369), 0f), _e373, _e375); + let _e368 = local_14; + z_1 = _e368; + let _e369 = z_1; + let _e372 = len_1; + param_113 = ((_e369 * 0.5f) + 0.5f); + param_114 = clamp(_e372, 0f, 1f); + param_115 = 1f; + let _e374 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_113), (¶m_114), (¶m_115)); + let _e375 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e374, 0f); + sphere = _e375.w; + let _e377 = len_1; + let _e378 = sphere; + let _e382 = fit[0u]; + let _e384 = fit[1u]; + return vec3(max((_e377 * _e378), 0f), _e382, _e384); } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -14175,82 +14777,82 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_13: vec3; + var local_15: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; var u: f32; var v_1: f32; - let _e212 = (*halfWidth); - let _e213 = (*halfHeight); - n_3 = cross(_e212, _e213); - let _e215 = n_3; - nLen = length(_e215); - let _e217 = nLen; - if (_e217 < 0.000000000001f) { - let _e219 = (*centre); - return _e219; - } - let _e220 = nLen; - let _e221 = n_3; - n_3 = (_e221 / vec3(_e220)); - let _e224 = (*centre); - let _e225 = (*world_2); - toPlane = (_e224 - _e225); - let _e227 = (*mirror); - let _e228 = n_3; - denom = dot(_e227, _e228); - let _e230 = denom; - if (abs(_e230) < 0.00001f) { - let _e233 = toPlane; - let _e234 = n_3; - let _e235 = toPlane; - let _e236 = n_3; - onPlane = (_e233 - (_e234 * dot(_e235, _e236))); + let _e221 = (*halfWidth); + let _e222 = (*halfHeight); + n_3 = cross(_e221, _e222); + let _e224 = n_3; + nLen = length(_e224); + let _e226 = nLen; + if (_e226 < 0.000000000001f) { + let _e228 = (*centre); + return _e228; + } + let _e229 = nLen; + let _e230 = n_3; + n_3 = (_e230 / vec3(_e229)); + let _e233 = (*centre); + let _e234 = (*world_2); + toPlane = (_e233 - _e234); + let _e236 = (*mirror); + let _e237 = n_3; + denom = dot(_e236, _e237); + let _e239 = denom; + if (abs(_e239) < 0.00001f) { + let _e242 = toPlane; + let _e243 = n_3; + let _e244 = toPlane; + let _e245 = n_3; + onPlane = (_e242 - (_e243 * dot(_e244, _e245))); } else { - let _e240 = toPlane; - let _e241 = n_3; - let _e243 = denom; - t_1 = (dot(_e240, _e241) / _e243); - let _e245 = t_1; - if (_e245 > 0f) { - let _e247 = (*mirror); - let _e248 = t_1; - local_13 = (_e247 * _e248); + let _e249 = toPlane; + let _e250 = n_3; + let _e252 = denom; + t_1 = (dot(_e249, _e250) / _e252); + let _e254 = t_1; + if (_e254 > 0f) { + let _e256 = (*mirror); + let _e257 = t_1; + local_15 = (_e256 * _e257); } else { - let _e250 = toPlane; - let _e251 = n_3; - let _e252 = toPlane; - let _e253 = n_3; - local_13 = (_e250 - (_e251 * dot(_e252, _e253))); - } - let _e257 = local_13; - onPlane = _e257; - } - let _e258 = onPlane; - let _e259 = toPlane; - offset_2 = (_e258 - _e259); - let _e261 = (*halfWidth); - let _e262 = (*halfWidth); - wLen2_ = max(dot(_e261, _e262), 0.000000000001f); - let _e265 = (*halfHeight); - let _e266 = (*halfHeight); - hLen2_ = max(dot(_e265, _e266), 0.000000000001f); - let _e269 = offset_2; + let _e259 = toPlane; + let _e260 = n_3; + let _e261 = toPlane; + let _e262 = n_3; + local_15 = (_e259 - (_e260 * dot(_e261, _e262))); + } + let _e266 = local_15; + onPlane = _e266; + } + let _e267 = onPlane; + let _e268 = toPlane; + offset_2 = (_e267 - _e268); let _e270 = (*halfWidth); - let _e272 = wLen2_; - u = clamp((dot(_e269, _e270) / _e272), -1f, 1f); - let _e275 = offset_2; - let _e276 = (*halfHeight); - let _e278 = hLen2_; - v_1 = clamp((dot(_e275, _e276) / _e278), -1f, 1f); - let _e281 = (*centre); - let _e282 = (*halfWidth); - let _e283 = u; - let _e286 = (*halfHeight); - let _e287 = v_1; - return ((_e281 + (_e282 * _e283)) + (_e286 * _e287)); + let _e271 = (*halfWidth); + wLen2_ = max(dot(_e270, _e271), 0.000000000001f); + let _e274 = (*halfHeight); + let _e275 = (*halfHeight); + hLen2_ = max(dot(_e274, _e275), 0.000000000001f); + let _e278 = offset_2; + let _e279 = (*halfWidth); + let _e281 = wLen2_; + u = clamp((dot(_e278, _e279) / _e281), -1f, 1f); + let _e284 = offset_2; + let _e285 = (*halfHeight); + let _e287 = hLen2_; + v_1 = clamp((dot(_e284, _e285) / _e287), -1f, 1f); + let _e290 = (*centre); + let _e291 = (*halfWidth); + let _e292 = u; + let _e295 = (*halfHeight); + let _e296 = v_1; + return ((_e290 + (_e291 * _e292)) + (_e295 * _e296)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr>, b_1: ptr>, n_4: ptr>) -> f32 { @@ -14259,34 +14861,34 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> var angle_2: f32; var axis_1: vec3; var len_2: f32; - var local_14: f32; + var local_16: f32; - let _e204 = (*a_3); - let _e205 = (*a_3); - ua = (_e204 / vec3(max(length(_e205), 0.000000000001f))); - let _e210 = (*b_1); - let _e211 = (*b_1); - ub = (_e210 / vec3(max(length(_e211), 0.000000000001f))); - let _e216 = ua; - let _e217 = ub; - angle_2 = acos(clamp(dot(_e216, _e217), -1f, 1f)); - let _e221 = ua; - let _e222 = ub; - axis_1 = cross(_e221, _e222); - let _e224 = axis_1; - len_2 = length(_e224); - let _e226 = len_2; - if (_e226 > 0.000001f) { - let _e228 = angle_2; - let _e229 = axis_1; - let _e230 = (*n_4); - let _e233 = len_2; - local_14 = ((_e228 * dot(_e229, _e230)) / _e233); + let _e213 = (*a_3); + let _e214 = (*a_3); + ua = (_e213 / vec3(max(length(_e214), 0.000000000001f))); + let _e219 = (*b_1); + let _e220 = (*b_1); + ub = (_e219 / vec3(max(length(_e220), 0.000000000001f))); + let _e225 = ua; + let _e226 = ub; + angle_2 = acos(clamp(dot(_e225, _e226), -1f, 1f)); + let _e230 = ua; + let _e231 = ub; + axis_1 = cross(_e230, _e231); + let _e233 = axis_1; + len_2 = length(_e233); + let _e235 = len_2; + if (_e235 > 0.000001f) { + let _e237 = angle_2; + let _e238 = axis_1; + let _e239 = (*n_4); + let _e242 = len_2; + local_16 = ((_e237 * dot(_e238, _e239)) / _e242); } else { - local_14 = 0f; + local_16 = 0f; } - let _e235 = local_14; - return _e235; + let _e244 = local_16; + return _e244; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr, 4>>, n_5: ptr>) -> f32 { @@ -14296,212 +14898,212 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr; var b_2: vec3; - var local_15: i32; + var local_17: i32; var ha: f32; var hb: f32; var d_4: f32; var q: vec3; - var local_16: f32; + var local_18: f32; var aAbove: bool; var bAbove: bool; - var local_17: f32; - var param_110: vec3; - var param_111: vec3; - var param_112: vec3; - var param_113: vec3; - var param_114: vec3; - var param_115: vec3; + var local_19: f32; + var param_116: vec3; + var param_117: vec3; + var param_118: vec3; + var param_119: vec3; + var param_120: vec3; + var param_121: vec3; total = 0f; exit = vec3(0f, 0f, 0f); entry = vec3(0f, 0f, 0f); i_8 = 0i; loop { - let _e218 = i_8; - if (_e218 < 4i) { - let _e220 = i_8; - let _e222 = (*corners_1)[_e220]; - a_4 = _e222; - let _e223 = i_8; - if (_e223 == 3i) { - local_15 = 0i; + let _e227 = i_8; + if (_e227 < 4i) { + let _e229 = i_8; + let _e231 = (*corners_1)[_e229]; + a_4 = _e231; + let _e232 = i_8; + if (_e232 == 3i) { + local_17 = 0i; } else { - let _e225 = i_8; - local_15 = (_e225 + 1i); - } - let _e227 = local_15; - let _e229 = (*corners_1)[_e227]; - b_2 = _e229; - let _e230 = a_4; - let _e231 = (*n_5); - ha = dot(_e230, _e231); - let _e233 = b_2; - let _e234 = (*n_5); - hb = dot(_e233, _e234); - let _e236 = ha; - let _e237 = hb; - d_4 = (_e236 - _e237); + let _e234 = i_8; + local_17 = (_e234 + 1i); + } + let _e236 = local_17; + let _e238 = (*corners_1)[_e236]; + b_2 = _e238; let _e239 = a_4; - let _e240 = b_2; - let _e241 = a_4; - let _e243 = d_4; - if (abs(_e243) > 0.000000000001f) { - let _e246 = ha; - let _e247 = d_4; - local_16 = (_e246 / _e247); + let _e240 = (*n_5); + ha = dot(_e239, _e240); + let _e242 = b_2; + let _e243 = (*n_5); + hb = dot(_e242, _e243); + let _e245 = ha; + let _e246 = hb; + d_4 = (_e245 - _e246); + let _e248 = a_4; + let _e249 = b_2; + let _e250 = a_4; + let _e252 = d_4; + if (abs(_e252) > 0.000000000001f) { + let _e255 = ha; + let _e256 = d_4; + local_18 = (_e255 / _e256); } else { - local_16 = 0f; - } - let _e249 = local_16; - q = (_e239 + ((_e240 - _e241) * _e249)); - let _e252 = ha; - aAbove = (_e252 > 0f); - let _e254 = hb; - bAbove = (_e254 > 0f); - let _e256 = aAbove; - let _e257 = bAbove; - if (_e256 || _e257) { - let _e259 = aAbove; - let _e260 = a_4; - let _e261 = q; - let _e264 = bAbove; - let _e265 = b_2; - let _e266 = q; - param_110 = select(_e261, _e260, vec3(_e259)); - param_111 = select(_e266, _e265, vec3(_e264)); - let _e269 = (*n_5); - param_112 = _e269; - let _e270 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_110), (¶m_111), (¶m_112)); - local_17 = _e270; + local_18 = 0f; + } + let _e258 = local_18; + q = (_e248 + ((_e249 - _e250) * _e258)); + let _e261 = ha; + aAbove = (_e261 > 0f); + let _e263 = hb; + bAbove = (_e263 > 0f); + let _e265 = aAbove; + let _e266 = bAbove; + if (_e265 || _e266) { + let _e268 = aAbove; + let _e269 = a_4; + let _e270 = q; + let _e273 = bAbove; + let _e274 = b_2; + let _e275 = q; + param_116 = select(_e270, _e269, vec3(_e268)); + param_117 = select(_e275, _e274, vec3(_e273)); + let _e278 = (*n_5); + param_118 = _e278; + let _e279 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_116), (¶m_117), (¶m_118)); + local_19 = _e279; } else { - local_17 = 0f; + local_19 = 0f; } - let _e271 = local_17; - let _e272 = total; - total = (_e272 + _e271); - let _e274 = aAbove; - let _e275 = bAbove; - let _e278 = q; - let _e279 = exit; - exit = select(_e279, _e278, vec3((_e274 && !(_e275)))); - let _e282 = aAbove; + let _e280 = local_19; + let _e281 = total; + total = (_e281 + _e280); + let _e283 = aAbove; let _e284 = bAbove; - let _e286 = q; - let _e287 = entry; - entry = select(_e287, _e286, vec3((!(_e282) && _e284))); + let _e287 = q; + let _e288 = exit; + exit = select(_e288, _e287, vec3((_e283 && !(_e284)))); + let _e291 = aAbove; + let _e293 = bAbove; + let _e295 = q; + let _e296 = entry; + entry = select(_e296, _e295, vec3((!(_e291) && _e293))); continue; } else { break; } continuing { - let _e290 = i_8; - i_8 = (_e290 + 1i); + let _e299 = i_8; + i_8 = (_e299 + 1i); } } - let _e292 = exit; - param_113 = _e292; - let _e293 = entry; - param_114 = _e293; - let _e294 = (*n_5); - param_115 = _e294; - let _e295 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_113), (¶m_114), (¶m_115)); - let _e296 = total; - total = (_e296 + _e295); - let _e298 = total; - return max((-(_e298) * 0.5f), 0f); + let _e301 = exit; + param_119 = _e301; + let _e302 = entry; + param_120 = _e302; + let _e303 = (*n_5); + param_121 = _e303; + let _e304 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_119), (¶m_120), (¶m_121)); + let _e305 = total; + total = (_e305 + _e304); + let _e307 = total; + return max((-(_e307) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_18: f32; - var local_19: f32; var local_20: f32; + var local_21: f32; + var local_22: f32; - let _e201 = (*slot_1); - let _e202 = (*slot_1); - lane = (_e201 - ((_e202 / 4i) * 4i)); - let _e206 = lane; - if (_e206 == 0i) { - let _e209 = (*four)[0u]; - local_18 = _e209; + let _e210 = (*slot_1); + let _e211 = (*slot_1); + lane = (_e210 - ((_e211 / 4i) * 4i)); + let _e215 = lane; + if (_e215 == 0i) { + let _e218 = (*four)[0u]; + local_20 = _e218; } else { - let _e210 = lane; - if (_e210 == 1i) { - let _e213 = (*four)[1u]; - local_19 = _e213; + let _e219 = lane; + if (_e219 == 1i) { + let _e222 = (*four)[1u]; + local_21 = _e222; } else { - let _e214 = lane; - if (_e214 == 2i) { - let _e217 = (*four)[2u]; - local_20 = _e217; + let _e223 = lane; + if (_e223 == 2i) { + let _e226 = (*four)[2u]; + local_22 = _e226; } else { - let _e219 = (*four)[3u]; - local_20 = _e219; + let _e228 = (*four)[3u]; + local_22 = _e228; } - let _e220 = local_20; - local_19 = _e220; + let _e229 = local_22; + local_21 = _e229; } - let _e221 = local_19; - local_18 = _e221; + let _e230 = local_21; + local_20 = _e230; } - let _e222 = local_18; - return _e222; + let _e231 = local_20; + return _e231; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { - var param_116: vec4; - var param_117: i32; + var param_122: vec4; + var param_123: i32; - let _e198 = (*slot_2); - let _e202 = light_list_info.scales[(_e198 / 4i)]; - param_116 = _e202; - let _e203 = (*slot_2); - param_117 = _e203; - let _e204 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_116), (¶m_117)); - return _e204; + let _e207 = (*slot_2); + let _e211 = light_list_info.scales[(_e207 / 4i)]; + param_122 = _e211; + let _e212 = (*slot_2); + param_123 = _e212; + let _e213 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_122), (¶m_123)); + return _e213; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_5: vec4; var b_3: vec4; - let _e200 = light_list_info.slot_rows[0i]; - let _e201 = (*row); - a_5 = abs((_e200 - vec4(_e201))); - let _e207 = light_list_info.slot_rows[1i]; - let _e208 = (*row); - b_3 = abs((_e207 - vec4(_e208))); - let _e213 = a_5[0u]; - let _e215 = a_5[1u]; - let _e218 = a_5[2u]; - let _e220 = a_5[3u]; - let _e224 = b_3[0u]; - let _e226 = b_3[1u]; - let _e229 = b_3[2u]; - let _e231 = b_3[3u]; - return (min(min(min(_e213, _e215), min(_e218, _e220)), min(min(_e224, _e226), min(_e229, _e231))) < 0.5f); + let _e209 = light_list_info.slot_rows[0i]; + let _e210 = (*row); + a_5 = abs((_e209 - vec4(_e210))); + let _e216 = light_list_info.slot_rows[1i]; + let _e217 = (*row); + b_3 = abs((_e216 - vec4(_e217))); + let _e222 = a_5[0u]; + let _e224 = a_5[1u]; + let _e227 = a_5[2u]; + let _e229 = a_5[3u]; + let _e233 = b_3[0u]; + let _e235 = b_3[1u]; + let _e238 = b_3[2u]; + let _e240 = b_3[3u]; + return (min(min(min(_e222, _e224), min(_e227, _e229)), min(min(_e233, _e235), min(_e238, _e240))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { - var param_118: vec4; - var param_119: i32; + var param_124: vec4; + var param_125: i32; - let _e198 = (*slot_3); - let _e202 = light_list_info.indices[(_e198 / 4i)]; - param_118 = _e202; - let _e203 = (*slot_3); - param_119 = _e203; - let _e204 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_118), (¶m_119)); - return _e204; + let _e207 = (*slot_3); + let _e211 = light_list_info.indices[(_e207 / 4i)]; + param_124 = _e211; + let _e212 = (*slot_3); + param_125 = _e212; + let _e213 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_124), (¶m_125)); + return _e213; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e197 = (*texel_2); - let _e201 = light_list_info.list[1u]; - let _e203 = (*row_1); - let _e207 = light_list_info.list[2u]; - let _e210 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e197 + 0.5f) * _e201), ((_e203 + 0.5f) * _e207)), 0f); - return _e210; + let _e206 = (*texel_2); + let _e210 = light_list_info.list[1u]; + let _e212 = (*row_1); + let _e216 = light_list_info.list[2u]; + let _e219 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e206 + 0.5f) * _e210), ((_e212 + 0.5f) * _e216)), 0f); + return _e219; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -14510,43 +15112,43 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var within: f32; var texel_3: f32; var four_1: vec4; - var param_120: f32; - var param_121: f32; - var param_122: vec4; - var param_123: i32; - - let _e205 = g_cluster_offset; - let _e206 = (*slot_4); - entry_1 = (_e205 + f32(_e206)); - let _e209 = entry_1; - row_2 = floor((_e209 / 16f)); - let _e212 = entry_1; - let _e213 = row_2; - within = (_e212 - (_e213 * 16f)); - let _e216 = within; - texel_3 = floor((_e216 / 4f)); - let _e221 = light_list_info.cluster_depth[3u]; + var param_126: f32; + var param_127: f32; + var param_128: vec4; + var param_129: i32; + + let _e214 = g_cluster_offset; + let _e215 = (*slot_4); + entry_1 = (_e214 + f32(_e215)); + let _e218 = entry_1; + row_2 = floor((_e218 / 16f)); + let _e221 = entry_1; let _e222 = row_2; - let _e224 = texel_3; - param_120 = _e224; - param_121 = (_e221 + _e222); - let _e225 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_120), (¶m_121)); - four_1 = _e225; - let _e226 = within; - let _e227 = texel_3; - let _e232 = four_1; - param_122 = _e232; - param_123 = i32(((_e226 - (_e227 * 4f)) + 0.5f)); - let _e233 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_122), (¶m_123)); - return _e233; + within = (_e221 - (_e222 * 16f)); + let _e225 = within; + texel_3 = floor((_e225 / 4f)); + let _e230 = light_list_info.cluster_depth[3u]; + let _e231 = row_2; + let _e233 = texel_3; + param_126 = _e233; + param_127 = (_e230 + _e231); + let _e234 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_126), (¶m_127)); + four_1 = _e234; + let _e235 = within; + let _e236 = texel_3; + let _e241 = four_1; + param_128 = _e241; + param_129 = i32(((_e235 - (_e236 * 4f)) + 0.5f)); + let _e242 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_128), (¶m_129)); + return _e242; } fn Clustered_u0028_() -> bool { - let _e197 = light_list_info.cluster_grid[3u]; - return (_e197 > 0.5f); + let _e206 = light_list_info.cluster_grid[3u]; + return (_e206 > 0.5f); } -fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_5: ptr) -> LightSample { +fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_6: ptr) -> LightSample { var light_3: LightSample; var position: vec4; var color_1: vec4; @@ -14555,14 +15157,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_21: f32; - var param_124: i32; - var param_125: i32; + var local_23: f32; + var param_130: i32; + var param_131: i32; var v_2: f32; var u_1: f32; - var local_22: f32; - var param_126: f32; - var param_127: i32; + var local_24: f32; + var param_132: f32; + var param_133: i32; var type_42: f32; var halfWidth_1: vec3; var halfHeight_1: vec3; @@ -14570,308 +15172,308 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_2: array, 4>; var behind: bool; var formFactor: f32; - var local_23: f32; - var param_128: array, 4>; - var param_129: vec3; + var local_25: f32; + var param_134: array, 4>; + var param_135: vec3; var area: f32; var radiance: f32; - var local_24: f32; + var local_26: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; var mirror_1: vec3; var representative: vec3; - var param_130: vec3; - var param_131: vec3; - var param_132: vec3; - var param_133: vec3; - var param_134: vec3; + var param_136: vec3; + var param_137: vec3; + var param_138: vec3; + var param_139: vec3; + var param_140: vec3; var toPoint: vec3; var pointDistance: f32; - var local_25: vec3; - var param_135: vec3; - var param_136: vec3; - var param_137: f32; - var param_138: array, 4>; + var local_27: vec3; + var param_141: vec3; + var param_142: vec3; + var param_143: f32; + var param_144: array, 4>; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; var distance_3: f32; - var param_139: f32; - var param_140: f32; + var param_145: f32; + var param_146: f32; var cosAngle: f32; light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e252 = (*index_2); - if (_e252 < 8i) { - let _e254 = (*index_2); - let _e257 = frag_info.light_position[_e254]; - position = _e257; - let _e258 = (*index_2); - let _e261 = frag_info.light_color[_e258]; - color_1 = _e261; - let _e262 = (*index_2); - let _e265 = frag_info.light_direction[_e262]; - direction = _e265; - let _e266 = (*index_2); - let _e269 = frag_info.light_cone[_e266]; - cone = _e269; + let _e261 = (*index_2); + if (_e261 < 8i) { + let _e263 = (*index_2); + let _e266 = frag_info.light_position[_e263]; + position = _e266; + let _e267 = (*index_2); + let _e270 = frag_info.light_color[_e267]; + color_1 = _e270; + let _e271 = (*index_2); + let _e274 = frag_info.light_direction[_e271]; + direction = _e274; + let _e275 = (*index_2); + let _e278 = frag_info.light_cone[_e275]; + cone = _e278; } else { - let _e270 = (*index_2); - slot_5 = (_e270 - 8i); - let _e272 = Clustered_u0028_(); - clustered = _e272; - let _e273 = clustered; - if _e273 { - let _e274 = slot_5; - param_124 = _e274; - let _e275 = ClusterRow_u0028_i1_u003b((¶m_124)); - local_21 = _e275; + let _e279 = (*index_2); + slot_5 = (_e279 - 8i); + let _e281 = Clustered_u0028_(); + clustered = _e281; + let _e282 = clustered; + if _e282 { + let _e283 = slot_5; + param_130 = _e283; + let _e284 = ClusterRow_u0028_i1_u003b((¶m_130)); + local_23 = _e284; } else { - let _e276 = slot_5; - param_125 = _e276; - let _e277 = LightListRow_u0028_i1_u003b((¶m_125)); - local_21 = _e277; - } - let _e278 = local_21; - listRow = _e278; - let _e279 = listRow; - let _e283 = light_list_info.list[2u]; - v_2 = ((_e279 + 0.5f) * _e283); - let _e287 = light_list_info.list[1u]; - u_1 = _e287; - let _e288 = u_1; - let _e290 = v_2; - let _e292 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e288), _e290), 0f); - position = _e292; - let _e293 = u_1; - let _e295 = v_2; - let _e297 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e293), _e295), 0f); - color_1 = _e297; - let _e298 = u_1; - let _e300 = v_2; - let _e302 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e298), _e300), 0f); - direction = _e302; - let _e303 = u_1; - let _e305 = v_2; - let _e307 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e303), _e305), 0f); - cone = _e307; - let _e308 = clustered; - if _e308 { - let _e309 = listRow; - param_126 = _e309; - let _e310 = InSlots_u0028_f1_u003b((¶m_126)); - local_22 = select(1f, 0f, _e310); + let _e285 = slot_5; + param_131 = _e285; + let _e286 = LightListRow_u0028_i1_u003b((¶m_131)); + local_23 = _e286; + } + let _e287 = local_23; + listRow = _e287; + let _e288 = listRow; + let _e292 = light_list_info.list[2u]; + v_2 = ((_e288 + 0.5f) * _e292); + let _e296 = light_list_info.list[1u]; + u_1 = _e296; + let _e297 = u_1; + let _e299 = v_2; + let _e301 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e297), _e299), 0f); + position = _e301; + let _e302 = u_1; + let _e304 = v_2; + let _e306 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e302), _e304), 0f); + color_1 = _e306; + let _e307 = u_1; + let _e309 = v_2; + let _e311 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e307), _e309), 0f); + direction = _e311; + let _e312 = u_1; + let _e314 = v_2; + let _e316 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e312), _e314), 0f); + cone = _e316; + let _e317 = clustered; + if _e317 { + let _e318 = listRow; + param_132 = _e318; + let _e319 = InSlots_u0028_f1_u003b((¶m_132)); + local_24 = select(1f, 0f, _e319); } else { - let _e312 = slot_5; - param_127 = _e312; - let _e313 = LightListScale_u0028_i1_u003b((¶m_127)); - local_22 = _e313; - } - let _e314 = local_22; - let _e316 = color_1[3u]; - color_1[3u] = (_e316 * _e314); - } - let _e320 = position[3u]; - type_42 = _e320; - let _e321 = type_42; - if (_e321 > 2.5f) { - let _e323 = direction; - halfWidth_1 = _e323.xyz; - let _e325 = cone; - halfHeight_1 = _e325.xyz; - let _e327 = position; - let _e329 = v_world_position_1; - toCentre = (_e327.xyz - _e329); - let _e331 = toCentre; - let _e332 = halfWidth_1; - let _e334 = halfHeight_1; - corners_2[0i] = ((_e331 - _e332) - _e334); - let _e337 = toCentre; - let _e338 = halfWidth_1; - let _e340 = halfHeight_1; - corners_2[1i] = ((_e337 + _e338) - _e340); - let _e343 = toCentre; - let _e344 = halfWidth_1; - let _e346 = halfHeight_1; - corners_2[2i] = ((_e343 + _e344) + _e346); - let _e349 = toCentre; - let _e350 = halfWidth_1; - let _e352 = halfHeight_1; - corners_2[3i] = ((_e349 - _e350) + _e352); - let _e355 = toCentre; - let _e356 = halfWidth_1; - let _e357 = halfHeight_1; - behind = (dot(_e355, cross(_e356, _e357)) >= 0f); - let _e361 = behind; - if _e361 { - local_23 = 0f; + let _e321 = slot_5; + param_133 = _e321; + let _e322 = LightListScale_u0028_i1_u003b((¶m_133)); + local_24 = _e322; + } + let _e323 = local_24; + let _e325 = color_1[3u]; + color_1[3u] = (_e325 * _e323); + } + let _e329 = position[3u]; + type_42 = _e329; + let _e330 = type_42; + if (_e330 > 2.5f) { + let _e332 = direction; + halfWidth_1 = _e332.xyz; + let _e334 = cone; + halfHeight_1 = _e334.xyz; + let _e336 = position; + let _e338 = v_world_position_1; + toCentre = (_e336.xyz - _e338); + let _e340 = toCentre; + let _e341 = halfWidth_1; + let _e343 = halfHeight_1; + corners_2[0i] = ((_e340 - _e341) - _e343); + let _e346 = toCentre; + let _e347 = halfWidth_1; + let _e349 = halfHeight_1; + corners_2[1i] = ((_e346 + _e347) - _e349); + let _e352 = toCentre; + let _e353 = halfWidth_1; + let _e355 = halfHeight_1; + corners_2[2i] = ((_e352 + _e353) + _e355); + let _e358 = toCentre; + let _e359 = halfWidth_1; + let _e361 = halfHeight_1; + corners_2[3i] = ((_e358 - _e359) + _e361); + let _e364 = toCentre; + let _e365 = halfWidth_1; + let _e366 = halfHeight_1; + behind = (dot(_e364, cross(_e365, _e366)) >= 0f); + let _e370 = behind; + if _e370 { + local_25 = 0f; } else { - let _e362 = corners_2; - param_128 = _e362; - let _e364 = (*s_5).n; - param_129 = _e364; - let _e365 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_128), (¶m_129)); - local_23 = _e365; - } - let _e366 = local_23; - formFactor = _e366; - let _e367 = halfWidth_1; - let _e368 = halfHeight_1; - area = (length(cross(_e367, _e368)) * 4f); - let _e372 = area; - if (_e372 > 0.000000001f) { - let _e374 = area; - local_24 = (1f / _e374); + let _e371 = corners_2; + param_134 = _e371; + let _e373 = (*s_6).n; + param_135 = _e373; + let _e374 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_134), (¶m_135)); + local_25 = _e374; + } + let _e375 = local_25; + formFactor = _e375; + let _e376 = halfWidth_1; + let _e377 = halfHeight_1; + area = (length(cross(_e376, _e377)) * 4f); + let _e381 = area; + if (_e381 > 0.000000001f) { + let _e383 = area; + local_26 = (1f / _e383); } else { - local_24 = 0f; - } - let _e376 = local_24; - radiance = _e376; - let _e377 = toCentre; - distance_2 = length(_e377); - let _e380 = direction[3u]; - if (_e380 > 0f) { - let _e382 = distance_2; - let _e384 = direction[3u]; - ratio_1 = (_e382 / _e384); - let _e386 = ratio_1; - let _e387 = ratio_1; - let _e389 = ratio_1; - let _e391 = ratio_1; - window_1 = clamp((1f - (((_e386 * _e387) * _e389) * _e391)), 0f, 1f); - let _e395 = window_1; - let _e396 = window_1; - let _e398 = radiance; - radiance = (_e398 * (_e395 * _e396)); - } - let _e401 = (*s_5).v; - let _e404 = (*s_5).n; - mirror_1 = reflect(-(_e401), _e404); - let _e406 = position; - param_130 = _e406.xyz; - let _e408 = halfWidth_1; - param_131 = _e408; - let _e409 = halfHeight_1; - param_132 = _e409; - let _e410 = v_world_position_1; - param_133 = _e410; - let _e411 = mirror_1; - param_134 = _e411; - let _e412 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_130), (¶m_131), (¶m_132), (¶m_133), (¶m_134)); - representative = _e412; - let _e413 = representative; - let _e414 = v_world_position_1; - toPoint = (_e413 - _e414); - let _e416 = toPoint; - pointDistance = length(_e416); - let _e418 = pointDistance; - if (_e418 > 0.000001f) { - let _e420 = toPoint; - let _e421 = pointDistance; - local_25 = (_e420 / vec3(_e421)); + local_26 = 0f; + } + let _e385 = local_26; + radiance = _e385; + let _e386 = toCentre; + distance_2 = length(_e386); + let _e389 = direction[3u]; + if (_e389 > 0f) { + let _e391 = distance_2; + let _e393 = direction[3u]; + ratio_1 = (_e391 / _e393); + let _e395 = ratio_1; + let _e396 = ratio_1; + let _e398 = ratio_1; + let _e400 = ratio_1; + window_1 = clamp((1f - (((_e395 * _e396) * _e398) * _e400)), 0f, 1f); + let _e404 = window_1; + let _e405 = window_1; + let _e407 = radiance; + radiance = (_e407 * (_e404 * _e405)); + } + let _e410 = (*s_6).v; + let _e413 = (*s_6).n; + mirror_1 = reflect(-(_e410), _e413); + let _e415 = position; + param_136 = _e415.xyz; + let _e417 = halfWidth_1; + param_137 = _e417; + let _e418 = halfHeight_1; + param_138 = _e418; + let _e419 = v_world_position_1; + param_139 = _e419; + let _e420 = mirror_1; + param_140 = _e420; + let _e421 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_136), (¶m_137), (¶m_138), (¶m_139), (¶m_140)); + representative = _e421; + let _e422 = representative; + let _e423 = v_world_position_1; + toPoint = (_e422 - _e423); + let _e425 = toPoint; + pointDistance = length(_e425); + let _e427 = pointDistance; + if (_e427 > 0.000001f) { + let _e429 = toPoint; + let _e430 = pointDistance; + local_27 = (_e429 / vec3(_e430)); } else { - let _e425 = (*s_5).n; - local_25 = _e425; - } - let _e426 = local_25; - light_3.l = _e426; - let _e429 = light_3.l; - let _e431 = (*s_5).v; - light_3.h = normalize((_e429 + _e431)); - let _e435 = formFactor; - light_3.n_dot_l = _e435; - let _e438 = (*s_5).n; - let _e440 = light_3.h; - light_3.n_dot_h = max(dot(_e438, _e440), 0f); - let _e445 = (*s_5).v; - let _e447 = light_3.h; - light_3.v_dot_h = max(dot(_e445, _e447), 0f); - let _e451 = color_1; - let _e454 = color_1[3u]; - let _e456 = radiance; - light_3.radiance = ((_e451.xyz * _e454) * _e456); + let _e434 = (*s_6).n; + local_27 = _e434; + } + let _e435 = local_27; + light_3.l = _e435; + let _e438 = light_3.l; + let _e440 = (*s_6).v; + light_3.h = normalize((_e438 + _e440)); + let _e444 = formFactor; + light_3.n_dot_l = _e444; + let _e447 = (*s_6).n; + let _e449 = light_3.h; + light_3.n_dot_h = max(dot(_e447, _e449), 0f); + let _e454 = (*s_6).v; + let _e456 = light_3.h; + light_3.v_dot_h = max(dot(_e454, _e456), 0f); + let _e460 = color_1; + let _e463 = color_1[3u]; + let _e465 = radiance; + light_3.radiance = ((_e460.xyz * _e463) * _e465); light_3.integrated = 1f; - let _e461 = (*s_5).n; - param_135 = _e461; - let _e463 = (*s_5).v; - param_136 = _e463; - let _e465 = (*s_5).roughness; - param_137 = _e465; - let _e466 = corners_2; - param_138 = _e466; - let _e467 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_135), (¶m_136), (¶m_137), (¶m_138)); - light_3.ltc = _e467; - let _e469 = light_3; - return _e469; - } - let _e470 = direction; - aim = normalize(_e470.xyz); + let _e470 = (*s_6).n; + param_141 = _e470; + let _e472 = (*s_6).v; + param_142 = _e472; + let _e474 = (*s_6).roughness; + param_143 = _e474; + let _e475 = corners_2; + param_144 = _e475; + let _e476 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_141), (¶m_142), (¶m_143), (¶m_144)); + light_3.ltc = _e476; + let _e478 = light_3; + return _e478; + } + let _e479 = direction; + aim = normalize(_e479.xyz); attenuation_1 = 1f; - let _e473 = type_42; - if (_e473 < 0.5f) { - let _e475 = aim; - light_3.l = -(_e475); + let _e482 = type_42; + if (_e482 < 0.5f) { + let _e484 = aim; + light_3.l = -(_e484); } else { - let _e478 = position; - let _e480 = v_world_position_1; - toLight_1 = (_e478.xyz - _e480); - let _e482 = toLight_1; - distance_3 = length(_e482); - let _e484 = distance_3; - if (_e484 < 0.000001f) { - let _e487 = (*s_5).n; - light_3.l = _e487; - let _e490 = (*s_5).n; - light_3.h = _e490; + let _e487 = position; + let _e489 = v_world_position_1; + toLight_1 = (_e487.xyz - _e489); + let _e491 = toLight_1; + distance_3 = length(_e491); + let _e493 = distance_3; + if (_e493 < 0.000001f) { + let _e496 = (*s_6).n; + light_3.l = _e496; + let _e499 = (*s_6).n; + light_3.h = _e499; light_3.radiance = vec3(0f, 0f, 0f); light_3.n_dot_l = 0f; light_3.n_dot_h = 0f; light_3.v_dot_h = 0f; - let _e496 = light_3; - return _e496; - } - let _e497 = toLight_1; - let _e498 = distance_3; - light_3.l = (_e497 / vec3(_e498)); - let _e502 = distance_3; - param_139 = _e502; - let _e504 = direction[3u]; - param_140 = _e504; - let _e505 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_139), (¶m_140)); - attenuation_1 = _e505; - let _e506 = type_42; - if (_e506 > 1.5f) { - let _e508 = aim; - let _e510 = light_3.l; - cosAngle = dot(_e508, -(_e510)); - let _e513 = cosAngle; - let _e515 = cone[1u]; - let _e518 = cone[0u]; - let _e520 = cone[1u]; - let _e524 = attenuation_1; - attenuation_1 = (_e524 * clamp(((_e513 - _e515) / (_e518 - _e520)), 0f, 1f)); - } - } - let _e527 = light_3.l; - let _e529 = (*s_5).v; - light_3.h = normalize((_e527 + _e529)); - let _e534 = (*s_5).n; + let _e505 = light_3; + return _e505; + } + let _e506 = toLight_1; + let _e507 = distance_3; + light_3.l = (_e506 / vec3(_e507)); + let _e511 = distance_3; + param_145 = _e511; + let _e513 = direction[3u]; + param_146 = _e513; + let _e514 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_145), (¶m_146)); + attenuation_1 = _e514; + let _e515 = type_42; + if (_e515 > 1.5f) { + let _e517 = aim; + let _e519 = light_3.l; + cosAngle = dot(_e517, -(_e519)); + let _e522 = cosAngle; + let _e524 = cone[1u]; + let _e527 = cone[0u]; + let _e529 = cone[1u]; + let _e533 = attenuation_1; + attenuation_1 = (_e533 * clamp(((_e522 - _e524) / (_e527 - _e529)), 0f, 1f)); + } + } let _e536 = light_3.l; - light_3.n_dot_l = max(dot(_e534, _e536), 0f); - let _e541 = (*s_5).n; - let _e543 = light_3.h; - light_3.n_dot_h = max(dot(_e541, _e543), 0f); - let _e548 = (*s_5).v; - let _e550 = light_3.h; - light_3.v_dot_h = max(dot(_e548, _e550), 0f); - let _e554 = color_1; - let _e557 = color_1[3u]; - let _e559 = attenuation_1; - light_3.radiance = ((_e554.xyz * _e557) * _e559); - let _e562 = light_3; - return _e562; + let _e538 = (*s_6).v; + light_3.h = normalize((_e536 + _e538)); + let _e543 = (*s_6).n; + let _e545 = light_3.l; + light_3.n_dot_l = max(dot(_e543, _e545), 0f); + let _e550 = (*s_6).n; + let _e552 = light_3.h; + light_3.n_dot_h = max(dot(_e550, _e552), 0f); + let _e557 = (*s_6).v; + let _e559 = light_3.h; + light_3.v_dot_h = max(dot(_e557, _e559), 0f); + let _e563 = color_1; + let _e566 = color_1[3u]; + let _e568 = attenuation_1; + light_3.radiance = ((_e563.xyz * _e566) * _e568); + let _e571 = light_3; + return _e571; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -14882,339 +15484,332 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_26: f32; + var local_28: f32; var cell: f32; var row_3: f32; var header: vec4; - var param_141: f32; - var param_142: f32; - - let _e210 = light_list_info.cluster_view_projection; - let _e211 = (*world_3); - clip_1 = (_e210 * vec4(_e211.x, _e211.y, _e211.z, 1f)); - let _e217 = clip_1; - let _e220 = clip_1[3u]; - ndc_1 = (_e217.xy / vec2(max(_e220, 0.000001f))); - let _e225 = light_list_info.cluster_grid; - grid = _e225.xyz; - let _e229 = light_list_info.cluster_depth[0u]; - near_1 = _e229; - let _e231 = ndc_1[0u]; - let _e235 = grid[0u]; - let _e239 = grid[0u]; - tx = clamp(floor((((_e231 * 0.5f) + 0.5f) * _e235)), 0f, (_e239 - 1f)); - let _e243 = ndc_1[1u]; - let _e247 = grid[1u]; - let _e251 = grid[1u]; - ty = clamp(floor((((_e243 * 0.5f) + 0.5f) * _e247)), 0f, (_e251 - 1f)); - let _e255 = clip_1[3u]; - let _e256 = near_1; - if (_e255 <= _e256) { - local_26 = 0f; + var param_147: f32; + var param_148: f32; + + let _e219 = light_list_info.cluster_view_projection; + let _e220 = (*world_3); + clip_1 = (_e219 * vec4(_e220.x, _e220.y, _e220.z, 1f)); + let _e226 = clip_1; + let _e229 = clip_1[3u]; + ndc_1 = (_e226.xy / vec2(max(_e229, 0.000001f))); + let _e234 = light_list_info.cluster_grid; + grid = _e234.xyz; + let _e238 = light_list_info.cluster_depth[0u]; + near_1 = _e238; + let _e240 = ndc_1[0u]; + let _e244 = grid[0u]; + let _e248 = grid[0u]; + tx = clamp(floor((((_e240 * 0.5f) + 0.5f) * _e244)), 0f, (_e248 - 1f)); + let _e252 = ndc_1[1u]; + let _e256 = grid[1u]; + let _e260 = grid[1u]; + ty = clamp(floor((((_e252 * 0.5f) + 0.5f) * _e256)), 0f, (_e260 - 1f)); + let _e264 = clip_1[3u]; + let _e265 = near_1; + if (_e264 <= _e265) { + local_28 = 0f; } else { - let _e259 = clip_1[3u]; - let _e260 = near_1; - let _e265 = light_list_info.cluster_depth[1u]; - let _e269 = grid[2u]; - local_26 = clamp(floor((log((_e259 / _e260)) * _e265)), 0f, (_e269 - 1f)); - } - let _e272 = local_26; - tz = _e272; - let _e273 = tx; - let _e274 = ty; - let _e276 = grid[0u]; - let _e279 = tz; - let _e281 = grid[0u]; - let _e284 = grid[1u]; - cell = ((_e273 + (_e274 * _e276)) + ((_e279 * _e281) * _e284)); - let _e287 = cell; - row_3 = floor((_e287 / 4f)); - let _e290 = cell; - let _e291 = row_3; - let _e296 = light_list_info.cluster_depth[2u]; - let _e297 = row_3; - param_141 = (_e290 - (_e291 * 4f)); - param_142 = (_e296 + _e297); - let _e299 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_141), (¶m_142)); - header = _e299; - let _e301 = header[0u]; - g_cluster_offset = _e301; - let _e303 = header[1u]; - g_cluster_count = _e303; + let _e268 = clip_1[3u]; + let _e269 = near_1; + let _e274 = light_list_info.cluster_depth[1u]; + let _e278 = grid[2u]; + local_28 = clamp(floor((log((_e268 / _e269)) * _e274)), 0f, (_e278 - 1f)); + } + let _e281 = local_28; + tz = _e281; + let _e282 = tx; + let _e283 = ty; + let _e285 = grid[0u]; + let _e288 = tz; + let _e290 = grid[0u]; + let _e293 = grid[1u]; + cell = ((_e282 + (_e283 * _e285)) + ((_e288 * _e290) * _e293)); + let _e296 = cell; + row_3 = floor((_e296 / 4f)); + let _e299 = cell; + let _e300 = row_3; + let _e305 = light_list_info.cluster_depth[2u]; + let _e306 = row_3; + param_147 = (_e299 - (_e300 * 4f)); + param_148 = (_e305 + _e306); + let _e308 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_147), (¶m_148)); + header = _e308; + let _e310 = header[0u]; + g_cluster_offset = _e310; + let _e312 = header[1u]; + g_cluster_count = _e312; return; } fn LightCount_u0028_() -> i32 { var tail: f32; - var param_143: vec3; + var param_149: vec3; - let _e199 = light_list_info.list[0u]; - tail = _e199; - let _e200 = Clustered_u0028_(); - if _e200 { - let _e201 = v_world_position_1; - param_143 = _e201; - FindCluster_u0028_vf3_u003b((¶m_143)); - let _e202 = g_cluster_count; - tail = _e202; + let _e208 = light_list_info.list[0u]; + tail = _e208; + let _e209 = Clustered_u0028_(); + if _e209 { + let _e210 = v_world_position_1; + param_149 = _e210; + FindCluster_u0028_vf3_u003b((¶m_149)); + let _e211 = g_cluster_count; + tail = _e211; } - let _e205 = frag_info.frame_params[1u]; - let _e209 = tail; - return (clamp(i32((_e205 + 0.5f)), 0i, 8i) + clamp(i32((_e209 + 0.5f)), 0i, 24i)); + let _e214 = frag_info.frame_params[1u]; + let _e218 = tail; + return (clamp(i32((_e214 + 0.5f)), 0i, 8i) + clamp(i32((_e218 + 0.5f)), 0i, 24i)); } -fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) -> vec3 { +fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) -> vec3 { var total_1: vec3; var count_1: i32; var i_9: i32; var light_4: LightSample; - var param_144: i32; - var param_145: Surface; + var param_150: i32; + var param_151: Surface; var visibility: f32; - var param_146: i32; - var local_27: f32; - var param_147: Surface; - var param_148: LightSample; - var param_149: i32; - var param_150: vec3; - var param_151: vec3; var param_152: i32; + var local_29: f32; var param_153: Surface; var param_154: LightSample; + var param_155: i32; + var param_156: vec3; + var param_157: vec3; + var param_158: i32; + var param_159: Surface; + var param_160: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e213 = LightCount_u0028_(); - count_1 = _e213; + let _e222 = LightCount_u0028_(); + count_1 = _e222; i_9 = 0i; loop { - let _e214 = i_9; - if (_e214 < 32i) { - let _e216 = i_9; - let _e217 = count_1; - if (_e216 >= _e217) { + let _e223 = i_9; + if (_e223 < 32i) { + let _e225 = i_9; + let _e226 = count_1; + if (_e225 >= _e226) { break; } - let _e219 = i_9; - param_144 = _e219; - let _e220 = (*s_6); - param_145 = _e220; - let _e221 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_144), (¶m_145)); - light_4 = _e221; - let _e223 = light_4.n_dot_l; - if (_e223 <= 0f) { + let _e228 = i_9; + param_150 = _e228; + let _e229 = (*s_7); + param_151 = _e229; + let _e230 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_150), (¶m_151)); + light_4 = _e230; + let _e232 = light_4.n_dot_l; + if (_e232 <= 0f) { continue; } - let _e225 = i_9; - param_146 = _e225; - let _e226 = LightHasShadow_u0028_i1_u003b((¶m_146)); - if _e226 { - let _e227 = (*s_6); - param_147 = _e227; - let _e228 = light_4; - param_148 = _e228; - let _e229 = i_9; - param_149 = _e229; - let _e230 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_147), (¶m_148), (¶m_149)); - let _e231 = v_world_position_1; - param_150 = _e231; - let _e233 = (*s_6).n; - param_151 = _e233; - let _e234 = i_9; - param_152 = _e234; - let _e235 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_150), (¶m_151), (¶m_152)); - local_27 = (_e230 * _e235); + let _e234 = i_9; + param_152 = _e234; + let _e235 = LightHasShadow_u0028_i1_u003b((¶m_152)); + if _e235 { + let _e236 = (*s_7); + param_153 = _e236; + let _e237 = light_4; + param_154 = _e237; + let _e238 = i_9; + param_155 = _e238; + let _e239 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_153), (¶m_154), (¶m_155)); + let _e240 = v_world_position_1; + param_156 = _e240; + let _e242 = (*s_7).n; + param_157 = _e242; + let _e243 = i_9; + param_158 = _e243; + let _e244 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_156), (¶m_157), (¶m_158)); + local_29 = (_e239 * _e244); } else { - local_27 = 1f; + local_29 = 1f; } - let _e237 = local_27; - visibility = _e237; - let _e238 = visibility; - if (_e238 <= 0f) { + let _e246 = local_29; + visibility = _e246; + let _e247 = visibility; + if (_e247 <= 0f) { continue; } - let _e240 = (*s_6); - param_153 = _e240; - let _e241 = light_4; - param_154 = _e241; - let _e242 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_153), (¶m_154)); - let _e244 = light_4.radiance; - let _e247 = light_4.n_dot_l; - let _e249 = visibility; - let _e251 = total_1; - total_1 = (_e251 + (((_e242 * _e244) * _e247) * _e249)); + let _e249 = (*s_7); + param_159 = _e249; + let _e250 = light_4; + param_160 = _e250; + let _e251 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_159), (¶m_160)); + let _e253 = light_4.radiance; + let _e256 = light_4.n_dot_l; + let _e258 = visibility; + let _e260 = total_1; + total_1 = (_e260 + (((_e251 * _e253) * _e256) * _e258)); continue; } else { break; } continuing { - let _e253 = i_9; - i_9 = (_e253 + 1i); + let _e262 = i_9; + i_9 = (_e262 + 1i); } } - let _e255 = total_1; - return _e255; + let _e264 = total_1; + return _e264; } fn SampleLightmap_u0028_() -> vec3 { var texel_4: vec4; - let _e196 = v_lightmap_uv_1; - let _e197 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e196); - texel_4 = _e197; - let _e198 = texel_4; - let _e201 = texel_4[3u]; - return ((_e198.xyz * _e201) * 8f); + let _e205 = v_lightmap_uv_1; + let _e206 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e205); + texel_4 = _e206; + let _e207 = texel_4; + let _e210 = texel_4[3u]; + return ((_e207.xyz * _e210) * 8f); } fn EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b(rho_2: ptr>, r_4: ptr, mu_1: ptr) -> vec3 { var e_1: f32; - var param_155: f32; - var param_156: f32; - var param_157: f32; - var param_158: vec3; - var param_159: f32; - - let _e204 = (*mu_1); - param_155 = _e204; - let _e205 = (*r_4); - param_156 = _e205; - let _e206 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_155), (¶m_156)); - e_1 = _e206; - let _e207 = (*rho_2); - let _e208 = e_1; - let _e210 = (*r_4); - param_157 = _e210; - let _e211 = FonAverage_u0028_f1_u003b((¶m_157)); - let _e212 = (*rho_2); - param_158 = _e212; - param_159 = _e211; - let _e213 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_158), (¶m_159)); - let _e214 = e_1; - return ((_e207 * _e208) + (_e213 * (1f - _e214))); + var param_161: f32; + var param_162: f32; + var param_163: f32; + var param_164: vec3; + var param_165: f32; + + let _e213 = (*mu_1); + param_161 = _e213; + let _e214 = (*r_4); + param_162 = _e214; + let _e215 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_161), (¶m_162)); + e_1 = _e215; + let _e216 = (*rho_2); + let _e217 = e_1; + let _e219 = (*r_4); + param_163 = _e219; + let _e220 = FonAverage_u0028_f1_u003b((¶m_163)); + let _e221 = (*rho_2); + param_164 = _e221; + param_165 = _e220; + let _e222 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_164), (¶m_165)); + let _e223 = e_1; + return ((_e216 * _e217) + (_e222 * (1f - _e223))); } fn MaterialLodBias_u0028_() -> f32 { - let _e197 = frag_info.target_origin[1u]; - return _e197; + let _e206 = frag_info.target_origin[1u]; + return _e206; } -fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) { +fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { var orm: vec3; - let _e197 = v_texcoord_1; - let _e198 = MaterialLodBias_u0028_(); - let _e199 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e197, _e198); - orm = _e199.xyz; - let _e202 = (*s_7).metallic; - let _e204 = orm[2u]; - (*s_7).metallic = clamp((_e202 * _e204), 0f, 1f); - let _e209 = (*s_7).roughness; - let _e211 = orm[1u]; - (*s_7).roughness = clamp((_e209 * _e211), 0.02f, 1f); + let _e206 = v_texcoord_1; + let _e207 = MaterialLodBias_u0028_(); + let _e208 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e206, _e207); + orm = _e208.xyz; + let _e211 = (*s_8).metallic; + let _e213 = orm[2u]; + (*s_8).metallic = clamp((_e211 * _e213), 0f, 1f); + let _e218 = (*s_8).roughness; + let _e220 = orm[1u]; + (*s_8).roughness = clamp((_e218 * _e220), 0.02f, 1f); return; } -fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e196 = (*srgb); - let _e199 = (*srgb); - let _e204 = (*srgb); - return mix((_e196 / vec3(12.92f)), pow(((_e199 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e204)); -} - -fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { +fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) { var emissive: vec3; - var param_160: vec3; - - let _e198 = v_texcoord_1; - let _e199 = MaterialLodBias_u0028_(); - let _e200 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e198, _e199); - param_160 = _e200.xyz; - let _e202 = SrgbToLinear_u0028_vf3_u003b((¶m_160)); - emissive = _e202; - let _e203 = emissive; - let _e205 = frag_info.emissive; - let _e210 = frag_info.material2_[3u]; - (*s_8).emissive = ((_e203 * _e205.xyz) * _e210); + var param_166: vec3; + + let _e207 = v_texcoord_1; + let _e208 = MaterialLodBias_u0028_(); + let _e209 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e207, _e208); + param_166 = _e209.xyz; + let _e211 = SrgbToLinear_u0028_vf3_u003b((¶m_166)); + emissive = _e211; + let _e212 = emissive; + let _e214 = frag_info.emissive; + let _e219 = frag_info.material2_[3u]; + (*s_9).emissive = ((_e212 * _e214.xyz) * _e219); return; } -fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) { +fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_10: ptr) { var occlusion: f32; - let _e197 = v_texcoord_1; - let _e198 = MaterialLodBias_u0028_(); - let _e199 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e197, _e198); - occlusion = _e199.x; - let _e201 = occlusion; - let _e204 = frag_info.material2_[2u]; - (*s_9).occlusion = mix(1f, _e201, clamp(_e204, 0f, 1f)); + let _e206 = v_texcoord_1; + let _e207 = MaterialLodBias_u0028_(); + let _e208 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e206, _e207); + occlusion = _e208.x; + let _e210 = occlusion; + let _e213 = frag_info.material2_[2u]; + (*s_10).occlusion = mix(1f, _e210, clamp(_e213, 0f, 1f)); return; } -fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_10: ptr) { +fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_11: ptr) { var sampledTexel: vec4; var t_2: vec3; var b_4: vec3; var sampled: vec3; - let _e200 = v_texcoord_1; - let _e201 = MaterialLodBias_u0028_(); - let _e202 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e200, _e201); - sampledTexel = _e202; - let _e203 = v_tangent_1; - t_2 = _e203.xyz; - let _e205 = t_2; - let _e207 = (*s_10).n; - let _e209 = (*s_10).n; - let _e210 = t_2; - t_2 = (_e205 - (_e207 * dot(_e209, _e210))); + let _e209 = v_texcoord_1; + let _e210 = MaterialLodBias_u0028_(); + let _e211 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e209, _e210); + sampledTexel = _e211; + let _e212 = v_tangent_1; + t_2 = _e212.xyz; let _e214 = t_2; - let _e215 = t_2; - if (dot(_e214, _e215) < 0.000000000001f) { + let _e216 = (*s_11).n; + let _e218 = (*s_11).n; + let _e219 = t_2; + t_2 = (_e214 - (_e216 * dot(_e218, _e219))); + let _e223 = t_2; + let _e224 = t_2; + if (dot(_e223, _e224) < 0.000000000001f) { return; } - let _e218 = t_2; - t_2 = normalize(_e218); - let _e221 = (*s_10).n; - let _e222 = t_2; - let _e225 = v_tangent_1[3u]; - b_4 = (cross(_e221, _e222) * _e225); - let _e227 = gl_FrontFacing_1; - if !(_e227) { - let _e229 = t_2; - t_2 = -(_e229); - } - let _e231 = sampledTexel; - sampled = ((_e231.xyz * 2f) - vec3(1f)); - let _e238 = frag_info.emissive[3u]; - if (_e238 > 0.5f) { - let _e240 = sampled; - let _e242 = sampled; - sampled[2u] = sqrt(max((1f - dot(_e240.xy, _e242.xy)), 0f)); - } - let _e251 = frag_info.material2_[1u]; - let _e252 = sampled; - let _e254 = (_e252.xy * _e251); - sampled[0u] = _e254.x; - sampled[1u] = _e254.y; - let _e259 = t_2; - let _e261 = sampled[0u]; - let _e263 = b_4; - let _e265 = sampled[1u]; - let _e269 = (*s_10).n; - let _e271 = sampled[2u]; - (*s_10).n = normalize((((_e259 * _e261) + (_e263 * _e265)) + (_e269 * _e271))); - let _e277 = (*s_10).n; - let _e279 = (*s_10).v; - (*s_10).n_dot_v = max(dot(_e277, _e279), 0.0001f); + let _e227 = t_2; + t_2 = normalize(_e227); + let _e230 = (*s_11).n; + let _e231 = t_2; + let _e234 = v_tangent_1[3u]; + b_4 = (cross(_e230, _e231) * _e234); + let _e236 = gl_FrontFacing_1; + if !(_e236) { + let _e238 = t_2; + t_2 = -(_e238); + } + let _e240 = sampledTexel; + sampled = ((_e240.xyz * 2f) - vec3(1f)); + let _e247 = frag_info.emissive[3u]; + if (_e247 > 0.5f) { + let _e249 = sampled; + let _e251 = sampled; + sampled[2u] = sqrt(max((1f - dot(_e249.xy, _e251.xy)), 0f)); + } + let _e260 = frag_info.material2_[1u]; + let _e261 = sampled; + let _e263 = (_e261.xy * _e260); + sampled[0u] = _e263.x; + sampled[1u] = _e263.y; + let _e268 = t_2; + let _e270 = sampled[0u]; + let _e272 = b_4; + let _e274 = sampled[1u]; + let _e278 = (*s_11).n; + let _e280 = sampled[2u]; + (*s_11).n = normalize((((_e268 * _e270) + (_e272 * _e274)) + (_e278 * _e280))); + let _e286 = (*s_11).n; + let _e288 = (*s_11).v; + (*s_11).n_dot_v = max(dot(_e286, _e288), 0.0001f); return; } fn AtlasTexel_u0028_vf2_u003b(texel_5: ptr>) -> vec4 { - let _e196 = (*texel_5); - let _e200 = irradiance_info.atlas; - let _e203 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e196 + vec2(0.5f)) * _e200.xy), 0f); - return _e203; + let _e205 = (*texel_5); + let _e209 = irradiance_info.atlas; + let _e212 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e205 + vec2(0.5f)) * _e209.xy), 0f); + return _e212; } fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr>, interior: ptr, uv_6: ptr>) -> vec4 { @@ -15222,50 +15817,50 @@ fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr; var f_3: vec2; var a_6: vec4; - var param_161: vec2; + var param_167: vec2; var b_5: vec4; - var param_162: vec2; - var c: vec4; - var param_163: vec2; + var param_168: vec2; + var c_1: vec4; + var param_169: vec2; var d_5: vec4; - var param_164: vec2; - - let _e209 = (*uv_6); - let _e210 = (*interior); - at_1 = ((vec2(1f) + (_e209 * _e210)) - vec2(0.5f)); - let _e216 = at_1; - low = floor(_e216); - let _e218 = at_1; - let _e219 = low; - f_3 = (_e218 - _e219); - let _e221 = (*corner); - let _e222 = low; - param_161 = (_e221 + _e222); - let _e224 = AtlasTexel_u0028_vf2_u003b((¶m_161)); - a_6 = _e224; - let _e225 = (*corner); - let _e226 = low; - param_162 = ((_e225 + _e226) + vec2(1f, 0f)); - let _e229 = AtlasTexel_u0028_vf2_u003b((¶m_162)); - b_5 = _e229; + var param_170: vec2; + + let _e218 = (*uv_6); + let _e219 = (*interior); + at_1 = ((vec2(1f) + (_e218 * _e219)) - vec2(0.5f)); + let _e225 = at_1; + low = floor(_e225); + let _e227 = at_1; + let _e228 = low; + f_3 = (_e227 - _e228); let _e230 = (*corner); let _e231 = low; - param_163 = ((_e230 + _e231) + vec2(0f, 1f)); - let _e234 = AtlasTexel_u0028_vf2_u003b((¶m_163)); - c = _e234; - let _e235 = (*corner); - let _e236 = low; - param_164 = ((_e235 + _e236) + vec2(1f, 1f)); - let _e239 = AtlasTexel_u0028_vf2_u003b((¶m_164)); - d_5 = _e239; - let _e240 = a_6; - let _e241 = b_5; - let _e243 = f_3[0u]; - let _e246 = c; - let _e247 = d_5; - let _e249 = f_3[0u]; - let _e253 = f_3[1u]; - return mix(mix(_e240, _e241, vec4(_e243)), mix(_e246, _e247, vec4(_e249)), vec4(_e253)); + param_167 = (_e230 + _e231); + let _e233 = AtlasTexel_u0028_vf2_u003b((¶m_167)); + a_6 = _e233; + let _e234 = (*corner); + let _e235 = low; + param_168 = ((_e234 + _e235) + vec2(1f, 0f)); + let _e238 = AtlasTexel_u0028_vf2_u003b((¶m_168)); + b_5 = _e238; + let _e239 = (*corner); + let _e240 = low; + param_169 = ((_e239 + _e240) + vec2(0f, 1f)); + let _e243 = AtlasTexel_u0028_vf2_u003b((¶m_169)); + c_1 = _e243; + let _e244 = (*corner); + let _e245 = low; + param_170 = ((_e244 + _e245) + vec2(1f, 1f)); + let _e248 = AtlasTexel_u0028_vf2_u003b((¶m_170)); + d_5 = _e248; + let _e249 = a_6; + let _e250 = b_5; + let _e252 = f_3[0u]; + let _e255 = c_1; + let _e256 = d_5; + let _e258 = f_3[0u]; + let _e262 = f_3[1u]; + return mix(mix(_e249, _e250, vec4(_e252)), mix(_e255, _e256, vec4(_e258)), vec4(_e262)); } fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec2 { @@ -15273,32 +15868,32 @@ fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec var n_6: vec3; var xy: vec2; - let _e200 = (*direction_1)[0u]; - let _e203 = (*direction_1)[1u]; - let _e207 = (*direction_1)[2u]; - sum_1 = ((abs(_e200) + abs(_e203)) + abs(_e207)); - let _e210 = sum_1; - if (_e210 <= 0f) { + let _e209 = (*direction_1)[0u]; + let _e212 = (*direction_1)[1u]; + let _e216 = (*direction_1)[2u]; + sum_1 = ((abs(_e209) + abs(_e212)) + abs(_e216)); + let _e219 = sum_1; + if (_e219 <= 0f) { return vec2(0.5f, 0.5f); } - let _e212 = (*direction_1); - let _e213 = sum_1; - n_6 = (_e212 / vec3(_e213)); - let _e216 = n_6; - xy = _e216.xy; - let _e219 = n_6[2u]; - if (_e219 < 0f) { - let _e222 = n_6[1u]; - let _e226 = n_6[0u]; - let _e231 = n_6[0u]; - let _e235 = n_6[1u]; - xy = vec2(((1f - abs(_e222)) * select(-1f, 1f, (_e226 >= 0f))), ((1f - abs(_e231)) * select(-1f, 1f, (_e235 >= 0f)))); - } - let _e240 = xy; - return ((_e240 * 0.5f) + vec2(0.5f)); + let _e221 = (*direction_1); + let _e222 = sum_1; + n_6 = (_e221 / vec3(_e222)); + let _e225 = n_6; + xy = _e225.xy; + let _e228 = n_6[2u]; + if (_e228 < 0f) { + let _e231 = n_6[1u]; + let _e235 = n_6[0u]; + let _e240 = n_6[0u]; + let _e244 = n_6[1u]; + xy = vec2(((1f - abs(_e231)) * select(-1f, 1f, (_e235 >= 0f))), ((1f - abs(_e240)) * select(-1f, 1f, (_e244 >= 0f)))); + } + let _e249 = xy; + return ((_e249 * 0.5f) + vec2(0.5f)); } -fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view: ptr>) -> vec3 { +fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view_1: ptr>) -> vec3 { var origin_2: vec3; var spacing: vec3; var counts: vec3; @@ -15320,9 +15915,9 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var irradianceCorner: vec2; var momentCorner: vec2; - var param_165: vec2; + var param_171: vec2; var trilinear: vec3; - var weight_2: f32; + var weight_3: f32; var probePosition: vec3; var toProbe: vec3; var distance_4: f32; @@ -15330,335 +15925,335 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; - var param_166: vec3; - var param_167: vec2; - var param_168: f32; - var param_169: vec2; + var param_172: vec3; + var param_173: vec2; + var param_174: f32; + var param_175: vec2; var chebyshev: f32; var variance_1: f32; var difference: f32; - var param_170: vec3; - var param_171: vec2; - var param_172: f32; - var param_173: vec2; - var local_28: vec3; + var param_176: vec3; + var param_177: vec2; + var param_178: f32; + var param_179: vec2; + var local_30: vec3; - let _e242 = irradiance_info.origin; - origin_2 = _e242.xyz; - let _e245 = irradiance_info.spacing; - spacing = _e245.xyz; - let _e248 = irradiance_info.counts; - counts = _e248.xyz; - let _e252 = irradiance_info.tiles[0u]; - irradianceTile = _e252; - let _e255 = irradiance_info.tiles[1u]; - depthTile = _e255; - let _e258 = irradiance_info.tiles[2u]; - columns = _e258; - let _e261 = irradiance_info.tiles[3u]; - momentsTop = _e261; - let _e262 = (*normal_1); - unit = normalize(_e262); - let _e264 = (*world_4); - let _e265 = unit; - let _e268 = irradiance_info.spacing[3u]; - let _e271 = (*view); - let _e274 = irradiance_info.counts[3u]; - biased = ((_e264 + (_e265 * _e268)) + (_e271 * _e274)); - let _e277 = biased; - let _e278 = origin_2; - let _e280 = spacing; - grid_1 = ((_e277 - _e278) / _e280); - let _e282 = grid_1; - let _e284 = counts; - base = clamp(floor(_e282), vec3(0f, 0f, 0f), (_e284 - vec3(2f))); - let _e288 = grid_1; - let _e289 = base; - f_4 = clamp((_e288 - _e289), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e251 = irradiance_info.origin; + origin_2 = _e251.xyz; + let _e254 = irradiance_info.spacing; + spacing = _e254.xyz; + let _e257 = irradiance_info.counts; + counts = _e257.xyz; + let _e261 = irradiance_info.tiles[0u]; + irradianceTile = _e261; + let _e264 = irradiance_info.tiles[1u]; + depthTile = _e264; + let _e267 = irradiance_info.tiles[2u]; + columns = _e267; + let _e270 = irradiance_info.tiles[3u]; + momentsTop = _e270; + let _e271 = (*normal_1); + unit = normalize(_e271); + let _e273 = (*world_4); + let _e274 = unit; + let _e277 = irradiance_info.spacing[3u]; + let _e280 = (*view_1); + let _e283 = irradiance_info.counts[3u]; + biased = ((_e273 + (_e274 * _e277)) + (_e280 * _e283)); + let _e286 = biased; + let _e287 = origin_2; + let _e289 = spacing; + grid_1 = ((_e286 - _e287) / _e289); + let _e291 = grid_1; + let _e293 = counts; + base = clamp(floor(_e291), vec3(0f, 0f, 0f), (_e293 - vec3(2f))); + let _e297 = grid_1; + let _e298 = base; + f_4 = clamp((_e297 - _e298), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); total_2 = vec3(0f, 0f, 0f); weights = 0f; corner_1 = 0i; loop { - let _e292 = corner_1; - if (_e292 < 8i) { - let _e294 = corner_1; - let _e297 = corner_1; - let _e302 = corner_1; - offset_3 = vec3(f32((_e294 & 1i)), f32(((_e297 >> bitcast(1i)) & 1i)), f32(((_e302 >> bitcast(2i)) & 1i))); - let _e308 = base; - let _e309 = offset_3; - cell_1 = (_e308 + _e309); - let _e312 = cell_1[2u]; - let _e314 = counts[1u]; - let _e317 = cell_1[1u]; - let _e320 = counts[0u]; - let _e323 = cell_1[0u]; - probe = ((((_e312 * _e314) + _e317) * _e320) + _e323); - let _e325 = probe; - let _e326 = columns; - let _e331 = probe; - let _e332 = columns; - tile_4 = vec2((_e325 - (floor((_e325 / _e326)) * _e326)), floor((_e331 / _e332))); - let _e336 = tile_4; - let _e337 = irradianceTile; - irradianceCorner = (_e336 * (_e337 + 2f)); - let _e341 = tile_4[0u]; - let _e342 = depthTile; - let _e345 = momentsTop; - let _e347 = tile_4[1u]; - let _e348 = depthTile; - momentCorner = vec2((_e341 * (_e342 + 2f)), (_e345 + (_e347 * (_e348 + 2f)))); - let _e353 = irradianceCorner; - param_165 = (_e353 + vec2(1f)); - let _e356 = AtlasTexel_u0028_vf2_u003b((¶m_165)); - if (_e356.w < 0.5f) { + let _e301 = corner_1; + if (_e301 < 8i) { + let _e303 = corner_1; + let _e306 = corner_1; + let _e311 = corner_1; + offset_3 = vec3(f32((_e303 & 1i)), f32(((_e306 >> bitcast(1i)) & 1i)), f32(((_e311 >> bitcast(2i)) & 1i))); + let _e317 = base; + let _e318 = offset_3; + cell_1 = (_e317 + _e318); + let _e321 = cell_1[2u]; + let _e323 = counts[1u]; + let _e326 = cell_1[1u]; + let _e329 = counts[0u]; + let _e332 = cell_1[0u]; + probe = ((((_e321 * _e323) + _e326) * _e329) + _e332); + let _e334 = probe; + let _e335 = columns; + let _e340 = probe; + let _e341 = columns; + tile_4 = vec2((_e334 - (floor((_e334 / _e335)) * _e335)), floor((_e340 / _e341))); + let _e345 = tile_4; + let _e346 = irradianceTile; + irradianceCorner = (_e345 * (_e346 + 2f)); + let _e350 = tile_4[0u]; + let _e351 = depthTile; + let _e354 = momentsTop; + let _e356 = tile_4[1u]; + let _e357 = depthTile; + momentCorner = vec2((_e350 * (_e351 + 2f)), (_e354 + (_e356 * (_e357 + 2f)))); + let _e362 = irradianceCorner; + param_171 = (_e362 + vec2(1f)); + let _e365 = AtlasTexel_u0028_vf2_u003b((¶m_171)); + if (_e365.w < 0.5f) { continue; } - let _e359 = f_4; - let _e361 = f_4; - let _e362 = offset_3; - trilinear = mix((vec3(1f, 1f, 1f) - _e359), _e361, _e362); - let _e365 = trilinear[0u]; - let _e367 = trilinear[1u]; - let _e370 = trilinear[2u]; - weight_2 = max(((_e365 * _e367) * _e370), 0.001f); - let _e373 = origin_2; - let _e374 = spacing; - let _e375 = cell_1; - probePosition = (_e373 + (_e374 * _e375)); - let _e378 = probePosition; - let _e379 = biased; - toProbe = (_e378 - _e379); - let _e381 = toProbe; - distance_4 = length(_e381); - let _e383 = distance_4; - if (_e383 > 0.000001f) { - let _e385 = toProbe; - let _e386 = distance_4; - direction_2 = (_e385 / vec3(_e386)); - let _e389 = unit; - let _e390 = probePosition; - let _e391 = (*world_4); - facing = ((dot(_e389, normalize((_e390 - _e391))) * 0.5f) + 0.5f); - let _e397 = facing; - let _e398 = facing; - probeWeight = ((_e397 * _e398) + 0.2f); - let _e401 = direction_2; - param_166 = -(_e401); - let _e403 = ProbeOctahedral_u0028_vf3_u003b((¶m_166)); - let _e404 = momentCorner; - param_167 = _e404; - let _e405 = depthTile; - param_168 = _e405; - param_169 = _e403; - let _e406 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_167), (¶m_168), (¶m_169)); - moments_3 = _e406.xy; + let _e368 = f_4; + let _e370 = f_4; + let _e371 = offset_3; + trilinear = mix((vec3(1f, 1f, 1f) - _e368), _e370, _e371); + let _e374 = trilinear[0u]; + let _e376 = trilinear[1u]; + let _e379 = trilinear[2u]; + weight_3 = max(((_e374 * _e376) * _e379), 0.001f); + let _e382 = origin_2; + let _e383 = spacing; + let _e384 = cell_1; + probePosition = (_e382 + (_e383 * _e384)); + let _e387 = probePosition; + let _e388 = biased; + toProbe = (_e387 - _e388); + let _e390 = toProbe; + distance_4 = length(_e390); + let _e392 = distance_4; + if (_e392 > 0.000001f) { + let _e394 = toProbe; + let _e395 = distance_4; + direction_2 = (_e394 / vec3(_e395)); + let _e398 = unit; + let _e399 = probePosition; + let _e400 = (*world_4); + facing = ((dot(_e398, normalize((_e399 - _e400))) * 0.5f) + 0.5f); + let _e406 = facing; + let _e407 = facing; + probeWeight = ((_e406 * _e407) + 0.2f); + let _e410 = direction_2; + param_172 = -(_e410); + let _e412 = ProbeOctahedral_u0028_vf3_u003b((¶m_172)); + let _e413 = momentCorner; + param_173 = _e413; + let _e414 = depthTile; + param_174 = _e414; + param_175 = _e412; + let _e415 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_173), (¶m_174), (¶m_175)); + moments_3 = _e415.xy; chebyshev = 1f; - let _e408 = distance_4; - let _e410 = moments_3[0u]; - if (_e408 > _e410) { - let _e413 = moments_3[1u]; - let _e415 = moments_3[0u]; - let _e417 = moments_3[0u]; - variance_1 = max((_e413 - (_e415 * _e417)), 0.000001f); - let _e421 = distance_4; - let _e423 = moments_3[0u]; - difference = (_e421 - _e423); - let _e425 = variance_1; - let _e426 = variance_1; - let _e427 = difference; - let _e428 = difference; - chebyshev = (_e425 / (_e426 + (_e427 * _e428))); - let _e432 = chebyshev; - let _e433 = chebyshev; - let _e435 = chebyshev; - chebyshev = ((_e432 * _e433) * _e435); + let _e417 = distance_4; + let _e419 = moments_3[0u]; + if (_e417 > _e419) { + let _e422 = moments_3[1u]; + let _e424 = moments_3[0u]; + let _e426 = moments_3[0u]; + variance_1 = max((_e422 - (_e424 * _e426)), 0.000001f); + let _e430 = distance_4; + let _e432 = moments_3[0u]; + difference = (_e430 - _e432); + let _e434 = variance_1; + let _e435 = variance_1; + let _e436 = difference; + let _e437 = difference; + chebyshev = (_e434 / (_e435 + (_e436 * _e437))); + let _e441 = chebyshev; + let _e442 = chebyshev; + let _e444 = chebyshev; + chebyshev = ((_e441 * _e442) * _e444); } - let _e437 = probeWeight; - let _e438 = chebyshev; - probeWeight = max((_e437 * max(_e438, 0.05f)), 0.000001f); - let _e442 = probeWeight; - if (_e442 < 0.2f) { - let _e444 = probeWeight; - let _e445 = probeWeight; - let _e448 = probeWeight; - probeWeight = (_e448 * ((_e444 * _e445) * 25f)); + let _e446 = probeWeight; + let _e447 = chebyshev; + probeWeight = max((_e446 * max(_e447, 0.05f)), 0.000001f); + let _e451 = probeWeight; + if (_e451 < 0.2f) { + let _e453 = probeWeight; + let _e454 = probeWeight; + let _e457 = probeWeight; + probeWeight = (_e457 * ((_e453 * _e454) * 25f)); } - let _e450 = probeWeight; - let _e451 = weight_2; - weight_2 = (_e451 * _e450); - } - let _e453 = unit; - param_170 = _e453; - let _e454 = ProbeOctahedral_u0028_vf3_u003b((¶m_170)); - let _e455 = irradianceCorner; - param_171 = _e455; - let _e456 = irradianceTile; - param_172 = _e456; - param_173 = _e454; - let _e457 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_171), (¶m_172), (¶m_173)); - let _e459 = weight_2; - let _e461 = total_2; - total_2 = (_e461 + (_e457.xyz * _e459)); - let _e463 = weight_2; - let _e464 = weights; - weights = (_e464 + _e463); + let _e459 = probeWeight; + let _e460 = weight_3; + weight_3 = (_e460 * _e459); + } + let _e462 = unit; + param_176 = _e462; + let _e463 = ProbeOctahedral_u0028_vf3_u003b((¶m_176)); + let _e464 = irradianceCorner; + param_177 = _e464; + let _e465 = irradianceTile; + param_178 = _e465; + param_179 = _e463; + let _e466 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_177), (¶m_178), (¶m_179)); + let _e468 = weight_3; + let _e470 = total_2; + total_2 = (_e470 + (_e466.xyz * _e468)); + let _e472 = weight_3; + let _e473 = weights; + weights = (_e473 + _e472); continue; } else { break; } continuing { - let _e466 = corner_1; - corner_1 = (_e466 + 1i); + let _e475 = corner_1; + corner_1 = (_e475 + 1i); } } - let _e468 = weights; - if (_e468 > 0f) { - let _e470 = total_2; - let _e471 = weights; - local_28 = (_e470 / vec3(_e471)); + let _e477 = weights; + if (_e477 > 0f) { + let _e479 = total_2; + let _e480 = weights; + local_30 = (_e479 / vec3(_e480)); } else { - local_28 = vec3(0f, 0f, 0f); + local_30 = vec3(0f, 0f, 0f); } - let _e474 = local_28; - return _e474; + let _e483 = local_30; + return _e483; } fn IrradianceEnabled_u0028_() -> bool { - let _e197 = irradiance_info.origin[3u]; - return (_e197 > 0.5f); + let _e206 = irradiance_info.origin[3u]; + return (_e206 > 0.5f); } -fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_11: ptr) { - var param_174: vec3; - var param_175: vec3; - var param_176: vec3; - var param_177: Surface; - var param_178: Surface; - var param_179: Surface; - - let _e202 = IrradianceEnabled_u0028_(); - if _e202 { - let _e203 = v_world_position_1; - param_174 = _e203; - let _e205 = (*s_11).n; - param_175 = _e205; - let _e207 = (*s_11).v; - param_176 = _e207; - let _e208 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_174), (¶m_175), (¶m_176)); - let _e211 = frag_info.material[2u]; - (*s_11).ambient = (_e208 * _e211); - } - let _e214 = (*s_11); - param_177 = _e214; - ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_177)); - let _e215 = param_177; - (*s_11) = _e215; - let _e216 = (*s_11); - param_178 = _e216; - ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_178)); - let _e217 = param_178; - (*s_11) = _e217; - let _e218 = (*s_11); - param_179 = _e218; - ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_179)); - let _e219 = param_179; - (*s_11) = _e219; +fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_12: ptr) { + var param_180: vec3; + var param_181: vec3; + var param_182: vec3; + var param_183: Surface; + var param_184: Surface; + var param_185: Surface; + + let _e211 = IrradianceEnabled_u0028_(); + if _e211 { + let _e212 = v_world_position_1; + param_180 = _e212; + let _e214 = (*s_12).n; + param_181 = _e214; + let _e216 = (*s_12).v; + param_182 = _e216; + let _e217 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_180), (¶m_181), (¶m_182)); + let _e220 = frag_info.material[2u]; + (*s_12).ambient = (_e217 * _e220); + } + let _e223 = (*s_12); + param_183 = _e223; + ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_183)); + let _e224 = param_183; + (*s_12) = _e224; + let _e225 = (*s_12); + param_184 = _e225; + ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_184)); + let _e226 = param_184; + (*s_12) = _e226; + let _e227 = (*s_12); + param_185 = _e227; + ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_185)); + let _e228 = param_185; + (*s_12) = _e228; return; } fn ReadSurface_u0028_() -> Surface { var texel_6: vec4; - var s_12: Surface; - var param_180: vec3; - var param_181: vec3; + var s_13: Surface; + var param_186: vec3; + var param_187: vec3; var cutoff: f32; var anchored: vec3; var noise_1: f32; - let _e202 = v_texcoord_1; - let _e203 = MaterialLodBias_u0028_(); - let _e204 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e202, _e203); - texel_6 = _e204; - let _e205 = texel_6; - param_180 = _e205.xyz; - let _e207 = SrgbToLinear_u0028_vf3_u003b((¶m_180)); - let _e209 = frag_info.base_color; - param_181 = _e209.xyz; - let _e211 = SrgbToLinear_u0028_vf3_u003b((¶m_181)); - let _e213 = v_color_1; - s_12.albedo = ((_e207 * _e211) * _e213.xyz); - let _e218 = texel_6[3u]; - let _e221 = frag_info.base_color[3u]; - let _e224 = v_color_1[3u]; - s_12.alpha = ((_e218 * _e221) * _e224); - let _e228 = s_12.albedo; - g_albedo = _e228; - let _e231 = frag_info.material2_[0u]; - cutoff = _e231; - let _e232 = cutoff; - if (_e232 >= 0f) { - let _e235 = s_12.alpha; - let _e236 = cutoff; - if (_e235 < _e236) { + let _e211 = v_texcoord_1; + let _e212 = MaterialLodBias_u0028_(); + let _e213 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e211, _e212); + texel_6 = _e213; + let _e214 = texel_6; + param_186 = _e214.xyz; + let _e216 = SrgbToLinear_u0028_vf3_u003b((¶m_186)); + let _e218 = frag_info.base_color; + param_187 = _e218.xyz; + let _e220 = SrgbToLinear_u0028_vf3_u003b((¶m_187)); + let _e222 = v_color_1; + s_13.albedo = ((_e216 * _e220) * _e222.xyz); + let _e227 = texel_6[3u]; + let _e230 = frag_info.base_color[3u]; + let _e233 = v_color_1[3u]; + s_13.alpha = ((_e227 * _e230) * _e233); + let _e237 = s_13.albedo; + g_albedo = _e237; + let _e240 = frag_info.material2_[0u]; + cutoff = _e240; + let _e241 = cutoff; + if (_e241 >= 0f) { + let _e244 = s_13.alpha; + let _e245 = cutoff; + if (_e244 < _e245) { discard; } } else { - let _e238 = cutoff; - if (_e238 < -1.5f) { - let _e240 = v_world_position_1; - anchored = floor((_e240 * 16f)); - let _e243 = anchored; - noise_1 = fract((sin(dot(_e243, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e249 = s_12.alpha; - let _e250 = noise_1; - if (_e249 < _e250) { + let _e247 = cutoff; + if (_e247 < -1.5f) { + let _e249 = v_world_position_1; + anchored = floor((_e249 * 16f)); + let _e252 = anchored; + noise_1 = fract((sin(dot(_e252, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e258 = s_13.alpha; + let _e259 = noise_1; + if (_e258 < _e259) { discard; } } } - let _e252 = cutoff; - let _e254 = cutoff; - g_premultiply = ((_e252 < 0f) && (_e254 > -0.75f)); - let _e257 = v_normal_1; - s_12.n = normalize(_e257); - let _e260 = gl_FrontFacing_1; - if !(_e260) { - let _e263 = s_12.n; - s_12.n = -(_e263); + let _e261 = cutoff; + let _e263 = cutoff; + g_premultiply = ((_e261 < 0f) && (_e263 > -0.75f)); + let _e266 = v_normal_1; + s_13.n = normalize(_e266); + let _e269 = gl_FrontFacing_1; + if !(_e269) { + let _e272 = s_13.n; + s_13.n = -(_e272); } - let _e267 = frag_info.camera_position; - let _e269 = v_world_position_1; - s_12.v = normalize((_e267.xyz - _e269)); - let _e274 = s_12.n; - let _e276 = s_12.v; - s_12.n_dot_v = max(dot(_e274, _e276), 0.0001f); - let _e282 = frag_info.material[0u]; - s_12.metallic = clamp(_e282, 0f, 1f); - let _e287 = frag_info.material[1u]; - s_12.roughness = clamp(_e287, 0.02f, 1f); - let _e291 = frag_info.ambient_ground; - let _e294 = frag_info.ambient_sky; - let _e298 = s_12.n[1u]; - let _e305 = frag_info.material[2u]; - s_12.ambient = (mix(_e291.xyz, _e294.xyz, vec3(((_e298 * 0.5f) + 0.5f))) * _e305); - let _e310 = frag_info.frame_params[0u]; - s_12.exposure = max(_e310, 0f); - s_12.occlusion = 1f; - s_12.emissive = vec3(0f, 0f, 0f); - let _e315 = s_12; - return _e315; + let _e276 = frag_info.camera_position; + let _e278 = v_world_position_1; + s_13.v = normalize((_e276.xyz - _e278)); + let _e283 = s_13.n; + let _e285 = s_13.v; + s_13.n_dot_v = max(dot(_e283, _e285), 0.0001f); + let _e291 = frag_info.material[0u]; + s_13.metallic = clamp(_e291, 0f, 1f); + let _e296 = frag_info.material[1u]; + s_13.roughness = clamp(_e296, 0.02f, 1f); + let _e300 = frag_info.ambient_ground; + let _e303 = frag_info.ambient_sky; + let _e307 = s_13.n[1u]; + let _e314 = frag_info.material[2u]; + s_13.ambient = (mix(_e300.xyz, _e303.xyz, vec3(((_e307 * 0.5f) + 0.5f))) * _e314); + let _e319 = frag_info.frame_params[0u]; + s_13.exposure = max(_e319, 0f); + s_13.occlusion = 1f; + s_13.emissive = vec3(0f, 0f, 0f); + let _e324 = s_13; + return _e324; } fn main_1() { - var s_13: Surface; - var param_182: Surface; - var param_183: Surface; + var s_14: Surface; + var param_188: Surface; + var param_189: Surface; var metallic: f32; var diffuseColor_1: vec3; - var param_184: vec3; - var param_185: f32; - var param_186: f32; + var param_190: vec3; + var param_191: f32; + var param_192: f32; var ambient: vec3; var levels: f32; var f0_3: vec3; @@ -15666,17 +16261,20 @@ fn main_1() { var irradiance: vec3; var prefiltered: vec3; var ab_1: vec2; - var param_187: f32; - var param_188: f32; + var param_193: f32; + var param_194: f32; var single_1: vec3; var specular_1: vec3; var missed: f32; var average_2: vec3; var multiple: vec3; - var param_189: Surface; - var param_190: vec3; - var param_191: f32; - var param_192: f32; + var lit_3: vec3; + var param_195: Surface; + var param_196: Surface; + var param_197: vec3; + var param_198: vec3; + var param_199: f32; + var param_200: f32; g_albedo = vec3(0f, 0f, 0f); g_debug_surface = vec3(0f, 0f, 0f); @@ -15684,113 +16282,123 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e221 = ReadSurface_u0028_(); - s_13 = _e221; - let _e222 = s_13; - param_182 = _e222; - ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_182)); - let _e223 = param_182; - s_13 = _e223; - let _e224 = s_13; - param_183 = _e224; - ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_183)); - let _e225 = param_183; - s_13 = _e225; - let _e227 = s_13.metallic; - metallic = clamp(_e227, 0f, 1f); - let _e230 = s_13.albedo; - let _e231 = metallic; - diffuseColor_1 = (_e230 * (1f - _e231)); - let _e234 = EonDiffuse_u0028_(); - if _e234 { - let _e235 = diffuseColor_1; - param_184 = _e235; - let _e237 = s_13.roughness; - param_185 = _e237; - let _e239 = s_13.n_dot_v; - param_186 = _e239; - let _e240 = EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_184), (¶m_185), (¶m_186)); - diffuseColor_1 = _e240; - } - let _e241 = diffuseColor_1; - let _e243 = s_13.ambient; - let _e246 = s_13.occlusion; - ambient = ((_e241 * _e243) * _e246); - let _e250 = frag_info.frame_params[3u]; - levels = _e250; - let _e251 = levels; - if (_e251 > 0f) { - let _e254 = s_13.albedo; - let _e255 = metallic; - f0_3 = mix(vec3(0.04f, 0.04f, 0.04f), _e254, vec3(_e255)); - let _e259 = s_13.v; - let _e262 = s_13.n; - reflected = reflect(-(_e259), _e262); - let _e265 = s_13.n; - let _e266 = levels; - let _e267 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e265, _e266); - irradiance = _e267.xyz; - let _e269 = reflected; - let _e271 = s_13.roughness; - let _e272 = levels; - let _e274 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e269, (_e271 * _e272)); - prefiltered = _e274.xyz; - let _e277 = s_13.roughness; - param_187 = _e277; - let _e279 = s_13.n_dot_v; - param_188 = _e279; - let _e280 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_187), (¶m_188)); - ab_1 = _e280; - let _e281 = f0_3; - let _e283 = ab_1[0u]; - let _e286 = ab_1[1u]; - single_1 = ((_e281 * _e283) + vec3(_e286)); - let _e289 = prefiltered; - let _e290 = single_1; - specular_1 = (_e289 * _e290); - let _e292 = EnergyCompensation_u0028_(); - if _e292 { - let _e294 = ab_1[0u]; - let _e296 = ab_1[1u]; - missed = (1f - (_e294 + _e296)); - let _e299 = f0_3; - let _e300 = f0_3; - average_2 = (_e299 + ((vec3(1f, 1f, 1f) - _e300) / vec3(21f))); - let _e305 = single_1; - let _e306 = average_2; - let _e308 = missed; - let _e309 = average_2; - multiple = ((_e305 * _e306) / (vec3(1f, 1f, 1f) - (_e309 * _e308))); - let _e313 = multiple; - let _e314 = missed; - let _e316 = irradiance; - let _e318 = specular_1; - specular_1 = (_e318 + ((_e313 * _e314) * _e316)); - } - let _e320 = diffuseColor_1; - let _e321 = irradiance; - let _e323 = specular_1; - let _e327 = frag_info.material[2u]; - let _e330 = s_13.occlusion; - ambient = ((((_e320 * _e321) + _e323) * _e327) * _e330); - } - let _e332 = diffuseColor_1; - let _e333 = SampleLightmap_u0028_(); - let _e336 = s_13.occlusion; - let _e338 = ambient; - ambient = (_e338 + ((_e332 * _e333) * _e336)); - let _e340 = s_13; - param_189 = _e340; - let _e341 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_189)); - let _e343 = s_13.occlusion; - let _e345 = ambient; - let _e348 = s_13.emissive; - param_190 = (((_e341 * _e343) + _e345) + _e348); - let _e351 = s_13.alpha; - param_191 = _e351; - let _e353 = s_13.roughness; - param_192 = _e353; - WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_190), (¶m_191), (¶m_192)); + let _e233 = ReadSurface_u0028_(); + s_14 = _e233; + let _e234 = s_14; + param_188 = _e234; + ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_188)); + let _e235 = param_188; + s_14 = _e235; + let _e236 = s_14; + param_189 = _e236; + ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_189)); + let _e237 = param_189; + s_14 = _e237; + let _e239 = s_14.metallic; + metallic = clamp(_e239, 0f, 1f); + let _e242 = s_14.albedo; + let _e243 = metallic; + diffuseColor_1 = (_e242 * (1f - _e243)); + let _e246 = EonDiffuse_u0028_(); + if _e246 { + let _e247 = diffuseColor_1; + param_190 = _e247; + let _e249 = s_14.roughness; + param_191 = _e249; + let _e251 = s_14.n_dot_v; + param_192 = _e251; + let _e252 = EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_190), (¶m_191), (¶m_192)); + diffuseColor_1 = _e252; + } + let _e253 = diffuseColor_1; + let _e255 = s_14.ambient; + let _e258 = s_14.occlusion; + ambient = ((_e253 * _e255) * _e258); + let _e262 = frag_info.frame_params[3u]; + levels = _e262; + let _e263 = levels; + if (_e263 > 0f) { + let _e266 = s_14.albedo; + let _e267 = metallic; + f0_3 = mix(vec3(0.04f, 0.04f, 0.04f), _e266, vec3(_e267)); + let _e271 = s_14.v; + let _e274 = s_14.n; + reflected = reflect(-(_e271), _e274); + let _e277 = s_14.n; + let _e278 = levels; + let _e279 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e277, _e278); + irradiance = _e279.xyz; + let _e281 = reflected; + let _e283 = s_14.roughness; + let _e284 = levels; + let _e286 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e281, (_e283 * _e284)); + prefiltered = _e286.xyz; + let _e289 = s_14.roughness; + param_193 = _e289; + let _e291 = s_14.n_dot_v; + param_194 = _e291; + let _e292 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_193), (¶m_194)); + ab_1 = _e292; + let _e293 = f0_3; + let _e295 = ab_1[0u]; + let _e298 = ab_1[1u]; + single_1 = ((_e293 * _e295) + vec3(_e298)); + let _e301 = prefiltered; + let _e302 = single_1; + specular_1 = (_e301 * _e302); + let _e304 = EnergyCompensation_u0028_(); + if _e304 { + let _e306 = ab_1[0u]; + let _e308 = ab_1[1u]; + missed = (1f - (_e306 + _e308)); + let _e311 = f0_3; + let _e312 = f0_3; + average_2 = (_e311 + ((vec3(1f, 1f, 1f) - _e312) / vec3(21f))); + let _e317 = single_1; + let _e318 = average_2; + let _e320 = missed; + let _e321 = average_2; + multiple = ((_e317 * _e318) / (vec3(1f, 1f, 1f) - (_e321 * _e320))); + let _e325 = multiple; + let _e326 = missed; + let _e328 = irradiance; + let _e330 = specular_1; + specular_1 = (_e330 + ((_e325 * _e326) * _e328)); + } + let _e332 = diffuseColor_1; + let _e333 = irradiance; + let _e335 = specular_1; + let _e339 = frag_info.material[2u]; + let _e342 = s_14.occlusion; + ambient = ((((_e332 * _e333) + _e335) * _e339) * _e342); + } + let _e344 = diffuseColor_1; + let _e345 = SampleLightmap_u0028_(); + let _e348 = s_14.occlusion; + let _e350 = ambient; + ambient = (_e350 + ((_e344 * _e345) * _e348)); + let _e352 = s_14; + param_195 = _e352; + let _e353 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_195)); + let _e355 = s_14.occlusion; + let _e357 = ambient; + let _e360 = s_14.emissive; + lit_3 = (((_e353 * _e355) + _e357) + _e360); + let _e362 = s_14; + param_196 = _e362; + let _e363 = lit_3; + param_197 = _e363; + let _e364 = WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b((¶m_196), (¶m_197)); + if _e364 { + return; + } + let _e365 = lit_3; + param_198 = _e365; + let _e367 = s_14.alpha; + param_199 = _e367; + let _e369 = s_14.roughness; + param_200 = _e369; + WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_198), (¶m_199), (¶m_200)); return; } @@ -15832,7 +16440,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember( name: 'light_position', @@ -15929,7 +16537,12 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, shadow_bias: vec4, target_origin: vec4, + debug_view: vec4, } struct PointShadow { @@ -16354,10 +16968,10 @@ var scene_colour_texture_tex: texture_2d; var scene_colour_texture_smp: sampler; fn ViewDepth_u0028_() -> f32 { - let _e261 = v_world_position_1; - let _e263 = fog_info.eye; - let _e267 = fog_info.forward; - return dot((_e261 - _e263.xyz), _e267.xyz); + let _e270 = v_world_position_1; + let _e272 = fog_info.eye; + let _e276 = fog_info.forward; + return dot((_e270 - _e272.xyz), _e276.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -16366,22 +16980,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e266 = ViewDepth_u0028_(); - z = abs(_e266); - let _e268 = z; - near = (_e268 / 5f); - let _e270 = z; - far = (_e270 / 200f); - let _e272 = far; - let _e273 = far; - let _e275 = far; - far3_ = ((_e272 * _e273) * _e275); - let _e277 = (*alpha); - let _e278 = near; - let _e279 = near; - let _e282 = far3_; - let _e283 = far3_; - return (_e277 * clamp((10f / ((0.00001f + (_e278 * _e279)) + (_e282 * _e283))), 0.01f, 3000f)); + let _e275 = ViewDepth_u0028_(); + z = abs(_e275); + let _e277 = z; + near = (_e277 / 5f); + let _e279 = z; + far = (_e279 / 200f); + let _e281 = far; + let _e282 = far; + let _e284 = far; + far3_ = ((_e281 * _e282) * _e284); + let _e286 = (*alpha); + let _e287 = near; + let _e288 = near; + let _e291 = far3_; + let _e292 = far3_; + return (_e286 * clamp((10f / ((0.00001f + (_e287 * _e288)) + (_e291 * _e292))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -16393,39 +17007,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e270 = fog_info.forward[3u]; - mode = _e270; - let _e271 = mode; - if (_e271 > 0.5f) { - let _e274 = frag_color[3u]; - alpha_1 = _e274; - let _e275 = alpha_1; - param = _e275; - let _e276 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e276; - let _e277 = frag_color; - let _e279 = weight; - let _e280 = (_e277.xyz * _e279); - let _e281 = alpha_1; - let _e282 = weight; - accumulate = vec4(_e280.x, _e280.y, _e280.z, (_e281 * _e282)); - let _e288 = mode; - let _e290 = mode; - revealage = ((_e288 > 1.5f) && (_e290 < 2.5f)); - let _e293 = revealage; - if _e293 { - let _e294 = alpha_1; - local = vec4(_e294); + let _e279 = fog_info.forward[3u]; + mode = _e279; + let _e280 = mode; + if (_e280 > 0.5f) { + let _e283 = frag_color[3u]; + alpha_1 = _e283; + let _e284 = alpha_1; + param = _e284; + let _e285 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e285; + let _e286 = frag_color; + let _e288 = weight; + let _e289 = (_e286.xyz * _e288); + let _e290 = alpha_1; + let _e291 = weight; + accumulate = vec4(_e289.x, _e289.y, _e289.z, (_e290 * _e291)); + let _e297 = mode; + let _e299 = mode; + revealage = ((_e297 > 1.5f) && (_e299 < 2.5f)); + let _e302 = revealage; + if _e302 { + let _e303 = alpha_1; + local = vec4(_e303); } else { - let _e296 = accumulate; - local = _e296; + let _e305 = accumulate; + local = _e305; } - let _e297 = local; - frag_color = _e297; - let _e298 = mode; - if (_e298 > 2.5f) { - let _e300 = alpha_1; - frag_surface = vec4(_e300); + let _e306 = local; + frag_color = _e306; + let _e307 = mode; + if (_e307 > 2.5f) { + let _e309 = alpha_1; + frag_surface = vec4(_e309); } } return; @@ -16434,29 +17048,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e264 = (*n)[0u]; - let _e267 = (*n)[1u]; - let _e271 = (*n)[2u]; - let _e274 = (*n); - (*n) = (_e274 / vec3(((abs(_e264) + abs(_e267)) + abs(_e271)))); - let _e277 = (*n); - e = _e277.xy; + let _e273 = (*n)[0u]; + let _e276 = (*n)[1u]; let _e280 = (*n)[2u]; - if (_e280 < 0f) { - let _e282 = (*n); - let _e288 = (*n)[0u]; - let _e292 = (*n)[1u]; - e = ((vec2(1f) - abs(_e282.yx)) * vec2(select(-1f, 1f, (_e288 >= 0f)), select(-1f, 1f, (_e292 >= 0f)))); - } - let _e297 = e; - return ((_e297 * 0.5f) + vec2(0.5f)); + let _e283 = (*n); + (*n) = (_e283 / vec3(((abs(_e273) + abs(_e276)) + abs(_e280)))); + let _e286 = (*n); + e = _e286.xy; + let _e289 = (*n)[2u]; + if (_e289 < 0f) { + let _e291 = (*n); + let _e297 = (*n)[0u]; + let _e301 = (*n)[1u]; + e = ((vec2(1f) - abs(_e291.yx)) * vec2(select(-1f, 1f, (_e297 >= 0f)), select(-1f, 1f, (_e301 >= 0f)))); + } + let _e306 = e; + return ((_e306 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e262 = (*linear); - let _e264 = (*linear); - let _e268 = (*linear); - return mix((_e262 * 12.92f), ((pow(_e264, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e268)); + let _e271 = (*linear); + let _e273 = (*linear); + let _e277 = (*linear); + return mix((_e271 * 12.92f), ((pow(_e273, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e277)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -16464,37 +17078,37 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e265 = g_albedo; - param_1 = clamp(_e265, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e267 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e267.x, _e267.y, _e267.z, 1f); - let _e272 = g_debug_surface_on; - if _e272 { - let _e273 = g_debug_surface; - let _e274 = ViewDepth_u0028_(); - frag_surface = vec4(_e273.x, _e273.y, _e273.z, _e274); + let _e274 = g_albedo; + param_1 = clamp(_e274, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e276 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e276.x, _e276.y, _e276.z, 1f); + let _e281 = g_debug_surface_on; + if _e281 { + let _e282 = g_debug_surface; + let _e283 = ViewDepth_u0028_(); + frag_surface = vec4(_e282.x, _e282.y, _e282.z, _e283); return; } - let _e279 = v_normal_1; - geometric = normalize(_e279); - let _e281 = gl_FrontFacing_1; - if !(_e281) { - let _e283 = geometric; - geometric = -(_e283); - } - let _e285 = geometric; - param_2 = _e285; - let _e286 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e287 = (*roughness); - let _e289 = ViewDepth_u0028_(); - frag_surface = vec4(_e286.x, _e286.y, clamp(_e287, 0f, 1f), _e289); + let _e288 = v_normal_1; + geometric = normalize(_e288); + let _e290 = gl_FrontFacing_1; + if !(_e290) { + let _e292 = geometric; + geometric = -(_e292); + } + let _e294 = geometric; + param_2 = _e294; + let _e295 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e296 = (*roughness); + let _e298 = ViewDepth_u0028_(); + frag_surface = vec4(_e295.x, _e295.y, clamp(_e296, 0f, 1f), _e298); return; } fn EyeDistance_u0028_() -> f32 { - let _e261 = v_world_position_1; - let _e263 = fog_info.eye; - return distance(_e261, _e263.xyz); + let _e270 = v_world_position_1; + let _e272 = fog_info.eye; + return distance(_e270, _e272.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -16504,41 +17118,41 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var k: f32; var local_1: f32; - let _e269 = fog_info.fog[3u]; - density = _e269; - let _e270 = density; - if (_e270 <= 0f) { - let _e272 = (*color); - return _e272; - } - let _e273 = EyeDistance_u0028_(); - d = _e273; - let _e276 = fog_info.eye[3u]; - falloff = _e276; - let _e277 = falloff; - if (_e277 > 0f) { - let _e279 = falloff; - let _e281 = v_world_position_1[1u]; - let _e284 = fog_info.eye[1u]; - k = (_e279 * (_e281 - _e284)); - let _e287 = k; - if (abs(_e287) > 0.001f) { - let _e290 = k; - let _e294 = k; - local_1 = ((1f - exp(-(_e290))) / _e294); + let _e278 = fog_info.fog[3u]; + density = _e278; + let _e279 = density; + if (_e279 <= 0f) { + let _e281 = (*color); + return _e281; + } + let _e282 = EyeDistance_u0028_(); + d = _e282; + let _e285 = fog_info.eye[3u]; + falloff = _e285; + let _e286 = falloff; + if (_e286 > 0f) { + let _e288 = falloff; + let _e290 = v_world_position_1[1u]; + let _e293 = fog_info.eye[1u]; + k = (_e288 * (_e290 - _e293)); + let _e296 = k; + if (abs(_e296) > 0.001f) { + let _e299 = k; + let _e303 = k; + local_1 = ((1f - exp(-(_e299))) / _e303); } else { - let _e296 = k; - local_1 = (1f - (0.5f * _e296)); + let _e305 = k; + local_1 = (1f - (0.5f * _e305)); } - let _e299 = local_1; - let _e300 = density; - density = (_e300 * _e299); + let _e308 = local_1; + let _e309 = density; + density = (_e309 * _e308); } - let _e303 = fog_info.fog; - let _e305 = (*color); - let _e306 = density; - let _e308 = d; - return mix(_e303.xyz, _e305, vec3(clamp(exp((-(_e306) * _e308)), 0f, 1f))); + let _e312 = fog_info.fog; + let _e314 = (*color); + let _e315 = density; + let _e317 = d; + return mix(_e312.xyz, _e314, vec3(clamp(exp((-(_e315) * _e317)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -16546,541 +17160,697 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e267 = g_premultiply; - let _e268 = (*alpha_2); - weight_1 = select(1f, _e268, _e267); - let _e270 = (*linearColor); - param_3 = _e270; - let _e271 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e272 = weight_1; - let _e273 = (_e271 * _e272); - let _e274 = (*alpha_2); - frag_color = vec4(_e273.x, _e273.y, _e273.z, _e274); - let _e279 = (*roughness_1); - param_4 = _e279; + let _e276 = g_premultiply; + let _e277 = (*alpha_2); + weight_1 = select(1f, _e277, _e276); + let _e279 = (*linearColor); + param_3 = _e279; + let _e280 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e281 = weight_1; + let _e282 = (_e280 * _e281); + let _e283 = (*alpha_2); + frag_color = vec4(_e282.x, _e282.y, _e282.z, _e283); + let _e288 = (*roughness_1); + param_4 = _e288; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; } +fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { + let _e271 = (*srgb); + let _e274 = (*srgb); + let _e279 = (*srgb); + return mix((_e271 / vec3(12.92f)), pow(((_e274 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e279)); +} + +fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { + var phi_2955_: bool; + + let _e271 = (*c); + let _e272 = (*c); + let _e274 = any((_e271 != _e272)); + phi_2955_ = _e274; + if !(_e274) { + let _e276 = (*c); + phi_2955_ = any((abs(_e276) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + } + let _e281 = phi_2955_; + return _e281; +} + +fn MapUv_u0028_i1_u003b(slot: ptr) -> vec2 { + var uvw: vec3; + + let _e272 = v_texcoord_1; + uvw = vec3(_e272.x, _e272.y, 1f); + let _e276 = (*slot); + let _e280 = layer_info.uv_transform[(_e276 * 2i)]; + let _e282 = uvw; + let _e284 = (*slot); + let _e289 = layer_info.uv_transform[((_e284 * 2i) + 1i)]; + let _e291 = uvw; + return vec2(dot(_e280.xyz, _e282), dot(_e289.xyz, _e291)); +} + +fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b(s: ptr, lit: ptr>) -> bool { + var view: f32; + var code: i32; + var shown: vec3; + var param_5: vec3; + var param_6: vec3; + var param_7: i32; + var grey: f32; + var param_8: vec3; + var param_9: vec3; + var local_2: vec3; + var weight_2: f32; + var local_3: f32; + var param_10: vec3; + var param_11: f32; + + let _e288 = frag_info.debug_view[0u]; + view = _e288; + let _e289 = view; + if (_e289 < 0.5f) { + return false; + } + let _e292 = gl_FragCoord_1[0u]; + let _e295 = frag_info.debug_view[1u]; + let _e298 = frag_info.debug_view[2u]; + if (_e292 < (_e295 * _e298)) { + return false; + } + let _e301 = view; + code = i32((_e301 + 0.5f)); + shown = vec3(0f, 0f, 0f); + let _e304 = code; + if (_e304 == 1i) { + let _e307 = (*s).albedo; + param_5 = clamp(_e307, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e309 = LinearToSrgb_u0028_vf3_u003b((¶m_5)); + shown = _e309; + } else { + let _e310 = code; + if (_e310 == 2i) { + let _e313 = (*s).n; + shown = ((_e313 * 0.5f) + vec3(0.5f, 0.5f, 0.5f)); + } else { + let _e316 = code; + if (_e316 == 3i) { + let _e319 = (*s).roughness; + shown = vec3(clamp(_e319, 0f, 1f)); + } else { + let _e322 = code; + if (_e322 == 4i) { + let _e325 = (*s).metallic; + shown = vec3(clamp(_e325, 0f, 1f)); + } else { + let _e328 = code; + if (_e328 == 5i) { + let _e331 = (*s).occlusion; + shown = vec3(clamp(_e331, 0f, 1f)); + } else { + let _e334 = code; + if (_e334 == 6i) { + let _e337 = (*s).emissive; + param_6 = clamp(_e337, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e339 = LinearToSrgb_u0028_vf3_u003b((¶m_6)); + shown = _e339; + } else { + let _e340 = code; + if (_e340 == 7i) { + param_7 = 0i; + let _e342 = MapUv_u0028_i1_u003b((¶m_7)); + let _e343 = fract(_e342); + shown = vec3(_e343.x, _e343.y, 0f); + } else { + let _e347 = code; + if (_e347 == 8i) { + let _e349 = (*lit); + param_8 = clamp(_e349, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e351 = LinearToSrgb_u0028_vf3_u003b((¶m_8)); + grey = dot(_e351, vec3(0.2126f, 0.7152f, 0.0722f)); + let _e353 = (*lit); + param_9 = _e353; + let _e354 = NonFinite_u0028_vf3_u003b((¶m_9)); + if _e354 { + local_2 = vec3(1f, 0f, 1f); + } else { + let _e355 = grey; + local_2 = vec3((_e355 * 0.5f)); + } + let _e358 = local_2; + shown = _e358; + } + } + } + } + } + } + } + } + let _e359 = g_premultiply; + if _e359 { + let _e361 = (*s).alpha; + local_3 = _e361; + } else { + local_3 = 1f; + } + let _e362 = local_3; + weight_2 = _e362; + let _e363 = shown; + param_10 = _e363; + let _e364 = SrgbToLinear_u0028_vf3_u003b((¶m_10)); + let _e365 = weight_2; + let _e366 = (_e364 * _e365); + let _e368 = (*s).alpha; + frag_color = vec4(_e366.x, _e366.y, _e366.z, _e368); + let _e374 = (*s).roughness; + param_11 = _e374; + WriteSurfaceGeometry_u0028_f1_u003b((¶m_11)); + WriteWeightedBlended_u0028_(); + return true; +} + fn V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b(n_dot_v: ptr, n_dot_l: ptr, alpha_3: ptr) -> f32 { var a2_: f32; var lambda_v: f32; var lambda_l: f32; - let _e267 = (*alpha_3); - let _e268 = (*alpha_3); - a2_ = (_e267 * _e268); - let _e270 = (*n_dot_l); - let _e271 = (*n_dot_v); - let _e272 = (*n_dot_v); - let _e274 = a2_; - let _e277 = a2_; - lambda_v = (_e270 * sqrt((((_e271 * _e272) * (1f - _e274)) + _e277))); + let _e276 = (*alpha_3); + let _e277 = (*alpha_3); + a2_ = (_e276 * _e277); + let _e279 = (*n_dot_l); + let _e280 = (*n_dot_v); let _e281 = (*n_dot_v); - let _e282 = (*n_dot_l); - let _e283 = (*n_dot_l); - let _e285 = a2_; - let _e288 = a2_; - lambda_l = (_e281 * sqrt((((_e282 * _e283) * (1f - _e285)) + _e288))); - let _e292 = lambda_v; - let _e293 = lambda_l; - return (0.5f / max((_e292 + _e293), 0.00001f)); + let _e283 = a2_; + let _e286 = a2_; + lambda_v = (_e279 * sqrt((((_e280 * _e281) * (1f - _e283)) + _e286))); + let _e290 = (*n_dot_v); + let _e291 = (*n_dot_l); + let _e292 = (*n_dot_l); + let _e294 = a2_; + let _e297 = a2_; + lambda_l = (_e290 * sqrt((((_e291 * _e292) * (1f - _e294)) + _e297))); + let _e301 = lambda_v; + let _e302 = lambda_l; + return (0.5f / max((_e301 + _e302), 0.00001f)); } fn D_GGX_u0028_f1_u003b_f1_u003b(n_dot_h: ptr, alpha_4: ptr) -> f32 { var a: f32; var k_1: f32; - let _e265 = (*n_dot_h); - let _e266 = (*alpha_4); - a = (_e265 * _e266); - let _e268 = (*alpha_4); - let _e269 = (*n_dot_h); - let _e270 = (*n_dot_h); - let _e273 = a; - let _e274 = a; - k_1 = (_e268 / max(((1f - (_e269 * _e270)) + (_e273 * _e274)), 0.0000001f)); - let _e279 = k_1; - let _e280 = k_1; - return ((_e279 * _e280) * 0.31830987f); + let _e274 = (*n_dot_h); + let _e275 = (*alpha_4); + a = (_e274 * _e275); + let _e277 = (*alpha_4); + let _e278 = (*n_dot_h); + let _e279 = (*n_dot_h); + let _e282 = a; + let _e283 = a; + k_1 = (_e277 / max(((1f - (_e278 * _e279)) + (_e282 * _e283)), 0.0000001f)); + let _e288 = k_1; + let _e289 = k_1; + return ((_e288 * _e289) * 0.31830987f); } fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>) -> vec3 { var axis: vec3; var len: f32; var angle: f32; - var local_2: vec3; + var local_4: vec3; - let _e267 = (*a_1); - let _e268 = (*b); - axis = cross(_e267, _e268); - let _e270 = axis; - len = length(_e270); - let _e272 = (*a_1); - let _e273 = (*b); - angle = acos(clamp(dot(_e272, _e273), -1f, 1f)); - let _e277 = len; - if (_e277 > 0.000001f) { - let _e279 = axis; - let _e280 = angle; - let _e281 = len; - local_2 = (_e279 * (_e280 / (_e281 * 6.2831855f))); + let _e276 = (*a_1); + let _e277 = (*b); + axis = cross(_e276, _e277); + let _e279 = axis; + len = length(_e279); + let _e281 = (*a_1); + let _e282 = (*b); + angle = acos(clamp(dot(_e281, _e282), -1f, 1f)); + let _e286 = len; + if (_e286 > 0.000001f) { + let _e288 = axis; + let _e289 = angle; + let _e290 = len; + local_4 = (_e288 * (_e289 / (_e290 * 6.2831855f))); } else { - local_2 = vec3(0f, 0f, 0f); + local_4 = vec3(0f, 0f, 0f); } - let _e285 = local_2; - return _e285; + let _e294 = local_4; + return _e294; } fn LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b(x: ptr, y: ptr, table: ptr) -> vec2 { var inTable: vec2; - let _e265 = (*x); - let _e266 = (*y); - inTable = ((vec2(_e265, _e266) * 0.984375f) + vec2(0.0078125f)); - let _e272 = inTable[0u]; - let _e274 = inTable[1u]; - let _e275 = (*table); - return vec2(_e272, ((_e274 + _e275) * 0.5f)); + let _e274 = (*x); + let _e275 = (*y); + inTable = ((vec2(_e274, _e275) * 0.984375f) + vec2(0.0078125f)); + let _e281 = inTable[0u]; + let _e283 = inTable[1u]; + let _e284 = (*table); + return vec2(_e281, ((_e283 + _e284) * 0.5f)); } fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: ptr>, v: ptr>, roughness_2: ptr, corners: ptr, 4>>) -> vec3 { var uv: vec2; var inverse: vec4; - var param_5: f32; - var param_6: f32; - var param_7: f32; + var param_12: f32; + var param_13: f32; + var param_14: f32; var fit: vec4; - var param_8: f32; - var param_9: f32; - var param_10: f32; + var param_15: f32; + var param_16: f32; + var param_17: f32; var along: vec3; var alongLength: f32; var t1_: vec3; - var local_3: vec3; + var local_5: vec3; var t2_: vec3; var minv: mat3x3; var i: i32; var p: vec3; var l: array, 4>; var f: vec3; - var param_11: vec3; - var param_12: vec3; - var param_13: vec3; - var param_14: vec3; - var param_15: vec3; - var param_16: vec3; - var param_17: vec3; var param_18: vec3; + var param_19: vec3; + var param_20: vec3; + var param_21: vec3; + var param_22: vec3; + var param_23: vec3; + var param_24: vec3; + var param_25: vec3; var len_1: f32; var z_1: f32; - var local_4: f32; + var local_6: f32; var sphere: f32; - var param_19: f32; - var param_20: f32; - var param_21: f32; + var param_26: f32; + var param_27: f32; + var param_28: f32; - let _e299 = (*roughness_2); - let _e301 = (*n_1); - let _e302 = (*v); - uv = vec2(clamp(_e299, 0f, 1f), sqrt(clamp((1f - dot(_e301, _e302)), 0f, 1f))); - let _e309 = uv[0u]; - param_5 = _e309; - let _e311 = uv[1u]; - param_6 = _e311; - param_7 = 0f; - let _e312 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_5), (¶m_6), (¶m_7)); - let _e313 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e312, 0f); - inverse = _e313; - let _e315 = uv[0u]; - param_8 = _e315; - let _e317 = uv[1u]; - param_9 = _e317; - param_10 = 1f; - let _e318 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_8), (¶m_9), (¶m_10)); - let _e319 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e318, 0f); - fit = _e319; - let _e320 = (*v); - let _e321 = (*n_1); - let _e322 = (*v); - let _e323 = (*n_1); - along = (_e320 - (_e321 * dot(_e322, _e323))); - let _e327 = along; - alongLength = length(_e327); - let _e329 = alongLength; - if (_e329 > 0.00001f) { - let _e331 = along; - let _e332 = alongLength; - local_3 = (_e331 / vec3(_e332)); + let _e308 = (*roughness_2); + let _e310 = (*n_1); + let _e311 = (*v); + uv = vec2(clamp(_e308, 0f, 1f), sqrt(clamp((1f - dot(_e310, _e311)), 0f, 1f))); + let _e318 = uv[0u]; + param_12 = _e318; + let _e320 = uv[1u]; + param_13 = _e320; + param_14 = 0f; + let _e321 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_12), (¶m_13), (¶m_14)); + let _e322 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e321, 0f); + inverse = _e322; + let _e324 = uv[0u]; + param_15 = _e324; + let _e326 = uv[1u]; + param_16 = _e326; + param_17 = 1f; + let _e327 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_15), (¶m_16), (¶m_17)); + let _e328 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e327, 0f); + fit = _e328; + let _e329 = (*v); + let _e330 = (*n_1); + let _e331 = (*v); + let _e332 = (*n_1); + along = (_e329 - (_e330 * dot(_e331, _e332))); + let _e336 = along; + alongLength = length(_e336); + let _e338 = alongLength; + if (_e338 > 0.00001f) { + let _e340 = along; + let _e341 = alongLength; + local_5 = (_e340 / vec3(_e341)); } else { - let _e335 = (*n_1); - let _e337 = (*n_1)[2u]; - local_3 = normalize(cross(_e335, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e337) < 0.999f))))); - } - let _e344 = local_3; - t1_ = _e344; - let _e345 = (*n_1); - let _e346 = t1_; - t2_ = cross(_e345, _e346); - let _e349 = inverse[0u]; - let _e351 = inverse[1u]; - let _e352 = vec3(_e349, 0f, _e351); - let _e354 = inverse[2u]; - let _e356 = inverse[3u]; - let _e357 = vec3(_e354, 0f, _e356); - minv = mat3x3(vec3(_e352.x, _e352.y, _e352.z), vec3(vec3(0f, 1f, 0f).x, vec3(0f, 1f, 0f).y, vec3(0f, 1f, 0f).z), vec3(_e357.x, _e357.y, _e357.z)); + let _e344 = (*n_1); + let _e346 = (*n_1)[2u]; + local_5 = normalize(cross(_e344, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e346) < 0.999f))))); + } + let _e353 = local_5; + t1_ = _e353; + let _e354 = (*n_1); + let _e355 = t1_; + t2_ = cross(_e354, _e355); + let _e358 = inverse[0u]; + let _e360 = inverse[1u]; + let _e361 = vec3(_e358, 0f, _e360); + let _e363 = inverse[2u]; + let _e365 = inverse[3u]; + let _e366 = vec3(_e363, 0f, _e365); + minv = mat3x3(vec3(_e361.x, _e361.y, _e361.z), vec3(vec3(0f, 1f, 0f).x, vec3(0f, 1f, 0f).y, vec3(0f, 1f, 0f).z), vec3(_e366.x, _e366.y, _e366.z)); i = 0i; loop { - let _e371 = i; - if (_e371 < 4i) { - let _e373 = i; - let _e375 = (*corners)[_e373]; - p = _e375; - let _e376 = i; - let _e377 = minv; - let _e378 = p; - let _e379 = t1_; - let _e381 = p; - let _e382 = t2_; - let _e384 = p; - let _e385 = (*n_1); - l[_e376] = normalize((_e377 * vec3(dot(_e378, _e379), dot(_e381, _e382), dot(_e384, _e385)))); + let _e380 = i; + if (_e380 < 4i) { + let _e382 = i; + let _e384 = (*corners)[_e382]; + p = _e384; + let _e385 = i; + let _e386 = minv; + let _e387 = p; + let _e388 = t1_; + let _e390 = p; + let _e391 = t2_; + let _e393 = p; + let _e394 = (*n_1); + l[_e385] = normalize((_e386 * vec3(dot(_e387, _e388), dot(_e390, _e391), dot(_e393, _e394)))); continue; } else { break; } continuing { - let _e391 = i; - i = (_e391 + 1i); - } - } - let _e394 = l[0i]; - param_11 = _e394; - let _e396 = l[1i]; - param_12 = _e396; - let _e397 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_11), (¶m_12)); - let _e399 = l[1i]; - param_13 = _e399; - let _e401 = l[2i]; - param_14 = _e401; - let _e402 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_13), (¶m_14)); - let _e405 = l[2i]; - param_15 = _e405; - let _e407 = l[3i]; - param_16 = _e407; - let _e408 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_15), (¶m_16)); - let _e411 = l[3i]; - param_17 = _e411; - let _e413 = l[0i]; - param_18 = _e413; - let _e414 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_17), (¶m_18)); - f = -((((_e397 + _e402) + _e408) + _e414)); - let _e417 = f; - len_1 = length(_e417); - let _e419 = len_1; - if (_e419 > 0.000000001f) { - let _e422 = f[2u]; - let _e423 = len_1; - local_4 = (_e422 / _e423); + let _e400 = i; + i = (_e400 + 1i); + } + } + let _e403 = l[0i]; + param_18 = _e403; + let _e405 = l[1i]; + param_19 = _e405; + let _e406 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_18), (¶m_19)); + let _e408 = l[1i]; + param_20 = _e408; + let _e410 = l[2i]; + param_21 = _e410; + let _e411 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_20), (¶m_21)); + let _e414 = l[2i]; + param_22 = _e414; + let _e416 = l[3i]; + param_23 = _e416; + let _e417 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_22), (¶m_23)); + let _e420 = l[3i]; + param_24 = _e420; + let _e422 = l[0i]; + param_25 = _e422; + let _e423 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_24), (¶m_25)); + f = -((((_e406 + _e411) + _e417) + _e423)); + let _e426 = f; + len_1 = length(_e426); + let _e428 = len_1; + if (_e428 > 0.000000001f) { + let _e431 = f[2u]; + let _e432 = len_1; + local_6 = (_e431 / _e432); } else { - local_4 = 0f; - } - let _e425 = local_4; - z_1 = _e425; - let _e426 = z_1; - let _e429 = len_1; - param_19 = ((_e426 * 0.5f) + 0.5f); - param_20 = clamp(_e429, 0f, 1f); - param_21 = 1f; - let _e431 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_19), (¶m_20), (¶m_21)); - let _e432 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e431, 0f); - sphere = _e432.w; - let _e434 = len_1; - let _e435 = sphere; - let _e439 = fit[0u]; - let _e441 = fit[1u]; - return vec3(max((_e434 * _e435), 0f), _e439, _e441); -} - -fn CoatLobe_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s: ptr, light: ptr) -> f32 { + local_6 = 0f; + } + let _e434 = local_6; + z_1 = _e434; + let _e435 = z_1; + let _e438 = len_1; + param_26 = ((_e435 * 0.5f) + 0.5f); + param_27 = clamp(_e438, 0f, 1f); + param_28 = 1f; + let _e440 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_26), (¶m_27), (¶m_28)); + let _e441 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e440, 0f); + sphere = _e441.w; + let _e443 = len_1; + let _e444 = sphere; + let _e448 = fit[0u]; + let _e450 = fit[1u]; + return vec3(max((_e443 * _e444), 0f), _e448, _e450); +} + +fn CoatLobe_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_1: ptr, light: ptr) -> f32 { var ltc: vec3; - var param_22: vec3; - var param_23: vec3; - var param_24: f32; - var param_25: array, 4>; + var param_29: vec3; + var param_30: vec3; + var param_31: f32; + var param_32: array, 4>; var lobe: f32; var alpha_5: f32; var n_dot_l_1: f32; var n_dot_h_1: f32; var d_1: f32; - var param_26: f32; - var param_27: f32; + var param_33: f32; + var param_34: f32; var vis: f32; - var param_28: f32; - var param_29: f32; - var param_30: f32; + var param_35: f32; + var param_36: f32; + var param_37: f32; var f_1: f32; - let _e281 = (*light).integrated; - if (_e281 > 0.5f) { - let _e283 = g_coat_n; - param_22 = _e283; - let _e285 = (*s).v; - param_23 = _e285; - let _e286 = g_coat_roughness; - param_24 = _e286; - let _e287 = g_rect_corners; - param_25 = _e287; - let _e288 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_22), (¶m_23), (¶m_24), (¶m_25)); - ltc = _e288; - let _e290 = ltc[0u]; - let _e292 = ltc[1u]; - let _e295 = ltc[2u]; - let _e301 = frag_info.material[3u]; - let _e304 = (*light).n_dot_l; - return (((_e290 * ((0.04f * _e292) + (0.96f * _e295))) * _e301) / max(_e304, 0.000001f)); - } - let _e307 = g_coat_roughness; - lobe = max(_e307, 0.045f); - let _e309 = lobe; - let _e310 = lobe; - alpha_5 = (_e309 * _e310); - let _e312 = g_coat_n; - let _e314 = (*light).l; - n_dot_l_1 = max(dot(_e312, _e314), 0f); - let _e317 = g_coat_n; - let _e319 = (*light).h; - n_dot_h_1 = max(dot(_e317, _e319), 0f); - let _e322 = n_dot_h_1; - param_26 = _e322; - let _e323 = alpha_5; - param_27 = _e323; - let _e324 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_26), (¶m_27)); - d_1 = _e324; - let _e325 = g_coat_n_dot_v; - param_28 = _e325; - let _e326 = n_dot_l_1; - param_29 = _e326; - let _e327 = alpha_5; - param_30 = _e327; - let _e328 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_28), (¶m_29), (¶m_30)); - vis = _e328; - let _e330 = (*light).v_dot_h; - f_1 = (0.04f + (0.96f * pow((1f - _e330), 5f))); - let _e335 = d_1; - let _e336 = vis; - let _e338 = f_1; - let _e342 = frag_info.material[3u]; - let _e344 = n_dot_l_1; - let _e347 = (*light).n_dot_l; - return (((((_e335 * _e336) * _e338) * _e342) * _e344) / max(_e347, 0.000001f)); + let _e290 = (*light).integrated; + if (_e290 > 0.5f) { + let _e292 = g_coat_n; + param_29 = _e292; + let _e294 = (*s_1).v; + param_30 = _e294; + let _e295 = g_coat_roughness; + param_31 = _e295; + let _e296 = g_rect_corners; + param_32 = _e296; + let _e297 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_29), (¶m_30), (¶m_31), (¶m_32)); + ltc = _e297; + let _e299 = ltc[0u]; + let _e301 = ltc[1u]; + let _e304 = ltc[2u]; + let _e310 = frag_info.material[3u]; + let _e313 = (*light).n_dot_l; + return (((_e299 * ((0.04f * _e301) + (0.96f * _e304))) * _e310) / max(_e313, 0.000001f)); + } + let _e316 = g_coat_roughness; + lobe = max(_e316, 0.045f); + let _e318 = lobe; + let _e319 = lobe; + alpha_5 = (_e318 * _e319); + let _e321 = g_coat_n; + let _e323 = (*light).l; + n_dot_l_1 = max(dot(_e321, _e323), 0f); + let _e326 = g_coat_n; + let _e328 = (*light).h; + n_dot_h_1 = max(dot(_e326, _e328), 0f); + let _e331 = n_dot_h_1; + param_33 = _e331; + let _e332 = alpha_5; + param_34 = _e332; + let _e333 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_33), (¶m_34)); + d_1 = _e333; + let _e334 = g_coat_n_dot_v; + param_35 = _e334; + let _e335 = n_dot_l_1; + param_36 = _e335; + let _e336 = alpha_5; + param_37 = _e336; + let _e337 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_35), (¶m_36), (¶m_37)); + vis = _e337; + let _e339 = (*light).v_dot_h; + f_1 = (0.04f + (0.96f * pow((1f - _e339), 5f))); + let _e344 = d_1; + let _e345 = vis; + let _e347 = f_1; + let _e351 = frag_info.material[3u]; + let _e353 = n_dot_l_1; + let _e356 = (*light).n_dot_l; + return (((((_e344 * _e345) * _e347) * _e351) * _e353) / max(_e356, 0.000001f)); } fn V_Neubelt_u0028_f1_u003b_f1_u003b(n_dot_v_1: ptr, n_dot_l_2: ptr) -> f32 { - let _e263 = (*n_dot_l_2); - let _e264 = (*n_dot_v_1); - let _e266 = (*n_dot_l_2); - let _e267 = (*n_dot_v_1); - return (1f / (4f * ((_e263 + _e264) - (_e266 * _e267)))); + let _e272 = (*n_dot_l_2); + let _e273 = (*n_dot_v_1); + let _e275 = (*n_dot_l_2); + let _e276 = (*n_dot_v_1); + return (1f / (4f * ((_e272 + _e273) - (_e275 * _e276)))); } fn D_Charlie_u0028_f1_u003b_f1_u003b(roughness_3: ptr, n_dot_h_2: ptr) -> f32 { var inv_alpha: f32; var sin2h: f32; - let _e265 = (*roughness_3); - let _e266 = (*roughness_3); - inv_alpha = (1f / (_e265 * _e266)); - let _e269 = (*n_dot_h_2); - let _e270 = (*n_dot_h_2); - sin2h = max((1f - (_e269 * _e270)), 0.0078125f); - let _e274 = inv_alpha; - let _e276 = sin2h; - let _e277 = inv_alpha; - return (((2f + _e274) * pow(_e276, (_e277 * 0.5f))) / 6.2831855f); + let _e274 = (*roughness_3); + let _e275 = (*roughness_3); + inv_alpha = (1f / (_e274 * _e275)); + let _e278 = (*n_dot_h_2); + let _e279 = (*n_dot_h_2); + sin2h = max((1f - (_e278 * _e279)), 0.0078125f); + let _e283 = inv_alpha; + let _e285 = sin2h; + let _e286 = inv_alpha; + return (((2f + _e283) * pow(_e285, (_e286 * 0.5f))) / 6.2831855f); } fn FonAlbedo_u0028_f1_u003b_f1_u003b(mu: ptr, r: ptr) -> f32 { var m: f32; var g: f32; - let _e265 = (*mu); - m = (1f - _e265); - let _e267 = m; - let _e268 = m; - let _e269 = m; - let _e270 = m; - g = (_e267 * (0.05710853f + (_e268 * (0.49188188f + (_e269 * (-0.33218145f + (_e270 * 0.071443f))))))); - let _e278 = (*r); - let _e279 = g; - let _e282 = (*r); - return ((1f + (_e278 * _e279)) / (1f + (0.2877934f * _e282))); + let _e274 = (*mu); + m = (1f - _e274); + let _e276 = m; + let _e277 = m; + let _e278 = m; + let _e279 = m; + g = (_e276 * (0.05710853f + (_e277 * (0.49188188f + (_e278 * (-0.33218145f + (_e279 * 0.071443f))))))); + let _e287 = (*r); + let _e288 = g; + let _e291 = (*r); + return ((1f + (_e287 * _e288)) / (1f + (0.2877934f * _e291))); } fn EonMultiAlbedo_u0028_vf3_u003b_f1_u003b(rho: ptr>, average: ptr) -> vec3 { - let _e263 = (*rho); - let _e264 = (*rho); - let _e266 = (*average); - let _e268 = (*rho); - let _e269 = (*average); - return (((_e263 * _e264) * _e266) / (vec3(1f, 1f, 1f) - (_e268 * (1f - _e269)))); + let _e272 = (*rho); + let _e273 = (*rho); + let _e275 = (*average); + let _e277 = (*rho); + let _e278 = (*average); + return (((_e272 * _e273) * _e275) / (vec3(1f, 1f, 1f) - (_e277 * (1f - _e278)))); } fn FonAverage_u0028_f1_u003b(r_1: ptr) -> f32 { - let _e262 = (*r_1); - let _e265 = (*r_1); - return ((1f + (0.07248821f * _e262)) / (1f + (0.2877934f * _e265))); + let _e271 = (*r_1); + let _e274 = (*r_1); + return ((1f + (0.07248821f * _e271)) / (1f + (0.2877934f * _e274))); } fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, r_2: ptr, mu_i: ptr, mu_o: ptr, l_dot_v: ptr) -> vec3 { - var s_1: f32; + var s_2: f32; var s_over_t: f32; - var local_5: f32; + var local_7: f32; var af: f32; var single: vec3; var average_1: f32; - var param_31: f32; + var param_38: f32; var multi: vec3; - var param_32: vec3; - var param_33: f32; - var param_34: f32; - var param_35: f32; - var param_36: f32; - var param_37: f32; + var param_39: vec3; + var param_40: f32; + var param_41: f32; + var param_42: f32; + var param_43: f32; + var param_44: f32; - let _e280 = (*l_dot_v); - let _e281 = (*mu_i); - let _e282 = (*mu_o); - s_1 = (_e280 - (_e281 * _e282)); - let _e285 = s_1; - if (_e285 > 0f) { - let _e287 = s_1; - let _e288 = (*mu_i); - let _e289 = (*mu_o); - local_5 = (_e287 / max(_e288, _e289)); + let _e289 = (*l_dot_v); + let _e290 = (*mu_i); + let _e291 = (*mu_o); + s_2 = (_e289 - (_e290 * _e291)); + let _e294 = s_2; + if (_e294 > 0f) { + let _e296 = s_2; + let _e297 = (*mu_i); + let _e298 = (*mu_o); + local_7 = (_e296 / max(_e297, _e298)); } else { - let _e292 = s_1; - local_5 = _e292; - } - let _e293 = local_5; - s_over_t = _e293; - let _e294 = (*r_2); - af = (1f / (1f + (0.2877934f * _e294))); - let _e298 = (*rho_1); - let _e299 = af; - let _e300 = (*r_2); - let _e301 = s_over_t; - single = (_e298 * (_e299 * (1f + (_e300 * _e301)))); - let _e306 = (*r_2); - param_31 = _e306; - let _e307 = FonAverage_u0028_f1_u003b((¶m_31)); - average_1 = _e307; - let _e308 = (*rho_1); - param_32 = _e308; - let _e309 = average_1; - param_33 = _e309; - let _e310 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_32), (¶m_33)); - let _e311 = (*mu_o); - param_34 = _e311; - let _e312 = (*r_2); - param_35 = _e312; - let _e313 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_34), (¶m_35)); - let _e316 = (*mu_i); - param_36 = _e316; - let _e317 = (*r_2); - param_37 = _e317; - let _e318 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_36), (¶m_37)); - let _e322 = average_1; - multi = (_e310 * ((max((1f - _e313), 0.0000001f) * max((1f - _e318), 0.0000001f)) / max((1f - _e322), 0.0000001f))); - let _e327 = single; - let _e328 = multi; - return ((_e327 + _e328) / vec3(3.1415927f)); + let _e301 = s_2; + local_7 = _e301; + } + let _e302 = local_7; + s_over_t = _e302; + let _e303 = (*r_2); + af = (1f / (1f + (0.2877934f * _e303))); + let _e307 = (*rho_1); + let _e308 = af; + let _e309 = (*r_2); + let _e310 = s_over_t; + single = (_e307 * (_e308 * (1f + (_e309 * _e310)))); + let _e315 = (*r_2); + param_38 = _e315; + let _e316 = FonAverage_u0028_f1_u003b((¶m_38)); + average_1 = _e316; + let _e317 = (*rho_1); + param_39 = _e317; + let _e318 = average_1; + param_40 = _e318; + let _e319 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_39), (¶m_40)); + let _e320 = (*mu_o); + param_41 = _e320; + let _e321 = (*r_2); + param_42 = _e321; + let _e322 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_41), (¶m_42)); + let _e325 = (*mu_i); + param_43 = _e325; + let _e326 = (*r_2); + param_44 = _e326; + let _e327 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_43), (¶m_44)); + let _e331 = average_1; + multi = (_e319 * ((max((1f - _e322), 0.0000001f) * max((1f - _e327), 0.0000001f)) / max((1f - _e331), 0.0000001f))); + let _e336 = single; + let _e337 = multi; + return ((_e336 + _e337) / vec3(3.1415927f)); } fn EonDiffuse_u0028_() -> bool { - let _e263 = frag_info.ambient_sky[3u]; - return (_e263 > 0.5f); + let _e272 = frag_info.ambient_sky[3u]; + return (_e272 > 0.5f); } fn EnvBrdf_u0028_f1_u003b_f1_u003b(roughness_4: ptr, n_dot_v_2: ptr) -> vec2 { var dfg: vec2; - var param_38: f32; - var param_39: f32; - var param_40: f32; - - let _e267 = (*roughness_4); - let _e269 = (*n_dot_v_2); - param_38 = clamp(_e267, 0f, 1f); - param_39 = sqrt(clamp((1f - _e269), 0f, 1f)); - param_40 = 1f; - let _e273 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_38), (¶m_39), (¶m_40)); - let _e274 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e273, 0f); - dfg = _e274.xy; - let _e277 = dfg[0u]; - let _e279 = dfg[1u]; - let _e282 = dfg[1u]; - return vec2((_e277 - _e279), _e282); -} + var param_45: f32; + var param_46: f32; + var param_47: f32; -fn MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(f0_: ptr>, s_2: ptr) -> vec3 { + let _e276 = (*roughness_4); + let _e278 = (*n_dot_v_2); + param_45 = clamp(_e276, 0f, 1f); + param_46 = sqrt(clamp((1f - _e278), 0f, 1f)); + param_47 = 1f; + let _e282 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_45), (¶m_46), (¶m_47)); + let _e283 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e282, 0f); + dfg = _e283.xy; + let _e286 = dfg[0u]; + let _e288 = dfg[1u]; + let _e291 = dfg[1u]; + return vec2((_e286 - _e288), _e291); +} + +fn MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(f0_: ptr>, s_3: ptr) -> vec3 { var ab: vec2; - var param_41: f32; - var param_42: f32; + var param_48: f32; + var param_49: f32; var ess: f32; - let _e268 = (*s_2).roughness; - param_41 = max(_e268, 0.045f); - let _e271 = (*s_2).n_dot_v; - param_42 = _e271; - let _e272 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_41), (¶m_42)); - ab = _e272; - let _e274 = ab[0u]; - let _e276 = ab[1u]; - ess = max((_e274 + _e276), 0.0001f); - let _e279 = (*f0_); - let _e280 = ess; - return (vec3(1f, 1f, 1f) + (_e279 * ((1f / _e280) - 1f))); + let _e277 = (*s_3).roughness; + param_48 = max(_e277, 0.045f); + let _e280 = (*s_3).n_dot_v; + param_49 = _e280; + let _e281 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_48), (¶m_49)); + ab = _e281; + let _e283 = ab[0u]; + let _e285 = ab[1u]; + ess = max((_e283 + _e285), 0.0001f); + let _e288 = (*f0_); + let _e289 = ess; + return (vec3(1f, 1f, 1f) + (_e288 * ((1f / _e289) - 1f))); } fn EnergyCompensation_u0028_() -> bool { - let _e263 = frag_info.target_origin[2u]; - return (_e263 > 0.5f); + let _e272 = frag_info.target_origin[2u]; + return (_e272 > 0.5f); } fn F_SchlickF90_u0028_vf3_u003b_vf3_u003b_f1_u003b(f0_1: ptr>, f90_: ptr>, v_dot_h: ptr) -> vec3 { var f_2: f32; - let _e265 = (*v_dot_h); - f_2 = pow((1f - _e265), 5f); - let _e268 = (*f0_1); - let _e269 = (*f90_); - let _e270 = (*f0_1); - let _e272 = f_2; - return (_e268 + ((_e269 - _e270) * _e272)); + let _e274 = (*v_dot_h); + f_2 = pow((1f - _e274), 5f); + let _e277 = (*f0_1); + let _e278 = (*f90_); + let _e279 = (*f0_1); + let _e281 = f_2; + return (_e277 + ((_e278 - _e279) * _e281)); } fn V_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(n_dot_l_3: ptr, n_dot_v_3: ptr, b_dot_v: ptr, t_dot_v: ptr, t_dot_l: ptr, b_dot_l: ptr, at: ptr, ab_1: ptr) -> f32 { var ggx_v: f32; var ggx_l: f32; - let _e271 = (*n_dot_l_3); - let _e272 = (*at); - let _e273 = (*t_dot_v); - let _e275 = (*ab_1); - let _e276 = (*b_dot_v); - let _e278 = (*n_dot_v_3); - ggx_v = (_e271 * length(vec3((_e272 * _e273), (_e275 * _e276), _e278))); - let _e282 = (*n_dot_v_3); - let _e283 = (*at); - let _e284 = (*t_dot_l); - let _e286 = (*ab_1); - let _e287 = (*b_dot_l); - let _e289 = (*n_dot_l_3); - ggx_l = (_e282 * length(vec3((_e283 * _e284), (_e286 * _e287), _e289))); - let _e293 = ggx_v; - let _e294 = ggx_l; - return clamp((0.5f / max((_e293 + _e294), 0.00001f)), 0f, 1f); + let _e280 = (*n_dot_l_3); + let _e281 = (*at); + let _e282 = (*t_dot_v); + let _e284 = (*ab_1); + let _e285 = (*b_dot_v); + let _e287 = (*n_dot_v_3); + ggx_v = (_e280 * length(vec3((_e281 * _e282), (_e284 * _e285), _e287))); + let _e291 = (*n_dot_v_3); + let _e292 = (*at); + let _e293 = (*t_dot_l); + let _e295 = (*ab_1); + let _e296 = (*b_dot_l); + let _e298 = (*n_dot_l_3); + ggx_l = (_e291 * length(vec3((_e292 * _e293), (_e295 * _e296), _e298))); + let _e302 = ggx_v; + let _e303 = ggx_l; + return clamp((0.5f / max((_e302 + _e303), 0.00001f)), 0f, 1f); } fn D_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(n_dot_h_3: ptr, t_dot_h: ptr, b_dot_h: ptr, at_1: ptr, ab_2: ptr) -> f32 { @@ -17088,266 +17858,266 @@ fn D_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(n_dot_h_3 var f_3: vec3; var w2_: f32; - let _e269 = (*at_1); - let _e270 = (*ab_2); - a2_1 = (_e269 * _e270); - let _e272 = (*ab_2); - let _e273 = (*t_dot_h); - let _e275 = (*at_1); - let _e276 = (*b_dot_h); - let _e278 = a2_1; - let _e279 = (*n_dot_h_3); - f_3 = vec3((_e272 * _e273), (_e275 * _e276), (_e278 * _e279)); - let _e282 = a2_1; - let _e283 = f_3; - let _e284 = f_3; - w2_ = (_e282 / max(dot(_e283, _e284), 0.000000000001f)); - let _e288 = a2_1; - let _e289 = w2_; - let _e291 = w2_; - return (((_e288 * _e289) * _e291) / 3.1415927f); -} - -fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_3: ptr, light_1: ptr) -> vec3 { + let _e278 = (*at_1); + let _e279 = (*ab_2); + a2_1 = (_e278 * _e279); + let _e281 = (*ab_2); + let _e282 = (*t_dot_h); + let _e284 = (*at_1); + let _e285 = (*b_dot_h); + let _e287 = a2_1; + let _e288 = (*n_dot_h_3); + f_3 = vec3((_e281 * _e282), (_e284 * _e285), (_e287 * _e288)); + let _e291 = a2_1; + let _e292 = f_3; + let _e293 = f_3; + w2_ = (_e291 / max(dot(_e292, _e293), 0.000000000001f)); + let _e297 = a2_1; + let _e298 = w2_; + let _e300 = w2_; + return (((_e297 * _e298) * _e300) / 3.1415927f); +} + +fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_4: ptr, light_1: ptr) -> vec3 { var lobe_1: f32; var alpha_6: f32; var f0_2: vec3; var f90_1: vec3; var diffuseColor: vec3; var d_2: f32; - var param_43: f32; - var param_44: f32; - var vis_1: f32; - var param_45: f32; - var param_46: f32; - var param_47: f32; - var at_2: f32; - var ab_3: f32; - var param_48: f32; - var param_49: f32; var param_50: f32; var param_51: f32; + var vis_1: f32; var param_52: f32; var param_53: f32; var param_54: f32; + var at_2: f32; + var ab_3: f32; var param_55: f32; var param_56: f32; var param_57: f32; var param_58: f32; var param_59: f32; var param_60: f32; - var f_4: vec3; - var param_61: vec3; - var param_62: vec3; + var param_61: f32; + var param_62: f32; var param_63: f32; - var specular: vec3; - var param_64: vec3; - var param_65: Surface; - var diffuse: vec3; - var param_66: vec3; + var param_64: f32; + var param_65: f32; + var param_66: f32; var param_67: f32; - var param_68: f32; - var param_69: f32; + var f_4: vec3; + var param_68: vec3; + var param_69: vec3; var param_70: f32; - var sheen: vec3; - var param_71: f32; - var param_72: f32; - var param_73: f32; + var specular: vec3; + var param_71: vec3; + var param_72: Surface; + var diffuse: vec3; + var param_73: vec3; var param_74: f32; - var param_75: Surface; - var param_76: LightSample; - - let _e311 = (*s_3).roughness; - lobe_1 = max(_e311, 0.045f); - let _e313 = lobe_1; - let _e314 = lobe_1; - alpha_6 = (_e313 * _e314); - let _e316 = g_f0_dielectric; - let _e318 = (*s_3).albedo; - let _e320 = (*s_3).metallic; - f0_2 = mix(_e316, _e318, vec3(_e320)); - let _e323 = g_f90_; - let _e325 = (*s_3).metallic; - f90_1 = vec3(mix(_e323, 1f, _e325)); - let _e329 = (*s_3).albedo; - let _e331 = (*s_3).metallic; - diffuseColor = (_e329 * (1f - _e331)); - let _e335 = (*light_1).n_dot_h; - param_43 = _e335; - let _e336 = alpha_6; - param_44 = _e336; - let _e337 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_43), (¶m_44)); - d_2 = _e337; - let _e339 = (*s_3).n_dot_v; - param_45 = _e339; - let _e341 = (*light_1).n_dot_l; - param_46 = _e341; - let _e342 = alpha_6; - param_47 = _e342; - let _e343 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_45), (¶m_46), (¶m_47)); - vis_1 = _e343; - let _e344 = g_aniso; - if (_e344 > 0f) { - let _e346 = alpha_6; - let _e347 = g_aniso; - let _e348 = g_aniso; - at_2 = mix(_e346, 1f, (_e347 * _e348)); - let _e351 = alpha_6; - ab_3 = max(_e351, 0.001f); - let _e353 = g_aniso_t; - let _e355 = (*light_1).h; - let _e357 = g_aniso_b; - let _e359 = (*light_1).h; - let _e362 = (*light_1).n_dot_h; - param_48 = _e362; - param_49 = dot(_e353, _e355); - param_50 = dot(_e357, _e359); - let _e363 = at_2; - param_51 = _e363; - let _e364 = ab_3; - param_52 = _e364; - let _e365 = D_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_48), (¶m_49), (¶m_50), (¶m_51), (¶m_52)); - d_2 = _e365; + var param_75: f32; + var param_76: f32; + var param_77: f32; + var sheen: vec3; + var param_78: f32; + var param_79: f32; + var param_80: f32; + var param_81: f32; + var param_82: Surface; + var param_83: LightSample; + + let _e320 = (*s_4).roughness; + lobe_1 = max(_e320, 0.045f); + let _e322 = lobe_1; + let _e323 = lobe_1; + alpha_6 = (_e322 * _e323); + let _e325 = g_f0_dielectric; + let _e327 = (*s_4).albedo; + let _e329 = (*s_4).metallic; + f0_2 = mix(_e325, _e327, vec3(_e329)); + let _e332 = g_f90_; + let _e334 = (*s_4).metallic; + f90_1 = vec3(mix(_e332, 1f, _e334)); + let _e338 = (*s_4).albedo; + let _e340 = (*s_4).metallic; + diffuseColor = (_e338 * (1f - _e340)); + let _e344 = (*light_1).n_dot_h; + param_50 = _e344; + let _e345 = alpha_6; + param_51 = _e345; + let _e346 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_50), (¶m_51)); + d_2 = _e346; + let _e348 = (*s_4).n_dot_v; + param_52 = _e348; + let _e350 = (*light_1).n_dot_l; + param_53 = _e350; + let _e351 = alpha_6; + param_54 = _e351; + let _e352 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_52), (¶m_53), (¶m_54)); + vis_1 = _e352; + let _e353 = g_aniso; + if (_e353 > 0f) { + let _e355 = alpha_6; + let _e356 = g_aniso; + let _e357 = g_aniso; + at_2 = mix(_e355, 1f, (_e356 * _e357)); + let _e360 = alpha_6; + ab_3 = max(_e360, 0.001f); + let _e362 = g_aniso_t; + let _e364 = (*light_1).h; let _e366 = g_aniso_b; - let _e368 = (*s_3).v; - let _e370 = g_aniso_t; - let _e372 = (*s_3).v; - let _e374 = g_aniso_t; - let _e376 = (*light_1).l; - let _e378 = g_aniso_b; - let _e380 = (*light_1).l; - let _e383 = (*light_1).n_dot_l; - param_53 = _e383; - let _e385 = (*s_3).n_dot_v; - param_54 = _e385; - param_55 = dot(_e366, _e368); - param_56 = dot(_e370, _e372); - param_57 = dot(_e374, _e376); - param_58 = dot(_e378, _e380); - let _e386 = at_2; - param_59 = _e386; - let _e387 = ab_3; - param_60 = _e387; - let _e388 = V_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_53), (¶m_54), (¶m_55), (¶m_56), (¶m_57), (¶m_58), (¶m_59), (¶m_60)); - vis_1 = _e388; - } - let _e389 = f0_2; - param_61 = _e389; - let _e390 = f90_1; - param_62 = _e390; - let _e392 = (*light_1).v_dot_h; - param_63 = _e392; - let _e393 = F_SchlickF90_u0028_vf3_u003b_vf3_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - f_4 = _e393; - let _e394 = f_4; - let _e395 = g_irid_fresnel; - let _e396 = g_iridescence; - f_4 = mix(_e394, _e395, vec3(_e396)); - let _e399 = d_2; - let _e400 = vis_1; - let _e402 = f_4; - let _e406 = frag_info.material[3u]; - specular = ((_e402 * (_e399 * _e400)) * _e406); - let _e409 = (*light_1).integrated; - if (_e409 > 0.5f) { - let _e413 = (*light_1).ltc[0u]; - let _e414 = f0_2; - let _e417 = (*light_1).ltc[1u]; - let _e419 = f90_1; - let _e420 = f0_2; - let _e424 = (*light_1).ltc[2u]; - let _e430 = frag_info.material[3u]; - let _e433 = (*light_1).n_dot_l; - specular = (((((_e414 * _e417) + ((_e419 - _e420) * _e424)) * _e413) * _e430) / vec3(max(_e433, 0.000001f))); - } - let _e437 = EnergyCompensation_u0028_(); - if _e437 { - let _e438 = f0_2; - param_64 = _e438; - let _e439 = (*s_3); - param_65 = _e439; - let _e440 = MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_64), (¶m_65)); - let _e441 = specular; - specular = (_e441 * _e440); - } - let _e443 = diffuseColor; - let _e444 = f_4; - diffuse = ((_e443 * (vec3(1f, 1f, 1f) - _e444)) / vec3(3.1415927f)); - let _e449 = EonDiffuse_u0028_(); - if _e449 { - let _e451 = (*s_3).n; - let _e453 = (*light_1).l; - let _e457 = (*light_1).l; - let _e459 = (*s_3).v; - let _e461 = diffuseColor; - param_66 = _e461; - let _e463 = (*s_3).roughness; - param_67 = _e463; - param_68 = clamp(dot(_e451, _e453), 0.0001f, 1f); - let _e465 = (*s_3).n_dot_v; - param_69 = _e465; - param_70 = dot(_e457, _e459); - let _e466 = EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_66), (¶m_67), (¶m_68), (¶m_69), (¶m_70)); - let _e467 = f_4; - diffuse = (_e466 * (vec3(1f, 1f, 1f) - _e467)); - } - let _e470 = g_transmission; - let _e472 = diffuse; - diffuse = (_e472 * (1f - _e470)); - let _e474 = g_sheen; - let _e475 = g_sheen_roughness; - param_71 = _e475; - let _e477 = (*light_1).n_dot_h; - param_72 = _e477; - let _e478 = D_Charlie_u0028_f1_u003b_f1_u003b((¶m_71), (¶m_72)); - let _e481 = (*s_3).n; - let _e483 = (*light_1).l; - let _e487 = (*s_3).n_dot_v; - param_73 = _e487; - param_74 = clamp(dot(_e481, _e483), 0f, 1f); - let _e488 = V_Neubelt_u0028_f1_u003b_f1_u003b((¶m_73), (¶m_74)); - sheen = ((_e474 * _e478) * _e488); - let _e490 = diffuse; - let _e491 = specular; - let _e493 = g_sheen_scale; - let _e495 = sheen; - let _e497 = g_coat_through; - let _e499 = g_coat; - let _e500 = (*s_3); - param_75 = _e500; - let _e501 = (*light_1); - param_76 = _e501; - let _e502 = CoatLobe_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_75), (¶m_76)); - return ((((((_e490 + _e491) * _e493) + _e495) * _e497) + vec3((_e499 * _e502))) * 3.1415927f); + let _e368 = (*light_1).h; + let _e371 = (*light_1).n_dot_h; + param_55 = _e371; + param_56 = dot(_e362, _e364); + param_57 = dot(_e366, _e368); + let _e372 = at_2; + param_58 = _e372; + let _e373 = ab_3; + param_59 = _e373; + let _e374 = D_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57), (¶m_58), (¶m_59)); + d_2 = _e374; + let _e375 = g_aniso_b; + let _e377 = (*s_4).v; + let _e379 = g_aniso_t; + let _e381 = (*s_4).v; + let _e383 = g_aniso_t; + let _e385 = (*light_1).l; + let _e387 = g_aniso_b; + let _e389 = (*light_1).l; + let _e392 = (*light_1).n_dot_l; + param_60 = _e392; + let _e394 = (*s_4).n_dot_v; + param_61 = _e394; + param_62 = dot(_e375, _e377); + param_63 = dot(_e379, _e381); + param_64 = dot(_e383, _e385); + param_65 = dot(_e387, _e389); + let _e395 = at_2; + param_66 = _e395; + let _e396 = ab_3; + param_67 = _e396; + let _e397 = V_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_60), (¶m_61), (¶m_62), (¶m_63), (¶m_64), (¶m_65), (¶m_66), (¶m_67)); + vis_1 = _e397; + } + let _e398 = f0_2; + param_68 = _e398; + let _e399 = f90_1; + param_69 = _e399; + let _e401 = (*light_1).v_dot_h; + param_70 = _e401; + let _e402 = F_SchlickF90_u0028_vf3_u003b_vf3_u003b_f1_u003b((¶m_68), (¶m_69), (¶m_70)); + f_4 = _e402; + let _e403 = f_4; + let _e404 = g_irid_fresnel; + let _e405 = g_iridescence; + f_4 = mix(_e403, _e404, vec3(_e405)); + let _e408 = d_2; + let _e409 = vis_1; + let _e411 = f_4; + let _e415 = frag_info.material[3u]; + specular = ((_e411 * (_e408 * _e409)) * _e415); + let _e418 = (*light_1).integrated; + if (_e418 > 0.5f) { + let _e422 = (*light_1).ltc[0u]; + let _e423 = f0_2; + let _e426 = (*light_1).ltc[1u]; + let _e428 = f90_1; + let _e429 = f0_2; + let _e433 = (*light_1).ltc[2u]; + let _e439 = frag_info.material[3u]; + let _e442 = (*light_1).n_dot_l; + specular = (((((_e423 * _e426) + ((_e428 - _e429) * _e433)) * _e422) * _e439) / vec3(max(_e442, 0.000001f))); + } + let _e446 = EnergyCompensation_u0028_(); + if _e446 { + let _e447 = f0_2; + param_71 = _e447; + let _e448 = (*s_4); + param_72 = _e448; + let _e449 = MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_71), (¶m_72)); + let _e450 = specular; + specular = (_e450 * _e449); + } + let _e452 = diffuseColor; + let _e453 = f_4; + diffuse = ((_e452 * (vec3(1f, 1f, 1f) - _e453)) / vec3(3.1415927f)); + let _e458 = EonDiffuse_u0028_(); + if _e458 { + let _e460 = (*s_4).n; + let _e462 = (*light_1).l; + let _e466 = (*light_1).l; + let _e468 = (*s_4).v; + let _e470 = diffuseColor; + param_73 = _e470; + let _e472 = (*s_4).roughness; + param_74 = _e472; + param_75 = clamp(dot(_e460, _e462), 0.0001f, 1f); + let _e474 = (*s_4).n_dot_v; + param_76 = _e474; + param_77 = dot(_e466, _e468); + let _e475 = EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_73), (¶m_74), (¶m_75), (¶m_76), (¶m_77)); + let _e476 = f_4; + diffuse = (_e475 * (vec3(1f, 1f, 1f) - _e476)); + } + let _e479 = g_transmission; + let _e481 = diffuse; + diffuse = (_e481 * (1f - _e479)); + let _e483 = g_sheen; + let _e484 = g_sheen_roughness; + param_78 = _e484; + let _e486 = (*light_1).n_dot_h; + param_79 = _e486; + let _e487 = D_Charlie_u0028_f1_u003b_f1_u003b((¶m_78), (¶m_79)); + let _e490 = (*s_4).n; + let _e492 = (*light_1).l; + let _e496 = (*s_4).n_dot_v; + param_80 = _e496; + param_81 = clamp(dot(_e490, _e492), 0f, 1f); + let _e497 = V_Neubelt_u0028_f1_u003b_f1_u003b((¶m_80), (¶m_81)); + sheen = ((_e483 * _e487) * _e497); + let _e499 = diffuse; + let _e500 = specular; + let _e502 = g_sheen_scale; + let _e504 = sheen; + let _e506 = g_coat_through; + let _e508 = g_coat; + let _e509 = (*s_4); + param_82 = _e509; + let _e510 = (*light_1); + param_83 = _e510; + let _e511 = CoatLobe_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_82), (¶m_83)); + return ((((((_e499 + _e500) * _e502) + _e504) * _e506) + vec3((_e508 * _e511))) * 3.1415927f); } fn PointShadowDiskTap_u0028_i1_u003b(i_1: ptr) -> vec2 { - let _e262 = (*i_1); - if (_e262 == 0i) { + let _e271 = (*i_1); + if (_e271 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e264 = (*i_1); - if (_e264 == 1i) { + let _e273 = (*i_1); + if (_e273 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e266 = (*i_1); - if (_e266 == 2i) { + let _e275 = (*i_1); + if (_e275 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e268 = (*i_1); - if (_e268 == 3i) { + let _e277 = (*i_1); + if (_e277 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e270 = (*i_1); - if (_e270 == 4i) { + let _e279 = (*i_1); + if (_e279 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e272 = (*i_1); - if (_e272 == 5i) { + let _e281 = (*i_1); + if (_e281 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e274 = (*i_1); - if (_e274 == 6i) { + let _e283 = (*i_1); + if (_e283 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -17355,212 +18125,212 @@ fn PointShadowDiskTap_u0028_i1_u003b(i_1: ptr) -> vec2 { fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_2: ptr, ca: ptr, sa: ptr, radius: ptr) -> vec2 { var p_1: vec2; - var param_77: i32; - - let _e267 = (*i_2); - param_77 = _e267; - let _e268 = PointShadowDiskTap_u0028_i1_u003b((¶m_77)); - p_1 = _e268; - let _e270 = p_1[0u]; - let _e271 = (*ca); - let _e274 = p_1[1u]; - let _e275 = (*sa); + var param_84: i32; + + let _e276 = (*i_2); + param_84 = _e276; + let _e277 = PointShadowDiskTap_u0028_i1_u003b((¶m_84)); + p_1 = _e277; let _e279 = p_1[0u]; - let _e280 = (*sa); + let _e280 = (*ca); let _e283 = p_1[1u]; - let _e284 = (*ca); - let _e288 = (*radius); - return (vec2(((_e270 * _e271) - (_e274 * _e275)), ((_e279 * _e280) + (_e283 * _e284))) * _e288); + let _e284 = (*sa); + let _e288 = p_1[0u]; + let _e289 = (*sa); + let _e292 = p_1[1u]; + let _e293 = (*ca); + let _e297 = (*radius); + return (vec2(((_e279 * _e280) - (_e283 * _e284)), ((_e288 * _e289) + (_e292 * _e293))) * _e297); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_6: vec2; + var local_8: vec2; var atlas: vec2; - let _e270 = point_shadow.params[0u]; - inset = _e270; - let _e271 = (*uv_1); - let _e272 = (*offset); - let _e274 = inset; - let _e275 = inset; - local_6 = clamp((_e271 + _e272), vec2(_e274), vec2((1f - _e275))); - let _e280 = local_6; - let _e281 = (*tile); - atlas = ((_e280 + _e281) * vec2(0.16666667f, 0.16666667f)); - let _e286 = point_shadow.params3_[0u]; - if (_e286 > 0.5f) { - let _e289 = atlas[1u]; - atlas[1u] = (1f - _e289); - } - let _e292 = atlas; - let _e293 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e292, 0f); - let _e295 = atlas; - let _e296 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e295, 0f); - let _e299 = (*range); - return (min(_e293.x, _e296.x) * _e299); + let _e279 = point_shadow.params[0u]; + inset = _e279; + let _e280 = (*uv_1); + let _e281 = (*offset); + let _e283 = inset; + let _e284 = inset; + local_8 = clamp((_e280 + _e281), vec2(_e283), vec2((1f - _e284))); + let _e289 = local_8; + let _e290 = (*tile); + atlas = ((_e289 + _e290) * vec2(0.16666667f, 0.16666667f)); + let _e295 = point_shadow.params3_[0u]; + if (_e295 > 0.5f) { + let _e298 = atlas[1u]; + atlas[1u] = (1f - _e298); + } + let _e301 = atlas; + let _e302 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e301, 0f); + let _e304 = atlas; + let _e305 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e304, 0f); + let _e308 = (*range); + return (min(_e302.x, _e305.x) * _e308); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_2: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { var stored: f32; - var param_78: vec2; - var param_79: vec2; - var param_80: vec2; - var param_81: f32; + var param_85: vec2; + var param_86: vec2; + var param_87: vec2; + var param_88: f32; - let _e271 = (*uv_2); - param_78 = _e271; - let _e272 = (*offset_1); - param_79 = _e272; - let _e273 = (*tile_1); - param_80 = _e273; - let _e274 = (*range_1); - param_81 = _e274; - let _e275 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_78), (¶m_79), (¶m_80), (¶m_81)); - stored = _e275; - let _e276 = stored; - let _e277 = (*range_1); - if (_e276 >= (_e277 * 0.999f)) { + let _e280 = (*uv_2); + param_85 = _e280; + let _e281 = (*offset_1); + param_86 = _e281; + let _e282 = (*tile_1); + param_87 = _e282; + let _e283 = (*range_1); + param_88 = _e283; + let _e284 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_85), (¶m_86), (¶m_87), (¶m_88)); + stored = _e284; + let _e285 = stored; + let _e286 = (*range_1); + if (_e285 >= (_e286 * 0.999f)) { return 1f; } - let _e280 = (*receiver); - let _e281 = stored; - return select(1f, 0f, (_e280 > _e281)); + let _e289 = (*receiver); + let _e290 = stored; + return select(1f, 0f, (_e289 > _e290)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_3: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { var lightRadius: f32; var minRadius: f32; var maxRadius: f32; - var param_82: vec2; - var param_83: vec2; - var param_84: vec2; - var param_85: f32; + var param_89: vec2; + var param_90: vec2; + var param_91: vec2; + var param_92: f32; var sum: f32; var count: f32; var i_3: i32; var stored_1: f32; - var param_86: i32; - var param_87: f32; - var param_88: f32; - var param_89: f32; - var param_90: vec2; - var param_91: vec2; - var param_92: vec2; - var param_93: f32; + var param_93: i32; + var param_94: f32; + var param_95: f32; + var param_96: f32; + var param_97: vec2; + var param_98: vec2; + var param_99: vec2; + var param_100: f32; var blocker: f32; var world: f32; (*blockerOut) = -1f; - let _e292 = point_shadow.params2_[1u]; - lightRadius = _e292; - let _e295 = point_shadow.params2_[0u]; - minRadius = _e295; - let _e298 = point_shadow.params2_[2u]; - maxRadius = _e298; - let _e299 = lightRadius; - if (_e299 <= 0f) { - let _e301 = (*uv_3); - param_82 = _e301; - param_83 = vec2(0f, 0f); - let _e302 = (*tile_2); - param_84 = _e302; - let _e303 = (*range_2); - param_85 = _e303; - let _e304 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_82), (¶m_83), (¶m_84), (¶m_85)); - (*blockerOut) = _e304; - let _e305 = minRadius; - return _e305; + let _e301 = point_shadow.params2_[1u]; + lightRadius = _e301; + let _e304 = point_shadow.params2_[0u]; + minRadius = _e304; + let _e307 = point_shadow.params2_[2u]; + maxRadius = _e307; + let _e308 = lightRadius; + if (_e308 <= 0f) { + let _e310 = (*uv_3); + param_89 = _e310; + param_90 = vec2(0f, 0f); + let _e311 = (*tile_2); + param_91 = _e311; + let _e312 = (*range_2); + param_92 = _e312; + let _e313 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_89), (¶m_90), (¶m_91), (¶m_92)); + (*blockerOut) = _e313; + let _e314 = minRadius; + return _e314; } sum = 0f; count = 0f; i_3 = 0i; loop { - let _e306 = i_3; - if (_e306 < 8i) { - let _e308 = i_3; - param_86 = _e308; - let _e309 = (*ca_1); - param_87 = _e309; - let _e310 = (*sa_1); - param_88 = _e310; - let _e311 = maxRadius; - param_89 = _e311; - let _e312 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_86), (¶m_87), (¶m_88), (¶m_89)); - let _e313 = (*uv_3); - param_90 = _e313; - param_91 = _e312; - let _e314 = (*tile_2); - param_92 = _e314; - let _e315 = (*range_2); - param_93 = _e315; - let _e316 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_90), (¶m_91), (¶m_92), (¶m_93)); - stored_1 = _e316; - let _e317 = stored_1; - let _e318 = (*range_2); - if (_e317 >= (_e318 * 0.999f)) { + let _e315 = i_3; + if (_e315 < 8i) { + let _e317 = i_3; + param_93 = _e317; + let _e318 = (*ca_1); + param_94 = _e318; + let _e319 = (*sa_1); + param_95 = _e319; + let _e320 = maxRadius; + param_96 = _e320; + let _e321 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_93), (¶m_94), (¶m_95), (¶m_96)); + let _e322 = (*uv_3); + param_97 = _e322; + param_98 = _e321; + let _e323 = (*tile_2); + param_99 = _e323; + let _e324 = (*range_2); + param_100 = _e324; + let _e325 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_97), (¶m_98), (¶m_99), (¶m_100)); + stored_1 = _e325; + let _e326 = stored_1; + let _e327 = (*range_2); + if (_e326 >= (_e327 * 0.999f)) { continue; } - let _e321 = stored_1; - let _e322 = (*receiver_1); - if (_e321 >= _e322) { + let _e330 = stored_1; + let _e331 = (*receiver_1); + if (_e330 >= _e331) { continue; } - let _e324 = stored_1; - let _e325 = sum; - sum = (_e325 + _e324); - let _e327 = count; - count = (_e327 + 1f); + let _e333 = stored_1; + let _e334 = sum; + sum = (_e334 + _e333); + let _e336 = count; + count = (_e336 + 1f); continue; } else { break; } continuing { - let _e329 = i_3; - i_3 = (_e329 + 1i); + let _e338 = i_3; + i_3 = (_e338 + 1i); } } - let _e331 = count; - if (_e331 < 0.5f) { + let _e340 = count; + if (_e340 < 0.5f) { return -1f; } - let _e333 = sum; - let _e334 = count; - blocker = max((_e333 / _e334), 0.0001f); - let _e337 = blocker; - (*blockerOut) = _e337; - let _e338 = lightRadius; - let _e339 = (*receiver_1); - let _e340 = blocker; - let _e344 = blocker; - world = ((_e338 * max((_e339 - _e340), 0f)) / _e344); - let _e346 = world; - let _e347 = (*receiver_1); - let _e349 = (*tanHalf); - let _e352 = minRadius; - let _e353 = maxRadius; - return clamp((_e346 / ((2f * _e347) * _e349)), _e352, _e353); + let _e342 = sum; + let _e343 = count; + blocker = max((_e342 / _e343), 0.0001f); + let _e346 = blocker; + (*blockerOut) = _e346; + let _e347 = lightRadius; + let _e348 = (*receiver_1); + let _e349 = blocker; + let _e353 = blocker; + world = ((_e347 * max((_e348 - _e349), 0f)) / _e353); + let _e355 = world; + let _e356 = (*receiver_1); + let _e358 = (*tanHalf); + let _e361 = minRadius; + let _e362 = maxRadius; + return clamp((_e355 / ((2f * _e356) * _e358)), _e361, _e362); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_7: vec2; + var local_9: vec2; - let _e263 = (*rows); - if (_e263 > 0f) { - let _e266 = gl_FragCoord_1[0u]; - let _e267 = (*rows); - let _e269 = gl_FragCoord_1[1u]; - local_7 = vec2(_e266, (_e267 - _e269)); + let _e272 = (*rows); + if (_e272 > 0f) { + let _e275 = gl_FragCoord_1[0u]; + let _e276 = (*rows); + let _e278 = gl_FragCoord_1[1u]; + local_9 = vec2(_e275, (_e276 - _e278)); } else { - let _e272 = gl_FragCoord_1; - local_7 = _e272.xy; + let _e281 = gl_FragCoord_1; + local_9 = _e281.xy; } - let _e274 = local_7; - return _e274; + let _e283 = local_9; + return _e283; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { - var slot: i32; + var slot_1: i32; var strength: f32; var toLight: vec3; var toLightLength: f32; @@ -17579,292 +18349,292 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var receiver_2: f32; var noise: f32; - var param_94: f32; + var param_101: f32; var angle_1: f32; var ca_2: f32; var sa_2: f32; var tanHalf_1: f32; var blocker_1: f32; var radius_1: f32; - var param_95: vec2; - var param_96: vec2; - var param_97: f32; - var param_98: f32; - var param_99: f32; - var param_100: f32; - var param_101: f32; - var param_102: f32; - var local_8: vec3; - var lit: f32; + var param_102: vec2; var param_103: vec2; - var param_104: vec2; - var param_105: vec2; + var param_104: f32; + var param_105: f32; var param_106: f32; var param_107: f32; - var i_4: i32; - var param_108: i32; + var param_108: f32; var param_109: f32; - var param_110: f32; - var param_111: f32; + var local_10: vec3; + var lit_1: f32; + var param_110: vec2; + var param_111: vec2; var param_112: vec2; - var param_113: vec2; - var param_114: vec2; - var param_115: f32; + var param_113: f32; + var param_114: f32; + var i_4: i32; + var param_115: i32; var param_116: f32; - var phi_2637_: bool; - var phi_2698_: bool; - - let _e315 = (*lightIndex); - let _e319 = point_shadow.slots[_e315][0u]; - slot = i32((_e319 + 0.5f)); - let _e322 = (*lightIndex); - let _e326 = point_shadow.slots[_e322][0u]; - if (_e326 < 0f) { + var param_117: f32; + var param_118: f32; + var param_119: vec2; + var param_120: vec2; + var param_121: vec2; + var param_122: f32; + var param_123: f32; + var phi_2646_: bool; + var phi_2707_: bool; + + let _e324 = (*lightIndex); + let _e328 = point_shadow.slots[_e324][0u]; + slot_1 = i32((_e328 + 0.5f)); + let _e331 = (*lightIndex); + let _e335 = point_shadow.slots[_e331][0u]; + if (_e335 < 0f) { return 1f; } - let _e330 = point_shadow.params[2u]; - strength = _e330; - let _e331 = strength; - if (_e331 <= 0f) { + let _e339 = point_shadow.params[2u]; + strength = _e339; + let _e340 = strength; + if (_e340 <= 0f) { return 1f; } - let _e333 = slot; - let _e336 = point_shadow.lights[_e333]; - let _e338 = (*world_1); - toLight = (_e336.xyz - _e338); - let _e340 = toLight; - toLightLength = max(length(_e340), 0.000001f); - let _e343 = (*normal); - let _e344 = toLight; - let _e345 = toLightLength; - nDotL = max(dot(_e343, (_e344 / vec3(_e345))), 0.15f); - let _e350 = nDotL; - let _e351 = nDotL; - let _e356 = nDotL; - let _e357 = nDotL; - slope = min((sqrt(max((1f - (_e350 * _e351)), 0f)) / (_e356 * _e357)), 8f); - let _e361 = toLightLength; - let _e363 = (*lightIndex); - let _e367 = point_shadow.slots[_e363][2u]; - let _e372 = point_shadow.params3_[1u]; - texel = (((2f * _e361) * max(_e367, 0.0001f)) * _e372); - let _e374 = (*world_1); - let _e375 = (*normal); - let _e376 = texel; - let _e380 = point_shadow.params[3u]; - let _e382 = slope; - origin = (_e374 + (((_e375 * _e376) * _e380) * (1f + _e382))); - let _e386 = origin; - let _e387 = slot; - let _e390 = point_shadow.lights[_e387]; - toFragment = (_e386 - _e390.xyz); - let _e393 = toFragment; - distance_ = length(_e393); - let _e395 = slot; - let _e399 = point_shadow.lights[_e395][3u]; - range_3 = max(_e399, 0.0001f); - let _e401 = distance_; - let _e402 = range_3; - if (_e401 >= _e402) { + let _e342 = slot_1; + let _e345 = point_shadow.lights[_e342]; + let _e347 = (*world_1); + toLight = (_e345.xyz - _e347); + let _e349 = toLight; + toLightLength = max(length(_e349), 0.000001f); + let _e352 = (*normal); + let _e353 = toLight; + let _e354 = toLightLength; + nDotL = max(dot(_e352, (_e353 / vec3(_e354))), 0.15f); + let _e359 = nDotL; + let _e360 = nDotL; + let _e365 = nDotL; + let _e366 = nDotL; + slope = min((sqrt(max((1f - (_e359 * _e360)), 0f)) / (_e365 * _e366)), 8f); + let _e370 = toLightLength; + let _e372 = (*lightIndex); + let _e376 = point_shadow.slots[_e372][2u]; + let _e381 = point_shadow.params3_[1u]; + texel = (((2f * _e370) * max(_e376, 0.0001f)) * _e381); + let _e383 = (*world_1); + let _e384 = (*normal); + let _e385 = texel; + let _e389 = point_shadow.params[3u]; + let _e391 = slope; + origin = (_e383 + (((_e384 * _e385) * _e389) * (1f + _e391))); + let _e395 = origin; + let _e396 = slot_1; + let _e399 = point_shadow.lights[_e396]; + toFragment = (_e395 - _e399.xyz); + let _e402 = toFragment; + distance_ = length(_e402); + let _e404 = slot_1; + let _e408 = point_shadow.lights[_e404][3u]; + range_3 = max(_e408, 0.0001f); + let _e410 = distance_; + let _e411 = range_3; + if (_e410 >= _e411) { return 1f; } face = 0i; - let _e404 = (*lightIndex); - let _e408 = point_shadow.slots[_e404][1u]; - if (_e408 < 0.5f) { - let _e410 = toFragment; - a_2 = abs(_e410); - let _e413 = a_2[0u]; - let _e415 = a_2[1u]; - let _e416 = (_e413 >= _e415); - phi_2637_ = _e416; - if _e416 { - let _e418 = a_2[0u]; - let _e420 = a_2[2u]; - phi_2637_ = (_e418 >= _e420); - } - let _e423 = phi_2637_; - if _e423 { - let _e425 = toFragment[0u]; - face = select(1i, 0i, (_e425 > 0f)); + let _e413 = (*lightIndex); + let _e417 = point_shadow.slots[_e413][1u]; + if (_e417 < 0.5f) { + let _e419 = toFragment; + a_2 = abs(_e419); + let _e422 = a_2[0u]; + let _e424 = a_2[1u]; + let _e425 = (_e422 >= _e424); + phi_2646_ = _e425; + if _e425 { + let _e427 = a_2[0u]; + let _e429 = a_2[2u]; + phi_2646_ = (_e427 >= _e429); + } + let _e432 = phi_2646_; + if _e432 { + let _e434 = toFragment[0u]; + face = select(1i, 0i, (_e434 > 0f)); } else { - let _e429 = a_2[1u]; - let _e431 = a_2[2u]; - if (_e429 >= _e431) { - let _e434 = toFragment[1u]; - face = select(3i, 2i, (_e434 > 0f)); + let _e438 = a_2[1u]; + let _e440 = a_2[2u]; + if (_e438 >= _e440) { + let _e443 = toFragment[1u]; + face = select(3i, 2i, (_e443 > 0f)); } else { - let _e438 = toFragment[2u]; - face = select(5i, 4i, (_e438 > 0f)); + let _e447 = toFragment[2u]; + face = select(5i, 4i, (_e447 > 0f)); } } } - let _e441 = slot; - let _e443 = face; - let _e447 = point_shadow.faces[((_e441 * 6i) + _e443)]; - let _e448 = origin; - clip = (_e447 * vec4(_e448.x, _e448.y, _e448.z, 1f)); - let _e455 = clip[3u]; - if (_e455 <= 0f) { + let _e450 = slot_1; + let _e452 = face; + let _e456 = point_shadow.faces[((_e450 * 6i) + _e452)]; + let _e457 = origin; + clip = (_e456 * vec4(_e457.x, _e457.y, _e457.z, 1f)); + let _e464 = clip[3u]; + if (_e464 <= 0f) { return 1f; } - let _e457 = clip; - let _e460 = clip[3u]; - ndc = (_e457.xy / vec2(_e460)); - let _e464 = ndc[0u]; - let _e466 = (abs(_e464) > 1f); - phi_2698_ = _e466; - if !(_e466) { - let _e469 = ndc[1u]; - phi_2698_ = (abs(_e469) > 1f); - } - let _e473 = phi_2698_; - if _e473 { + let _e466 = clip; + let _e469 = clip[3u]; + ndc = (_e466.xy / vec2(_e469)); + let _e473 = ndc[0u]; + let _e475 = (abs(_e473) > 1f); + phi_2707_ = _e475; + if !(_e475) { + let _e478 = ndc[1u]; + phi_2707_ = (abs(_e478) > 1f); + } + let _e482 = phi_2707_; + if _e482 { return 1f; } - let _e475 = ndc[0u]; - let _e479 = ndc[1u]; - uv_4 = vec2(((_e475 * 0.5f) + 0.5f), (0.5f - (_e479 * 0.5f))); - let _e483 = face; - let _e485 = slot; - tile_3 = vec2(f32(_e483), f32(_e485)); - let _e488 = distance_; - let _e491 = point_shadow.params[1u]; - receiver_2 = (_e488 - _e491); - let _e495 = frag_info.target_origin[0u]; - param_94 = _e495; - let _e496 = FragCoordFromTop_u0028_f1_u003b((¶m_94)); - noise = fract((52.982918f * fract(dot(_e496, vec2(0.06711056f, 0.00583715f))))); - let _e501 = noise; - angle_1 = (_e501 * 6.2831855f); - let _e503 = angle_1; - ca_2 = cos(_e503); - let _e505 = angle_1; - sa_2 = sin(_e505); - let _e507 = (*lightIndex); - let _e511 = point_shadow.slots[_e507][2u]; - tanHalf_1 = max(_e511, 0.0001f); + let _e484 = ndc[0u]; + let _e488 = ndc[1u]; + uv_4 = vec2(((_e484 * 0.5f) + 0.5f), (0.5f - (_e488 * 0.5f))); + let _e492 = face; + let _e494 = slot_1; + tile_3 = vec2(f32(_e492), f32(_e494)); + let _e497 = distance_; + let _e500 = point_shadow.params[1u]; + receiver_2 = (_e497 - _e500); + let _e504 = frag_info.target_origin[0u]; + param_101 = _e504; + let _e505 = FragCoordFromTop_u0028_f1_u003b((¶m_101)); + noise = fract((52.982918f * fract(dot(_e505, vec2(0.06711056f, 0.00583715f))))); + let _e510 = noise; + angle_1 = (_e510 * 6.2831855f); + let _e512 = angle_1; + ca_2 = cos(_e512); + let _e514 = angle_1; + sa_2 = sin(_e514); + let _e516 = (*lightIndex); + let _e520 = point_shadow.slots[_e516][2u]; + tanHalf_1 = max(_e520, 0.0001f); blocker_1 = -1f; - let _e513 = uv_4; - param_95 = _e513; - let _e514 = tile_3; - param_96 = _e514; - let _e515 = range_3; - param_97 = _e515; - let _e516 = receiver_2; - param_98 = _e516; - let _e517 = ca_2; - param_99 = _e517; - let _e518 = sa_2; - param_100 = _e518; - let _e519 = tanHalf_1; - param_101 = _e519; - let _e520 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_95), (¶m_96), (¶m_97), (¶m_98), (¶m_99), (¶m_100), (¶m_101), (¶m_102)); - let _e521 = param_102; - blocker_1 = _e521; - radius_1 = _e520; - let _e524 = point_shadow.params2_[3u]; - if (_e524 > 0.5f) { + let _e522 = uv_4; + param_102 = _e522; + let _e523 = tile_3; + param_103 = _e523; + let _e524 = range_3; + param_104 = _e524; + let _e525 = receiver_2; + param_105 = _e525; + let _e526 = ca_2; + param_106 = _e526; + let _e527 = sa_2; + param_107 = _e527; + let _e528 = tanHalf_1; + param_108 = _e528; + let _e529 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_102), (¶m_103), (¶m_104), (¶m_105), (¶m_106), (¶m_107), (¶m_108), (¶m_109)); + let _e530 = param_109; + blocker_1 = _e530; + radius_1 = _e529; + let _e533 = point_shadow.params2_[3u]; + if (_e533 > 0.5f) { g_debug_surface_on = true; - let _e526 = radius_1; - if (_e526 < 0f) { - local_8 = vec3(0f, 0f, 1f); + let _e535 = radius_1; + if (_e535 < 0f) { + local_10 = vec3(0f, 0f, 1f); } else { - let _e528 = radius_1; - let _e531 = point_shadow.params2_[2u]; - let _e535 = blocker_1; - let _e536 = range_3; - local_8 = vec3(clamp((_e528 / max(_e531, 0.000001f)), 0f, 1f), clamp((_e535 / _e536), 0f, 1f), 0f); + let _e537 = radius_1; + let _e540 = point_shadow.params2_[2u]; + let _e544 = blocker_1; + let _e545 = range_3; + local_10 = vec3(clamp((_e537 / max(_e540, 0.000001f)), 0f, 1f), clamp((_e544 / _e545), 0f, 1f), 0f); } - let _e540 = local_8; - g_debug_surface = _e540; + let _e549 = local_10; + g_debug_surface = _e549; } - let _e541 = radius_1; - if (_e541 < 0f) { + let _e550 = radius_1; + if (_e550 < 0f) { return 1f; } - let _e543 = uv_4; - param_103 = _e543; - param_104 = vec2(0f, 0f); - let _e544 = tile_3; - param_105 = _e544; - let _e545 = range_3; - param_106 = _e545; - let _e546 = receiver_2; - param_107 = _e546; - let _e547 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_103), (¶m_104), (¶m_105), (¶m_106), (¶m_107)); - lit = _e547; - let _e548 = radius_1; - if (_e548 > 0f) { + let _e552 = uv_4; + param_110 = _e552; + param_111 = vec2(0f, 0f); + let _e553 = tile_3; + param_112 = _e553; + let _e554 = range_3; + param_113 = _e554; + let _e555 = receiver_2; + param_114 = _e555; + let _e556 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_110), (¶m_111), (¶m_112), (¶m_113), (¶m_114)); + lit_1 = _e556; + let _e557 = radius_1; + if (_e557 > 0f) { i_4 = 0i; loop { - let _e550 = i_4; - if (_e550 < 8i) { - let _e552 = i_4; - param_108 = _e552; - let _e553 = ca_2; - param_109 = _e553; - let _e554 = sa_2; - param_110 = _e554; - let _e555 = radius_1; - param_111 = _e555; - let _e556 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_108), (¶m_109), (¶m_110), (¶m_111)); - let _e557 = uv_4; - param_112 = _e557; - param_113 = _e556; - let _e558 = tile_3; - param_114 = _e558; - let _e559 = range_3; - param_115 = _e559; - let _e560 = receiver_2; - param_116 = _e560; - let _e561 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_112), (¶m_113), (¶m_114), (¶m_115), (¶m_116)); - let _e562 = lit; - lit = (_e562 + _e561); + let _e559 = i_4; + if (_e559 < 8i) { + let _e561 = i_4; + param_115 = _e561; + let _e562 = ca_2; + param_116 = _e562; + let _e563 = sa_2; + param_117 = _e563; + let _e564 = radius_1; + param_118 = _e564; + let _e565 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_115), (¶m_116), (¶m_117), (¶m_118)); + let _e566 = uv_4; + param_119 = _e566; + param_120 = _e565; + let _e567 = tile_3; + param_121 = _e567; + let _e568 = range_3; + param_122 = _e568; + let _e569 = receiver_2; + param_123 = _e569; + let _e570 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_119), (¶m_120), (¶m_121), (¶m_122), (¶m_123)); + let _e571 = lit_1; + lit_1 = (_e571 + _e570); continue; } else { break; } continuing { - let _e564 = i_4; - i_4 = (_e564 + 1i); + let _e573 = i_4; + i_4 = (_e573 + 1i); } } - let _e566 = lit; - lit = (_e566 * 0.11111111f); + let _e575 = lit_1; + lit_1 = (_e575 * 0.11111111f); } - let _e568 = lit; - let _e569 = strength; - return mix(1f, _e568, clamp(_e569, 0f, 1f)); + let _e577 = lit_1; + let _e578 = strength; + return mix(1f, _e577, clamp(_e578, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_5: ptr, n_2: ptr, turn: ptr) -> vec2 { var r_3: f32; var theta: f32; - let _e266 = (*i_5); - let _e269 = (*n_2); - r_3 = sqrt(((f32(_e266) + 0.5f) / f32(_e269))); - let _e273 = (*i_5); - let _e276 = (*turn); - theta = ((f32(_e273) * 2.3999631f) + _e276); - let _e278 = r_3; - let _e279 = theta; - let _e281 = theta; - return (vec2(cos(_e279), sin(_e281)) * _e278); + let _e275 = (*i_5); + let _e278 = (*n_2); + r_3 = sqrt(((f32(_e275) + 0.5f) / f32(_e278))); + let _e282 = (*i_5); + let _e285 = (*turn); + theta = ((f32(_e282) * 2.3999631f) + _e285); + let _e287 = r_3; + let _e288 = theta; + let _e290 = theta; + return (vec2(cos(_e288), sin(_e290)) * _e287); } fn ShadowNoise_u0028_() -> f32 { var at_3: vec2; - var param_117: f32; + var param_124: f32; - let _e265 = frag_info.target_origin[0u]; - param_117 = _e265; - let _e266 = FragCoordFromTop_u0028_f1_u003b((¶m_117)); - let _e269 = frag_info.target_origin[3u]; - at_3 = (_e266 + vec2((5.588238f * max(_e269, 0f)))); - let _e274 = at_3; - return fract((52.982918f * fract(dot(_e274, vec2(0.06711056f, 0.00583715f))))); + let _e274 = frag_info.target_origin[0u]; + param_124 = _e274; + let _e275 = FragCoordFromTop_u0028_f1_u003b((¶m_124)); + let _e278 = frag_info.target_origin[3u]; + at_3 = (_e275 + vec2((5.588238f * max(_e278, 0f)))); + let _e283 = at_3; + return fract((52.982918f * fract(dot(_e283, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -17873,88 +18643,88 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_4: f32; - let _e263 = (*depth); - d_4 = ((2f * clamp(_e263, 0f, 1f)) - 1f); - let _e267 = d_4; - let _e270 = d_4; - return vec2(exp((40f * _e267)), -(exp((-5f * _e270)))); + let _e272 = (*depth); + d_4 = ((2f * clamp(_e272, 0f, 1f)) - 1f); + let _e276 = d_4; + let _e279 = d_4; + return vec2(exp((40f * _e276)), -(exp((-5f * _e279)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { var warped: vec2; - var param_118: f32; + var param_125: f32; var scale: vec2; var positive: f32; - var param_119: vec2; - var param_120: f32; - var param_121: f32; - var param_122: f32; + var param_126: vec2; + var param_127: f32; + var param_128: f32; + var param_129: f32; var negative: f32; - var param_123: vec2; - var param_124: f32; - var param_125: f32; - var param_126: f32; + var param_130: vec2; + var param_131: f32; + var param_132: f32; + var param_133: f32; - let _e277 = (*depth_1); - param_118 = _e277; - let _e278 = EvsmWarp_u0028_f1_u003b((¶m_118)); - warped = _e278; - let _e279 = warped; - scale = (vec2(0.004f, 0.0005f) * _e279); - let _e282 = scale[0u]; - let _e284 = scale[0u]; - let _e286 = (*moments_1); - param_119 = _e286.xy; - let _e289 = warped[0u]; - param_120 = _e289; - param_121 = (_e282 * _e284); - let _e290 = (*bleed_1); - param_122 = _e290; - let _e291 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_119), (¶m_120), (¶m_121), (¶m_122)); - positive = _e291; - let _e293 = scale[1u]; - let _e295 = scale[1u]; - let _e297 = (*moments_1); - param_123 = _e297.zw; - let _e300 = warped[1u]; - param_124 = _e300; - param_125 = (_e293 * _e295); - let _e301 = (*bleed_1); - param_126 = _e301; - let _e302 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_123), (¶m_124), (¶m_125), (¶m_126)); - negative = _e302; - let _e303 = positive; - let _e304 = negative; - return min(_e303, _e304); -} - -fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_4: ptr, light_2: ptr, lightIndex_1: ptr) -> f32 { + let _e286 = (*depth_1); + param_125 = _e286; + let _e287 = EvsmWarp_u0028_f1_u003b((¶m_125)); + warped = _e287; + let _e288 = warped; + scale = (vec2(0.004f, 0.0005f) * _e288); + let _e291 = scale[0u]; + let _e293 = scale[0u]; + let _e295 = (*moments_1); + param_126 = _e295.xy; + let _e298 = warped[0u]; + param_127 = _e298; + param_128 = (_e291 * _e293); + let _e299 = (*bleed_1); + param_129 = _e299; + let _e300 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_126), (¶m_127), (¶m_128), (¶m_129)); + positive = _e300; + let _e302 = scale[1u]; + let _e304 = scale[1u]; + let _e306 = (*moments_1); + param_130 = _e306.zw; + let _e309 = warped[1u]; + param_131 = _e309; + param_132 = (_e302 * _e304); + let _e310 = (*bleed_1); + param_133 = _e310; + let _e311 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_130), (¶m_131), (¶m_132), (¶m_133)); + negative = _e311; + let _e312 = positive; + let _e313 = negative; + return min(_e312, _e313); +} + +fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_5: ptr, light_2: ptr, lightIndex_1: ptr) -> f32 { var strength_1: f32; var cascadeCount: i32; var viewDistance: f32; @@ -17968,8 +18738,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_9: mat4x4; - var local_10: mat4x4; + var local_11: mat4x4; + var local_12: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -17983,17 +18753,17 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_11: f32; - var local_12: f32; + var local_13: f32; + var local_14: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; var softness: f32; - var lit_1: f32; + var lit_2: f32; var moments_2: vec4; - var param_127: vec4; - var param_128: f32; - var param_129: f32; + var param_134: vec4; + var param_135: f32; + var param_136: f32; var y_1: i32; var x_1: i32; var occluder: f32; @@ -18004,65 +18774,65 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var blockerCount: f32; var i_6: i32; var disc: vec2; - var param_130: i32; - var param_131: i32; - var param_132: f32; + var param_137: i32; + var param_138: i32; + var param_139: f32; var tapBias: f32; var occluder_1: f32; var gap: f32; var radius_2: f32; var i_7: i32; var disc_1: vec2; - var param_133: i32; - var param_134: i32; - var param_135: f32; + var param_140: i32; + var param_141: i32; + var param_142: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3857_: bool; - var phi_3868_: bool; - var phi_4029_: bool; - var phi_4036_: bool; - var phi_4043_: bool; - - let _e329 = frag_info.shadow_params[3u]; - strength_1 = _e329; - let _e330 = strength_1; - if (_e330 <= 0f) { + var phi_4028_: bool; + var phi_4039_: bool; + var phi_4200_: bool; + var phi_4207_: bool; + var phi_4214_: bool; + + let _e338 = frag_info.shadow_params[3u]; + strength_1 = _e338; + let _e339 = strength_1; + if (_e339 <= 0f) { return 1f; } - let _e332 = (*lightIndex_1); - let _e335 = frag_info.frame_params[2u]; - if (_e332 != i32((_e335 + 0.5f))) { + let _e341 = (*lightIndex_1); + let _e344 = frag_info.frame_params[2u]; + if (_e341 != i32((_e344 + 0.5f))) { return 1f; } - let _e341 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e341 + 0.5f)); - let _e344 = v_world_position_1; - let _e346 = frag_info.camera_position; - viewDistance = length((_e344 - _e346.xyz)); + let _e350 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e350 + 0.5f)); + let _e353 = v_world_position_1; + let _e355 = frag_info.camera_position; + viewDistance = length((_e353 - _e355.xyz)); cascade = 0i; - let _e350 = cascadeCount; - let _e351 = (_e350 > 1i); - phi_3857_ = _e351; - if _e351 { - let _e352 = viewDistance; - let _e355 = frag_info.shadow_cascades[0u]; - phi_3857_ = (_e352 > _e355); - } - let _e358 = phi_3857_; - if _e358 { - cascade = 1i; - } let _e359 = cascadeCount; - let _e360 = (_e359 > 2i); - phi_3868_ = _e360; + let _e360 = (_e359 > 1i); + phi_4028_ = _e360; if _e360 { let _e361 = viewDistance; - let _e364 = frag_info.shadow_cascades[1u]; - phi_3868_ = (_e361 > _e364); + let _e364 = frag_info.shadow_cascades[0u]; + phi_4028_ = (_e361 > _e364); } - let _e367 = phi_3868_; + let _e367 = phi_4028_; if _e367 { + cascade = 1i; + } + let _e368 = cascadeCount; + let _e369 = (_e368 > 2i); + phi_4039_ = _e369; + if _e369 { + let _e370 = viewDistance; + let _e373 = frag_info.shadow_cascades[1u]; + phi_4039_ = (_e370 > _e373); + } + let _e376 = phi_4039_; + if _e376 { cascade = 2i; } uv_5 = vec2(0f, 0f); @@ -18073,232 +18843,232 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e368 = attempt; - if (_e368 < 3i) { - let _e370 = cascade; - let _e371 = attempt; - which = (_e370 + _e371); - let _e373 = which; - let _e374 = cascadeCount; - if (_e373 >= _e374) { + let _e377 = attempt; + if (_e377 < 3i) { + let _e379 = cascade; + let _e380 = attempt; + which = (_e379 + _e380); + let _e382 = which; + let _e383 = cascadeCount; + if (_e382 >= _e383) { break; } - let _e376 = which; - if (_e376 == 0i) { - let _e379 = frag_info.shadow_matrix; - local_9 = _e379; + let _e385 = which; + if (_e385 == 0i) { + let _e388 = frag_info.shadow_matrix; + local_11 = _e388; } else { - let _e380 = which; - if (_e380 == 1i) { - let _e383 = frag_info.shadow_matrix_far; - local_10 = _e383; + let _e389 = which; + if (_e389 == 1i) { + let _e392 = frag_info.shadow_matrix_far; + local_12 = _e392; } else { - let _e385 = frag_info.shadow_matrix_farthest; - local_10 = _e385; + let _e394 = frag_info.shadow_matrix_farthest; + local_12 = _e394; } - let _e386 = local_10; - local_9 = _e386; - } - let _e387 = local_9; - matrix = _e387; - let _e390 = matrix[0][0u]; - let _e393 = matrix[1][0u]; - let _e396 = matrix[2][0u]; - axisX = vec3(_e390, _e393, _e396); - let _e400 = matrix[0][1u]; - let _e403 = matrix[1][1u]; - let _e406 = matrix[2][1u]; - axisY = vec3(_e400, _e403, _e406); - let _e408 = axisX; - rowX = max(length(_e408), 0.000001f); - let _e411 = axisY; - rowY = max(length(_e411), 0.000001f); - let _e416 = frag_info.shadow_cascades[3u]; - let _e418 = rowX; - texelMetres = ((2f * _e416) / _e418); - let _e422 = frag_info.shadow_cascades[3u]; - let _e425 = (*s_4).n; - let _e426 = axisX; - let _e429 = rowX; - let _e430 = rowX; - let _e434 = (*s_4).n; - let _e435 = axisY; - let _e438 = rowY; - let _e439 = rowY; - reach = ((3f * _e422) * ((abs(dot(_e425, _e426)) / (_e429 * _e430)) + (abs(dot(_e434, _e435)) / (_e438 * _e439)))); - let _e446 = frag_info.shadow_params[2u]; - let _e447 = texelMetres; - flatOffset = min(_e446, _e447); - let _e449 = v_world_position_1; - let _e451 = (*s_4).n; - let _e452 = flatOffset; - let _e453 = reach; - origin_1 = (_e449 + (_e451 * (_e452 + _e453))); - let _e457 = matrix; - let _e458 = origin_1; - lightSpace = (_e457 * vec4(_e458.x, _e458.y, _e458.z, 1f)); - let _e465 = lightSpace[3u]; - if (_e465 <= 0f) { + let _e395 = local_12; + local_11 = _e395; + } + let _e396 = local_11; + matrix = _e396; + let _e399 = matrix[0][0u]; + let _e402 = matrix[1][0u]; + let _e405 = matrix[2][0u]; + axisX = vec3(_e399, _e402, _e405); + let _e409 = matrix[0][1u]; + let _e412 = matrix[1][1u]; + let _e415 = matrix[2][1u]; + axisY = vec3(_e409, _e412, _e415); + let _e417 = axisX; + rowX = max(length(_e417), 0.000001f); + let _e420 = axisY; + rowY = max(length(_e420), 0.000001f); + let _e425 = frag_info.shadow_cascades[3u]; + let _e427 = rowX; + texelMetres = ((2f * _e425) / _e427); + let _e431 = frag_info.shadow_cascades[3u]; + let _e434 = (*s_5).n; + let _e435 = axisX; + let _e438 = rowX; + let _e439 = rowX; + let _e443 = (*s_5).n; + let _e444 = axisY; + let _e447 = rowY; + let _e448 = rowY; + reach = ((3f * _e431) * ((abs(dot(_e434, _e435)) / (_e438 * _e439)) + (abs(dot(_e443, _e444)) / (_e447 * _e448)))); + let _e455 = frag_info.shadow_params[2u]; + let _e456 = texelMetres; + flatOffset = min(_e455, _e456); + let _e458 = v_world_position_1; + let _e460 = (*s_5).n; + let _e461 = flatOffset; + let _e462 = reach; + origin_1 = (_e458 + (_e460 * (_e461 + _e462))); + let _e466 = matrix; + let _e467 = origin_1; + lightSpace = (_e466 * vec4(_e467.x, _e467.y, _e467.z, 1f)); + let _e474 = lightSpace[3u]; + if (_e474 <= 0f) { continue; } - let _e467 = lightSpace; - let _e470 = lightSpace[3u]; - candidate = (_e467.xyz / vec3(_e470)); - let _e474 = candidate[0u]; - let _e478 = candidate[1u]; - inTile = vec2(((_e474 * 0.5f) + 0.5f), (0.5f - (_e478 * 0.5f))); - let _e483 = inTile[0u]; - let _e484 = (_e483 < 0f); - phi_4029_ = _e484; - if !(_e484) { - let _e487 = inTile[0u]; - phi_4029_ = (_e487 > 1f); - } - let _e490 = phi_4029_; - phi_4036_ = _e490; - if !(_e490) { - let _e493 = inTile[1u]; - phi_4036_ = (_e493 < 0f); - } - let _e496 = phi_4036_; - phi_4043_ = _e496; - if !(_e496) { - let _e499 = inTile[1u]; - phi_4043_ = (_e499 > 1f); - } - let _e502 = phi_4043_; - if _e502 { + let _e476 = lightSpace; + let _e479 = lightSpace[3u]; + candidate = (_e476.xyz / vec3(_e479)); + let _e483 = candidate[0u]; + let _e487 = candidate[1u]; + inTile = vec2(((_e483 * 0.5f) + 0.5f), (0.5f - (_e487 * 0.5f))); + let _e492 = inTile[0u]; + let _e493 = (_e492 < 0f); + phi_4200_ = _e493; + if !(_e493) { + let _e496 = inTile[0u]; + phi_4200_ = (_e496 > 1f); + } + let _e499 = phi_4200_; + phi_4207_ = _e499; + if !(_e499) { + let _e502 = inTile[1u]; + phi_4207_ = (_e502 < 0f); + } + let _e505 = phi_4207_; + phi_4214_ = _e505; + if !(_e505) { + let _e508 = inTile[1u]; + phi_4214_ = (_e508 > 1f); + } + let _e511 = phi_4214_; + if _e511 { continue; } - let _e504 = candidate[2u]; - if (_e504 > 1f) { - let _e506 = which; - let _e507 = cascadeCount; - if (_e506 < (_e507 - 1i)) { + let _e513 = candidate[2u]; + if (_e513 > 1f) { + let _e515 = which; + let _e516 = cascadeCount; + if (_e515 < (_e516 - 1i)) { continue; } candidate[2u] = 1f; } - let _e512 = inTile[0u]; - let _e513 = which; - let _e516 = cascadeCount; - let _e520 = inTile[1u]; - uv_5 = vec2(((_e512 + f32(_e513)) / f32(_e516)), _e520); - let _e522 = candidate; - projected = _e522; - let _e523 = which; - cascade = _e523; - let _e524 = texelMetres; - cascadeTexel = _e524; - let _e527 = matrix[0][2u]; - let _e530 = matrix[1][2u]; - let _e533 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e527, _e530, _e533)), 0.000001f)); - let _e539 = (*s_4).n; - let _e542 = matrix[0][2u]; - let _e545 = matrix[1][2u]; - let _e548 = matrix[2][2u]; - let _e552 = cascadeDepth; - cosSlope = clamp((abs(dot(_e539, vec3(_e542, _e545, _e548))) * _e552), 0.1f, 1f); - let _e555 = texelMetres; - let _e556 = cosSlope; - let _e557 = cosSlope; - let _e563 = cosSlope; - let _e565 = cascadeDepth; - receiverSlope = (((_e555 * sqrt(max((1f - (_e556 * _e557)), 0f))) / _e563) / _e565); + let _e521 = inTile[0u]; + let _e522 = which; + let _e525 = cascadeCount; + let _e529 = inTile[1u]; + uv_5 = vec2(((_e521 + f32(_e522)) / f32(_e525)), _e529); + let _e531 = candidate; + projected = _e531; + let _e532 = which; + cascade = _e532; + let _e533 = texelMetres; + cascadeTexel = _e533; + let _e536 = matrix[0][2u]; + let _e539 = matrix[1][2u]; + let _e542 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e536, _e539, _e542)), 0.000001f)); + let _e548 = (*s_5).n; + let _e551 = matrix[0][2u]; + let _e554 = matrix[1][2u]; + let _e557 = matrix[2][2u]; + let _e561 = cascadeDepth; + cosSlope = clamp((abs(dot(_e548, vec3(_e551, _e554, _e557))) * _e561), 0.1f, 1f); + let _e564 = texelMetres; + let _e565 = cosSlope; + let _e566 = cosSlope; + let _e572 = cosSlope; + let _e574 = cascadeDepth; + receiverSlope = (((_e564 * sqrt(max((1f - (_e565 * _e566)), 0f))) / _e572) / _e574); found = true; break; } else { break; } continuing { - let _e567 = attempt; - attempt = (_e567 + 1i); + let _e576 = attempt; + attempt = (_e576 + 1i); } } - let _e569 = found; - if !(_e569) { + let _e578 = found; + if !(_e578) { return 1f; } - let _e571 = cascade; - if (_e571 == 0i) { - let _e575 = frag_info.shadow_bias[0u]; - local_11 = _e575; + let _e580 = cascade; + if (_e580 == 0i) { + let _e584 = frag_info.shadow_bias[0u]; + local_13 = _e584; } else { - let _e576 = cascade; - if (_e576 == 1i) { - let _e580 = frag_info.shadow_bias[1u]; - local_12 = _e580; + let _e585 = cascade; + if (_e585 == 1i) { + let _e589 = frag_info.shadow_bias[1u]; + local_14 = _e589; } else { - let _e583 = frag_info.shadow_bias[2u]; - local_12 = _e583; - } - let _e584 = local_12; - local_11 = _e584; - } - let _e585 = local_11; - bias = _e585; - let _e588 = frag_info.shadow_params[0u]; - let _e591 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e588, _e591); - let _e593 = cascade; - let _e595 = cascadeCount; - let _e599 = texel_1; - tileLo = (vec2((f32(_e593) / f32(_e595)), 0f) + (_e599 * 0.5f)); + let _e592 = frag_info.shadow_bias[2u]; + local_14 = _e592; + } + let _e593 = local_14; + local_13 = _e593; + } + let _e594 = local_13; + bias = _e594; + let _e597 = frag_info.shadow_params[0u]; + let _e600 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e597, _e600); let _e602 = cascade; - let _e605 = cascadeCount; - let _e609 = texel_1; - tileHi = (vec2((f32((_e602 + 1i)) / f32(_e605)), 1f) - (_e609 * 0.5f)); - let _e614 = frag_info.ambient_ground[3u]; - softness = _e614; - lit_1 = 0f; - let _e615 = softness; - if (_e615 < 0f) { - let _e617 = uv_5; - let _e618 = tileLo; - let _e619 = tileHi; - let _e621 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e617, _e618, _e619), 0f); - moments_2 = _e621; - let _e623 = projected[2u]; - let _e624 = bias; - let _e626 = softness; - let _e630 = moments_2; - param_127 = _e630; - param_128 = (_e623 - _e624); - param_129 = clamp((-(_e626) - 1f), 0f, 0.95f); - let _e631 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_127), (¶m_128), (¶m_129)); - lit_1 = _e631; + let _e604 = cascadeCount; + let _e608 = texel_1; + tileLo = (vec2((f32(_e602) / f32(_e604)), 0f) + (_e608 * 0.5f)); + let _e611 = cascade; + let _e614 = cascadeCount; + let _e618 = texel_1; + tileHi = (vec2((f32((_e611 + 1i)) / f32(_e614)), 1f) - (_e618 * 0.5f)); + let _e623 = frag_info.ambient_ground[3u]; + softness = _e623; + lit_2 = 0f; + let _e624 = softness; + if (_e624 < 0f) { + let _e626 = uv_5; + let _e627 = tileLo; + let _e628 = tileHi; + let _e630 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e626, _e627, _e628), 0f); + moments_2 = _e630; + let _e632 = projected[2u]; + let _e633 = bias; + let _e635 = softness; + let _e639 = moments_2; + param_134 = _e639; + param_135 = (_e632 - _e633); + param_136 = clamp((-(_e635) - 1f), 0f, 0.95f); + let _e640 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_134), (¶m_135), (¶m_136)); + lit_2 = _e640; } else { - let _e632 = softness; - if (_e632 <= 0f) { + let _e641 = softness; + if (_e641 <= 0f) { y_1 = -1i; loop { - let _e634 = y_1; - if (_e634 <= 1i) { + let _e643 = y_1; + if (_e643 <= 1i) { x_1 = -1i; loop { - let _e636 = x_1; - if (_e636 <= 1i) { - let _e638 = uv_5; - let _e639 = x_1; - let _e641 = y_1; - let _e644 = texel_1; - let _e647 = tileLo; - let _e648 = tileHi; - let _e650 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e638 + (vec2(f32(_e639), f32(_e641)) * _e644)), _e647, _e648), 0f); - occluder = _e650.x; - let _e653 = projected[2u]; - let _e654 = bias; - let _e656 = occluder; - let _e659 = lit_1; - lit_1 = (_e659 + select(1f, 0f, ((_e653 - _e654) > _e656))); + let _e645 = x_1; + if (_e645 <= 1i) { + let _e647 = uv_5; + let _e648 = x_1; + let _e650 = y_1; + let _e653 = texel_1; + let _e656 = tileLo; + let _e657 = tileHi; + let _e659 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e647 + (vec2(f32(_e648), f32(_e650)) * _e653)), _e656, _e657), 0f); + occluder = _e659.x; + let _e662 = projected[2u]; + let _e663 = bias; + let _e665 = occluder; + let _e668 = lit_2; + lit_2 = (_e668 + select(1f, 0f, ((_e662 - _e663) > _e665))); continue; } else { break; } continuing { - let _e661 = x_1; - x_1 = (_e661 + 1i); + let _e670 = x_1; + x_1 = (_e670 + 1i); } } continue; @@ -18306,145 +19076,145 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e663 = y_1; - y_1 = (_e663 + 1i); + let _e672 = y_1; + y_1 = (_e672 + 1i); } } - let _e665 = lit_1; - lit_1 = (_e665 * 0.11111111f); + let _e674 = lit_2; + lit_2 = (_e674 * 0.11111111f); } else { - let _e667 = softness; - spread = tan(min(_e667, 0.5f)); - let _e670 = ShadowNoise_u0028_(); - turn_1 = (_e670 * 6.2831855f); - let _e672 = spread; - let _e674 = projected[2u]; - let _e676 = cascadeDepth; - let _e678 = cascadeTexel; - searchRadius = clamp((((_e672 * _e674) * _e676) / _e678), 1f, 16f); + let _e676 = softness; + spread = tan(min(_e676, 0.5f)); + let _e679 = ShadowNoise_u0028_(); + turn_1 = (_e679 * 6.2831855f); + let _e681 = spread; + let _e683 = projected[2u]; + let _e685 = cascadeDepth; + let _e687 = cascadeTexel; + searchRadius = clamp((((_e681 * _e683) * _e685) / _e687), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_6 = 0i; loop { - let _e681 = i_6; - if (_e681 < 16i) { - let _e683 = i_6; - param_130 = _e683; - param_131 = 16i; - let _e684 = turn_1; - param_132 = _e684; - let _e685 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_130), (¶m_131), (¶m_132)); - disc = _e685; - let _e686 = bias; - let _e687 = disc; - let _e689 = searchRadius; - let _e693 = receiverSlope; - tapBias = (_e686 + (max(((length(_e687) * _e689) - 1.5f), 0f) * _e693)); - let _e696 = uv_5; - let _e697 = disc; - let _e698 = texel_1; - let _e700 = searchRadius; - let _e703 = tileLo; - let _e704 = tileHi; - let _e706 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e696 + ((_e697 * _e698) * _e700)), _e703, _e704), 0f); - occluder_1 = _e706.x; - let _e709 = projected[2u]; - let _e710 = tapBias; - let _e712 = occluder_1; - if ((_e709 - _e710) > _e712) { - let _e714 = occluder_1; - let _e715 = blockerSum; - blockerSum = (_e715 + _e714); - let _e717 = blockerCount; - blockerCount = (_e717 + 1f); + let _e690 = i_6; + if (_e690 < 16i) { + let _e692 = i_6; + param_137 = _e692; + param_138 = 16i; + let _e693 = turn_1; + param_139 = _e693; + let _e694 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_137), (¶m_138), (¶m_139)); + disc = _e694; + let _e695 = bias; + let _e696 = disc; + let _e698 = searchRadius; + let _e702 = receiverSlope; + tapBias = (_e695 + (max(((length(_e696) * _e698) - 1.5f), 0f) * _e702)); + let _e705 = uv_5; + let _e706 = disc; + let _e707 = texel_1; + let _e709 = searchRadius; + let _e712 = tileLo; + let _e713 = tileHi; + let _e715 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e705 + ((_e706 * _e707) * _e709)), _e712, _e713), 0f); + occluder_1 = _e715.x; + let _e718 = projected[2u]; + let _e719 = tapBias; + let _e721 = occluder_1; + if ((_e718 - _e719) > _e721) { + let _e723 = occluder_1; + let _e724 = blockerSum; + blockerSum = (_e724 + _e723); + let _e726 = blockerCount; + blockerCount = (_e726 + 1f); } continue; } else { break; } continuing { - let _e719 = i_6; - i_6 = (_e719 + 1i); + let _e728 = i_6; + i_6 = (_e728 + 1i); } } - let _e721 = blockerCount; - if (_e721 <= 0f) { + let _e730 = blockerCount; + if (_e730 <= 0f) { return 1f; } - let _e724 = projected[2u]; - let _e725 = blockerSum; - let _e726 = blockerCount; - let _e730 = cascadeDepth; - gap = (max((_e724 - (_e725 / _e726)), 0f) * _e730); - let _e732 = spread; - let _e733 = gap; - let _e735 = cascadeTexel; - radius_2 = clamp(((_e732 * _e733) / _e735), 1f, 16f); + let _e733 = projected[2u]; + let _e734 = blockerSum; + let _e735 = blockerCount; + let _e739 = cascadeDepth; + gap = (max((_e733 - (_e734 / _e735)), 0f) * _e739); + let _e741 = spread; + let _e742 = gap; + let _e744 = cascadeTexel; + radius_2 = clamp(((_e741 * _e742) / _e744), 1f, 16f); i_7 = 0i; loop { - let _e738 = i_7; - if (_e738 < 16i) { - let _e740 = turn_1; - let _e742 = i_7; - param_133 = _e742; - param_134 = 16i; - param_135 = (_e740 + 1f); - let _e743 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_133), (¶m_134), (¶m_135)); - disc_1 = _e743; - let _e744 = bias; - let _e745 = disc_1; - let _e747 = radius_2; - let _e751 = receiverSlope; - tapBias_1 = (_e744 + (max(((length(_e745) * _e747) - 1.5f), 0f) * _e751)); - let _e754 = uv_5; - let _e755 = disc_1; - let _e756 = texel_1; - let _e758 = radius_2; - let _e761 = tileLo; - let _e762 = tileHi; - let _e764 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e754 + ((_e755 * _e756) * _e758)), _e761, _e762), 0f); - occluder_2 = _e764.x; - let _e767 = projected[2u]; - let _e768 = tapBias_1; - let _e770 = occluder_2; - let _e773 = lit_1; - lit_1 = (_e773 + select(1f, 0f, ((_e767 - _e768) > _e770))); + let _e747 = i_7; + if (_e747 < 16i) { + let _e749 = turn_1; + let _e751 = i_7; + param_140 = _e751; + param_141 = 16i; + param_142 = (_e749 + 1f); + let _e752 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_140), (¶m_141), (¶m_142)); + disc_1 = _e752; + let _e753 = bias; + let _e754 = disc_1; + let _e756 = radius_2; + let _e760 = receiverSlope; + tapBias_1 = (_e753 + (max(((length(_e754) * _e756) - 1.5f), 0f) * _e760)); + let _e763 = uv_5; + let _e764 = disc_1; + let _e765 = texel_1; + let _e767 = radius_2; + let _e770 = tileLo; + let _e771 = tileHi; + let _e773 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e763 + ((_e764 * _e765) * _e767)), _e770, _e771), 0f); + occluder_2 = _e773.x; + let _e776 = projected[2u]; + let _e777 = tapBias_1; + let _e779 = occluder_2; + let _e782 = lit_2; + lit_2 = (_e782 + select(1f, 0f, ((_e776 - _e777) > _e779))); continue; } else { break; } continuing { - let _e775 = i_7; - i_7 = (_e775 + 1i); + let _e784 = i_7; + i_7 = (_e784 + 1i); } } - let _e777 = lit_1; - lit_1 = (_e777 * 0.0625f); + let _e786 = lit_2; + lit_2 = (_e786 * 0.0625f); } } - let _e779 = lit_1; - let _e780 = strength_1; - return mix(1f, _e779, clamp(_e780, 0f, 1f)); + let _e788 = lit_2; + let _e789 = strength_1; + return mix(1f, _e788, clamp(_e789, 0f, 1f)); } -fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_5: ptr, light_3: ptr, index: ptr) -> f32 { - var param_136: Surface; - var param_137: LightSample; - var param_138: i32; +fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_6: ptr, light_3: ptr, index: ptr) -> f32 { + var param_143: Surface; + var param_144: LightSample; + var param_145: i32; - let _e267 = (*s_5); - param_136 = _e267; - let _e268 = (*light_3); - param_137 = _e268; - let _e269 = (*index); - param_138 = _e269; - let _e270 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_136), (¶m_137), (¶m_138)); - return _e270; + let _e276 = (*s_6); + param_143 = _e276; + let _e277 = (*light_3); + param_144 = _e277; + let _e278 = (*index); + param_145 = _e278; + let _e279 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_143), (¶m_144), (¶m_145)); + return _e279; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e262 = (*index_1); - return (_e262 < 8i); + let _e271 = (*index_1); + return (_e271 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -18452,26 +19222,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e266 = (*distance_1); - let _e267 = (*distance_1); - attenuation = (1f / max((_e266 * _e267), 0.0001f)); - let _e271 = (*range_4); - if (_e271 > 0f) { - let _e273 = (*distance_1); - let _e274 = (*range_4); - ratio = (_e273 / _e274); - let _e276 = ratio; - let _e277 = ratio; - let _e279 = ratio; - let _e281 = ratio; - window = clamp((1f - (((_e276 * _e277) * _e279) * _e281)), 0f, 1f); - let _e285 = window; - let _e286 = window; - let _e288 = attenuation; - attenuation = (_e288 * (_e285 * _e286)); - } - let _e290 = attenuation; - return _e290; + let _e275 = (*distance_1); + let _e276 = (*distance_1); + attenuation = (1f / max((_e275 * _e276), 0.0001f)); + let _e280 = (*range_4); + if (_e280 > 0f) { + let _e282 = (*distance_1); + let _e283 = (*range_4); + ratio = (_e282 / _e283); + let _e285 = ratio; + let _e286 = ratio; + let _e288 = ratio; + let _e290 = ratio; + window = clamp((1f - (((_e285 * _e286) * _e288) * _e290)), 0f, 1f); + let _e294 = window; + let _e295 = window; + let _e297 = attenuation; + attenuation = (_e297 * (_e294 * _e295)); + } + let _e299 = attenuation; + return _e299; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -18481,82 +19251,82 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_13: vec3; + var local_15: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; var u: f32; var v_1: f32; - let _e278 = (*halfWidth); - let _e279 = (*halfHeight); - n_3 = cross(_e278, _e279); - let _e281 = n_3; - nLen = length(_e281); - let _e283 = nLen; - if (_e283 < 0.000000000001f) { - let _e285 = (*centre); - return _e285; - } - let _e286 = nLen; - let _e287 = n_3; - n_3 = (_e287 / vec3(_e286)); - let _e290 = (*centre); - let _e291 = (*world_2); - toPlane = (_e290 - _e291); - let _e293 = (*mirror); - let _e294 = n_3; - denom = dot(_e293, _e294); - let _e296 = denom; - if (abs(_e296) < 0.00001f) { - let _e299 = toPlane; - let _e300 = n_3; - let _e301 = toPlane; - let _e302 = n_3; - onPlane = (_e299 - (_e300 * dot(_e301, _e302))); + let _e287 = (*halfWidth); + let _e288 = (*halfHeight); + n_3 = cross(_e287, _e288); + let _e290 = n_3; + nLen = length(_e290); + let _e292 = nLen; + if (_e292 < 0.000000000001f) { + let _e294 = (*centre); + return _e294; + } + let _e295 = nLen; + let _e296 = n_3; + n_3 = (_e296 / vec3(_e295)); + let _e299 = (*centre); + let _e300 = (*world_2); + toPlane = (_e299 - _e300); + let _e302 = (*mirror); + let _e303 = n_3; + denom = dot(_e302, _e303); + let _e305 = denom; + if (abs(_e305) < 0.00001f) { + let _e308 = toPlane; + let _e309 = n_3; + let _e310 = toPlane; + let _e311 = n_3; + onPlane = (_e308 - (_e309 * dot(_e310, _e311))); } else { - let _e306 = toPlane; - let _e307 = n_3; - let _e309 = denom; - t_1 = (dot(_e306, _e307) / _e309); - let _e311 = t_1; - if (_e311 > 0f) { - let _e313 = (*mirror); - let _e314 = t_1; - local_13 = (_e313 * _e314); + let _e315 = toPlane; + let _e316 = n_3; + let _e318 = denom; + t_1 = (dot(_e315, _e316) / _e318); + let _e320 = t_1; + if (_e320 > 0f) { + let _e322 = (*mirror); + let _e323 = t_1; + local_15 = (_e322 * _e323); } else { - let _e316 = toPlane; - let _e317 = n_3; - let _e318 = toPlane; - let _e319 = n_3; - local_13 = (_e316 - (_e317 * dot(_e318, _e319))); - } - let _e323 = local_13; - onPlane = _e323; - } - let _e324 = onPlane; - let _e325 = toPlane; - offset_2 = (_e324 - _e325); - let _e327 = (*halfWidth); - let _e328 = (*halfWidth); - wLen2_ = max(dot(_e327, _e328), 0.000000000001f); - let _e331 = (*halfHeight); - let _e332 = (*halfHeight); - hLen2_ = max(dot(_e331, _e332), 0.000000000001f); - let _e335 = offset_2; + let _e325 = toPlane; + let _e326 = n_3; + let _e327 = toPlane; + let _e328 = n_3; + local_15 = (_e325 - (_e326 * dot(_e327, _e328))); + } + let _e332 = local_15; + onPlane = _e332; + } + let _e333 = onPlane; + let _e334 = toPlane; + offset_2 = (_e333 - _e334); let _e336 = (*halfWidth); - let _e338 = wLen2_; - u = clamp((dot(_e335, _e336) / _e338), -1f, 1f); - let _e341 = offset_2; - let _e342 = (*halfHeight); - let _e344 = hLen2_; - v_1 = clamp((dot(_e341, _e342) / _e344), -1f, 1f); - let _e347 = (*centre); - let _e348 = (*halfWidth); - let _e349 = u; - let _e352 = (*halfHeight); - let _e353 = v_1; - return ((_e347 + (_e348 * _e349)) + (_e352 * _e353)); + let _e337 = (*halfWidth); + wLen2_ = max(dot(_e336, _e337), 0.000000000001f); + let _e340 = (*halfHeight); + let _e341 = (*halfHeight); + hLen2_ = max(dot(_e340, _e341), 0.000000000001f); + let _e344 = offset_2; + let _e345 = (*halfWidth); + let _e347 = wLen2_; + u = clamp((dot(_e344, _e345) / _e347), -1f, 1f); + let _e350 = offset_2; + let _e351 = (*halfHeight); + let _e353 = hLen2_; + v_1 = clamp((dot(_e350, _e351) / _e353), -1f, 1f); + let _e356 = (*centre); + let _e357 = (*halfWidth); + let _e358 = u; + let _e361 = (*halfHeight); + let _e362 = v_1; + return ((_e356 + (_e357 * _e358)) + (_e361 * _e362)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr>, b_1: ptr>, n_4: ptr>) -> f32 { @@ -18565,34 +19335,34 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> var angle_2: f32; var axis_1: vec3; var len_2: f32; - var local_14: f32; + var local_16: f32; - let _e270 = (*a_3); - let _e271 = (*a_3); - ua = (_e270 / vec3(max(length(_e271), 0.000000000001f))); - let _e276 = (*b_1); - let _e277 = (*b_1); - ub = (_e276 / vec3(max(length(_e277), 0.000000000001f))); - let _e282 = ua; - let _e283 = ub; - angle_2 = acos(clamp(dot(_e282, _e283), -1f, 1f)); - let _e287 = ua; - let _e288 = ub; - axis_1 = cross(_e287, _e288); - let _e290 = axis_1; - len_2 = length(_e290); - let _e292 = len_2; - if (_e292 > 0.000001f) { - let _e294 = angle_2; - let _e295 = axis_1; - let _e296 = (*n_4); - let _e299 = len_2; - local_14 = ((_e294 * dot(_e295, _e296)) / _e299); + let _e279 = (*a_3); + let _e280 = (*a_3); + ua = (_e279 / vec3(max(length(_e280), 0.000000000001f))); + let _e285 = (*b_1); + let _e286 = (*b_1); + ub = (_e285 / vec3(max(length(_e286), 0.000000000001f))); + let _e291 = ua; + let _e292 = ub; + angle_2 = acos(clamp(dot(_e291, _e292), -1f, 1f)); + let _e296 = ua; + let _e297 = ub; + axis_1 = cross(_e296, _e297); + let _e299 = axis_1; + len_2 = length(_e299); + let _e301 = len_2; + if (_e301 > 0.000001f) { + let _e303 = angle_2; + let _e304 = axis_1; + let _e305 = (*n_4); + let _e308 = len_2; + local_16 = ((_e303 * dot(_e304, _e305)) / _e308); } else { - local_14 = 0f; + local_16 = 0f; } - let _e301 = local_14; - return _e301; + let _e310 = local_16; + return _e310; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr, 4>>, n_5: ptr>) -> f32 { @@ -18602,273 +19372,273 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr; var b_2: vec3; - var local_15: i32; + var local_17: i32; var ha: f32; var hb: f32; var d_5: f32; var q: vec3; - var local_16: f32; + var local_18: f32; var aAbove: bool; var bAbove: bool; - var local_17: f32; - var param_139: vec3; - var param_140: vec3; - var param_141: vec3; - var param_142: vec3; - var param_143: vec3; - var param_144: vec3; + var local_19: f32; + var param_146: vec3; + var param_147: vec3; + var param_148: vec3; + var param_149: vec3; + var param_150: vec3; + var param_151: vec3; total = 0f; exit = vec3(0f, 0f, 0f); entry = vec3(0f, 0f, 0f); i_8 = 0i; loop { - let _e284 = i_8; - if (_e284 < 4i) { - let _e286 = i_8; - let _e288 = (*corners_1)[_e286]; - a_4 = _e288; - let _e289 = i_8; - if (_e289 == 3i) { - local_15 = 0i; + let _e293 = i_8; + if (_e293 < 4i) { + let _e295 = i_8; + let _e297 = (*corners_1)[_e295]; + a_4 = _e297; + let _e298 = i_8; + if (_e298 == 3i) { + local_17 = 0i; } else { - let _e291 = i_8; - local_15 = (_e291 + 1i); - } - let _e293 = local_15; - let _e295 = (*corners_1)[_e293]; - b_2 = _e295; - let _e296 = a_4; - let _e297 = (*n_5); - ha = dot(_e296, _e297); - let _e299 = b_2; - let _e300 = (*n_5); - hb = dot(_e299, _e300); - let _e302 = ha; - let _e303 = hb; - d_5 = (_e302 - _e303); + let _e300 = i_8; + local_17 = (_e300 + 1i); + } + let _e302 = local_17; + let _e304 = (*corners_1)[_e302]; + b_2 = _e304; let _e305 = a_4; - let _e306 = b_2; - let _e307 = a_4; - let _e309 = d_5; - if (abs(_e309) > 0.000000000001f) { - let _e312 = ha; - let _e313 = d_5; - local_16 = (_e312 / _e313); + let _e306 = (*n_5); + ha = dot(_e305, _e306); + let _e308 = b_2; + let _e309 = (*n_5); + hb = dot(_e308, _e309); + let _e311 = ha; + let _e312 = hb; + d_5 = (_e311 - _e312); + let _e314 = a_4; + let _e315 = b_2; + let _e316 = a_4; + let _e318 = d_5; + if (abs(_e318) > 0.000000000001f) { + let _e321 = ha; + let _e322 = d_5; + local_18 = (_e321 / _e322); } else { - local_16 = 0f; - } - let _e315 = local_16; - q = (_e305 + ((_e306 - _e307) * _e315)); - let _e318 = ha; - aAbove = (_e318 > 0f); - let _e320 = hb; - bAbove = (_e320 > 0f); - let _e322 = aAbove; - let _e323 = bAbove; - if (_e322 || _e323) { - let _e325 = aAbove; - let _e326 = a_4; - let _e327 = q; - let _e330 = bAbove; - let _e331 = b_2; - let _e332 = q; - param_139 = select(_e327, _e326, vec3(_e325)); - param_140 = select(_e332, _e331, vec3(_e330)); - let _e335 = (*n_5); - param_141 = _e335; - let _e336 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_139), (¶m_140), (¶m_141)); - local_17 = _e336; + local_18 = 0f; + } + let _e324 = local_18; + q = (_e314 + ((_e315 - _e316) * _e324)); + let _e327 = ha; + aAbove = (_e327 > 0f); + let _e329 = hb; + bAbove = (_e329 > 0f); + let _e331 = aAbove; + let _e332 = bAbove; + if (_e331 || _e332) { + let _e334 = aAbove; + let _e335 = a_4; + let _e336 = q; + let _e339 = bAbove; + let _e340 = b_2; + let _e341 = q; + param_146 = select(_e336, _e335, vec3(_e334)); + param_147 = select(_e341, _e340, vec3(_e339)); + let _e344 = (*n_5); + param_148 = _e344; + let _e345 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_146), (¶m_147), (¶m_148)); + local_19 = _e345; } else { - local_17 = 0f; - } - let _e337 = local_17; - let _e338 = total; - total = (_e338 + _e337); - let _e340 = aAbove; - let _e341 = bAbove; - let _e344 = q; - let _e345 = exit; - exit = select(_e345, _e344, vec3((_e340 && !(_e341)))); - let _e348 = aAbove; + local_19 = 0f; + } + let _e346 = local_19; + let _e347 = total; + total = (_e347 + _e346); + let _e349 = aAbove; let _e350 = bAbove; - let _e352 = q; - let _e353 = entry; - entry = select(_e353, _e352, vec3((!(_e348) && _e350))); + let _e353 = q; + let _e354 = exit; + exit = select(_e354, _e353, vec3((_e349 && !(_e350)))); + let _e357 = aAbove; + let _e359 = bAbove; + let _e361 = q; + let _e362 = entry; + entry = select(_e362, _e361, vec3((!(_e357) && _e359))); continue; } else { break; } continuing { - let _e356 = i_8; - i_8 = (_e356 + 1i); + let _e365 = i_8; + i_8 = (_e365 + 1i); } } - let _e358 = exit; - param_142 = _e358; - let _e359 = entry; - param_143 = _e359; - let _e360 = (*n_5); - param_144 = _e360; - let _e361 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_142), (¶m_143), (¶m_144)); - let _e362 = total; - total = (_e362 + _e361); - let _e364 = total; - return max((-(_e364) * 0.5f), 0f); + let _e367 = exit; + param_149 = _e367; + let _e368 = entry; + param_150 = _e368; + let _e369 = (*n_5); + param_151 = _e369; + let _e370 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_149), (¶m_150), (¶m_151)); + let _e371 = total; + total = (_e371 + _e370); + let _e373 = total; + return max((-(_e373) * 0.5f), 0f); } -fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { +fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_2: ptr) -> f32 { var lane: i32; - var local_18: f32; - var local_19: f32; var local_20: f32; + var local_21: f32; + var local_22: f32; - let _e267 = (*slot_1); - let _e268 = (*slot_1); - lane = (_e267 - ((_e268 / 4i) * 4i)); - let _e272 = lane; - if (_e272 == 0i) { - let _e275 = (*four)[0u]; - local_18 = _e275; + let _e276 = (*slot_2); + let _e277 = (*slot_2); + lane = (_e276 - ((_e277 / 4i) * 4i)); + let _e281 = lane; + if (_e281 == 0i) { + let _e284 = (*four)[0u]; + local_20 = _e284; } else { - let _e276 = lane; - if (_e276 == 1i) { - let _e279 = (*four)[1u]; - local_19 = _e279; + let _e285 = lane; + if (_e285 == 1i) { + let _e288 = (*four)[1u]; + local_21 = _e288; } else { - let _e280 = lane; - if (_e280 == 2i) { - let _e283 = (*four)[2u]; - local_20 = _e283; + let _e289 = lane; + if (_e289 == 2i) { + let _e292 = (*four)[2u]; + local_22 = _e292; } else { - let _e285 = (*four)[3u]; - local_20 = _e285; + let _e294 = (*four)[3u]; + local_22 = _e294; } - let _e286 = local_20; - local_19 = _e286; + let _e295 = local_22; + local_21 = _e295; } - let _e287 = local_19; - local_18 = _e287; + let _e296 = local_21; + local_20 = _e296; } - let _e288 = local_18; - return _e288; + let _e297 = local_20; + return _e297; } -fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { - var param_145: vec4; - var param_146: i32; +fn LightListScale_u0028_i1_u003b(slot_3: ptr) -> f32 { + var param_152: vec4; + var param_153: i32; - let _e264 = (*slot_2); - let _e268 = light_list_info.scales[(_e264 / 4i)]; - param_145 = _e268; - let _e269 = (*slot_2); - param_146 = _e269; - let _e270 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_145), (¶m_146)); - return _e270; + let _e273 = (*slot_3); + let _e277 = light_list_info.scales[(_e273 / 4i)]; + param_152 = _e277; + let _e278 = (*slot_3); + param_153 = _e278; + let _e279 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_152), (¶m_153)); + return _e279; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_5: vec4; var b_3: vec4; - let _e266 = light_list_info.slot_rows[0i]; - let _e267 = (*row); - a_5 = abs((_e266 - vec4(_e267))); - let _e273 = light_list_info.slot_rows[1i]; - let _e274 = (*row); - b_3 = abs((_e273 - vec4(_e274))); - let _e279 = a_5[0u]; - let _e281 = a_5[1u]; - let _e284 = a_5[2u]; - let _e286 = a_5[3u]; - let _e290 = b_3[0u]; - let _e292 = b_3[1u]; - let _e295 = b_3[2u]; - let _e297 = b_3[3u]; - return (min(min(min(_e279, _e281), min(_e284, _e286)), min(min(_e290, _e292), min(_e295, _e297))) < 0.5f); -} - -fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { - var param_147: vec4; - var param_148: i32; - - let _e264 = (*slot_3); - let _e268 = light_list_info.indices[(_e264 / 4i)]; - param_147 = _e268; - let _e269 = (*slot_3); - param_148 = _e269; - let _e270 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_147), (¶m_148)); - return _e270; + let _e275 = light_list_info.slot_rows[0i]; + let _e276 = (*row); + a_5 = abs((_e275 - vec4(_e276))); + let _e282 = light_list_info.slot_rows[1i]; + let _e283 = (*row); + b_3 = abs((_e282 - vec4(_e283))); + let _e288 = a_5[0u]; + let _e290 = a_5[1u]; + let _e293 = a_5[2u]; + let _e295 = a_5[3u]; + let _e299 = b_3[0u]; + let _e301 = b_3[1u]; + let _e304 = b_3[2u]; + let _e306 = b_3[3u]; + return (min(min(min(_e288, _e290), min(_e293, _e295)), min(min(_e299, _e301), min(_e304, _e306))) < 0.5f); +} + +fn LightListRow_u0028_i1_u003b(slot_4: ptr) -> f32 { + var param_154: vec4; + var param_155: i32; + + let _e273 = (*slot_4); + let _e277 = light_list_info.indices[(_e273 / 4i)]; + param_154 = _e277; + let _e278 = (*slot_4); + param_155 = _e278; + let _e279 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_154), (¶m_155)); + return _e279; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e263 = (*texel_2); - let _e267 = light_list_info.list[1u]; - let _e269 = (*row_1); - let _e273 = light_list_info.list[2u]; - let _e276 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e263 + 0.5f) * _e267), ((_e269 + 0.5f) * _e273)), 0f); - return _e276; + let _e272 = (*texel_2); + let _e276 = light_list_info.list[1u]; + let _e278 = (*row_1); + let _e282 = light_list_info.list[2u]; + let _e285 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e272 + 0.5f) * _e276), ((_e278 + 0.5f) * _e282)), 0f); + return _e285; } -fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { +fn ClusterRow_u0028_i1_u003b(slot_5: ptr) -> f32 { var entry_1: f32; var row_2: f32; var within: f32; var texel_3: f32; var four_1: vec4; - var param_149: f32; - var param_150: f32; - var param_151: vec4; - var param_152: i32; - - let _e271 = g_cluster_offset; - let _e272 = (*slot_4); - entry_1 = (_e271 + f32(_e272)); - let _e275 = entry_1; - row_2 = floor((_e275 / 16f)); - let _e278 = entry_1; - let _e279 = row_2; - within = (_e278 - (_e279 * 16f)); - let _e282 = within; - texel_3 = floor((_e282 / 4f)); - let _e287 = light_list_info.cluster_depth[3u]; + var param_156: f32; + var param_157: f32; + var param_158: vec4; + var param_159: i32; + + let _e280 = g_cluster_offset; + let _e281 = (*slot_5); + entry_1 = (_e280 + f32(_e281)); + let _e284 = entry_1; + row_2 = floor((_e284 / 16f)); + let _e287 = entry_1; let _e288 = row_2; - let _e290 = texel_3; - param_149 = _e290; - param_150 = (_e287 + _e288); - let _e291 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_149), (¶m_150)); - four_1 = _e291; - let _e292 = within; - let _e293 = texel_3; - let _e298 = four_1; - param_151 = _e298; - param_152 = i32(((_e292 - (_e293 * 4f)) + 0.5f)); - let _e299 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_151), (¶m_152)); - return _e299; + within = (_e287 - (_e288 * 16f)); + let _e291 = within; + texel_3 = floor((_e291 / 4f)); + let _e296 = light_list_info.cluster_depth[3u]; + let _e297 = row_2; + let _e299 = texel_3; + param_156 = _e299; + param_157 = (_e296 + _e297); + let _e300 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_156), (¶m_157)); + four_1 = _e300; + let _e301 = within; + let _e302 = texel_3; + let _e307 = four_1; + param_158 = _e307; + param_159 = i32(((_e301 - (_e302 * 4f)) + 0.5f)); + let _e308 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_158), (¶m_159)); + return _e308; } fn Clustered_u0028_() -> bool { - let _e263 = light_list_info.cluster_grid[3u]; - return (_e263 > 0.5f); + let _e272 = light_list_info.cluster_grid[3u]; + return (_e272 > 0.5f); } -fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_6: ptr) -> LightSample { +fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_7: ptr) -> LightSample { var light_4: LightSample; var position: vec4; var color_1: vec4; var direction: vec4; var cone: vec4; - var slot_5: i32; + var slot_6: i32; var clustered: bool; var listRow: f32; - var local_21: f32; - var param_153: i32; - var param_154: i32; + var local_23: f32; + var param_160: i32; + var param_161: i32; var v_2: f32; var u_1: f32; - var local_22: f32; - var param_155: f32; - var param_156: i32; + var local_24: f32; + var param_162: f32; + var param_163: i32; var type_45: f32; var halfWidth_1: vec3; var halfHeight_1: vec3; @@ -18876,310 +19646,310 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_2: array, 4>; var behind: bool; var formFactor: f32; - var local_23: f32; - var param_157: array, 4>; - var param_158: vec3; + var local_25: f32; + var param_164: array, 4>; + var param_165: vec3; var area: f32; var radiance: f32; - var local_24: f32; + var local_26: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; var mirror_1: vec3; var representative: vec3; - var param_159: vec3; - var param_160: vec3; - var param_161: vec3; - var param_162: vec3; - var param_163: vec3; + var param_166: vec3; + var param_167: vec3; + var param_168: vec3; + var param_169: vec3; + var param_170: vec3; var toPoint: vec3; var pointDistance: f32; - var local_25: vec3; - var param_164: vec3; - var param_165: vec3; - var param_166: f32; - var param_167: array, 4>; + var local_27: vec3; + var param_171: vec3; + var param_172: vec3; + var param_173: f32; + var param_174: array, 4>; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; var distance_3: f32; - var param_168: f32; - var param_169: f32; + var param_175: f32; + var param_176: f32; var cosAngle: f32; light_4.integrated = 0f; light_4.ltc = vec3(0f, 0f, 0f); - let _e318 = (*index_2); - if (_e318 < 8i) { - let _e320 = (*index_2); - let _e323 = frag_info.light_position[_e320]; - position = _e323; - let _e324 = (*index_2); - let _e327 = frag_info.light_color[_e324]; - color_1 = _e327; - let _e328 = (*index_2); - let _e331 = frag_info.light_direction[_e328]; - direction = _e331; - let _e332 = (*index_2); - let _e335 = frag_info.light_cone[_e332]; - cone = _e335; + let _e327 = (*index_2); + if (_e327 < 8i) { + let _e329 = (*index_2); + let _e332 = frag_info.light_position[_e329]; + position = _e332; + let _e333 = (*index_2); + let _e336 = frag_info.light_color[_e333]; + color_1 = _e336; + let _e337 = (*index_2); + let _e340 = frag_info.light_direction[_e337]; + direction = _e340; + let _e341 = (*index_2); + let _e344 = frag_info.light_cone[_e341]; + cone = _e344; } else { - let _e336 = (*index_2); - slot_5 = (_e336 - 8i); - let _e338 = Clustered_u0028_(); - clustered = _e338; - let _e339 = clustered; - if _e339 { - let _e340 = slot_5; - param_153 = _e340; - let _e341 = ClusterRow_u0028_i1_u003b((¶m_153)); - local_21 = _e341; + let _e345 = (*index_2); + slot_6 = (_e345 - 8i); + let _e347 = Clustered_u0028_(); + clustered = _e347; + let _e348 = clustered; + if _e348 { + let _e349 = slot_6; + param_160 = _e349; + let _e350 = ClusterRow_u0028_i1_u003b((¶m_160)); + local_23 = _e350; } else { - let _e342 = slot_5; - param_154 = _e342; - let _e343 = LightListRow_u0028_i1_u003b((¶m_154)); - local_21 = _e343; - } - let _e344 = local_21; - listRow = _e344; - let _e345 = listRow; - let _e349 = light_list_info.list[2u]; - v_2 = ((_e345 + 0.5f) * _e349); - let _e353 = light_list_info.list[1u]; - u_1 = _e353; - let _e354 = u_1; - let _e356 = v_2; - let _e358 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e354), _e356), 0f); - position = _e358; - let _e359 = u_1; - let _e361 = v_2; - let _e363 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e359), _e361), 0f); - color_1 = _e363; - let _e364 = u_1; - let _e366 = v_2; - let _e368 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e364), _e366), 0f); - direction = _e368; - let _e369 = u_1; - let _e371 = v_2; - let _e373 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e369), _e371), 0f); - cone = _e373; - let _e374 = clustered; - if _e374 { - let _e375 = listRow; - param_155 = _e375; - let _e376 = InSlots_u0028_f1_u003b((¶m_155)); - local_22 = select(1f, 0f, _e376); + let _e351 = slot_6; + param_161 = _e351; + let _e352 = LightListRow_u0028_i1_u003b((¶m_161)); + local_23 = _e352; + } + let _e353 = local_23; + listRow = _e353; + let _e354 = listRow; + let _e358 = light_list_info.list[2u]; + v_2 = ((_e354 + 0.5f) * _e358); + let _e362 = light_list_info.list[1u]; + u_1 = _e362; + let _e363 = u_1; + let _e365 = v_2; + let _e367 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e363), _e365), 0f); + position = _e367; + let _e368 = u_1; + let _e370 = v_2; + let _e372 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e368), _e370), 0f); + color_1 = _e372; + let _e373 = u_1; + let _e375 = v_2; + let _e377 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e373), _e375), 0f); + direction = _e377; + let _e378 = u_1; + let _e380 = v_2; + let _e382 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e378), _e380), 0f); + cone = _e382; + let _e383 = clustered; + if _e383 { + let _e384 = listRow; + param_162 = _e384; + let _e385 = InSlots_u0028_f1_u003b((¶m_162)); + local_24 = select(1f, 0f, _e385); } else { - let _e378 = slot_5; - param_156 = _e378; - let _e379 = LightListScale_u0028_i1_u003b((¶m_156)); - local_22 = _e379; - } - let _e380 = local_22; - let _e382 = color_1[3u]; - color_1[3u] = (_e382 * _e380); - } - let _e386 = position[3u]; - type_45 = _e386; - let _e387 = type_45; - if (_e387 > 2.5f) { - let _e389 = direction; - halfWidth_1 = _e389.xyz; - let _e391 = cone; - halfHeight_1 = _e391.xyz; - let _e393 = position; - let _e395 = v_world_position_1; - toCentre = (_e393.xyz - _e395); - let _e397 = toCentre; - let _e398 = halfWidth_1; - let _e400 = halfHeight_1; - corners_2[0i] = ((_e397 - _e398) - _e400); - let _e403 = toCentre; - let _e404 = halfWidth_1; - let _e406 = halfHeight_1; - corners_2[1i] = ((_e403 + _e404) - _e406); - let _e409 = toCentre; - let _e410 = halfWidth_1; - let _e412 = halfHeight_1; - corners_2[2i] = ((_e409 + _e410) + _e412); - let _e415 = toCentre; - let _e416 = halfWidth_1; - let _e418 = halfHeight_1; - corners_2[3i] = ((_e415 - _e416) + _e418); - let _e421 = toCentre; - let _e422 = halfWidth_1; - let _e423 = halfHeight_1; - behind = (dot(_e421, cross(_e422, _e423)) >= 0f); - let _e427 = behind; - if _e427 { - local_23 = 0f; + let _e387 = slot_6; + param_163 = _e387; + let _e388 = LightListScale_u0028_i1_u003b((¶m_163)); + local_24 = _e388; + } + let _e389 = local_24; + let _e391 = color_1[3u]; + color_1[3u] = (_e391 * _e389); + } + let _e395 = position[3u]; + type_45 = _e395; + let _e396 = type_45; + if (_e396 > 2.5f) { + let _e398 = direction; + halfWidth_1 = _e398.xyz; + let _e400 = cone; + halfHeight_1 = _e400.xyz; + let _e402 = position; + let _e404 = v_world_position_1; + toCentre = (_e402.xyz - _e404); + let _e406 = toCentre; + let _e407 = halfWidth_1; + let _e409 = halfHeight_1; + corners_2[0i] = ((_e406 - _e407) - _e409); + let _e412 = toCentre; + let _e413 = halfWidth_1; + let _e415 = halfHeight_1; + corners_2[1i] = ((_e412 + _e413) - _e415); + let _e418 = toCentre; + let _e419 = halfWidth_1; + let _e421 = halfHeight_1; + corners_2[2i] = ((_e418 + _e419) + _e421); + let _e424 = toCentre; + let _e425 = halfWidth_1; + let _e427 = halfHeight_1; + corners_2[3i] = ((_e424 - _e425) + _e427); + let _e430 = toCentre; + let _e431 = halfWidth_1; + let _e432 = halfHeight_1; + behind = (dot(_e430, cross(_e431, _e432)) >= 0f); + let _e436 = behind; + if _e436 { + local_25 = 0f; } else { - let _e428 = corners_2; - param_157 = _e428; - let _e430 = (*s_6).n; - param_158 = _e430; - let _e431 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_157), (¶m_158)); - local_23 = _e431; - } - let _e432 = local_23; - formFactor = _e432; - let _e433 = halfWidth_1; - let _e434 = halfHeight_1; - area = (length(cross(_e433, _e434)) * 4f); - let _e438 = area; - if (_e438 > 0.000000001f) { - let _e440 = area; - local_24 = (1f / _e440); + let _e437 = corners_2; + param_164 = _e437; + let _e439 = (*s_7).n; + param_165 = _e439; + let _e440 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_164), (¶m_165)); + local_25 = _e440; + } + let _e441 = local_25; + formFactor = _e441; + let _e442 = halfWidth_1; + let _e443 = halfHeight_1; + area = (length(cross(_e442, _e443)) * 4f); + let _e447 = area; + if (_e447 > 0.000000001f) { + let _e449 = area; + local_26 = (1f / _e449); } else { - local_24 = 0f; - } - let _e442 = local_24; - radiance = _e442; - let _e443 = toCentre; - distance_2 = length(_e443); - let _e446 = direction[3u]; - if (_e446 > 0f) { - let _e448 = distance_2; - let _e450 = direction[3u]; - ratio_1 = (_e448 / _e450); - let _e452 = ratio_1; - let _e453 = ratio_1; - let _e455 = ratio_1; - let _e457 = ratio_1; - window_1 = clamp((1f - (((_e452 * _e453) * _e455) * _e457)), 0f, 1f); - let _e461 = window_1; - let _e462 = window_1; - let _e464 = radiance; - radiance = (_e464 * (_e461 * _e462)); - } - let _e467 = (*s_6).v; - let _e470 = (*s_6).n; - mirror_1 = reflect(-(_e467), _e470); - let _e472 = position; - param_159 = _e472.xyz; - let _e474 = halfWidth_1; - param_160 = _e474; - let _e475 = halfHeight_1; - param_161 = _e475; - let _e476 = v_world_position_1; - param_162 = _e476; - let _e477 = mirror_1; - param_163 = _e477; - let _e478 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_159), (¶m_160), (¶m_161), (¶m_162), (¶m_163)); - representative = _e478; - let _e479 = representative; - let _e480 = v_world_position_1; - toPoint = (_e479 - _e480); - let _e482 = toPoint; - pointDistance = length(_e482); - let _e484 = pointDistance; - if (_e484 > 0.000001f) { - let _e486 = toPoint; - let _e487 = pointDistance; - local_25 = (_e486 / vec3(_e487)); + local_26 = 0f; + } + let _e451 = local_26; + radiance = _e451; + let _e452 = toCentre; + distance_2 = length(_e452); + let _e455 = direction[3u]; + if (_e455 > 0f) { + let _e457 = distance_2; + let _e459 = direction[3u]; + ratio_1 = (_e457 / _e459); + let _e461 = ratio_1; + let _e462 = ratio_1; + let _e464 = ratio_1; + let _e466 = ratio_1; + window_1 = clamp((1f - (((_e461 * _e462) * _e464) * _e466)), 0f, 1f); + let _e470 = window_1; + let _e471 = window_1; + let _e473 = radiance; + radiance = (_e473 * (_e470 * _e471)); + } + let _e476 = (*s_7).v; + let _e479 = (*s_7).n; + mirror_1 = reflect(-(_e476), _e479); + let _e481 = position; + param_166 = _e481.xyz; + let _e483 = halfWidth_1; + param_167 = _e483; + let _e484 = halfHeight_1; + param_168 = _e484; + let _e485 = v_world_position_1; + param_169 = _e485; + let _e486 = mirror_1; + param_170 = _e486; + let _e487 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_166), (¶m_167), (¶m_168), (¶m_169), (¶m_170)); + representative = _e487; + let _e488 = representative; + let _e489 = v_world_position_1; + toPoint = (_e488 - _e489); + let _e491 = toPoint; + pointDistance = length(_e491); + let _e493 = pointDistance; + if (_e493 > 0.000001f) { + let _e495 = toPoint; + let _e496 = pointDistance; + local_27 = (_e495 / vec3(_e496)); } else { - let _e491 = (*s_6).n; - local_25 = _e491; - } - let _e492 = local_25; - light_4.l = _e492; - let _e495 = light_4.l; - let _e497 = (*s_6).v; - light_4.h = normalize((_e495 + _e497)); - let _e501 = formFactor; - light_4.n_dot_l = _e501; - let _e504 = (*s_6).n; - let _e506 = light_4.h; - light_4.n_dot_h = max(dot(_e504, _e506), 0f); - let _e511 = (*s_6).v; - let _e513 = light_4.h; - light_4.v_dot_h = max(dot(_e511, _e513), 0f); - let _e517 = color_1; - let _e520 = color_1[3u]; - let _e522 = radiance; - light_4.radiance = ((_e517.xyz * _e520) * _e522); + let _e500 = (*s_7).n; + local_27 = _e500; + } + let _e501 = local_27; + light_4.l = _e501; + let _e504 = light_4.l; + let _e506 = (*s_7).v; + light_4.h = normalize((_e504 + _e506)); + let _e510 = formFactor; + light_4.n_dot_l = _e510; + let _e513 = (*s_7).n; + let _e515 = light_4.h; + light_4.n_dot_h = max(dot(_e513, _e515), 0f); + let _e520 = (*s_7).v; + let _e522 = light_4.h; + light_4.v_dot_h = max(dot(_e520, _e522), 0f); + let _e526 = color_1; + let _e529 = color_1[3u]; + let _e531 = radiance; + light_4.radiance = ((_e526.xyz * _e529) * _e531); light_4.integrated = 1f; - let _e527 = (*s_6).n; - param_164 = _e527; - let _e529 = (*s_6).v; - param_165 = _e529; - let _e531 = (*s_6).roughness; - param_166 = _e531; - let _e532 = corners_2; - param_167 = _e532; - let _e533 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_164), (¶m_165), (¶m_166), (¶m_167)); - light_4.ltc = _e533; - let _e535 = corners_2; - g_rect_corners = _e535; - let _e536 = light_4; - return _e536; - } - let _e537 = direction; - aim = normalize(_e537.xyz); + let _e536 = (*s_7).n; + param_171 = _e536; + let _e538 = (*s_7).v; + param_172 = _e538; + let _e540 = (*s_7).roughness; + param_173 = _e540; + let _e541 = corners_2; + param_174 = _e541; + let _e542 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_171), (¶m_172), (¶m_173), (¶m_174)); + light_4.ltc = _e542; + let _e544 = corners_2; + g_rect_corners = _e544; + let _e545 = light_4; + return _e545; + } + let _e546 = direction; + aim = normalize(_e546.xyz); attenuation_1 = 1f; - let _e540 = type_45; - if (_e540 < 0.5f) { - let _e542 = aim; - light_4.l = -(_e542); + let _e549 = type_45; + if (_e549 < 0.5f) { + let _e551 = aim; + light_4.l = -(_e551); } else { - let _e545 = position; - let _e547 = v_world_position_1; - toLight_1 = (_e545.xyz - _e547); - let _e549 = toLight_1; - distance_3 = length(_e549); - let _e551 = distance_3; - if (_e551 < 0.000001f) { - let _e554 = (*s_6).n; - light_4.l = _e554; - let _e557 = (*s_6).n; - light_4.h = _e557; + let _e554 = position; + let _e556 = v_world_position_1; + toLight_1 = (_e554.xyz - _e556); + let _e558 = toLight_1; + distance_3 = length(_e558); + let _e560 = distance_3; + if (_e560 < 0.000001f) { + let _e563 = (*s_7).n; + light_4.l = _e563; + let _e566 = (*s_7).n; + light_4.h = _e566; light_4.radiance = vec3(0f, 0f, 0f); light_4.n_dot_l = 0f; light_4.n_dot_h = 0f; light_4.v_dot_h = 0f; - let _e563 = light_4; - return _e563; - } - let _e564 = toLight_1; - let _e565 = distance_3; - light_4.l = (_e564 / vec3(_e565)); - let _e569 = distance_3; - param_168 = _e569; - let _e571 = direction[3u]; - param_169 = _e571; - let _e572 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_168), (¶m_169)); - attenuation_1 = _e572; - let _e573 = type_45; - if (_e573 > 1.5f) { - let _e575 = aim; - let _e577 = light_4.l; - cosAngle = dot(_e575, -(_e577)); - let _e580 = cosAngle; - let _e582 = cone[1u]; - let _e585 = cone[0u]; - let _e587 = cone[1u]; - let _e591 = attenuation_1; - attenuation_1 = (_e591 * clamp(((_e580 - _e582) / (_e585 - _e587)), 0f, 1f)); - } - } - let _e594 = light_4.l; - let _e596 = (*s_6).v; - light_4.h = normalize((_e594 + _e596)); - let _e601 = (*s_6).n; + let _e572 = light_4; + return _e572; + } + let _e573 = toLight_1; + let _e574 = distance_3; + light_4.l = (_e573 / vec3(_e574)); + let _e578 = distance_3; + param_175 = _e578; + let _e580 = direction[3u]; + param_176 = _e580; + let _e581 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_175), (¶m_176)); + attenuation_1 = _e581; + let _e582 = type_45; + if (_e582 > 1.5f) { + let _e584 = aim; + let _e586 = light_4.l; + cosAngle = dot(_e584, -(_e586)); + let _e589 = cosAngle; + let _e591 = cone[1u]; + let _e594 = cone[0u]; + let _e596 = cone[1u]; + let _e600 = attenuation_1; + attenuation_1 = (_e600 * clamp(((_e589 - _e591) / (_e594 - _e596)), 0f, 1f)); + } + } let _e603 = light_4.l; - light_4.n_dot_l = max(dot(_e601, _e603), 0f); - let _e608 = (*s_6).n; - let _e610 = light_4.h; - light_4.n_dot_h = max(dot(_e608, _e610), 0f); - let _e615 = (*s_6).v; - let _e617 = light_4.h; - light_4.v_dot_h = max(dot(_e615, _e617), 0f); - let _e621 = color_1; - let _e624 = color_1[3u]; - let _e626 = attenuation_1; - light_4.radiance = ((_e621.xyz * _e624) * _e626); - let _e629 = light_4; - return _e629; + let _e605 = (*s_7).v; + light_4.h = normalize((_e603 + _e605)); + let _e610 = (*s_7).n; + let _e612 = light_4.l; + light_4.n_dot_l = max(dot(_e610, _e612), 0f); + let _e617 = (*s_7).n; + let _e619 = light_4.h; + light_4.n_dot_h = max(dot(_e617, _e619), 0f); + let _e624 = (*s_7).v; + let _e626 = light_4.h; + light_4.v_dot_h = max(dot(_e624, _e626), 0f); + let _e630 = color_1; + let _e633 = color_1[3u]; + let _e635 = attenuation_1; + light_4.radiance = ((_e630.xyz * _e633) * _e635); + let _e638 = light_4; + return _e638; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -19190,188 +19960,188 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_26: f32; + var local_28: f32; var cell: f32; var row_3: f32; var header: vec4; - var param_170: f32; - var param_171: f32; - - let _e276 = light_list_info.cluster_view_projection; - let _e277 = (*world_3); - clip_1 = (_e276 * vec4(_e277.x, _e277.y, _e277.z, 1f)); - let _e283 = clip_1; - let _e286 = clip_1[3u]; - ndc_1 = (_e283.xy / vec2(max(_e286, 0.000001f))); - let _e291 = light_list_info.cluster_grid; - grid = _e291.xyz; - let _e295 = light_list_info.cluster_depth[0u]; - near_1 = _e295; - let _e297 = ndc_1[0u]; - let _e301 = grid[0u]; - let _e305 = grid[0u]; - tx = clamp(floor((((_e297 * 0.5f) + 0.5f) * _e301)), 0f, (_e305 - 1f)); - let _e309 = ndc_1[1u]; - let _e313 = grid[1u]; - let _e317 = grid[1u]; - ty = clamp(floor((((_e309 * 0.5f) + 0.5f) * _e313)), 0f, (_e317 - 1f)); - let _e321 = clip_1[3u]; - let _e322 = near_1; - if (_e321 <= _e322) { - local_26 = 0f; + var param_177: f32; + var param_178: f32; + + let _e285 = light_list_info.cluster_view_projection; + let _e286 = (*world_3); + clip_1 = (_e285 * vec4(_e286.x, _e286.y, _e286.z, 1f)); + let _e292 = clip_1; + let _e295 = clip_1[3u]; + ndc_1 = (_e292.xy / vec2(max(_e295, 0.000001f))); + let _e300 = light_list_info.cluster_grid; + grid = _e300.xyz; + let _e304 = light_list_info.cluster_depth[0u]; + near_1 = _e304; + let _e306 = ndc_1[0u]; + let _e310 = grid[0u]; + let _e314 = grid[0u]; + tx = clamp(floor((((_e306 * 0.5f) + 0.5f) * _e310)), 0f, (_e314 - 1f)); + let _e318 = ndc_1[1u]; + let _e322 = grid[1u]; + let _e326 = grid[1u]; + ty = clamp(floor((((_e318 * 0.5f) + 0.5f) * _e322)), 0f, (_e326 - 1f)); + let _e330 = clip_1[3u]; + let _e331 = near_1; + if (_e330 <= _e331) { + local_28 = 0f; } else { - let _e325 = clip_1[3u]; - let _e326 = near_1; - let _e331 = light_list_info.cluster_depth[1u]; - let _e335 = grid[2u]; - local_26 = clamp(floor((log((_e325 / _e326)) * _e331)), 0f, (_e335 - 1f)); - } - let _e338 = local_26; - tz = _e338; - let _e339 = tx; - let _e340 = ty; - let _e342 = grid[0u]; - let _e345 = tz; - let _e347 = grid[0u]; - let _e350 = grid[1u]; - cell = ((_e339 + (_e340 * _e342)) + ((_e345 * _e347) * _e350)); - let _e353 = cell; - row_3 = floor((_e353 / 4f)); - let _e356 = cell; - let _e357 = row_3; - let _e362 = light_list_info.cluster_depth[2u]; - let _e363 = row_3; - param_170 = (_e356 - (_e357 * 4f)); - param_171 = (_e362 + _e363); - let _e365 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_170), (¶m_171)); - header = _e365; - let _e367 = header[0u]; - g_cluster_offset = _e367; - let _e369 = header[1u]; - g_cluster_count = _e369; + let _e334 = clip_1[3u]; + let _e335 = near_1; + let _e340 = light_list_info.cluster_depth[1u]; + let _e344 = grid[2u]; + local_28 = clamp(floor((log((_e334 / _e335)) * _e340)), 0f, (_e344 - 1f)); + } + let _e347 = local_28; + tz = _e347; + let _e348 = tx; + let _e349 = ty; + let _e351 = grid[0u]; + let _e354 = tz; + let _e356 = grid[0u]; + let _e359 = grid[1u]; + cell = ((_e348 + (_e349 * _e351)) + ((_e354 * _e356) * _e359)); + let _e362 = cell; + row_3 = floor((_e362 / 4f)); + let _e365 = cell; + let _e366 = row_3; + let _e371 = light_list_info.cluster_depth[2u]; + let _e372 = row_3; + param_177 = (_e365 - (_e366 * 4f)); + param_178 = (_e371 + _e372); + let _e374 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_177), (¶m_178)); + header = _e374; + let _e376 = header[0u]; + g_cluster_offset = _e376; + let _e378 = header[1u]; + g_cluster_count = _e378; return; } fn LightCount_u0028_() -> i32 { var tail: f32; - var param_172: vec3; + var param_179: vec3; - let _e265 = light_list_info.list[0u]; - tail = _e265; - let _e266 = Clustered_u0028_(); - if _e266 { - let _e267 = v_world_position_1; - param_172 = _e267; - FindCluster_u0028_vf3_u003b((¶m_172)); - let _e268 = g_cluster_count; - tail = _e268; + let _e274 = light_list_info.list[0u]; + tail = _e274; + let _e275 = Clustered_u0028_(); + if _e275 { + let _e276 = v_world_position_1; + param_179 = _e276; + FindCluster_u0028_vf3_u003b((¶m_179)); + let _e277 = g_cluster_count; + tail = _e277; } - let _e271 = frag_info.frame_params[1u]; - let _e275 = tail; - return (clamp(i32((_e271 + 0.5f)), 0i, 8i) + clamp(i32((_e275 + 0.5f)), 0i, 24i)); + let _e280 = frag_info.frame_params[1u]; + let _e284 = tail; + return (clamp(i32((_e280 + 0.5f)), 0i, 8i) + clamp(i32((_e284 + 0.5f)), 0i, 24i)); } -fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) -> vec3 { +fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) -> vec3 { var total_1: vec3; var count_1: i32; var i_9: i32; var light_5: LightSample; - var param_173: i32; - var param_174: Surface; + var param_180: i32; + var param_181: Surface; var visibility: f32; - var param_175: i32; - var local_27: f32; - var param_176: Surface; - var param_177: LightSample; - var param_178: i32; - var param_179: vec3; - var param_180: vec3; - var param_181: i32; - var param_182: Surface; - var param_183: LightSample; + var param_182: i32; + var local_29: f32; + var param_183: Surface; + var param_184: LightSample; + var param_185: i32; + var param_186: vec3; + var param_187: vec3; + var param_188: i32; + var param_189: Surface; + var param_190: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e279 = LightCount_u0028_(); - count_1 = _e279; + let _e288 = LightCount_u0028_(); + count_1 = _e288; i_9 = 0i; loop { - let _e280 = i_9; - if (_e280 < 32i) { - let _e282 = i_9; - let _e283 = count_1; - if (_e282 >= _e283) { + let _e289 = i_9; + if (_e289 < 32i) { + let _e291 = i_9; + let _e292 = count_1; + if (_e291 >= _e292) { break; } - let _e285 = i_9; - param_173 = _e285; - let _e286 = (*s_7); - param_174 = _e286; - let _e287 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_173), (¶m_174)); - light_5 = _e287; - let _e289 = light_5.n_dot_l; - if (_e289 <= 0f) { + let _e294 = i_9; + param_180 = _e294; + let _e295 = (*s_8); + param_181 = _e295; + let _e296 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_180), (¶m_181)); + light_5 = _e296; + let _e298 = light_5.n_dot_l; + if (_e298 <= 0f) { continue; } - let _e291 = i_9; - param_175 = _e291; - let _e292 = LightHasShadow_u0028_i1_u003b((¶m_175)); - if _e292 { - let _e293 = (*s_7); - param_176 = _e293; - let _e294 = light_5; - param_177 = _e294; - let _e295 = i_9; - param_178 = _e295; - let _e296 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_176), (¶m_177), (¶m_178)); - let _e297 = v_world_position_1; - param_179 = _e297; - let _e299 = (*s_7).n; - param_180 = _e299; - let _e300 = i_9; - param_181 = _e300; - let _e301 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_179), (¶m_180), (¶m_181)); - local_27 = (_e296 * _e301); + let _e300 = i_9; + param_182 = _e300; + let _e301 = LightHasShadow_u0028_i1_u003b((¶m_182)); + if _e301 { + let _e302 = (*s_8); + param_183 = _e302; + let _e303 = light_5; + param_184 = _e303; + let _e304 = i_9; + param_185 = _e304; + let _e305 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_183), (¶m_184), (¶m_185)); + let _e306 = v_world_position_1; + param_186 = _e306; + let _e308 = (*s_8).n; + param_187 = _e308; + let _e309 = i_9; + param_188 = _e309; + let _e310 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_186), (¶m_187), (¶m_188)); + local_29 = (_e305 * _e310); } else { - local_27 = 1f; + local_29 = 1f; } - let _e303 = local_27; - visibility = _e303; - let _e304 = visibility; - if (_e304 <= 0f) { + let _e312 = local_29; + visibility = _e312; + let _e313 = visibility; + if (_e313 <= 0f) { continue; } - let _e306 = (*s_7); - param_182 = _e306; - let _e307 = light_5; - param_183 = _e307; - let _e308 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_182), (¶m_183)); - let _e310 = light_5.radiance; - let _e313 = light_5.n_dot_l; - let _e315 = visibility; - let _e317 = total_1; - total_1 = (_e317 + (((_e308 * _e310) * _e313) * _e315)); + let _e315 = (*s_8); + param_189 = _e315; + let _e316 = light_5; + param_190 = _e316; + let _e317 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_189), (¶m_190)); + let _e319 = light_5.radiance; + let _e322 = light_5.n_dot_l; + let _e324 = visibility; + let _e326 = total_1; + total_1 = (_e326 + (((_e317 * _e319) * _e322) * _e324)); continue; } else { break; } continuing { - let _e319 = i_9; - i_9 = (_e319 + 1i); + let _e328 = i_9; + i_9 = (_e328 + 1i); } } - let _e321 = total_1; - return _e321; + let _e330 = total_1; + return _e330; } fn SampleLightmap_u0028_() -> vec3 { var texel_4: vec4; - let _e262 = v_lightmap_uv_1; - let _e263 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e262); - texel_4 = _e263; - let _e264 = texel_4; - let _e267 = texel_4[3u]; - return ((_e264.xyz * _e267) * 8f); + let _e271 = v_lightmap_uv_1; + let _e272 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e271); + texel_4 = _e272; + let _e273 = texel_4; + let _e276 = texel_4[3u]; + return ((_e273.xyz * _e276) * 8f); } fn SceneColourLevel_u0028_vf2_u003b_i1_u003b(uv_6: ptr>, level: ptr) -> vec3 { @@ -19379,315 +20149,315 @@ fn SceneColourLevel_u0028_vf2_u003b_i1_u003b(uv_6: ptr>, lev var inset_1: vec2; var at_4: vec2; - let _e266 = (*level); - let _e269 = layer_info.scene_levels[_e266]; - rect = _e269; - let _e271 = layer_info.scene_colour; - inset_1 = (_e271.yz * 0.5f); - let _e274 = rect; - let _e276 = (*uv_6); - let _e277 = rect; - let _e280 = inset_1; - let _e281 = rect; - let _e283 = inset_1; - let _e285 = inset_1; - at_4 = (_e274.xy + clamp((_e276 * _e277.zw), _e280, max((_e281.zw - _e283), _e285))); - let _e289 = at_4; - let _e290 = textureSampleLevel(scene_colour_texture_tex, scene_colour_texture_smp, _e289, 0f); - return _e290.xyz; + let _e275 = (*level); + let _e278 = layer_info.scene_levels[_e275]; + rect = _e278; + let _e280 = layer_info.scene_colour; + inset_1 = (_e280.yz * 0.5f); + let _e283 = rect; + let _e285 = (*uv_6); + let _e286 = rect; + let _e289 = inset_1; + let _e290 = rect; + let _e292 = inset_1; + let _e294 = inset_1; + at_4 = (_e283.xy + clamp((_e285 * _e286.zw), _e289, max((_e290.zw - _e292), _e294))); + let _e298 = at_4; + let _e299 = textureSampleLevel(scene_colour_texture_tex, scene_colour_texture_smp, _e298, 0f); + return _e299.xyz; } fn SceneColourAt_u0028_vf3_u003b_f1_u003b(world_4: ptr>, lod: ptr) -> vec3 { var clip_2: vec4; var ndc_2: vec2; - var view: vec2; + var view_1: vec2; var uv_7: vec2; var top: f32; var level_1: f32; var lower: f32; var near_2: vec3; - var param_184: vec2; - var param_185: i32; + var param_191: vec2; + var param_192: i32; var far_1: vec3; - var param_186: vec2; - var param_187: i32; - - let _e277 = layer_info.scene_view_projection; - let _e278 = (*world_4); - clip_2 = (_e277 * vec4(_e278.x, _e278.y, _e278.z, 1f)); - let _e284 = clip_2; - let _e287 = clip_2[3u]; - ndc_2 = (_e284.xy / vec2(max(_e287, 0.000001f))); - let _e292 = ndc_2[0u]; - let _e296 = ndc_2[1u]; - view = clamp(vec2(((_e292 * 0.5f) + 0.5f), (0.5f - (_e296 * 0.5f))), vec2(0f, 0f), vec2(1f, 1f)); - let _e302 = layer_info.scene_viewport; - let _e304 = view; - let _e306 = layer_info.scene_viewport; - uv_7 = (_e302.xy + (_e304 * _e306.zw)); - let _e312 = layer_info.scene_colour[0u]; - top = (_e312 - 1f); - let _e314 = (*lod); - let _e315 = top; - level_1 = clamp(_e314, 0f, _e315); - let _e317 = level_1; - lower = floor(_e317); - let _e319 = lower; - let _e321 = uv_7; - param_184 = _e321; - param_185 = i32(_e319); - let _e322 = SceneColourLevel_u0028_vf2_u003b_i1_u003b((¶m_184), (¶m_185)); - near_2 = _e322; - let _e323 = lower; - let _e325 = top; - let _e328 = uv_7; - param_186 = _e328; - param_187 = i32(min((_e323 + 1f), _e325)); - let _e329 = SceneColourLevel_u0028_vf2_u003b_i1_u003b((¶m_186), (¶m_187)); - far_1 = _e329; - let _e330 = near_2; - let _e331 = far_1; - let _e332 = level_1; - let _e333 = lower; - return mix(_e330, _e331, vec3((_e332 - _e333))); + var param_193: vec2; + var param_194: i32; + + let _e286 = layer_info.scene_view_projection; + let _e287 = (*world_4); + clip_2 = (_e286 * vec4(_e287.x, _e287.y, _e287.z, 1f)); + let _e293 = clip_2; + let _e296 = clip_2[3u]; + ndc_2 = (_e293.xy / vec2(max(_e296, 0.000001f))); + let _e301 = ndc_2[0u]; + let _e305 = ndc_2[1u]; + view_1 = clamp(vec2(((_e301 * 0.5f) + 0.5f), (0.5f - (_e305 * 0.5f))), vec2(0f, 0f), vec2(1f, 1f)); + let _e311 = layer_info.scene_viewport; + let _e313 = view_1; + let _e315 = layer_info.scene_viewport; + uv_7 = (_e311.xy + (_e313 * _e315.zw)); + let _e321 = layer_info.scene_colour[0u]; + top = (_e321 - 1f); + let _e323 = (*lod); + let _e324 = top; + level_1 = clamp(_e323, 0f, _e324); + let _e326 = level_1; + lower = floor(_e326); + let _e328 = lower; + let _e330 = uv_7; + param_191 = _e330; + param_192 = i32(_e328); + let _e331 = SceneColourLevel_u0028_vf2_u003b_i1_u003b((¶m_191), (¶m_192)); + near_2 = _e331; + let _e332 = lower; + let _e334 = top; + let _e337 = uv_7; + param_193 = _e337; + param_194 = i32(min((_e332 + 1f), _e334)); + let _e338 = SceneColourLevel_u0028_vf2_u003b_i1_u003b((¶m_193), (¶m_194)); + far_1 = _e338; + let _e339 = near_2; + let _e340 = far_1; + let _e341 = level_1; + let _e342 = lower; + return mix(_e339, _e340, vec3((_e341 - _e342))); } fn IorInfinite_u0028_() -> bool { - let _e263 = layer_info.coat[2u]; - return (_e263 == 0f); + let _e272 = layer_info.coat[2u]; + return (_e272 == 0f); } fn DispersionSpread_u0028_f1_u003b(ior: ptr) -> f32 { - var local_28: f32; + var local_30: f32; - let _e263 = IorInfinite_u0028_(); - if _e263 { - local_28 = 0f; + let _e272 = IorInfinite_u0028_(); + if _e272 { + local_30 = 0f; } else { - let _e264 = (*ior); - let _e269 = layer_info.transmission[3u]; - local_28 = (((_e264 - 1f) * 0.025f) * _e269); + let _e273 = (*ior); + let _e278 = layer_info.transmission[3u]; + local_30 = (((_e273 - 1f) * 0.025f) * _e278); } - let _e271 = local_28; - return _e271; + let _e280 = local_30; + return _e280; } fn RefractionIor_u0028_() -> f32 { - var local_29: f32; + var local_31: f32; - let _e262 = IorInfinite_u0028_(); - if _e262 { - local_29 = 10000f; + let _e271 = IorInfinite_u0028_(); + if _e271 { + local_31 = 10000f; } else { - let _e265 = layer_info.coat[2u]; - local_29 = max(_e265, 1f); + let _e274 = layer_info.coat[2u]; + local_31 = max(_e274, 1f); } - let _e267 = local_29; - return _e267; + let _e276 = local_31; + return _e276; } -fn SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) -> vec3 { +fn SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) -> vec3 { var ior_1: f32; var spread_1: f32; - var param_188: f32; + var param_195: f32; var width: f32; var lod_1: f32; var thin: bool; var red: vec3; - var local_30: vec3; + var local_32: vec3; var green: vec3; - var local_31: vec3; + var local_33: vec3; var blue: vec3; - var local_32: vec3; - var param_189: vec3; - var param_190: f32; - var param_191: vec3; - var param_192: f32; - var param_193: vec3; - var param_194: f32; + var local_34: vec3; + var param_196: vec3; + var param_197: f32; + var param_198: vec3; + var param_199: f32; + var param_200: vec3; + var param_201: f32; - let _e280 = RefractionIor_u0028_(); - ior_1 = _e280; - let _e281 = ior_1; - param_188 = _e281; - let _e282 = DispersionSpread_u0028_f1_u003b((¶m_188)); - spread_1 = _e282; - let _e286 = layer_info.scene_levels[0i][2u]; - let _e289 = layer_info.scene_colour[1u]; - width = (_e286 / max(_e289, 0.000000001f)); - let _e292 = width; - let _e296 = (*s_8).roughness; - let _e298 = ior_1; - lod_1 = ((log2(max(_e292, 1f)) * _e296) * clamp(((_e298 * 2f) - 2f), 0f, 1f)); - let _e303 = g_thickness; - thin = (_e303 <= 0f); - let _e305 = thin; - if _e305 { - let _e307 = (*s_8).v; - local_30 = -(_e307); + let _e289 = RefractionIor_u0028_(); + ior_1 = _e289; + let _e290 = ior_1; + param_195 = _e290; + let _e291 = DispersionSpread_u0028_f1_u003b((¶m_195)); + spread_1 = _e291; + let _e295 = layer_info.scene_levels[0i][2u]; + let _e298 = layer_info.scene_colour[1u]; + width = (_e295 / max(_e298, 0.000000001f)); + let _e301 = width; + let _e305 = (*s_9).roughness; + let _e307 = ior_1; + lod_1 = ((log2(max(_e301, 1f)) * _e305) * clamp(((_e307 * 2f) - 2f), 0f, 1f)); + let _e312 = g_thickness; + thin = (_e312 <= 0f); + let _e314 = thin; + if _e314 { + let _e316 = (*s_9).v; + local_32 = -(_e316); } else { - let _e310 = (*s_8).v; - let _e313 = (*s_8).n; - let _e314 = ior_1; - let _e315 = spread_1; - local_30 = refract(-(_e310), _e313, (1f / max((_e314 - _e315), 1f))); - } - let _e320 = local_30; - red = _e320; - let _e321 = thin; - if _e321 { - let _e323 = (*s_8).v; - local_31 = -(_e323); + let _e319 = (*s_9).v; + let _e322 = (*s_9).n; + let _e323 = ior_1; + let _e324 = spread_1; + local_32 = refract(-(_e319), _e322, (1f / max((_e323 - _e324), 1f))); + } + let _e329 = local_32; + red = _e329; + let _e330 = thin; + if _e330 { + let _e332 = (*s_9).v; + local_33 = -(_e332); } else { - let _e326 = (*s_8).v; - let _e329 = (*s_8).n; - let _e330 = ior_1; - local_31 = refract(-(_e326), _e329, (1f / _e330)); - } - let _e333 = local_31; - green = _e333; - let _e334 = thin; - if _e334 { - let _e336 = (*s_8).v; - local_32 = -(_e336); + let _e335 = (*s_9).v; + let _e338 = (*s_9).n; + let _e339 = ior_1; + local_33 = refract(-(_e335), _e338, (1f / _e339)); + } + let _e342 = local_33; + green = _e342; + let _e343 = thin; + if _e343 { + let _e345 = (*s_9).v; + local_34 = -(_e345); } else { - let _e339 = (*s_8).v; - let _e342 = (*s_8).n; - let _e343 = ior_1; - let _e344 = spread_1; - local_32 = refract(-(_e339), _e342, (1f / (_e343 + _e344))); - } - let _e348 = local_32; - blue = _e348; - let _e349 = v_world_position_1; - let _e350 = red; - let _e351 = g_thickness; - param_189 = (_e349 + (_e350 * _e351)); - let _e354 = lod_1; - param_190 = _e354; - let _e355 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_189), (¶m_190)); - let _e357 = v_world_position_1; - let _e358 = green; - let _e359 = g_thickness; - param_191 = (_e357 + (_e358 * _e359)); - let _e362 = lod_1; - param_192 = _e362; - let _e363 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_191), (¶m_192)); - let _e365 = v_world_position_1; - let _e366 = blue; - let _e367 = g_thickness; - param_193 = (_e365 + (_e366 * _e367)); - let _e370 = lod_1; - param_194 = _e370; - let _e371 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_193), (¶m_194)); - return vec3(_e355.x, _e363.y, _e371.z); -} - -fn TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_f1_u003b(s_9: ptr, levels: ptr) -> vec3 { + let _e348 = (*s_9).v; + let _e351 = (*s_9).n; + let _e352 = ior_1; + let _e353 = spread_1; + local_34 = refract(-(_e348), _e351, (1f / (_e352 + _e353))); + } + let _e357 = local_34; + blue = _e357; + let _e358 = v_world_position_1; + let _e359 = red; + let _e360 = g_thickness; + param_196 = (_e358 + (_e359 * _e360)); + let _e363 = lod_1; + param_197 = _e363; + let _e364 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_196), (¶m_197)); + let _e366 = v_world_position_1; + let _e367 = green; + let _e368 = g_thickness; + param_198 = (_e366 + (_e367 * _e368)); + let _e371 = lod_1; + param_199 = _e371; + let _e372 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_198), (¶m_199)); + let _e374 = v_world_position_1; + let _e375 = blue; + let _e376 = g_thickness; + param_200 = (_e374 + (_e375 * _e376)); + let _e379 = lod_1; + param_201 = _e379; + let _e380 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_200), (¶m_201)); + return vec3(_e364.x, _e372.y, _e380.z); +} + +fn TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_f1_u003b(s_10: ptr, levels: ptr) -> vec3 { var ior_2: f32; var spread_2: f32; - var param_195: f32; + var param_202: f32; var lod_2: f32; var thin_1: bool; var red_1: vec3; - var local_33: vec3; + var local_35: vec3; var green_1: vec3; - var local_34: vec3; + var local_36: vec3; var blue_1: vec3; - var local_35: vec3; + var local_37: vec3; - let _e274 = RefractionIor_u0028_(); - ior_2 = _e274; - let _e275 = ior_2; - param_195 = _e275; - let _e276 = DispersionSpread_u0028_f1_u003b((¶m_195)); - spread_2 = _e276; - let _e278 = (*s_9).roughness; - let _e279 = ior_2; - let _e284 = (*levels); - lod_2 = ((_e278 * clamp(((_e279 * 2f) - 2f), 0f, 1f)) * _e284); - let _e286 = g_thickness; - thin_1 = (_e286 <= 0f); - let _e288 = thin_1; - if _e288 { - let _e290 = (*s_9).v; - local_33 = -(_e290); + let _e283 = RefractionIor_u0028_(); + ior_2 = _e283; + let _e284 = ior_2; + param_202 = _e284; + let _e285 = DispersionSpread_u0028_f1_u003b((¶m_202)); + spread_2 = _e285; + let _e287 = (*s_10).roughness; + let _e288 = ior_2; + let _e293 = (*levels); + lod_2 = ((_e287 * clamp(((_e288 * 2f) - 2f), 0f, 1f)) * _e293); + let _e295 = g_thickness; + thin_1 = (_e295 <= 0f); + let _e297 = thin_1; + if _e297 { + let _e299 = (*s_10).v; + local_35 = -(_e299); } else { - let _e293 = (*s_9).v; - let _e296 = (*s_9).n; - let _e297 = ior_2; - let _e298 = spread_2; - local_33 = refract(-(_e293), _e296, (1f / max((_e297 - _e298), 1f))); - } - let _e303 = local_33; - red_1 = _e303; - let _e304 = thin_1; - if _e304 { - let _e306 = (*s_9).v; - local_34 = -(_e306); + let _e302 = (*s_10).v; + let _e305 = (*s_10).n; + let _e306 = ior_2; + let _e307 = spread_2; + local_35 = refract(-(_e302), _e305, (1f / max((_e306 - _e307), 1f))); + } + let _e312 = local_35; + red_1 = _e312; + let _e313 = thin_1; + if _e313 { + let _e315 = (*s_10).v; + local_36 = -(_e315); } else { - let _e309 = (*s_9).v; - let _e312 = (*s_9).n; - let _e313 = ior_2; - local_34 = refract(-(_e309), _e312, (1f / _e313)); - } - let _e316 = local_34; - green_1 = _e316; - let _e317 = thin_1; - if _e317 { - let _e319 = (*s_9).v; - local_35 = -(_e319); + let _e318 = (*s_10).v; + let _e321 = (*s_10).n; + let _e322 = ior_2; + local_36 = refract(-(_e318), _e321, (1f / _e322)); + } + let _e325 = local_36; + green_1 = _e325; + let _e326 = thin_1; + if _e326 { + let _e328 = (*s_10).v; + local_37 = -(_e328); } else { - let _e322 = (*s_9).v; - let _e325 = (*s_9).n; - let _e326 = ior_2; - let _e327 = spread_2; - local_35 = refract(-(_e322), _e325, (1f / (_e326 + _e327))); - } - let _e331 = local_35; - blue_1 = _e331; - let _e332 = red_1; - let _e333 = lod_2; - let _e334 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e332, _e333); - let _e336 = green_1; - let _e337 = lod_2; - let _e338 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e336, _e337); - let _e340 = blue_1; - let _e341 = lod_2; - let _e342 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e340, _e341); - return vec3(_e334.x, _e338.y, _e342.z); + let _e331 = (*s_10).v; + let _e334 = (*s_10).n; + let _e335 = ior_2; + let _e336 = spread_2; + local_37 = refract(-(_e331), _e334, (1f / (_e335 + _e336))); + } + let _e340 = local_37; + blue_1 = _e340; + let _e341 = red_1; + let _e342 = lod_2; + let _e343 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e341, _e342); + let _e345 = green_1; + let _e346 = lod_2; + let _e347 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e345, _e346); + let _e349 = blue_1; + let _e350 = lod_2; + let _e351 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e349, _e350); + return vec3(_e343.x, _e347.y, _e351.z); } fn SceneColourBound_u0028_() -> bool { - let _e263 = layer_info.scene_colour[0u]; - return (_e263 > 0f); + let _e272 = layer_info.scene_colour[0u]; + return (_e272 > 0f); } fn EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b(rho_2: ptr>, r_4: ptr, mu_1: ptr) -> vec3 { var e_1: f32; - var param_196: f32; - var param_197: f32; - var param_198: f32; - var param_199: vec3; - var param_200: f32; + var param_203: f32; + var param_204: f32; + var param_205: f32; + var param_206: vec3; + var param_207: f32; - let _e270 = (*mu_1); - param_196 = _e270; - let _e271 = (*r_4); - param_197 = _e271; - let _e272 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_196), (¶m_197)); - e_1 = _e272; - let _e273 = (*rho_2); - let _e274 = e_1; - let _e276 = (*r_4); - param_198 = _e276; - let _e277 = FonAverage_u0028_f1_u003b((¶m_198)); - let _e278 = (*rho_2); - param_199 = _e278; - param_200 = _e277; - let _e279 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_199), (¶m_200)); - let _e280 = e_1; - return ((_e273 * _e274) + (_e279 * (1f - _e280))); + let _e279 = (*mu_1); + param_203 = _e279; + let _e280 = (*r_4); + param_204 = _e280; + let _e281 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_203), (¶m_204)); + e_1 = _e281; + let _e282 = (*rho_2); + let _e283 = e_1; + let _e285 = (*r_4); + param_205 = _e285; + let _e286 = FonAverage_u0028_f1_u003b((¶m_205)); + let _e287 = (*rho_2); + param_206 = _e287; + param_207 = _e286; + let _e288 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_206), (¶m_207)); + let _e289 = e_1; + return ((_e282 * _e283) + (_e288 * (1f - _e289))); } fn IridescenceSensitivity_u0028_f1_u003b_vf3_u003b(opd: ptr, shift: ptr>) -> vec3 { @@ -19697,30 +20467,30 @@ fn IridescenceSensitivity_u0028_f1_u003b_vf3_u003b(opd: ptr, shif var variance_1: vec3; var xyz: vec3; - let _e268 = (*opd); - phase = ((6.2831855f * _e268) * 0.000000001f); + let _e277 = (*opd); + phase = ((6.2831855f * _e277) * 0.000000001f); val = vec3(0.00000000000054856f, 0.00000000000044201f, 0.00000000000052481f); pos = vec3(1681000f, 1795300f, 2208400f); variance_1 = vec3(4327800000f, 9304600000f, 6612100000f); - let _e271 = val; - let _e272 = variance_1; - let _e276 = pos; - let _e277 = phase; - let _e279 = (*shift); - let _e283 = phase; - let _e284 = phase; - let _e287 = variance_1; - xyz = (((_e271 * sqrt((_e272 * 6.2831855f))) * cos(((_e276 * _e277) + _e279))) * exp((_e287 * -((_e283 * _e284))))); - let _e291 = phase; - let _e294 = (*shift)[0u]; - let _e298 = phase; + let _e280 = val; + let _e281 = variance_1; + let _e285 = pos; + let _e286 = phase; + let _e288 = (*shift); + let _e292 = phase; + let _e293 = phase; + let _e296 = variance_1; + xyz = (((_e280 * sqrt((_e281 * 6.2831855f))) * cos(((_e285 * _e286) + _e288))) * exp((_e296 * -((_e292 * _e293))))); let _e300 = phase; - let _e305 = xyz[0u]; - xyz[0u] = (_e305 + ((0.00000001644083f * cos(((2239900f * _e291) + _e294))) * exp(((-4528200000f * _e298) * _e300)))); - let _e308 = xyz; - xyz = (_e308 / vec3(0.00000010685f)); - let _e311 = xyz; - return (mat3x3(vec3(3.2404542f, -0.969266f, 0.0556434f), vec3(-1.5371385f, 1.8760108f, -0.2040259f), vec3(-0.4985314f, 0.041556f, 1.0572252f)) * _e311); + let _e303 = (*shift)[0u]; + let _e307 = phase; + let _e309 = phase; + let _e314 = xyz[0u]; + xyz[0u] = (_e314 + ((0.00000001644083f * cos(((2239900f * _e300) + _e303))) * exp(((-4528200000f * _e307) * _e309)))); + let _e317 = xyz; + xyz = (_e317 / vec3(0.00000010685f)); + let _e320 = xyz; + return (mat3x3(vec3(3.2404542f, -0.969266f, 0.0556434f), vec3(-1.5371385f, 1.8760108f, -0.2040259f), vec3(-0.4985314f, 0.041556f, 1.0572252f)) * _e320); } fn FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b(film: ptr, cos1_: ptr, thickness: ptr, base: ptr>) -> vec3 { @@ -19746,312 +20516,291 @@ fn FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b(film: ptr; var cm: vec3; var m_1: i32; - var param_201: f32; - var param_202: vec3; - var local_36: vec3; - - let _e290 = (*film); - let _e291 = (*thickness); - eta2_ = mix(1f, _e290, smoothstep(0f, 0.03f, _e291)); - let _e294 = (*cos1_); - let _e295 = (*cos1_); - let _e298 = eta2_; - let _e299 = eta2_; - sin2Sq = ((1f - (_e294 * _e295)) / (_e298 * _e299)); - let _e302 = sin2Sq; - cos2Sq = (1f - _e302); - let _e304 = cos2Sq; - cos2_ = sqrt(max(_e304, 0f)); + var param_208: f32; + var param_209: vec3; + var local_38: vec3; + + let _e299 = (*film); + let _e300 = (*thickness); + eta2_ = mix(1f, _e299, smoothstep(0f, 0.03f, _e300)); + let _e303 = (*cos1_); + let _e304 = (*cos1_); let _e307 = eta2_; - let _e309 = eta2_; - r0_ = ((_e307 - 1f) / (_e309 + 1f)); - let _e312 = r0_; - let _e313 = r0_; - let _e315 = r0_; - let _e316 = r0_; - let _e319 = (*cos1_); - r12_ = ((_e312 * _e313) + ((1f - (_e315 * _e316)) * pow((1f - _e319), 5f))); - let _e324 = r12_; - t121_ = (1f - _e324); - let _e326 = eta2_; - phi12_ = select(0f, 3.1415927f, (_e326 < 1f)); - let _e329 = phi12_; - phi21_ = (3.1415927f - _e329); - let _e331 = (*base); - sqrtBase = sqrt(clamp(_e331, vec3(0f, 0f, 0f), vec3(0.9999f, 0.9999f, 0.9999f))); - let _e334 = sqrtBase; - let _e336 = sqrtBase; - baseIor = ((vec3(1f, 1f, 1f) + _e334) / (vec3(1f, 1f, 1f) - _e336)); - let _e339 = baseIor; - let _e340 = eta2_; - let _e343 = baseIor; - let _e344 = eta2_; - r1_ = ((_e339 - vec3(_e340)) / (_e343 + vec3(_e344))); - let _e348 = r1_; - let _e349 = r1_; - r1_ = (_e349 * _e348); - let _e351 = r1_; - let _e352 = r1_; - let _e354 = cos2_; - r23_ = (_e351 + ((vec3(1f, 1f, 1f) - _e352) * pow((1f - _e354), 5f))); - let _e360 = baseIor[0u]; - let _e361 = eta2_; - let _e365 = baseIor[1u]; - let _e366 = eta2_; - let _e370 = baseIor[2u]; - let _e371 = eta2_; - phi23_ = vec3(select(0f, 3.1415927f, (_e360 < _e361)), select(0f, 3.1415927f, (_e365 < _e366)), select(0f, 3.1415927f, (_e370 < _e371))); + let _e308 = eta2_; + sin2Sq = ((1f - (_e303 * _e304)) / (_e307 * _e308)); + let _e311 = sin2Sq; + cos2Sq = (1f - _e311); + let _e313 = cos2Sq; + cos2_ = sqrt(max(_e313, 0f)); + let _e316 = eta2_; + let _e318 = eta2_; + r0_ = ((_e316 - 1f) / (_e318 + 1f)); + let _e321 = r0_; + let _e322 = r0_; + let _e324 = r0_; + let _e325 = r0_; + let _e328 = (*cos1_); + r12_ = ((_e321 * _e322) + ((1f - (_e324 * _e325)) * pow((1f - _e328), 5f))); + let _e333 = r12_; + t121_ = (1f - _e333); + let _e335 = eta2_; + phi12_ = select(0f, 3.1415927f, (_e335 < 1f)); + let _e338 = phi12_; + phi21_ = (3.1415927f - _e338); + let _e340 = (*base); + sqrtBase = sqrt(clamp(_e340, vec3(0f, 0f, 0f), vec3(0.9999f, 0.9999f, 0.9999f))); + let _e343 = sqrtBase; + let _e345 = sqrtBase; + baseIor = ((vec3(1f, 1f, 1f) + _e343) / (vec3(1f, 1f, 1f) - _e345)); + let _e348 = baseIor; + let _e349 = eta2_; + let _e352 = baseIor; + let _e353 = eta2_; + r1_ = ((_e348 - vec3(_e349)) / (_e352 + vec3(_e353))); + let _e357 = r1_; + let _e358 = r1_; + r1_ = (_e358 * _e357); + let _e360 = r1_; + let _e361 = r1_; + let _e363 = cos2_; + r23_ = (_e360 + ((vec3(1f, 1f, 1f) - _e361) * pow((1f - _e363), 5f))); + let _e369 = baseIor[0u]; + let _e370 = eta2_; + let _e374 = baseIor[1u]; let _e375 = eta2_; - let _e377 = (*thickness); - let _e379 = cos2_; - opd_1 = (((2f * _e375) * _e377) * _e379); - let _e381 = phi21_; - let _e383 = phi23_; - phi = (vec3(_e381) + _e383); - let _e385 = r12_; - let _e386 = r23_; - r123_ = clamp((_e386 * _e385), vec3(0.00001f, 0.00001f, 0.00001f), vec3(0.9999f, 0.9999f, 0.9999f)); - let _e389 = r123_; - rootR123_ = sqrt(_e389); - let _e391 = t121_; - let _e392 = t121_; - let _e394 = r23_; - let _e396 = r123_; - rs = ((_e394 * (_e391 * _e392)) / (vec3(1f, 1f, 1f) - _e396)); - let _e399 = r12_; - let _e401 = rs; - total_2 = (vec3(_e399) + _e401); - let _e403 = rs; - let _e404 = t121_; - cm = (_e403 - vec3(_e404)); + let _e379 = baseIor[2u]; + let _e380 = eta2_; + phi23_ = vec3(select(0f, 3.1415927f, (_e369 < _e370)), select(0f, 3.1415927f, (_e374 < _e375)), select(0f, 3.1415927f, (_e379 < _e380))); + let _e384 = eta2_; + let _e386 = (*thickness); + let _e388 = cos2_; + opd_1 = (((2f * _e384) * _e386) * _e388); + let _e390 = phi21_; + let _e392 = phi23_; + phi = (vec3(_e390) + _e392); + let _e394 = r12_; + let _e395 = r23_; + r123_ = clamp((_e395 * _e394), vec3(0.00001f, 0.00001f, 0.00001f), vec3(0.9999f, 0.9999f, 0.9999f)); + let _e398 = r123_; + rootR123_ = sqrt(_e398); + let _e400 = t121_; + let _e401 = t121_; + let _e403 = r23_; + let _e405 = r123_; + rs = ((_e403 * (_e400 * _e401)) / (vec3(1f, 1f, 1f) - _e405)); + let _e408 = r12_; + let _e410 = rs; + total_2 = (vec3(_e408) + _e410); + let _e412 = rs; + let _e413 = t121_; + cm = (_e412 - vec3(_e413)); m_1 = 1i; loop { - let _e407 = m_1; - if (_e407 <= 2i) { - let _e409 = rootR123_; - let _e410 = cm; - cm = (_e410 * _e409); - let _e412 = cm; - let _e414 = m_1; - let _e416 = opd_1; - let _e418 = m_1; - let _e420 = phi; - param_201 = (f32(_e414) * _e416); - param_202 = (_e420 * f32(_e418)); - let _e422 = IridescenceSensitivity_u0028_f1_u003b_vf3_u003b((¶m_201), (¶m_202)); - let _e424 = total_2; - total_2 = (_e424 + ((_e412 * 2f) * _e422)); + let _e416 = m_1; + if (_e416 <= 2i) { + let _e418 = rootR123_; + let _e419 = cm; + cm = (_e419 * _e418); + let _e421 = cm; + let _e423 = m_1; + let _e425 = opd_1; + let _e427 = m_1; + let _e429 = phi; + param_208 = (f32(_e423) * _e425); + param_209 = (_e429 * f32(_e427)); + let _e431 = IridescenceSensitivity_u0028_f1_u003b_vf3_u003b((¶m_208), (¶m_209)); + let _e433 = total_2; + total_2 = (_e433 + ((_e421 * 2f) * _e431)); continue; } else { break; } continuing { - let _e426 = m_1; - m_1 = (_e426 + 1i); + let _e435 = m_1; + m_1 = (_e435 + 1i); } } - let _e428 = cos2Sq; - if (_e428 < 0f) { - local_36 = vec3(1f, 1f, 1f); + let _e437 = cos2Sq; + if (_e437 < 0f) { + local_38 = vec3(1f, 1f, 1f); } else { - let _e430 = total_2; - local_36 = max(_e430, vec3(0f, 0f, 0f)); + let _e439 = total_2; + local_38 = max(_e439, vec3(0f, 0f, 0f)); } - let _e432 = local_36; - return _e432; + let _e441 = local_38; + return _e441; } -fn ReadIridescence_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_10: ptr) { +fn ReadIridescence_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_11: ptr) { var f0_3: vec3; - var param_203: f32; - var param_204: f32; - var param_205: f32; - var param_206: vec3; - - let _e267 = g_f0_dielectric; - let _e269 = (*s_10).albedo; - let _e271 = (*s_10).metallic; - f0_3 = mix(_e267, _e269, vec3(clamp(_e271, 0f, 1f))); - let _e277 = layer_info.iridescence[1u]; - param_203 = _e277; - let _e279 = (*s_10).n_dot_v; - param_204 = _e279; - let _e282 = layer_info.iridescence[2u]; - param_205 = _e282; - let _e283 = f0_3; - param_206 = _e283; - let _e284 = FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b((¶m_203), (¶m_204), (¶m_205), (¶m_206)); - g_irid_fresnel = _e284; + var param_210: f32; + var param_211: f32; + var param_212: f32; + var param_213: vec3; + + let _e276 = g_f0_dielectric; + let _e278 = (*s_11).albedo; + let _e280 = (*s_11).metallic; + f0_3 = mix(_e276, _e278, vec3(clamp(_e280, 0f, 1f))); + let _e286 = layer_info.iridescence[1u]; + param_210 = _e286; + let _e288 = (*s_11).n_dot_v; + param_211 = _e288; + let _e291 = layer_info.iridescence[2u]; + param_212 = _e291; + let _e292 = f0_3; + param_213 = _e292; + let _e293 = FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b((¶m_210), (¶m_211), (¶m_212), (¶m_213)); + g_irid_fresnel = _e293; return; } -fn ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_11: ptr) { - var param_207: f32; - var param_208: f32; - var param_209: f32; +fn ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_12: ptr) { + var param_214: f32; + var param_215: f32; + var param_216: f32; var t_2: vec3; var usable: bool; - var local_37: vec3; + var local_39: vec3; var b_4: vec3; var turn_2: vec2; var along_1: vec3; - var local_38: f32; - var local_39: vec3; - var phi_4717_: bool; - - let _e274 = (*s_11).n_dot_v; - let _e278 = g_sheen_roughness; - param_207 = _e278; - param_208 = sqrt(clamp((1f - _e274), 0f, 1f)); - param_209 = 1f; - let _e279 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_207), (¶m_208), (¶m_209)); - let _e280 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e279, 0f); - g_sheen_albedo = _e280.z; - let _e283 = g_sheen[0u]; - let _e285 = g_sheen[1u]; - let _e288 = g_sheen[2u]; - let _e290 = g_sheen_albedo; - g_sheen_scale = (1f - (max(max(_e283, _e285), _e288) * _e290)); - let _e293 = v_tangent_1; - let _e296 = (*s_11).n; - let _e298 = (*s_11).n; - let _e299 = v_tangent_1; - t_2 = (_e293.xyz - (_e296 * dot(_e298, _e299.xyz))); - let _e304 = t_2; - let _e305 = t_2; - usable = (dot(_e304, _e305) > 0.000000000001f); - let _e308 = usable; - if _e308 { - let _e309 = t_2; - local_37 = normalize(_e309); - } else { - local_37 = vec3(1f, 0f, 0f); - } - let _e311 = local_37; - t_2 = _e311; - let _e313 = (*s_11).n; + var local_40: f32; + var local_41: vec3; + var phi_4888_: bool; + + let _e283 = (*s_12).n_dot_v; + let _e287 = g_sheen_roughness; + param_214 = _e287; + param_215 = sqrt(clamp((1f - _e283), 0f, 1f)); + param_216 = 1f; + let _e288 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_214), (¶m_215), (¶m_216)); + let _e289 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e288, 0f); + g_sheen_albedo = _e289.z; + let _e292 = g_sheen[0u]; + let _e294 = g_sheen[1u]; + let _e297 = g_sheen[2u]; + let _e299 = g_sheen_albedo; + g_sheen_scale = (1f - (max(max(_e292, _e294), _e297) * _e299)); + let _e302 = v_tangent_1; + let _e305 = (*s_12).n; + let _e307 = (*s_12).n; + let _e308 = v_tangent_1; + t_2 = (_e302.xyz - (_e305 * dot(_e307, _e308.xyz))); + let _e313 = t_2; let _e314 = t_2; - let _e317 = v_tangent_1[3u]; - b_4 = (cross(_e313, _e314) * _e317); - let _e319 = gl_FrontFacing_1; - if !(_e319) { - let _e321 = t_2; - t_2 = -(_e321); - } - let _e324 = layer_info.anisotropy; - turn_2 = _e324.yz; - let _e326 = t_2; - let _e328 = turn_2[0u]; - let _e330 = b_4; - let _e332 = turn_2[1u]; - along_1 = ((_e326 * _e328) + (_e330 * _e332)); - let _e335 = usable; - phi_4717_ = _e335; - if _e335 { - let _e336 = along_1; - let _e337 = along_1; - phi_4717_ = (dot(_e336, _e337) > 0.000000000001f); - } - let _e341 = phi_4717_; - if _e341 { - let _e344 = layer_info.anisotropy[0u]; - local_38 = clamp(_e344, 0f, 1f); + usable = (dot(_e313, _e314) > 0.000000000001f); + let _e317 = usable; + if _e317 { + let _e318 = t_2; + local_39 = normalize(_e318); } else { - local_38 = 0f; - } - let _e346 = local_38; - g_aniso = _e346; - let _e347 = g_aniso; - if (_e347 > 0f) { - let _e349 = along_1; - local_39 = normalize(_e349); + local_39 = vec3(1f, 0f, 0f); + } + let _e320 = local_39; + t_2 = _e320; + let _e322 = (*s_12).n; + let _e323 = t_2; + let _e326 = v_tangent_1[3u]; + b_4 = (cross(_e322, _e323) * _e326); + let _e328 = gl_FrontFacing_1; + if !(_e328) { + let _e330 = t_2; + t_2 = -(_e330); + } + let _e333 = layer_info.anisotropy; + turn_2 = _e333.yz; + let _e335 = t_2; + let _e337 = turn_2[0u]; + let _e339 = b_4; + let _e341 = turn_2[1u]; + along_1 = ((_e335 * _e337) + (_e339 * _e341)); + let _e344 = usable; + phi_4888_ = _e344; + if _e344 { + let _e345 = along_1; + let _e346 = along_1; + phi_4888_ = (dot(_e345, _e346) > 0.000000000001f); + } + let _e350 = phi_4888_; + if _e350 { + let _e353 = layer_info.anisotropy[0u]; + local_40 = clamp(_e353, 0f, 1f); } else { - let _e351 = t_2; - local_39 = _e351; - } - let _e352 = local_39; - g_aniso_t = _e352; - let _e354 = (*s_11).n; - let _e355 = g_aniso_t; - g_aniso_b = cross(_e354, _e355); + local_40 = 0f; + } + let _e355 = local_40; + g_aniso = _e355; + let _e356 = g_aniso; + if (_e356 > 0f) { + let _e358 = along_1; + local_41 = normalize(_e358); + } else { + let _e360 = t_2; + local_41 = _e360; + } + let _e361 = local_41; + g_aniso_t = _e361; + let _e363 = (*s_12).n; + let _e364 = g_aniso_t; + g_aniso_b = cross(_e363, _e364); return; } fn MaterialLodBias_u0028_() -> f32 { - let _e263 = frag_info.target_origin[1u]; - return _e263; -} - -fn MapUv_u0028_i1_u003b(slot_6: ptr) -> vec2 { - var uvw: vec3; - - let _e263 = v_texcoord_1; - uvw = vec3(_e263.x, _e263.y, 1f); - let _e267 = (*slot_6); - let _e271 = layer_info.uv_transform[(_e267 * 2i)]; - let _e273 = uvw; - let _e275 = (*slot_6); - let _e280 = layer_info.uv_transform[((_e275 * 2i) + 1i)]; - let _e282 = uvw; - return vec2(dot(_e271.xyz, _e273), dot(_e280.xyz, _e282)); + let _e272 = frag_info.target_origin[1u]; + return _e272; } -fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_12: ptr) { +fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_13: ptr) { var orm: vec3; - var param_210: i32; - - param_210 = 1i; - let _e264 = MapUv_u0028_i1_u003b((¶m_210)); - let _e265 = MaterialLodBias_u0028_(); - let _e266 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e264, _e265); - orm = _e266.xyz; - let _e269 = (*s_12).metallic; - let _e271 = orm[2u]; - (*s_12).metallic = clamp((_e269 * _e271), 0f, 1f); - let _e276 = (*s_12).roughness; - let _e278 = orm[1u]; - (*s_12).roughness = clamp((_e276 * _e278), 0.02f, 1f); + var param_217: i32; + + param_217 = 1i; + let _e273 = MapUv_u0028_i1_u003b((¶m_217)); + let _e274 = MaterialLodBias_u0028_(); + let _e275 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e273, _e274); + orm = _e275.xyz; + let _e278 = (*s_13).metallic; + let _e280 = orm[2u]; + (*s_13).metallic = clamp((_e278 * _e280), 0f, 1f); + let _e285 = (*s_13).roughness; + let _e287 = orm[1u]; + (*s_13).roughness = clamp((_e285 * _e287), 0.02f, 1f); return; } -fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e262 = (*srgb); - let _e265 = (*srgb); - let _e270 = (*srgb); - return mix((_e262 / vec3(12.92f)), pow(((_e265 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e270)); -} - -fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_13: ptr) { +fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_14: ptr) { var emissive: vec3; - var param_211: i32; - var param_212: vec3; - - param_211 = 4i; - let _e265 = MapUv_u0028_i1_u003b((¶m_211)); - let _e266 = MaterialLodBias_u0028_(); - let _e267 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e265, _e266); - param_212 = _e267.xyz; - let _e269 = SrgbToLinear_u0028_vf3_u003b((¶m_212)); - emissive = _e269; - let _e270 = emissive; - let _e272 = frag_info.emissive; - let _e277 = frag_info.material2_[3u]; - (*s_13).emissive = ((_e270 * _e272.xyz) * _e277); + var param_218: i32; + var param_219: vec3; + + param_218 = 4i; + let _e274 = MapUv_u0028_i1_u003b((¶m_218)); + let _e275 = MaterialLodBias_u0028_(); + let _e276 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e274, _e275); + param_219 = _e276.xyz; + let _e278 = SrgbToLinear_u0028_vf3_u003b((¶m_219)); + emissive = _e278; + let _e279 = emissive; + let _e281 = frag_info.emissive; + let _e286 = frag_info.material2_[3u]; + (*s_14).emissive = ((_e279 * _e281.xyz) * _e286); return; } -fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_14: ptr) { +fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_15: ptr) { var occlusion: f32; - var param_213: i32; - - param_213 = 3i; - let _e264 = MapUv_u0028_i1_u003b((¶m_213)); - let _e265 = MaterialLodBias_u0028_(); - let _e266 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e264, _e265); - occlusion = _e266.x; - let _e268 = occlusion; - let _e271 = frag_info.material2_[2u]; - (*s_14).occlusion = mix(1f, _e268, clamp(_e271, 0f, 1f)); + var param_220: i32; + + param_220 = 3i; + let _e273 = MapUv_u0028_i1_u003b((¶m_220)); + let _e274 = MaterialLodBias_u0028_(); + let _e275 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e273, _e274); + occlusion = _e275.x; + let _e277 = occlusion; + let _e280 = frag_info.material2_[2u]; + (*s_15).occlusion = mix(1f, _e277, clamp(_e280, 0f, 1f)); return; } @@ -20059,176 +20808,176 @@ fn MapMatrix_u0028_i1_u003b(slot_7: ptr) -> vec4 { var u_2: vec4; var v_3: vec4; - let _e264 = (*slot_7); - let _e268 = layer_info.uv_transform[(_e264 * 2i)]; - u_2 = _e268; - let _e269 = (*slot_7); - let _e274 = layer_info.uv_transform[((_e269 * 2i) + 1i)]; - v_3 = _e274; - let _e276 = u_2[0u]; - let _e278 = u_2[1u]; - let _e280 = v_3[0u]; - let _e282 = v_3[1u]; - return vec4(_e276, _e278, _e280, _e282); + let _e273 = (*slot_7); + let _e277 = layer_info.uv_transform[(_e273 * 2i)]; + u_2 = _e277; + let _e278 = (*slot_7); + let _e283 = layer_info.uv_transform[((_e278 * 2i) + 1i)]; + v_3 = _e283; + let _e285 = u_2[0u]; + let _e287 = u_2[1u]; + let _e289 = v_3[0u]; + let _e291 = v_3[1u]; + return vec4(_e285, _e287, _e289, _e291); } -fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_15: ptr) { +fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_16: ptr) { var sampledTexel: vec4; - var param_214: i32; + var param_221: i32; var t_3: vec3; var b_5: vec3; var m_2: vec4; - var param_215: i32; + var param_222: i32; var det: f32; var flip: f32; var front: vec3; - var local_40: vec3; + var local_42: vec3; var turned: vec3; var turns: bool; - var local_41: vec3; - var local_42: vec3; + var local_43: vec3; + var local_44: vec3; var sampled: vec3; - var phi_3536_: bool; - var phi_3543_: bool; - var phi_3550_: bool; - var phi_3557_: bool; - - param_214 = 2i; - let _e277 = MapUv_u0028_i1_u003b((¶m_214)); - let _e278 = MaterialLodBias_u0028_(); - let _e279 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e277, _e278); - sampledTexel = _e279; - let _e280 = v_tangent_1; - t_3 = _e280.xyz; - let _e282 = t_3; - let _e284 = (*s_15).n; - let _e286 = (*s_15).n; - let _e287 = t_3; - t_3 = (_e282 - (_e284 * dot(_e286, _e287))); + var phi_3707_: bool; + var phi_3714_: bool; + var phi_3721_: bool; + var phi_3728_: bool; + + param_221 = 2i; + let _e286 = MapUv_u0028_i1_u003b((¶m_221)); + let _e287 = MaterialLodBias_u0028_(); + let _e288 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e286, _e287); + sampledTexel = _e288; + let _e289 = v_tangent_1; + t_3 = _e289.xyz; let _e291 = t_3; - let _e292 = t_3; - if (dot(_e291, _e292) < 0.000000000001f) { + let _e293 = (*s_16).n; + let _e295 = (*s_16).n; + let _e296 = t_3; + t_3 = (_e291 - (_e293 * dot(_e295, _e296))); + let _e300 = t_3; + let _e301 = t_3; + if (dot(_e300, _e301) < 0.000000000001f) { return; } - let _e295 = t_3; - t_3 = normalize(_e295); - let _e298 = (*s_15).n; - let _e299 = t_3; - let _e302 = v_tangent_1[3u]; - b_5 = (cross(_e298, _e299) * _e302); - param_215 = 2i; - let _e304 = MapMatrix_u0028_i1_u003b((¶m_215)); - m_2 = _e304; - let _e306 = m_2[0u]; - let _e308 = m_2[3u]; - let _e311 = m_2[1u]; - let _e313 = m_2[2u]; - det = ((_e306 * _e308) - (_e311 * _e313)); - let _e316 = det; - flip = select(1f, -1f, (_e316 < 0f)); - let _e319 = gl_FrontFacing_1; - if _e319 { - let _e320 = b_5; - local_40 = _e320; + let _e304 = t_3; + t_3 = normalize(_e304); + let _e307 = (*s_16).n; + let _e308 = t_3; + let _e311 = v_tangent_1[3u]; + b_5 = (cross(_e307, _e308) * _e311); + param_222 = 2i; + let _e313 = MapMatrix_u0028_i1_u003b((¶m_222)); + m_2 = _e313; + let _e315 = m_2[0u]; + let _e317 = m_2[3u]; + let _e320 = m_2[1u]; + let _e322 = m_2[2u]; + det = ((_e315 * _e317) - (_e320 * _e322)); + let _e325 = det; + flip = select(1f, -1f, (_e325 < 0f)); + let _e328 = gl_FrontFacing_1; + if _e328 { + let _e329 = b_5; + local_42 = _e329; } else { - let _e321 = b_5; - local_40 = -(_e321); - } - let _e323 = local_40; - front = _e323; - let _e324 = t_3; - let _e326 = m_2[3u]; - let _e328 = front; - let _e330 = m_2[2u]; - let _e333 = flip; - turned = (((_e324 * _e326) + (_e328 * _e330)) * _e333); - let _e336 = m_2[1u]; - let _e337 = (_e336 != 0f); - phi_3536_ = _e337; - if !(_e337) { - let _e340 = m_2[2u]; - phi_3536_ = (_e340 != 0f); - } - let _e343 = phi_3536_; - phi_3543_ = _e343; - if !(_e343) { - let _e346 = m_2[0u]; - phi_3543_ = (_e346 < 0f); - } - let _e349 = phi_3543_; - phi_3550_ = _e349; - if !(_e349) { - let _e352 = m_2[3u]; - phi_3550_ = (_e352 < 0f); - } - let _e355 = phi_3550_; - phi_3557_ = _e355; - if _e355 { - let _e356 = turned; - let _e357 = turned; - phi_3557_ = (dot(_e356, _e357) > 0.000000000001f); - } - let _e361 = phi_3557_; - turns = _e361; - let _e362 = turns; - if _e362 { - let _e363 = turned; - local_41 = normalize(_e363); + let _e330 = b_5; + local_42 = -(_e330); + } + let _e332 = local_42; + front = _e332; + let _e333 = t_3; + let _e335 = m_2[3u]; + let _e337 = front; + let _e339 = m_2[2u]; + let _e342 = flip; + turned = (((_e333 * _e335) + (_e337 * _e339)) * _e342); + let _e345 = m_2[1u]; + let _e346 = (_e345 != 0f); + phi_3707_ = _e346; + if !(_e346) { + let _e349 = m_2[2u]; + phi_3707_ = (_e349 != 0f); + } + let _e352 = phi_3707_; + phi_3714_ = _e352; + if !(_e352) { + let _e355 = m_2[0u]; + phi_3714_ = (_e355 < 0f); + } + let _e358 = phi_3714_; + phi_3721_ = _e358; + if !(_e358) { + let _e361 = m_2[3u]; + phi_3721_ = (_e361 < 0f); + } + let _e364 = phi_3721_; + phi_3728_ = _e364; + if _e364 { + let _e365 = turned; + let _e366 = turned; + phi_3728_ = (dot(_e365, _e366) > 0.000000000001f); + } + let _e370 = phi_3728_; + turns = _e370; + let _e371 = turns; + if _e371 { + let _e372 = turned; + local_43 = normalize(_e372); } else { - let _e365 = t_3; - local_41 = _e365; - } - let _e366 = local_41; - t_3 = _e366; - let _e367 = turns; - if _e367 { - let _e369 = (*s_15).n; - let _e370 = t_3; - let _e373 = v_tangent_1[3u]; - let _e375 = flip; - local_42 = ((cross(_e369, _e370) * _e373) * _e375); + let _e374 = t_3; + local_43 = _e374; + } + let _e375 = local_43; + t_3 = _e375; + let _e376 = turns; + if _e376 { + let _e378 = (*s_16).n; + let _e379 = t_3; + let _e382 = v_tangent_1[3u]; + let _e384 = flip; + local_44 = ((cross(_e378, _e379) * _e382) * _e384); } else { - let _e377 = b_5; - local_42 = _e377; - } - let _e378 = local_42; - b_5 = _e378; - let _e379 = gl_FrontFacing_1; - if !(_e379) { - let _e381 = t_3; - t_3 = -(_e381); - } - let _e383 = sampledTexel; - sampled = ((_e383.xyz * 2f) - vec3(1f)); - let _e390 = frag_info.emissive[3u]; - if (_e390 > 0.5f) { - let _e392 = sampled; - let _e394 = sampled; - sampled[2u] = sqrt(max((1f - dot(_e392.xy, _e394.xy)), 0f)); - } - let _e403 = frag_info.material2_[1u]; - let _e404 = sampled; - let _e406 = (_e404.xy * _e403); - sampled[0u] = _e406.x; - sampled[1u] = _e406.y; - let _e411 = t_3; - let _e413 = sampled[0u]; - let _e415 = b_5; - let _e417 = sampled[1u]; - let _e421 = (*s_15).n; - let _e423 = sampled[2u]; - (*s_15).n = normalize((((_e411 * _e413) + (_e415 * _e417)) + (_e421 * _e423))); - let _e429 = (*s_15).n; - let _e431 = (*s_15).v; - (*s_15).n_dot_v = max(dot(_e429, _e431), 0.0001f); + let _e386 = b_5; + local_44 = _e386; + } + let _e387 = local_44; + b_5 = _e387; + let _e388 = gl_FrontFacing_1; + if !(_e388) { + let _e390 = t_3; + t_3 = -(_e390); + } + let _e392 = sampledTexel; + sampled = ((_e392.xyz * 2f) - vec3(1f)); + let _e399 = frag_info.emissive[3u]; + if (_e399 > 0.5f) { + let _e401 = sampled; + let _e403 = sampled; + sampled[2u] = sqrt(max((1f - dot(_e401.xy, _e403.xy)), 0f)); + } + let _e412 = frag_info.material2_[1u]; + let _e413 = sampled; + let _e415 = (_e413.xy * _e412); + sampled[0u] = _e415.x; + sampled[1u] = _e415.y; + let _e420 = t_3; + let _e422 = sampled[0u]; + let _e424 = b_5; + let _e426 = sampled[1u]; + let _e430 = (*s_16).n; + let _e432 = sampled[2u]; + (*s_16).n = normalize((((_e420 * _e422) + (_e424 * _e426)) + (_e430 * _e432))); + let _e438 = (*s_16).n; + let _e440 = (*s_16).v; + (*s_16).n_dot_v = max(dot(_e438, _e440), 0.0001f); return; } fn AtlasTexel_u0028_vf2_u003b(texel_5: ptr>) -> vec4 { - let _e262 = (*texel_5); - let _e266 = irradiance_info.atlas; - let _e269 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e262 + vec2(0.5f)) * _e266.xy), 0f); - return _e269; + let _e271 = (*texel_5); + let _e275 = irradiance_info.atlas; + let _e278 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e271 + vec2(0.5f)) * _e275.xy), 0f); + return _e278; } fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr>, interior: ptr, uv_8: ptr>) -> vec4 { @@ -20236,50 +20985,50 @@ fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr; var f_5: vec2; var a_6: vec4; - var param_216: vec2; + var param_223: vec2; var b_6: vec4; - var param_217: vec2; - var c: vec4; - var param_218: vec2; + var param_224: vec2; + var c_1: vec4; + var param_225: vec2; var d_6: vec4; - var param_219: vec2; - - let _e275 = (*uv_8); - let _e276 = (*interior); - at_5 = ((vec2(1f) + (_e275 * _e276)) - vec2(0.5f)); - let _e282 = at_5; - low = floor(_e282); - let _e284 = at_5; - let _e285 = low; - f_5 = (_e284 - _e285); - let _e287 = (*corner); - let _e288 = low; - param_216 = (_e287 + _e288); - let _e290 = AtlasTexel_u0028_vf2_u003b((¶m_216)); - a_6 = _e290; - let _e291 = (*corner); - let _e292 = low; - param_217 = ((_e291 + _e292) + vec2(1f, 0f)); - let _e295 = AtlasTexel_u0028_vf2_u003b((¶m_217)); - b_6 = _e295; + var param_226: vec2; + + let _e284 = (*uv_8); + let _e285 = (*interior); + at_5 = ((vec2(1f) + (_e284 * _e285)) - vec2(0.5f)); + let _e291 = at_5; + low = floor(_e291); + let _e293 = at_5; + let _e294 = low; + f_5 = (_e293 - _e294); let _e296 = (*corner); let _e297 = low; - param_218 = ((_e296 + _e297) + vec2(0f, 1f)); - let _e300 = AtlasTexel_u0028_vf2_u003b((¶m_218)); - c = _e300; - let _e301 = (*corner); - let _e302 = low; - param_219 = ((_e301 + _e302) + vec2(1f, 1f)); - let _e305 = AtlasTexel_u0028_vf2_u003b((¶m_219)); - d_6 = _e305; - let _e306 = a_6; - let _e307 = b_6; - let _e309 = f_5[0u]; - let _e312 = c; - let _e313 = d_6; - let _e315 = f_5[0u]; - let _e319 = f_5[1u]; - return mix(mix(_e306, _e307, vec4(_e309)), mix(_e312, _e313, vec4(_e315)), vec4(_e319)); + param_223 = (_e296 + _e297); + let _e299 = AtlasTexel_u0028_vf2_u003b((¶m_223)); + a_6 = _e299; + let _e300 = (*corner); + let _e301 = low; + param_224 = ((_e300 + _e301) + vec2(1f, 0f)); + let _e304 = AtlasTexel_u0028_vf2_u003b((¶m_224)); + b_6 = _e304; + let _e305 = (*corner); + let _e306 = low; + param_225 = ((_e305 + _e306) + vec2(0f, 1f)); + let _e309 = AtlasTexel_u0028_vf2_u003b((¶m_225)); + c_1 = _e309; + let _e310 = (*corner); + let _e311 = low; + param_226 = ((_e310 + _e311) + vec2(1f, 1f)); + let _e314 = AtlasTexel_u0028_vf2_u003b((¶m_226)); + d_6 = _e314; + let _e315 = a_6; + let _e316 = b_6; + let _e318 = f_5[0u]; + let _e321 = c_1; + let _e322 = d_6; + let _e324 = f_5[0u]; + let _e328 = f_5[1u]; + return mix(mix(_e315, _e316, vec4(_e318)), mix(_e321, _e322, vec4(_e324)), vec4(_e328)); } fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec2 { @@ -20287,32 +21036,32 @@ fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec var n_6: vec3; var xy: vec2; - let _e266 = (*direction_1)[0u]; - let _e269 = (*direction_1)[1u]; - let _e273 = (*direction_1)[2u]; - sum_1 = ((abs(_e266) + abs(_e269)) + abs(_e273)); - let _e276 = sum_1; - if (_e276 <= 0f) { + let _e275 = (*direction_1)[0u]; + let _e278 = (*direction_1)[1u]; + let _e282 = (*direction_1)[2u]; + sum_1 = ((abs(_e275) + abs(_e278)) + abs(_e282)); + let _e285 = sum_1; + if (_e285 <= 0f) { return vec2(0.5f, 0.5f); } - let _e278 = (*direction_1); - let _e279 = sum_1; - n_6 = (_e278 / vec3(_e279)); - let _e282 = n_6; - xy = _e282.xy; - let _e285 = n_6[2u]; - if (_e285 < 0f) { - let _e288 = n_6[1u]; - let _e292 = n_6[0u]; - let _e297 = n_6[0u]; - let _e301 = n_6[1u]; - xy = vec2(((1f - abs(_e288)) * select(-1f, 1f, (_e292 >= 0f))), ((1f - abs(_e297)) * select(-1f, 1f, (_e301 >= 0f)))); - } - let _e306 = xy; - return ((_e306 * 0.5f) + vec2(0.5f)); + let _e287 = (*direction_1); + let _e288 = sum_1; + n_6 = (_e287 / vec3(_e288)); + let _e291 = n_6; + xy = _e291.xy; + let _e294 = n_6[2u]; + if (_e294 < 0f) { + let _e297 = n_6[1u]; + let _e301 = n_6[0u]; + let _e306 = n_6[0u]; + let _e310 = n_6[1u]; + xy = vec2(((1f - abs(_e297)) * select(-1f, 1f, (_e301 >= 0f))), ((1f - abs(_e306)) * select(-1f, 1f, (_e310 >= 0f)))); + } + let _e315 = xy; + return ((_e315 * 0.5f) + vec2(0.5f)); } -fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_5: ptr>, normal_1: ptr>, view_1: ptr>) -> vec3 { +fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_5: ptr>, normal_1: ptr>, view_2: ptr>) -> vec3 { var origin_2: vec3; var spacing: vec3; var counts: vec3; @@ -20334,9 +21083,9 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_5: ptr; var irradianceCorner: vec2; var momentCorner: vec2; - var param_220: vec2; + var param_227: vec2; var trilinear: vec3; - var weight_2: f32; + var weight_3: f32; var probePosition: vec3; var toProbe: vec3; var distance_4: f32; @@ -20344,426 +21093,426 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_5: ptr; - var param_221: vec3; - var param_222: vec2; - var param_223: f32; - var param_224: vec2; + var param_228: vec3; + var param_229: vec2; + var param_230: f32; + var param_231: vec2; var chebyshev: f32; var variance_2: f32; var difference: f32; - var param_225: vec3; - var param_226: vec2; - var param_227: f32; - var param_228: vec2; - var local_43: vec3; + var param_232: vec3; + var param_233: vec2; + var param_234: f32; + var param_235: vec2; + var local_45: vec3; - let _e308 = irradiance_info.origin; - origin_2 = _e308.xyz; - let _e311 = irradiance_info.spacing; - spacing = _e311.xyz; - let _e314 = irradiance_info.counts; - counts = _e314.xyz; - let _e318 = irradiance_info.tiles[0u]; - irradianceTile = _e318; - let _e321 = irradiance_info.tiles[1u]; - depthTile = _e321; - let _e324 = irradiance_info.tiles[2u]; - columns = _e324; - let _e327 = irradiance_info.tiles[3u]; - momentsTop = _e327; - let _e328 = (*normal_1); - unit = normalize(_e328); - let _e330 = (*world_5); - let _e331 = unit; - let _e334 = irradiance_info.spacing[3u]; - let _e337 = (*view_1); - let _e340 = irradiance_info.counts[3u]; - biased = ((_e330 + (_e331 * _e334)) + (_e337 * _e340)); - let _e343 = biased; - let _e344 = origin_2; - let _e346 = spacing; - grid_1 = ((_e343 - _e344) / _e346); - let _e348 = grid_1; - let _e350 = counts; - base_1 = clamp(floor(_e348), vec3(0f, 0f, 0f), (_e350 - vec3(2f))); - let _e354 = grid_1; - let _e355 = base_1; - f_6 = clamp((_e354 - _e355), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e317 = irradiance_info.origin; + origin_2 = _e317.xyz; + let _e320 = irradiance_info.spacing; + spacing = _e320.xyz; + let _e323 = irradiance_info.counts; + counts = _e323.xyz; + let _e327 = irradiance_info.tiles[0u]; + irradianceTile = _e327; + let _e330 = irradiance_info.tiles[1u]; + depthTile = _e330; + let _e333 = irradiance_info.tiles[2u]; + columns = _e333; + let _e336 = irradiance_info.tiles[3u]; + momentsTop = _e336; + let _e337 = (*normal_1); + unit = normalize(_e337); + let _e339 = (*world_5); + let _e340 = unit; + let _e343 = irradiance_info.spacing[3u]; + let _e346 = (*view_2); + let _e349 = irradiance_info.counts[3u]; + biased = ((_e339 + (_e340 * _e343)) + (_e346 * _e349)); + let _e352 = biased; + let _e353 = origin_2; + let _e355 = spacing; + grid_1 = ((_e352 - _e353) / _e355); + let _e357 = grid_1; + let _e359 = counts; + base_1 = clamp(floor(_e357), vec3(0f, 0f, 0f), (_e359 - vec3(2f))); + let _e363 = grid_1; + let _e364 = base_1; + f_6 = clamp((_e363 - _e364), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); total_3 = vec3(0f, 0f, 0f); weights = 0f; corner_1 = 0i; loop { - let _e358 = corner_1; - if (_e358 < 8i) { - let _e360 = corner_1; - let _e363 = corner_1; - let _e368 = corner_1; - offset_3 = vec3(f32((_e360 & 1i)), f32(((_e363 >> bitcast(1i)) & 1i)), f32(((_e368 >> bitcast(2i)) & 1i))); - let _e374 = base_1; - let _e375 = offset_3; - cell_1 = (_e374 + _e375); - let _e378 = cell_1[2u]; - let _e380 = counts[1u]; - let _e383 = cell_1[1u]; - let _e386 = counts[0u]; - let _e389 = cell_1[0u]; - probe = ((((_e378 * _e380) + _e383) * _e386) + _e389); - let _e391 = probe; - let _e392 = columns; - let _e397 = probe; - let _e398 = columns; - tile_4 = vec2((_e391 - (floor((_e391 / _e392)) * _e392)), floor((_e397 / _e398))); - let _e402 = tile_4; - let _e403 = irradianceTile; - irradianceCorner = (_e402 * (_e403 + 2f)); - let _e407 = tile_4[0u]; - let _e408 = depthTile; - let _e411 = momentsTop; - let _e413 = tile_4[1u]; - let _e414 = depthTile; - momentCorner = vec2((_e407 * (_e408 + 2f)), (_e411 + (_e413 * (_e414 + 2f)))); - let _e419 = irradianceCorner; - param_220 = (_e419 + vec2(1f)); - let _e422 = AtlasTexel_u0028_vf2_u003b((¶m_220)); - if (_e422.w < 0.5f) { + let _e367 = corner_1; + if (_e367 < 8i) { + let _e369 = corner_1; + let _e372 = corner_1; + let _e377 = corner_1; + offset_3 = vec3(f32((_e369 & 1i)), f32(((_e372 >> bitcast(1i)) & 1i)), f32(((_e377 >> bitcast(2i)) & 1i))); + let _e383 = base_1; + let _e384 = offset_3; + cell_1 = (_e383 + _e384); + let _e387 = cell_1[2u]; + let _e389 = counts[1u]; + let _e392 = cell_1[1u]; + let _e395 = counts[0u]; + let _e398 = cell_1[0u]; + probe = ((((_e387 * _e389) + _e392) * _e395) + _e398); + let _e400 = probe; + let _e401 = columns; + let _e406 = probe; + let _e407 = columns; + tile_4 = vec2((_e400 - (floor((_e400 / _e401)) * _e401)), floor((_e406 / _e407))); + let _e411 = tile_4; + let _e412 = irradianceTile; + irradianceCorner = (_e411 * (_e412 + 2f)); + let _e416 = tile_4[0u]; + let _e417 = depthTile; + let _e420 = momentsTop; + let _e422 = tile_4[1u]; + let _e423 = depthTile; + momentCorner = vec2((_e416 * (_e417 + 2f)), (_e420 + (_e422 * (_e423 + 2f)))); + let _e428 = irradianceCorner; + param_227 = (_e428 + vec2(1f)); + let _e431 = AtlasTexel_u0028_vf2_u003b((¶m_227)); + if (_e431.w < 0.5f) { continue; } - let _e425 = f_6; - let _e427 = f_6; - let _e428 = offset_3; - trilinear = mix((vec3(1f, 1f, 1f) - _e425), _e427, _e428); - let _e431 = trilinear[0u]; - let _e433 = trilinear[1u]; - let _e436 = trilinear[2u]; - weight_2 = max(((_e431 * _e433) * _e436), 0.001f); - let _e439 = origin_2; - let _e440 = spacing; - let _e441 = cell_1; - probePosition = (_e439 + (_e440 * _e441)); - let _e444 = probePosition; - let _e445 = biased; - toProbe = (_e444 - _e445); - let _e447 = toProbe; - distance_4 = length(_e447); - let _e449 = distance_4; - if (_e449 > 0.000001f) { - let _e451 = toProbe; - let _e452 = distance_4; - direction_2 = (_e451 / vec3(_e452)); - let _e455 = unit; - let _e456 = probePosition; - let _e457 = (*world_5); - facing = ((dot(_e455, normalize((_e456 - _e457))) * 0.5f) + 0.5f); - let _e463 = facing; - let _e464 = facing; - probeWeight = ((_e463 * _e464) + 0.2f); - let _e467 = direction_2; - param_221 = -(_e467); - let _e469 = ProbeOctahedral_u0028_vf3_u003b((¶m_221)); - let _e470 = momentCorner; - param_222 = _e470; - let _e471 = depthTile; - param_223 = _e471; - param_224 = _e469; - let _e472 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_222), (¶m_223), (¶m_224)); - moments_3 = _e472.xy; + let _e434 = f_6; + let _e436 = f_6; + let _e437 = offset_3; + trilinear = mix((vec3(1f, 1f, 1f) - _e434), _e436, _e437); + let _e440 = trilinear[0u]; + let _e442 = trilinear[1u]; + let _e445 = trilinear[2u]; + weight_3 = max(((_e440 * _e442) * _e445), 0.001f); + let _e448 = origin_2; + let _e449 = spacing; + let _e450 = cell_1; + probePosition = (_e448 + (_e449 * _e450)); + let _e453 = probePosition; + let _e454 = biased; + toProbe = (_e453 - _e454); + let _e456 = toProbe; + distance_4 = length(_e456); + let _e458 = distance_4; + if (_e458 > 0.000001f) { + let _e460 = toProbe; + let _e461 = distance_4; + direction_2 = (_e460 / vec3(_e461)); + let _e464 = unit; + let _e465 = probePosition; + let _e466 = (*world_5); + facing = ((dot(_e464, normalize((_e465 - _e466))) * 0.5f) + 0.5f); + let _e472 = facing; + let _e473 = facing; + probeWeight = ((_e472 * _e473) + 0.2f); + let _e476 = direction_2; + param_228 = -(_e476); + let _e478 = ProbeOctahedral_u0028_vf3_u003b((¶m_228)); + let _e479 = momentCorner; + param_229 = _e479; + let _e480 = depthTile; + param_230 = _e480; + param_231 = _e478; + let _e481 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_229), (¶m_230), (¶m_231)); + moments_3 = _e481.xy; chebyshev = 1f; - let _e474 = distance_4; - let _e476 = moments_3[0u]; - if (_e474 > _e476) { - let _e479 = moments_3[1u]; - let _e481 = moments_3[0u]; - let _e483 = moments_3[0u]; - variance_2 = max((_e479 - (_e481 * _e483)), 0.000001f); - let _e487 = distance_4; - let _e489 = moments_3[0u]; - difference = (_e487 - _e489); - let _e491 = variance_2; - let _e492 = variance_2; - let _e493 = difference; - let _e494 = difference; - chebyshev = (_e491 / (_e492 + (_e493 * _e494))); - let _e498 = chebyshev; - let _e499 = chebyshev; - let _e501 = chebyshev; - chebyshev = ((_e498 * _e499) * _e501); + let _e483 = distance_4; + let _e485 = moments_3[0u]; + if (_e483 > _e485) { + let _e488 = moments_3[1u]; + let _e490 = moments_3[0u]; + let _e492 = moments_3[0u]; + variance_2 = max((_e488 - (_e490 * _e492)), 0.000001f); + let _e496 = distance_4; + let _e498 = moments_3[0u]; + difference = (_e496 - _e498); + let _e500 = variance_2; + let _e501 = variance_2; + let _e502 = difference; + let _e503 = difference; + chebyshev = (_e500 / (_e501 + (_e502 * _e503))); + let _e507 = chebyshev; + let _e508 = chebyshev; + let _e510 = chebyshev; + chebyshev = ((_e507 * _e508) * _e510); } - let _e503 = probeWeight; - let _e504 = chebyshev; - probeWeight = max((_e503 * max(_e504, 0.05f)), 0.000001f); - let _e508 = probeWeight; - if (_e508 < 0.2f) { - let _e510 = probeWeight; - let _e511 = probeWeight; - let _e514 = probeWeight; - probeWeight = (_e514 * ((_e510 * _e511) * 25f)); + let _e512 = probeWeight; + let _e513 = chebyshev; + probeWeight = max((_e512 * max(_e513, 0.05f)), 0.000001f); + let _e517 = probeWeight; + if (_e517 < 0.2f) { + let _e519 = probeWeight; + let _e520 = probeWeight; + let _e523 = probeWeight; + probeWeight = (_e523 * ((_e519 * _e520) * 25f)); } - let _e516 = probeWeight; - let _e517 = weight_2; - weight_2 = (_e517 * _e516); - } - let _e519 = unit; - param_225 = _e519; - let _e520 = ProbeOctahedral_u0028_vf3_u003b((¶m_225)); - let _e521 = irradianceCorner; - param_226 = _e521; - let _e522 = irradianceTile; - param_227 = _e522; - param_228 = _e520; - let _e523 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_226), (¶m_227), (¶m_228)); - let _e525 = weight_2; - let _e527 = total_3; - total_3 = (_e527 + (_e523.xyz * _e525)); - let _e529 = weight_2; - let _e530 = weights; - weights = (_e530 + _e529); + let _e525 = probeWeight; + let _e526 = weight_3; + weight_3 = (_e526 * _e525); + } + let _e528 = unit; + param_232 = _e528; + let _e529 = ProbeOctahedral_u0028_vf3_u003b((¶m_232)); + let _e530 = irradianceCorner; + param_233 = _e530; + let _e531 = irradianceTile; + param_234 = _e531; + param_235 = _e529; + let _e532 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_233), (¶m_234), (¶m_235)); + let _e534 = weight_3; + let _e536 = total_3; + total_3 = (_e536 + (_e532.xyz * _e534)); + let _e538 = weight_3; + let _e539 = weights; + weights = (_e539 + _e538); continue; } else { break; } continuing { - let _e532 = corner_1; - corner_1 = (_e532 + 1i); + let _e541 = corner_1; + corner_1 = (_e541 + 1i); } } - let _e534 = weights; - if (_e534 > 0f) { - let _e536 = total_3; - let _e537 = weights; - local_43 = (_e536 / vec3(_e537)); + let _e543 = weights; + if (_e543 > 0f) { + let _e545 = total_3; + let _e546 = weights; + local_45 = (_e545 / vec3(_e546)); } else { - local_43 = vec3(0f, 0f, 0f); + local_45 = vec3(0f, 0f, 0f); } - let _e540 = local_43; - return _e540; + let _e549 = local_45; + return _e549; } fn IrradianceEnabled_u0028_() -> bool { - let _e263 = irradiance_info.origin[3u]; - return (_e263 > 0.5f); -} - -fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_16: ptr) { - var param_229: vec3; - var param_230: vec3; - var param_231: vec3; - var param_232: Surface; - var param_233: Surface; - var param_234: Surface; - - let _e268 = IrradianceEnabled_u0028_(); - if _e268 { - let _e269 = v_world_position_1; - param_229 = _e269; - let _e271 = (*s_16).n; - param_230 = _e271; - let _e273 = (*s_16).v; - param_231 = _e273; - let _e274 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_229), (¶m_230), (¶m_231)); - let _e277 = frag_info.material[2u]; - (*s_16).ambient = (_e274 * _e277); - } - let _e280 = (*s_16); - param_232 = _e280; - ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_232)); - let _e281 = param_232; - (*s_16) = _e281; - let _e282 = (*s_16); - param_233 = _e282; - ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_233)); - let _e283 = param_233; - (*s_16) = _e283; - let _e284 = (*s_16); - param_234 = _e284; - ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_234)); - let _e285 = param_234; - (*s_16) = _e285; + let _e272 = irradiance_info.origin[3u]; + return (_e272 > 0.5f); +} + +fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_17: ptr) { + var param_236: vec3; + var param_237: vec3; + var param_238: vec3; + var param_239: Surface; + var param_240: Surface; + var param_241: Surface; + + let _e277 = IrradianceEnabled_u0028_(); + if _e277 { + let _e278 = v_world_position_1; + param_236 = _e278; + let _e280 = (*s_17).n; + param_237 = _e280; + let _e282 = (*s_17).v; + param_238 = _e282; + let _e283 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_236), (¶m_237), (¶m_238)); + let _e286 = frag_info.material[2u]; + (*s_17).ambient = (_e283 * _e286); + } + let _e289 = (*s_17); + param_239 = _e289; + ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_239)); + let _e290 = param_239; + (*s_17) = _e290; + let _e291 = (*s_17); + param_240 = _e291; + ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_240)); + let _e292 = param_240; + (*s_17) = _e292; + let _e293 = (*s_17); + param_241 = _e293; + ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_241)); + let _e294 = param_241; + (*s_17) = _e294; return; } -fn ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_17: ptr) { +fn ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_18: ptr) { var coatTexel: vec4; var sheenTexel: vec4; - var param_235: vec3; + var param_242: vec3; var ior_3: f32; var r_5: f32; - var local_44: f32; + var local_46: f32; var distance_5: f32; - var local_45: vec3; + var local_47: vec3; var fc: f32; - let _e271 = v_texcoord_1; - let _e272 = MaterialLodBias_u0028_(); - let _e273 = textureSampleBias(coat_texture_tex, coat_texture_smp, _e271, _e272); - coatTexel = _e273; - let _e274 = v_texcoord_1; - let _e275 = MaterialLodBias_u0028_(); - let _e276 = textureSampleBias(sheen_texture_tex, sheen_texture_smp, _e274, _e275); - sheenTexel = _e276; - let _e278 = layer_info.sheen; - let _e280 = sheenTexel; - param_235 = _e280.xyz; - let _e282 = SrgbToLinear_u0028_vf3_u003b((¶m_235)); - g_sheen = (_e278.xyz * _e282); - let _e286 = layer_info.sheen[3u]; - let _e288 = sheenTexel[3u]; - g_sheen_roughness = clamp((_e286 * _e288), 0.07f, 1f); - let _e293 = layer_info.coat[2u]; - ior_3 = max(_e293, 1f); - let _e295 = IorInfinite_u0028_(); - if _e295 { - local_44 = 1f; + let _e280 = v_texcoord_1; + let _e281 = MaterialLodBias_u0028_(); + let _e282 = textureSampleBias(coat_texture_tex, coat_texture_smp, _e280, _e281); + coatTexel = _e282; + let _e283 = v_texcoord_1; + let _e284 = MaterialLodBias_u0028_(); + let _e285 = textureSampleBias(sheen_texture_tex, sheen_texture_smp, _e283, _e284); + sheenTexel = _e285; + let _e287 = layer_info.sheen; + let _e289 = sheenTexel; + param_242 = _e289.xyz; + let _e291 = SrgbToLinear_u0028_vf3_u003b((¶m_242)); + g_sheen = (_e287.xyz * _e291); + let _e295 = layer_info.sheen[3u]; + let _e297 = sheenTexel[3u]; + g_sheen_roughness = clamp((_e295 * _e297), 0.07f, 1f); + let _e302 = layer_info.coat[2u]; + ior_3 = max(_e302, 1f); + let _e304 = IorInfinite_u0028_(); + if _e304 { + local_46 = 1f; } else { - let _e296 = ior_3; - let _e298 = ior_3; - local_44 = ((_e296 - 1f) / (_e298 + 1f)); - } - let _e301 = local_44; - r_5 = _e301; - let _e302 = r_5; - let _e303 = r_5; - let _e307 = layer_info.specular; - let _e313 = layer_info.specular[3u]; - g_f0_dielectric = (min((vec3((_e302 * _e303)) * _e307.xyz), vec3(1f, 1f, 1f)) * _e313); - let _e317 = layer_info.specular[3u]; - g_f90_ = _e317; - let _e320 = layer_info.coat[0u]; - let _e322 = coatTexel[0u]; - g_coat = clamp((_e320 * _e322), 0f, 1f); - let _e327 = layer_info.coat[1u]; - let _e329 = coatTexel[1u]; - g_coat_roughness = clamp((_e327 * _e329), 0.02f, 1f); - let _e334 = layer_info.transmission[0u]; - let _e336 = coatTexel[2u]; - g_transmission = clamp((_e334 * _e336), 0f, 1f); - let _e341 = layer_info.transmission[1u]; - let _e343 = coatTexel[3u]; - g_thickness = max((_e341 * _e343), 0f); - let _e348 = layer_info.transmission[2u]; - distance_5 = _e348; - let _e349 = distance_5; - if (_e349 > 0f) { - let _e352 = layer_info.attenuation; - let _e355 = g_thickness; - let _e356 = distance_5; - local_45 = pow(max(_e352.xyz, vec3(0.0001f, 0.0001f, 0.0001f)), vec3((_e355 / _e356))); + let _e305 = ior_3; + let _e307 = ior_3; + local_46 = ((_e305 - 1f) / (_e307 + 1f)); + } + let _e310 = local_46; + r_5 = _e310; + let _e311 = r_5; + let _e312 = r_5; + let _e316 = layer_info.specular; + let _e322 = layer_info.specular[3u]; + g_f0_dielectric = (min((vec3((_e311 * _e312)) * _e316.xyz), vec3(1f, 1f, 1f)) * _e322); + let _e326 = layer_info.specular[3u]; + g_f90_ = _e326; + let _e329 = layer_info.coat[0u]; + let _e331 = coatTexel[0u]; + g_coat = clamp((_e329 * _e331), 0f, 1f); + let _e336 = layer_info.coat[1u]; + let _e338 = coatTexel[1u]; + g_coat_roughness = clamp((_e336 * _e338), 0.02f, 1f); + let _e343 = layer_info.transmission[0u]; + let _e345 = coatTexel[2u]; + g_transmission = clamp((_e343 * _e345), 0f, 1f); + let _e350 = layer_info.transmission[1u]; + let _e352 = coatTexel[3u]; + g_thickness = max((_e350 * _e352), 0f); + let _e357 = layer_info.transmission[2u]; + distance_5 = _e357; + let _e358 = distance_5; + if (_e358 > 0f) { + let _e361 = layer_info.attenuation; + let _e364 = g_thickness; + let _e365 = distance_5; + local_47 = pow(max(_e361.xyz, vec3(0.0001f, 0.0001f, 0.0001f)), vec3((_e364 / _e365))); } else { - local_45 = vec3(1f, 1f, 1f); - } - let _e360 = local_45; - g_transmittance = _e360; - let _e363 = layer_info.iridescence[0u]; - g_iridescence = clamp(_e363, 0f, 1f); - let _e366 = (*s_17).n; - g_coat_n = _e366; - let _e368 = (*s_17).n; - let _e370 = (*s_17).v; - g_coat_n_dot_v = max(dot(_e368, _e370), 0.0001f); - let _e373 = g_coat_n_dot_v; - fc = (0.04f + (0.96f * pow((1f - _e373), 5f))); - let _e378 = g_coat; - let _e379 = fc; - g_coat_through = (1f - (_e378 * _e379)); + local_47 = vec3(1f, 1f, 1f); + } + let _e369 = local_47; + g_transmittance = _e369; + let _e372 = layer_info.iridescence[0u]; + g_iridescence = clamp(_e372, 0f, 1f); + let _e375 = (*s_18).n; + g_coat_n = _e375; + let _e377 = (*s_18).n; + let _e379 = (*s_18).v; + g_coat_n_dot_v = max(dot(_e377, _e379), 0.0001f); + let _e382 = g_coat_n_dot_v; + fc = (0.04f + (0.96f * pow((1f - _e382), 5f))); + let _e387 = g_coat; + let _e388 = fc; + g_coat_through = (1f - (_e387 * _e388)); return; } fn ReadSurface_u0028_() -> Surface { var texel_6: vec4; - var param_236: i32; - var s_18: Surface; - var param_237: vec3; - var param_238: vec3; + var param_243: i32; + var s_19: Surface; + var param_244: vec3; + var param_245: vec3; var cutoff: f32; var anchored: vec3; var noise_1: f32; - param_236 = 0i; - let _e269 = MapUv_u0028_i1_u003b((¶m_236)); - let _e270 = MaterialLodBias_u0028_(); - let _e271 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e269, _e270); - texel_6 = _e271; - let _e272 = texel_6; - param_237 = _e272.xyz; - let _e274 = SrgbToLinear_u0028_vf3_u003b((¶m_237)); - let _e276 = frag_info.base_color; - param_238 = _e276.xyz; - let _e278 = SrgbToLinear_u0028_vf3_u003b((¶m_238)); - let _e280 = v_color_1; - s_18.albedo = ((_e274 * _e278) * _e280.xyz); - let _e285 = texel_6[3u]; - let _e288 = frag_info.base_color[3u]; - let _e291 = v_color_1[3u]; - s_18.alpha = ((_e285 * _e288) * _e291); - let _e295 = s_18.albedo; - g_albedo = _e295; - let _e298 = frag_info.material2_[0u]; - cutoff = _e298; - let _e299 = cutoff; - if (_e299 >= 0f) { - let _e302 = s_18.alpha; - let _e303 = cutoff; - if (_e302 < _e303) { + param_243 = 0i; + let _e278 = MapUv_u0028_i1_u003b((¶m_243)); + let _e279 = MaterialLodBias_u0028_(); + let _e280 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e278, _e279); + texel_6 = _e280; + let _e281 = texel_6; + param_244 = _e281.xyz; + let _e283 = SrgbToLinear_u0028_vf3_u003b((¶m_244)); + let _e285 = frag_info.base_color; + param_245 = _e285.xyz; + let _e287 = SrgbToLinear_u0028_vf3_u003b((¶m_245)); + let _e289 = v_color_1; + s_19.albedo = ((_e283 * _e287) * _e289.xyz); + let _e294 = texel_6[3u]; + let _e297 = frag_info.base_color[3u]; + let _e300 = v_color_1[3u]; + s_19.alpha = ((_e294 * _e297) * _e300); + let _e304 = s_19.albedo; + g_albedo = _e304; + let _e307 = frag_info.material2_[0u]; + cutoff = _e307; + let _e308 = cutoff; + if (_e308 >= 0f) { + let _e311 = s_19.alpha; + let _e312 = cutoff; + if (_e311 < _e312) { discard; } } else { - let _e305 = cutoff; - if (_e305 < -1.5f) { - let _e307 = v_world_position_1; - anchored = floor((_e307 * 16f)); - let _e310 = anchored; - noise_1 = fract((sin(dot(_e310, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e316 = s_18.alpha; - let _e317 = noise_1; - if (_e316 < _e317) { + let _e314 = cutoff; + if (_e314 < -1.5f) { + let _e316 = v_world_position_1; + anchored = floor((_e316 * 16f)); + let _e319 = anchored; + noise_1 = fract((sin(dot(_e319, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e325 = s_19.alpha; + let _e326 = noise_1; + if (_e325 < _e326) { discard; } } } - let _e319 = cutoff; - let _e321 = cutoff; - g_premultiply = ((_e319 < 0f) && (_e321 > -0.75f)); - let _e324 = v_normal_1; - s_18.n = normalize(_e324); - let _e327 = gl_FrontFacing_1; - if !(_e327) { - let _e330 = s_18.n; - s_18.n = -(_e330); - } - let _e334 = frag_info.camera_position; - let _e336 = v_world_position_1; - s_18.v = normalize((_e334.xyz - _e336)); - let _e341 = s_18.n; - let _e343 = s_18.v; - s_18.n_dot_v = max(dot(_e341, _e343), 0.0001f); - let _e349 = frag_info.material[0u]; - s_18.metallic = clamp(_e349, 0f, 1f); - let _e354 = frag_info.material[1u]; - s_18.roughness = clamp(_e354, 0.02f, 1f); - let _e358 = frag_info.ambient_ground; - let _e361 = frag_info.ambient_sky; - let _e365 = s_18.n[1u]; - let _e372 = frag_info.material[2u]; - s_18.ambient = (mix(_e358.xyz, _e361.xyz, vec3(((_e365 * 0.5f) + 0.5f))) * _e372); - let _e377 = frag_info.frame_params[0u]; - s_18.exposure = max(_e377, 0f); - s_18.occlusion = 1f; - s_18.emissive = vec3(0f, 0f, 0f); - let _e382 = s_18; - return _e382; + let _e328 = cutoff; + let _e330 = cutoff; + g_premultiply = ((_e328 < 0f) && (_e330 > -0.75f)); + let _e333 = v_normal_1; + s_19.n = normalize(_e333); + let _e336 = gl_FrontFacing_1; + if !(_e336) { + let _e339 = s_19.n; + s_19.n = -(_e339); + } + let _e343 = frag_info.camera_position; + let _e345 = v_world_position_1; + s_19.v = normalize((_e343.xyz - _e345)); + let _e350 = s_19.n; + let _e352 = s_19.v; + s_19.n_dot_v = max(dot(_e350, _e352), 0.0001f); + let _e358 = frag_info.material[0u]; + s_19.metallic = clamp(_e358, 0f, 1f); + let _e363 = frag_info.material[1u]; + s_19.roughness = clamp(_e363, 0.02f, 1f); + let _e367 = frag_info.ambient_ground; + let _e370 = frag_info.ambient_sky; + let _e374 = s_19.n[1u]; + let _e381 = frag_info.material[2u]; + s_19.ambient = (mix(_e367.xyz, _e370.xyz, vec3(((_e374 * 0.5f) + 0.5f))) * _e381); + let _e386 = frag_info.frame_params[0u]; + s_19.exposure = max(_e386, 0f); + s_19.occlusion = 1f; + s_19.emissive = vec3(0f, 0f, 0f); + let _e391 = s_19; + return _e391; } fn main_1() { - var s_19: Surface; - var param_239: Surface; - var param_240: Surface; - var param_241: Surface; - var param_242: Surface; - var param_243: Surface; + var s_20: Surface; + var param_246: Surface; + var param_247: Surface; + var param_248: Surface; + var param_249: Surface; + var param_250: Surface; var metallic: f32; var diffuseColor_1: vec3; - var param_244: vec3; - var param_245: f32; - var param_246: f32; + var param_251: vec3; + var param_252: f32; + var param_253: f32; var ambient: vec3; var levels_1: f32; var coatAmbient: vec3; @@ -20779,8 +21528,8 @@ fn main_1() { var irradiance: vec3; var prefiltered: vec3; var ab_4: vec2; - var param_247: f32; - var param_248: f32; + var param_254: f32; + var param_255: f32; var single_1: vec3; var specular_1: vec3; var missed: f32; @@ -20788,23 +21537,26 @@ fn main_1() { var multiple: vec3; var reflects: vec3; var through: vec3; - var local_46: vec3; - var param_249: Surface; - var param_250: f32; + var local_48: vec3; + var param_256: Surface; + var param_257: f32; var coatPrefiltered: vec3; var coatAb: vec2; - var param_251: f32; - var param_252: f32; + var param_258: f32; + var param_259: f32; var sceneAb: vec2; - var param_253: f32; - var param_254: f32; + var param_260: f32; + var param_261: f32; var sceneReflects: vec3; - var param_255: Surface; + var param_262: Surface; var sheenAmbient: vec3; - var param_256: Surface; - var param_257: vec3; - var param_258: f32; - var param_259: f32; + var lit_3: vec3; + var param_263: Surface; + var param_264: Surface; + var param_265: vec3; + var param_266: vec3; + var param_267: f32; + var param_268: f32; g_albedo = vec3(0f, 0f, 0f); g_debug_surface = vec3(0f, 0f, 0f); @@ -20831,249 +21583,259 @@ fn main_1() { g_transmittance = vec3(1f, 1f, 1f); g_iridescence = 0f; g_irid_fresnel = vec3(0.04f, 0.04f, 0.04f); - let _e313 = ReadSurface_u0028_(); - s_19 = _e313; - let _e314 = s_19; - param_239 = _e314; - ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_239)); - let _e315 = s_19; - param_240 = _e315; - ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_240)); - let _e316 = param_240; - s_19 = _e316; - let _e317 = s_19; - param_241 = _e317; - ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_241)); - let _e318 = param_241; - s_19 = _e318; - let _e319 = s_19; - param_242 = _e319; - ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_242)); - let _e320 = s_19; - param_243 = _e320; - ReadIridescence_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_243)); - let _e322 = s_19.metallic; - metallic = clamp(_e322, 0f, 1f); - let _e325 = s_19.albedo; - let _e326 = metallic; - diffuseColor_1 = (_e325 * (1f - _e326)); - let _e329 = EonDiffuse_u0028_(); - if _e329 { - let _e330 = diffuseColor_1; - param_244 = _e330; - let _e332 = s_19.roughness; - param_245 = _e332; - let _e334 = s_19.n_dot_v; - param_246 = _e334; - let _e335 = EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_244), (¶m_245), (¶m_246)); - diffuseColor_1 = _e335; - } - let _e336 = diffuseColor_1; - let _e338 = s_19.ambient; - let _e341 = s_19.occlusion; - ambient = ((_e336 * _e338) * _e341); - let _e343 = SceneColourBound_u0028_(); - let _e344 = g_transmittance; - let _e347 = g_transmission; - let _e350 = ambient; - ambient = (_e350 * mix(vec3(1f, 1f, 1f), select(_e344, vec3(0f, 0f, 0f), vec3(_e343)), vec3(_e347))); - let _e354 = frag_info.frame_params[3u]; - levels_1 = _e354; + let _e325 = ReadSurface_u0028_(); + s_20 = _e325; + let _e326 = s_20; + param_246 = _e326; + ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_246)); + let _e327 = s_20; + param_247 = _e327; + ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_247)); + let _e328 = param_247; + s_20 = _e328; + let _e329 = s_20; + param_248 = _e329; + ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_248)); + let _e330 = param_248; + s_20 = _e330; + let _e331 = s_20; + param_249 = _e331; + ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_249)); + let _e332 = s_20; + param_250 = _e332; + ReadIridescence_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_250)); + let _e334 = s_20.metallic; + metallic = clamp(_e334, 0f, 1f); + let _e337 = s_20.albedo; + let _e338 = metallic; + diffuseColor_1 = (_e337 * (1f - _e338)); + let _e341 = EonDiffuse_u0028_(); + if _e341 { + let _e342 = diffuseColor_1; + param_251 = _e342; + let _e344 = s_20.roughness; + param_252 = _e344; + let _e346 = s_20.n_dot_v; + param_253 = _e346; + let _e347 = EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_251), (¶m_252), (¶m_253)); + diffuseColor_1 = _e347; + } + let _e348 = diffuseColor_1; + let _e350 = s_20.ambient; + let _e353 = s_20.occlusion; + ambient = ((_e348 * _e350) * _e353); + let _e355 = SceneColourBound_u0028_(); + let _e356 = g_transmittance; + let _e359 = g_transmission; + let _e362 = ambient; + ambient = (_e362 * mix(vec3(1f, 1f, 1f), select(_e356, vec3(0f, 0f, 0f), vec3(_e355)), vec3(_e359))); + let _e366 = frag_info.frame_params[3u]; + levels_1 = _e366; coatAmbient = vec3(0f, 0f, 0f); - let _e356 = s_19.ambient; - sheenIncoming = _e356; - let _e357 = levels_1; - if (_e357 > 0f) { - let _e359 = g_f0_dielectric; - let _e361 = s_19.albedo; - let _e362 = metallic; - f0_4 = mix(_e359, _e361, vec3(_e362)); - let _e365 = g_f90_; - let _e366 = metallic; - f90_2 = mix(_e365, 1f, _e366); - let _e369 = s_19.n; - bent = _e369; - let _e370 = g_aniso; - if (_e370 > 0f) { - let _e372 = g_aniso_t; - let _e374 = s_19.v; - across = cross(_e372, _e374); - let _e376 = across; - let _e377 = g_aniso_t; - anisoN = cross(_e376, _e377); - let _e379 = g_aniso; - let _e381 = s_19.roughness; - bend = (1f - (_e379 * (1f - _e381))); - let _e385 = bend; - let _e386 = bend; - let _e388 = bend; - let _e390 = bend; - bend4_ = (((_e385 * _e386) * _e388) * _e390); - let _e392 = anisoN; - let _e394 = s_19.n; - let _e395 = bend4_; - bent = normalize(mix(_e392, _e394, vec3(_e395))); - } - let _e400 = s_19.v; - let _e402 = bent; - reflected = reflect(-(_e400), _e402); - let _e405 = s_19.n; - let _e406 = levels_1; - let _e407 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e405, _e406); - irradiance = _e407.xyz; - let _e409 = reflected; - let _e411 = s_19.roughness; - let _e412 = levels_1; - let _e414 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e409, (_e411 * _e412)); - prefiltered = _e414.xyz; - let _e417 = s_19.roughness; - param_247 = _e417; - let _e419 = s_19.n_dot_v; - param_248 = _e419; - let _e420 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_247), (¶m_248)); - ab_4 = _e420; - let _e421 = f0_4; - let _e422 = g_irid_fresnel; - let _e423 = g_iridescence; - let _e427 = ab_4[0u]; - let _e429 = f90_2; - let _e431 = ab_4[1u]; - single_1 = ((mix(_e421, _e422, vec3(_e423)) * _e427) + vec3((_e429 * _e431))); - let _e435 = prefiltered; - let _e436 = single_1; - specular_1 = (_e435 * _e436); - let _e438 = EnergyCompensation_u0028_(); - if _e438 { - let _e440 = ab_4[0u]; - let _e442 = ab_4[1u]; - missed = (1f - (_e440 + _e442)); - let _e445 = f0_4; - let _e446 = f0_4; - average_2 = (_e445 + ((vec3(1f, 1f, 1f) - _e446) / vec3(21f))); - let _e451 = single_1; - let _e452 = average_2; - let _e454 = missed; - let _e455 = average_2; - multiple = ((_e451 * _e452) / (vec3(1f, 1f, 1f) - (_e455 * _e454))); - let _e459 = multiple; - let _e460 = missed; - let _e462 = irradiance; - let _e464 = specular_1; - specular_1 = (_e464 + ((_e459 * _e460) * _e462)); - } - let _e466 = diffuseColor_1; - let _e467 = irradiance; - let _e469 = specular_1; - let _e473 = frag_info.material[2u]; - let _e476 = s_19.occlusion; - ambient = ((((_e466 * _e467) + _e469) * _e473) * _e476); - let _e478 = g_f0_dielectric; - let _e479 = g_irid_fresnel; - let _e480 = g_iridescence; - let _e484 = ab_4[0u]; - let _e486 = g_f90_; - let _e488 = ab_4[1u]; - reflects = ((mix(_e478, _e479, vec3(_e480)) * _e484) + vec3((_e486 * _e488))); - let _e492 = SceneColourBound_u0028_(); - if _e492 { - local_46 = vec3(0f, 0f, 0f); + let _e368 = s_20.ambient; + sheenIncoming = _e368; + let _e369 = levels_1; + if (_e369 > 0f) { + let _e371 = g_f0_dielectric; + let _e373 = s_20.albedo; + let _e374 = metallic; + f0_4 = mix(_e371, _e373, vec3(_e374)); + let _e377 = g_f90_; + let _e378 = metallic; + f90_2 = mix(_e377, 1f, _e378); + let _e381 = s_20.n; + bent = _e381; + let _e382 = g_aniso; + if (_e382 > 0f) { + let _e384 = g_aniso_t; + let _e386 = s_20.v; + across = cross(_e384, _e386); + let _e388 = across; + let _e389 = g_aniso_t; + anisoN = cross(_e388, _e389); + let _e391 = g_aniso; + let _e393 = s_20.roughness; + bend = (1f - (_e391 * (1f - _e393))); + let _e397 = bend; + let _e398 = bend; + let _e400 = bend; + let _e402 = bend; + bend4_ = (((_e397 * _e398) * _e400) * _e402); + let _e404 = anisoN; + let _e406 = s_20.n; + let _e407 = bend4_; + bent = normalize(mix(_e404, _e406, vec3(_e407))); + } + let _e412 = s_20.v; + let _e414 = bent; + reflected = reflect(-(_e412), _e414); + let _e417 = s_20.n; + let _e418 = levels_1; + let _e419 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e417, _e418); + irradiance = _e419.xyz; + let _e421 = reflected; + let _e423 = s_20.roughness; + let _e424 = levels_1; + let _e426 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e421, (_e423 * _e424)); + prefiltered = _e426.xyz; + let _e429 = s_20.roughness; + param_254 = _e429; + let _e431 = s_20.n_dot_v; + param_255 = _e431; + let _e432 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_254), (¶m_255)); + ab_4 = _e432; + let _e433 = f0_4; + let _e434 = g_irid_fresnel; + let _e435 = g_iridescence; + let _e439 = ab_4[0u]; + let _e441 = f90_2; + let _e443 = ab_4[1u]; + single_1 = ((mix(_e433, _e434, vec3(_e435)) * _e439) + vec3((_e441 * _e443))); + let _e447 = prefiltered; + let _e448 = single_1; + specular_1 = (_e447 * _e448); + let _e450 = EnergyCompensation_u0028_(); + if _e450 { + let _e452 = ab_4[0u]; + let _e454 = ab_4[1u]; + missed = (1f - (_e452 + _e454)); + let _e457 = f0_4; + let _e458 = f0_4; + average_2 = (_e457 + ((vec3(1f, 1f, 1f) - _e458) / vec3(21f))); + let _e463 = single_1; + let _e464 = average_2; + let _e466 = missed; + let _e467 = average_2; + multiple = ((_e463 * _e464) / (vec3(1f, 1f, 1f) - (_e467 * _e466))); + let _e471 = multiple; + let _e472 = missed; + let _e474 = irradiance; + let _e476 = specular_1; + specular_1 = (_e476 + ((_e471 * _e472) * _e474)); + } + let _e478 = diffuseColor_1; + let _e479 = irradiance; + let _e481 = specular_1; + let _e485 = frag_info.material[2u]; + let _e488 = s_20.occlusion; + ambient = ((((_e478 * _e479) + _e481) * _e485) * _e488); + let _e490 = g_f0_dielectric; + let _e491 = g_irid_fresnel; + let _e492 = g_iridescence; + let _e496 = ab_4[0u]; + let _e498 = g_f90_; + let _e500 = ab_4[1u]; + reflects = ((mix(_e490, _e491, vec3(_e492)) * _e496) + vec3((_e498 * _e500))); + let _e504 = SceneColourBound_u0028_(); + if _e504 { + local_48 = vec3(0f, 0f, 0f); } else { - let _e493 = s_19; - param_249 = _e493; - let _e494 = levels_1; - param_250 = _e494; - let _e495 = TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_f1_u003b((¶m_249), (¶m_250)); - let _e496 = g_transmittance; - let _e498 = reflects; - local_46 = ((_e495 * _e496) * (vec3(1f, 1f, 1f) - min(_e498, vec3(1f, 1f, 1f)))); - } - let _e502 = local_46; - through = _e502; - let _e503 = diffuseColor_1; - let _e504 = through; - let _e505 = irradiance; - let _e508 = g_transmission; - let _e512 = frag_info.material[2u]; - let _e515 = s_19.occlusion; - let _e517 = ambient; - ambient = (_e517 + ((((_e503 * (_e504 - _e505)) * _e508) * _e512) * _e515)); - let _e520 = s_19.v; - let _e522 = g_coat_n; - let _e524 = g_coat_roughness; - let _e525 = levels_1; - let _e527 = textureSampleLevel(environment_texture_tex, environment_texture_smp, reflect(-(_e520), _e522), (_e524 * _e525)); - coatPrefiltered = _e527.xyz; - let _e529 = g_coat_roughness; - param_251 = _e529; - let _e530 = g_coat_n_dot_v; - param_252 = _e530; - let _e531 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_251), (¶m_252)); - coatAb = _e531; - let _e532 = coatPrefiltered; - let _e534 = coatAb[0u]; - let _e537 = coatAb[1u]; - let _e540 = g_coat; - let _e544 = frag_info.material[2u]; - let _e547 = s_19.occlusion; - coatAmbient = ((((_e532 * ((0.04f * _e534) + _e537)) * _e540) * _e544) * _e547); - let _e550 = s_19.n; - let _e551 = g_sheen_roughness; - let _e552 = levels_1; - let _e554 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e550, (_e551 * _e552)); - let _e558 = frag_info.material[2u]; - sheenIncoming = (_e554.xyz * _e558); - } - let _e560 = SceneColourBound_u0028_(); - if _e560 { - let _e562 = s_19.roughness; - param_253 = _e562; - let _e564 = s_19.n_dot_v; - param_254 = _e564; - let _e565 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_253), (¶m_254)); - sceneAb = _e565; - let _e566 = g_f0_dielectric; - let _e567 = g_irid_fresnel; - let _e568 = g_iridescence; - let _e572 = sceneAb[0u]; - let _e574 = g_f90_; - let _e576 = sceneAb[1u]; - sceneReflects = ((mix(_e566, _e567, vec3(_e568)) * _e572) + vec3((_e574 * _e576))); - let _e580 = diffuseColor_1; - let _e581 = s_19; - param_255 = _e581; - let _e582 = SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_255)); - let _e584 = g_transmittance; - let _e586 = sceneReflects; - let _e590 = g_transmission; - let _e592 = ambient; - ambient = (_e592 + ((((_e580 * _e582) * _e584) * (vec3(1f, 1f, 1f) - min(_e586, vec3(1f, 1f, 1f)))) * _e590)); - } - let _e594 = diffuseColor_1; - let _e595 = SampleLightmap_u0028_(); - let _e598 = s_19.occlusion; - let _e600 = ambient; - ambient = (_e600 + ((_e594 * _e595) * _e598)); - let _e602 = g_sheen; - let _e603 = g_sheen_albedo; - let _e605 = sheenIncoming; - let _e608 = s_19.occlusion; - sheenAmbient = (((_e602 * _e603) * _e605) * _e608); - let _e610 = s_19; - param_256 = _e610; - let _e611 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_256)); - let _e613 = s_19.occlusion; - let _e615 = ambient; - let _e616 = g_sheen_scale; - let _e618 = sheenAmbient; - let _e621 = s_19.emissive; - let _e623 = g_coat_through; - let _e626 = coatAmbient; - param_257 = (((_e611 * _e613) + ((((_e615 * _e616) + _e618) + _e621) * _e623)) + _e626); - let _e629 = s_19.alpha; - param_258 = _e629; - let _e631 = s_19.roughness; - param_259 = _e631; - WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_257), (¶m_258), (¶m_259)); + let _e505 = s_20; + param_256 = _e505; + let _e506 = levels_1; + param_257 = _e506; + let _e507 = TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_f1_u003b((¶m_256), (¶m_257)); + let _e508 = g_transmittance; + let _e510 = reflects; + local_48 = ((_e507 * _e508) * (vec3(1f, 1f, 1f) - min(_e510, vec3(1f, 1f, 1f)))); + } + let _e514 = local_48; + through = _e514; + let _e515 = diffuseColor_1; + let _e516 = through; + let _e517 = irradiance; + let _e520 = g_transmission; + let _e524 = frag_info.material[2u]; + let _e527 = s_20.occlusion; + let _e529 = ambient; + ambient = (_e529 + ((((_e515 * (_e516 - _e517)) * _e520) * _e524) * _e527)); + let _e532 = s_20.v; + let _e534 = g_coat_n; + let _e536 = g_coat_roughness; + let _e537 = levels_1; + let _e539 = textureSampleLevel(environment_texture_tex, environment_texture_smp, reflect(-(_e532), _e534), (_e536 * _e537)); + coatPrefiltered = _e539.xyz; + let _e541 = g_coat_roughness; + param_258 = _e541; + let _e542 = g_coat_n_dot_v; + param_259 = _e542; + let _e543 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_258), (¶m_259)); + coatAb = _e543; + let _e544 = coatPrefiltered; + let _e546 = coatAb[0u]; + let _e549 = coatAb[1u]; + let _e552 = g_coat; + let _e556 = frag_info.material[2u]; + let _e559 = s_20.occlusion; + coatAmbient = ((((_e544 * ((0.04f * _e546) + _e549)) * _e552) * _e556) * _e559); + let _e562 = s_20.n; + let _e563 = g_sheen_roughness; + let _e564 = levels_1; + let _e566 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e562, (_e563 * _e564)); + let _e570 = frag_info.material[2u]; + sheenIncoming = (_e566.xyz * _e570); + } + let _e572 = SceneColourBound_u0028_(); + if _e572 { + let _e574 = s_20.roughness; + param_260 = _e574; + let _e576 = s_20.n_dot_v; + param_261 = _e576; + let _e577 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_260), (¶m_261)); + sceneAb = _e577; + let _e578 = g_f0_dielectric; + let _e579 = g_irid_fresnel; + let _e580 = g_iridescence; + let _e584 = sceneAb[0u]; + let _e586 = g_f90_; + let _e588 = sceneAb[1u]; + sceneReflects = ((mix(_e578, _e579, vec3(_e580)) * _e584) + vec3((_e586 * _e588))); + let _e592 = diffuseColor_1; + let _e593 = s_20; + param_262 = _e593; + let _e594 = SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_262)); + let _e596 = g_transmittance; + let _e598 = sceneReflects; + let _e602 = g_transmission; + let _e604 = ambient; + ambient = (_e604 + ((((_e592 * _e594) * _e596) * (vec3(1f, 1f, 1f) - min(_e598, vec3(1f, 1f, 1f)))) * _e602)); + } + let _e606 = diffuseColor_1; + let _e607 = SampleLightmap_u0028_(); + let _e610 = s_20.occlusion; + let _e612 = ambient; + ambient = (_e612 + ((_e606 * _e607) * _e610)); + let _e614 = g_sheen; + let _e615 = g_sheen_albedo; + let _e617 = sheenIncoming; + let _e620 = s_20.occlusion; + sheenAmbient = (((_e614 * _e615) * _e617) * _e620); + let _e622 = s_20; + param_263 = _e622; + let _e623 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_263)); + let _e625 = s_20.occlusion; + let _e627 = ambient; + let _e628 = g_sheen_scale; + let _e630 = sheenAmbient; + let _e633 = s_20.emissive; + let _e635 = g_coat_through; + let _e638 = coatAmbient; + lit_3 = (((_e623 * _e625) + ((((_e627 * _e628) + _e630) + _e633) * _e635)) + _e638); + let _e640 = s_20; + param_264 = _e640; + let _e641 = lit_3; + param_265 = _e641; + let _e642 = WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b((¶m_264), (¶m_265)); + if _e642 { + return; + } + let _e643 = lit_3; + param_266 = _e643; + let _e645 = s_20.alpha; + param_267 = _e645; + let _e647 = s_20.roughness; + param_268 = _e647; + WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_266), (¶m_267), (¶m_268)); return; } @@ -21115,7 +21877,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember( name: 'light_position', @@ -21212,6 +21974,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, shadow_bias: vec4, target_origin: vec4, + debug_view: vec4, } struct PointShadow { @@ -21665,10 +22433,10 @@ var shadow_texture_tex: texture_2d; var shadow_texture_smp: sampler; fn ViewDepth_u0028_() -> f32 { - let _e177 = v_world_position_1; - let _e179 = fog_info.eye; - let _e183 = fog_info.forward; - return dot((_e177 - _e179.xyz), _e183.xyz); + let _e186 = v_world_position_1; + let _e188 = fog_info.eye; + let _e192 = fog_info.forward; + return dot((_e186 - _e188.xyz), _e192.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -21677,22 +22445,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e182 = ViewDepth_u0028_(); - z = abs(_e182); - let _e184 = z; - near = (_e184 / 5f); - let _e186 = z; - far = (_e186 / 200f); - let _e188 = far; - let _e189 = far; - let _e191 = far; - far3_ = ((_e188 * _e189) * _e191); - let _e193 = (*alpha); - let _e194 = near; - let _e195 = near; - let _e198 = far3_; - let _e199 = far3_; - return (_e193 * clamp((10f / ((0.00001f + (_e194 * _e195)) + (_e198 * _e199))), 0.01f, 3000f)); + let _e191 = ViewDepth_u0028_(); + z = abs(_e191); + let _e193 = z; + near = (_e193 / 5f); + let _e195 = z; + far = (_e195 / 200f); + let _e197 = far; + let _e198 = far; + let _e200 = far; + far3_ = ((_e197 * _e198) * _e200); + let _e202 = (*alpha); + let _e203 = near; + let _e204 = near; + let _e207 = far3_; + let _e208 = far3_; + return (_e202 * clamp((10f / ((0.00001f + (_e203 * _e204)) + (_e207 * _e208))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -21704,39 +22472,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e186 = fog_info.forward[3u]; - mode = _e186; - let _e187 = mode; - if (_e187 > 0.5f) { - let _e190 = frag_color[3u]; - alpha_1 = _e190; - let _e191 = alpha_1; - param = _e191; - let _e192 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e192; - let _e193 = frag_color; - let _e195 = weight; - let _e196 = (_e193.xyz * _e195); - let _e197 = alpha_1; - let _e198 = weight; - accumulate = vec4(_e196.x, _e196.y, _e196.z, (_e197 * _e198)); - let _e204 = mode; - let _e206 = mode; - revealage = ((_e204 > 1.5f) && (_e206 < 2.5f)); - let _e209 = revealage; - if _e209 { - let _e210 = alpha_1; - local = vec4(_e210); + let _e195 = fog_info.forward[3u]; + mode = _e195; + let _e196 = mode; + if (_e196 > 0.5f) { + let _e199 = frag_color[3u]; + alpha_1 = _e199; + let _e200 = alpha_1; + param = _e200; + let _e201 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e201; + let _e202 = frag_color; + let _e204 = weight; + let _e205 = (_e202.xyz * _e204); + let _e206 = alpha_1; + let _e207 = weight; + accumulate = vec4(_e205.x, _e205.y, _e205.z, (_e206 * _e207)); + let _e213 = mode; + let _e215 = mode; + revealage = ((_e213 > 1.5f) && (_e215 < 2.5f)); + let _e218 = revealage; + if _e218 { + let _e219 = alpha_1; + local = vec4(_e219); } else { - let _e212 = accumulate; - local = _e212; + let _e221 = accumulate; + local = _e221; } - let _e213 = local; - frag_color = _e213; - let _e214 = mode; - if (_e214 > 2.5f) { - let _e216 = alpha_1; - frag_surface = vec4(_e216); + let _e222 = local; + frag_color = _e222; + let _e223 = mode; + if (_e223 > 2.5f) { + let _e225 = alpha_1; + frag_surface = vec4(_e225); } } return; @@ -21745,29 +22513,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e180 = (*n)[0u]; - let _e183 = (*n)[1u]; - let _e187 = (*n)[2u]; - let _e190 = (*n); - (*n) = (_e190 / vec3(((abs(_e180) + abs(_e183)) + abs(_e187)))); - let _e193 = (*n); - e = _e193.xy; + let _e189 = (*n)[0u]; + let _e192 = (*n)[1u]; let _e196 = (*n)[2u]; - if (_e196 < 0f) { - let _e198 = (*n); - let _e204 = (*n)[0u]; - let _e208 = (*n)[1u]; - e = ((vec2(1f) - abs(_e198.yx)) * vec2(select(-1f, 1f, (_e204 >= 0f)), select(-1f, 1f, (_e208 >= 0f)))); + let _e199 = (*n); + (*n) = (_e199 / vec3(((abs(_e189) + abs(_e192)) + abs(_e196)))); + let _e202 = (*n); + e = _e202.xy; + let _e205 = (*n)[2u]; + if (_e205 < 0f) { + let _e207 = (*n); + let _e213 = (*n)[0u]; + let _e217 = (*n)[1u]; + e = ((vec2(1f) - abs(_e207.yx)) * vec2(select(-1f, 1f, (_e213 >= 0f)), select(-1f, 1f, (_e217 >= 0f)))); } - let _e213 = e; - return ((_e213 * 0.5f) + vec2(0.5f)); + let _e222 = e; + return ((_e222 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e178 = (*linear); - let _e180 = (*linear); - let _e184 = (*linear); - return mix((_e178 * 12.92f), ((pow(_e180, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e184)); + let _e187 = (*linear); + let _e189 = (*linear); + let _e193 = (*linear); + return mix((_e187 * 12.92f), ((pow(_e189, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e193)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -21775,37 +22543,37 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e181 = g_albedo; - param_1 = clamp(_e181, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e183 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e183.x, _e183.y, _e183.z, 1f); - let _e188 = g_debug_surface_on; - if _e188 { - let _e189 = g_debug_surface; - let _e190 = ViewDepth_u0028_(); - frag_surface = vec4(_e189.x, _e189.y, _e189.z, _e190); + let _e190 = g_albedo; + param_1 = clamp(_e190, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e192 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e192.x, _e192.y, _e192.z, 1f); + let _e197 = g_debug_surface_on; + if _e197 { + let _e198 = g_debug_surface; + let _e199 = ViewDepth_u0028_(); + frag_surface = vec4(_e198.x, _e198.y, _e198.z, _e199); return; } - let _e195 = v_normal_1; - geometric = normalize(_e195); - let _e197 = gl_FrontFacing_1; - if !(_e197) { - let _e199 = geometric; - geometric = -(_e199); - } - let _e201 = geometric; - param_2 = _e201; - let _e202 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e203 = (*roughness); - let _e205 = ViewDepth_u0028_(); - frag_surface = vec4(_e202.x, _e202.y, clamp(_e203, 0f, 1f), _e205); + let _e204 = v_normal_1; + geometric = normalize(_e204); + let _e206 = gl_FrontFacing_1; + if !(_e206) { + let _e208 = geometric; + geometric = -(_e208); + } + let _e210 = geometric; + param_2 = _e210; + let _e211 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e212 = (*roughness); + let _e214 = ViewDepth_u0028_(); + frag_surface = vec4(_e211.x, _e211.y, clamp(_e212, 0f, 1f), _e214); return; } fn EyeDistance_u0028_() -> f32 { - let _e177 = v_world_position_1; - let _e179 = fog_info.eye; - return distance(_e177, _e179.xyz); + let _e186 = v_world_position_1; + let _e188 = fog_info.eye; + return distance(_e186, _e188.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -21815,41 +22583,41 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var k: f32; var local_1: f32; - let _e185 = fog_info.fog[3u]; - density = _e185; - let _e186 = density; - if (_e186 <= 0f) { - let _e188 = (*color); - return _e188; - } - let _e189 = EyeDistance_u0028_(); - d = _e189; - let _e192 = fog_info.eye[3u]; - falloff = _e192; - let _e193 = falloff; - if (_e193 > 0f) { - let _e195 = falloff; - let _e197 = v_world_position_1[1u]; - let _e200 = fog_info.eye[1u]; - k = (_e195 * (_e197 - _e200)); - let _e203 = k; - if (abs(_e203) > 0.001f) { - let _e206 = k; - let _e210 = k; - local_1 = ((1f - exp(-(_e206))) / _e210); + let _e194 = fog_info.fog[3u]; + density = _e194; + let _e195 = density; + if (_e195 <= 0f) { + let _e197 = (*color); + return _e197; + } + let _e198 = EyeDistance_u0028_(); + d = _e198; + let _e201 = fog_info.eye[3u]; + falloff = _e201; + let _e202 = falloff; + if (_e202 > 0f) { + let _e204 = falloff; + let _e206 = v_world_position_1[1u]; + let _e209 = fog_info.eye[1u]; + k = (_e204 * (_e206 - _e209)); + let _e212 = k; + if (abs(_e212) > 0.001f) { + let _e215 = k; + let _e219 = k; + local_1 = ((1f - exp(-(_e215))) / _e219); } else { - let _e212 = k; - local_1 = (1f - (0.5f * _e212)); + let _e221 = k; + local_1 = (1f - (0.5f * _e221)); } - let _e215 = local_1; - let _e216 = density; - density = (_e216 * _e215); + let _e224 = local_1; + let _e225 = density; + density = (_e225 * _e224); } - let _e219 = fog_info.fog; - let _e221 = (*color); - let _e222 = density; - let _e224 = d; - return mix(_e219.xyz, _e221, vec3(clamp(exp((-(_e222) * _e224)), 0f, 1f))); + let _e228 = fog_info.fog; + let _e230 = (*color); + let _e231 = density; + let _e233 = d; + return mix(_e228.xyz, _e230, vec3(clamp(exp((-(_e231) * _e233)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -21857,77 +22625,217 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e183 = g_premultiply; - let _e184 = (*alpha_2); - weight_1 = select(1f, _e184, _e183); - let _e186 = (*linearColor); - param_3 = _e186; - let _e187 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e188 = weight_1; - let _e189 = (_e187 * _e188); - let _e190 = (*alpha_2); - frag_color = vec4(_e189.x, _e189.y, _e189.z, _e190); - let _e195 = (*roughness_1); - param_4 = _e195; + let _e192 = g_premultiply; + let _e193 = (*alpha_2); + weight_1 = select(1f, _e193, _e192); + let _e195 = (*linearColor); + param_3 = _e195; + let _e196 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e197 = weight_1; + let _e198 = (_e196 * _e197); + let _e199 = (*alpha_2); + frag_color = vec4(_e198.x, _e198.y, _e198.z, _e199); + let _e204 = (*roughness_1); + param_4 = _e204; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; } -fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s: ptr, light: ptr) -> vec3 { - var bands: f32; - var quantized: f32; - var fraction: f32; - var ramp: f32; - - let _e184 = (*s).roughness; - bands = mix(5f, 2f, _e184); - let _e187 = (*light).n_dot_l; - let _e188 = bands; - let _e191 = bands; - quantized = (floor((_e187 * _e188)) / _e191); - let _e194 = (*light).n_dot_l; - let _e195 = bands; - fraction = fract((_e194 * _e195)); - let _e198 = fraction; - let _e200 = bands; - let _e202 = quantized; - quantized = (_e202 + (smoothstep(0.85f, 1f, _e198) / _e200)); - let _e204 = quantized; - let _e206 = (*light).n_dot_l; - ramp = (_e204 / max(_e206, 0.001f)); - let _e210 = (*s).albedo; - let _e211 = ramp; - return (_e210 * _e211); +fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { + let _e187 = (*srgb); + let _e190 = (*srgb); + let _e195 = (*srgb); + return mix((_e187 / vec3(12.92f)), pow(((_e190 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e195)); } -fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e178 = (*i); - if (_e178 == 0i) { - return vec2(-0.94201624f, -0.39906216f); +fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { + var phi_2504_: bool; + + let _e187 = (*c); + let _e188 = (*c); + let _e190 = any((_e187 != _e188)); + phi_2504_ = _e190; + if !(_e190) { + let _e192 = (*c); + phi_2504_ = any((abs(_e192) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); } - let _e180 = (*i); - if (_e180 == 1i) { - return vec2(0.9455861f, -0.76890725f); + let _e197 = phi_2504_; + return _e197; +} + +fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b(s: ptr, lit: ptr>) -> bool { + var view: f32; + var code: i32; + var shown: vec3; + var param_5: vec3; + var param_6: vec3; + var grey: f32; + var param_7: vec3; + var param_8: vec3; + var local_2: vec3; + var weight_2: f32; + var local_3: f32; + var param_9: vec3; + var param_10: f32; + + let _e203 = frag_info.debug_view[0u]; + view = _e203; + let _e204 = view; + if (_e204 < 0.5f) { + return false; + } + let _e207 = gl_FragCoord_1[0u]; + let _e210 = frag_info.debug_view[1u]; + let _e213 = frag_info.debug_view[2u]; + if (_e207 < (_e210 * _e213)) { + return false; + } + let _e216 = view; + code = i32((_e216 + 0.5f)); + shown = vec3(0f, 0f, 0f); + let _e219 = code; + if (_e219 == 1i) { + let _e222 = (*s).albedo; + param_5 = clamp(_e222, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e224 = LinearToSrgb_u0028_vf3_u003b((¶m_5)); + shown = _e224; + } else { + let _e225 = code; + if (_e225 == 2i) { + let _e228 = (*s).n; + shown = ((_e228 * 0.5f) + vec3(0.5f, 0.5f, 0.5f)); + } else { + let _e231 = code; + if (_e231 == 3i) { + let _e234 = (*s).roughness; + shown = vec3(clamp(_e234, 0f, 1f)); + } else { + let _e237 = code; + if (_e237 == 4i) { + let _e240 = (*s).metallic; + shown = vec3(clamp(_e240, 0f, 1f)); + } else { + let _e243 = code; + if (_e243 == 5i) { + let _e246 = (*s).occlusion; + shown = vec3(clamp(_e246, 0f, 1f)); + } else { + let _e249 = code; + if (_e249 == 6i) { + let _e252 = (*s).emissive; + param_6 = clamp(_e252, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e254 = LinearToSrgb_u0028_vf3_u003b((¶m_6)); + shown = _e254; + } else { + let _e255 = code; + if (_e255 == 7i) { + let _e257 = v_texcoord_1; + let _e258 = fract(_e257); + shown = vec3(_e258.x, _e258.y, 0f); + } else { + let _e262 = code; + if (_e262 == 8i) { + let _e264 = (*lit); + param_7 = clamp(_e264, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e266 = LinearToSrgb_u0028_vf3_u003b((¶m_7)); + grey = dot(_e266, vec3(0.2126f, 0.7152f, 0.0722f)); + let _e268 = (*lit); + param_8 = _e268; + let _e269 = NonFinite_u0028_vf3_u003b((¶m_8)); + if _e269 { + local_2 = vec3(1f, 0f, 1f); + } else { + let _e270 = grey; + local_2 = vec3((_e270 * 0.5f)); + } + let _e273 = local_2; + shown = _e273; + } + } + } + } + } + } + } } - let _e182 = (*i); - if (_e182 == 2i) { + let _e274 = g_premultiply; + if _e274 { + let _e276 = (*s).alpha; + local_3 = _e276; + } else { + local_3 = 1f; + } + let _e277 = local_3; + weight_2 = _e277; + let _e278 = shown; + param_9 = _e278; + let _e279 = SrgbToLinear_u0028_vf3_u003b((¶m_9)); + let _e280 = weight_2; + let _e281 = (_e279 * _e280); + let _e283 = (*s).alpha; + frag_color = vec4(_e281.x, _e281.y, _e281.z, _e283); + let _e289 = (*s).roughness; + param_10 = _e289; + WriteSurfaceGeometry_u0028_f1_u003b((¶m_10)); + WriteWeightedBlended_u0028_(); + return true; +} + +fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_1: ptr, light: ptr) -> vec3 { + var bands: f32; + var quantized: f32; + var fraction: f32; + var ramp: f32; + + let _e193 = (*s_1).roughness; + bands = mix(5f, 2f, _e193); + let _e196 = (*light).n_dot_l; + let _e197 = bands; + let _e200 = bands; + quantized = (floor((_e196 * _e197)) / _e200); + let _e203 = (*light).n_dot_l; + let _e204 = bands; + fraction = fract((_e203 * _e204)); + let _e207 = fraction; + let _e209 = bands; + let _e211 = quantized; + quantized = (_e211 + (smoothstep(0.85f, 1f, _e207) / _e209)); + let _e213 = quantized; + let _e215 = (*light).n_dot_l; + ramp = (_e213 / max(_e215, 0.001f)); + let _e219 = (*s_1).albedo; + let _e220 = ramp; + return (_e219 * _e220); +} + +fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { + let _e187 = (*i); + if (_e187 == 0i) { + return vec2(-0.94201624f, -0.39906216f); + } + let _e189 = (*i); + if (_e189 == 1i) { + return vec2(0.9455861f, -0.76890725f); + } + let _e191 = (*i); + if (_e191 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e184 = (*i); - if (_e184 == 3i) { + let _e193 = (*i); + if (_e193 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e186 = (*i); - if (_e186 == 4i) { + let _e195 = (*i); + if (_e195 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e188 = (*i); - if (_e188 == 5i) { + let _e197 = (*i); + if (_e197 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e190 = (*i); - if (_e190 == 6i) { + let _e199 = (*i); + if (_e199 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -21935,208 +22843,208 @@ fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr, ca: ptr, sa: ptr, radius: ptr) -> vec2 { var p: vec2; - var param_5: i32; - - let _e183 = (*i_1); - param_5 = _e183; - let _e184 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); - p = _e184; - let _e186 = p[0u]; - let _e187 = (*ca); - let _e190 = p[1u]; - let _e191 = (*sa); + var param_11: i32; + + let _e192 = (*i_1); + param_11 = _e192; + let _e193 = PointShadowDiskTap_u0028_i1_u003b((¶m_11)); + p = _e193; let _e195 = p[0u]; - let _e196 = (*sa); + let _e196 = (*ca); let _e199 = p[1u]; - let _e200 = (*ca); - let _e204 = (*radius); - return (vec2(((_e186 * _e187) - (_e190 * _e191)), ((_e195 * _e196) + (_e199 * _e200))) * _e204); + let _e200 = (*sa); + let _e204 = p[0u]; + let _e205 = (*sa); + let _e208 = p[1u]; + let _e209 = (*ca); + let _e213 = (*radius); + return (vec2(((_e195 * _e196) - (_e199 * _e200)), ((_e204 * _e205) + (_e208 * _e209))) * _e213); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_2: vec2; + var local_4: vec2; var atlas: vec2; - let _e186 = point_shadow.params[0u]; - inset = _e186; - let _e187 = (*uv); - let _e188 = (*offset); - let _e190 = inset; - let _e191 = inset; - local_2 = clamp((_e187 + _e188), vec2(_e190), vec2((1f - _e191))); - let _e196 = local_2; - let _e197 = (*tile); - atlas = ((_e196 + _e197) * vec2(0.16666667f, 0.16666667f)); - let _e202 = point_shadow.params3_[0u]; - if (_e202 > 0.5f) { - let _e205 = atlas[1u]; - atlas[1u] = (1f - _e205); - } - let _e208 = atlas; - let _e209 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e208, 0f); - let _e211 = atlas; - let _e212 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e211, 0f); - let _e215 = (*range); - return (min(_e209.x, _e212.x) * _e215); + let _e195 = point_shadow.params[0u]; + inset = _e195; + let _e196 = (*uv); + let _e197 = (*offset); + let _e199 = inset; + let _e200 = inset; + local_4 = clamp((_e196 + _e197), vec2(_e199), vec2((1f - _e200))); + let _e205 = local_4; + let _e206 = (*tile); + atlas = ((_e205 + _e206) * vec2(0.16666667f, 0.16666667f)); + let _e211 = point_shadow.params3_[0u]; + if (_e211 > 0.5f) { + let _e214 = atlas[1u]; + atlas[1u] = (1f - _e214); + } + let _e217 = atlas; + let _e218 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e217, 0f); + let _e220 = atlas; + let _e221 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e220, 0f); + let _e224 = (*range); + return (min(_e218.x, _e221.x) * _e224); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { var stored: f32; - var param_6: vec2; - var param_7: vec2; - var param_8: vec2; - var param_9: f32; + var param_12: vec2; + var param_13: vec2; + var param_14: vec2; + var param_15: f32; - let _e187 = (*uv_1); - param_6 = _e187; - let _e188 = (*offset_1); - param_7 = _e188; - let _e189 = (*tile_1); - param_8 = _e189; - let _e190 = (*range_1); - param_9 = _e190; - let _e191 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); - stored = _e191; - let _e192 = stored; - let _e193 = (*range_1); - if (_e192 >= (_e193 * 0.999f)) { + let _e196 = (*uv_1); + param_12 = _e196; + let _e197 = (*offset_1); + param_13 = _e197; + let _e198 = (*tile_1); + param_14 = _e198; + let _e199 = (*range_1); + param_15 = _e199; + let _e200 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_12), (¶m_13), (¶m_14), (¶m_15)); + stored = _e200; + let _e201 = stored; + let _e202 = (*range_1); + if (_e201 >= (_e202 * 0.999f)) { return 1f; } - let _e196 = (*receiver); - let _e197 = stored; - return select(1f, 0f, (_e196 > _e197)); + let _e205 = (*receiver); + let _e206 = stored; + return select(1f, 0f, (_e205 > _e206)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { var lightRadius: f32; var minRadius: f32; var maxRadius: f32; - var param_10: vec2; - var param_11: vec2; - var param_12: vec2; - var param_13: f32; + var param_16: vec2; + var param_17: vec2; + var param_18: vec2; + var param_19: f32; var sum: f32; var count: f32; var i_2: i32; var stored_1: f32; - var param_14: i32; - var param_15: f32; - var param_16: f32; - var param_17: f32; - var param_18: vec2; - var param_19: vec2; - var param_20: vec2; + var param_20: i32; var param_21: f32; + var param_22: f32; + var param_23: f32; + var param_24: vec2; + var param_25: vec2; + var param_26: vec2; + var param_27: f32; var blocker: f32; var world: f32; (*blockerOut) = -1f; - let _e208 = point_shadow.params2_[1u]; - lightRadius = _e208; - let _e211 = point_shadow.params2_[0u]; - minRadius = _e211; - let _e214 = point_shadow.params2_[2u]; - maxRadius = _e214; - let _e215 = lightRadius; - if (_e215 <= 0f) { - let _e217 = (*uv_2); - param_10 = _e217; - param_11 = vec2(0f, 0f); - let _e218 = (*tile_2); - param_12 = _e218; - let _e219 = (*range_2); - param_13 = _e219; - let _e220 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); - (*blockerOut) = _e220; - let _e221 = minRadius; - return _e221; + let _e217 = point_shadow.params2_[1u]; + lightRadius = _e217; + let _e220 = point_shadow.params2_[0u]; + minRadius = _e220; + let _e223 = point_shadow.params2_[2u]; + maxRadius = _e223; + let _e224 = lightRadius; + if (_e224 <= 0f) { + let _e226 = (*uv_2); + param_16 = _e226; + param_17 = vec2(0f, 0f); + let _e227 = (*tile_2); + param_18 = _e227; + let _e228 = (*range_2); + param_19 = _e228; + let _e229 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_16), (¶m_17), (¶m_18), (¶m_19)); + (*blockerOut) = _e229; + let _e230 = minRadius; + return _e230; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e222 = i_2; - if (_e222 < 8i) { - let _e224 = i_2; - param_14 = _e224; - let _e225 = (*ca_1); - param_15 = _e225; - let _e226 = (*sa_1); - param_16 = _e226; - let _e227 = maxRadius; - param_17 = _e227; - let _e228 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); - let _e229 = (*uv_2); - param_18 = _e229; - param_19 = _e228; - let _e230 = (*tile_2); - param_20 = _e230; - let _e231 = (*range_2); - param_21 = _e231; - let _e232 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); - stored_1 = _e232; - let _e233 = stored_1; - let _e234 = (*range_2); - if (_e233 >= (_e234 * 0.999f)) { + let _e231 = i_2; + if (_e231 < 8i) { + let _e233 = i_2; + param_20 = _e233; + let _e234 = (*ca_1); + param_21 = _e234; + let _e235 = (*sa_1); + param_22 = _e235; + let _e236 = maxRadius; + param_23 = _e236; + let _e237 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_20), (¶m_21), (¶m_22), (¶m_23)); + let _e238 = (*uv_2); + param_24 = _e238; + param_25 = _e237; + let _e239 = (*tile_2); + param_26 = _e239; + let _e240 = (*range_2); + param_27 = _e240; + let _e241 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_24), (¶m_25), (¶m_26), (¶m_27)); + stored_1 = _e241; + let _e242 = stored_1; + let _e243 = (*range_2); + if (_e242 >= (_e243 * 0.999f)) { continue; } - let _e237 = stored_1; - let _e238 = (*receiver_1); - if (_e237 >= _e238) { + let _e246 = stored_1; + let _e247 = (*receiver_1); + if (_e246 >= _e247) { continue; } - let _e240 = stored_1; - let _e241 = sum; - sum = (_e241 + _e240); - let _e243 = count; - count = (_e243 + 1f); + let _e249 = stored_1; + let _e250 = sum; + sum = (_e250 + _e249); + let _e252 = count; + count = (_e252 + 1f); continue; } else { break; } continuing { - let _e245 = i_2; - i_2 = (_e245 + 1i); + let _e254 = i_2; + i_2 = (_e254 + 1i); } } - let _e247 = count; - if (_e247 < 0.5f) { + let _e256 = count; + if (_e256 < 0.5f) { return -1f; } - let _e249 = sum; - let _e250 = count; - blocker = max((_e249 / _e250), 0.0001f); - let _e253 = blocker; - (*blockerOut) = _e253; - let _e254 = lightRadius; - let _e255 = (*receiver_1); - let _e256 = blocker; - let _e260 = blocker; - world = ((_e254 * max((_e255 - _e256), 0f)) / _e260); - let _e262 = world; - let _e263 = (*receiver_1); - let _e265 = (*tanHalf); - let _e268 = minRadius; - let _e269 = maxRadius; - return clamp((_e262 / ((2f * _e263) * _e265)), _e268, _e269); + let _e258 = sum; + let _e259 = count; + blocker = max((_e258 / _e259), 0.0001f); + let _e262 = blocker; + (*blockerOut) = _e262; + let _e263 = lightRadius; + let _e264 = (*receiver_1); + let _e265 = blocker; + let _e269 = blocker; + world = ((_e263 * max((_e264 - _e265), 0f)) / _e269); + let _e271 = world; + let _e272 = (*receiver_1); + let _e274 = (*tanHalf); + let _e277 = minRadius; + let _e278 = maxRadius; + return clamp((_e271 / ((2f * _e272) * _e274)), _e277, _e278); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_3: vec2; + var local_5: vec2; - let _e179 = (*rows); - if (_e179 > 0f) { - let _e182 = gl_FragCoord_1[0u]; - let _e183 = (*rows); - let _e185 = gl_FragCoord_1[1u]; - local_3 = vec2(_e182, (_e183 - _e185)); + let _e188 = (*rows); + if (_e188 > 0f) { + let _e191 = gl_FragCoord_1[0u]; + let _e192 = (*rows); + let _e194 = gl_FragCoord_1[1u]; + local_5 = vec2(_e191, (_e192 - _e194)); } else { - let _e188 = gl_FragCoord_1; - local_3 = _e188.xy; + let _e197 = gl_FragCoord_1; + local_5 = _e197.xy; } - let _e190 = local_3; - return _e190; + let _e199 = local_5; + return _e199; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -22159,292 +23067,292 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var receiver_2: f32; var noise: f32; - var param_22: f32; + var param_28: f32; var angle: f32; var ca_2: f32; var sa_2: f32; var tanHalf_1: f32; var blocker_1: f32; var radius_1: f32; - var param_23: vec2; - var param_24: vec2; - var param_25: f32; - var param_26: f32; - var param_27: f32; - var param_28: f32; - var param_29: f32; - var param_30: f32; - var local_4: vec3; - var lit: f32; - var param_31: vec2; - var param_32: vec2; - var param_33: vec2; + var param_29: vec2; + var param_30: vec2; + var param_31: f32; + var param_32: f32; + var param_33: f32; var param_34: f32; var param_35: f32; + var param_36: f32; + var local_6: vec3; + var lit_1: f32; + var param_37: vec2; + var param_38: vec2; + var param_39: vec2; + var param_40: f32; + var param_41: f32; var i_3: i32; - var param_36: i32; - var param_37: f32; - var param_38: f32; - var param_39: f32; - var param_40: vec2; - var param_41: vec2; - var param_42: vec2; + var param_42: i32; var param_43: f32; var param_44: f32; - var phi_2184_: bool; - var phi_2245_: bool; - - let _e231 = (*lightIndex); - let _e235 = point_shadow.slots[_e231][0u]; - slot = i32((_e235 + 0.5f)); - let _e238 = (*lightIndex); - let _e242 = point_shadow.slots[_e238][0u]; - if (_e242 < 0f) { + var param_45: f32; + var param_46: vec2; + var param_47: vec2; + var param_48: vec2; + var param_49: f32; + var param_50: f32; + var phi_2193_: bool; + var phi_2254_: bool; + + let _e240 = (*lightIndex); + let _e244 = point_shadow.slots[_e240][0u]; + slot = i32((_e244 + 0.5f)); + let _e247 = (*lightIndex); + let _e251 = point_shadow.slots[_e247][0u]; + if (_e251 < 0f) { return 1f; } - let _e246 = point_shadow.params[2u]; - strength = _e246; - let _e247 = strength; - if (_e247 <= 0f) { + let _e255 = point_shadow.params[2u]; + strength = _e255; + let _e256 = strength; + if (_e256 <= 0f) { return 1f; } - let _e249 = slot; - let _e252 = point_shadow.lights[_e249]; - let _e254 = (*world_1); - toLight = (_e252.xyz - _e254); - let _e256 = toLight; - toLightLength = max(length(_e256), 0.000001f); - let _e259 = (*normal); - let _e260 = toLight; - let _e261 = toLightLength; - nDotL = max(dot(_e259, (_e260 / vec3(_e261))), 0.15f); - let _e266 = nDotL; - let _e267 = nDotL; - let _e272 = nDotL; - let _e273 = nDotL; - slope = min((sqrt(max((1f - (_e266 * _e267)), 0f)) / (_e272 * _e273)), 8f); - let _e277 = toLightLength; - let _e279 = (*lightIndex); - let _e283 = point_shadow.slots[_e279][2u]; - let _e288 = point_shadow.params3_[1u]; - texel = (((2f * _e277) * max(_e283, 0.0001f)) * _e288); - let _e290 = (*world_1); - let _e291 = (*normal); - let _e292 = texel; - let _e296 = point_shadow.params[3u]; - let _e298 = slope; - origin = (_e290 + (((_e291 * _e292) * _e296) * (1f + _e298))); - let _e302 = origin; - let _e303 = slot; - let _e306 = point_shadow.lights[_e303]; - toFragment = (_e302 - _e306.xyz); - let _e309 = toFragment; - distance_ = length(_e309); - let _e311 = slot; - let _e315 = point_shadow.lights[_e311][3u]; - range_3 = max(_e315, 0.0001f); - let _e317 = distance_; - let _e318 = range_3; - if (_e317 >= _e318) { + let _e258 = slot; + let _e261 = point_shadow.lights[_e258]; + let _e263 = (*world_1); + toLight = (_e261.xyz - _e263); + let _e265 = toLight; + toLightLength = max(length(_e265), 0.000001f); + let _e268 = (*normal); + let _e269 = toLight; + let _e270 = toLightLength; + nDotL = max(dot(_e268, (_e269 / vec3(_e270))), 0.15f); + let _e275 = nDotL; + let _e276 = nDotL; + let _e281 = nDotL; + let _e282 = nDotL; + slope = min((sqrt(max((1f - (_e275 * _e276)), 0f)) / (_e281 * _e282)), 8f); + let _e286 = toLightLength; + let _e288 = (*lightIndex); + let _e292 = point_shadow.slots[_e288][2u]; + let _e297 = point_shadow.params3_[1u]; + texel = (((2f * _e286) * max(_e292, 0.0001f)) * _e297); + let _e299 = (*world_1); + let _e300 = (*normal); + let _e301 = texel; + let _e305 = point_shadow.params[3u]; + let _e307 = slope; + origin = (_e299 + (((_e300 * _e301) * _e305) * (1f + _e307))); + let _e311 = origin; + let _e312 = slot; + let _e315 = point_shadow.lights[_e312]; + toFragment = (_e311 - _e315.xyz); + let _e318 = toFragment; + distance_ = length(_e318); + let _e320 = slot; + let _e324 = point_shadow.lights[_e320][3u]; + range_3 = max(_e324, 0.0001f); + let _e326 = distance_; + let _e327 = range_3; + if (_e326 >= _e327) { return 1f; } face = 0i; - let _e320 = (*lightIndex); - let _e324 = point_shadow.slots[_e320][1u]; - if (_e324 < 0.5f) { - let _e326 = toFragment; - a = abs(_e326); - let _e329 = a[0u]; - let _e331 = a[1u]; - let _e332 = (_e329 >= _e331); - phi_2184_ = _e332; - if _e332 { - let _e334 = a[0u]; - let _e336 = a[2u]; - phi_2184_ = (_e334 >= _e336); - } - let _e339 = phi_2184_; - if _e339 { - let _e341 = toFragment[0u]; - face = select(1i, 0i, (_e341 > 0f)); + let _e329 = (*lightIndex); + let _e333 = point_shadow.slots[_e329][1u]; + if (_e333 < 0.5f) { + let _e335 = toFragment; + a = abs(_e335); + let _e338 = a[0u]; + let _e340 = a[1u]; + let _e341 = (_e338 >= _e340); + phi_2193_ = _e341; + if _e341 { + let _e343 = a[0u]; + let _e345 = a[2u]; + phi_2193_ = (_e343 >= _e345); + } + let _e348 = phi_2193_; + if _e348 { + let _e350 = toFragment[0u]; + face = select(1i, 0i, (_e350 > 0f)); } else { - let _e345 = a[1u]; - let _e347 = a[2u]; - if (_e345 >= _e347) { - let _e350 = toFragment[1u]; - face = select(3i, 2i, (_e350 > 0f)); + let _e354 = a[1u]; + let _e356 = a[2u]; + if (_e354 >= _e356) { + let _e359 = toFragment[1u]; + face = select(3i, 2i, (_e359 > 0f)); } else { - let _e354 = toFragment[2u]; - face = select(5i, 4i, (_e354 > 0f)); + let _e363 = toFragment[2u]; + face = select(5i, 4i, (_e363 > 0f)); } } } - let _e357 = slot; - let _e359 = face; - let _e363 = point_shadow.faces[((_e357 * 6i) + _e359)]; - let _e364 = origin; - clip = (_e363 * vec4(_e364.x, _e364.y, _e364.z, 1f)); - let _e371 = clip[3u]; - if (_e371 <= 0f) { + let _e366 = slot; + let _e368 = face; + let _e372 = point_shadow.faces[((_e366 * 6i) + _e368)]; + let _e373 = origin; + clip = (_e372 * vec4(_e373.x, _e373.y, _e373.z, 1f)); + let _e380 = clip[3u]; + if (_e380 <= 0f) { return 1f; } - let _e373 = clip; - let _e376 = clip[3u]; - ndc = (_e373.xy / vec2(_e376)); - let _e380 = ndc[0u]; - let _e382 = (abs(_e380) > 1f); - phi_2245_ = _e382; - if !(_e382) { - let _e385 = ndc[1u]; - phi_2245_ = (abs(_e385) > 1f); - } - let _e389 = phi_2245_; - if _e389 { + let _e382 = clip; + let _e385 = clip[3u]; + ndc = (_e382.xy / vec2(_e385)); + let _e389 = ndc[0u]; + let _e391 = (abs(_e389) > 1f); + phi_2254_ = _e391; + if !(_e391) { + let _e394 = ndc[1u]; + phi_2254_ = (abs(_e394) > 1f); + } + let _e398 = phi_2254_; + if _e398 { return 1f; } - let _e391 = ndc[0u]; - let _e395 = ndc[1u]; - uv_3 = vec2(((_e391 * 0.5f) + 0.5f), (0.5f - (_e395 * 0.5f))); - let _e399 = face; - let _e401 = slot; - tile_3 = vec2(f32(_e399), f32(_e401)); - let _e404 = distance_; - let _e407 = point_shadow.params[1u]; - receiver_2 = (_e404 - _e407); - let _e411 = frag_info.target_origin[0u]; - param_22 = _e411; - let _e412 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); - noise = fract((52.982918f * fract(dot(_e412, vec2(0.06711056f, 0.00583715f))))); - let _e417 = noise; - angle = (_e417 * 6.2831855f); - let _e419 = angle; - ca_2 = cos(_e419); - let _e421 = angle; - sa_2 = sin(_e421); - let _e423 = (*lightIndex); - let _e427 = point_shadow.slots[_e423][2u]; - tanHalf_1 = max(_e427, 0.0001f); + let _e400 = ndc[0u]; + let _e404 = ndc[1u]; + uv_3 = vec2(((_e400 * 0.5f) + 0.5f), (0.5f - (_e404 * 0.5f))); + let _e408 = face; + let _e410 = slot; + tile_3 = vec2(f32(_e408), f32(_e410)); + let _e413 = distance_; + let _e416 = point_shadow.params[1u]; + receiver_2 = (_e413 - _e416); + let _e420 = frag_info.target_origin[0u]; + param_28 = _e420; + let _e421 = FragCoordFromTop_u0028_f1_u003b((¶m_28)); + noise = fract((52.982918f * fract(dot(_e421, vec2(0.06711056f, 0.00583715f))))); + let _e426 = noise; + angle = (_e426 * 6.2831855f); + let _e428 = angle; + ca_2 = cos(_e428); + let _e430 = angle; + sa_2 = sin(_e430); + let _e432 = (*lightIndex); + let _e436 = point_shadow.slots[_e432][2u]; + tanHalf_1 = max(_e436, 0.0001f); blocker_1 = -1f; - let _e429 = uv_3; - param_23 = _e429; - let _e430 = tile_3; - param_24 = _e430; - let _e431 = range_3; - param_25 = _e431; - let _e432 = receiver_2; - param_26 = _e432; - let _e433 = ca_2; - param_27 = _e433; - let _e434 = sa_2; - param_28 = _e434; - let _e435 = tanHalf_1; - param_29 = _e435; - let _e436 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e437 = param_30; - blocker_1 = _e437; - radius_1 = _e436; - let _e440 = point_shadow.params2_[3u]; - if (_e440 > 0.5f) { + let _e438 = uv_3; + param_29 = _e438; + let _e439 = tile_3; + param_30 = _e439; + let _e440 = range_3; + param_31 = _e440; + let _e441 = receiver_2; + param_32 = _e441; + let _e442 = ca_2; + param_33 = _e442; + let _e443 = sa_2; + param_34 = _e443; + let _e444 = tanHalf_1; + param_35 = _e444; + let _e445 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_29), (¶m_30), (¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35), (¶m_36)); + let _e446 = param_36; + blocker_1 = _e446; + radius_1 = _e445; + let _e449 = point_shadow.params2_[3u]; + if (_e449 > 0.5f) { g_debug_surface_on = true; - let _e442 = radius_1; - if (_e442 < 0f) { - local_4 = vec3(0f, 0f, 1f); + let _e451 = radius_1; + if (_e451 < 0f) { + local_6 = vec3(0f, 0f, 1f); } else { - let _e444 = radius_1; - let _e447 = point_shadow.params2_[2u]; - let _e451 = blocker_1; - let _e452 = range_3; - local_4 = vec3(clamp((_e444 / max(_e447, 0.000001f)), 0f, 1f), clamp((_e451 / _e452), 0f, 1f), 0f); + let _e453 = radius_1; + let _e456 = point_shadow.params2_[2u]; + let _e460 = blocker_1; + let _e461 = range_3; + local_6 = vec3(clamp((_e453 / max(_e456, 0.000001f)), 0f, 1f), clamp((_e460 / _e461), 0f, 1f), 0f); } - let _e456 = local_4; - g_debug_surface = _e456; + let _e465 = local_6; + g_debug_surface = _e465; } - let _e457 = radius_1; - if (_e457 < 0f) { + let _e466 = radius_1; + if (_e466 < 0f) { return 1f; } - let _e459 = uv_3; - param_31 = _e459; - param_32 = vec2(0f, 0f); - let _e460 = tile_3; - param_33 = _e460; - let _e461 = range_3; - param_34 = _e461; - let _e462 = receiver_2; - param_35 = _e462; - let _e463 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); - lit = _e463; - let _e464 = radius_1; - if (_e464 > 0f) { + let _e468 = uv_3; + param_37 = _e468; + param_38 = vec2(0f, 0f); + let _e469 = tile_3; + param_39 = _e469; + let _e470 = range_3; + param_40 = _e470; + let _e471 = receiver_2; + param_41 = _e471; + let _e472 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_37), (¶m_38), (¶m_39), (¶m_40), (¶m_41)); + lit_1 = _e472; + let _e473 = radius_1; + if (_e473 > 0f) { i_3 = 0i; loop { - let _e466 = i_3; - if (_e466 < 8i) { - let _e468 = i_3; - param_36 = _e468; - let _e469 = ca_2; - param_37 = _e469; - let _e470 = sa_2; - param_38 = _e470; - let _e471 = radius_1; - param_39 = _e471; - let _e472 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - let _e473 = uv_3; - param_40 = _e473; - param_41 = _e472; - let _e474 = tile_3; - param_42 = _e474; - let _e475 = range_3; - param_43 = _e475; - let _e476 = receiver_2; - param_44 = _e476; - let _e477 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); - let _e478 = lit; - lit = (_e478 + _e477); + let _e475 = i_3; + if (_e475 < 8i) { + let _e477 = i_3; + param_42 = _e477; + let _e478 = ca_2; + param_43 = _e478; + let _e479 = sa_2; + param_44 = _e479; + let _e480 = radius_1; + param_45 = _e480; + let _e481 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_42), (¶m_43), (¶m_44), (¶m_45)); + let _e482 = uv_3; + param_46 = _e482; + param_47 = _e481; + let _e483 = tile_3; + param_48 = _e483; + let _e484 = range_3; + param_49 = _e484; + let _e485 = receiver_2; + param_50 = _e485; + let _e486 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_46), (¶m_47), (¶m_48), (¶m_49), (¶m_50)); + let _e487 = lit_1; + lit_1 = (_e487 + _e486); continue; } else { break; } continuing { - let _e480 = i_3; - i_3 = (_e480 + 1i); + let _e489 = i_3; + i_3 = (_e489 + 1i); } } - let _e482 = lit; - lit = (_e482 * 0.11111111f); + let _e491 = lit_1; + lit_1 = (_e491 * 0.11111111f); } - let _e484 = lit; - let _e485 = strength; - return mix(1f, _e484, clamp(_e485, 0f, 1f)); + let _e493 = lit_1; + let _e494 = strength; + return mix(1f, _e493, clamp(_e494, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r: f32; var theta: f32; - let _e182 = (*i_4); - let _e185 = (*n_1); - r = sqrt(((f32(_e182) + 0.5f) / f32(_e185))); - let _e189 = (*i_4); - let _e192 = (*turn); - theta = ((f32(_e189) * 2.3999631f) + _e192); - let _e194 = r; - let _e195 = theta; - let _e197 = theta; - return (vec2(cos(_e195), sin(_e197)) * _e194); + let _e191 = (*i_4); + let _e194 = (*n_1); + r = sqrt(((f32(_e191) + 0.5f) / f32(_e194))); + let _e198 = (*i_4); + let _e201 = (*turn); + theta = ((f32(_e198) * 2.3999631f) + _e201); + let _e203 = r; + let _e204 = theta; + let _e206 = theta; + return (vec2(cos(_e204), sin(_e206)) * _e203); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; - var param_45: f32; + var param_51: f32; - let _e181 = frag_info.target_origin[0u]; - param_45 = _e181; - let _e182 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); - let _e185 = frag_info.target_origin[3u]; - at = (_e182 + vec2((5.588238f * max(_e185, 0f)))); - let _e190 = at; - return fract((52.982918f * fract(dot(_e190, vec2(0.06711056f, 0.00583715f))))); + let _e190 = frag_info.target_origin[0u]; + param_51 = _e190; + let _e191 = FragCoordFromTop_u0028_f1_u003b((¶m_51)); + let _e194 = frag_info.target_origin[3u]; + at = (_e191 + vec2((5.588238f * max(_e194, 0f)))); + let _e199 = at; + return fract((52.982918f * fract(dot(_e199, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -22453,88 +23361,88 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_2: f32; - let _e179 = (*depth); - d_2 = ((2f * clamp(_e179, 0f, 1f)) - 1f); - let _e183 = d_2; - let _e186 = d_2; - return vec2(exp((40f * _e183)), -(exp((-5f * _e186)))); + let _e188 = (*depth); + d_2 = ((2f * clamp(_e188, 0f, 1f)) - 1f); + let _e192 = d_2; + let _e195 = d_2; + return vec2(exp((40f * _e192)), -(exp((-5f * _e195)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { var warped: vec2; - var param_46: f32; + var param_52: f32; var scale: vec2; var positive: f32; - var param_47: vec2; - var param_48: f32; - var param_49: f32; - var param_50: f32; - var negative: f32; - var param_51: vec2; - var param_52: f32; - var param_53: f32; + var param_53: vec2; var param_54: f32; + var param_55: f32; + var param_56: f32; + var negative: f32; + var param_57: vec2; + var param_58: f32; + var param_59: f32; + var param_60: f32; - let _e193 = (*depth_1); - param_46 = _e193; - let _e194 = EvsmWarp_u0028_f1_u003b((¶m_46)); - warped = _e194; - let _e195 = warped; - scale = (vec2(0.004f, 0.0005f) * _e195); - let _e198 = scale[0u]; - let _e200 = scale[0u]; - let _e202 = (*moments_1); - param_47 = _e202.xy; - let _e205 = warped[0u]; - param_48 = _e205; - param_49 = (_e198 * _e200); - let _e206 = (*bleed_1); - param_50 = _e206; - let _e207 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); - positive = _e207; - let _e209 = scale[1u]; - let _e211 = scale[1u]; - let _e213 = (*moments_1); - param_51 = _e213.zw; - let _e216 = warped[1u]; - param_52 = _e216; - param_53 = (_e209 * _e211); - let _e217 = (*bleed_1); - param_54 = _e217; - let _e218 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); - negative = _e218; - let _e219 = positive; - let _e220 = negative; - return min(_e219, _e220); -} - -fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_1: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { + let _e202 = (*depth_1); + param_52 = _e202; + let _e203 = EvsmWarp_u0028_f1_u003b((¶m_52)); + warped = _e203; + let _e204 = warped; + scale = (vec2(0.004f, 0.0005f) * _e204); + let _e207 = scale[0u]; + let _e209 = scale[0u]; + let _e211 = (*moments_1); + param_53 = _e211.xy; + let _e214 = warped[0u]; + param_54 = _e214; + param_55 = (_e207 * _e209); + let _e215 = (*bleed_1); + param_56 = _e215; + let _e216 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_53), (¶m_54), (¶m_55), (¶m_56)); + positive = _e216; + let _e218 = scale[1u]; + let _e220 = scale[1u]; + let _e222 = (*moments_1); + param_57 = _e222.zw; + let _e225 = warped[1u]; + param_58 = _e225; + param_59 = (_e218 * _e220); + let _e226 = (*bleed_1); + param_60 = _e226; + let _e227 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_57), (¶m_58), (¶m_59), (¶m_60)); + negative = _e227; + let _e228 = positive; + let _e229 = negative; + return min(_e228, _e229); +} + +fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { var strength_1: f32; var cascadeCount: i32; var viewDistance: f32; @@ -22548,8 +23456,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_5: mat4x4; - var local_6: mat4x4; + var local_7: mat4x4; + var local_8: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -22563,17 +23471,17 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_7: f32; - var local_8: f32; + var local_9: f32; + var local_10: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; var softness: f32; - var lit_1: f32; + var lit_2: f32; var moments_2: vec4; - var param_55: vec4; - var param_56: f32; - var param_57: f32; + var param_61: vec4; + var param_62: f32; + var param_63: f32; var y: i32; var x: i32; var occluder: f32; @@ -22584,65 +23492,65 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var blockerCount: f32; var i_5: i32; var disc: vec2; - var param_58: i32; - var param_59: i32; - var param_60: f32; + var param_64: i32; + var param_65: i32; + var param_66: f32; var tapBias: f32; var occluder_1: f32; var gap: f32; var radius_2: f32; var i_6: i32; var disc_1: vec2; - var param_61: i32; - var param_62: i32; - var param_63: f32; + var param_67: i32; + var param_68: i32; + var param_69: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3303_: bool; - var phi_3314_: bool; - var phi_3475_: bool; - var phi_3482_: bool; - var phi_3489_: bool; - - let _e245 = frag_info.shadow_params[3u]; - strength_1 = _e245; - let _e246 = strength_1; - if (_e246 <= 0f) { + var phi_3473_: bool; + var phi_3484_: bool; + var phi_3645_: bool; + var phi_3652_: bool; + var phi_3659_: bool; + + let _e254 = frag_info.shadow_params[3u]; + strength_1 = _e254; + let _e255 = strength_1; + if (_e255 <= 0f) { return 1f; } - let _e248 = (*lightIndex_1); - let _e251 = frag_info.frame_params[2u]; - if (_e248 != i32((_e251 + 0.5f))) { + let _e257 = (*lightIndex_1); + let _e260 = frag_info.frame_params[2u]; + if (_e257 != i32((_e260 + 0.5f))) { return 1f; } - let _e257 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e257 + 0.5f)); - let _e260 = v_world_position_1; - let _e262 = frag_info.camera_position; - viewDistance = length((_e260 - _e262.xyz)); + let _e266 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e266 + 0.5f)); + let _e269 = v_world_position_1; + let _e271 = frag_info.camera_position; + viewDistance = length((_e269 - _e271.xyz)); cascade = 0i; - let _e266 = cascadeCount; - let _e267 = (_e266 > 1i); - phi_3303_ = _e267; - if _e267 { - let _e268 = viewDistance; - let _e271 = frag_info.shadow_cascades[0u]; - phi_3303_ = (_e268 > _e271); - } - let _e274 = phi_3303_; - if _e274 { - cascade = 1i; - } let _e275 = cascadeCount; - let _e276 = (_e275 > 2i); - phi_3314_ = _e276; + let _e276 = (_e275 > 1i); + phi_3473_ = _e276; if _e276 { let _e277 = viewDistance; - let _e280 = frag_info.shadow_cascades[1u]; - phi_3314_ = (_e277 > _e280); + let _e280 = frag_info.shadow_cascades[0u]; + phi_3473_ = (_e277 > _e280); } - let _e283 = phi_3314_; + let _e283 = phi_3473_; if _e283 { + cascade = 1i; + } + let _e284 = cascadeCount; + let _e285 = (_e284 > 2i); + phi_3484_ = _e285; + if _e285 { + let _e286 = viewDistance; + let _e289 = frag_info.shadow_cascades[1u]; + phi_3484_ = (_e286 > _e289); + } + let _e292 = phi_3484_; + if _e292 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -22653,232 +23561,232 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e284 = attempt; - if (_e284 < 3i) { - let _e286 = cascade; - let _e287 = attempt; - which = (_e286 + _e287); - let _e289 = which; - let _e290 = cascadeCount; - if (_e289 >= _e290) { + let _e293 = attempt; + if (_e293 < 3i) { + let _e295 = cascade; + let _e296 = attempt; + which = (_e295 + _e296); + let _e298 = which; + let _e299 = cascadeCount; + if (_e298 >= _e299) { break; } - let _e292 = which; - if (_e292 == 0i) { - let _e295 = frag_info.shadow_matrix; - local_5 = _e295; + let _e301 = which; + if (_e301 == 0i) { + let _e304 = frag_info.shadow_matrix; + local_7 = _e304; } else { - let _e296 = which; - if (_e296 == 1i) { - let _e299 = frag_info.shadow_matrix_far; - local_6 = _e299; + let _e305 = which; + if (_e305 == 1i) { + let _e308 = frag_info.shadow_matrix_far; + local_8 = _e308; } else { - let _e301 = frag_info.shadow_matrix_farthest; - local_6 = _e301; + let _e310 = frag_info.shadow_matrix_farthest; + local_8 = _e310; } - let _e302 = local_6; - local_5 = _e302; - } - let _e303 = local_5; - matrix = _e303; - let _e306 = matrix[0][0u]; - let _e309 = matrix[1][0u]; - let _e312 = matrix[2][0u]; - axisX = vec3(_e306, _e309, _e312); - let _e316 = matrix[0][1u]; - let _e319 = matrix[1][1u]; - let _e322 = matrix[2][1u]; - axisY = vec3(_e316, _e319, _e322); - let _e324 = axisX; - rowX = max(length(_e324), 0.000001f); - let _e327 = axisY; - rowY = max(length(_e327), 0.000001f); - let _e332 = frag_info.shadow_cascades[3u]; - let _e334 = rowX; - texelMetres = ((2f * _e332) / _e334); - let _e338 = frag_info.shadow_cascades[3u]; - let _e341 = (*s_1).n; - let _e342 = axisX; - let _e345 = rowX; - let _e346 = rowX; - let _e350 = (*s_1).n; - let _e351 = axisY; - let _e354 = rowY; - let _e355 = rowY; - reach = ((3f * _e338) * ((abs(dot(_e341, _e342)) / (_e345 * _e346)) + (abs(dot(_e350, _e351)) / (_e354 * _e355)))); - let _e362 = frag_info.shadow_params[2u]; - let _e363 = texelMetres; - flatOffset = min(_e362, _e363); - let _e365 = v_world_position_1; - let _e367 = (*s_1).n; - let _e368 = flatOffset; - let _e369 = reach; - origin_1 = (_e365 + (_e367 * (_e368 + _e369))); - let _e373 = matrix; - let _e374 = origin_1; - lightSpace = (_e373 * vec4(_e374.x, _e374.y, _e374.z, 1f)); - let _e381 = lightSpace[3u]; - if (_e381 <= 0f) { + let _e311 = local_8; + local_7 = _e311; + } + let _e312 = local_7; + matrix = _e312; + let _e315 = matrix[0][0u]; + let _e318 = matrix[1][0u]; + let _e321 = matrix[2][0u]; + axisX = vec3(_e315, _e318, _e321); + let _e325 = matrix[0][1u]; + let _e328 = matrix[1][1u]; + let _e331 = matrix[2][1u]; + axisY = vec3(_e325, _e328, _e331); + let _e333 = axisX; + rowX = max(length(_e333), 0.000001f); + let _e336 = axisY; + rowY = max(length(_e336), 0.000001f); + let _e341 = frag_info.shadow_cascades[3u]; + let _e343 = rowX; + texelMetres = ((2f * _e341) / _e343); + let _e347 = frag_info.shadow_cascades[3u]; + let _e350 = (*s_2).n; + let _e351 = axisX; + let _e354 = rowX; + let _e355 = rowX; + let _e359 = (*s_2).n; + let _e360 = axisY; + let _e363 = rowY; + let _e364 = rowY; + reach = ((3f * _e347) * ((abs(dot(_e350, _e351)) / (_e354 * _e355)) + (abs(dot(_e359, _e360)) / (_e363 * _e364)))); + let _e371 = frag_info.shadow_params[2u]; + let _e372 = texelMetres; + flatOffset = min(_e371, _e372); + let _e374 = v_world_position_1; + let _e376 = (*s_2).n; + let _e377 = flatOffset; + let _e378 = reach; + origin_1 = (_e374 + (_e376 * (_e377 + _e378))); + let _e382 = matrix; + let _e383 = origin_1; + lightSpace = (_e382 * vec4(_e383.x, _e383.y, _e383.z, 1f)); + let _e390 = lightSpace[3u]; + if (_e390 <= 0f) { continue; } - let _e383 = lightSpace; - let _e386 = lightSpace[3u]; - candidate = (_e383.xyz / vec3(_e386)); - let _e390 = candidate[0u]; - let _e394 = candidate[1u]; - inTile = vec2(((_e390 * 0.5f) + 0.5f), (0.5f - (_e394 * 0.5f))); - let _e399 = inTile[0u]; - let _e400 = (_e399 < 0f); - phi_3475_ = _e400; - if !(_e400) { - let _e403 = inTile[0u]; - phi_3475_ = (_e403 > 1f); - } - let _e406 = phi_3475_; - phi_3482_ = _e406; - if !(_e406) { - let _e409 = inTile[1u]; - phi_3482_ = (_e409 < 0f); - } - let _e412 = phi_3482_; - phi_3489_ = _e412; - if !(_e412) { - let _e415 = inTile[1u]; - phi_3489_ = (_e415 > 1f); - } - let _e418 = phi_3489_; - if _e418 { + let _e392 = lightSpace; + let _e395 = lightSpace[3u]; + candidate = (_e392.xyz / vec3(_e395)); + let _e399 = candidate[0u]; + let _e403 = candidate[1u]; + inTile = vec2(((_e399 * 0.5f) + 0.5f), (0.5f - (_e403 * 0.5f))); + let _e408 = inTile[0u]; + let _e409 = (_e408 < 0f); + phi_3645_ = _e409; + if !(_e409) { + let _e412 = inTile[0u]; + phi_3645_ = (_e412 > 1f); + } + let _e415 = phi_3645_; + phi_3652_ = _e415; + if !(_e415) { + let _e418 = inTile[1u]; + phi_3652_ = (_e418 < 0f); + } + let _e421 = phi_3652_; + phi_3659_ = _e421; + if !(_e421) { + let _e424 = inTile[1u]; + phi_3659_ = (_e424 > 1f); + } + let _e427 = phi_3659_; + if _e427 { continue; } - let _e420 = candidate[2u]; - if (_e420 > 1f) { - let _e422 = which; - let _e423 = cascadeCount; - if (_e422 < (_e423 - 1i)) { + let _e429 = candidate[2u]; + if (_e429 > 1f) { + let _e431 = which; + let _e432 = cascadeCount; + if (_e431 < (_e432 - 1i)) { continue; } candidate[2u] = 1f; } - let _e428 = inTile[0u]; - let _e429 = which; - let _e432 = cascadeCount; - let _e436 = inTile[1u]; - uv_4 = vec2(((_e428 + f32(_e429)) / f32(_e432)), _e436); - let _e438 = candidate; - projected = _e438; - let _e439 = which; - cascade = _e439; - let _e440 = texelMetres; - cascadeTexel = _e440; - let _e443 = matrix[0][2u]; - let _e446 = matrix[1][2u]; - let _e449 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e443, _e446, _e449)), 0.000001f)); - let _e455 = (*s_1).n; - let _e458 = matrix[0][2u]; - let _e461 = matrix[1][2u]; - let _e464 = matrix[2][2u]; - let _e468 = cascadeDepth; - cosSlope = clamp((abs(dot(_e455, vec3(_e458, _e461, _e464))) * _e468), 0.1f, 1f); - let _e471 = texelMetres; - let _e472 = cosSlope; - let _e473 = cosSlope; - let _e479 = cosSlope; - let _e481 = cascadeDepth; - receiverSlope = (((_e471 * sqrt(max((1f - (_e472 * _e473)), 0f))) / _e479) / _e481); + let _e437 = inTile[0u]; + let _e438 = which; + let _e441 = cascadeCount; + let _e445 = inTile[1u]; + uv_4 = vec2(((_e437 + f32(_e438)) / f32(_e441)), _e445); + let _e447 = candidate; + projected = _e447; + let _e448 = which; + cascade = _e448; + let _e449 = texelMetres; + cascadeTexel = _e449; + let _e452 = matrix[0][2u]; + let _e455 = matrix[1][2u]; + let _e458 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e452, _e455, _e458)), 0.000001f)); + let _e464 = (*s_2).n; + let _e467 = matrix[0][2u]; + let _e470 = matrix[1][2u]; + let _e473 = matrix[2][2u]; + let _e477 = cascadeDepth; + cosSlope = clamp((abs(dot(_e464, vec3(_e467, _e470, _e473))) * _e477), 0.1f, 1f); + let _e480 = texelMetres; + let _e481 = cosSlope; + let _e482 = cosSlope; + let _e488 = cosSlope; + let _e490 = cascadeDepth; + receiverSlope = (((_e480 * sqrt(max((1f - (_e481 * _e482)), 0f))) / _e488) / _e490); found = true; break; } else { break; } continuing { - let _e483 = attempt; - attempt = (_e483 + 1i); + let _e492 = attempt; + attempt = (_e492 + 1i); } } - let _e485 = found; - if !(_e485) { + let _e494 = found; + if !(_e494) { return 1f; } - let _e487 = cascade; - if (_e487 == 0i) { - let _e491 = frag_info.shadow_bias[0u]; - local_7 = _e491; + let _e496 = cascade; + if (_e496 == 0i) { + let _e500 = frag_info.shadow_bias[0u]; + local_9 = _e500; } else { - let _e492 = cascade; - if (_e492 == 1i) { - let _e496 = frag_info.shadow_bias[1u]; - local_8 = _e496; + let _e501 = cascade; + if (_e501 == 1i) { + let _e505 = frag_info.shadow_bias[1u]; + local_10 = _e505; } else { - let _e499 = frag_info.shadow_bias[2u]; - local_8 = _e499; - } - let _e500 = local_8; - local_7 = _e500; - } - let _e501 = local_7; - bias = _e501; - let _e504 = frag_info.shadow_params[0u]; - let _e507 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e504, _e507); - let _e509 = cascade; - let _e511 = cascadeCount; - let _e515 = texel_1; - tileLo = (vec2((f32(_e509) / f32(_e511)), 0f) + (_e515 * 0.5f)); + let _e508 = frag_info.shadow_bias[2u]; + local_10 = _e508; + } + let _e509 = local_10; + local_9 = _e509; + } + let _e510 = local_9; + bias = _e510; + let _e513 = frag_info.shadow_params[0u]; + let _e516 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e513, _e516); let _e518 = cascade; - let _e521 = cascadeCount; - let _e525 = texel_1; - tileHi = (vec2((f32((_e518 + 1i)) / f32(_e521)), 1f) - (_e525 * 0.5f)); - let _e530 = frag_info.ambient_ground[3u]; - softness = _e530; - lit_1 = 0f; - let _e531 = softness; - if (_e531 < 0f) { - let _e533 = uv_4; - let _e534 = tileLo; - let _e535 = tileHi; - let _e537 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e533, _e534, _e535), 0f); - moments_2 = _e537; - let _e539 = projected[2u]; - let _e540 = bias; - let _e542 = softness; - let _e546 = moments_2; - param_55 = _e546; - param_56 = (_e539 - _e540); - param_57 = clamp((-(_e542) - 1f), 0f, 0.95f); - let _e547 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); - lit_1 = _e547; + let _e520 = cascadeCount; + let _e524 = texel_1; + tileLo = (vec2((f32(_e518) / f32(_e520)), 0f) + (_e524 * 0.5f)); + let _e527 = cascade; + let _e530 = cascadeCount; + let _e534 = texel_1; + tileHi = (vec2((f32((_e527 + 1i)) / f32(_e530)), 1f) - (_e534 * 0.5f)); + let _e539 = frag_info.ambient_ground[3u]; + softness = _e539; + lit_2 = 0f; + let _e540 = softness; + if (_e540 < 0f) { + let _e542 = uv_4; + let _e543 = tileLo; + let _e544 = tileHi; + let _e546 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e542, _e543, _e544), 0f); + moments_2 = _e546; + let _e548 = projected[2u]; + let _e549 = bias; + let _e551 = softness; + let _e555 = moments_2; + param_61 = _e555; + param_62 = (_e548 - _e549); + param_63 = clamp((-(_e551) - 1f), 0f, 0.95f); + let _e556 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); + lit_2 = _e556; } else { - let _e548 = softness; - if (_e548 <= 0f) { + let _e557 = softness; + if (_e557 <= 0f) { y = -1i; loop { - let _e550 = y; - if (_e550 <= 1i) { + let _e559 = y; + if (_e559 <= 1i) { x = -1i; loop { - let _e552 = x; - if (_e552 <= 1i) { - let _e554 = uv_4; - let _e555 = x; - let _e557 = y; - let _e560 = texel_1; - let _e563 = tileLo; - let _e564 = tileHi; - let _e566 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e554 + (vec2(f32(_e555), f32(_e557)) * _e560)), _e563, _e564), 0f); - occluder = _e566.x; - let _e569 = projected[2u]; - let _e570 = bias; - let _e572 = occluder; - let _e575 = lit_1; - lit_1 = (_e575 + select(1f, 0f, ((_e569 - _e570) > _e572))); + let _e561 = x; + if (_e561 <= 1i) { + let _e563 = uv_4; + let _e564 = x; + let _e566 = y; + let _e569 = texel_1; + let _e572 = tileLo; + let _e573 = tileHi; + let _e575 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e563 + (vec2(f32(_e564), f32(_e566)) * _e569)), _e572, _e573), 0f); + occluder = _e575.x; + let _e578 = projected[2u]; + let _e579 = bias; + let _e581 = occluder; + let _e584 = lit_2; + lit_2 = (_e584 + select(1f, 0f, ((_e578 - _e579) > _e581))); continue; } else { break; } continuing { - let _e577 = x; - x = (_e577 + 1i); + let _e586 = x; + x = (_e586 + 1i); } } continue; @@ -22886,145 +23794,145 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e579 = y; - y = (_e579 + 1i); + let _e588 = y; + y = (_e588 + 1i); } } - let _e581 = lit_1; - lit_1 = (_e581 * 0.11111111f); + let _e590 = lit_2; + lit_2 = (_e590 * 0.11111111f); } else { - let _e583 = softness; - spread = tan(min(_e583, 0.5f)); - let _e586 = ShadowNoise_u0028_(); - turn_1 = (_e586 * 6.2831855f); - let _e588 = spread; - let _e590 = projected[2u]; - let _e592 = cascadeDepth; - let _e594 = cascadeTexel; - searchRadius = clamp((((_e588 * _e590) * _e592) / _e594), 1f, 16f); + let _e592 = softness; + spread = tan(min(_e592, 0.5f)); + let _e595 = ShadowNoise_u0028_(); + turn_1 = (_e595 * 6.2831855f); + let _e597 = spread; + let _e599 = projected[2u]; + let _e601 = cascadeDepth; + let _e603 = cascadeTexel; + searchRadius = clamp((((_e597 * _e599) * _e601) / _e603), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e597 = i_5; - if (_e597 < 16i) { - let _e599 = i_5; - param_58 = _e599; - param_59 = 16i; - let _e600 = turn_1; - param_60 = _e600; - let _e601 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); - disc = _e601; - let _e602 = bias; - let _e603 = disc; - let _e605 = searchRadius; - let _e609 = receiverSlope; - tapBias = (_e602 + (max(((length(_e603) * _e605) - 1.5f), 0f) * _e609)); - let _e612 = uv_4; - let _e613 = disc; - let _e614 = texel_1; - let _e616 = searchRadius; - let _e619 = tileLo; - let _e620 = tileHi; - let _e622 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e612 + ((_e613 * _e614) * _e616)), _e619, _e620), 0f); - occluder_1 = _e622.x; - let _e625 = projected[2u]; - let _e626 = tapBias; - let _e628 = occluder_1; - if ((_e625 - _e626) > _e628) { - let _e630 = occluder_1; - let _e631 = blockerSum; - blockerSum = (_e631 + _e630); - let _e633 = blockerCount; - blockerCount = (_e633 + 1f); + let _e606 = i_5; + if (_e606 < 16i) { + let _e608 = i_5; + param_64 = _e608; + param_65 = 16i; + let _e609 = turn_1; + param_66 = _e609; + let _e610 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_64), (¶m_65), (¶m_66)); + disc = _e610; + let _e611 = bias; + let _e612 = disc; + let _e614 = searchRadius; + let _e618 = receiverSlope; + tapBias = (_e611 + (max(((length(_e612) * _e614) - 1.5f), 0f) * _e618)); + let _e621 = uv_4; + let _e622 = disc; + let _e623 = texel_1; + let _e625 = searchRadius; + let _e628 = tileLo; + let _e629 = tileHi; + let _e631 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e621 + ((_e622 * _e623) * _e625)), _e628, _e629), 0f); + occluder_1 = _e631.x; + let _e634 = projected[2u]; + let _e635 = tapBias; + let _e637 = occluder_1; + if ((_e634 - _e635) > _e637) { + let _e639 = occluder_1; + let _e640 = blockerSum; + blockerSum = (_e640 + _e639); + let _e642 = blockerCount; + blockerCount = (_e642 + 1f); } continue; } else { break; } continuing { - let _e635 = i_5; - i_5 = (_e635 + 1i); + let _e644 = i_5; + i_5 = (_e644 + 1i); } } - let _e637 = blockerCount; - if (_e637 <= 0f) { + let _e646 = blockerCount; + if (_e646 <= 0f) { return 1f; } - let _e640 = projected[2u]; - let _e641 = blockerSum; - let _e642 = blockerCount; - let _e646 = cascadeDepth; - gap = (max((_e640 - (_e641 / _e642)), 0f) * _e646); - let _e648 = spread; - let _e649 = gap; - let _e651 = cascadeTexel; - radius_2 = clamp(((_e648 * _e649) / _e651), 1f, 16f); + let _e649 = projected[2u]; + let _e650 = blockerSum; + let _e651 = blockerCount; + let _e655 = cascadeDepth; + gap = (max((_e649 - (_e650 / _e651)), 0f) * _e655); + let _e657 = spread; + let _e658 = gap; + let _e660 = cascadeTexel; + radius_2 = clamp(((_e657 * _e658) / _e660), 1f, 16f); i_6 = 0i; loop { - let _e654 = i_6; - if (_e654 < 16i) { - let _e656 = turn_1; - let _e658 = i_6; - param_61 = _e658; - param_62 = 16i; - param_63 = (_e656 + 1f); - let _e659 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - disc_1 = _e659; - let _e660 = bias; - let _e661 = disc_1; - let _e663 = radius_2; - let _e667 = receiverSlope; - tapBias_1 = (_e660 + (max(((length(_e661) * _e663) - 1.5f), 0f) * _e667)); - let _e670 = uv_4; - let _e671 = disc_1; - let _e672 = texel_1; - let _e674 = radius_2; - let _e677 = tileLo; - let _e678 = tileHi; - let _e680 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e670 + ((_e671 * _e672) * _e674)), _e677, _e678), 0f); - occluder_2 = _e680.x; - let _e683 = projected[2u]; - let _e684 = tapBias_1; - let _e686 = occluder_2; - let _e689 = lit_1; - lit_1 = (_e689 + select(1f, 0f, ((_e683 - _e684) > _e686))); + let _e663 = i_6; + if (_e663 < 16i) { + let _e665 = turn_1; + let _e667 = i_6; + param_67 = _e667; + param_68 = 16i; + param_69 = (_e665 + 1f); + let _e668 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_67), (¶m_68), (¶m_69)); + disc_1 = _e668; + let _e669 = bias; + let _e670 = disc_1; + let _e672 = radius_2; + let _e676 = receiverSlope; + tapBias_1 = (_e669 + (max(((length(_e670) * _e672) - 1.5f), 0f) * _e676)); + let _e679 = uv_4; + let _e680 = disc_1; + let _e681 = texel_1; + let _e683 = radius_2; + let _e686 = tileLo; + let _e687 = tileHi; + let _e689 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e679 + ((_e680 * _e681) * _e683)), _e686, _e687), 0f); + occluder_2 = _e689.x; + let _e692 = projected[2u]; + let _e693 = tapBias_1; + let _e695 = occluder_2; + let _e698 = lit_2; + lit_2 = (_e698 + select(1f, 0f, ((_e692 - _e693) > _e695))); continue; } else { break; } continuing { - let _e691 = i_6; - i_6 = (_e691 + 1i); + let _e700 = i_6; + i_6 = (_e700 + 1i); } } - let _e693 = lit_1; - lit_1 = (_e693 * 0.0625f); + let _e702 = lit_2; + lit_2 = (_e702 * 0.0625f); } } - let _e695 = lit_1; - let _e696 = strength_1; - return mix(1f, _e695, clamp(_e696, 0f, 1f)); + let _e704 = lit_2; + let _e705 = strength_1; + return mix(1f, _e704, clamp(_e705, 0f, 1f)); } -fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_2: ptr, index: ptr) -> f32 { - var param_64: Surface; - var param_65: LightSample; - var param_66: i32; +fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_3: ptr, light_2: ptr, index: ptr) -> f32 { + var param_70: Surface; + var param_71: LightSample; + var param_72: i32; - let _e183 = (*s_2); - param_64 = _e183; - let _e184 = (*light_2); - param_65 = _e184; - let _e185 = (*index); - param_66 = _e185; - let _e186 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); - return _e186; + let _e192 = (*s_3); + param_70 = _e192; + let _e193 = (*light_2); + param_71 = _e193; + let _e194 = (*index); + param_72 = _e194; + let _e195 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_70), (¶m_71), (¶m_72)); + return _e195; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e178 = (*index_1); - return (_e178 < 8i); + let _e187 = (*index_1); + return (_e187 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -23032,26 +23940,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e182 = (*distance_1); - let _e183 = (*distance_1); - attenuation = (1f / max((_e182 * _e183), 0.0001f)); - let _e187 = (*range_4); - if (_e187 > 0f) { - let _e189 = (*distance_1); - let _e190 = (*range_4); - ratio = (_e189 / _e190); - let _e192 = ratio; - let _e193 = ratio; - let _e195 = ratio; - let _e197 = ratio; - window = clamp((1f - (((_e192 * _e193) * _e195) * _e197)), 0f, 1f); - let _e201 = window; - let _e202 = window; - let _e204 = attenuation; - attenuation = (_e204 * (_e201 * _e202)); - } - let _e206 = attenuation; - return _e206; + let _e191 = (*distance_1); + let _e192 = (*distance_1); + attenuation = (1f / max((_e191 * _e192), 0.0001f)); + let _e196 = (*range_4); + if (_e196 > 0f) { + let _e198 = (*distance_1); + let _e199 = (*range_4); + ratio = (_e198 / _e199); + let _e201 = ratio; + let _e202 = ratio; + let _e204 = ratio; + let _e206 = ratio; + window = clamp((1f - (((_e201 * _e202) * _e204) * _e206)), 0f, 1f); + let _e210 = window; + let _e211 = window; + let _e213 = attenuation; + attenuation = (_e213 * (_e210 * _e211)); + } + let _e215 = attenuation; + return _e215; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -23061,82 +23969,82 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_9: vec3; + var local_11: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; var u: f32; var v: f32; - let _e194 = (*halfWidth); - let _e195 = (*halfHeight); - n_2 = cross(_e194, _e195); - let _e197 = n_2; - nLen = length(_e197); - let _e199 = nLen; - if (_e199 < 0.000000000001f) { - let _e201 = (*centre); - return _e201; - } - let _e202 = nLen; - let _e203 = n_2; - n_2 = (_e203 / vec3(_e202)); - let _e206 = (*centre); - let _e207 = (*world_2); - toPlane = (_e206 - _e207); - let _e209 = (*mirror); - let _e210 = n_2; - denom = dot(_e209, _e210); - let _e212 = denom; - if (abs(_e212) < 0.00001f) { - let _e215 = toPlane; - let _e216 = n_2; - let _e217 = toPlane; - let _e218 = n_2; - onPlane = (_e215 - (_e216 * dot(_e217, _e218))); + let _e203 = (*halfWidth); + let _e204 = (*halfHeight); + n_2 = cross(_e203, _e204); + let _e206 = n_2; + nLen = length(_e206); + let _e208 = nLen; + if (_e208 < 0.000000000001f) { + let _e210 = (*centre); + return _e210; + } + let _e211 = nLen; + let _e212 = n_2; + n_2 = (_e212 / vec3(_e211)); + let _e215 = (*centre); + let _e216 = (*world_2); + toPlane = (_e215 - _e216); + let _e218 = (*mirror); + let _e219 = n_2; + denom = dot(_e218, _e219); + let _e221 = denom; + if (abs(_e221) < 0.00001f) { + let _e224 = toPlane; + let _e225 = n_2; + let _e226 = toPlane; + let _e227 = n_2; + onPlane = (_e224 - (_e225 * dot(_e226, _e227))); } else { - let _e222 = toPlane; - let _e223 = n_2; - let _e225 = denom; - t_1 = (dot(_e222, _e223) / _e225); - let _e227 = t_1; - if (_e227 > 0f) { - let _e229 = (*mirror); - let _e230 = t_1; - local_9 = (_e229 * _e230); + let _e231 = toPlane; + let _e232 = n_2; + let _e234 = denom; + t_1 = (dot(_e231, _e232) / _e234); + let _e236 = t_1; + if (_e236 > 0f) { + let _e238 = (*mirror); + let _e239 = t_1; + local_11 = (_e238 * _e239); } else { - let _e232 = toPlane; - let _e233 = n_2; - let _e234 = toPlane; - let _e235 = n_2; - local_9 = (_e232 - (_e233 * dot(_e234, _e235))); - } - let _e239 = local_9; - onPlane = _e239; - } - let _e240 = onPlane; - let _e241 = toPlane; - offset_2 = (_e240 - _e241); - let _e243 = (*halfWidth); - let _e244 = (*halfWidth); - wLen2_ = max(dot(_e243, _e244), 0.000000000001f); - let _e247 = (*halfHeight); - let _e248 = (*halfHeight); - hLen2_ = max(dot(_e247, _e248), 0.000000000001f); - let _e251 = offset_2; + let _e241 = toPlane; + let _e242 = n_2; + let _e243 = toPlane; + let _e244 = n_2; + local_11 = (_e241 - (_e242 * dot(_e243, _e244))); + } + let _e248 = local_11; + onPlane = _e248; + } + let _e249 = onPlane; + let _e250 = toPlane; + offset_2 = (_e249 - _e250); let _e252 = (*halfWidth); - let _e254 = wLen2_; - u = clamp((dot(_e251, _e252) / _e254), -1f, 1f); - let _e257 = offset_2; - let _e258 = (*halfHeight); - let _e260 = hLen2_; - v = clamp((dot(_e257, _e258) / _e260), -1f, 1f); - let _e263 = (*centre); - let _e264 = (*halfWidth); - let _e265 = u; - let _e268 = (*halfHeight); - let _e269 = v; - return ((_e263 + (_e264 * _e265)) + (_e268 * _e269)); + let _e253 = (*halfWidth); + wLen2_ = max(dot(_e252, _e253), 0.000000000001f); + let _e256 = (*halfHeight); + let _e257 = (*halfHeight); + hLen2_ = max(dot(_e256, _e257), 0.000000000001f); + let _e260 = offset_2; + let _e261 = (*halfWidth); + let _e263 = wLen2_; + u = clamp((dot(_e260, _e261) / _e263), -1f, 1f); + let _e266 = offset_2; + let _e267 = (*halfHeight); + let _e269 = hLen2_; + v = clamp((dot(_e266, _e267) / _e269), -1f, 1f); + let _e272 = (*centre); + let _e273 = (*halfWidth); + let _e274 = u; + let _e277 = (*halfHeight); + let _e278 = v; + return ((_e272 + (_e273 * _e274)) + (_e277 * _e278)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>, n_3: ptr>) -> f32 { @@ -23145,34 +24053,34 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_10: f32; + var local_12: f32; - let _e186 = (*a_1); - let _e187 = (*a_1); - ua = (_e186 / vec3(max(length(_e187), 0.000000000001f))); - let _e192 = (*b); - let _e193 = (*b); - ub = (_e192 / vec3(max(length(_e193), 0.000000000001f))); - let _e198 = ua; - let _e199 = ub; - angle_1 = acos(clamp(dot(_e198, _e199), -1f, 1f)); - let _e203 = ua; - let _e204 = ub; - axis = cross(_e203, _e204); - let _e206 = axis; - len = length(_e206); - let _e208 = len; - if (_e208 > 0.000001f) { - let _e210 = angle_1; - let _e211 = axis; - let _e212 = (*n_3); - let _e215 = len; - local_10 = ((_e210 * dot(_e211, _e212)) / _e215); + let _e195 = (*a_1); + let _e196 = (*a_1); + ua = (_e195 / vec3(max(length(_e196), 0.000000000001f))); + let _e201 = (*b); + let _e202 = (*b); + ub = (_e201 / vec3(max(length(_e202), 0.000000000001f))); + let _e207 = ua; + let _e208 = ub; + angle_1 = acos(clamp(dot(_e207, _e208), -1f, 1f)); + let _e212 = ua; + let _e213 = ub; + axis = cross(_e212, _e213); + let _e215 = axis; + len = length(_e215); + let _e217 = len; + if (_e217 > 0.000001f) { + let _e219 = angle_1; + let _e220 = axis; + let _e221 = (*n_3); + let _e224 = len; + local_12 = ((_e219 * dot(_e220, _e221)) / _e224); } else { - local_10 = 0f; + local_12 = 0f; } - let _e217 = local_10; - return _e217; + let _e226 = local_12; + return _e226; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr, 4>>, n_4: ptr>) -> f32 { @@ -23182,212 +24090,212 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_11: i32; + var local_13: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_12: f32; + var local_14: f32; var aAbove: bool; var bAbove: bool; - var local_13: f32; - var param_67: vec3; - var param_68: vec3; - var param_69: vec3; - var param_70: vec3; - var param_71: vec3; - var param_72: vec3; + var local_15: f32; + var param_73: vec3; + var param_74: vec3; + var param_75: vec3; + var param_76: vec3; + var param_77: vec3; + var param_78: vec3; total = 0f; exit = vec3(0f, 0f, 0f); entry = vec3(0f, 0f, 0f); i_7 = 0i; loop { - let _e200 = i_7; - if (_e200 < 4i) { - let _e202 = i_7; - let _e204 = (*corners)[_e202]; - a_2 = _e204; - let _e205 = i_7; - if (_e205 == 3i) { - local_11 = 0i; + let _e209 = i_7; + if (_e209 < 4i) { + let _e211 = i_7; + let _e213 = (*corners)[_e211]; + a_2 = _e213; + let _e214 = i_7; + if (_e214 == 3i) { + local_13 = 0i; } else { - let _e207 = i_7; - local_11 = (_e207 + 1i); + let _e216 = i_7; + local_13 = (_e216 + 1i); } - let _e209 = local_11; - let _e211 = (*corners)[_e209]; - b_1 = _e211; - let _e212 = a_2; - let _e213 = (*n_4); - ha = dot(_e212, _e213); - let _e215 = b_1; - let _e216 = (*n_4); - hb = dot(_e215, _e216); - let _e218 = ha; - let _e219 = hb; - d_3 = (_e218 - _e219); + let _e218 = local_13; + let _e220 = (*corners)[_e218]; + b_1 = _e220; let _e221 = a_2; - let _e222 = b_1; - let _e223 = a_2; - let _e225 = d_3; - if (abs(_e225) > 0.000000000001f) { - let _e228 = ha; - let _e229 = d_3; - local_12 = (_e228 / _e229); + let _e222 = (*n_4); + ha = dot(_e221, _e222); + let _e224 = b_1; + let _e225 = (*n_4); + hb = dot(_e224, _e225); + let _e227 = ha; + let _e228 = hb; + d_3 = (_e227 - _e228); + let _e230 = a_2; + let _e231 = b_1; + let _e232 = a_2; + let _e234 = d_3; + if (abs(_e234) > 0.000000000001f) { + let _e237 = ha; + let _e238 = d_3; + local_14 = (_e237 / _e238); } else { - local_12 = 0f; - } - let _e231 = local_12; - q = (_e221 + ((_e222 - _e223) * _e231)); - let _e234 = ha; - aAbove = (_e234 > 0f); - let _e236 = hb; - bAbove = (_e236 > 0f); - let _e238 = aAbove; - let _e239 = bAbove; - if (_e238 || _e239) { - let _e241 = aAbove; - let _e242 = a_2; - let _e243 = q; - let _e246 = bAbove; - let _e247 = b_1; - let _e248 = q; - param_67 = select(_e243, _e242, vec3(_e241)); - param_68 = select(_e248, _e247, vec3(_e246)); - let _e251 = (*n_4); - param_69 = _e251; - let _e252 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); - local_13 = _e252; + local_14 = 0f; + } + let _e240 = local_14; + q = (_e230 + ((_e231 - _e232) * _e240)); + let _e243 = ha; + aAbove = (_e243 > 0f); + let _e245 = hb; + bAbove = (_e245 > 0f); + let _e247 = aAbove; + let _e248 = bAbove; + if (_e247 || _e248) { + let _e250 = aAbove; + let _e251 = a_2; + let _e252 = q; + let _e255 = bAbove; + let _e256 = b_1; + let _e257 = q; + param_73 = select(_e252, _e251, vec3(_e250)); + param_74 = select(_e257, _e256, vec3(_e255)); + let _e260 = (*n_4); + param_75 = _e260; + let _e261 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_73), (¶m_74), (¶m_75)); + local_15 = _e261; } else { - local_13 = 0f; - } - let _e253 = local_13; - let _e254 = total; - total = (_e254 + _e253); - let _e256 = aAbove; - let _e257 = bAbove; - let _e260 = q; - let _e261 = exit; - exit = select(_e261, _e260, vec3((_e256 && !(_e257)))); - let _e264 = aAbove; + local_15 = 0f; + } + let _e262 = local_15; + let _e263 = total; + total = (_e263 + _e262); + let _e265 = aAbove; let _e266 = bAbove; - let _e268 = q; - let _e269 = entry; - entry = select(_e269, _e268, vec3((!(_e264) && _e266))); + let _e269 = q; + let _e270 = exit; + exit = select(_e270, _e269, vec3((_e265 && !(_e266)))); + let _e273 = aAbove; + let _e275 = bAbove; + let _e277 = q; + let _e278 = entry; + entry = select(_e278, _e277, vec3((!(_e273) && _e275))); continue; } else { break; } continuing { - let _e272 = i_7; - i_7 = (_e272 + 1i); + let _e281 = i_7; + i_7 = (_e281 + 1i); } } - let _e274 = exit; - param_70 = _e274; - let _e275 = entry; - param_71 = _e275; - let _e276 = (*n_4); - param_72 = _e276; - let _e277 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); - let _e278 = total; - total = (_e278 + _e277); - let _e280 = total; - return max((-(_e280) * 0.5f), 0f); + let _e283 = exit; + param_76 = _e283; + let _e284 = entry; + param_77 = _e284; + let _e285 = (*n_4); + param_78 = _e285; + let _e286 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_76), (¶m_77), (¶m_78)); + let _e287 = total; + total = (_e287 + _e286); + let _e289 = total; + return max((-(_e289) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_14: f32; - var local_15: f32; var local_16: f32; + var local_17: f32; + var local_18: f32; - let _e183 = (*slot_1); - let _e184 = (*slot_1); - lane = (_e183 - ((_e184 / 4i) * 4i)); - let _e188 = lane; - if (_e188 == 0i) { - let _e191 = (*four)[0u]; - local_14 = _e191; + let _e192 = (*slot_1); + let _e193 = (*slot_1); + lane = (_e192 - ((_e193 / 4i) * 4i)); + let _e197 = lane; + if (_e197 == 0i) { + let _e200 = (*four)[0u]; + local_16 = _e200; } else { - let _e192 = lane; - if (_e192 == 1i) { - let _e195 = (*four)[1u]; - local_15 = _e195; + let _e201 = lane; + if (_e201 == 1i) { + let _e204 = (*four)[1u]; + local_17 = _e204; } else { - let _e196 = lane; - if (_e196 == 2i) { - let _e199 = (*four)[2u]; - local_16 = _e199; + let _e205 = lane; + if (_e205 == 2i) { + let _e208 = (*four)[2u]; + local_18 = _e208; } else { - let _e201 = (*four)[3u]; - local_16 = _e201; + let _e210 = (*four)[3u]; + local_18 = _e210; } - let _e202 = local_16; - local_15 = _e202; + let _e211 = local_18; + local_17 = _e211; } - let _e203 = local_15; - local_14 = _e203; + let _e212 = local_17; + local_16 = _e212; } - let _e204 = local_14; - return _e204; + let _e213 = local_16; + return _e213; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { - var param_73: vec4; - var param_74: i32; - - let _e180 = (*slot_2); - let _e184 = light_list_info.scales[(_e180 / 4i)]; - param_73 = _e184; - let _e185 = (*slot_2); - param_74 = _e185; - let _e186 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); - return _e186; + var param_79: vec4; + var param_80: i32; + + let _e189 = (*slot_2); + let _e193 = light_list_info.scales[(_e189 / 4i)]; + param_79 = _e193; + let _e194 = (*slot_2); + param_80 = _e194; + let _e195 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); + return _e195; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_3: vec4; var b_2: vec4; - let _e182 = light_list_info.slot_rows[0i]; - let _e183 = (*row); - a_3 = abs((_e182 - vec4(_e183))); - let _e189 = light_list_info.slot_rows[1i]; - let _e190 = (*row); - b_2 = abs((_e189 - vec4(_e190))); - let _e195 = a_3[0u]; - let _e197 = a_3[1u]; - let _e200 = a_3[2u]; - let _e202 = a_3[3u]; - let _e206 = b_2[0u]; - let _e208 = b_2[1u]; - let _e211 = b_2[2u]; - let _e213 = b_2[3u]; - return (min(min(min(_e195, _e197), min(_e200, _e202)), min(min(_e206, _e208), min(_e211, _e213))) < 0.5f); + let _e191 = light_list_info.slot_rows[0i]; + let _e192 = (*row); + a_3 = abs((_e191 - vec4(_e192))); + let _e198 = light_list_info.slot_rows[1i]; + let _e199 = (*row); + b_2 = abs((_e198 - vec4(_e199))); + let _e204 = a_3[0u]; + let _e206 = a_3[1u]; + let _e209 = a_3[2u]; + let _e211 = a_3[3u]; + let _e215 = b_2[0u]; + let _e217 = b_2[1u]; + let _e220 = b_2[2u]; + let _e222 = b_2[3u]; + return (min(min(min(_e204, _e206), min(_e209, _e211)), min(min(_e215, _e217), min(_e220, _e222))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { - var param_75: vec4; - var param_76: i32; - - let _e180 = (*slot_3); - let _e184 = light_list_info.indices[(_e180 / 4i)]; - param_75 = _e184; - let _e185 = (*slot_3); - param_76 = _e185; - let _e186 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); - return _e186; + var param_81: vec4; + var param_82: i32; + + let _e189 = (*slot_3); + let _e193 = light_list_info.indices[(_e189 / 4i)]; + param_81 = _e193; + let _e194 = (*slot_3); + param_82 = _e194; + let _e195 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_81), (¶m_82)); + return _e195; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e179 = (*texel_2); - let _e183 = light_list_info.list[1u]; - let _e185 = (*row_1); - let _e189 = light_list_info.list[2u]; - let _e192 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e179 + 0.5f) * _e183), ((_e185 + 0.5f) * _e189)), 0f); - return _e192; + let _e188 = (*texel_2); + let _e192 = light_list_info.list[1u]; + let _e194 = (*row_1); + let _e198 = light_list_info.list[2u]; + let _e201 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e188 + 0.5f) * _e192), ((_e194 + 0.5f) * _e198)), 0f); + return _e201; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -23396,43 +24304,43 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var within: f32; var texel_3: f32; var four_1: vec4; - var param_77: f32; - var param_78: f32; - var param_79: vec4; - var param_80: i32; + var param_83: f32; + var param_84: f32; + var param_85: vec4; + var param_86: i32; - let _e187 = g_cluster_offset; - let _e188 = (*slot_4); - entry_1 = (_e187 + f32(_e188)); - let _e191 = entry_1; - row_2 = floor((_e191 / 16f)); - let _e194 = entry_1; - let _e195 = row_2; - within = (_e194 - (_e195 * 16f)); - let _e198 = within; - texel_3 = floor((_e198 / 4f)); - let _e203 = light_list_info.cluster_depth[3u]; + let _e196 = g_cluster_offset; + let _e197 = (*slot_4); + entry_1 = (_e196 + f32(_e197)); + let _e200 = entry_1; + row_2 = floor((_e200 / 16f)); + let _e203 = entry_1; let _e204 = row_2; - let _e206 = texel_3; - param_77 = _e206; - param_78 = (_e203 + _e204); - let _e207 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); - four_1 = _e207; - let _e208 = within; - let _e209 = texel_3; - let _e214 = four_1; - param_79 = _e214; - param_80 = i32(((_e208 - (_e209 * 4f)) + 0.5f)); - let _e215 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); - return _e215; + within = (_e203 - (_e204 * 16f)); + let _e207 = within; + texel_3 = floor((_e207 / 4f)); + let _e212 = light_list_info.cluster_depth[3u]; + let _e213 = row_2; + let _e215 = texel_3; + param_83 = _e215; + param_84 = (_e212 + _e213); + let _e216 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_83), (¶m_84)); + four_1 = _e216; + let _e217 = within; + let _e218 = texel_3; + let _e223 = four_1; + param_85 = _e223; + param_86 = i32(((_e217 - (_e218 * 4f)) + 0.5f)); + let _e224 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_85), (¶m_86)); + return _e224; } fn Clustered_u0028_() -> bool { - let _e179 = light_list_info.cluster_grid[3u]; - return (_e179 > 0.5f); + let _e188 = light_list_info.cluster_grid[3u]; + return (_e188 > 0.5f); } -fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_3: ptr) -> LightSample { +fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_4: ptr) -> LightSample { var light_3: LightSample; var position: vec4; var color_1: vec4; @@ -23441,14 +24349,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_17: f32; - var param_81: i32; - var param_82: i32; + var local_19: f32; + var param_87: i32; + var param_88: i32; var v_1: f32; var u_1: f32; - var local_18: f32; - var param_83: f32; - var param_84: i32; + var local_20: f32; + var param_89: f32; + var param_90: i32; var type_39: f32; var halfWidth_1: vec3; var halfHeight_1: vec3; @@ -23456,293 +24364,293 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_19: f32; - var param_85: array, 4>; - var param_86: vec3; + var local_21: f32; + var param_91: array, 4>; + var param_92: vec3; var area: f32; var radiance: f32; - var local_20: f32; + var local_22: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; var mirror_1: vec3; var representative: vec3; - var param_87: vec3; - var param_88: vec3; - var param_89: vec3; - var param_90: vec3; - var param_91: vec3; + var param_93: vec3; + var param_94: vec3; + var param_95: vec3; + var param_96: vec3; + var param_97: vec3; var toPoint: vec3; var pointDistance: f32; - var local_21: vec3; + var local_23: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; var distance_3: f32; - var param_92: f32; - var param_93: f32; + var param_98: f32; + var param_99: f32; var cosAngle: f32; light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e230 = (*index_2); - if (_e230 < 8i) { - let _e232 = (*index_2); - let _e235 = frag_info.light_position[_e232]; - position = _e235; - let _e236 = (*index_2); - let _e239 = frag_info.light_color[_e236]; - color_1 = _e239; - let _e240 = (*index_2); - let _e243 = frag_info.light_direction[_e240]; - direction = _e243; - let _e244 = (*index_2); - let _e247 = frag_info.light_cone[_e244]; - cone = _e247; + let _e239 = (*index_2); + if (_e239 < 8i) { + let _e241 = (*index_2); + let _e244 = frag_info.light_position[_e241]; + position = _e244; + let _e245 = (*index_2); + let _e248 = frag_info.light_color[_e245]; + color_1 = _e248; + let _e249 = (*index_2); + let _e252 = frag_info.light_direction[_e249]; + direction = _e252; + let _e253 = (*index_2); + let _e256 = frag_info.light_cone[_e253]; + cone = _e256; } else { - let _e248 = (*index_2); - slot_5 = (_e248 - 8i); - let _e250 = Clustered_u0028_(); - clustered = _e250; - let _e251 = clustered; - if _e251 { - let _e252 = slot_5; - param_81 = _e252; - let _e253 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_17 = _e253; + let _e257 = (*index_2); + slot_5 = (_e257 - 8i); + let _e259 = Clustered_u0028_(); + clustered = _e259; + let _e260 = clustered; + if _e260 { + let _e261 = slot_5; + param_87 = _e261; + let _e262 = ClusterRow_u0028_i1_u003b((¶m_87)); + local_19 = _e262; } else { - let _e254 = slot_5; - param_82 = _e254; - let _e255 = LightListRow_u0028_i1_u003b((¶m_82)); - local_17 = _e255; - } - let _e256 = local_17; - listRow = _e256; - let _e257 = listRow; - let _e261 = light_list_info.list[2u]; - v_1 = ((_e257 + 0.5f) * _e261); - let _e265 = light_list_info.list[1u]; - u_1 = _e265; - let _e266 = u_1; - let _e268 = v_1; - let _e270 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e266), _e268), 0f); - position = _e270; - let _e271 = u_1; - let _e273 = v_1; - let _e275 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e271), _e273), 0f); - color_1 = _e275; - let _e276 = u_1; - let _e278 = v_1; - let _e280 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e276), _e278), 0f); - direction = _e280; - let _e281 = u_1; - let _e283 = v_1; - let _e285 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e281), _e283), 0f); - cone = _e285; - let _e286 = clustered; - if _e286 { - let _e287 = listRow; - param_83 = _e287; - let _e288 = InSlots_u0028_f1_u003b((¶m_83)); - local_18 = select(1f, 0f, _e288); + let _e263 = slot_5; + param_88 = _e263; + let _e264 = LightListRow_u0028_i1_u003b((¶m_88)); + local_19 = _e264; + } + let _e265 = local_19; + listRow = _e265; + let _e266 = listRow; + let _e270 = light_list_info.list[2u]; + v_1 = ((_e266 + 0.5f) * _e270); + let _e274 = light_list_info.list[1u]; + u_1 = _e274; + let _e275 = u_1; + let _e277 = v_1; + let _e279 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e275), _e277), 0f); + position = _e279; + let _e280 = u_1; + let _e282 = v_1; + let _e284 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e280), _e282), 0f); + color_1 = _e284; + let _e285 = u_1; + let _e287 = v_1; + let _e289 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e285), _e287), 0f); + direction = _e289; + let _e290 = u_1; + let _e292 = v_1; + let _e294 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e290), _e292), 0f); + cone = _e294; + let _e295 = clustered; + if _e295 { + let _e296 = listRow; + param_89 = _e296; + let _e297 = InSlots_u0028_f1_u003b((¶m_89)); + local_20 = select(1f, 0f, _e297); } else { - let _e290 = slot_5; - param_84 = _e290; - let _e291 = LightListScale_u0028_i1_u003b((¶m_84)); - local_18 = _e291; - } - let _e292 = local_18; - let _e294 = color_1[3u]; - color_1[3u] = (_e294 * _e292); - } - let _e298 = position[3u]; - type_39 = _e298; - let _e299 = type_39; - if (_e299 > 2.5f) { - let _e301 = direction; - halfWidth_1 = _e301.xyz; - let _e303 = cone; - halfHeight_1 = _e303.xyz; - let _e305 = position; - let _e307 = v_world_position_1; - toCentre = (_e305.xyz - _e307); - let _e309 = toCentre; - let _e310 = halfWidth_1; - let _e312 = halfHeight_1; - corners_1[0i] = ((_e309 - _e310) - _e312); - let _e315 = toCentre; - let _e316 = halfWidth_1; - let _e318 = halfHeight_1; - corners_1[1i] = ((_e315 + _e316) - _e318); - let _e321 = toCentre; - let _e322 = halfWidth_1; - let _e324 = halfHeight_1; - corners_1[2i] = ((_e321 + _e322) + _e324); - let _e327 = toCentre; - let _e328 = halfWidth_1; - let _e330 = halfHeight_1; - corners_1[3i] = ((_e327 - _e328) + _e330); - let _e333 = toCentre; - let _e334 = halfWidth_1; - let _e335 = halfHeight_1; - behind = (dot(_e333, cross(_e334, _e335)) >= 0f); - let _e339 = behind; - if _e339 { - local_19 = 0f; + let _e299 = slot_5; + param_90 = _e299; + let _e300 = LightListScale_u0028_i1_u003b((¶m_90)); + local_20 = _e300; + } + let _e301 = local_20; + let _e303 = color_1[3u]; + color_1[3u] = (_e303 * _e301); + } + let _e307 = position[3u]; + type_39 = _e307; + let _e308 = type_39; + if (_e308 > 2.5f) { + let _e310 = direction; + halfWidth_1 = _e310.xyz; + let _e312 = cone; + halfHeight_1 = _e312.xyz; + let _e314 = position; + let _e316 = v_world_position_1; + toCentre = (_e314.xyz - _e316); + let _e318 = toCentre; + let _e319 = halfWidth_1; + let _e321 = halfHeight_1; + corners_1[0i] = ((_e318 - _e319) - _e321); + let _e324 = toCentre; + let _e325 = halfWidth_1; + let _e327 = halfHeight_1; + corners_1[1i] = ((_e324 + _e325) - _e327); + let _e330 = toCentre; + let _e331 = halfWidth_1; + let _e333 = halfHeight_1; + corners_1[2i] = ((_e330 + _e331) + _e333); + let _e336 = toCentre; + let _e337 = halfWidth_1; + let _e339 = halfHeight_1; + corners_1[3i] = ((_e336 - _e337) + _e339); + let _e342 = toCentre; + let _e343 = halfWidth_1; + let _e344 = halfHeight_1; + behind = (dot(_e342, cross(_e343, _e344)) >= 0f); + let _e348 = behind; + if _e348 { + local_21 = 0f; } else { - let _e340 = corners_1; - param_85 = _e340; - let _e342 = (*s_3).n; - param_86 = _e342; - let _e343 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_19 = _e343; - } - let _e344 = local_19; - formFactor = _e344; - let _e345 = halfWidth_1; - let _e346 = halfHeight_1; - area = (length(cross(_e345, _e346)) * 4f); - let _e350 = area; - if (_e350 > 0.000000001f) { - let _e352 = area; - local_20 = (1f / _e352); + let _e349 = corners_1; + param_91 = _e349; + let _e351 = (*s_4).n; + param_92 = _e351; + let _e352 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_91), (¶m_92)); + local_21 = _e352; + } + let _e353 = local_21; + formFactor = _e353; + let _e354 = halfWidth_1; + let _e355 = halfHeight_1; + area = (length(cross(_e354, _e355)) * 4f); + let _e359 = area; + if (_e359 > 0.000000001f) { + let _e361 = area; + local_22 = (1f / _e361); } else { - local_20 = 0f; - } - let _e354 = local_20; - radiance = _e354; - let _e355 = toCentre; - distance_2 = length(_e355); - let _e358 = direction[3u]; - if (_e358 > 0f) { - let _e360 = distance_2; - let _e362 = direction[3u]; - ratio_1 = (_e360 / _e362); - let _e364 = ratio_1; - let _e365 = ratio_1; - let _e367 = ratio_1; - let _e369 = ratio_1; - window_1 = clamp((1f - (((_e364 * _e365) * _e367) * _e369)), 0f, 1f); - let _e373 = window_1; - let _e374 = window_1; - let _e376 = radiance; - radiance = (_e376 * (_e373 * _e374)); - } - let _e379 = (*s_3).v; - let _e382 = (*s_3).n; - mirror_1 = reflect(-(_e379), _e382); - let _e384 = position; - param_87 = _e384.xyz; - let _e386 = halfWidth_1; - param_88 = _e386; - let _e387 = halfHeight_1; - param_89 = _e387; - let _e388 = v_world_position_1; - param_90 = _e388; - let _e389 = mirror_1; - param_91 = _e389; - let _e390 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); - representative = _e390; - let _e391 = representative; - let _e392 = v_world_position_1; - toPoint = (_e391 - _e392); - let _e394 = toPoint; - pointDistance = length(_e394); - let _e396 = pointDistance; - if (_e396 > 0.000001f) { - let _e398 = toPoint; - let _e399 = pointDistance; - local_21 = (_e398 / vec3(_e399)); + local_22 = 0f; + } + let _e363 = local_22; + radiance = _e363; + let _e364 = toCentre; + distance_2 = length(_e364); + let _e367 = direction[3u]; + if (_e367 > 0f) { + let _e369 = distance_2; + let _e371 = direction[3u]; + ratio_1 = (_e369 / _e371); + let _e373 = ratio_1; + let _e374 = ratio_1; + let _e376 = ratio_1; + let _e378 = ratio_1; + window_1 = clamp((1f - (((_e373 * _e374) * _e376) * _e378)), 0f, 1f); + let _e382 = window_1; + let _e383 = window_1; + let _e385 = radiance; + radiance = (_e385 * (_e382 * _e383)); + } + let _e388 = (*s_4).v; + let _e391 = (*s_4).n; + mirror_1 = reflect(-(_e388), _e391); + let _e393 = position; + param_93 = _e393.xyz; + let _e395 = halfWidth_1; + param_94 = _e395; + let _e396 = halfHeight_1; + param_95 = _e396; + let _e397 = v_world_position_1; + param_96 = _e397; + let _e398 = mirror_1; + param_97 = _e398; + let _e399 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_93), (¶m_94), (¶m_95), (¶m_96), (¶m_97)); + representative = _e399; + let _e400 = representative; + let _e401 = v_world_position_1; + toPoint = (_e400 - _e401); + let _e403 = toPoint; + pointDistance = length(_e403); + let _e405 = pointDistance; + if (_e405 > 0.000001f) { + let _e407 = toPoint; + let _e408 = pointDistance; + local_23 = (_e407 / vec3(_e408)); } else { - let _e403 = (*s_3).n; - local_21 = _e403; - } - let _e404 = local_21; - light_3.l = _e404; - let _e407 = light_3.l; - let _e409 = (*s_3).v; - light_3.h = normalize((_e407 + _e409)); - let _e413 = formFactor; - light_3.n_dot_l = _e413; - let _e416 = (*s_3).n; - let _e418 = light_3.h; - light_3.n_dot_h = max(dot(_e416, _e418), 0f); - let _e423 = (*s_3).v; - let _e425 = light_3.h; - light_3.v_dot_h = max(dot(_e423, _e425), 0f); - let _e429 = color_1; - let _e432 = color_1[3u]; - let _e434 = radiance; - light_3.radiance = ((_e429.xyz * _e432) * _e434); - let _e437 = light_3; - return _e437; - } - let _e438 = direction; - aim = normalize(_e438.xyz); + let _e412 = (*s_4).n; + local_23 = _e412; + } + let _e413 = local_23; + light_3.l = _e413; + let _e416 = light_3.l; + let _e418 = (*s_4).v; + light_3.h = normalize((_e416 + _e418)); + let _e422 = formFactor; + light_3.n_dot_l = _e422; + let _e425 = (*s_4).n; + let _e427 = light_3.h; + light_3.n_dot_h = max(dot(_e425, _e427), 0f); + let _e432 = (*s_4).v; + let _e434 = light_3.h; + light_3.v_dot_h = max(dot(_e432, _e434), 0f); + let _e438 = color_1; + let _e441 = color_1[3u]; + let _e443 = radiance; + light_3.radiance = ((_e438.xyz * _e441) * _e443); + let _e446 = light_3; + return _e446; + } + let _e447 = direction; + aim = normalize(_e447.xyz); attenuation_1 = 1f; - let _e441 = type_39; - if (_e441 < 0.5f) { - let _e443 = aim; - light_3.l = -(_e443); + let _e450 = type_39; + if (_e450 < 0.5f) { + let _e452 = aim; + light_3.l = -(_e452); } else { - let _e446 = position; - let _e448 = v_world_position_1; - toLight_1 = (_e446.xyz - _e448); - let _e450 = toLight_1; - distance_3 = length(_e450); - let _e452 = distance_3; - if (_e452 < 0.000001f) { - let _e455 = (*s_3).n; - light_3.l = _e455; - let _e458 = (*s_3).n; - light_3.h = _e458; + let _e455 = position; + let _e457 = v_world_position_1; + toLight_1 = (_e455.xyz - _e457); + let _e459 = toLight_1; + distance_3 = length(_e459); + let _e461 = distance_3; + if (_e461 < 0.000001f) { + let _e464 = (*s_4).n; + light_3.l = _e464; + let _e467 = (*s_4).n; + light_3.h = _e467; light_3.radiance = vec3(0f, 0f, 0f); light_3.n_dot_l = 0f; light_3.n_dot_h = 0f; light_3.v_dot_h = 0f; - let _e464 = light_3; - return _e464; - } - let _e465 = toLight_1; - let _e466 = distance_3; - light_3.l = (_e465 / vec3(_e466)); - let _e470 = distance_3; - param_92 = _e470; - let _e472 = direction[3u]; - param_93 = _e472; - let _e473 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); - attenuation_1 = _e473; - let _e474 = type_39; - if (_e474 > 1.5f) { - let _e476 = aim; - let _e478 = light_3.l; - cosAngle = dot(_e476, -(_e478)); - let _e481 = cosAngle; - let _e483 = cone[1u]; - let _e486 = cone[0u]; - let _e488 = cone[1u]; - let _e492 = attenuation_1; - attenuation_1 = (_e492 * clamp(((_e481 - _e483) / (_e486 - _e488)), 0f, 1f)); - } - } - let _e495 = light_3.l; - let _e497 = (*s_3).v; - light_3.h = normalize((_e495 + _e497)); - let _e502 = (*s_3).n; + let _e473 = light_3; + return _e473; + } + let _e474 = toLight_1; + let _e475 = distance_3; + light_3.l = (_e474 / vec3(_e475)); + let _e479 = distance_3; + param_98 = _e479; + let _e481 = direction[3u]; + param_99 = _e481; + let _e482 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_98), (¶m_99)); + attenuation_1 = _e482; + let _e483 = type_39; + if (_e483 > 1.5f) { + let _e485 = aim; + let _e487 = light_3.l; + cosAngle = dot(_e485, -(_e487)); + let _e490 = cosAngle; + let _e492 = cone[1u]; + let _e495 = cone[0u]; + let _e497 = cone[1u]; + let _e501 = attenuation_1; + attenuation_1 = (_e501 * clamp(((_e490 - _e492) / (_e495 - _e497)), 0f, 1f)); + } + } let _e504 = light_3.l; - light_3.n_dot_l = max(dot(_e502, _e504), 0f); - let _e509 = (*s_3).n; - let _e511 = light_3.h; - light_3.n_dot_h = max(dot(_e509, _e511), 0f); - let _e516 = (*s_3).v; - let _e518 = light_3.h; - light_3.v_dot_h = max(dot(_e516, _e518), 0f); - let _e522 = color_1; - let _e525 = color_1[3u]; - let _e527 = attenuation_1; - light_3.radiance = ((_e522.xyz * _e525) * _e527); - let _e530 = light_3; - return _e530; + let _e506 = (*s_4).v; + light_3.h = normalize((_e504 + _e506)); + let _e511 = (*s_4).n; + let _e513 = light_3.l; + light_3.n_dot_l = max(dot(_e511, _e513), 0f); + let _e518 = (*s_4).n; + let _e520 = light_3.h; + light_3.n_dot_h = max(dot(_e518, _e520), 0f); + let _e525 = (*s_4).v; + let _e527 = light_3.h; + light_3.v_dot_h = max(dot(_e525, _e527), 0f); + let _e531 = color_1; + let _e534 = color_1[3u]; + let _e536 = attenuation_1; + light_3.radiance = ((_e531.xyz * _e534) * _e536); + let _e539 = light_3; + return _e539; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -23753,312 +24661,305 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_22: f32; + var local_24: f32; var cell: f32; var row_3: f32; var header: vec4; - var param_94: f32; - var param_95: f32; + var param_100: f32; + var param_101: f32; - let _e192 = light_list_info.cluster_view_projection; - let _e193 = (*world_3); - clip_1 = (_e192 * vec4(_e193.x, _e193.y, _e193.z, 1f)); - let _e199 = clip_1; - let _e202 = clip_1[3u]; - ndc_1 = (_e199.xy / vec2(max(_e202, 0.000001f))); - let _e207 = light_list_info.cluster_grid; - grid = _e207.xyz; - let _e211 = light_list_info.cluster_depth[0u]; - near_1 = _e211; - let _e213 = ndc_1[0u]; - let _e217 = grid[0u]; - let _e221 = grid[0u]; - tx = clamp(floor((((_e213 * 0.5f) + 0.5f) * _e217)), 0f, (_e221 - 1f)); - let _e225 = ndc_1[1u]; - let _e229 = grid[1u]; - let _e233 = grid[1u]; - ty = clamp(floor((((_e225 * 0.5f) + 0.5f) * _e229)), 0f, (_e233 - 1f)); - let _e237 = clip_1[3u]; - let _e238 = near_1; - if (_e237 <= _e238) { - local_22 = 0f; + let _e201 = light_list_info.cluster_view_projection; + let _e202 = (*world_3); + clip_1 = (_e201 * vec4(_e202.x, _e202.y, _e202.z, 1f)); + let _e208 = clip_1; + let _e211 = clip_1[3u]; + ndc_1 = (_e208.xy / vec2(max(_e211, 0.000001f))); + let _e216 = light_list_info.cluster_grid; + grid = _e216.xyz; + let _e220 = light_list_info.cluster_depth[0u]; + near_1 = _e220; + let _e222 = ndc_1[0u]; + let _e226 = grid[0u]; + let _e230 = grid[0u]; + tx = clamp(floor((((_e222 * 0.5f) + 0.5f) * _e226)), 0f, (_e230 - 1f)); + let _e234 = ndc_1[1u]; + let _e238 = grid[1u]; + let _e242 = grid[1u]; + ty = clamp(floor((((_e234 * 0.5f) + 0.5f) * _e238)), 0f, (_e242 - 1f)); + let _e246 = clip_1[3u]; + let _e247 = near_1; + if (_e246 <= _e247) { + local_24 = 0f; } else { - let _e241 = clip_1[3u]; - let _e242 = near_1; - let _e247 = light_list_info.cluster_depth[1u]; - let _e251 = grid[2u]; - local_22 = clamp(floor((log((_e241 / _e242)) * _e247)), 0f, (_e251 - 1f)); - } - let _e254 = local_22; - tz = _e254; - let _e255 = tx; - let _e256 = ty; - let _e258 = grid[0u]; - let _e261 = tz; - let _e263 = grid[0u]; - let _e266 = grid[1u]; - cell = ((_e255 + (_e256 * _e258)) + ((_e261 * _e263) * _e266)); - let _e269 = cell; - row_3 = floor((_e269 / 4f)); - let _e272 = cell; - let _e273 = row_3; - let _e278 = light_list_info.cluster_depth[2u]; - let _e279 = row_3; - param_94 = (_e272 - (_e273 * 4f)); - param_95 = (_e278 + _e279); - let _e281 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); - header = _e281; - let _e283 = header[0u]; - g_cluster_offset = _e283; - let _e285 = header[1u]; - g_cluster_count = _e285; + let _e250 = clip_1[3u]; + let _e251 = near_1; + let _e256 = light_list_info.cluster_depth[1u]; + let _e260 = grid[2u]; + local_24 = clamp(floor((log((_e250 / _e251)) * _e256)), 0f, (_e260 - 1f)); + } + let _e263 = local_24; + tz = _e263; + let _e264 = tx; + let _e265 = ty; + let _e267 = grid[0u]; + let _e270 = tz; + let _e272 = grid[0u]; + let _e275 = grid[1u]; + cell = ((_e264 + (_e265 * _e267)) + ((_e270 * _e272) * _e275)); + let _e278 = cell; + row_3 = floor((_e278 / 4f)); + let _e281 = cell; + let _e282 = row_3; + let _e287 = light_list_info.cluster_depth[2u]; + let _e288 = row_3; + param_100 = (_e281 - (_e282 * 4f)); + param_101 = (_e287 + _e288); + let _e290 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_100), (¶m_101)); + header = _e290; + let _e292 = header[0u]; + g_cluster_offset = _e292; + let _e294 = header[1u]; + g_cluster_count = _e294; return; } fn LightCount_u0028_() -> i32 { var tail: f32; - var param_96: vec3; + var param_102: vec3; - let _e181 = light_list_info.list[0u]; - tail = _e181; - let _e182 = Clustered_u0028_(); - if _e182 { - let _e183 = v_world_position_1; - param_96 = _e183; - FindCluster_u0028_vf3_u003b((¶m_96)); - let _e184 = g_cluster_count; - tail = _e184; + let _e190 = light_list_info.list[0u]; + tail = _e190; + let _e191 = Clustered_u0028_(); + if _e191 { + let _e192 = v_world_position_1; + param_102 = _e192; + FindCluster_u0028_vf3_u003b((¶m_102)); + let _e193 = g_cluster_count; + tail = _e193; } - let _e187 = frag_info.frame_params[1u]; - let _e191 = tail; - return (clamp(i32((_e187 + 0.5f)), 0i, 8i) + clamp(i32((_e191 + 0.5f)), 0i, 24i)); + let _e196 = frag_info.frame_params[1u]; + let _e200 = tail; + return (clamp(i32((_e196 + 0.5f)), 0i, 8i) + clamp(i32((_e200 + 0.5f)), 0i, 24i)); } -fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_4: ptr) -> vec3 { +fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_5: ptr) -> vec3 { var total_1: vec3; var count_1: i32; var i_8: i32; var light_4: LightSample; - var param_97: i32; - var param_98: Surface; + var param_103: i32; + var param_104: Surface; var visibility: f32; - var param_99: i32; - var local_23: f32; - var param_100: Surface; - var param_101: LightSample; - var param_102: i32; - var param_103: vec3; - var param_104: vec3; var param_105: i32; + var local_25: f32; var param_106: Surface; var param_107: LightSample; + var param_108: i32; + var param_109: vec3; + var param_110: vec3; + var param_111: i32; + var param_112: Surface; + var param_113: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e195 = LightCount_u0028_(); - count_1 = _e195; + let _e204 = LightCount_u0028_(); + count_1 = _e204; i_8 = 0i; loop { - let _e196 = i_8; - if (_e196 < 32i) { - let _e198 = i_8; - let _e199 = count_1; - if (_e198 >= _e199) { + let _e205 = i_8; + if (_e205 < 32i) { + let _e207 = i_8; + let _e208 = count_1; + if (_e207 >= _e208) { break; } - let _e201 = i_8; - param_97 = _e201; - let _e202 = (*s_4); - param_98 = _e202; - let _e203 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); - light_4 = _e203; - let _e205 = light_4.n_dot_l; - if (_e205 <= 0f) { + let _e210 = i_8; + param_103 = _e210; + let _e211 = (*s_5); + param_104 = _e211; + let _e212 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_103), (¶m_104)); + light_4 = _e212; + let _e214 = light_4.n_dot_l; + if (_e214 <= 0f) { continue; } - let _e207 = i_8; - param_99 = _e207; - let _e208 = LightHasShadow_u0028_i1_u003b((¶m_99)); - if _e208 { - let _e209 = (*s_4); - param_100 = _e209; - let _e210 = light_4; - param_101 = _e210; - let _e211 = i_8; - param_102 = _e211; - let _e212 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); - let _e213 = v_world_position_1; - param_103 = _e213; - let _e215 = (*s_4).n; - param_104 = _e215; - let _e216 = i_8; - param_105 = _e216; - let _e217 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_23 = (_e212 * _e217); + let _e216 = i_8; + param_105 = _e216; + let _e217 = LightHasShadow_u0028_i1_u003b((¶m_105)); + if _e217 { + let _e218 = (*s_5); + param_106 = _e218; + let _e219 = light_4; + param_107 = _e219; + let _e220 = i_8; + param_108 = _e220; + let _e221 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_106), (¶m_107), (¶m_108)); + let _e222 = v_world_position_1; + param_109 = _e222; + let _e224 = (*s_5).n; + param_110 = _e224; + let _e225 = i_8; + param_111 = _e225; + let _e226 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_109), (¶m_110), (¶m_111)); + local_25 = (_e221 * _e226); } else { - local_23 = 1f; + local_25 = 1f; } - let _e219 = local_23; - visibility = _e219; - let _e220 = visibility; - if (_e220 <= 0f) { + let _e228 = local_25; + visibility = _e228; + let _e229 = visibility; + if (_e229 <= 0f) { continue; } - let _e222 = (*s_4); - param_106 = _e222; - let _e223 = light_4; - param_107 = _e223; - let _e224 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); - let _e226 = light_4.radiance; - let _e229 = light_4.n_dot_l; - let _e231 = visibility; - let _e233 = total_1; - total_1 = (_e233 + (((_e224 * _e226) * _e229) * _e231)); + let _e231 = (*s_5); + param_112 = _e231; + let _e232 = light_4; + param_113 = _e232; + let _e233 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_112), (¶m_113)); + let _e235 = light_4.radiance; + let _e238 = light_4.n_dot_l; + let _e240 = visibility; + let _e242 = total_1; + total_1 = (_e242 + (((_e233 * _e235) * _e238) * _e240)); continue; } else { break; } continuing { - let _e235 = i_8; - i_8 = (_e235 + 1i); + let _e244 = i_8; + i_8 = (_e244 + 1i); } } - let _e237 = total_1; - return _e237; + let _e246 = total_1; + return _e246; } fn SampleLightmap_u0028_() -> vec3 { var texel_4: vec4; - let _e178 = v_lightmap_uv_1; - let _e179 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e178); - texel_4 = _e179; - let _e180 = texel_4; - let _e183 = texel_4[3u]; - return ((_e180.xyz * _e183) * 8f); + let _e187 = v_lightmap_uv_1; + let _e188 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e187); + texel_4 = _e188; + let _e189 = texel_4; + let _e192 = texel_4[3u]; + return ((_e189.xyz * _e192) * 8f); } fn MaterialLodBias_u0028_() -> f32 { - let _e179 = frag_info.target_origin[1u]; - return _e179; + let _e188 = frag_info.target_origin[1u]; + return _e188; } -fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_5: ptr) { +fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) { var orm: vec3; - let _e179 = v_texcoord_1; - let _e180 = MaterialLodBias_u0028_(); - let _e181 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e179, _e180); - orm = _e181.xyz; - let _e184 = (*s_5).metallic; - let _e186 = orm[2u]; - (*s_5).metallic = clamp((_e184 * _e186), 0f, 1f); - let _e191 = (*s_5).roughness; - let _e193 = orm[1u]; - (*s_5).roughness = clamp((_e191 * _e193), 0.02f, 1f); + let _e188 = v_texcoord_1; + let _e189 = MaterialLodBias_u0028_(); + let _e190 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e188, _e189); + orm = _e190.xyz; + let _e193 = (*s_6).metallic; + let _e195 = orm[2u]; + (*s_6).metallic = clamp((_e193 * _e195), 0f, 1f); + let _e200 = (*s_6).roughness; + let _e202 = orm[1u]; + (*s_6).roughness = clamp((_e200 * _e202), 0.02f, 1f); return; } -fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e178 = (*srgb); - let _e181 = (*srgb); - let _e186 = (*srgb); - return mix((_e178 / vec3(12.92f)), pow(((_e181 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e186)); -} - -fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) { +fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) { var emissive: vec3; - var param_108: vec3; + var param_114: vec3; - let _e180 = v_texcoord_1; - let _e181 = MaterialLodBias_u0028_(); - let _e182 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e180, _e181); - param_108 = _e182.xyz; - let _e184 = SrgbToLinear_u0028_vf3_u003b((¶m_108)); - emissive = _e184; - let _e185 = emissive; - let _e187 = frag_info.emissive; - let _e192 = frag_info.material2_[3u]; - (*s_6).emissive = ((_e185 * _e187.xyz) * _e192); + let _e189 = v_texcoord_1; + let _e190 = MaterialLodBias_u0028_(); + let _e191 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e189, _e190); + param_114 = _e191.xyz; + let _e193 = SrgbToLinear_u0028_vf3_u003b((¶m_114)); + emissive = _e193; + let _e194 = emissive; + let _e196 = frag_info.emissive; + let _e201 = frag_info.material2_[3u]; + (*s_7).emissive = ((_e194 * _e196.xyz) * _e201); return; } -fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) { +fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { var occlusion: f32; - let _e179 = v_texcoord_1; - let _e180 = MaterialLodBias_u0028_(); - let _e181 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e179, _e180); - occlusion = _e181.x; - let _e183 = occlusion; - let _e186 = frag_info.material2_[2u]; - (*s_7).occlusion = mix(1f, _e183, clamp(_e186, 0f, 1f)); + let _e188 = v_texcoord_1; + let _e189 = MaterialLodBias_u0028_(); + let _e190 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e188, _e189); + occlusion = _e190.x; + let _e192 = occlusion; + let _e195 = frag_info.material2_[2u]; + (*s_8).occlusion = mix(1f, _e192, clamp(_e195, 0f, 1f)); return; } -fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { +fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) { var sampledTexel: vec4; var t_2: vec3; var b_3: vec3; var sampled: vec3; - let _e182 = v_texcoord_1; - let _e183 = MaterialLodBias_u0028_(); - let _e184 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e182, _e183); - sampledTexel = _e184; - let _e185 = v_tangent_1; - t_2 = _e185.xyz; - let _e187 = t_2; - let _e189 = (*s_8).n; - let _e191 = (*s_8).n; - let _e192 = t_2; - t_2 = (_e187 - (_e189 * dot(_e191, _e192))); + let _e191 = v_texcoord_1; + let _e192 = MaterialLodBias_u0028_(); + let _e193 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e191, _e192); + sampledTexel = _e193; + let _e194 = v_tangent_1; + t_2 = _e194.xyz; let _e196 = t_2; - let _e197 = t_2; - if (dot(_e196, _e197) < 0.000000000001f) { + let _e198 = (*s_9).n; + let _e200 = (*s_9).n; + let _e201 = t_2; + t_2 = (_e196 - (_e198 * dot(_e200, _e201))); + let _e205 = t_2; + let _e206 = t_2; + if (dot(_e205, _e206) < 0.000000000001f) { return; } - let _e200 = t_2; - t_2 = normalize(_e200); - let _e203 = (*s_8).n; - let _e204 = t_2; - let _e207 = v_tangent_1[3u]; - b_3 = (cross(_e203, _e204) * _e207); - let _e209 = gl_FrontFacing_1; - if !(_e209) { - let _e211 = t_2; - t_2 = -(_e211); - } - let _e213 = sampledTexel; - sampled = ((_e213.xyz * 2f) - vec3(1f)); - let _e220 = frag_info.emissive[3u]; - if (_e220 > 0.5f) { - let _e222 = sampled; - let _e224 = sampled; - sampled[2u] = sqrt(max((1f - dot(_e222.xy, _e224.xy)), 0f)); - } - let _e233 = frag_info.material2_[1u]; - let _e234 = sampled; - let _e236 = (_e234.xy * _e233); - sampled[0u] = _e236.x; - sampled[1u] = _e236.y; - let _e241 = t_2; - let _e243 = sampled[0u]; - let _e245 = b_3; - let _e247 = sampled[1u]; - let _e251 = (*s_8).n; - let _e253 = sampled[2u]; - (*s_8).n = normalize((((_e241 * _e243) + (_e245 * _e247)) + (_e251 * _e253))); - let _e259 = (*s_8).n; - let _e261 = (*s_8).v; - (*s_8).n_dot_v = max(dot(_e259, _e261), 0.0001f); + let _e209 = t_2; + t_2 = normalize(_e209); + let _e212 = (*s_9).n; + let _e213 = t_2; + let _e216 = v_tangent_1[3u]; + b_3 = (cross(_e212, _e213) * _e216); + let _e218 = gl_FrontFacing_1; + if !(_e218) { + let _e220 = t_2; + t_2 = -(_e220); + } + let _e222 = sampledTexel; + sampled = ((_e222.xyz * 2f) - vec3(1f)); + let _e229 = frag_info.emissive[3u]; + if (_e229 > 0.5f) { + let _e231 = sampled; + let _e233 = sampled; + sampled[2u] = sqrt(max((1f - dot(_e231.xy, _e233.xy)), 0f)); + } + let _e242 = frag_info.material2_[1u]; + let _e243 = sampled; + let _e245 = (_e243.xy * _e242); + sampled[0u] = _e245.x; + sampled[1u] = _e245.y; + let _e250 = t_2; + let _e252 = sampled[0u]; + let _e254 = b_3; + let _e256 = sampled[1u]; + let _e260 = (*s_9).n; + let _e262 = sampled[2u]; + (*s_9).n = normalize((((_e250 * _e252) + (_e254 * _e256)) + (_e260 * _e262))); + let _e268 = (*s_9).n; + let _e270 = (*s_9).v; + (*s_9).n_dot_v = max(dot(_e268, _e270), 0.0001f); return; } fn AtlasTexel_u0028_vf2_u003b(texel_5: ptr>) -> vec4 { - let _e178 = (*texel_5); - let _e182 = irradiance_info.atlas; - let _e185 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e178 + vec2(0.5f)) * _e182.xy), 0f); - return _e185; + let _e187 = (*texel_5); + let _e191 = irradiance_info.atlas; + let _e194 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e187 + vec2(0.5f)) * _e191.xy), 0f); + return _e194; } fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr>, interior: ptr, uv_5: ptr>) -> vec4 { @@ -24066,50 +24967,50 @@ fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr; var f: vec2; var a_4: vec4; - var param_109: vec2; + var param_115: vec2; var b_4: vec4; - var param_110: vec2; - var c: vec4; - var param_111: vec2; + var param_116: vec2; + var c_1: vec4; + var param_117: vec2; var d_4: vec4; - var param_112: vec2; + var param_118: vec2; - let _e191 = (*uv_5); - let _e192 = (*interior); - at_1 = ((vec2(1f) + (_e191 * _e192)) - vec2(0.5f)); - let _e198 = at_1; - low = floor(_e198); - let _e200 = at_1; - let _e201 = low; - f = (_e200 - _e201); - let _e203 = (*corner); - let _e204 = low; - param_109 = (_e203 + _e204); - let _e206 = AtlasTexel_u0028_vf2_u003b((¶m_109)); - a_4 = _e206; - let _e207 = (*corner); - let _e208 = low; - param_110 = ((_e207 + _e208) + vec2(1f, 0f)); - let _e211 = AtlasTexel_u0028_vf2_u003b((¶m_110)); - b_4 = _e211; + let _e200 = (*uv_5); + let _e201 = (*interior); + at_1 = ((vec2(1f) + (_e200 * _e201)) - vec2(0.5f)); + let _e207 = at_1; + low = floor(_e207); + let _e209 = at_1; + let _e210 = low; + f = (_e209 - _e210); let _e212 = (*corner); let _e213 = low; - param_111 = ((_e212 + _e213) + vec2(0f, 1f)); - let _e216 = AtlasTexel_u0028_vf2_u003b((¶m_111)); - c = _e216; - let _e217 = (*corner); - let _e218 = low; - param_112 = ((_e217 + _e218) + vec2(1f, 1f)); - let _e221 = AtlasTexel_u0028_vf2_u003b((¶m_112)); - d_4 = _e221; - let _e222 = a_4; - let _e223 = b_4; - let _e225 = f[0u]; - let _e228 = c; - let _e229 = d_4; - let _e231 = f[0u]; - let _e235 = f[1u]; - return mix(mix(_e222, _e223, vec4(_e225)), mix(_e228, _e229, vec4(_e231)), vec4(_e235)); + param_115 = (_e212 + _e213); + let _e215 = AtlasTexel_u0028_vf2_u003b((¶m_115)); + a_4 = _e215; + let _e216 = (*corner); + let _e217 = low; + param_116 = ((_e216 + _e217) + vec2(1f, 0f)); + let _e220 = AtlasTexel_u0028_vf2_u003b((¶m_116)); + b_4 = _e220; + let _e221 = (*corner); + let _e222 = low; + param_117 = ((_e221 + _e222) + vec2(0f, 1f)); + let _e225 = AtlasTexel_u0028_vf2_u003b((¶m_117)); + c_1 = _e225; + let _e226 = (*corner); + let _e227 = low; + param_118 = ((_e226 + _e227) + vec2(1f, 1f)); + let _e230 = AtlasTexel_u0028_vf2_u003b((¶m_118)); + d_4 = _e230; + let _e231 = a_4; + let _e232 = b_4; + let _e234 = f[0u]; + let _e237 = c_1; + let _e238 = d_4; + let _e240 = f[0u]; + let _e244 = f[1u]; + return mix(mix(_e231, _e232, vec4(_e234)), mix(_e237, _e238, vec4(_e240)), vec4(_e244)); } fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec2 { @@ -24117,32 +25018,32 @@ fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec var n_5: vec3; var xy: vec2; - let _e182 = (*direction_1)[0u]; - let _e185 = (*direction_1)[1u]; - let _e189 = (*direction_1)[2u]; - sum_1 = ((abs(_e182) + abs(_e185)) + abs(_e189)); - let _e192 = sum_1; - if (_e192 <= 0f) { + let _e191 = (*direction_1)[0u]; + let _e194 = (*direction_1)[1u]; + let _e198 = (*direction_1)[2u]; + sum_1 = ((abs(_e191) + abs(_e194)) + abs(_e198)); + let _e201 = sum_1; + if (_e201 <= 0f) { return vec2(0.5f, 0.5f); } - let _e194 = (*direction_1); - let _e195 = sum_1; - n_5 = (_e194 / vec3(_e195)); - let _e198 = n_5; - xy = _e198.xy; - let _e201 = n_5[2u]; - if (_e201 < 0f) { - let _e204 = n_5[1u]; - let _e208 = n_5[0u]; - let _e213 = n_5[0u]; - let _e217 = n_5[1u]; - xy = vec2(((1f - abs(_e204)) * select(-1f, 1f, (_e208 >= 0f))), ((1f - abs(_e213)) * select(-1f, 1f, (_e217 >= 0f)))); - } - let _e222 = xy; - return ((_e222 * 0.5f) + vec2(0.5f)); + let _e203 = (*direction_1); + let _e204 = sum_1; + n_5 = (_e203 / vec3(_e204)); + let _e207 = n_5; + xy = _e207.xy; + let _e210 = n_5[2u]; + if (_e210 < 0f) { + let _e213 = n_5[1u]; + let _e217 = n_5[0u]; + let _e222 = n_5[0u]; + let _e226 = n_5[1u]; + xy = vec2(((1f - abs(_e213)) * select(-1f, 1f, (_e217 >= 0f))), ((1f - abs(_e222)) * select(-1f, 1f, (_e226 >= 0f)))); + } + let _e231 = xy; + return ((_e231 * 0.5f) + vec2(0.5f)); } -fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view: ptr>) -> vec3 { +fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view_1: ptr>) -> vec3 { var origin_2: vec3; var spacing: vec3; var counts: vec3; @@ -24164,9 +25065,9 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var irradianceCorner: vec2; var momentCorner: vec2; - var param_113: vec2; + var param_119: vec2; var trilinear: vec3; - var weight_2: f32; + var weight_3: f32; var probePosition: vec3; var toProbe: vec3; var distance_4: f32; @@ -24174,336 +25075,339 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; - var param_114: vec3; - var param_115: vec2; - var param_116: f32; - var param_117: vec2; + var param_120: vec3; + var param_121: vec2; + var param_122: f32; + var param_123: vec2; var chebyshev: f32; var variance_1: f32; var difference: f32; - var param_118: vec3; - var param_119: vec2; - var param_120: f32; - var param_121: vec2; - var local_24: vec3; - - let _e224 = irradiance_info.origin; - origin_2 = _e224.xyz; - let _e227 = irradiance_info.spacing; - spacing = _e227.xyz; - let _e230 = irradiance_info.counts; - counts = _e230.xyz; - let _e234 = irradiance_info.tiles[0u]; - irradianceTile = _e234; - let _e237 = irradiance_info.tiles[1u]; - depthTile = _e237; - let _e240 = irradiance_info.tiles[2u]; - columns = _e240; - let _e243 = irradiance_info.tiles[3u]; - momentsTop = _e243; - let _e244 = (*normal_1); - unit = normalize(_e244); - let _e246 = (*world_4); - let _e247 = unit; - let _e250 = irradiance_info.spacing[3u]; - let _e253 = (*view); - let _e256 = irradiance_info.counts[3u]; - biased = ((_e246 + (_e247 * _e250)) + (_e253 * _e256)); - let _e259 = biased; - let _e260 = origin_2; - let _e262 = spacing; - grid_1 = ((_e259 - _e260) / _e262); - let _e264 = grid_1; - let _e266 = counts; - base = clamp(floor(_e264), vec3(0f, 0f, 0f), (_e266 - vec3(2f))); - let _e270 = grid_1; - let _e271 = base; - f_1 = clamp((_e270 - _e271), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + var param_124: vec3; + var param_125: vec2; + var param_126: f32; + var param_127: vec2; + var local_26: vec3; + + let _e233 = irradiance_info.origin; + origin_2 = _e233.xyz; + let _e236 = irradiance_info.spacing; + spacing = _e236.xyz; + let _e239 = irradiance_info.counts; + counts = _e239.xyz; + let _e243 = irradiance_info.tiles[0u]; + irradianceTile = _e243; + let _e246 = irradiance_info.tiles[1u]; + depthTile = _e246; + let _e249 = irradiance_info.tiles[2u]; + columns = _e249; + let _e252 = irradiance_info.tiles[3u]; + momentsTop = _e252; + let _e253 = (*normal_1); + unit = normalize(_e253); + let _e255 = (*world_4); + let _e256 = unit; + let _e259 = irradiance_info.spacing[3u]; + let _e262 = (*view_1); + let _e265 = irradiance_info.counts[3u]; + biased = ((_e255 + (_e256 * _e259)) + (_e262 * _e265)); + let _e268 = biased; + let _e269 = origin_2; + let _e271 = spacing; + grid_1 = ((_e268 - _e269) / _e271); + let _e273 = grid_1; + let _e275 = counts; + base = clamp(floor(_e273), vec3(0f, 0f, 0f), (_e275 - vec3(2f))); + let _e279 = grid_1; + let _e280 = base; + f_1 = clamp((_e279 - _e280), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); total_2 = vec3(0f, 0f, 0f); weights = 0f; corner_1 = 0i; loop { - let _e274 = corner_1; - if (_e274 < 8i) { - let _e276 = corner_1; - let _e279 = corner_1; - let _e284 = corner_1; - offset_3 = vec3(f32((_e276 & 1i)), f32(((_e279 >> bitcast(1i)) & 1i)), f32(((_e284 >> bitcast(2i)) & 1i))); - let _e290 = base; - let _e291 = offset_3; - cell_1 = (_e290 + _e291); - let _e294 = cell_1[2u]; - let _e296 = counts[1u]; - let _e299 = cell_1[1u]; - let _e302 = counts[0u]; - let _e305 = cell_1[0u]; - probe = ((((_e294 * _e296) + _e299) * _e302) + _e305); - let _e307 = probe; - let _e308 = columns; - let _e313 = probe; - let _e314 = columns; - tile_4 = vec2((_e307 - (floor((_e307 / _e308)) * _e308)), floor((_e313 / _e314))); - let _e318 = tile_4; - let _e319 = irradianceTile; - irradianceCorner = (_e318 * (_e319 + 2f)); - let _e323 = tile_4[0u]; - let _e324 = depthTile; - let _e327 = momentsTop; - let _e329 = tile_4[1u]; - let _e330 = depthTile; - momentCorner = vec2((_e323 * (_e324 + 2f)), (_e327 + (_e329 * (_e330 + 2f)))); - let _e335 = irradianceCorner; - param_113 = (_e335 + vec2(1f)); - let _e338 = AtlasTexel_u0028_vf2_u003b((¶m_113)); - if (_e338.w < 0.5f) { + let _e283 = corner_1; + if (_e283 < 8i) { + let _e285 = corner_1; + let _e288 = corner_1; + let _e293 = corner_1; + offset_3 = vec3(f32((_e285 & 1i)), f32(((_e288 >> bitcast(1i)) & 1i)), f32(((_e293 >> bitcast(2i)) & 1i))); + let _e299 = base; + let _e300 = offset_3; + cell_1 = (_e299 + _e300); + let _e303 = cell_1[2u]; + let _e305 = counts[1u]; + let _e308 = cell_1[1u]; + let _e311 = counts[0u]; + let _e314 = cell_1[0u]; + probe = ((((_e303 * _e305) + _e308) * _e311) + _e314); + let _e316 = probe; + let _e317 = columns; + let _e322 = probe; + let _e323 = columns; + tile_4 = vec2((_e316 - (floor((_e316 / _e317)) * _e317)), floor((_e322 / _e323))); + let _e327 = tile_4; + let _e328 = irradianceTile; + irradianceCorner = (_e327 * (_e328 + 2f)); + let _e332 = tile_4[0u]; + let _e333 = depthTile; + let _e336 = momentsTop; + let _e338 = tile_4[1u]; + let _e339 = depthTile; + momentCorner = vec2((_e332 * (_e333 + 2f)), (_e336 + (_e338 * (_e339 + 2f)))); + let _e344 = irradianceCorner; + param_119 = (_e344 + vec2(1f)); + let _e347 = AtlasTexel_u0028_vf2_u003b((¶m_119)); + if (_e347.w < 0.5f) { continue; } - let _e341 = f_1; - let _e343 = f_1; - let _e344 = offset_3; - trilinear = mix((vec3(1f, 1f, 1f) - _e341), _e343, _e344); - let _e347 = trilinear[0u]; - let _e349 = trilinear[1u]; - let _e352 = trilinear[2u]; - weight_2 = max(((_e347 * _e349) * _e352), 0.001f); - let _e355 = origin_2; - let _e356 = spacing; - let _e357 = cell_1; - probePosition = (_e355 + (_e356 * _e357)); - let _e360 = probePosition; - let _e361 = biased; - toProbe = (_e360 - _e361); - let _e363 = toProbe; - distance_4 = length(_e363); - let _e365 = distance_4; - if (_e365 > 0.000001f) { - let _e367 = toProbe; - let _e368 = distance_4; - direction_2 = (_e367 / vec3(_e368)); - let _e371 = unit; - let _e372 = probePosition; - let _e373 = (*world_4); - facing = ((dot(_e371, normalize((_e372 - _e373))) * 0.5f) + 0.5f); - let _e379 = facing; - let _e380 = facing; - probeWeight = ((_e379 * _e380) + 0.2f); - let _e383 = direction_2; - param_114 = -(_e383); - let _e385 = ProbeOctahedral_u0028_vf3_u003b((¶m_114)); - let _e386 = momentCorner; - param_115 = _e386; - let _e387 = depthTile; - param_116 = _e387; - param_117 = _e385; - let _e388 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_115), (¶m_116), (¶m_117)); - moments_3 = _e388.xy; + let _e350 = f_1; + let _e352 = f_1; + let _e353 = offset_3; + trilinear = mix((vec3(1f, 1f, 1f) - _e350), _e352, _e353); + let _e356 = trilinear[0u]; + let _e358 = trilinear[1u]; + let _e361 = trilinear[2u]; + weight_3 = max(((_e356 * _e358) * _e361), 0.001f); + let _e364 = origin_2; + let _e365 = spacing; + let _e366 = cell_1; + probePosition = (_e364 + (_e365 * _e366)); + let _e369 = probePosition; + let _e370 = biased; + toProbe = (_e369 - _e370); + let _e372 = toProbe; + distance_4 = length(_e372); + let _e374 = distance_4; + if (_e374 > 0.000001f) { + let _e376 = toProbe; + let _e377 = distance_4; + direction_2 = (_e376 / vec3(_e377)); + let _e380 = unit; + let _e381 = probePosition; + let _e382 = (*world_4); + facing = ((dot(_e380, normalize((_e381 - _e382))) * 0.5f) + 0.5f); + let _e388 = facing; + let _e389 = facing; + probeWeight = ((_e388 * _e389) + 0.2f); + let _e392 = direction_2; + param_120 = -(_e392); + let _e394 = ProbeOctahedral_u0028_vf3_u003b((¶m_120)); + let _e395 = momentCorner; + param_121 = _e395; + let _e396 = depthTile; + param_122 = _e396; + param_123 = _e394; + let _e397 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_121), (¶m_122), (¶m_123)); + moments_3 = _e397.xy; chebyshev = 1f; - let _e390 = distance_4; - let _e392 = moments_3[0u]; - if (_e390 > _e392) { - let _e395 = moments_3[1u]; - let _e397 = moments_3[0u]; - let _e399 = moments_3[0u]; - variance_1 = max((_e395 - (_e397 * _e399)), 0.000001f); - let _e403 = distance_4; - let _e405 = moments_3[0u]; - difference = (_e403 - _e405); - let _e407 = variance_1; - let _e408 = variance_1; - let _e409 = difference; - let _e410 = difference; - chebyshev = (_e407 / (_e408 + (_e409 * _e410))); - let _e414 = chebyshev; - let _e415 = chebyshev; - let _e417 = chebyshev; - chebyshev = ((_e414 * _e415) * _e417); + let _e399 = distance_4; + let _e401 = moments_3[0u]; + if (_e399 > _e401) { + let _e404 = moments_3[1u]; + let _e406 = moments_3[0u]; + let _e408 = moments_3[0u]; + variance_1 = max((_e404 - (_e406 * _e408)), 0.000001f); + let _e412 = distance_4; + let _e414 = moments_3[0u]; + difference = (_e412 - _e414); + let _e416 = variance_1; + let _e417 = variance_1; + let _e418 = difference; + let _e419 = difference; + chebyshev = (_e416 / (_e417 + (_e418 * _e419))); + let _e423 = chebyshev; + let _e424 = chebyshev; + let _e426 = chebyshev; + chebyshev = ((_e423 * _e424) * _e426); } - let _e419 = probeWeight; - let _e420 = chebyshev; - probeWeight = max((_e419 * max(_e420, 0.05f)), 0.000001f); - let _e424 = probeWeight; - if (_e424 < 0.2f) { - let _e426 = probeWeight; - let _e427 = probeWeight; - let _e430 = probeWeight; - probeWeight = (_e430 * ((_e426 * _e427) * 25f)); + let _e428 = probeWeight; + let _e429 = chebyshev; + probeWeight = max((_e428 * max(_e429, 0.05f)), 0.000001f); + let _e433 = probeWeight; + if (_e433 < 0.2f) { + let _e435 = probeWeight; + let _e436 = probeWeight; + let _e439 = probeWeight; + probeWeight = (_e439 * ((_e435 * _e436) * 25f)); } - let _e432 = probeWeight; - let _e433 = weight_2; - weight_2 = (_e433 * _e432); - } - let _e435 = unit; - param_118 = _e435; - let _e436 = ProbeOctahedral_u0028_vf3_u003b((¶m_118)); - let _e437 = irradianceCorner; - param_119 = _e437; - let _e438 = irradianceTile; - param_120 = _e438; - param_121 = _e436; - let _e439 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_119), (¶m_120), (¶m_121)); - let _e441 = weight_2; - let _e443 = total_2; - total_2 = (_e443 + (_e439.xyz * _e441)); - let _e445 = weight_2; - let _e446 = weights; - weights = (_e446 + _e445); + let _e441 = probeWeight; + let _e442 = weight_3; + weight_3 = (_e442 * _e441); + } + let _e444 = unit; + param_124 = _e444; + let _e445 = ProbeOctahedral_u0028_vf3_u003b((¶m_124)); + let _e446 = irradianceCorner; + param_125 = _e446; + let _e447 = irradianceTile; + param_126 = _e447; + param_127 = _e445; + let _e448 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_125), (¶m_126), (¶m_127)); + let _e450 = weight_3; + let _e452 = total_2; + total_2 = (_e452 + (_e448.xyz * _e450)); + let _e454 = weight_3; + let _e455 = weights; + weights = (_e455 + _e454); continue; } else { break; } continuing { - let _e448 = corner_1; - corner_1 = (_e448 + 1i); + let _e457 = corner_1; + corner_1 = (_e457 + 1i); } } - let _e450 = weights; - if (_e450 > 0f) { - let _e452 = total_2; - let _e453 = weights; - local_24 = (_e452 / vec3(_e453)); + let _e459 = weights; + if (_e459 > 0f) { + let _e461 = total_2; + let _e462 = weights; + local_26 = (_e461 / vec3(_e462)); } else { - local_24 = vec3(0f, 0f, 0f); + local_26 = vec3(0f, 0f, 0f); } - let _e456 = local_24; - return _e456; + let _e465 = local_26; + return _e465; } fn IrradianceEnabled_u0028_() -> bool { - let _e179 = irradiance_info.origin[3u]; - return (_e179 > 0.5f); + let _e188 = irradiance_info.origin[3u]; + return (_e188 > 0.5f); } -fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) { - var param_122: vec3; - var param_123: vec3; - var param_124: vec3; - var param_125: Surface; - var param_126: Surface; - var param_127: Surface; - - let _e184 = IrradianceEnabled_u0028_(); - if _e184 { - let _e185 = v_world_position_1; - param_122 = _e185; - let _e187 = (*s_9).n; - param_123 = _e187; - let _e189 = (*s_9).v; - param_124 = _e189; - let _e190 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_122), (¶m_123), (¶m_124)); - let _e193 = frag_info.material[2u]; - (*s_9).ambient = (_e190 * _e193); - } - let _e196 = (*s_9); - param_125 = _e196; - ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_125)); - let _e197 = param_125; - (*s_9) = _e197; - let _e198 = (*s_9); - param_126 = _e198; - ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_126)); - let _e199 = param_126; - (*s_9) = _e199; - let _e200 = (*s_9); - param_127 = _e200; - ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_127)); - let _e201 = param_127; - (*s_9) = _e201; +fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_10: ptr) { + var param_128: vec3; + var param_129: vec3; + var param_130: vec3; + var param_131: Surface; + var param_132: Surface; + var param_133: Surface; + + let _e193 = IrradianceEnabled_u0028_(); + if _e193 { + let _e194 = v_world_position_1; + param_128 = _e194; + let _e196 = (*s_10).n; + param_129 = _e196; + let _e198 = (*s_10).v; + param_130 = _e198; + let _e199 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_128), (¶m_129), (¶m_130)); + let _e202 = frag_info.material[2u]; + (*s_10).ambient = (_e199 * _e202); + } + let _e205 = (*s_10); + param_131 = _e205; + ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_131)); + let _e206 = param_131; + (*s_10) = _e206; + let _e207 = (*s_10); + param_132 = _e207; + ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_132)); + let _e208 = param_132; + (*s_10) = _e208; + let _e209 = (*s_10); + param_133 = _e209; + ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_133)); + let _e210 = param_133; + (*s_10) = _e210; return; } fn ReadSurface_u0028_() -> Surface { var texel_6: vec4; - var s_10: Surface; - var param_128: vec3; - var param_129: vec3; + var s_11: Surface; + var param_134: vec3; + var param_135: vec3; var cutoff: f32; var anchored: vec3; var noise_1: f32; - let _e184 = v_texcoord_1; - let _e185 = MaterialLodBias_u0028_(); - let _e186 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e184, _e185); - texel_6 = _e186; - let _e187 = texel_6; - param_128 = _e187.xyz; - let _e189 = SrgbToLinear_u0028_vf3_u003b((¶m_128)); - let _e191 = frag_info.base_color; - param_129 = _e191.xyz; - let _e193 = SrgbToLinear_u0028_vf3_u003b((¶m_129)); - let _e195 = v_color_1; - s_10.albedo = ((_e189 * _e193) * _e195.xyz); - let _e200 = texel_6[3u]; - let _e203 = frag_info.base_color[3u]; - let _e206 = v_color_1[3u]; - s_10.alpha = ((_e200 * _e203) * _e206); - let _e210 = s_10.albedo; - g_albedo = _e210; - let _e213 = frag_info.material2_[0u]; - cutoff = _e213; - let _e214 = cutoff; - if (_e214 >= 0f) { - let _e217 = s_10.alpha; - let _e218 = cutoff; - if (_e217 < _e218) { + let _e193 = v_texcoord_1; + let _e194 = MaterialLodBias_u0028_(); + let _e195 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e193, _e194); + texel_6 = _e195; + let _e196 = texel_6; + param_134 = _e196.xyz; + let _e198 = SrgbToLinear_u0028_vf3_u003b((¶m_134)); + let _e200 = frag_info.base_color; + param_135 = _e200.xyz; + let _e202 = SrgbToLinear_u0028_vf3_u003b((¶m_135)); + let _e204 = v_color_1; + s_11.albedo = ((_e198 * _e202) * _e204.xyz); + let _e209 = texel_6[3u]; + let _e212 = frag_info.base_color[3u]; + let _e215 = v_color_1[3u]; + s_11.alpha = ((_e209 * _e212) * _e215); + let _e219 = s_11.albedo; + g_albedo = _e219; + let _e222 = frag_info.material2_[0u]; + cutoff = _e222; + let _e223 = cutoff; + if (_e223 >= 0f) { + let _e226 = s_11.alpha; + let _e227 = cutoff; + if (_e226 < _e227) { discard; } } else { - let _e220 = cutoff; - if (_e220 < -1.5f) { - let _e222 = v_world_position_1; - anchored = floor((_e222 * 16f)); - let _e225 = anchored; - noise_1 = fract((sin(dot(_e225, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e231 = s_10.alpha; - let _e232 = noise_1; - if (_e231 < _e232) { + let _e229 = cutoff; + if (_e229 < -1.5f) { + let _e231 = v_world_position_1; + anchored = floor((_e231 * 16f)); + let _e234 = anchored; + noise_1 = fract((sin(dot(_e234, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e240 = s_11.alpha; + let _e241 = noise_1; + if (_e240 < _e241) { discard; } } } - let _e234 = cutoff; - let _e236 = cutoff; - g_premultiply = ((_e234 < 0f) && (_e236 > -0.75f)); - let _e239 = v_normal_1; - s_10.n = normalize(_e239); - let _e242 = gl_FrontFacing_1; - if !(_e242) { - let _e245 = s_10.n; - s_10.n = -(_e245); - } - let _e249 = frag_info.camera_position; - let _e251 = v_world_position_1; - s_10.v = normalize((_e249.xyz - _e251)); - let _e256 = s_10.n; - let _e258 = s_10.v; - s_10.n_dot_v = max(dot(_e256, _e258), 0.0001f); - let _e264 = frag_info.material[0u]; - s_10.metallic = clamp(_e264, 0f, 1f); - let _e269 = frag_info.material[1u]; - s_10.roughness = clamp(_e269, 0.02f, 1f); - let _e273 = frag_info.ambient_ground; - let _e276 = frag_info.ambient_sky; - let _e280 = s_10.n[1u]; - let _e287 = frag_info.material[2u]; - s_10.ambient = (mix(_e273.xyz, _e276.xyz, vec3(((_e280 * 0.5f) + 0.5f))) * _e287); - let _e292 = frag_info.frame_params[0u]; - s_10.exposure = max(_e292, 0f); - s_10.occlusion = 1f; - s_10.emissive = vec3(0f, 0f, 0f); - let _e297 = s_10; - return _e297; + let _e243 = cutoff; + let _e245 = cutoff; + g_premultiply = ((_e243 < 0f) && (_e245 > -0.75f)); + let _e248 = v_normal_1; + s_11.n = normalize(_e248); + let _e251 = gl_FrontFacing_1; + if !(_e251) { + let _e254 = s_11.n; + s_11.n = -(_e254); + } + let _e258 = frag_info.camera_position; + let _e260 = v_world_position_1; + s_11.v = normalize((_e258.xyz - _e260)); + let _e265 = s_11.n; + let _e267 = s_11.v; + s_11.n_dot_v = max(dot(_e265, _e267), 0.0001f); + let _e273 = frag_info.material[0u]; + s_11.metallic = clamp(_e273, 0f, 1f); + let _e278 = frag_info.material[1u]; + s_11.roughness = clamp(_e278, 0.02f, 1f); + let _e282 = frag_info.ambient_ground; + let _e285 = frag_info.ambient_sky; + let _e289 = s_11.n[1u]; + let _e296 = frag_info.material[2u]; + s_11.ambient = (mix(_e282.xyz, _e285.xyz, vec3(((_e289 * 0.5f) + 0.5f))) * _e296); + let _e301 = frag_info.frame_params[0u]; + s_11.exposure = max(_e301, 0f); + s_11.occlusion = 1f; + s_11.emissive = vec3(0f, 0f, 0f); + let _e306 = s_11; + return _e306; } fn main_1() { - var s_11: Surface; - var param_130: Surface; - var param_131: Surface; + var s_12: Surface; + var param_136: Surface; + var param_137: Surface; var rim: f32; var ambient: vec3; - var param_132: Surface; - var param_133: vec3; - var param_134: f32; - var param_135: f32; + var lit_3: vec3; + var param_138: Surface; + var param_139: Surface; + var param_140: vec3; + var param_141: vec3; + var param_142: f32; + var param_143: f32; g_albedo = vec3(0f, 0f, 0f); g_debug_surface = vec3(0f, 0f, 0f); @@ -24511,39 +25415,49 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e186 = ReadSurface_u0028_(); - s_11 = _e186; - let _e187 = s_11; - param_130 = _e187; - ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_130)); - let _e188 = param_130; - s_11 = _e188; - let _e189 = s_11; - param_131 = _e189; - ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_131)); - let _e190 = param_131; - s_11 = _e190; - let _e192 = s_11.n_dot_v; - let _e197 = frag_info.material[3u]; - rim = (pow((1f - _e192), 3f) * _e197); - let _e200 = s_11.albedo; - let _e202 = s_11.ambient; - let _e203 = SampleLightmap_u0028_(); - let _e207 = s_11.occlusion; - ambient = ((_e200 * (_e202 + _e203)) * _e207); - let _e209 = s_11; - param_132 = _e209; - let _e210 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_132)); - let _e212 = s_11.occlusion; - let _e214 = ambient; - let _e216 = rim; - let _e221 = s_11.emissive; - param_133 = ((((_e210 * _e212) + _e214) + vec3((_e216 * 0.35f))) + _e221); - let _e224 = s_11.alpha; - param_134 = _e224; - let _e226 = s_11.roughness; - param_135 = _e226; - WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_133), (¶m_134), (¶m_135)); + let _e198 = ReadSurface_u0028_(); + s_12 = _e198; + let _e199 = s_12; + param_136 = _e199; + ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_136)); + let _e200 = param_136; + s_12 = _e200; + let _e201 = s_12; + param_137 = _e201; + ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_137)); + let _e202 = param_137; + s_12 = _e202; + let _e204 = s_12.n_dot_v; + let _e209 = frag_info.material[3u]; + rim = (pow((1f - _e204), 3f) * _e209); + let _e212 = s_12.albedo; + let _e214 = s_12.ambient; + let _e215 = SampleLightmap_u0028_(); + let _e219 = s_12.occlusion; + ambient = ((_e212 * (_e214 + _e215)) * _e219); + let _e221 = s_12; + param_138 = _e221; + let _e222 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_138)); + let _e224 = s_12.occlusion; + let _e226 = ambient; + let _e228 = rim; + let _e233 = s_12.emissive; + lit_3 = ((((_e222 * _e224) + _e226) + vec3((_e228 * 0.35f))) + _e233); + let _e235 = s_12; + param_139 = _e235; + let _e236 = lit_3; + param_140 = _e236; + let _e237 = WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b((¶m_139), (¶m_140)); + if _e237 { + return; + } + let _e238 = lit_3; + param_141 = _e238; + let _e240 = s_12.alpha; + param_142 = _e240; + let _e242 = s_12.roughness; + param_143 = _e242; + WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_141), (¶m_142), (¶m_143)); return; } @@ -24585,7 +25499,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember( name: 'light_position', @@ -24682,6 +25596,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3; var dither: f32; var param_20: vec2; - - let _e190 = composite_info.lens[0u]; - distortion = _e190; - let _e191 = distortion; - if (_e191 != 0f) { - let _e193 = v_uv_1; - param_9 = _e193; - let _e194 = distortion; - param_10 = _e194; - let _e195 = Distort_u0028_vf2_u003b_f1_u003b((¶m_9), (¶m_10)); - local_3 = _e195; + var raw: vec4; + var param_21: vec3; + var phi_1262_: bool; + + let _e192 = composite_info.lens[0u]; + distortion = _e192; + let _e193 = distortion; + if (_e193 != 0f) { + let _e195 = v_uv_1; + param_9 = _e195; + let _e196 = distortion; + param_10 = _e196; + let _e197 = Distort_u0028_vf2_u003b_f1_u003b((¶m_9), (¶m_10)); + local_3 = _e197; } else { - let _e196 = v_uv_1; - local_3 = _e196; - } - let _e197 = local_3; - uv = _e197; - let _e200 = composite_info.look[3u]; - dispersion = _e200; - let _e201 = dispersion; - if (_e201 > 0f) { - let _e203 = uv; - fromCentre_1 = (_e203 - vec2(0.5f, 0.5f)); - let _e205 = fromCentre_1; - let _e206 = dispersion; - step_uv = (_e205 * _e206); - let _e208 = uv; - let _e209 = textureSample(scene_texture_tex, scene_texture_smp, _e208); - scene = _e209; + let _e198 = v_uv_1; + local_3 = _e198; + } + let _e199 = local_3; + uv = _e199; + let _e202 = composite_info.look[3u]; + dispersion = _e202; + let _e203 = dispersion; + if (_e203 > 0f) { + let _e205 = uv; + fromCentre_1 = (_e205 - vec2(0.5f, 0.5f)); + let _e207 = fromCentre_1; + let _e208 = dispersion; + step_uv = (_e207 * _e208); let _e210 = uv; - let _e211 = step_uv; - let _e213 = textureSample(scene_texture_tex, scene_texture_smp, (_e210 + _e211)); - scene[0u] = _e213.x; - let _e216 = uv; - let _e217 = step_uv; - let _e219 = textureSample(scene_texture_tex, scene_texture_smp, (_e216 - _e217)); - scene[2u] = _e219.z; + let _e211 = textureSample(scene_texture_tex, scene_texture_smp, _e210); + scene = _e211; + let _e212 = uv; + let _e213 = step_uv; + let _e215 = textureSample(scene_texture_tex, scene_texture_smp, (_e212 + _e213)); + scene[0u] = _e215.x; + let _e218 = uv; + let _e219 = step_uv; + let _e221 = textureSample(scene_texture_tex, scene_texture_smp, (_e218 - _e219)); + scene[2u] = _e221.z; } else { - let _e222 = uv; - let _e223 = textureSample(scene_texture_tex, scene_texture_smp, _e222); - scene = _e223; - } - let _e224 = uv; - let _e225 = textureSample(bloom_texture_tex, bloom_texture_smp, _e224); - bloom = _e225.xyz; - let _e228 = composite_info.ao_texel; - half_texel = (_e228.xy * 0.5f); - let _e231 = uv; - let _e233 = half_texel[0u]; - let _e235 = half_texel[1u]; - let _e238 = textureSample(ao_texture_tex, ao_texture_smp, (_e231 + vec2(_e233, _e235))); - let _e239 = uv; - let _e241 = half_texel[0u]; - let _e244 = half_texel[1u]; - let _e247 = textureSample(ao_texture_tex, ao_texture_smp, (_e239 + vec2(-(_e241), _e244))); - let _e249 = uv; - let _e251 = half_texel[0u]; - let _e253 = half_texel[1u]; - let _e257 = textureSample(ao_texture_tex, ao_texture_smp, (_e249 + vec2(_e251, -(_e253)))); - let _e259 = uv; - let _e261 = half_texel[0u]; - let _e264 = half_texel[1u]; - let _e268 = textureSample(ao_texture_tex, ao_texture_smp, (_e259 + vec2(-(_e261), -(_e264)))); - occlusion = ((((_e238 + _e247) + _e257) + _e268) * 0.25f); - let _e272 = occlusion[3u]; - ao = _e272; - let _e273 = ao; - let _e276 = composite_info.params[3u]; - ao = mix(1f, _e273, clamp(_e276, 0f, 1f)); - let _e279 = uv; - let _e280 = textureSample(contact_shadow_texture_tex, contact_shadow_texture_smp, _e279); - contact = _e280.x; - let _e282 = contact; - let _e285 = composite_info.contact[0u]; - let _e288 = ao; - ao = (_e288 * mix(1f, _e282, clamp(_e285, 0f, 1f))); - let _e290 = uv; - let _e291 = textureSample(local_exposure_texture_tex, local_exposure_texture_smp, _e290); - localStops = _e291.x; - let _e293 = scene; - let _e295 = localStops; - let _e298 = composite_info.contact[3u]; - exposed = (_e293.xyz * exp2((_e295 * _e298))); - let _e302 = exposed; - let _e303 = ao; - let _e305 = bloom; - let _e308 = composite_info.params[1u]; - color_10 = ((_e302 * _e303) + (_e305 * _e308)); - let _e311 = occlusion; - let _e315 = composite_info.contact[2u]; - let _e319 = composite_info.params[3u]; - let _e322 = color_10; - color_10 = (_e322 + ((_e311.xyz * _e315) * clamp(_e319, 0f, 1f))); - let _e326 = composite_info.params[0u]; - let _e328 = color_10; - color_10 = (_e328 * max(_e326, 0f)); - let _e332 = composite_info.params[2u]; - let _e335 = color_10; - param_11 = _e335; - param_12 = i32((_e332 + 0.5f)); - let _e336 = TonemapBy_u0028_vf3_u003b_i1_u003b((¶m_11), (¶m_12)); - color_10 = _e336; - let _e339 = composite_info.look[0u]; - contrast = _e339; - let _e342 = composite_info.look[1u]; - saturation = _e342; - let _e345 = composite_info.look[2u]; - temperature = _e345; - let _e346 = contrast; - if (_e346 != 1f) { - let _e348 = color_10; - let _e352 = contrast; - color_10 = (vec3(0.18f, 0.18f, 0.18f) * pow((max(_e348, vec3(0f, 0f, 0f)) / vec3(0.18f)), vec3(_e352))); - } - let _e356 = color_10; - param_13 = _e356; - let _e357 = Luma_u0028_vf3_u003b((¶m_13)); - let _e359 = color_10; - let _e360 = saturation; - color_10 = mix(vec3(_e357), _e359, vec3(_e360)); - let _e363 = temperature; - let _e366 = temperature; - let _e370 = color_10; - color_10 = (_e370 * vec3((1f + (_e363 * 0.1f)), 1f, (1f - (_e366 * 0.1f)))); - let _e373 = composite_info.lift; - lift = _e373.xyz; - let _e376 = composite_info.gamma; - gammaCurve = _e376.xyz; - let _e379 = composite_info.gain; - gain = _e379.xyz; - let _e381 = color_10; - let _e382 = lift; - let _e385 = lift; - color_10 = ((_e381 * (vec3(1f, 1f, 1f) - _e382)) + _e385); - let _e387 = color_10; - color_10 = max(_e387, vec3(0f, 0f, 0f)); - let _e389 = gammaCurve; - if any((_e389 != vec3(1f, 1f, 1f))) { - let _e392 = color_10; - let _e393 = gammaCurve; - color_10 = pow(_e392, (vec3(1f, 1f, 1f) / _e393)); - } - let _e396 = gain; - let _e397 = color_10; - color_10 = (_e397 * _e396); - let _e401 = composite_info.output_encode[1u]; - balance = _e401; - let _e404 = composite_info.output_encode[2u]; - tint = _e404; - let _e405 = balance; - let _e407 = tint; - if ((_e405 != 0f) || (_e407 != 0f)) { - let _e410 = balance; - let _e413 = tint; - let _e416 = balance; - let _e420 = color_10; - color_10 = (_e420 * vec3((1f + (_e410 * 0.2f)), (1f + (_e413 * 0.15f)), (1f - (_e416 * 0.2f)))); - let _e422 = tint; - let _e425 = color_10[0u]; - color_10[0u] = (_e425 - (_e422 * 0.075f)); - let _e428 = tint; - let _e431 = color_10[2u]; - color_10[2u] = (_e431 - (_e428 * 0.075f)); - } - let _e436 = composite_info.ao_texel[2u]; - lutStrength = _e436; - let _e437 = lutStrength; - if (_e437 > 0f) { - let _e439 = color_10; - param_14 = clamp(_e439, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e441 = LinearToSrgb_u0028_vf3_u003b((¶m_14)); - encodedIn = _e441; - let _e444 = composite_info.ao_texel[3u]; - let _e446 = encodedIn; - param_15 = _e446; - param_16 = max(_e444, 2f); - let _e447 = SampleLut_u0028_vf3_u003b_f1_u003b((¶m_15), (¶m_16)); - param_17 = _e447; - let _e448 = SrgbToLinear_u0028_vf3_u003b((¶m_17)); - graded = _e448; - let _e449 = color_10; - let _e450 = graded; - let _e451 = lutStrength; - color_10 = mix(_e449, _e450, vec3(clamp(_e451, 0f, 1f))); - } - let _e457 = composite_info.look_more[0u]; - vignette = _e457; - let _e458 = vignette; - if (_e458 > 0f) { - let _e460 = v_uv_1; - fromCentre_2 = (_e460 - vec2(0.5f, 0.5f)); - let _e464 = composite_info.look_more[3u]; - aspect_1 = max(_e464, 0.0001f); - let _e466 = aspect_1; - let _e469 = composite_info.look_more[1u]; - let _e472 = fromCentre_2[0u]; - fromCentre_2[0u] = (_e472 * mix(1f, _e466, _e469)); - let _e475 = fromCentre_2; - radius = (length(_e475) * 1.4142135f); - let _e478 = vignette; - let _e480 = radius; - let _e483 = color_10; - color_10 = (_e483 * mix(1f, (1f - _e478), clamp(_e480, 0f, 1f))); - } - let _e485 = color_10; - param_18 = max(_e485, vec3(0f, 0f, 0f)); - let _e487 = LinearToSrgb_u0028_vf3_u003b((¶m_18)); - encoded_1 = _e487; - let _e490 = composite_info.look_more[2u]; - grain = _e490; - let _e491 = grain; - if (_e491 > 0f) { - let _e493 = TargetFragCoord_u0028_(); - param_19 = _e493; - let _e494 = Hash_u0028_vf2_u003b((¶m_19)); - let _e496 = grain; - let _e499 = encoded_1; - encoded_1 = (_e499 + vec3(((_e494 - 0.5f) * _e496))); - } - let _e503 = composite_info.output_encode[0u]; - dither = _e503; - let _e504 = dither; - if (_e504 > 0f) { - let _e506 = TargetFragCoord_u0028_(); - param_20 = _e506; - let _e507 = BayerCell_u0028_vf2_u003b((¶m_20)); - let _e509 = dither; - let _e512 = encoded_1; - encoded_1 = (_e512 + vec3(((_e507 + 0.03125f) * _e509))); - } - let _e514 = encoded_1; - let _e516 = scene[3u]; - frag_color = vec4(_e514.x, _e514.y, _e514.z, _e516); + let _e224 = uv; + let _e225 = textureSample(scene_texture_tex, scene_texture_smp, _e224); + scene = _e225; + } + let _e226 = uv; + let _e227 = textureSample(bloom_texture_tex, bloom_texture_smp, _e226); + bloom = _e227.xyz; + let _e230 = composite_info.ao_texel; + half_texel = (_e230.xy * 0.5f); + let _e233 = uv; + let _e235 = half_texel[0u]; + let _e237 = half_texel[1u]; + let _e240 = textureSample(ao_texture_tex, ao_texture_smp, (_e233 + vec2(_e235, _e237))); + let _e241 = uv; + let _e243 = half_texel[0u]; + let _e246 = half_texel[1u]; + let _e249 = textureSample(ao_texture_tex, ao_texture_smp, (_e241 + vec2(-(_e243), _e246))); + let _e251 = uv; + let _e253 = half_texel[0u]; + let _e255 = half_texel[1u]; + let _e259 = textureSample(ao_texture_tex, ao_texture_smp, (_e251 + vec2(_e253, -(_e255)))); + let _e261 = uv; + let _e263 = half_texel[0u]; + let _e266 = half_texel[1u]; + let _e270 = textureSample(ao_texture_tex, ao_texture_smp, (_e261 + vec2(-(_e263), -(_e266)))); + occlusion = ((((_e240 + _e249) + _e259) + _e270) * 0.25f); + let _e274 = occlusion[3u]; + ao = _e274; + let _e275 = ao; + let _e278 = composite_info.params[3u]; + ao = mix(1f, _e275, clamp(_e278, 0f, 1f)); + let _e281 = uv; + let _e282 = textureSample(contact_shadow_texture_tex, contact_shadow_texture_smp, _e281); + contact = _e282.x; + let _e284 = contact; + let _e287 = composite_info.contact[0u]; + let _e290 = ao; + ao = (_e290 * mix(1f, _e284, clamp(_e287, 0f, 1f))); + let _e292 = uv; + let _e293 = textureSample(local_exposure_texture_tex, local_exposure_texture_smp, _e292); + localStops = _e293.x; + let _e295 = scene; + let _e297 = localStops; + let _e300 = composite_info.contact[3u]; + exposed = (_e295.xyz * exp2((_e297 * _e300))); + let _e304 = exposed; + let _e305 = ao; + let _e307 = bloom; + let _e310 = composite_info.params[1u]; + color_10 = ((_e304 * _e305) + (_e307 * _e310)); + let _e313 = occlusion; + let _e317 = composite_info.contact[2u]; + let _e321 = composite_info.params[3u]; + let _e324 = color_10; + color_10 = (_e324 + ((_e313.xyz * _e317) * clamp(_e321, 0f, 1f))); + let _e328 = composite_info.params[0u]; + let _e330 = color_10; + color_10 = (_e330 * max(_e328, 0f)); + let _e334 = composite_info.params[2u]; + let _e337 = color_10; + param_11 = _e337; + param_12 = i32((_e334 + 0.5f)); + let _e338 = TonemapBy_u0028_vf3_u003b_i1_u003b((¶m_11), (¶m_12)); + color_10 = _e338; + let _e341 = composite_info.look[0u]; + contrast = _e341; + let _e344 = composite_info.look[1u]; + saturation = _e344; + let _e347 = composite_info.look[2u]; + temperature = _e347; + let _e348 = contrast; + if (_e348 != 1f) { + let _e350 = color_10; + let _e354 = contrast; + color_10 = (vec3(0.18f, 0.18f, 0.18f) * pow((max(_e350, vec3(0f, 0f, 0f)) / vec3(0.18f)), vec3(_e354))); + } + let _e358 = color_10; + param_13 = _e358; + let _e359 = Luma_u0028_vf3_u003b((¶m_13)); + let _e361 = color_10; + let _e362 = saturation; + color_10 = mix(vec3(_e359), _e361, vec3(_e362)); + let _e365 = temperature; + let _e368 = temperature; + let _e372 = color_10; + color_10 = (_e372 * vec3((1f + (_e365 * 0.1f)), 1f, (1f - (_e368 * 0.1f)))); + let _e375 = composite_info.lift; + lift = _e375.xyz; + let _e378 = composite_info.gamma; + gammaCurve = _e378.xyz; + let _e381 = composite_info.gain; + gain = _e381.xyz; + let _e383 = color_10; + let _e384 = lift; + let _e387 = lift; + color_10 = ((_e383 * (vec3(1f, 1f, 1f) - _e384)) + _e387); + let _e389 = color_10; + color_10 = max(_e389, vec3(0f, 0f, 0f)); + let _e391 = gammaCurve; + if any((_e391 != vec3(1f, 1f, 1f))) { + let _e394 = color_10; + let _e395 = gammaCurve; + color_10 = pow(_e394, (vec3(1f, 1f, 1f) / _e395)); + } + let _e398 = gain; + let _e399 = color_10; + color_10 = (_e399 * _e398); + let _e403 = composite_info.output_encode[1u]; + balance = _e403; + let _e406 = composite_info.output_encode[2u]; + tint = _e406; + let _e407 = balance; + let _e409 = tint; + if ((_e407 != 0f) || (_e409 != 0f)) { + let _e412 = balance; + let _e415 = tint; + let _e418 = balance; + let _e422 = color_10; + color_10 = (_e422 * vec3((1f + (_e412 * 0.2f)), (1f + (_e415 * 0.15f)), (1f - (_e418 * 0.2f)))); + let _e424 = tint; + let _e427 = color_10[0u]; + color_10[0u] = (_e427 - (_e424 * 0.075f)); + let _e430 = tint; + let _e433 = color_10[2u]; + color_10[2u] = (_e433 - (_e430 * 0.075f)); + } + let _e438 = composite_info.ao_texel[2u]; + lutStrength = _e438; + let _e439 = lutStrength; + if (_e439 > 0f) { + let _e441 = color_10; + param_14 = clamp(_e441, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e443 = LinearToSrgb_u0028_vf3_u003b((¶m_14)); + encodedIn = _e443; + let _e446 = composite_info.ao_texel[3u]; + let _e448 = encodedIn; + param_15 = _e448; + param_16 = max(_e446, 2f); + let _e449 = SampleLut_u0028_vf3_u003b_f1_u003b((¶m_15), (¶m_16)); + param_17 = _e449; + let _e450 = SrgbToLinear_u0028_vf3_u003b((¶m_17)); + graded = _e450; + let _e451 = color_10; + let _e452 = graded; + let _e453 = lutStrength; + color_10 = mix(_e451, _e452, vec3(clamp(_e453, 0f, 1f))); + } + let _e459 = composite_info.look_more[0u]; + vignette = _e459; + let _e460 = vignette; + if (_e460 > 0f) { + let _e462 = v_uv_1; + fromCentre_2 = (_e462 - vec2(0.5f, 0.5f)); + let _e466 = composite_info.look_more[3u]; + aspect_1 = max(_e466, 0.0001f); + let _e468 = aspect_1; + let _e471 = composite_info.look_more[1u]; + let _e474 = fromCentre_2[0u]; + fromCentre_2[0u] = (_e474 * mix(1f, _e468, _e471)); + let _e477 = fromCentre_2; + radius = (length(_e477) * 1.4142135f); + let _e480 = vignette; + let _e482 = radius; + let _e485 = color_10; + color_10 = (_e485 * mix(1f, (1f - _e480), clamp(_e482, 0f, 1f))); + } + let _e487 = color_10; + param_18 = max(_e487, vec3(0f, 0f, 0f)); + let _e489 = LinearToSrgb_u0028_vf3_u003b((¶m_18)); + encoded_1 = _e489; + let _e492 = composite_info.look_more[2u]; + grain = _e492; + let _e493 = grain; + if (_e493 > 0f) { + let _e495 = TargetFragCoord_u0028_(); + param_19 = _e495; + let _e496 = Hash_u0028_vf2_u003b((¶m_19)); + let _e498 = grain; + let _e501 = encoded_1; + encoded_1 = (_e501 + vec3(((_e496 - 0.5f) * _e498))); + } + let _e505 = composite_info.output_encode[0u]; + dither = _e505; + let _e506 = dither; + if (_e506 > 0f) { + let _e508 = TargetFragCoord_u0028_(); + param_20 = _e508; + let _e509 = BayerCell_u0028_vf2_u003b((¶m_20)); + let _e511 = dither; + let _e514 = encoded_1; + encoded_1 = (_e514 + vec3(((_e509 + 0.03125f) * _e511))); + } + let _e516 = encoded_1; + let _e518 = scene[3u]; + frag_color = vec4(_e516.x, _e516.y, _e516.z, _e518); + let _e525 = composite_info.lens[1u]; + let _e526 = (_e525 > 0.5f); + phi_1262_ = _e526; + if _e526 { + let _e528 = v_uv_1[0u]; + let _e531 = composite_info.lens[2u]; + phi_1262_ = (_e528 >= _e531); + } + let _e534 = phi_1262_; + if _e534 { + let _e535 = v_uv_1; + let _e536 = textureSampleLevel(scene_texture_tex, scene_texture_smp, _e535, 0f); + raw = _e536; + let _e537 = raw; + param_21 = clamp(_e537.xyz, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e540 = LinearToSrgb_u0028_vf3_u003b((¶m_21)); + let _e542 = raw[3u]; + frag_color = vec4(_e540.x, _e540.y, _e540.z, _e542); + } return; } @@ -43782,6 +44723,7 @@ struct FragInfo { ambient_ground: vec4, shadow_bias: vec4, target_origin: vec4, + debug_view: vec4, } struct PointShadow { @@ -43850,10 +44792,10 @@ var v_tangent_1: vec4; var v_lightmap_uv_1: vec2; fn ViewDepth_u0028_() -> f32 { - let _e167 = v_world_position_1; - let _e169 = fog_info.eye; - let _e173 = fog_info.forward; - return dot((_e167 - _e169.xyz), _e173.xyz); + let _e176 = v_world_position_1; + let _e178 = fog_info.eye; + let _e182 = fog_info.forward; + return dot((_e176 - _e178.xyz), _e182.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -43862,22 +44804,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e172 = ViewDepth_u0028_(); - z = abs(_e172); - let _e174 = z; - near = (_e174 / 5f); - let _e176 = z; - far = (_e176 / 200f); - let _e178 = far; - let _e179 = far; - let _e181 = far; - far3_ = ((_e178 * _e179) * _e181); - let _e183 = (*alpha); - let _e184 = near; - let _e185 = near; - let _e188 = far3_; - let _e189 = far3_; - return (_e183 * clamp((10f / ((0.00001f + (_e184 * _e185)) + (_e188 * _e189))), 0.01f, 3000f)); + let _e181 = ViewDepth_u0028_(); + z = abs(_e181); + let _e183 = z; + near = (_e183 / 5f); + let _e185 = z; + far = (_e185 / 200f); + let _e187 = far; + let _e188 = far; + let _e190 = far; + far3_ = ((_e187 * _e188) * _e190); + let _e192 = (*alpha); + let _e193 = near; + let _e194 = near; + let _e197 = far3_; + let _e198 = far3_; + return (_e192 * clamp((10f / ((0.00001f + (_e193 * _e194)) + (_e197 * _e198))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -43889,39 +44831,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e176 = fog_info.forward[3u]; - mode = _e176; - let _e177 = mode; - if (_e177 > 0.5f) { - let _e180 = frag_color[3u]; - alpha_1 = _e180; - let _e181 = alpha_1; - param = _e181; - let _e182 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e182; - let _e183 = frag_color; - let _e185 = weight; - let _e186 = (_e183.xyz * _e185); - let _e187 = alpha_1; - let _e188 = weight; - accumulate = vec4(_e186.x, _e186.y, _e186.z, (_e187 * _e188)); - let _e194 = mode; - let _e196 = mode; - revealage = ((_e194 > 1.5f) && (_e196 < 2.5f)); - let _e199 = revealage; - if _e199 { - let _e200 = alpha_1; - local = vec4(_e200); + let _e185 = fog_info.forward[3u]; + mode = _e185; + let _e186 = mode; + if (_e186 > 0.5f) { + let _e189 = frag_color[3u]; + alpha_1 = _e189; + let _e190 = alpha_1; + param = _e190; + let _e191 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e191; + let _e192 = frag_color; + let _e194 = weight; + let _e195 = (_e192.xyz * _e194); + let _e196 = alpha_1; + let _e197 = weight; + accumulate = vec4(_e195.x, _e195.y, _e195.z, (_e196 * _e197)); + let _e203 = mode; + let _e205 = mode; + revealage = ((_e203 > 1.5f) && (_e205 < 2.5f)); + let _e208 = revealage; + if _e208 { + let _e209 = alpha_1; + local = vec4(_e209); } else { - let _e202 = accumulate; - local = _e202; + let _e211 = accumulate; + local = _e211; } - let _e203 = local; - frag_color = _e203; - let _e204 = mode; - if (_e204 > 2.5f) { - let _e206 = alpha_1; - frag_surface = vec4(_e206); + let _e212 = local; + frag_color = _e212; + let _e213 = mode; + if (_e213 > 2.5f) { + let _e215 = alpha_1; + frag_surface = vec4(_e215); } } return; @@ -43930,29 +44872,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e170 = (*n)[0u]; - let _e173 = (*n)[1u]; - let _e177 = (*n)[2u]; - let _e180 = (*n); - (*n) = (_e180 / vec3(((abs(_e170) + abs(_e173)) + abs(_e177)))); - let _e183 = (*n); - e = _e183.xy; + let _e179 = (*n)[0u]; + let _e182 = (*n)[1u]; let _e186 = (*n)[2u]; - if (_e186 < 0f) { - let _e188 = (*n); - let _e194 = (*n)[0u]; - let _e198 = (*n)[1u]; - e = ((vec2(1f) - abs(_e188.yx)) * vec2(select(-1f, 1f, (_e194 >= 0f)), select(-1f, 1f, (_e198 >= 0f)))); + let _e189 = (*n); + (*n) = (_e189 / vec3(((abs(_e179) + abs(_e182)) + abs(_e186)))); + let _e192 = (*n); + e = _e192.xy; + let _e195 = (*n)[2u]; + if (_e195 < 0f) { + let _e197 = (*n); + let _e203 = (*n)[0u]; + let _e207 = (*n)[1u]; + e = ((vec2(1f) - abs(_e197.yx)) * vec2(select(-1f, 1f, (_e203 >= 0f)), select(-1f, 1f, (_e207 >= 0f)))); } - let _e203 = e; - return ((_e203 * 0.5f) + vec2(0.5f)); + let _e212 = e; + return ((_e212 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e168 = (*linear); - let _e170 = (*linear); - let _e174 = (*linear); - return mix((_e168 * 12.92f), ((pow(_e170, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e174)); + let _e177 = (*linear); + let _e179 = (*linear); + let _e183 = (*linear); + return mix((_e177 * 12.92f), ((pow(_e179, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e183)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -43960,81 +44902,81 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e171 = g_albedo; - param_1 = clamp(_e171, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e173 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e173.x, _e173.y, _e173.z, 1f); - let _e178 = g_debug_surface_on; - if _e178 { - let _e179 = g_debug_surface; - let _e180 = ViewDepth_u0028_(); - frag_surface = vec4(_e179.x, _e179.y, _e179.z, _e180); + let _e180 = g_albedo; + param_1 = clamp(_e180, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e182 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e182.x, _e182.y, _e182.z, 1f); + let _e187 = g_debug_surface_on; + if _e187 { + let _e188 = g_debug_surface; + let _e189 = ViewDepth_u0028_(); + frag_surface = vec4(_e188.x, _e188.y, _e188.z, _e189); return; } - let _e185 = v_normal_1; - geometric = normalize(_e185); - let _e187 = gl_FrontFacing_1; - if !(_e187) { - let _e189 = geometric; - geometric = -(_e189); - } - let _e191 = geometric; - param_2 = _e191; - let _e192 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e193 = (*roughness); - let _e195 = ViewDepth_u0028_(); - frag_surface = vec4(_e192.x, _e192.y, clamp(_e193, 0f, 1f), _e195); - return; -} - -fn EyeDistance_u0028_() -> f32 { - let _e167 = v_world_position_1; - let _e169 = fog_info.eye; - return distance(_e167, _e169.xyz); -} - -fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { - var density: f32; + let _e194 = v_normal_1; + geometric = normalize(_e194); + let _e196 = gl_FrontFacing_1; + if !(_e196) { + let _e198 = geometric; + geometric = -(_e198); + } + let _e200 = geometric; + param_2 = _e200; + let _e201 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e202 = (*roughness); + let _e204 = ViewDepth_u0028_(); + frag_surface = vec4(_e201.x, _e201.y, clamp(_e202, 0f, 1f), _e204); + return; +} + +fn EyeDistance_u0028_() -> f32 { + let _e176 = v_world_position_1; + let _e178 = fog_info.eye; + return distance(_e176, _e178.xyz); +} + +fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { + var density: f32; var d: f32; var falloff: f32; var k: f32; var local_1: f32; - let _e175 = fog_info.fog[3u]; - density = _e175; - let _e176 = density; - if (_e176 <= 0f) { - let _e178 = (*color); - return _e178; - } - let _e179 = EyeDistance_u0028_(); - d = _e179; - let _e182 = fog_info.eye[3u]; - falloff = _e182; - let _e183 = falloff; - if (_e183 > 0f) { - let _e185 = falloff; - let _e187 = v_world_position_1[1u]; - let _e190 = fog_info.eye[1u]; - k = (_e185 * (_e187 - _e190)); - let _e193 = k; - if (abs(_e193) > 0.001f) { - let _e196 = k; - let _e200 = k; - local_1 = ((1f - exp(-(_e196))) / _e200); + let _e184 = fog_info.fog[3u]; + density = _e184; + let _e185 = density; + if (_e185 <= 0f) { + let _e187 = (*color); + return _e187; + } + let _e188 = EyeDistance_u0028_(); + d = _e188; + let _e191 = fog_info.eye[3u]; + falloff = _e191; + let _e192 = falloff; + if (_e192 > 0f) { + let _e194 = falloff; + let _e196 = v_world_position_1[1u]; + let _e199 = fog_info.eye[1u]; + k = (_e194 * (_e196 - _e199)); + let _e202 = k; + if (abs(_e202) > 0.001f) { + let _e205 = k; + let _e209 = k; + local_1 = ((1f - exp(-(_e205))) / _e209); } else { - let _e202 = k; - local_1 = (1f - (0.5f * _e202)); + let _e211 = k; + local_1 = (1f - (0.5f * _e211)); } - let _e205 = local_1; - let _e206 = density; - density = (_e206 * _e205); + let _e214 = local_1; + let _e215 = density; + density = (_e215 * _e214); } - let _e209 = fog_info.fog; - let _e211 = (*color); - let _e212 = density; - let _e214 = d; - return mix(_e209.xyz, _e211, vec3(clamp(exp((-(_e212) * _e214)), 0f, 1f))); + let _e218 = fog_info.fog; + let _e220 = (*color); + let _e221 = density; + let _e223 = d; + return mix(_e218.xyz, _e220, vec3(clamp(exp((-(_e221) * _e223)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -44042,55 +44984,195 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e173 = g_premultiply; - let _e174 = (*alpha_2); - weight_1 = select(1f, _e174, _e173); - let _e176 = (*linearColor); - param_3 = _e176; - let _e177 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e178 = weight_1; - let _e179 = (_e177 * _e178); - let _e180 = (*alpha_2); - frag_color = vec4(_e179.x, _e179.y, _e179.z, _e180); - let _e185 = (*roughness_1); - param_4 = _e185; + let _e182 = g_premultiply; + let _e183 = (*alpha_2); + weight_1 = select(1f, _e183, _e182); + let _e185 = (*linearColor); + param_3 = _e185; + let _e186 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e187 = weight_1; + let _e188 = (_e186 * _e187); + let _e189 = (*alpha_2); + frag_color = vec4(_e188.x, _e188.y, _e188.z, _e189); + let _e194 = (*roughness_1); + param_4 = _e194; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; } -fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s: ptr, light: ptr) -> vec3 { - let _e170 = (*s).albedo; - return _e170; +fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { + let _e177 = (*srgb); + let _e180 = (*srgb); + let _e185 = (*srgb); + return mix((_e177 / vec3(12.92f)), pow(((_e180 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e185)); +} + +fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { + var phi_2694_: bool; + + let _e177 = (*c); + let _e178 = (*c); + let _e180 = any((_e177 != _e178)); + phi_2694_ = _e180; + if !(_e180) { + let _e182 = (*c); + phi_2694_ = any((abs(_e182) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + } + let _e187 = phi_2694_; + return _e187; +} + +fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b(s: ptr, lit: ptr>) -> bool { + var view: f32; + var code: i32; + var shown: vec3; + var param_5: vec3; + var param_6: vec3; + var grey: f32; + var param_7: vec3; + var param_8: vec3; + var local_2: vec3; + var weight_2: f32; + var local_3: f32; + var param_9: vec3; + var param_10: f32; + + let _e193 = frag_info.debug_view[0u]; + view = _e193; + let _e194 = view; + if (_e194 < 0.5f) { + return false; + } + let _e197 = gl_FragCoord_1[0u]; + let _e200 = frag_info.debug_view[1u]; + let _e203 = frag_info.debug_view[2u]; + if (_e197 < (_e200 * _e203)) { + return false; + } + let _e206 = view; + code = i32((_e206 + 0.5f)); + shown = vec3(0f, 0f, 0f); + let _e209 = code; + if (_e209 == 1i) { + let _e212 = (*s).albedo; + param_5 = clamp(_e212, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e214 = LinearToSrgb_u0028_vf3_u003b((¶m_5)); + shown = _e214; + } else { + let _e215 = code; + if (_e215 == 2i) { + let _e218 = (*s).n; + shown = ((_e218 * 0.5f) + vec3(0.5f, 0.5f, 0.5f)); + } else { + let _e221 = code; + if (_e221 == 3i) { + let _e224 = (*s).roughness; + shown = vec3(clamp(_e224, 0f, 1f)); + } else { + let _e227 = code; + if (_e227 == 4i) { + let _e230 = (*s).metallic; + shown = vec3(clamp(_e230, 0f, 1f)); + } else { + let _e233 = code; + if (_e233 == 5i) { + let _e236 = (*s).occlusion; + shown = vec3(clamp(_e236, 0f, 1f)); + } else { + let _e239 = code; + if (_e239 == 6i) { + let _e242 = (*s).emissive; + param_6 = clamp(_e242, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e244 = LinearToSrgb_u0028_vf3_u003b((¶m_6)); + shown = _e244; + } else { + let _e245 = code; + if (_e245 == 7i) { + let _e247 = v_texcoord_1; + let _e248 = fract(_e247); + shown = vec3(_e248.x, _e248.y, 0f); + } else { + let _e252 = code; + if (_e252 == 8i) { + let _e254 = (*lit); + param_7 = clamp(_e254, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e256 = LinearToSrgb_u0028_vf3_u003b((¶m_7)); + grey = dot(_e256, vec3(0.2126f, 0.7152f, 0.0722f)); + let _e258 = (*lit); + param_8 = _e258; + let _e259 = NonFinite_u0028_vf3_u003b((¶m_8)); + if _e259 { + local_2 = vec3(1f, 0f, 1f); + } else { + let _e260 = grey; + local_2 = vec3((_e260 * 0.5f)); + } + let _e263 = local_2; + shown = _e263; + } + } + } + } + } + } + } + } + let _e264 = g_premultiply; + if _e264 { + let _e266 = (*s).alpha; + local_3 = _e266; + } else { + local_3 = 1f; + } + let _e267 = local_3; + weight_2 = _e267; + let _e268 = shown; + param_9 = _e268; + let _e269 = SrgbToLinear_u0028_vf3_u003b((¶m_9)); + let _e270 = weight_2; + let _e271 = (_e269 * _e270); + let _e273 = (*s).alpha; + frag_color = vec4(_e271.x, _e271.y, _e271.z, _e273); + let _e279 = (*s).roughness; + param_10 = _e279; + WriteSurfaceGeometry_u0028_f1_u003b((¶m_10)); + WriteWeightedBlended_u0028_(); + return true; +} + +fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_1: ptr, light: ptr) -> vec3 { + let _e179 = (*s_1).albedo; + return _e179; } fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e168 = (*i); - if (_e168 == 0i) { + let _e177 = (*i); + if (_e177 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e170 = (*i); - if (_e170 == 1i) { + let _e179 = (*i); + if (_e179 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e172 = (*i); - if (_e172 == 2i) { + let _e181 = (*i); + if (_e181 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e174 = (*i); - if (_e174 == 3i) { + let _e183 = (*i); + if (_e183 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e176 = (*i); - if (_e176 == 4i) { + let _e185 = (*i); + if (_e185 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e178 = (*i); - if (_e178 == 5i) { + let _e187 = (*i); + if (_e187 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e180 = (*i); - if (_e180 == 6i) { + let _e189 = (*i); + if (_e189 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -44098,208 +45180,208 @@ fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr, ca: ptr, sa: ptr, radius: ptr) -> vec2 { var p: vec2; - var param_5: i32; - - let _e173 = (*i_1); - param_5 = _e173; - let _e174 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); - p = _e174; - let _e176 = p[0u]; - let _e177 = (*ca); - let _e180 = p[1u]; - let _e181 = (*sa); + var param_11: i32; + + let _e182 = (*i_1); + param_11 = _e182; + let _e183 = PointShadowDiskTap_u0028_i1_u003b((¶m_11)); + p = _e183; let _e185 = p[0u]; - let _e186 = (*sa); + let _e186 = (*ca); let _e189 = p[1u]; - let _e190 = (*ca); - let _e194 = (*radius); - return (vec2(((_e176 * _e177) - (_e180 * _e181)), ((_e185 * _e186) + (_e189 * _e190))) * _e194); + let _e190 = (*sa); + let _e194 = p[0u]; + let _e195 = (*sa); + let _e198 = p[1u]; + let _e199 = (*ca); + let _e203 = (*radius); + return (vec2(((_e185 * _e186) - (_e189 * _e190)), ((_e194 * _e195) + (_e198 * _e199))) * _e203); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_2: vec2; + var local_4: vec2; var atlas: vec2; - let _e176 = point_shadow.params[0u]; - inset = _e176; - let _e177 = (*uv); - let _e178 = (*offset); - let _e180 = inset; - let _e181 = inset; - local_2 = clamp((_e177 + _e178), vec2(_e180), vec2((1f - _e181))); - let _e186 = local_2; - let _e187 = (*tile); - atlas = ((_e186 + _e187) * vec2(0.16666667f, 0.16666667f)); - let _e192 = point_shadow.params3_[0u]; - if (_e192 > 0.5f) { - let _e195 = atlas[1u]; - atlas[1u] = (1f - _e195); - } - let _e198 = atlas; - let _e199 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e198, 0f); - let _e201 = atlas; - let _e202 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e201, 0f); - let _e205 = (*range); - return (min(_e199.x, _e202.x) * _e205); + let _e185 = point_shadow.params[0u]; + inset = _e185; + let _e186 = (*uv); + let _e187 = (*offset); + let _e189 = inset; + let _e190 = inset; + local_4 = clamp((_e186 + _e187), vec2(_e189), vec2((1f - _e190))); + let _e195 = local_4; + let _e196 = (*tile); + atlas = ((_e195 + _e196) * vec2(0.16666667f, 0.16666667f)); + let _e201 = point_shadow.params3_[0u]; + if (_e201 > 0.5f) { + let _e204 = atlas[1u]; + atlas[1u] = (1f - _e204); + } + let _e207 = atlas; + let _e208 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e207, 0f); + let _e210 = atlas; + let _e211 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e210, 0f); + let _e214 = (*range); + return (min(_e208.x, _e211.x) * _e214); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { var stored: f32; - var param_6: vec2; - var param_7: vec2; - var param_8: vec2; - var param_9: f32; + var param_12: vec2; + var param_13: vec2; + var param_14: vec2; + var param_15: f32; - let _e177 = (*uv_1); - param_6 = _e177; - let _e178 = (*offset_1); - param_7 = _e178; - let _e179 = (*tile_1); - param_8 = _e179; - let _e180 = (*range_1); - param_9 = _e180; - let _e181 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); - stored = _e181; - let _e182 = stored; - let _e183 = (*range_1); - if (_e182 >= (_e183 * 0.999f)) { + let _e186 = (*uv_1); + param_12 = _e186; + let _e187 = (*offset_1); + param_13 = _e187; + let _e188 = (*tile_1); + param_14 = _e188; + let _e189 = (*range_1); + param_15 = _e189; + let _e190 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_12), (¶m_13), (¶m_14), (¶m_15)); + stored = _e190; + let _e191 = stored; + let _e192 = (*range_1); + if (_e191 >= (_e192 * 0.999f)) { return 1f; } - let _e186 = (*receiver); - let _e187 = stored; - return select(1f, 0f, (_e186 > _e187)); + let _e195 = (*receiver); + let _e196 = stored; + return select(1f, 0f, (_e195 > _e196)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { var lightRadius: f32; var minRadius: f32; var maxRadius: f32; - var param_10: vec2; - var param_11: vec2; - var param_12: vec2; - var param_13: f32; + var param_16: vec2; + var param_17: vec2; + var param_18: vec2; + var param_19: f32; var sum: f32; var count: f32; var i_2: i32; var stored_1: f32; - var param_14: i32; - var param_15: f32; - var param_16: f32; - var param_17: f32; - var param_18: vec2; - var param_19: vec2; - var param_20: vec2; + var param_20: i32; var param_21: f32; + var param_22: f32; + var param_23: f32; + var param_24: vec2; + var param_25: vec2; + var param_26: vec2; + var param_27: f32; var blocker: f32; var world: f32; (*blockerOut) = -1f; - let _e198 = point_shadow.params2_[1u]; - lightRadius = _e198; - let _e201 = point_shadow.params2_[0u]; - minRadius = _e201; - let _e204 = point_shadow.params2_[2u]; - maxRadius = _e204; - let _e205 = lightRadius; - if (_e205 <= 0f) { - let _e207 = (*uv_2); - param_10 = _e207; - param_11 = vec2(0f, 0f); - let _e208 = (*tile_2); - param_12 = _e208; - let _e209 = (*range_2); - param_13 = _e209; - let _e210 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); - (*blockerOut) = _e210; - let _e211 = minRadius; - return _e211; + let _e207 = point_shadow.params2_[1u]; + lightRadius = _e207; + let _e210 = point_shadow.params2_[0u]; + minRadius = _e210; + let _e213 = point_shadow.params2_[2u]; + maxRadius = _e213; + let _e214 = lightRadius; + if (_e214 <= 0f) { + let _e216 = (*uv_2); + param_16 = _e216; + param_17 = vec2(0f, 0f); + let _e217 = (*tile_2); + param_18 = _e217; + let _e218 = (*range_2); + param_19 = _e218; + let _e219 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_16), (¶m_17), (¶m_18), (¶m_19)); + (*blockerOut) = _e219; + let _e220 = minRadius; + return _e220; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e212 = i_2; - if (_e212 < 8i) { - let _e214 = i_2; - param_14 = _e214; - let _e215 = (*ca_1); - param_15 = _e215; - let _e216 = (*sa_1); - param_16 = _e216; - let _e217 = maxRadius; - param_17 = _e217; - let _e218 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); - let _e219 = (*uv_2); - param_18 = _e219; - param_19 = _e218; - let _e220 = (*tile_2); - param_20 = _e220; - let _e221 = (*range_2); - param_21 = _e221; - let _e222 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); - stored_1 = _e222; - let _e223 = stored_1; - let _e224 = (*range_2); - if (_e223 >= (_e224 * 0.999f)) { + let _e221 = i_2; + if (_e221 < 8i) { + let _e223 = i_2; + param_20 = _e223; + let _e224 = (*ca_1); + param_21 = _e224; + let _e225 = (*sa_1); + param_22 = _e225; + let _e226 = maxRadius; + param_23 = _e226; + let _e227 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_20), (¶m_21), (¶m_22), (¶m_23)); + let _e228 = (*uv_2); + param_24 = _e228; + param_25 = _e227; + let _e229 = (*tile_2); + param_26 = _e229; + let _e230 = (*range_2); + param_27 = _e230; + let _e231 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_24), (¶m_25), (¶m_26), (¶m_27)); + stored_1 = _e231; + let _e232 = stored_1; + let _e233 = (*range_2); + if (_e232 >= (_e233 * 0.999f)) { continue; } - let _e227 = stored_1; - let _e228 = (*receiver_1); - if (_e227 >= _e228) { + let _e236 = stored_1; + let _e237 = (*receiver_1); + if (_e236 >= _e237) { continue; } - let _e230 = stored_1; - let _e231 = sum; - sum = (_e231 + _e230); - let _e233 = count; - count = (_e233 + 1f); + let _e239 = stored_1; + let _e240 = sum; + sum = (_e240 + _e239); + let _e242 = count; + count = (_e242 + 1f); continue; } else { break; } continuing { - let _e235 = i_2; - i_2 = (_e235 + 1i); + let _e244 = i_2; + i_2 = (_e244 + 1i); } } - let _e237 = count; - if (_e237 < 0.5f) { + let _e246 = count; + if (_e246 < 0.5f) { return -1f; } - let _e239 = sum; - let _e240 = count; - blocker = max((_e239 / _e240), 0.0001f); - let _e243 = blocker; - (*blockerOut) = _e243; - let _e244 = lightRadius; - let _e245 = (*receiver_1); - let _e246 = blocker; - let _e250 = blocker; - world = ((_e244 * max((_e245 - _e246), 0f)) / _e250); - let _e252 = world; - let _e253 = (*receiver_1); - let _e255 = (*tanHalf); - let _e258 = minRadius; - let _e259 = maxRadius; - return clamp((_e252 / ((2f * _e253) * _e255)), _e258, _e259); + let _e248 = sum; + let _e249 = count; + blocker = max((_e248 / _e249), 0.0001f); + let _e252 = blocker; + (*blockerOut) = _e252; + let _e253 = lightRadius; + let _e254 = (*receiver_1); + let _e255 = blocker; + let _e259 = blocker; + world = ((_e253 * max((_e254 - _e255), 0f)) / _e259); + let _e261 = world; + let _e262 = (*receiver_1); + let _e264 = (*tanHalf); + let _e267 = minRadius; + let _e268 = maxRadius; + return clamp((_e261 / ((2f * _e262) * _e264)), _e267, _e268); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_3: vec2; + var local_5: vec2; - let _e169 = (*rows); - if (_e169 > 0f) { - let _e172 = gl_FragCoord_1[0u]; - let _e173 = (*rows); - let _e175 = gl_FragCoord_1[1u]; - local_3 = vec2(_e172, (_e173 - _e175)); + let _e178 = (*rows); + if (_e178 > 0f) { + let _e181 = gl_FragCoord_1[0u]; + let _e182 = (*rows); + let _e184 = gl_FragCoord_1[1u]; + local_5 = vec2(_e181, (_e182 - _e184)); } else { - let _e178 = gl_FragCoord_1; - local_3 = _e178.xy; + let _e187 = gl_FragCoord_1; + local_5 = _e187.xy; } - let _e180 = local_3; - return _e180; + let _e189 = local_5; + return _e189; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -44322,292 +45404,292 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var receiver_2: f32; var noise: f32; - var param_22: f32; + var param_28: f32; var angle: f32; var ca_2: f32; var sa_2: f32; var tanHalf_1: f32; var blocker_1: f32; var radius_1: f32; - var param_23: vec2; - var param_24: vec2; - var param_25: f32; - var param_26: f32; - var param_27: f32; - var param_28: f32; - var param_29: f32; - var param_30: f32; - var local_4: vec3; - var lit: f32; - var param_31: vec2; - var param_32: vec2; - var param_33: vec2; + var param_29: vec2; + var param_30: vec2; + var param_31: f32; + var param_32: f32; + var param_33: f32; var param_34: f32; var param_35: f32; + var param_36: f32; + var local_6: vec3; + var lit_1: f32; + var param_37: vec2; + var param_38: vec2; + var param_39: vec2; + var param_40: f32; + var param_41: f32; var i_3: i32; - var param_36: i32; - var param_37: f32; - var param_38: f32; - var param_39: f32; - var param_40: vec2; - var param_41: vec2; - var param_42: vec2; + var param_42: i32; var param_43: f32; var param_44: f32; - var phi_2374_: bool; - var phi_2435_: bool; - - let _e221 = (*lightIndex); - let _e225 = point_shadow.slots[_e221][0u]; - slot = i32((_e225 + 0.5f)); - let _e228 = (*lightIndex); - let _e232 = point_shadow.slots[_e228][0u]; - if (_e232 < 0f) { + var param_45: f32; + var param_46: vec2; + var param_47: vec2; + var param_48: vec2; + var param_49: f32; + var param_50: f32; + var phi_2383_: bool; + var phi_2444_: bool; + + let _e230 = (*lightIndex); + let _e234 = point_shadow.slots[_e230][0u]; + slot = i32((_e234 + 0.5f)); + let _e237 = (*lightIndex); + let _e241 = point_shadow.slots[_e237][0u]; + if (_e241 < 0f) { return 1f; } - let _e236 = point_shadow.params[2u]; - strength = _e236; - let _e237 = strength; - if (_e237 <= 0f) { + let _e245 = point_shadow.params[2u]; + strength = _e245; + let _e246 = strength; + if (_e246 <= 0f) { return 1f; } - let _e239 = slot; - let _e242 = point_shadow.lights[_e239]; - let _e244 = (*world_1); - toLight = (_e242.xyz - _e244); - let _e246 = toLight; - toLightLength = max(length(_e246), 0.000001f); - let _e249 = (*normal); - let _e250 = toLight; - let _e251 = toLightLength; - nDotL = max(dot(_e249, (_e250 / vec3(_e251))), 0.15f); - let _e256 = nDotL; - let _e257 = nDotL; - let _e262 = nDotL; - let _e263 = nDotL; - slope = min((sqrt(max((1f - (_e256 * _e257)), 0f)) / (_e262 * _e263)), 8f); - let _e267 = toLightLength; - let _e269 = (*lightIndex); - let _e273 = point_shadow.slots[_e269][2u]; - let _e278 = point_shadow.params3_[1u]; - texel = (((2f * _e267) * max(_e273, 0.0001f)) * _e278); - let _e280 = (*world_1); - let _e281 = (*normal); - let _e282 = texel; - let _e286 = point_shadow.params[3u]; - let _e288 = slope; - origin = (_e280 + (((_e281 * _e282) * _e286) * (1f + _e288))); - let _e292 = origin; - let _e293 = slot; - let _e296 = point_shadow.lights[_e293]; - toFragment = (_e292 - _e296.xyz); - let _e299 = toFragment; - distance_ = length(_e299); - let _e301 = slot; - let _e305 = point_shadow.lights[_e301][3u]; - range_3 = max(_e305, 0.0001f); - let _e307 = distance_; - let _e308 = range_3; - if (_e307 >= _e308) { + let _e248 = slot; + let _e251 = point_shadow.lights[_e248]; + let _e253 = (*world_1); + toLight = (_e251.xyz - _e253); + let _e255 = toLight; + toLightLength = max(length(_e255), 0.000001f); + let _e258 = (*normal); + let _e259 = toLight; + let _e260 = toLightLength; + nDotL = max(dot(_e258, (_e259 / vec3(_e260))), 0.15f); + let _e265 = nDotL; + let _e266 = nDotL; + let _e271 = nDotL; + let _e272 = nDotL; + slope = min((sqrt(max((1f - (_e265 * _e266)), 0f)) / (_e271 * _e272)), 8f); + let _e276 = toLightLength; + let _e278 = (*lightIndex); + let _e282 = point_shadow.slots[_e278][2u]; + let _e287 = point_shadow.params3_[1u]; + texel = (((2f * _e276) * max(_e282, 0.0001f)) * _e287); + let _e289 = (*world_1); + let _e290 = (*normal); + let _e291 = texel; + let _e295 = point_shadow.params[3u]; + let _e297 = slope; + origin = (_e289 + (((_e290 * _e291) * _e295) * (1f + _e297))); + let _e301 = origin; + let _e302 = slot; + let _e305 = point_shadow.lights[_e302]; + toFragment = (_e301 - _e305.xyz); + let _e308 = toFragment; + distance_ = length(_e308); + let _e310 = slot; + let _e314 = point_shadow.lights[_e310][3u]; + range_3 = max(_e314, 0.0001f); + let _e316 = distance_; + let _e317 = range_3; + if (_e316 >= _e317) { return 1f; } face = 0i; - let _e310 = (*lightIndex); - let _e314 = point_shadow.slots[_e310][1u]; - if (_e314 < 0.5f) { - let _e316 = toFragment; - a = abs(_e316); - let _e319 = a[0u]; - let _e321 = a[1u]; - let _e322 = (_e319 >= _e321); - phi_2374_ = _e322; - if _e322 { - let _e324 = a[0u]; - let _e326 = a[2u]; - phi_2374_ = (_e324 >= _e326); - } - let _e329 = phi_2374_; - if _e329 { - let _e331 = toFragment[0u]; - face = select(1i, 0i, (_e331 > 0f)); + let _e319 = (*lightIndex); + let _e323 = point_shadow.slots[_e319][1u]; + if (_e323 < 0.5f) { + let _e325 = toFragment; + a = abs(_e325); + let _e328 = a[0u]; + let _e330 = a[1u]; + let _e331 = (_e328 >= _e330); + phi_2383_ = _e331; + if _e331 { + let _e333 = a[0u]; + let _e335 = a[2u]; + phi_2383_ = (_e333 >= _e335); + } + let _e338 = phi_2383_; + if _e338 { + let _e340 = toFragment[0u]; + face = select(1i, 0i, (_e340 > 0f)); } else { - let _e335 = a[1u]; - let _e337 = a[2u]; - if (_e335 >= _e337) { - let _e340 = toFragment[1u]; - face = select(3i, 2i, (_e340 > 0f)); + let _e344 = a[1u]; + let _e346 = a[2u]; + if (_e344 >= _e346) { + let _e349 = toFragment[1u]; + face = select(3i, 2i, (_e349 > 0f)); } else { - let _e344 = toFragment[2u]; - face = select(5i, 4i, (_e344 > 0f)); + let _e353 = toFragment[2u]; + face = select(5i, 4i, (_e353 > 0f)); } } } - let _e347 = slot; - let _e349 = face; - let _e353 = point_shadow.faces[((_e347 * 6i) + _e349)]; - let _e354 = origin; - clip = (_e353 * vec4(_e354.x, _e354.y, _e354.z, 1f)); - let _e361 = clip[3u]; - if (_e361 <= 0f) { + let _e356 = slot; + let _e358 = face; + let _e362 = point_shadow.faces[((_e356 * 6i) + _e358)]; + let _e363 = origin; + clip = (_e362 * vec4(_e363.x, _e363.y, _e363.z, 1f)); + let _e370 = clip[3u]; + if (_e370 <= 0f) { return 1f; } - let _e363 = clip; - let _e366 = clip[3u]; - ndc = (_e363.xy / vec2(_e366)); - let _e370 = ndc[0u]; - let _e372 = (abs(_e370) > 1f); - phi_2435_ = _e372; - if !(_e372) { - let _e375 = ndc[1u]; - phi_2435_ = (abs(_e375) > 1f); - } - let _e379 = phi_2435_; - if _e379 { + let _e372 = clip; + let _e375 = clip[3u]; + ndc = (_e372.xy / vec2(_e375)); + let _e379 = ndc[0u]; + let _e381 = (abs(_e379) > 1f); + phi_2444_ = _e381; + if !(_e381) { + let _e384 = ndc[1u]; + phi_2444_ = (abs(_e384) > 1f); + } + let _e388 = phi_2444_; + if _e388 { return 1f; } - let _e381 = ndc[0u]; - let _e385 = ndc[1u]; - uv_3 = vec2(((_e381 * 0.5f) + 0.5f), (0.5f - (_e385 * 0.5f))); - let _e389 = face; - let _e391 = slot; - tile_3 = vec2(f32(_e389), f32(_e391)); - let _e394 = distance_; - let _e397 = point_shadow.params[1u]; - receiver_2 = (_e394 - _e397); - let _e401 = frag_info.target_origin[0u]; - param_22 = _e401; - let _e402 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); - noise = fract((52.982918f * fract(dot(_e402, vec2(0.06711056f, 0.00583715f))))); - let _e407 = noise; - angle = (_e407 * 6.2831855f); - let _e409 = angle; - ca_2 = cos(_e409); - let _e411 = angle; - sa_2 = sin(_e411); - let _e413 = (*lightIndex); - let _e417 = point_shadow.slots[_e413][2u]; - tanHalf_1 = max(_e417, 0.0001f); + let _e390 = ndc[0u]; + let _e394 = ndc[1u]; + uv_3 = vec2(((_e390 * 0.5f) + 0.5f), (0.5f - (_e394 * 0.5f))); + let _e398 = face; + let _e400 = slot; + tile_3 = vec2(f32(_e398), f32(_e400)); + let _e403 = distance_; + let _e406 = point_shadow.params[1u]; + receiver_2 = (_e403 - _e406); + let _e410 = frag_info.target_origin[0u]; + param_28 = _e410; + let _e411 = FragCoordFromTop_u0028_f1_u003b((¶m_28)); + noise = fract((52.982918f * fract(dot(_e411, vec2(0.06711056f, 0.00583715f))))); + let _e416 = noise; + angle = (_e416 * 6.2831855f); + let _e418 = angle; + ca_2 = cos(_e418); + let _e420 = angle; + sa_2 = sin(_e420); + let _e422 = (*lightIndex); + let _e426 = point_shadow.slots[_e422][2u]; + tanHalf_1 = max(_e426, 0.0001f); blocker_1 = -1f; - let _e419 = uv_3; - param_23 = _e419; - let _e420 = tile_3; - param_24 = _e420; - let _e421 = range_3; - param_25 = _e421; - let _e422 = receiver_2; - param_26 = _e422; - let _e423 = ca_2; - param_27 = _e423; - let _e424 = sa_2; - param_28 = _e424; - let _e425 = tanHalf_1; - param_29 = _e425; - let _e426 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e427 = param_30; - blocker_1 = _e427; - radius_1 = _e426; - let _e430 = point_shadow.params2_[3u]; - if (_e430 > 0.5f) { + let _e428 = uv_3; + param_29 = _e428; + let _e429 = tile_3; + param_30 = _e429; + let _e430 = range_3; + param_31 = _e430; + let _e431 = receiver_2; + param_32 = _e431; + let _e432 = ca_2; + param_33 = _e432; + let _e433 = sa_2; + param_34 = _e433; + let _e434 = tanHalf_1; + param_35 = _e434; + let _e435 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_29), (¶m_30), (¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35), (¶m_36)); + let _e436 = param_36; + blocker_1 = _e436; + radius_1 = _e435; + let _e439 = point_shadow.params2_[3u]; + if (_e439 > 0.5f) { g_debug_surface_on = true; - let _e432 = radius_1; - if (_e432 < 0f) { - local_4 = vec3(0f, 0f, 1f); + let _e441 = radius_1; + if (_e441 < 0f) { + local_6 = vec3(0f, 0f, 1f); } else { - let _e434 = radius_1; - let _e437 = point_shadow.params2_[2u]; - let _e441 = blocker_1; - let _e442 = range_3; - local_4 = vec3(clamp((_e434 / max(_e437, 0.000001f)), 0f, 1f), clamp((_e441 / _e442), 0f, 1f), 0f); + let _e443 = radius_1; + let _e446 = point_shadow.params2_[2u]; + let _e450 = blocker_1; + let _e451 = range_3; + local_6 = vec3(clamp((_e443 / max(_e446, 0.000001f)), 0f, 1f), clamp((_e450 / _e451), 0f, 1f), 0f); } - let _e446 = local_4; - g_debug_surface = _e446; + let _e455 = local_6; + g_debug_surface = _e455; } - let _e447 = radius_1; - if (_e447 < 0f) { + let _e456 = radius_1; + if (_e456 < 0f) { return 1f; } - let _e449 = uv_3; - param_31 = _e449; - param_32 = vec2(0f, 0f); - let _e450 = tile_3; - param_33 = _e450; - let _e451 = range_3; - param_34 = _e451; - let _e452 = receiver_2; - param_35 = _e452; - let _e453 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); - lit = _e453; - let _e454 = radius_1; - if (_e454 > 0f) { + let _e458 = uv_3; + param_37 = _e458; + param_38 = vec2(0f, 0f); + let _e459 = tile_3; + param_39 = _e459; + let _e460 = range_3; + param_40 = _e460; + let _e461 = receiver_2; + param_41 = _e461; + let _e462 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_37), (¶m_38), (¶m_39), (¶m_40), (¶m_41)); + lit_1 = _e462; + let _e463 = radius_1; + if (_e463 > 0f) { i_3 = 0i; loop { - let _e456 = i_3; - if (_e456 < 8i) { - let _e458 = i_3; - param_36 = _e458; - let _e459 = ca_2; - param_37 = _e459; - let _e460 = sa_2; - param_38 = _e460; - let _e461 = radius_1; - param_39 = _e461; - let _e462 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - let _e463 = uv_3; - param_40 = _e463; - param_41 = _e462; - let _e464 = tile_3; - param_42 = _e464; - let _e465 = range_3; - param_43 = _e465; - let _e466 = receiver_2; - param_44 = _e466; - let _e467 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); - let _e468 = lit; - lit = (_e468 + _e467); + let _e465 = i_3; + if (_e465 < 8i) { + let _e467 = i_3; + param_42 = _e467; + let _e468 = ca_2; + param_43 = _e468; + let _e469 = sa_2; + param_44 = _e469; + let _e470 = radius_1; + param_45 = _e470; + let _e471 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_42), (¶m_43), (¶m_44), (¶m_45)); + let _e472 = uv_3; + param_46 = _e472; + param_47 = _e471; + let _e473 = tile_3; + param_48 = _e473; + let _e474 = range_3; + param_49 = _e474; + let _e475 = receiver_2; + param_50 = _e475; + let _e476 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_46), (¶m_47), (¶m_48), (¶m_49), (¶m_50)); + let _e477 = lit_1; + lit_1 = (_e477 + _e476); continue; } else { break; } continuing { - let _e470 = i_3; - i_3 = (_e470 + 1i); + let _e479 = i_3; + i_3 = (_e479 + 1i); } } - let _e472 = lit; - lit = (_e472 * 0.11111111f); + let _e481 = lit_1; + lit_1 = (_e481 * 0.11111111f); } - let _e474 = lit; - let _e475 = strength; - return mix(1f, _e474, clamp(_e475, 0f, 1f)); + let _e483 = lit_1; + let _e484 = strength; + return mix(1f, _e483, clamp(_e484, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r: f32; var theta: f32; - let _e172 = (*i_4); - let _e175 = (*n_1); - r = sqrt(((f32(_e172) + 0.5f) / f32(_e175))); - let _e179 = (*i_4); - let _e182 = (*turn); - theta = ((f32(_e179) * 2.3999631f) + _e182); - let _e184 = r; - let _e185 = theta; - let _e187 = theta; - return (vec2(cos(_e185), sin(_e187)) * _e184); + let _e181 = (*i_4); + let _e184 = (*n_1); + r = sqrt(((f32(_e181) + 0.5f) / f32(_e184))); + let _e188 = (*i_4); + let _e191 = (*turn); + theta = ((f32(_e188) * 2.3999631f) + _e191); + let _e193 = r; + let _e194 = theta; + let _e196 = theta; + return (vec2(cos(_e194), sin(_e196)) * _e193); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; - var param_45: f32; + var param_51: f32; - let _e171 = frag_info.target_origin[0u]; - param_45 = _e171; - let _e172 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); - let _e175 = frag_info.target_origin[3u]; - at = (_e172 + vec2((5.588238f * max(_e175, 0f)))); - let _e180 = at; - return fract((52.982918f * fract(dot(_e180, vec2(0.06711056f, 0.00583715f))))); + let _e180 = frag_info.target_origin[0u]; + param_51 = _e180; + let _e181 = FragCoordFromTop_u0028_f1_u003b((¶m_51)); + let _e184 = frag_info.target_origin[3u]; + at = (_e181 + vec2((5.588238f * max(_e184, 0f)))); + let _e189 = at; + return fract((52.982918f * fract(dot(_e189, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -44616,88 +45698,88 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_2: f32; - let _e169 = (*depth); - d_2 = ((2f * clamp(_e169, 0f, 1f)) - 1f); - let _e173 = d_2; - let _e176 = d_2; - return vec2(exp((40f * _e173)), -(exp((-5f * _e176)))); + let _e178 = (*depth); + d_2 = ((2f * clamp(_e178, 0f, 1f)) - 1f); + let _e182 = d_2; + let _e185 = d_2; + return vec2(exp((40f * _e182)), -(exp((-5f * _e185)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { var warped: vec2; - var param_46: f32; + var param_52: f32; var scale: vec2; var positive: f32; - var param_47: vec2; - var param_48: f32; - var param_49: f32; - var param_50: f32; - var negative: f32; - var param_51: vec2; - var param_52: f32; - var param_53: f32; + var param_53: vec2; var param_54: f32; + var param_55: f32; + var param_56: f32; + var negative: f32; + var param_57: vec2; + var param_58: f32; + var param_59: f32; + var param_60: f32; + + let _e192 = (*depth_1); + param_52 = _e192; + let _e193 = EvsmWarp_u0028_f1_u003b((¶m_52)); + warped = _e193; + let _e194 = warped; + scale = (vec2(0.004f, 0.0005f) * _e194); + let _e197 = scale[0u]; + let _e199 = scale[0u]; + let _e201 = (*moments_1); + param_53 = _e201.xy; + let _e204 = warped[0u]; + param_54 = _e204; + param_55 = (_e197 * _e199); + let _e205 = (*bleed_1); + param_56 = _e205; + let _e206 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_53), (¶m_54), (¶m_55), (¶m_56)); + positive = _e206; + let _e208 = scale[1u]; + let _e210 = scale[1u]; + let _e212 = (*moments_1); + param_57 = _e212.zw; + let _e215 = warped[1u]; + param_58 = _e215; + param_59 = (_e208 * _e210); + let _e216 = (*bleed_1); + param_60 = _e216; + let _e217 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_57), (¶m_58), (¶m_59), (¶m_60)); + negative = _e217; + let _e218 = positive; + let _e219 = negative; + return min(_e218, _e219); +} - let _e183 = (*depth_1); - param_46 = _e183; - let _e184 = EvsmWarp_u0028_f1_u003b((¶m_46)); - warped = _e184; - let _e185 = warped; - scale = (vec2(0.004f, 0.0005f) * _e185); - let _e188 = scale[0u]; - let _e190 = scale[0u]; - let _e192 = (*moments_1); - param_47 = _e192.xy; - let _e195 = warped[0u]; - param_48 = _e195; - param_49 = (_e188 * _e190); - let _e196 = (*bleed_1); - param_50 = _e196; - let _e197 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); - positive = _e197; - let _e199 = scale[1u]; - let _e201 = scale[1u]; - let _e203 = (*moments_1); - param_51 = _e203.zw; - let _e206 = warped[1u]; - param_52 = _e206; - param_53 = (_e199 * _e201); - let _e207 = (*bleed_1); - param_54 = _e207; - let _e208 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); - negative = _e208; - let _e209 = positive; - let _e210 = negative; - return min(_e209, _e210); -} - -fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_1: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { +fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { var strength_1: f32; var cascadeCount: i32; var viewDistance: f32; @@ -44711,8 +45793,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_5: mat4x4; - var local_6: mat4x4; + var local_7: mat4x4; + var local_8: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -44726,17 +45808,17 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_7: f32; - var local_8: f32; + var local_9: f32; + var local_10: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; var softness: f32; - var lit_1: f32; + var lit_2: f32; var moments_2: vec4; - var param_55: vec4; - var param_56: f32; - var param_57: f32; + var param_61: vec4; + var param_62: f32; + var param_63: f32; var y: i32; var x: i32; var occluder: f32; @@ -44747,65 +45829,65 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var blockerCount: f32; var i_5: i32; var disc: vec2; - var param_58: i32; - var param_59: i32; - var param_60: f32; + var param_64: i32; + var param_65: i32; + var param_66: f32; var tapBias: f32; var occluder_1: f32; var gap: f32; var radius_2: f32; var i_6: i32; var disc_1: vec2; - var param_61: i32; - var param_62: i32; - var param_63: f32; + var param_67: i32; + var param_68: i32; + var param_69: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_2754_: bool; - var phi_2765_: bool; - var phi_2926_: bool; - var phi_2933_: bool; - var phi_2940_: bool; - - let _e235 = frag_info.shadow_params[3u]; - strength_1 = _e235; - let _e236 = strength_1; - if (_e236 <= 0f) { + var phi_2924_: bool; + var phi_2935_: bool; + var phi_3096_: bool; + var phi_3103_: bool; + var phi_3110_: bool; + + let _e244 = frag_info.shadow_params[3u]; + strength_1 = _e244; + let _e245 = strength_1; + if (_e245 <= 0f) { return 1f; } - let _e238 = (*lightIndex_1); - let _e241 = frag_info.frame_params[2u]; - if (_e238 != i32((_e241 + 0.5f))) { + let _e247 = (*lightIndex_1); + let _e250 = frag_info.frame_params[2u]; + if (_e247 != i32((_e250 + 0.5f))) { return 1f; } - let _e247 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e247 + 0.5f)); - let _e250 = v_world_position_1; - let _e252 = frag_info.camera_position; - viewDistance = length((_e250 - _e252.xyz)); + let _e256 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e256 + 0.5f)); + let _e259 = v_world_position_1; + let _e261 = frag_info.camera_position; + viewDistance = length((_e259 - _e261.xyz)); cascade = 0i; - let _e256 = cascadeCount; - let _e257 = (_e256 > 1i); - phi_2754_ = _e257; - if _e257 { - let _e258 = viewDistance; - let _e261 = frag_info.shadow_cascades[0u]; - phi_2754_ = (_e258 > _e261); - } - let _e264 = phi_2754_; - if _e264 { - cascade = 1i; - } let _e265 = cascadeCount; - let _e266 = (_e265 > 2i); - phi_2765_ = _e266; + let _e266 = (_e265 > 1i); + phi_2924_ = _e266; if _e266 { let _e267 = viewDistance; - let _e270 = frag_info.shadow_cascades[1u]; - phi_2765_ = (_e267 > _e270); + let _e270 = frag_info.shadow_cascades[0u]; + phi_2924_ = (_e267 > _e270); } - let _e273 = phi_2765_; + let _e273 = phi_2924_; if _e273 { + cascade = 1i; + } + let _e274 = cascadeCount; + let _e275 = (_e274 > 2i); + phi_2935_ = _e275; + if _e275 { + let _e276 = viewDistance; + let _e279 = frag_info.shadow_cascades[1u]; + phi_2935_ = (_e276 > _e279); + } + let _e282 = phi_2935_; + if _e282 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -44816,232 +45898,232 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e274 = attempt; - if (_e274 < 3i) { - let _e276 = cascade; - let _e277 = attempt; - which = (_e276 + _e277); - let _e279 = which; - let _e280 = cascadeCount; - if (_e279 >= _e280) { + let _e283 = attempt; + if (_e283 < 3i) { + let _e285 = cascade; + let _e286 = attempt; + which = (_e285 + _e286); + let _e288 = which; + let _e289 = cascadeCount; + if (_e288 >= _e289) { break; } - let _e282 = which; - if (_e282 == 0i) { - let _e285 = frag_info.shadow_matrix; - local_5 = _e285; + let _e291 = which; + if (_e291 == 0i) { + let _e294 = frag_info.shadow_matrix; + local_7 = _e294; } else { - let _e286 = which; - if (_e286 == 1i) { - let _e289 = frag_info.shadow_matrix_far; - local_6 = _e289; + let _e295 = which; + if (_e295 == 1i) { + let _e298 = frag_info.shadow_matrix_far; + local_8 = _e298; } else { - let _e291 = frag_info.shadow_matrix_farthest; - local_6 = _e291; + let _e300 = frag_info.shadow_matrix_farthest; + local_8 = _e300; } - let _e292 = local_6; - local_5 = _e292; - } - let _e293 = local_5; - matrix = _e293; - let _e296 = matrix[0][0u]; - let _e299 = matrix[1][0u]; - let _e302 = matrix[2][0u]; - axisX = vec3(_e296, _e299, _e302); - let _e306 = matrix[0][1u]; - let _e309 = matrix[1][1u]; - let _e312 = matrix[2][1u]; - axisY = vec3(_e306, _e309, _e312); - let _e314 = axisX; - rowX = max(length(_e314), 0.000001f); - let _e317 = axisY; - rowY = max(length(_e317), 0.000001f); - let _e322 = frag_info.shadow_cascades[3u]; - let _e324 = rowX; - texelMetres = ((2f * _e322) / _e324); - let _e328 = frag_info.shadow_cascades[3u]; - let _e331 = (*s_1).n; - let _e332 = axisX; - let _e335 = rowX; - let _e336 = rowX; - let _e340 = (*s_1).n; - let _e341 = axisY; - let _e344 = rowY; - let _e345 = rowY; - reach = ((3f * _e328) * ((abs(dot(_e331, _e332)) / (_e335 * _e336)) + (abs(dot(_e340, _e341)) / (_e344 * _e345)))); - let _e352 = frag_info.shadow_params[2u]; - let _e353 = texelMetres; - flatOffset = min(_e352, _e353); - let _e355 = v_world_position_1; - let _e357 = (*s_1).n; - let _e358 = flatOffset; - let _e359 = reach; - origin_1 = (_e355 + (_e357 * (_e358 + _e359))); - let _e363 = matrix; - let _e364 = origin_1; - lightSpace = (_e363 * vec4(_e364.x, _e364.y, _e364.z, 1f)); - let _e371 = lightSpace[3u]; - if (_e371 <= 0f) { + let _e301 = local_8; + local_7 = _e301; + } + let _e302 = local_7; + matrix = _e302; + let _e305 = matrix[0][0u]; + let _e308 = matrix[1][0u]; + let _e311 = matrix[2][0u]; + axisX = vec3(_e305, _e308, _e311); + let _e315 = matrix[0][1u]; + let _e318 = matrix[1][1u]; + let _e321 = matrix[2][1u]; + axisY = vec3(_e315, _e318, _e321); + let _e323 = axisX; + rowX = max(length(_e323), 0.000001f); + let _e326 = axisY; + rowY = max(length(_e326), 0.000001f); + let _e331 = frag_info.shadow_cascades[3u]; + let _e333 = rowX; + texelMetres = ((2f * _e331) / _e333); + let _e337 = frag_info.shadow_cascades[3u]; + let _e340 = (*s_2).n; + let _e341 = axisX; + let _e344 = rowX; + let _e345 = rowX; + let _e349 = (*s_2).n; + let _e350 = axisY; + let _e353 = rowY; + let _e354 = rowY; + reach = ((3f * _e337) * ((abs(dot(_e340, _e341)) / (_e344 * _e345)) + (abs(dot(_e349, _e350)) / (_e353 * _e354)))); + let _e361 = frag_info.shadow_params[2u]; + let _e362 = texelMetres; + flatOffset = min(_e361, _e362); + let _e364 = v_world_position_1; + let _e366 = (*s_2).n; + let _e367 = flatOffset; + let _e368 = reach; + origin_1 = (_e364 + (_e366 * (_e367 + _e368))); + let _e372 = matrix; + let _e373 = origin_1; + lightSpace = (_e372 * vec4(_e373.x, _e373.y, _e373.z, 1f)); + let _e380 = lightSpace[3u]; + if (_e380 <= 0f) { continue; } - let _e373 = lightSpace; - let _e376 = lightSpace[3u]; - candidate = (_e373.xyz / vec3(_e376)); - let _e380 = candidate[0u]; - let _e384 = candidate[1u]; - inTile = vec2(((_e380 * 0.5f) + 0.5f), (0.5f - (_e384 * 0.5f))); - let _e389 = inTile[0u]; - let _e390 = (_e389 < 0f); - phi_2926_ = _e390; - if !(_e390) { - let _e393 = inTile[0u]; - phi_2926_ = (_e393 > 1f); - } - let _e396 = phi_2926_; - phi_2933_ = _e396; - if !(_e396) { - let _e399 = inTile[1u]; - phi_2933_ = (_e399 < 0f); - } - let _e402 = phi_2933_; - phi_2940_ = _e402; - if !(_e402) { - let _e405 = inTile[1u]; - phi_2940_ = (_e405 > 1f); - } - let _e408 = phi_2940_; - if _e408 { + let _e382 = lightSpace; + let _e385 = lightSpace[3u]; + candidate = (_e382.xyz / vec3(_e385)); + let _e389 = candidate[0u]; + let _e393 = candidate[1u]; + inTile = vec2(((_e389 * 0.5f) + 0.5f), (0.5f - (_e393 * 0.5f))); + let _e398 = inTile[0u]; + let _e399 = (_e398 < 0f); + phi_3096_ = _e399; + if !(_e399) { + let _e402 = inTile[0u]; + phi_3096_ = (_e402 > 1f); + } + let _e405 = phi_3096_; + phi_3103_ = _e405; + if !(_e405) { + let _e408 = inTile[1u]; + phi_3103_ = (_e408 < 0f); + } + let _e411 = phi_3103_; + phi_3110_ = _e411; + if !(_e411) { + let _e414 = inTile[1u]; + phi_3110_ = (_e414 > 1f); + } + let _e417 = phi_3110_; + if _e417 { continue; } - let _e410 = candidate[2u]; - if (_e410 > 1f) { - let _e412 = which; - let _e413 = cascadeCount; - if (_e412 < (_e413 - 1i)) { + let _e419 = candidate[2u]; + if (_e419 > 1f) { + let _e421 = which; + let _e422 = cascadeCount; + if (_e421 < (_e422 - 1i)) { continue; } candidate[2u] = 1f; } - let _e418 = inTile[0u]; - let _e419 = which; - let _e422 = cascadeCount; - let _e426 = inTile[1u]; - uv_4 = vec2(((_e418 + f32(_e419)) / f32(_e422)), _e426); - let _e428 = candidate; - projected = _e428; - let _e429 = which; - cascade = _e429; - let _e430 = texelMetres; - cascadeTexel = _e430; - let _e433 = matrix[0][2u]; - let _e436 = matrix[1][2u]; - let _e439 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e433, _e436, _e439)), 0.000001f)); - let _e445 = (*s_1).n; - let _e448 = matrix[0][2u]; - let _e451 = matrix[1][2u]; - let _e454 = matrix[2][2u]; - let _e458 = cascadeDepth; - cosSlope = clamp((abs(dot(_e445, vec3(_e448, _e451, _e454))) * _e458), 0.1f, 1f); - let _e461 = texelMetres; - let _e462 = cosSlope; - let _e463 = cosSlope; - let _e469 = cosSlope; - let _e471 = cascadeDepth; - receiverSlope = (((_e461 * sqrt(max((1f - (_e462 * _e463)), 0f))) / _e469) / _e471); + let _e427 = inTile[0u]; + let _e428 = which; + let _e431 = cascadeCount; + let _e435 = inTile[1u]; + uv_4 = vec2(((_e427 + f32(_e428)) / f32(_e431)), _e435); + let _e437 = candidate; + projected = _e437; + let _e438 = which; + cascade = _e438; + let _e439 = texelMetres; + cascadeTexel = _e439; + let _e442 = matrix[0][2u]; + let _e445 = matrix[1][2u]; + let _e448 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e442, _e445, _e448)), 0.000001f)); + let _e454 = (*s_2).n; + let _e457 = matrix[0][2u]; + let _e460 = matrix[1][2u]; + let _e463 = matrix[2][2u]; + let _e467 = cascadeDepth; + cosSlope = clamp((abs(dot(_e454, vec3(_e457, _e460, _e463))) * _e467), 0.1f, 1f); + let _e470 = texelMetres; + let _e471 = cosSlope; + let _e472 = cosSlope; + let _e478 = cosSlope; + let _e480 = cascadeDepth; + receiverSlope = (((_e470 * sqrt(max((1f - (_e471 * _e472)), 0f))) / _e478) / _e480); found = true; break; } else { break; } continuing { - let _e473 = attempt; - attempt = (_e473 + 1i); + let _e482 = attempt; + attempt = (_e482 + 1i); } } - let _e475 = found; - if !(_e475) { + let _e484 = found; + if !(_e484) { return 1f; } - let _e477 = cascade; - if (_e477 == 0i) { - let _e481 = frag_info.shadow_bias[0u]; - local_7 = _e481; + let _e486 = cascade; + if (_e486 == 0i) { + let _e490 = frag_info.shadow_bias[0u]; + local_9 = _e490; } else { - let _e482 = cascade; - if (_e482 == 1i) { - let _e486 = frag_info.shadow_bias[1u]; - local_8 = _e486; + let _e491 = cascade; + if (_e491 == 1i) { + let _e495 = frag_info.shadow_bias[1u]; + local_10 = _e495; } else { - let _e489 = frag_info.shadow_bias[2u]; - local_8 = _e489; - } - let _e490 = local_8; - local_7 = _e490; - } - let _e491 = local_7; - bias = _e491; - let _e494 = frag_info.shadow_params[0u]; - let _e497 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e494, _e497); - let _e499 = cascade; - let _e501 = cascadeCount; - let _e505 = texel_1; - tileLo = (vec2((f32(_e499) / f32(_e501)), 0f) + (_e505 * 0.5f)); + let _e498 = frag_info.shadow_bias[2u]; + local_10 = _e498; + } + let _e499 = local_10; + local_9 = _e499; + } + let _e500 = local_9; + bias = _e500; + let _e503 = frag_info.shadow_params[0u]; + let _e506 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e503, _e506); let _e508 = cascade; - let _e511 = cascadeCount; - let _e515 = texel_1; - tileHi = (vec2((f32((_e508 + 1i)) / f32(_e511)), 1f) - (_e515 * 0.5f)); - let _e520 = frag_info.ambient_ground[3u]; - softness = _e520; - lit_1 = 0f; - let _e521 = softness; - if (_e521 < 0f) { - let _e523 = uv_4; - let _e524 = tileLo; - let _e525 = tileHi; - let _e527 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e523, _e524, _e525), 0f); - moments_2 = _e527; - let _e529 = projected[2u]; - let _e530 = bias; - let _e532 = softness; - let _e536 = moments_2; - param_55 = _e536; - param_56 = (_e529 - _e530); - param_57 = clamp((-(_e532) - 1f), 0f, 0.95f); - let _e537 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); - lit_1 = _e537; + let _e510 = cascadeCount; + let _e514 = texel_1; + tileLo = (vec2((f32(_e508) / f32(_e510)), 0f) + (_e514 * 0.5f)); + let _e517 = cascade; + let _e520 = cascadeCount; + let _e524 = texel_1; + tileHi = (vec2((f32((_e517 + 1i)) / f32(_e520)), 1f) - (_e524 * 0.5f)); + let _e529 = frag_info.ambient_ground[3u]; + softness = _e529; + lit_2 = 0f; + let _e530 = softness; + if (_e530 < 0f) { + let _e532 = uv_4; + let _e533 = tileLo; + let _e534 = tileHi; + let _e536 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e532, _e533, _e534), 0f); + moments_2 = _e536; + let _e538 = projected[2u]; + let _e539 = bias; + let _e541 = softness; + let _e545 = moments_2; + param_61 = _e545; + param_62 = (_e538 - _e539); + param_63 = clamp((-(_e541) - 1f), 0f, 0.95f); + let _e546 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); + lit_2 = _e546; } else { - let _e538 = softness; - if (_e538 <= 0f) { + let _e547 = softness; + if (_e547 <= 0f) { y = -1i; loop { - let _e540 = y; - if (_e540 <= 1i) { + let _e549 = y; + if (_e549 <= 1i) { x = -1i; loop { - let _e542 = x; - if (_e542 <= 1i) { - let _e544 = uv_4; - let _e545 = x; - let _e547 = y; - let _e550 = texel_1; - let _e553 = tileLo; - let _e554 = tileHi; - let _e556 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e544 + (vec2(f32(_e545), f32(_e547)) * _e550)), _e553, _e554), 0f); - occluder = _e556.x; - let _e559 = projected[2u]; - let _e560 = bias; - let _e562 = occluder; - let _e565 = lit_1; - lit_1 = (_e565 + select(1f, 0f, ((_e559 - _e560) > _e562))); + let _e551 = x; + if (_e551 <= 1i) { + let _e553 = uv_4; + let _e554 = x; + let _e556 = y; + let _e559 = texel_1; + let _e562 = tileLo; + let _e563 = tileHi; + let _e565 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e553 + (vec2(f32(_e554), f32(_e556)) * _e559)), _e562, _e563), 0f); + occluder = _e565.x; + let _e568 = projected[2u]; + let _e569 = bias; + let _e571 = occluder; + let _e574 = lit_2; + lit_2 = (_e574 + select(1f, 0f, ((_e568 - _e569) > _e571))); continue; } else { break; } continuing { - let _e567 = x; - x = (_e567 + 1i); + let _e576 = x; + x = (_e576 + 1i); } } continue; @@ -45049,145 +46131,145 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e569 = y; - y = (_e569 + 1i); + let _e578 = y; + y = (_e578 + 1i); } } - let _e571 = lit_1; - lit_1 = (_e571 * 0.11111111f); + let _e580 = lit_2; + lit_2 = (_e580 * 0.11111111f); } else { - let _e573 = softness; - spread = tan(min(_e573, 0.5f)); - let _e576 = ShadowNoise_u0028_(); - turn_1 = (_e576 * 6.2831855f); - let _e578 = spread; - let _e580 = projected[2u]; - let _e582 = cascadeDepth; - let _e584 = cascadeTexel; - searchRadius = clamp((((_e578 * _e580) * _e582) / _e584), 1f, 16f); + let _e582 = softness; + spread = tan(min(_e582, 0.5f)); + let _e585 = ShadowNoise_u0028_(); + turn_1 = (_e585 * 6.2831855f); + let _e587 = spread; + let _e589 = projected[2u]; + let _e591 = cascadeDepth; + let _e593 = cascadeTexel; + searchRadius = clamp((((_e587 * _e589) * _e591) / _e593), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e587 = i_5; - if (_e587 < 16i) { - let _e589 = i_5; - param_58 = _e589; - param_59 = 16i; - let _e590 = turn_1; - param_60 = _e590; - let _e591 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); - disc = _e591; - let _e592 = bias; - let _e593 = disc; - let _e595 = searchRadius; - let _e599 = receiverSlope; - tapBias = (_e592 + (max(((length(_e593) * _e595) - 1.5f), 0f) * _e599)); - let _e602 = uv_4; - let _e603 = disc; - let _e604 = texel_1; - let _e606 = searchRadius; - let _e609 = tileLo; - let _e610 = tileHi; - let _e612 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e602 + ((_e603 * _e604) * _e606)), _e609, _e610), 0f); - occluder_1 = _e612.x; - let _e615 = projected[2u]; - let _e616 = tapBias; - let _e618 = occluder_1; - if ((_e615 - _e616) > _e618) { - let _e620 = occluder_1; - let _e621 = blockerSum; - blockerSum = (_e621 + _e620); - let _e623 = blockerCount; - blockerCount = (_e623 + 1f); + let _e596 = i_5; + if (_e596 < 16i) { + let _e598 = i_5; + param_64 = _e598; + param_65 = 16i; + let _e599 = turn_1; + param_66 = _e599; + let _e600 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_64), (¶m_65), (¶m_66)); + disc = _e600; + let _e601 = bias; + let _e602 = disc; + let _e604 = searchRadius; + let _e608 = receiverSlope; + tapBias = (_e601 + (max(((length(_e602) * _e604) - 1.5f), 0f) * _e608)); + let _e611 = uv_4; + let _e612 = disc; + let _e613 = texel_1; + let _e615 = searchRadius; + let _e618 = tileLo; + let _e619 = tileHi; + let _e621 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e611 + ((_e612 * _e613) * _e615)), _e618, _e619), 0f); + occluder_1 = _e621.x; + let _e624 = projected[2u]; + let _e625 = tapBias; + let _e627 = occluder_1; + if ((_e624 - _e625) > _e627) { + let _e629 = occluder_1; + let _e630 = blockerSum; + blockerSum = (_e630 + _e629); + let _e632 = blockerCount; + blockerCount = (_e632 + 1f); } continue; } else { break; } continuing { - let _e625 = i_5; - i_5 = (_e625 + 1i); + let _e634 = i_5; + i_5 = (_e634 + 1i); } } - let _e627 = blockerCount; - if (_e627 <= 0f) { + let _e636 = blockerCount; + if (_e636 <= 0f) { return 1f; } - let _e630 = projected[2u]; - let _e631 = blockerSum; - let _e632 = blockerCount; - let _e636 = cascadeDepth; - gap = (max((_e630 - (_e631 / _e632)), 0f) * _e636); - let _e638 = spread; - let _e639 = gap; - let _e641 = cascadeTexel; - radius_2 = clamp(((_e638 * _e639) / _e641), 1f, 16f); + let _e639 = projected[2u]; + let _e640 = blockerSum; + let _e641 = blockerCount; + let _e645 = cascadeDepth; + gap = (max((_e639 - (_e640 / _e641)), 0f) * _e645); + let _e647 = spread; + let _e648 = gap; + let _e650 = cascadeTexel; + radius_2 = clamp(((_e647 * _e648) / _e650), 1f, 16f); i_6 = 0i; loop { - let _e644 = i_6; - if (_e644 < 16i) { - let _e646 = turn_1; - let _e648 = i_6; - param_61 = _e648; - param_62 = 16i; - param_63 = (_e646 + 1f); - let _e649 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - disc_1 = _e649; - let _e650 = bias; - let _e651 = disc_1; - let _e653 = radius_2; - let _e657 = receiverSlope; - tapBias_1 = (_e650 + (max(((length(_e651) * _e653) - 1.5f), 0f) * _e657)); - let _e660 = uv_4; - let _e661 = disc_1; - let _e662 = texel_1; - let _e664 = radius_2; - let _e667 = tileLo; - let _e668 = tileHi; - let _e670 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e660 + ((_e661 * _e662) * _e664)), _e667, _e668), 0f); - occluder_2 = _e670.x; - let _e673 = projected[2u]; - let _e674 = tapBias_1; - let _e676 = occluder_2; - let _e679 = lit_1; - lit_1 = (_e679 + select(1f, 0f, ((_e673 - _e674) > _e676))); + let _e653 = i_6; + if (_e653 < 16i) { + let _e655 = turn_1; + let _e657 = i_6; + param_67 = _e657; + param_68 = 16i; + param_69 = (_e655 + 1f); + let _e658 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_67), (¶m_68), (¶m_69)); + disc_1 = _e658; + let _e659 = bias; + let _e660 = disc_1; + let _e662 = radius_2; + let _e666 = receiverSlope; + tapBias_1 = (_e659 + (max(((length(_e660) * _e662) - 1.5f), 0f) * _e666)); + let _e669 = uv_4; + let _e670 = disc_1; + let _e671 = texel_1; + let _e673 = radius_2; + let _e676 = tileLo; + let _e677 = tileHi; + let _e679 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e669 + ((_e670 * _e671) * _e673)), _e676, _e677), 0f); + occluder_2 = _e679.x; + let _e682 = projected[2u]; + let _e683 = tapBias_1; + let _e685 = occluder_2; + let _e688 = lit_2; + lit_2 = (_e688 + select(1f, 0f, ((_e682 - _e683) > _e685))); continue; } else { break; } continuing { - let _e681 = i_6; - i_6 = (_e681 + 1i); + let _e690 = i_6; + i_6 = (_e690 + 1i); } } - let _e683 = lit_1; - lit_1 = (_e683 * 0.0625f); + let _e692 = lit_2; + lit_2 = (_e692 * 0.0625f); } } - let _e685 = lit_1; - let _e686 = strength_1; - return mix(1f, _e685, clamp(_e686, 0f, 1f)); + let _e694 = lit_2; + let _e695 = strength_1; + return mix(1f, _e694, clamp(_e695, 0f, 1f)); } -fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_2: ptr, index: ptr) -> f32 { - var param_64: Surface; - var param_65: LightSample; - var param_66: i32; +fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_3: ptr, light_2: ptr, index: ptr) -> f32 { + var param_70: Surface; + var param_71: LightSample; + var param_72: i32; - let _e173 = (*s_2); - param_64 = _e173; - let _e174 = (*light_2); - param_65 = _e174; - let _e175 = (*index); - param_66 = _e175; - let _e176 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); - return _e176; + let _e182 = (*s_3); + param_70 = _e182; + let _e183 = (*light_2); + param_71 = _e183; + let _e184 = (*index); + param_72 = _e184; + let _e185 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_70), (¶m_71), (¶m_72)); + return _e185; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e168 = (*index_1); - return (_e168 < 8i); + let _e177 = (*index_1); + return (_e177 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -45195,26 +46277,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e172 = (*distance_1); - let _e173 = (*distance_1); - attenuation = (1f / max((_e172 * _e173), 0.0001f)); - let _e177 = (*range_4); - if (_e177 > 0f) { - let _e179 = (*distance_1); - let _e180 = (*range_4); - ratio = (_e179 / _e180); - let _e182 = ratio; - let _e183 = ratio; - let _e185 = ratio; - let _e187 = ratio; - window = clamp((1f - (((_e182 * _e183) * _e185) * _e187)), 0f, 1f); - let _e191 = window; - let _e192 = window; - let _e194 = attenuation; - attenuation = (_e194 * (_e191 * _e192)); - } - let _e196 = attenuation; - return _e196; + let _e181 = (*distance_1); + let _e182 = (*distance_1); + attenuation = (1f / max((_e181 * _e182), 0.0001f)); + let _e186 = (*range_4); + if (_e186 > 0f) { + let _e188 = (*distance_1); + let _e189 = (*range_4); + ratio = (_e188 / _e189); + let _e191 = ratio; + let _e192 = ratio; + let _e194 = ratio; + let _e196 = ratio; + window = clamp((1f - (((_e191 * _e192) * _e194) * _e196)), 0f, 1f); + let _e200 = window; + let _e201 = window; + let _e203 = attenuation; + attenuation = (_e203 * (_e200 * _e201)); + } + let _e205 = attenuation; + return _e205; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -45224,82 +46306,82 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_9: vec3; + var local_11: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; var u: f32; var v: f32; - let _e184 = (*halfWidth); - let _e185 = (*halfHeight); - n_2 = cross(_e184, _e185); - let _e187 = n_2; - nLen = length(_e187); - let _e189 = nLen; - if (_e189 < 0.000000000001f) { - let _e191 = (*centre); - return _e191; - } - let _e192 = nLen; - let _e193 = n_2; - n_2 = (_e193 / vec3(_e192)); - let _e196 = (*centre); - let _e197 = (*world_2); - toPlane = (_e196 - _e197); - let _e199 = (*mirror); - let _e200 = n_2; - denom = dot(_e199, _e200); - let _e202 = denom; - if (abs(_e202) < 0.00001f) { - let _e205 = toPlane; - let _e206 = n_2; - let _e207 = toPlane; - let _e208 = n_2; - onPlane = (_e205 - (_e206 * dot(_e207, _e208))); + let _e193 = (*halfWidth); + let _e194 = (*halfHeight); + n_2 = cross(_e193, _e194); + let _e196 = n_2; + nLen = length(_e196); + let _e198 = nLen; + if (_e198 < 0.000000000001f) { + let _e200 = (*centre); + return _e200; + } + let _e201 = nLen; + let _e202 = n_2; + n_2 = (_e202 / vec3(_e201)); + let _e205 = (*centre); + let _e206 = (*world_2); + toPlane = (_e205 - _e206); + let _e208 = (*mirror); + let _e209 = n_2; + denom = dot(_e208, _e209); + let _e211 = denom; + if (abs(_e211) < 0.00001f) { + let _e214 = toPlane; + let _e215 = n_2; + let _e216 = toPlane; + let _e217 = n_2; + onPlane = (_e214 - (_e215 * dot(_e216, _e217))); } else { - let _e212 = toPlane; - let _e213 = n_2; - let _e215 = denom; - t_1 = (dot(_e212, _e213) / _e215); - let _e217 = t_1; - if (_e217 > 0f) { - let _e219 = (*mirror); - let _e220 = t_1; - local_9 = (_e219 * _e220); + let _e221 = toPlane; + let _e222 = n_2; + let _e224 = denom; + t_1 = (dot(_e221, _e222) / _e224); + let _e226 = t_1; + if (_e226 > 0f) { + let _e228 = (*mirror); + let _e229 = t_1; + local_11 = (_e228 * _e229); } else { - let _e222 = toPlane; - let _e223 = n_2; - let _e224 = toPlane; - let _e225 = n_2; - local_9 = (_e222 - (_e223 * dot(_e224, _e225))); - } - let _e229 = local_9; - onPlane = _e229; - } - let _e230 = onPlane; - let _e231 = toPlane; - offset_2 = (_e230 - _e231); - let _e233 = (*halfWidth); - let _e234 = (*halfWidth); - wLen2_ = max(dot(_e233, _e234), 0.000000000001f); - let _e237 = (*halfHeight); - let _e238 = (*halfHeight); - hLen2_ = max(dot(_e237, _e238), 0.000000000001f); - let _e241 = offset_2; + let _e231 = toPlane; + let _e232 = n_2; + let _e233 = toPlane; + let _e234 = n_2; + local_11 = (_e231 - (_e232 * dot(_e233, _e234))); + } + let _e238 = local_11; + onPlane = _e238; + } + let _e239 = onPlane; + let _e240 = toPlane; + offset_2 = (_e239 - _e240); let _e242 = (*halfWidth); - let _e244 = wLen2_; - u = clamp((dot(_e241, _e242) / _e244), -1f, 1f); - let _e247 = offset_2; - let _e248 = (*halfHeight); - let _e250 = hLen2_; - v = clamp((dot(_e247, _e248) / _e250), -1f, 1f); - let _e253 = (*centre); - let _e254 = (*halfWidth); - let _e255 = u; - let _e258 = (*halfHeight); - let _e259 = v; - return ((_e253 + (_e254 * _e255)) + (_e258 * _e259)); + let _e243 = (*halfWidth); + wLen2_ = max(dot(_e242, _e243), 0.000000000001f); + let _e246 = (*halfHeight); + let _e247 = (*halfHeight); + hLen2_ = max(dot(_e246, _e247), 0.000000000001f); + let _e250 = offset_2; + let _e251 = (*halfWidth); + let _e253 = wLen2_; + u = clamp((dot(_e250, _e251) / _e253), -1f, 1f); + let _e256 = offset_2; + let _e257 = (*halfHeight); + let _e259 = hLen2_; + v = clamp((dot(_e256, _e257) / _e259), -1f, 1f); + let _e262 = (*centre); + let _e263 = (*halfWidth); + let _e264 = u; + let _e267 = (*halfHeight); + let _e268 = v; + return ((_e262 + (_e263 * _e264)) + (_e267 * _e268)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>, n_3: ptr>) -> f32 { @@ -45308,34 +46390,34 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_10: f32; + var local_12: f32; - let _e176 = (*a_1); - let _e177 = (*a_1); - ua = (_e176 / vec3(max(length(_e177), 0.000000000001f))); - let _e182 = (*b); - let _e183 = (*b); - ub = (_e182 / vec3(max(length(_e183), 0.000000000001f))); - let _e188 = ua; - let _e189 = ub; - angle_1 = acos(clamp(dot(_e188, _e189), -1f, 1f)); - let _e193 = ua; - let _e194 = ub; - axis = cross(_e193, _e194); - let _e196 = axis; - len = length(_e196); - let _e198 = len; - if (_e198 > 0.000001f) { - let _e200 = angle_1; - let _e201 = axis; - let _e202 = (*n_3); - let _e205 = len; - local_10 = ((_e200 * dot(_e201, _e202)) / _e205); + let _e185 = (*a_1); + let _e186 = (*a_1); + ua = (_e185 / vec3(max(length(_e186), 0.000000000001f))); + let _e191 = (*b); + let _e192 = (*b); + ub = (_e191 / vec3(max(length(_e192), 0.000000000001f))); + let _e197 = ua; + let _e198 = ub; + angle_1 = acos(clamp(dot(_e197, _e198), -1f, 1f)); + let _e202 = ua; + let _e203 = ub; + axis = cross(_e202, _e203); + let _e205 = axis; + len = length(_e205); + let _e207 = len; + if (_e207 > 0.000001f) { + let _e209 = angle_1; + let _e210 = axis; + let _e211 = (*n_3); + let _e214 = len; + local_12 = ((_e209 * dot(_e210, _e211)) / _e214); } else { - local_10 = 0f; + local_12 = 0f; } - let _e207 = local_10; - return _e207; + let _e216 = local_12; + return _e216; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr, 4>>, n_4: ptr>) -> f32 { @@ -45345,212 +46427,212 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_11: i32; + var local_13: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_12: f32; + var local_14: f32; var aAbove: bool; var bAbove: bool; - var local_13: f32; - var param_67: vec3; - var param_68: vec3; - var param_69: vec3; - var param_70: vec3; - var param_71: vec3; - var param_72: vec3; + var local_15: f32; + var param_73: vec3; + var param_74: vec3; + var param_75: vec3; + var param_76: vec3; + var param_77: vec3; + var param_78: vec3; total = 0f; exit = vec3(0f, 0f, 0f); entry = vec3(0f, 0f, 0f); i_7 = 0i; loop { - let _e190 = i_7; - if (_e190 < 4i) { - let _e192 = i_7; - let _e194 = (*corners)[_e192]; - a_2 = _e194; - let _e195 = i_7; - if (_e195 == 3i) { - local_11 = 0i; + let _e199 = i_7; + if (_e199 < 4i) { + let _e201 = i_7; + let _e203 = (*corners)[_e201]; + a_2 = _e203; + let _e204 = i_7; + if (_e204 == 3i) { + local_13 = 0i; } else { - let _e197 = i_7; - local_11 = (_e197 + 1i); - } - let _e199 = local_11; - let _e201 = (*corners)[_e199]; - b_1 = _e201; - let _e202 = a_2; - let _e203 = (*n_4); - ha = dot(_e202, _e203); - let _e205 = b_1; - let _e206 = (*n_4); - hb = dot(_e205, _e206); - let _e208 = ha; - let _e209 = hb; - d_3 = (_e208 - _e209); + let _e206 = i_7; + local_13 = (_e206 + 1i); + } + let _e208 = local_13; + let _e210 = (*corners)[_e208]; + b_1 = _e210; let _e211 = a_2; - let _e212 = b_1; - let _e213 = a_2; - let _e215 = d_3; - if (abs(_e215) > 0.000000000001f) { - let _e218 = ha; - let _e219 = d_3; - local_12 = (_e218 / _e219); + let _e212 = (*n_4); + ha = dot(_e211, _e212); + let _e214 = b_1; + let _e215 = (*n_4); + hb = dot(_e214, _e215); + let _e217 = ha; + let _e218 = hb; + d_3 = (_e217 - _e218); + let _e220 = a_2; + let _e221 = b_1; + let _e222 = a_2; + let _e224 = d_3; + if (abs(_e224) > 0.000000000001f) { + let _e227 = ha; + let _e228 = d_3; + local_14 = (_e227 / _e228); } else { - local_12 = 0f; + local_14 = 0f; } - let _e221 = local_12; - q = (_e211 + ((_e212 - _e213) * _e221)); - let _e224 = ha; - aAbove = (_e224 > 0f); - let _e226 = hb; - bAbove = (_e226 > 0f); - let _e228 = aAbove; - let _e229 = bAbove; - if (_e228 || _e229) { - let _e231 = aAbove; - let _e232 = a_2; - let _e233 = q; - let _e236 = bAbove; - let _e237 = b_1; - let _e238 = q; - param_67 = select(_e233, _e232, vec3(_e231)); - param_68 = select(_e238, _e237, vec3(_e236)); - let _e241 = (*n_4); - param_69 = _e241; - let _e242 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); - local_13 = _e242; + let _e230 = local_14; + q = (_e220 + ((_e221 - _e222) * _e230)); + let _e233 = ha; + aAbove = (_e233 > 0f); + let _e235 = hb; + bAbove = (_e235 > 0f); + let _e237 = aAbove; + let _e238 = bAbove; + if (_e237 || _e238) { + let _e240 = aAbove; + let _e241 = a_2; + let _e242 = q; + let _e245 = bAbove; + let _e246 = b_1; + let _e247 = q; + param_73 = select(_e242, _e241, vec3(_e240)); + param_74 = select(_e247, _e246, vec3(_e245)); + let _e250 = (*n_4); + param_75 = _e250; + let _e251 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_73), (¶m_74), (¶m_75)); + local_15 = _e251; } else { - local_13 = 0f; + local_15 = 0f; } - let _e243 = local_13; - let _e244 = total; - total = (_e244 + _e243); - let _e246 = aAbove; - let _e247 = bAbove; - let _e250 = q; - let _e251 = exit; - exit = select(_e251, _e250, vec3((_e246 && !(_e247)))); - let _e254 = aAbove; + let _e252 = local_15; + let _e253 = total; + total = (_e253 + _e252); + let _e255 = aAbove; let _e256 = bAbove; - let _e258 = q; - let _e259 = entry; - entry = select(_e259, _e258, vec3((!(_e254) && _e256))); + let _e259 = q; + let _e260 = exit; + exit = select(_e260, _e259, vec3((_e255 && !(_e256)))); + let _e263 = aAbove; + let _e265 = bAbove; + let _e267 = q; + let _e268 = entry; + entry = select(_e268, _e267, vec3((!(_e263) && _e265))); continue; } else { break; } continuing { - let _e262 = i_7; - i_7 = (_e262 + 1i); + let _e271 = i_7; + i_7 = (_e271 + 1i); } } - let _e264 = exit; - param_70 = _e264; - let _e265 = entry; - param_71 = _e265; - let _e266 = (*n_4); - param_72 = _e266; - let _e267 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); - let _e268 = total; - total = (_e268 + _e267); - let _e270 = total; - return max((-(_e270) * 0.5f), 0f); + let _e273 = exit; + param_76 = _e273; + let _e274 = entry; + param_77 = _e274; + let _e275 = (*n_4); + param_78 = _e275; + let _e276 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_76), (¶m_77), (¶m_78)); + let _e277 = total; + total = (_e277 + _e276); + let _e279 = total; + return max((-(_e279) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_14: f32; - var local_15: f32; var local_16: f32; + var local_17: f32; + var local_18: f32; - let _e173 = (*slot_1); - let _e174 = (*slot_1); - lane = (_e173 - ((_e174 / 4i) * 4i)); - let _e178 = lane; - if (_e178 == 0i) { - let _e181 = (*four)[0u]; - local_14 = _e181; + let _e182 = (*slot_1); + let _e183 = (*slot_1); + lane = (_e182 - ((_e183 / 4i) * 4i)); + let _e187 = lane; + if (_e187 == 0i) { + let _e190 = (*four)[0u]; + local_16 = _e190; } else { - let _e182 = lane; - if (_e182 == 1i) { - let _e185 = (*four)[1u]; - local_15 = _e185; + let _e191 = lane; + if (_e191 == 1i) { + let _e194 = (*four)[1u]; + local_17 = _e194; } else { - let _e186 = lane; - if (_e186 == 2i) { - let _e189 = (*four)[2u]; - local_16 = _e189; + let _e195 = lane; + if (_e195 == 2i) { + let _e198 = (*four)[2u]; + local_18 = _e198; } else { - let _e191 = (*four)[3u]; - local_16 = _e191; + let _e200 = (*four)[3u]; + local_18 = _e200; } - let _e192 = local_16; - local_15 = _e192; + let _e201 = local_18; + local_17 = _e201; } - let _e193 = local_15; - local_14 = _e193; + let _e202 = local_17; + local_16 = _e202; } - let _e194 = local_14; - return _e194; + let _e203 = local_16; + return _e203; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { - var param_73: vec4; - var param_74: i32; + var param_79: vec4; + var param_80: i32; - let _e170 = (*slot_2); - let _e174 = light_list_info.scales[(_e170 / 4i)]; - param_73 = _e174; - let _e175 = (*slot_2); - param_74 = _e175; - let _e176 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); - return _e176; + let _e179 = (*slot_2); + let _e183 = light_list_info.scales[(_e179 / 4i)]; + param_79 = _e183; + let _e184 = (*slot_2); + param_80 = _e184; + let _e185 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); + return _e185; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_3: vec4; var b_2: vec4; - let _e172 = light_list_info.slot_rows[0i]; - let _e173 = (*row); - a_3 = abs((_e172 - vec4(_e173))); - let _e179 = light_list_info.slot_rows[1i]; - let _e180 = (*row); - b_2 = abs((_e179 - vec4(_e180))); - let _e185 = a_3[0u]; - let _e187 = a_3[1u]; - let _e190 = a_3[2u]; - let _e192 = a_3[3u]; - let _e196 = b_2[0u]; - let _e198 = b_2[1u]; - let _e201 = b_2[2u]; - let _e203 = b_2[3u]; - return (min(min(min(_e185, _e187), min(_e190, _e192)), min(min(_e196, _e198), min(_e201, _e203))) < 0.5f); + let _e181 = light_list_info.slot_rows[0i]; + let _e182 = (*row); + a_3 = abs((_e181 - vec4(_e182))); + let _e188 = light_list_info.slot_rows[1i]; + let _e189 = (*row); + b_2 = abs((_e188 - vec4(_e189))); + let _e194 = a_3[0u]; + let _e196 = a_3[1u]; + let _e199 = a_3[2u]; + let _e201 = a_3[3u]; + let _e205 = b_2[0u]; + let _e207 = b_2[1u]; + let _e210 = b_2[2u]; + let _e212 = b_2[3u]; + return (min(min(min(_e194, _e196), min(_e199, _e201)), min(min(_e205, _e207), min(_e210, _e212))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { - var param_75: vec4; - var param_76: i32; + var param_81: vec4; + var param_82: i32; - let _e170 = (*slot_3); - let _e174 = light_list_info.indices[(_e170 / 4i)]; - param_75 = _e174; - let _e175 = (*slot_3); - param_76 = _e175; - let _e176 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); - return _e176; + let _e179 = (*slot_3); + let _e183 = light_list_info.indices[(_e179 / 4i)]; + param_81 = _e183; + let _e184 = (*slot_3); + param_82 = _e184; + let _e185 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_81), (¶m_82)); + return _e185; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e169 = (*texel_2); - let _e173 = light_list_info.list[1u]; - let _e175 = (*row_1); - let _e179 = light_list_info.list[2u]; - let _e182 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e169 + 0.5f) * _e173), ((_e175 + 0.5f) * _e179)), 0f); - return _e182; + let _e178 = (*texel_2); + let _e182 = light_list_info.list[1u]; + let _e184 = (*row_1); + let _e188 = light_list_info.list[2u]; + let _e191 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e178 + 0.5f) * _e182), ((_e184 + 0.5f) * _e188)), 0f); + return _e191; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -45559,43 +46641,43 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var within: f32; var texel_3: f32; var four_1: vec4; - var param_77: f32; - var param_78: f32; - var param_79: vec4; - var param_80: i32; + var param_83: f32; + var param_84: f32; + var param_85: vec4; + var param_86: i32; - let _e177 = g_cluster_offset; - let _e178 = (*slot_4); - entry_1 = (_e177 + f32(_e178)); - let _e181 = entry_1; - row_2 = floor((_e181 / 16f)); - let _e184 = entry_1; - let _e185 = row_2; - within = (_e184 - (_e185 * 16f)); - let _e188 = within; - texel_3 = floor((_e188 / 4f)); - let _e193 = light_list_info.cluster_depth[3u]; + let _e186 = g_cluster_offset; + let _e187 = (*slot_4); + entry_1 = (_e186 + f32(_e187)); + let _e190 = entry_1; + row_2 = floor((_e190 / 16f)); + let _e193 = entry_1; let _e194 = row_2; - let _e196 = texel_3; - param_77 = _e196; - param_78 = (_e193 + _e194); - let _e197 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); - four_1 = _e197; - let _e198 = within; - let _e199 = texel_3; - let _e204 = four_1; - param_79 = _e204; - param_80 = i32(((_e198 - (_e199 * 4f)) + 0.5f)); - let _e205 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); - return _e205; + within = (_e193 - (_e194 * 16f)); + let _e197 = within; + texel_3 = floor((_e197 / 4f)); + let _e202 = light_list_info.cluster_depth[3u]; + let _e203 = row_2; + let _e205 = texel_3; + param_83 = _e205; + param_84 = (_e202 + _e203); + let _e206 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_83), (¶m_84)); + four_1 = _e206; + let _e207 = within; + let _e208 = texel_3; + let _e213 = four_1; + param_85 = _e213; + param_86 = i32(((_e207 - (_e208 * 4f)) + 0.5f)); + let _e214 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_85), (¶m_86)); + return _e214; } fn Clustered_u0028_() -> bool { - let _e169 = light_list_info.cluster_grid[3u]; - return (_e169 > 0.5f); + let _e178 = light_list_info.cluster_grid[3u]; + return (_e178 > 0.5f); } -fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_3: ptr) -> LightSample { +fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_4: ptr) -> LightSample { var light_3: LightSample; var position: vec4; var color_1: vec4; @@ -45604,14 +46686,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_17: f32; - var param_81: i32; - var param_82: i32; + var local_19: f32; + var param_87: i32; + var param_88: i32; var v_1: f32; var u_1: f32; - var local_18: f32; - var param_83: f32; - var param_84: i32; + var local_20: f32; + var param_89: f32; + var param_90: i32; var type_39: f32; var halfWidth_1: vec3; var halfHeight_1: vec3; @@ -45619,293 +46701,293 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_19: f32; - var param_85: array, 4>; - var param_86: vec3; + var local_21: f32; + var param_91: array, 4>; + var param_92: vec3; var area: f32; var radiance: f32; - var local_20: f32; + var local_22: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; var mirror_1: vec3; var representative: vec3; - var param_87: vec3; - var param_88: vec3; - var param_89: vec3; - var param_90: vec3; - var param_91: vec3; + var param_93: vec3; + var param_94: vec3; + var param_95: vec3; + var param_96: vec3; + var param_97: vec3; var toPoint: vec3; var pointDistance: f32; - var local_21: vec3; + var local_23: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; var distance_3: f32; - var param_92: f32; - var param_93: f32; + var param_98: f32; + var param_99: f32; var cosAngle: f32; light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e220 = (*index_2); - if (_e220 < 8i) { - let _e222 = (*index_2); - let _e225 = frag_info.light_position[_e222]; - position = _e225; - let _e226 = (*index_2); - let _e229 = frag_info.light_color[_e226]; - color_1 = _e229; - let _e230 = (*index_2); - let _e233 = frag_info.light_direction[_e230]; - direction = _e233; - let _e234 = (*index_2); - let _e237 = frag_info.light_cone[_e234]; - cone = _e237; + let _e229 = (*index_2); + if (_e229 < 8i) { + let _e231 = (*index_2); + let _e234 = frag_info.light_position[_e231]; + position = _e234; + let _e235 = (*index_2); + let _e238 = frag_info.light_color[_e235]; + color_1 = _e238; + let _e239 = (*index_2); + let _e242 = frag_info.light_direction[_e239]; + direction = _e242; + let _e243 = (*index_2); + let _e246 = frag_info.light_cone[_e243]; + cone = _e246; } else { - let _e238 = (*index_2); - slot_5 = (_e238 - 8i); - let _e240 = Clustered_u0028_(); - clustered = _e240; - let _e241 = clustered; - if _e241 { - let _e242 = slot_5; - param_81 = _e242; - let _e243 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_17 = _e243; + let _e247 = (*index_2); + slot_5 = (_e247 - 8i); + let _e249 = Clustered_u0028_(); + clustered = _e249; + let _e250 = clustered; + if _e250 { + let _e251 = slot_5; + param_87 = _e251; + let _e252 = ClusterRow_u0028_i1_u003b((¶m_87)); + local_19 = _e252; } else { - let _e244 = slot_5; - param_82 = _e244; - let _e245 = LightListRow_u0028_i1_u003b((¶m_82)); - local_17 = _e245; - } - let _e246 = local_17; - listRow = _e246; - let _e247 = listRow; - let _e251 = light_list_info.list[2u]; - v_1 = ((_e247 + 0.5f) * _e251); - let _e255 = light_list_info.list[1u]; - u_1 = _e255; - let _e256 = u_1; - let _e258 = v_1; - let _e260 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e256), _e258), 0f); - position = _e260; - let _e261 = u_1; - let _e263 = v_1; - let _e265 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e261), _e263), 0f); - color_1 = _e265; - let _e266 = u_1; - let _e268 = v_1; - let _e270 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e266), _e268), 0f); - direction = _e270; - let _e271 = u_1; - let _e273 = v_1; - let _e275 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e271), _e273), 0f); - cone = _e275; - let _e276 = clustered; - if _e276 { - let _e277 = listRow; - param_83 = _e277; - let _e278 = InSlots_u0028_f1_u003b((¶m_83)); - local_18 = select(1f, 0f, _e278); + let _e253 = slot_5; + param_88 = _e253; + let _e254 = LightListRow_u0028_i1_u003b((¶m_88)); + local_19 = _e254; + } + let _e255 = local_19; + listRow = _e255; + let _e256 = listRow; + let _e260 = light_list_info.list[2u]; + v_1 = ((_e256 + 0.5f) * _e260); + let _e264 = light_list_info.list[1u]; + u_1 = _e264; + let _e265 = u_1; + let _e267 = v_1; + let _e269 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e265), _e267), 0f); + position = _e269; + let _e270 = u_1; + let _e272 = v_1; + let _e274 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e270), _e272), 0f); + color_1 = _e274; + let _e275 = u_1; + let _e277 = v_1; + let _e279 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e275), _e277), 0f); + direction = _e279; + let _e280 = u_1; + let _e282 = v_1; + let _e284 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e280), _e282), 0f); + cone = _e284; + let _e285 = clustered; + if _e285 { + let _e286 = listRow; + param_89 = _e286; + let _e287 = InSlots_u0028_f1_u003b((¶m_89)); + local_20 = select(1f, 0f, _e287); } else { - let _e280 = slot_5; - param_84 = _e280; - let _e281 = LightListScale_u0028_i1_u003b((¶m_84)); - local_18 = _e281; - } - let _e282 = local_18; - let _e284 = color_1[3u]; - color_1[3u] = (_e284 * _e282); - } - let _e288 = position[3u]; - type_39 = _e288; - let _e289 = type_39; - if (_e289 > 2.5f) { - let _e291 = direction; - halfWidth_1 = _e291.xyz; - let _e293 = cone; - halfHeight_1 = _e293.xyz; - let _e295 = position; - let _e297 = v_world_position_1; - toCentre = (_e295.xyz - _e297); - let _e299 = toCentre; - let _e300 = halfWidth_1; - let _e302 = halfHeight_1; - corners_1[0i] = ((_e299 - _e300) - _e302); - let _e305 = toCentre; - let _e306 = halfWidth_1; - let _e308 = halfHeight_1; - corners_1[1i] = ((_e305 + _e306) - _e308); - let _e311 = toCentre; - let _e312 = halfWidth_1; - let _e314 = halfHeight_1; - corners_1[2i] = ((_e311 + _e312) + _e314); - let _e317 = toCentre; - let _e318 = halfWidth_1; - let _e320 = halfHeight_1; - corners_1[3i] = ((_e317 - _e318) + _e320); - let _e323 = toCentre; - let _e324 = halfWidth_1; - let _e325 = halfHeight_1; - behind = (dot(_e323, cross(_e324, _e325)) >= 0f); - let _e329 = behind; - if _e329 { - local_19 = 0f; + let _e289 = slot_5; + param_90 = _e289; + let _e290 = LightListScale_u0028_i1_u003b((¶m_90)); + local_20 = _e290; + } + let _e291 = local_20; + let _e293 = color_1[3u]; + color_1[3u] = (_e293 * _e291); + } + let _e297 = position[3u]; + type_39 = _e297; + let _e298 = type_39; + if (_e298 > 2.5f) { + let _e300 = direction; + halfWidth_1 = _e300.xyz; + let _e302 = cone; + halfHeight_1 = _e302.xyz; + let _e304 = position; + let _e306 = v_world_position_1; + toCentre = (_e304.xyz - _e306); + let _e308 = toCentre; + let _e309 = halfWidth_1; + let _e311 = halfHeight_1; + corners_1[0i] = ((_e308 - _e309) - _e311); + let _e314 = toCentre; + let _e315 = halfWidth_1; + let _e317 = halfHeight_1; + corners_1[1i] = ((_e314 + _e315) - _e317); + let _e320 = toCentre; + let _e321 = halfWidth_1; + let _e323 = halfHeight_1; + corners_1[2i] = ((_e320 + _e321) + _e323); + let _e326 = toCentre; + let _e327 = halfWidth_1; + let _e329 = halfHeight_1; + corners_1[3i] = ((_e326 - _e327) + _e329); + let _e332 = toCentre; + let _e333 = halfWidth_1; + let _e334 = halfHeight_1; + behind = (dot(_e332, cross(_e333, _e334)) >= 0f); + let _e338 = behind; + if _e338 { + local_21 = 0f; } else { - let _e330 = corners_1; - param_85 = _e330; - let _e332 = (*s_3).n; - param_86 = _e332; - let _e333 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_19 = _e333; - } - let _e334 = local_19; - formFactor = _e334; - let _e335 = halfWidth_1; - let _e336 = halfHeight_1; - area = (length(cross(_e335, _e336)) * 4f); - let _e340 = area; - if (_e340 > 0.000000001f) { - let _e342 = area; - local_20 = (1f / _e342); + let _e339 = corners_1; + param_91 = _e339; + let _e341 = (*s_4).n; + param_92 = _e341; + let _e342 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_91), (¶m_92)); + local_21 = _e342; + } + let _e343 = local_21; + formFactor = _e343; + let _e344 = halfWidth_1; + let _e345 = halfHeight_1; + area = (length(cross(_e344, _e345)) * 4f); + let _e349 = area; + if (_e349 > 0.000000001f) { + let _e351 = area; + local_22 = (1f / _e351); } else { - local_20 = 0f; - } - let _e344 = local_20; - radiance = _e344; - let _e345 = toCentre; - distance_2 = length(_e345); - let _e348 = direction[3u]; - if (_e348 > 0f) { - let _e350 = distance_2; - let _e352 = direction[3u]; - ratio_1 = (_e350 / _e352); - let _e354 = ratio_1; - let _e355 = ratio_1; - let _e357 = ratio_1; - let _e359 = ratio_1; - window_1 = clamp((1f - (((_e354 * _e355) * _e357) * _e359)), 0f, 1f); - let _e363 = window_1; - let _e364 = window_1; - let _e366 = radiance; - radiance = (_e366 * (_e363 * _e364)); - } - let _e369 = (*s_3).v; - let _e372 = (*s_3).n; - mirror_1 = reflect(-(_e369), _e372); - let _e374 = position; - param_87 = _e374.xyz; - let _e376 = halfWidth_1; - param_88 = _e376; - let _e377 = halfHeight_1; - param_89 = _e377; - let _e378 = v_world_position_1; - param_90 = _e378; - let _e379 = mirror_1; - param_91 = _e379; - let _e380 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); - representative = _e380; - let _e381 = representative; - let _e382 = v_world_position_1; - toPoint = (_e381 - _e382); - let _e384 = toPoint; - pointDistance = length(_e384); - let _e386 = pointDistance; - if (_e386 > 0.000001f) { - let _e388 = toPoint; - let _e389 = pointDistance; - local_21 = (_e388 / vec3(_e389)); + local_22 = 0f; + } + let _e353 = local_22; + radiance = _e353; + let _e354 = toCentre; + distance_2 = length(_e354); + let _e357 = direction[3u]; + if (_e357 > 0f) { + let _e359 = distance_2; + let _e361 = direction[3u]; + ratio_1 = (_e359 / _e361); + let _e363 = ratio_1; + let _e364 = ratio_1; + let _e366 = ratio_1; + let _e368 = ratio_1; + window_1 = clamp((1f - (((_e363 * _e364) * _e366) * _e368)), 0f, 1f); + let _e372 = window_1; + let _e373 = window_1; + let _e375 = radiance; + radiance = (_e375 * (_e372 * _e373)); + } + let _e378 = (*s_4).v; + let _e381 = (*s_4).n; + mirror_1 = reflect(-(_e378), _e381); + let _e383 = position; + param_93 = _e383.xyz; + let _e385 = halfWidth_1; + param_94 = _e385; + let _e386 = halfHeight_1; + param_95 = _e386; + let _e387 = v_world_position_1; + param_96 = _e387; + let _e388 = mirror_1; + param_97 = _e388; + let _e389 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_93), (¶m_94), (¶m_95), (¶m_96), (¶m_97)); + representative = _e389; + let _e390 = representative; + let _e391 = v_world_position_1; + toPoint = (_e390 - _e391); + let _e393 = toPoint; + pointDistance = length(_e393); + let _e395 = pointDistance; + if (_e395 > 0.000001f) { + let _e397 = toPoint; + let _e398 = pointDistance; + local_23 = (_e397 / vec3(_e398)); } else { - let _e393 = (*s_3).n; - local_21 = _e393; - } - let _e394 = local_21; - light_3.l = _e394; - let _e397 = light_3.l; - let _e399 = (*s_3).v; - light_3.h = normalize((_e397 + _e399)); - let _e403 = formFactor; - light_3.n_dot_l = _e403; - let _e406 = (*s_3).n; - let _e408 = light_3.h; - light_3.n_dot_h = max(dot(_e406, _e408), 0f); - let _e413 = (*s_3).v; - let _e415 = light_3.h; - light_3.v_dot_h = max(dot(_e413, _e415), 0f); - let _e419 = color_1; - let _e422 = color_1[3u]; - let _e424 = radiance; - light_3.radiance = ((_e419.xyz * _e422) * _e424); - let _e427 = light_3; - return _e427; - } - let _e428 = direction; - aim = normalize(_e428.xyz); + let _e402 = (*s_4).n; + local_23 = _e402; + } + let _e403 = local_23; + light_3.l = _e403; + let _e406 = light_3.l; + let _e408 = (*s_4).v; + light_3.h = normalize((_e406 + _e408)); + let _e412 = formFactor; + light_3.n_dot_l = _e412; + let _e415 = (*s_4).n; + let _e417 = light_3.h; + light_3.n_dot_h = max(dot(_e415, _e417), 0f); + let _e422 = (*s_4).v; + let _e424 = light_3.h; + light_3.v_dot_h = max(dot(_e422, _e424), 0f); + let _e428 = color_1; + let _e431 = color_1[3u]; + let _e433 = radiance; + light_3.radiance = ((_e428.xyz * _e431) * _e433); + let _e436 = light_3; + return _e436; + } + let _e437 = direction; + aim = normalize(_e437.xyz); attenuation_1 = 1f; - let _e431 = type_39; - if (_e431 < 0.5f) { - let _e433 = aim; - light_3.l = -(_e433); + let _e440 = type_39; + if (_e440 < 0.5f) { + let _e442 = aim; + light_3.l = -(_e442); } else { - let _e436 = position; - let _e438 = v_world_position_1; - toLight_1 = (_e436.xyz - _e438); - let _e440 = toLight_1; - distance_3 = length(_e440); - let _e442 = distance_3; - if (_e442 < 0.000001f) { - let _e445 = (*s_3).n; - light_3.l = _e445; - let _e448 = (*s_3).n; - light_3.h = _e448; + let _e445 = position; + let _e447 = v_world_position_1; + toLight_1 = (_e445.xyz - _e447); + let _e449 = toLight_1; + distance_3 = length(_e449); + let _e451 = distance_3; + if (_e451 < 0.000001f) { + let _e454 = (*s_4).n; + light_3.l = _e454; + let _e457 = (*s_4).n; + light_3.h = _e457; light_3.radiance = vec3(0f, 0f, 0f); light_3.n_dot_l = 0f; light_3.n_dot_h = 0f; light_3.v_dot_h = 0f; - let _e454 = light_3; - return _e454; + let _e463 = light_3; + return _e463; } - let _e455 = toLight_1; - let _e456 = distance_3; - light_3.l = (_e455 / vec3(_e456)); - let _e460 = distance_3; - param_92 = _e460; - let _e462 = direction[3u]; - param_93 = _e462; - let _e463 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); - attenuation_1 = _e463; - let _e464 = type_39; - if (_e464 > 1.5f) { - let _e466 = aim; - let _e468 = light_3.l; - cosAngle = dot(_e466, -(_e468)); - let _e471 = cosAngle; - let _e473 = cone[1u]; - let _e476 = cone[0u]; - let _e478 = cone[1u]; - let _e482 = attenuation_1; - attenuation_1 = (_e482 * clamp(((_e471 - _e473) / (_e476 - _e478)), 0f, 1f)); - } - } - let _e485 = light_3.l; - let _e487 = (*s_3).v; - light_3.h = normalize((_e485 + _e487)); - let _e492 = (*s_3).n; + let _e464 = toLight_1; + let _e465 = distance_3; + light_3.l = (_e464 / vec3(_e465)); + let _e469 = distance_3; + param_98 = _e469; + let _e471 = direction[3u]; + param_99 = _e471; + let _e472 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_98), (¶m_99)); + attenuation_1 = _e472; + let _e473 = type_39; + if (_e473 > 1.5f) { + let _e475 = aim; + let _e477 = light_3.l; + cosAngle = dot(_e475, -(_e477)); + let _e480 = cosAngle; + let _e482 = cone[1u]; + let _e485 = cone[0u]; + let _e487 = cone[1u]; + let _e491 = attenuation_1; + attenuation_1 = (_e491 * clamp(((_e480 - _e482) / (_e485 - _e487)), 0f, 1f)); + } + } let _e494 = light_3.l; - light_3.n_dot_l = max(dot(_e492, _e494), 0f); - let _e499 = (*s_3).n; - let _e501 = light_3.h; - light_3.n_dot_h = max(dot(_e499, _e501), 0f); - let _e506 = (*s_3).v; - let _e508 = light_3.h; - light_3.v_dot_h = max(dot(_e506, _e508), 0f); - let _e512 = color_1; - let _e515 = color_1[3u]; - let _e517 = attenuation_1; - light_3.radiance = ((_e512.xyz * _e515) * _e517); - let _e520 = light_3; - return _e520; + let _e496 = (*s_4).v; + light_3.h = normalize((_e494 + _e496)); + let _e501 = (*s_4).n; + let _e503 = light_3.l; + light_3.n_dot_l = max(dot(_e501, _e503), 0f); + let _e508 = (*s_4).n; + let _e510 = light_3.h; + light_3.n_dot_h = max(dot(_e508, _e510), 0f); + let _e515 = (*s_4).v; + let _e517 = light_3.h; + light_3.v_dot_h = max(dot(_e515, _e517), 0f); + let _e521 = color_1; + let _e524 = color_1[3u]; + let _e526 = attenuation_1; + light_3.radiance = ((_e521.xyz * _e524) * _e526); + let _e529 = light_3; + return _e529; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -45916,393 +46998,386 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_22: f32; + var local_24: f32; var cell: f32; var row_3: f32; var header: vec4; - var param_94: f32; - var param_95: f32; + var param_100: f32; + var param_101: f32; - let _e182 = light_list_info.cluster_view_projection; - let _e183 = (*world_3); - clip_1 = (_e182 * vec4(_e183.x, _e183.y, _e183.z, 1f)); - let _e189 = clip_1; - let _e192 = clip_1[3u]; - ndc_1 = (_e189.xy / vec2(max(_e192, 0.000001f))); - let _e197 = light_list_info.cluster_grid; - grid = _e197.xyz; - let _e201 = light_list_info.cluster_depth[0u]; - near_1 = _e201; - let _e203 = ndc_1[0u]; - let _e207 = grid[0u]; - let _e211 = grid[0u]; - tx = clamp(floor((((_e203 * 0.5f) + 0.5f) * _e207)), 0f, (_e211 - 1f)); - let _e215 = ndc_1[1u]; - let _e219 = grid[1u]; - let _e223 = grid[1u]; - ty = clamp(floor((((_e215 * 0.5f) + 0.5f) * _e219)), 0f, (_e223 - 1f)); - let _e227 = clip_1[3u]; - let _e228 = near_1; - if (_e227 <= _e228) { - local_22 = 0f; + let _e191 = light_list_info.cluster_view_projection; + let _e192 = (*world_3); + clip_1 = (_e191 * vec4(_e192.x, _e192.y, _e192.z, 1f)); + let _e198 = clip_1; + let _e201 = clip_1[3u]; + ndc_1 = (_e198.xy / vec2(max(_e201, 0.000001f))); + let _e206 = light_list_info.cluster_grid; + grid = _e206.xyz; + let _e210 = light_list_info.cluster_depth[0u]; + near_1 = _e210; + let _e212 = ndc_1[0u]; + let _e216 = grid[0u]; + let _e220 = grid[0u]; + tx = clamp(floor((((_e212 * 0.5f) + 0.5f) * _e216)), 0f, (_e220 - 1f)); + let _e224 = ndc_1[1u]; + let _e228 = grid[1u]; + let _e232 = grid[1u]; + ty = clamp(floor((((_e224 * 0.5f) + 0.5f) * _e228)), 0f, (_e232 - 1f)); + let _e236 = clip_1[3u]; + let _e237 = near_1; + if (_e236 <= _e237) { + local_24 = 0f; } else { - let _e231 = clip_1[3u]; - let _e232 = near_1; - let _e237 = light_list_info.cluster_depth[1u]; - let _e241 = grid[2u]; - local_22 = clamp(floor((log((_e231 / _e232)) * _e237)), 0f, (_e241 - 1f)); - } - let _e244 = local_22; - tz = _e244; - let _e245 = tx; - let _e246 = ty; - let _e248 = grid[0u]; - let _e251 = tz; - let _e253 = grid[0u]; - let _e256 = grid[1u]; - cell = ((_e245 + (_e246 * _e248)) + ((_e251 * _e253) * _e256)); - let _e259 = cell; - row_3 = floor((_e259 / 4f)); - let _e262 = cell; - let _e263 = row_3; - let _e268 = light_list_info.cluster_depth[2u]; - let _e269 = row_3; - param_94 = (_e262 - (_e263 * 4f)); - param_95 = (_e268 + _e269); - let _e271 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); - header = _e271; - let _e273 = header[0u]; - g_cluster_offset = _e273; - let _e275 = header[1u]; - g_cluster_count = _e275; + let _e240 = clip_1[3u]; + let _e241 = near_1; + let _e246 = light_list_info.cluster_depth[1u]; + let _e250 = grid[2u]; + local_24 = clamp(floor((log((_e240 / _e241)) * _e246)), 0f, (_e250 - 1f)); + } + let _e253 = local_24; + tz = _e253; + let _e254 = tx; + let _e255 = ty; + let _e257 = grid[0u]; + let _e260 = tz; + let _e262 = grid[0u]; + let _e265 = grid[1u]; + cell = ((_e254 + (_e255 * _e257)) + ((_e260 * _e262) * _e265)); + let _e268 = cell; + row_3 = floor((_e268 / 4f)); + let _e271 = cell; + let _e272 = row_3; + let _e277 = light_list_info.cluster_depth[2u]; + let _e278 = row_3; + param_100 = (_e271 - (_e272 * 4f)); + param_101 = (_e277 + _e278); + let _e280 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_100), (¶m_101)); + header = _e280; + let _e282 = header[0u]; + g_cluster_offset = _e282; + let _e284 = header[1u]; + g_cluster_count = _e284; return; } fn LightCount_u0028_() -> i32 { var tail: f32; - var param_96: vec3; + var param_102: vec3; - let _e171 = light_list_info.list[0u]; - tail = _e171; - let _e172 = Clustered_u0028_(); - if _e172 { - let _e173 = v_world_position_1; - param_96 = _e173; - FindCluster_u0028_vf3_u003b((¶m_96)); - let _e174 = g_cluster_count; - tail = _e174; + let _e180 = light_list_info.list[0u]; + tail = _e180; + let _e181 = Clustered_u0028_(); + if _e181 { + let _e182 = v_world_position_1; + param_102 = _e182; + FindCluster_u0028_vf3_u003b((¶m_102)); + let _e183 = g_cluster_count; + tail = _e183; } - let _e177 = frag_info.frame_params[1u]; - let _e181 = tail; - return (clamp(i32((_e177 + 0.5f)), 0i, 8i) + clamp(i32((_e181 + 0.5f)), 0i, 24i)); + let _e186 = frag_info.frame_params[1u]; + let _e190 = tail; + return (clamp(i32((_e186 + 0.5f)), 0i, 8i) + clamp(i32((_e190 + 0.5f)), 0i, 24i)); } -fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_4: ptr) -> vec3 { +fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_5: ptr) -> vec3 { var total_1: vec3; var count_1: i32; var i_8: i32; var light_4: LightSample; - var param_97: i32; - var param_98: Surface; + var param_103: i32; + var param_104: Surface; var visibility: f32; - var param_99: i32; - var local_23: f32; - var param_100: Surface; - var param_101: LightSample; - var param_102: i32; - var param_103: vec3; - var param_104: vec3; var param_105: i32; + var local_25: f32; var param_106: Surface; var param_107: LightSample; + var param_108: i32; + var param_109: vec3; + var param_110: vec3; + var param_111: i32; + var param_112: Surface; + var param_113: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e185 = LightCount_u0028_(); - count_1 = _e185; + let _e194 = LightCount_u0028_(); + count_1 = _e194; i_8 = 0i; loop { - let _e186 = i_8; - if (_e186 < 32i) { - let _e188 = i_8; - let _e189 = count_1; - if (_e188 >= _e189) { + let _e195 = i_8; + if (_e195 < 32i) { + let _e197 = i_8; + let _e198 = count_1; + if (_e197 >= _e198) { break; } - let _e191 = i_8; - param_97 = _e191; - let _e192 = (*s_4); - param_98 = _e192; - let _e193 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); - light_4 = _e193; - let _e195 = light_4.n_dot_l; - if (_e195 <= 0f) { + let _e200 = i_8; + param_103 = _e200; + let _e201 = (*s_5); + param_104 = _e201; + let _e202 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_103), (¶m_104)); + light_4 = _e202; + let _e204 = light_4.n_dot_l; + if (_e204 <= 0f) { continue; } - let _e197 = i_8; - param_99 = _e197; - let _e198 = LightHasShadow_u0028_i1_u003b((¶m_99)); - if _e198 { - let _e199 = (*s_4); - param_100 = _e199; - let _e200 = light_4; - param_101 = _e200; - let _e201 = i_8; - param_102 = _e201; - let _e202 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); - let _e203 = v_world_position_1; - param_103 = _e203; - let _e205 = (*s_4).n; - param_104 = _e205; - let _e206 = i_8; - param_105 = _e206; - let _e207 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_23 = (_e202 * _e207); + let _e206 = i_8; + param_105 = _e206; + let _e207 = LightHasShadow_u0028_i1_u003b((¶m_105)); + if _e207 { + let _e208 = (*s_5); + param_106 = _e208; + let _e209 = light_4; + param_107 = _e209; + let _e210 = i_8; + param_108 = _e210; + let _e211 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_106), (¶m_107), (¶m_108)); + let _e212 = v_world_position_1; + param_109 = _e212; + let _e214 = (*s_5).n; + param_110 = _e214; + let _e215 = i_8; + param_111 = _e215; + let _e216 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_109), (¶m_110), (¶m_111)); + local_25 = (_e211 * _e216); } else { - local_23 = 1f; + local_25 = 1f; } - let _e209 = local_23; - visibility = _e209; - let _e210 = visibility; - if (_e210 <= 0f) { + let _e218 = local_25; + visibility = _e218; + let _e219 = visibility; + if (_e219 <= 0f) { continue; } - let _e212 = (*s_4); - param_106 = _e212; - let _e213 = light_4; - param_107 = _e213; - let _e214 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); - let _e216 = light_4.radiance; - let _e219 = light_4.n_dot_l; - let _e221 = visibility; - let _e223 = total_1; - total_1 = (_e223 + (((_e214 * _e216) * _e219) * _e221)); + let _e221 = (*s_5); + param_112 = _e221; + let _e222 = light_4; + param_113 = _e222; + let _e223 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_112), (¶m_113)); + let _e225 = light_4.radiance; + let _e228 = light_4.n_dot_l; + let _e230 = visibility; + let _e232 = total_1; + total_1 = (_e232 + (((_e223 * _e225) * _e228) * _e230)); continue; } else { break; } continuing { - let _e225 = i_8; - i_8 = (_e225 + 1i); + let _e234 = i_8; + i_8 = (_e234 + 1i); } } - let _e227 = total_1; - return _e227; -} - -fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e168 = (*srgb); - let _e171 = (*srgb); - let _e176 = (*srgb); - return mix((_e168 / vec3(12.92f)), pow(((_e171 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e176)); + let _e236 = total_1; + return _e236; } fn ImpostorRight_u0028_vf3_u003b(d_4: ptr>) -> vec3 { var up: vec3; - let _e170 = (*d_4)[1u]; - up = select(vec3(0f, 1f, 0f), vec3(0f, 0f, -1f), vec3((abs(_e170) > 0.999f))); - let _e175 = up; - let _e176 = (*d_4); - return normalize(cross(_e175, _e176)); + let _e179 = (*d_4)[1u]; + up = select(vec3(0f, 1f, 0f), vec3(0f, 0f, -1f), vec3((abs(_e179) > 0.999f))); + let _e184 = up; + let _e185 = (*d_4); + return normalize(cross(_e184, _e185)); } fn ImpostorDecode_u0028_vf2_u003b(uv_5: ptr>) -> vec3 { var p_1: vec2; var y_1: f32; - var s_5: vec2; + var s_6: vec2; var folded: vec2; var xz: vec2; - let _e173 = (*uv_5); - p_1 = ((_e173 * 2f) - vec2(1f, 1f)); - let _e177 = p_1[0u]; - let _e181 = p_1[1u]; - y_1 = ((1f - abs(_e177)) - abs(_e181)); - let _e185 = p_1[0u]; - let _e189 = p_1[1u]; - s_5 = vec2(select(-1f, 1f, (_e185 >= 0f)), select(-1f, 1f, (_e189 >= 0f))); - let _e193 = p_1; - let _e197 = s_5; - folded = ((vec2(1f, 1f) - abs(_e193.yx)) * _e197); - let _e199 = y_1; - let _e201 = p_1; - let _e202 = folded; - xz = select(_e202, _e201, vec2((_e199 >= 0f))); - let _e206 = xz[0u]; - let _e207 = y_1; - let _e209 = xz[1u]; - return normalize(vec3(_e206, _e207, _e209)); + let _e182 = (*uv_5); + p_1 = ((_e182 * 2f) - vec2(1f, 1f)); + let _e186 = p_1[0u]; + let _e190 = p_1[1u]; + y_1 = ((1f - abs(_e186)) - abs(_e190)); + let _e194 = p_1[0u]; + let _e198 = p_1[1u]; + s_6 = vec2(select(-1f, 1f, (_e194 >= 0f)), select(-1f, 1f, (_e198 >= 0f))); + let _e202 = p_1; + let _e206 = s_6; + folded = ((vec2(1f, 1f) - abs(_e202.yx)) * _e206); + let _e208 = y_1; + let _e210 = p_1; + let _e211 = folded; + xz = select(_e211, _e210, vec2((_e208 >= 0f))); + let _e215 = xz[0u]; + let _e216 = y_1; + let _e218 = xz[1u]; + return normalize(vec3(_e215, _e216, _e218)); } fn ImpostorViewUv_u0028_vf2_u003b_vf3_u003b(cell_1: ptr>, offset_3: ptr>) -> vec2 { var d_5: vec3; - var param_108: vec2; + var param_114: vec2; var right: vec3; - var param_109: vec3; + var param_115: vec3; var up_1: vec3; - var local_24: vec2; - - let _e175 = (*cell_1); - param_108 = (_e175 / vec2(7f)); - let _e178 = ImpostorDecode_u0028_vf2_u003b((¶m_108)); - d_5 = _e178; - let _e179 = d_5; - param_109 = _e179; - let _e180 = ImpostorRight_u0028_vf3_u003b((¶m_109)); - right = _e180; - let _e181 = d_5; - let _e182 = right; - up_1 = cross(_e181, _e182); - let _e184 = (*offset_3); - let _e185 = right; - let _e189 = (*offset_3); - let _e190 = up_1; - local_24 = vec2(((dot(_e184, _e185) * 0.5f) + 0.5f), (0.5f - (dot(_e189, _e190) * 0.5f))); - let _e195 = (*cell_1); - let _e196 = local_24; - return ((_e195 + clamp(_e196, vec2(0f, 0f), vec2(1f, 1f))) / vec2(8f)); + var local_26: vec2; + + let _e184 = (*cell_1); + param_114 = (_e184 / vec2(7f)); + let _e187 = ImpostorDecode_u0028_vf2_u003b((¶m_114)); + d_5 = _e187; + let _e188 = d_5; + param_115 = _e188; + let _e189 = ImpostorRight_u0028_vf3_u003b((¶m_115)); + right = _e189; + let _e190 = d_5; + let _e191 = right; + up_1 = cross(_e190, _e191); + let _e193 = (*offset_3); + let _e194 = right; + let _e198 = (*offset_3); + let _e199 = up_1; + local_26 = vec2(((dot(_e193, _e194) * 0.5f) + 0.5f), (0.5f - (dot(_e198, _e199) * 0.5f))); + let _e204 = (*cell_1); + let _e205 = local_26; + return ((_e204 + clamp(_e205, vec2(0f, 0f), vec2(1f, 1f))) / vec2(8f)); } fn ImpostorEncode_u0028_vf3_u003b(d_6: ptr>) -> vec2 { var a_4: vec3; var p_2: vec2; - var s_6: vec2; + var s_7: vec2; var folded_1: vec2; - let _e172 = (*d_6); - a_4 = abs(_e172); - let _e174 = (*d_6); - let _e177 = a_4[0u]; - let _e179 = a_4[1u]; - let _e182 = a_4[2u]; - p_2 = (_e174.xz / vec2(max(((_e177 + _e179) + _e182), 0.00000001f))); - let _e188 = p_2[0u]; - let _e192 = p_2[1u]; - s_6 = vec2(select(-1f, 1f, (_e188 >= 0f)), select(-1f, 1f, (_e192 >= 0f))); - let _e196 = p_2; - let _e200 = s_6; - folded_1 = ((vec2(1f, 1f) - abs(_e196.yx)) * _e200); - let _e203 = (*d_6)[1u]; + let _e181 = (*d_6); + a_4 = abs(_e181); + let _e183 = (*d_6); + let _e186 = a_4[0u]; + let _e188 = a_4[1u]; + let _e191 = a_4[2u]; + p_2 = (_e183.xz / vec2(max(((_e186 + _e188) + _e191), 0.00000001f))); + let _e197 = p_2[0u]; + let _e201 = p_2[1u]; + s_7 = vec2(select(-1f, 1f, (_e197 >= 0f)), select(-1f, 1f, (_e201 >= 0f))); let _e205 = p_2; - let _e206 = folded_1; - return ((select(_e206, _e205, vec2((_e203 >= 0f))) * 0.5f) + vec2(0.5f, 0.5f)); + let _e209 = s_7; + folded_1 = ((vec2(1f, 1f) - abs(_e205.yx)) * _e209); + let _e212 = (*d_6)[1u]; + let _e214 = p_2; + let _e215 = folded_1; + return ((select(_e215, _e214, vec2((_e212 >= 0f))) * 0.5f) + vec2(0.5f, 0.5f)); } fn MaterialLodBias_u0028_() -> f32 { - let _e169 = frag_info.target_origin[1u]; - return _e169; + let _e178 = frag_info.target_origin[1u]; + return _e178; } fn ReadSurface_u0028_() -> Surface { var texel_4: vec4; - var s_7: Surface; - var param_110: vec3; - var param_111: vec3; + var s_8: Surface; + var param_116: vec3; + var param_117: vec3; var cutoff: f32; var anchored: vec3; var noise_1: f32; - let _e174 = v_texcoord_1; - let _e175 = MaterialLodBias_u0028_(); - let _e176 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e174, _e175); - texel_4 = _e176; - let _e177 = texel_4; - param_110 = _e177.xyz; - let _e179 = SrgbToLinear_u0028_vf3_u003b((¶m_110)); - let _e181 = frag_info.base_color; - param_111 = _e181.xyz; - let _e183 = SrgbToLinear_u0028_vf3_u003b((¶m_111)); - let _e185 = v_color_1; - s_7.albedo = ((_e179 * _e183) * _e185.xyz); - let _e190 = texel_4[3u]; - let _e193 = frag_info.base_color[3u]; - let _e196 = v_color_1[3u]; - s_7.alpha = ((_e190 * _e193) * _e196); - let _e200 = s_7.albedo; - g_albedo = _e200; - let _e203 = frag_info.material2_[0u]; - cutoff = _e203; - let _e204 = cutoff; - if (_e204 >= 0f) { - let _e207 = s_7.alpha; - let _e208 = cutoff; - if (_e207 < _e208) { + let _e183 = v_texcoord_1; + let _e184 = MaterialLodBias_u0028_(); + let _e185 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e183, _e184); + texel_4 = _e185; + let _e186 = texel_4; + param_116 = _e186.xyz; + let _e188 = SrgbToLinear_u0028_vf3_u003b((¶m_116)); + let _e190 = frag_info.base_color; + param_117 = _e190.xyz; + let _e192 = SrgbToLinear_u0028_vf3_u003b((¶m_117)); + let _e194 = v_color_1; + s_8.albedo = ((_e188 * _e192) * _e194.xyz); + let _e199 = texel_4[3u]; + let _e202 = frag_info.base_color[3u]; + let _e205 = v_color_1[3u]; + s_8.alpha = ((_e199 * _e202) * _e205); + let _e209 = s_8.albedo; + g_albedo = _e209; + let _e212 = frag_info.material2_[0u]; + cutoff = _e212; + let _e213 = cutoff; + if (_e213 >= 0f) { + let _e216 = s_8.alpha; + let _e217 = cutoff; + if (_e216 < _e217) { discard; } } else { - let _e210 = cutoff; - if (_e210 < -1.5f) { - let _e212 = v_world_position_1; - anchored = floor((_e212 * 16f)); - let _e215 = anchored; - noise_1 = fract((sin(dot(_e215, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e221 = s_7.alpha; - let _e222 = noise_1; - if (_e221 < _e222) { + let _e219 = cutoff; + if (_e219 < -1.5f) { + let _e221 = v_world_position_1; + anchored = floor((_e221 * 16f)); + let _e224 = anchored; + noise_1 = fract((sin(dot(_e224, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e230 = s_8.alpha; + let _e231 = noise_1; + if (_e230 < _e231) { discard; } } } - let _e224 = cutoff; - let _e226 = cutoff; - g_premultiply = ((_e224 < 0f) && (_e226 > -0.75f)); - let _e229 = v_normal_1; - s_7.n = normalize(_e229); - let _e232 = gl_FrontFacing_1; - if !(_e232) { - let _e235 = s_7.n; - s_7.n = -(_e235); - } - let _e239 = frag_info.camera_position; - let _e241 = v_world_position_1; - s_7.v = normalize((_e239.xyz - _e241)); - let _e246 = s_7.n; - let _e248 = s_7.v; - s_7.n_dot_v = max(dot(_e246, _e248), 0.0001f); - let _e254 = frag_info.material[0u]; - s_7.metallic = clamp(_e254, 0f, 1f); - let _e259 = frag_info.material[1u]; - s_7.roughness = clamp(_e259, 0.02f, 1f); - let _e263 = frag_info.ambient_ground; - let _e266 = frag_info.ambient_sky; - let _e270 = s_7.n[1u]; - let _e277 = frag_info.material[2u]; - s_7.ambient = (mix(_e263.xyz, _e266.xyz, vec3(((_e270 * 0.5f) + 0.5f))) * _e277); - let _e282 = frag_info.frame_params[0u]; - s_7.exposure = max(_e282, 0f); - s_7.occlusion = 1f; - s_7.emissive = vec3(0f, 0f, 0f); - let _e287 = s_7; - return _e287; + let _e233 = cutoff; + let _e235 = cutoff; + g_premultiply = ((_e233 < 0f) && (_e235 > -0.75f)); + let _e238 = v_normal_1; + s_8.n = normalize(_e238); + let _e241 = gl_FrontFacing_1; + if !(_e241) { + let _e244 = s_8.n; + s_8.n = -(_e244); + } + let _e248 = frag_info.camera_position; + let _e250 = v_world_position_1; + s_8.v = normalize((_e248.xyz - _e250)); + let _e255 = s_8.n; + let _e257 = s_8.v; + s_8.n_dot_v = max(dot(_e255, _e257), 0.0001f); + let _e263 = frag_info.material[0u]; + s_8.metallic = clamp(_e263, 0f, 1f); + let _e268 = frag_info.material[1u]; + s_8.roughness = clamp(_e268, 0.02f, 1f); + let _e272 = frag_info.ambient_ground; + let _e275 = frag_info.ambient_sky; + let _e279 = s_8.n[1u]; + let _e286 = frag_info.material[2u]; + s_8.ambient = (mix(_e272.xyz, _e275.xyz, vec3(((_e279 * 0.5f) + 0.5f))) * _e286); + let _e291 = frag_info.frame_params[0u]; + s_8.exposure = max(_e291, 0f); + s_8.occlusion = 1f; + s_8.emissive = vec3(0f, 0f, 0f); + let _e296 = s_8; + return _e296; } fn main_1() { - var s_8: Surface; + var s_9: Surface; var d_7: vec3; var right_1: vec3; - var param_112: vec3; + var param_118: vec3; var up_2: vec3; var offset_4: vec3; var g: vec2; - var param_113: vec3; + var param_119: vec3; var base: vec2; var f: vec2; var lower: bool; var c0_: vec2; - var local_25: vec2; + var local_27: vec2; var c1_: vec2; var c2_: vec2; var w: vec3; - var local_26: vec3; + var local_28: vec3; var uv0_: vec2; - var param_114: vec2; - var param_115: vec3; + var param_120: vec2; + var param_121: vec3; var uv1_: vec2; - var param_116: vec2; - var param_117: vec3; + var param_122: vec2; + var param_123: vec3; var uv2_: vec2; - var param_118: vec2; - var param_119: vec3; + var param_124: vec2; + var param_125: vec3; var a0_: vec4; var a1_: vec4; var a2_: vec4; @@ -46312,17 +47387,20 @@ fn main_1() { var wa: vec3; var alpha_3: f32; var srgb_1: vec3; - var local_27: vec3; + var local_29: vec3; var worldRight: vec3; var worldFacing: vec3; var worldUp: vec3; - var param_120: vec3; - var param_121: vec3; + var param_126: vec3; + var param_127: vec3; var ambient: vec3; - var param_122: Surface; - var param_123: vec3; - var param_124: f32; - var param_125: f32; + var lit_3: vec3; + var param_128: Surface; + var param_129: Surface; + var param_130: vec3; + var param_131: vec3; + var param_132: f32; + var param_133: f32; g_albedo = vec3(0f, 0f, 0f); g_debug_surface = vec3(0f, 0f, 0f); @@ -46330,179 +47408,189 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e213 = ReadSurface_u0028_(); - s_8 = _e213; - let _e214 = v_color_1; - d_7 = normalize(_e214.xyz); - let _e217 = d_7; - param_112 = _e217; - let _e218 = ImpostorRight_u0028_vf3_u003b((¶m_112)); - right_1 = _e218; - let _e219 = d_7; - let _e220 = right_1; - up_2 = cross(_e219, _e220); - let _e222 = right_1; - let _e224 = v_texcoord_1[0u]; - let _e228 = up_2; - let _e230 = v_texcoord_1[1u]; - offset_4 = ((_e222 * ((_e224 * 2f) - 1f)) + (_e228 * (1f - (_e230 * 2f)))); - let _e235 = d_7; - param_113 = _e235; - let _e236 = ImpostorEncode_u0028_vf3_u003b((¶m_113)); - g = (_e236 * 7f); - let _e238 = g; - base = clamp(floor(_e238), vec2(0f, 0f), vec2(6f, 6f)); - let _e241 = g; - let _e242 = base; - f = (_e241 - _e242); - let _e245 = f[0u]; - let _e247 = f[1u]; - lower = ((_e245 + _e247) < 1f); - let _e250 = lower; - if _e250 { - let _e251 = base; - local_25 = _e251; + let _e225 = ReadSurface_u0028_(); + s_9 = _e225; + let _e226 = v_color_1; + d_7 = normalize(_e226.xyz); + let _e229 = d_7; + param_118 = _e229; + let _e230 = ImpostorRight_u0028_vf3_u003b((¶m_118)); + right_1 = _e230; + let _e231 = d_7; + let _e232 = right_1; + up_2 = cross(_e231, _e232); + let _e234 = right_1; + let _e236 = v_texcoord_1[0u]; + let _e240 = up_2; + let _e242 = v_texcoord_1[1u]; + offset_4 = ((_e234 * ((_e236 * 2f) - 1f)) + (_e240 * (1f - (_e242 * 2f)))); + let _e247 = d_7; + param_119 = _e247; + let _e248 = ImpostorEncode_u0028_vf3_u003b((¶m_119)); + g = (_e248 * 7f); + let _e250 = g; + base = clamp(floor(_e250), vec2(0f, 0f), vec2(6f, 6f)); + let _e253 = g; + let _e254 = base; + f = (_e253 - _e254); + let _e257 = f[0u]; + let _e259 = f[1u]; + lower = ((_e257 + _e259) < 1f); + let _e262 = lower; + if _e262 { + let _e263 = base; + local_27 = _e263; } else { - let _e252 = base; - local_25 = (_e252 + vec2(1f, 1f)); - } - let _e254 = local_25; - c0_ = _e254; - let _e255 = base; - c1_ = (_e255 + vec2(1f, 0f)); - let _e257 = base; - c2_ = (_e257 + vec2(0f, 1f)); - let _e259 = lower; - if _e259 { - let _e261 = f[0u]; - let _e264 = f[1u]; - let _e267 = f[0u]; - let _e269 = f[1u]; - local_26 = vec3(((1f - _e261) - _e264), _e267, _e269); + let _e264 = base; + local_27 = (_e264 + vec2(1f, 1f)); + } + let _e266 = local_27; + c0_ = _e266; + let _e267 = base; + c1_ = (_e267 + vec2(1f, 0f)); + let _e269 = base; + c2_ = (_e269 + vec2(0f, 1f)); + let _e271 = lower; + if _e271 { + let _e273 = f[0u]; + let _e276 = f[1u]; + let _e279 = f[0u]; + let _e281 = f[1u]; + local_28 = vec3(((1f - _e273) - _e276), _e279, _e281); } else { - let _e272 = f[0u]; - let _e274 = f[1u]; - let _e278 = f[1u]; - let _e281 = f[0u]; - local_26 = vec3(((_e272 + _e274) - 1f), (1f - _e278), (1f - _e281)); - } - let _e284 = local_26; - w = _e284; - let _e285 = c0_; - param_114 = _e285; - let _e286 = offset_4; - param_115 = _e286; - let _e287 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_114), (¶m_115)); - uv0_ = _e287; - let _e288 = c1_; - param_116 = _e288; - let _e289 = offset_4; - param_117 = _e289; - let _e290 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_116), (¶m_117)); - uv1_ = _e290; - let _e291 = c2_; - param_118 = _e291; - let _e292 = offset_4; - param_119 = _e292; - let _e293 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_118), (¶m_119)); - uv2_ = _e293; - let _e294 = uv0_; - let _e295 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e294, 0f); - a0_ = _e295; - let _e296 = uv1_; - let _e297 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e296, 0f); - a1_ = _e297; - let _e298 = uv2_; - let _e299 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e298, 0f); - a2_ = _e299; - let _e300 = uv0_; - let _e301 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e300, 0f); - n0_ = _e301; - let _e302 = uv1_; - let _e303 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e302, 0f); - n1_ = _e303; - let _e304 = uv2_; - let _e305 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e304, 0f); - n2_ = _e305; - let _e306 = w; - let _e308 = a0_[3u]; - let _e310 = a1_[3u]; - let _e312 = a2_[3u]; - wa = (_e306 * vec3(_e308, _e310, _e312)); - let _e316 = wa[0u]; - let _e318 = wa[1u]; - let _e321 = wa[2u]; - alpha_3 = ((_e316 + _e318) + _e321); - let _e323 = alpha_3; - if (_e323 < 0.5f) { + let _e284 = f[0u]; + let _e286 = f[1u]; + let _e290 = f[1u]; + let _e293 = f[0u]; + local_28 = vec3(((_e284 + _e286) - 1f), (1f - _e290), (1f - _e293)); + } + let _e296 = local_28; + w = _e296; + let _e297 = c0_; + param_120 = _e297; + let _e298 = offset_4; + param_121 = _e298; + let _e299 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_120), (¶m_121)); + uv0_ = _e299; + let _e300 = c1_; + param_122 = _e300; + let _e301 = offset_4; + param_123 = _e301; + let _e302 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_122), (¶m_123)); + uv1_ = _e302; + let _e303 = c2_; + param_124 = _e303; + let _e304 = offset_4; + param_125 = _e304; + let _e305 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_124), (¶m_125)); + uv2_ = _e305; + let _e306 = uv0_; + let _e307 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e306, 0f); + a0_ = _e307; + let _e308 = uv1_; + let _e309 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e308, 0f); + a1_ = _e309; + let _e310 = uv2_; + let _e311 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e310, 0f); + a2_ = _e311; + let _e312 = uv0_; + let _e313 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e312, 0f); + n0_ = _e313; + let _e314 = uv1_; + let _e315 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e314, 0f); + n1_ = _e315; + let _e316 = uv2_; + let _e317 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e316, 0f); + n2_ = _e317; + let _e318 = w; + let _e320 = a0_[3u]; + let _e322 = a1_[3u]; + let _e324 = a2_[3u]; + wa = (_e318 * vec3(_e320, _e322, _e324)); + let _e328 = wa[0u]; + let _e330 = wa[1u]; + let _e333 = wa[2u]; + alpha_3 = ((_e328 + _e330) + _e333); + let _e335 = alpha_3; + if (_e335 < 0.5f) { discard; } - let _e325 = a0_; - let _e328 = wa[0u]; - let _e330 = a1_; - let _e333 = wa[1u]; - let _e336 = a2_; - let _e339 = wa[2u]; - let _e342 = alpha_3; - srgb_1 = ((((_e325.xyz * _e328) + (_e330.xyz * _e333)) + (_e336.xyz * _e339)) / vec3(_e342)); - let _e345 = n0_; - let _e350 = wa[0u]; - let _e352 = n1_; - let _e357 = wa[1u]; - let _e360 = n2_; - let _e365 = wa[2u]; - local_27 = (((((_e345.xyz * 2f) - vec3(1f, 1f, 1f)) * _e350) + (((_e352.xyz * 2f) - vec3(1f, 1f, 1f)) * _e357)) + (((_e360.xyz * 2f) - vec3(1f, 1f, 1f)) * _e365)); - let _e368 = local_27; - let _e369 = local_27; - let _e372 = local_27; - let _e373 = d_7; - local_27 = normalize(select(_e373, _e372, vec3((dot(_e368, _e369) > 0.000000000001f)))); - let _e377 = v_tangent_1; - worldRight = normalize(_e377.xyz); - let _e380 = v_normal_1; - worldFacing = normalize(_e380); - let _e382 = worldFacing; - let _e383 = worldRight; - worldUp = cross(_e382, _e383); - let _e385 = worldRight; - let _e386 = local_27; - let _e387 = right_1; - let _e390 = worldUp; - let _e391 = local_27; - let _e392 = up_2; - let _e396 = worldFacing; - let _e397 = local_27; - let _e398 = d_7; - s_8.n = normalize((((_e385 * dot(_e386, _e387)) + (_e390 * dot(_e391, _e392))) + (_e396 * dot(_e397, _e398)))); - let _e405 = s_8.n; - let _e407 = s_8.v; - s_8.n_dot_v = max(dot(_e405, _e407), 0.0001f); - let _e411 = srgb_1; - param_120 = _e411; - let _e412 = SrgbToLinear_u0028_vf3_u003b((¶m_120)); - let _e414 = frag_info.base_color; - param_121 = _e414.xyz; - let _e416 = SrgbToLinear_u0028_vf3_u003b((¶m_121)); - s_8.albedo = (_e412 * _e416); - s_8.alpha = 1f; - let _e421 = s_8.albedo; - g_albedo = _e421; - let _e423 = frag_info.ambient_ground; - let _e426 = frag_info.ambient_sky; - let _e430 = s_8.n[1u]; - let _e437 = frag_info.material[2u]; - s_8.ambient = (mix(_e423.xyz, _e426.xyz, vec3(((_e430 * 0.5f) + 0.5f))) * _e437); - let _e441 = s_8.albedo; - let _e443 = s_8.ambient; - ambient = (_e441 * _e443); - let _e445 = s_8; - param_122 = _e445; - let _e446 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_122)); - let _e447 = ambient; - param_123 = (_e446 + _e447); - param_124 = 1f; - param_125 = 1f; - WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_123), (¶m_124), (¶m_125)); + let _e337 = a0_; + let _e340 = wa[0u]; + let _e342 = a1_; + let _e345 = wa[1u]; + let _e348 = a2_; + let _e351 = wa[2u]; + let _e354 = alpha_3; + srgb_1 = ((((_e337.xyz * _e340) + (_e342.xyz * _e345)) + (_e348.xyz * _e351)) / vec3(_e354)); + let _e357 = n0_; + let _e362 = wa[0u]; + let _e364 = n1_; + let _e369 = wa[1u]; + let _e372 = n2_; + let _e377 = wa[2u]; + local_29 = (((((_e357.xyz * 2f) - vec3(1f, 1f, 1f)) * _e362) + (((_e364.xyz * 2f) - vec3(1f, 1f, 1f)) * _e369)) + (((_e372.xyz * 2f) - vec3(1f, 1f, 1f)) * _e377)); + let _e380 = local_29; + let _e381 = local_29; + let _e384 = local_29; + let _e385 = d_7; + local_29 = normalize(select(_e385, _e384, vec3((dot(_e380, _e381) > 0.000000000001f)))); + let _e389 = v_tangent_1; + worldRight = normalize(_e389.xyz); + let _e392 = v_normal_1; + worldFacing = normalize(_e392); + let _e394 = worldFacing; + let _e395 = worldRight; + worldUp = cross(_e394, _e395); + let _e397 = worldRight; + let _e398 = local_29; + let _e399 = right_1; + let _e402 = worldUp; + let _e403 = local_29; + let _e404 = up_2; + let _e408 = worldFacing; + let _e409 = local_29; + let _e410 = d_7; + s_9.n = normalize((((_e397 * dot(_e398, _e399)) + (_e402 * dot(_e403, _e404))) + (_e408 * dot(_e409, _e410)))); + let _e417 = s_9.n; + let _e419 = s_9.v; + s_9.n_dot_v = max(dot(_e417, _e419), 0.0001f); + let _e423 = srgb_1; + param_126 = _e423; + let _e424 = SrgbToLinear_u0028_vf3_u003b((¶m_126)); + let _e426 = frag_info.base_color; + param_127 = _e426.xyz; + let _e428 = SrgbToLinear_u0028_vf3_u003b((¶m_127)); + s_9.albedo = (_e424 * _e428); + s_9.alpha = 1f; + let _e433 = s_9.albedo; + g_albedo = _e433; + let _e435 = frag_info.ambient_ground; + let _e438 = frag_info.ambient_sky; + let _e442 = s_9.n[1u]; + let _e449 = frag_info.material[2u]; + s_9.ambient = (mix(_e435.xyz, _e438.xyz, vec3(((_e442 * 0.5f) + 0.5f))) * _e449); + let _e453 = s_9.albedo; + let _e455 = s_9.ambient; + ambient = (_e453 * _e455); + let _e457 = s_9; + param_128 = _e457; + let _e458 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_128)); + let _e459 = ambient; + lit_3 = (_e458 + _e459); + let _e461 = s_9; + param_129 = _e461; + let _e462 = lit_3; + param_130 = _e462; + let _e463 = WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_vf3_u003b((¶m_129), (¶m_130)); + if _e463 { + return; + } + let _e464 = lit_3; + param_131 = _e464; + param_132 = 1f; + param_133 = 1f; + WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_131), (¶m_132), (¶m_133)); return; } @@ -46544,7 +47632,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember( name: 'light_position', @@ -46641,6 +47729,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3 _budgets = { + 'debug-view-split': 0.01, 'teapot-generated-normals': 0.01, 'shadow-teapot': 0.01, 'bloom-sphere': 0.01, diff --git a/packages/flutter3d_webgpu/test/goldens/debug-view-split.png b/packages/flutter3d_webgpu/test/goldens/debug-view-split.png new file mode 100644 index 0000000000000000000000000000000000000000..d3015fd602a706396f78bec63a67c910891541e0 GIT binary patch literal 25814 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

    dC&xvE!C^bROwp zzvv_hMdqa?QxBWY{@dKMH;~YJ{W&UYS5`-zndlhyfsyu;N z@3e*L6>W+rG_o%*wGc2}+IvHRC53s?;S8Dbf*L(%DM zncU?^ORMZRYb0wYm$|ecoc;C%o8=?Xy0U!RCdi)~f~t=gso8F=Sn4+p2idwF96&7U zlqKdXL!;W$YtIOqhoOrl%<5_RJFxA@l;1N8=*{hiJztE(6S3@iI&ykNrhY_I zpV=~6cW987vb&NJ_U_+I^%r=!hp zu-rWR576PZOQavWT~BITsRRD0APez=urFrvX+pVzEI`Cr1puX&mseEBcM$NFCA|6$?ketnM*6jY2VU9ztzfmpK1XL zQDfBj?Y|mSkZ#4Yy;gSni`8srJN%oT2DX`2nv4QBUM6b*i@4zLnvK5uuFkSb=A#3? zU~Y^{IS4OdVSjoWugzcNb1ZeXU8dal&Wxnq`+;*MG4NSO!(SIWfGLTsrKG_Mnf>e2 zzNhA*rX>thQ&aItNskm$q-LZfV}vYEzDUKm3V%#Zb^aG_!c_)VWu6HkFLs_A35SV% zVnTJFAyuWn5g$sWbAP@c1rYB3wq5l8l;D!6b$101f2}^rmH^&-hu{WB>p2o@ z;6P4D8un`zhr=BWFA(JiG+we&Z|24ZRGbHt&&}7@U&HtwVZITU4x~B-Y!bq%j$Isj ze8nYN3A?p!^t&#q?> zEg6FgCB^66ODu8u+e26zE_Ve0mJG#kvPtNaP6~n1e+@ZLCb<&J+qARq_ss;A7u1D! zSssO#2+*r)*$ufjC}|s{@+OGZB6{4Hz?(H;M10|^lg^vkx)#kH&n2w86AdanwrP!& z{kSR*os;E{c}5CM!s#7rXrIK1w)V~5y)wJkRM_E5$Bixdv(OLgCzJLR{>pOGqn`6oxV4_U=Dl!5a2tca_ zpU8{VUHRr6s|J4Z!&)bGnm?)R$DS?|Dnyr3KdB=#27ReZUxh2J?*9xHc%Hwr*+{sd zooP;Co&n6AH4RMc0zQ5W>zwAdYw93}^<40;mce&>X{%g-`@d?0N#ueMS3m_}3v2Zh z2vGh@Q2Gs0e6j*G0m~r!wq#$luNL;h8kUj5r*0Gj$^+w9EabV>povaUYSi|gU@gv| zswwvV*q;IOf2Su!>)zy$JB!>7k`?k!N}h0AP5H$GOPKDX;b$CTmVA71&E+1H|zax3TKmRwS$t)OynS6=S|1nvmtfk^(gg&3|TF&^g)S;^k|Cv z^Wz<5QJ(_#i*#J-d{u+*loSK>Im9@favt4G}@W6q$=cZO2$X>^n! zHNzcrNl|e2s{cR^zew?4pzAg&k@?K33xhob>VC2?qmf)b{kGe&HbS)lAFD_^I+ZWy zX(QOU)x>!vLOH4Yj~Bc$QS}ix#09N@1g<3%AXYL zgQCojM&i{?-%ppYQM21Gw?FPrMSUS6n#RW->-F3~=Y{fa2!gqJjcZ1|SXcB{5TF=W z>J%}aHN@VPY*s8gH`1lvS5*t)Q3}Y99pFC4erNq|h^l+EP22c;hyOoZd6eNxvHs_i zkDUYOLp^x9?r#u1|LQx`AMOEy?6w7re_`nijX66Feqewi!(K(L( zQXyh7qH7cJ$|EIzS(+6jJhg^pxZHlVeeDV;F>swEx!~4j&aa;qowEshQTv^WujV7& zVALjM!l^HWlDm%327s5XzS(kIHjv7JiS9{uSI>ir9{u+rq3r31nMexmX1TL*FIL_X zH7|Or3n?z&1J5rvsE`r7{1B0c~|G7|0uaH zwnmp+PEU^a?S~}WF52n>@+Ua|{0<2j?}-LEFAo|F$gH67x|G`#%H^roy!QFf$M;_M zMnudTlPV-&CABuf%APz7F5D(9MbEP)k{JjJzAjK|VNlWFUlOM@o3(@6wVM6Aig2BR z>L+zh6%@=jjWVUP%2%)qy;Oj@68yXTa;0(AZAFLdk;1MA(va=zFomIxv9<-ELdAB+ zxqFIJPc^vw>hFEgcsv>S_#z+(fsfOB<#qD-ry^Y5i+a$2Nj76M@o?IcNrvUHr%tK7 zErx*rQUUC8&Amal4n1)%Uf#LdVl;ALRi8=yF3HL5OG-`Sd?JCfZ~HFRLnA6oA`#u7 zD=kvM7J&#NiXT^PJ+NKwh~g9xz6YO zpPElVMN@}d5UkE2)PHq#bq(pY5-k{$wNG?gd^5#<>ED;+IW_-;ay9tXWmL?;rE?{@ zd!kwSM#FEa-7rdY&_t_R3#}J9MeRL13hm%SZO)YDoWkU|gG-lRz=dP|uG-ohMg&E; zmMi~TuPt55w~+}sw}om8ZyMprs`uAdZbj&5zff|li;}ayG!YpzaFZQE=A+ZnP&9{h zUhou#0nS1tPmO~|{y)(BCp(HKL3*I(zCJ5-exIn2++yjCBe(_}-RRp9 z3yDwm&m0Jp&7~*KML0A{2GjR$-?J@=VSq^(a5s<*|CF;Q)pc-KV^7EM)81N=BO@2Z zlo7zN)@i#EaZFeBiOs>4kp+9t#f{Hgz9Thn+n^X1=VUc=UOjZoPs16>8I^4B+2ugb zdvvOk_koxxK)Nhg57G~C0TsO?`JFL5c(+FeuY;HiP?akVufelWCBZ;PicG*RT_hWK z^G#s@K|TN#OCuZL`=7z2D6ARUy<%kk85H=gCL8@4F+I1UM$H1){M{6 zfOzL2e)<96=W7Gl>J)<55^|h6pyZE8`w9E;c^x4+!&o5_mApEwz}?JfKyn|Z6#5^H z;nsvBR~JP%wk?2U^%|3ka!Fwzd|C^WumOxEbqe}Gf~s0CQF7gJBEA@`hjCW>dk(e$ z(j)+5U(Ole+@%1(g3=^i#$6AWbmG)Cc^l+WW%lS#1(N}98~jy*Qeh9_gDo$SXjTeB^jl?T<}ol)HQ>3-o`GyYJ*rU}ist!1w1RR~%ns zLiZ2~+}|G?m!)2N447pd62-w>371hcXNv)e)ZP@p#$(?SO&CUNzkw6R`nNk%aJdsM z8pt4;!@EMKsU@;z(HTJ_RNN1>dc4Z+ohw?EOM?|7YG*uHc&-Fv$ND5^=BlON4w9o( zwO0N;0@L8GjM!ozjqkkGwoEACRUQKstkc~u5clr9I2j{je#z7d=ywN@oy$~Nrdw^% zy3Os=9#%Ja%=-IxwK@^X+^zz7VG@l4WMCcF<-DivW#T`iK(C|$sJeKaQH4lY1KAE1 zU^8(k1=q~u2hP<(uQX)@&ASC&WP zIRg_&Rf5(i;H=XD@Y=egYHBEfpW5$_gZRNFRU87m7HZaj{MG;)G-R8*4y$;MiC=$X zw)Y^$>&;Z*JWT6va3>+G$edz*1eZBKdMa_+IWCwJ*fymj28oET)N6~0l4pzsWHPWB zxNuF`XU&y{mM;B|r!k#$19OMJeQ`*wSsmkN#APJc3~fmBM?t`hc!tTq-TCyJf|j{2 zE*_?I^cY^l^DL=wD60}whFC4te*Y8CexpEVfkIC^rmPtJv`Sf5izaw{1a~+3|G}0A z1F1_9ThT|UX6Uj2+);^r9wRqq&eXE+-)+()**>5I_78yn+an+~NWUT^JzZt*;2`dD zJdHP}6P*!0^(|-L(Xtk+6P)C$uu zpcGL>tL)1EVJ}&1H6sKEe0^NPs(ldzg4@eMGO(vRYBCNxHa5_-h*gXIDNz^P2>^0C z1&~;^OIik~;4I>EK^ZfXdPd#8XfPm58F~(%8%br5f9`(Avbj7{cIENu*(MivKMx%c zVnzY$LlO@_vBZz_<6}~lU5LZBub}%OimoO;jE8ii9k}YcPa*J#6emq_Wud^X3FT?5 zT1W-pC|}O}j~Whz&)9^kt2e;h`rNue*5TfOwsptfIv{zMoROgw;ib{+<^Vl631vqg zK!|zI*;#JKs!iz%2o$svVd(oT^K>4o_Pzp8)+kx^7Df4H!*6&2|7z}V(Gn|Co+`ZTi!&j$S`M~(HFH6O0apL#Qttr2 z**ZoRb6@iR2ZwZ;b${s2dE{5Xza1Bs@WN*pALElbIK2Zd)7A@T!kL-)fw!qo>p|aG zfC~>O$}g`)EV_lF%f4w2QBhIHXbQNliDbW4rpW^2)HJD}81ag^vZ`Zfe6D{rMV{N* z%GN%xwvfDrtwaxLcl-lX3|1|Tmal#7vUASMOM@VeC18JKA3Pj<>Z#)N#f4vGbCjOz zm?{*ByLCs(WjuY1N&8$#36wHD+DmI0b0{vo@TrG z!S>+8;7$awQW6+V2C|5Sh_9Q;2D%bqUz9;I!_7I9UeYYOf$6N_&!bVF89Rihm+5C-womMg$gwuuiEt!C>Ffv& zJWBQ?ncjKE+9G$sT3|GhB5ATD*t4%K5_F%6eA3IE9&FWtTwamq7Y#!=4Qx*g!`x7E zN|&dw;Ku-YvZG`ovnH;CbZO_v}pxZsCT*BPV+Ih8sNq t{vV2C8q0aLBO{nI{)78W%wLl2UGWmn!FN3YHYT|OQ`c21P__E-e*kE%r-1+f literal 0 HcmV?d00001 diff --git a/site/content/core/architecture.md b/site/content/core/architecture.md index a3d0a7ab0..4b380b4fb 100644 --- a/site/content/core/architecture.md +++ b/site/content/core/architecture.md @@ -53,7 +53,7 @@ Each exists for a different reason, and none of them is a fallback for another. Its shaders are GLSL ES 3.00 generated from `flutter3d_shaders`. Nothing checked that the generated file was current for a long time, and it cost two failures that reported nothing: a uniform member the browser's copy had never heard of, and a sky shader from before the sky was rewritten. `tool/ci.sh` regenerates it and fails on the diff now, and the compiled Impeller bundle, which cannot be diffed, is held by a freshness rule instead. -**`flutter3d_cpu`** is the one that makes the agreement mean something. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. It is also what makes 86 golden scenes checkable in a headless run, and what caught three bugs that every simulation test passed. +**`flutter3d_cpu`** is the one that makes the agreement mean something. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. It is also what makes 87 golden scenes checkable in a headless run, and what caught three bugs that every simulation test passed. **`flutter3d_webgpu`** asks a question none of the other three could: whether the *shader* half of the contract is a seam. The first three all read one text (`impellerc` compiles the GLSL, the WebGL generator translates it, the software rasteriser transcribes it into Dart by hand), and WebGPU cannot, because WGSL is a different language and a browser will not take SPIR-V. So its table is generated down a second toolchain, the same manifest through `glslangValidator` and then `naga`, and all thirty-nine stages come out. What it declines it declines by name: no blend constant (WebGPU has no colour/alpha pair for one) and no wireframe (no polygon fill mode in the API at all). Two more used to be on that list. Rendering into a mip is on now and cost no code: a reflection probe convolves its own chain, and a face and a level are `baseArrayLayer` and `baseMipLevel` on an ordinary view here. Block compression is on too, and what it cost was the asking: a WebGPU device gets exactly the features it requested, and requesting one the adapter does not carry rejects the device instead of handing back a lesser one, so the adapter is asked, the intersection is requested, and the capability answers from what was granted. diff --git a/site/content/core/index.md b/site/content/core/index.md index 38be00eab..aaaed965c 100644 --- a/site/content/core/index.md +++ b/site/content/core/index.md @@ -25,7 +25,7 @@ Nothing in this section is genre knowledge. That property is what made the secon |---|---| | `flutter3d_impeller` | `flutter_gpu`, over Metal and Vulkan. The production one | | `flutter3d_webgl` | WebGL2 in the browser. Runs all three games at a fixed resolution and a lower frame rate, and is what an ordinary web build opens | -| `flutter3d_cpu` | Nothing: it rasterises in Dart, so 86 golden scenes stay checkable with no GPU in the room | +| `flutter3d_cpu` | Nothing: it rasterises in Dart, so 87 golden scenes stay checkable with no GPU in the room | | `flutter3d_webgpu` | WebGPU in a browser that has an adapter. Reached by `--dart-define=FLUTTER3D_WEBGPU=true`, because a build that can try it ships it | `flutter3d_conformance` is the suite each of them passed before it belonged in that table, and [Writing a HAL backend](/core/backends/) is the guide for writing the fifth. diff --git a/site/content/core/rendering.md b/site/content/core/rendering.md index 17b9f8116..cf40e97d7 100644 --- a/site/content/core/rendering.md +++ b/site/content/core/rendering.md @@ -487,7 +487,7 @@ It is marched at half resolution and brought up by depth, so a halo behind a pil ```dart SkySettings( - enabled: true, // off by default: eighty-six goldens are recorded against none + enabled: true, // off by default: eighty-seven goldens are recorded against none zenith: Vector3(0.10, 0.22, 0.52), horizon: Vector3(0.42, 0.50, 0.62), nadir: Vector3(0.06, 0.06, 0.07), // what fills the frame looking down at nothing diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 3c2cefc3a..de9e67e48 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11234 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-six scenes stay checkable in a headless run. +There are 11243 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-seven scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/packages.md b/site/content/reference/packages.md index bf7443725..733a6cf12 100644 --- a/site/content/reference/packages.md +++ b/site/content/reference/packages.md @@ -60,7 +60,7 @@ A rasteriser written in Dart. `CpuDevice` implements the same HAL, plus PNG outp Not a fallback. Two hardware backends agreeing proves less than it looks like: both are driven by a C API and both rasterise on a GPU, so an assumption shared by graphics hardware would be invisible to the pair of them. This one shares nothing with either, no driver, no shading language, no command buffer. -It is how eighty-six golden scenes are checkable with no GPU in the room, and it is a dev dependency of every game because three shipped bugs would have been caught by rendering a single frame in a test. It stopped being *only* a dev dependency once `flutter3d_app` started reaching for it as the runtime fallback when Impeller will not start, which makes it a real production path now, if a last-resort one. +It is how eighty-seven golden scenes are checkable with no GPU in the room, and it is a dev dependency of every game because three shipped bugs would have been caught by rendering a single frame in a test. It stopped being *only* a dev dependency once `flutter3d_app` started reaching for it as the runtime fallback when Impeller will not start, which makes it a real production path now, if a last-resort one. ### `flutter3d_conformance` The suite any fourth backend would have to pass before it counted as one, plus the cross-backend comparison with per-scene budgets. diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index be1da9c15..0df1a3dd2 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,10 +1,10 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11234 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11243 tests need a GPU. --- # Testing -11234 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11243 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| @@ -20,7 +20,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flutter3d_game_racing` | 223 | | `flutter3d_webgl` | 62 | | `flutter3d_game_platformer` | 221 | | `flutter3d_hardware` | 70 | | `apps/flutter3d_demo_platformer` | 208 | | `flutter3d_impeller` | 64 | -| `flutter3d_cpu` | 364 | | `apps/flutter3d_demo_strategy` | 50 | +| `flutter3d_cpu` | 373 | | `apps/flutter3d_demo_strategy` | 50 | | `apps/flutter3d_editor` | 234 | | `apps/flutter3d_demo_arcade` | 20 | | `apps/flutter3d_demo_racing` | 162 | | `pointer_lock` | 28 | | `flutter3d_physics` | 214 | | `flutter3d_webgpu` | 193 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11208 rather than 11234: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11217 rather than 11243: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. @@ -52,7 +52,7 @@ tool/ci.sh # shaders, analyze, every test ## Four independent golden sets, not one -Eighty-six scenes are rendered four times: through Impeller, through the software rasteriser in `flutter3d_cpu`, through WebGL2, and through WebGPU, the last two in a driven browser. Each backend is held to zero differing pixels against its own set, with a per-channel tolerance of 8. +Eighty-seven scenes are rendered four times: through Impeller, through the software rasteriser in `flutter3d_cpu`, through WebGL2, and through WebGPU, the last two in a driven browser. Each backend is held to zero differing pixels against its own set, with a per-channel tolerance of 8. The browser sets are recorded when a branch lands, not beside it (`golden_web.sh` holds one fixed port for the whole of its run), so a new scene is in the other sets for as long as that takes. Which scenes, and what they are waiting for, is `_provisional` in `flutter3d_webgl/test/cross_backend_test.dart`: the comparison is skipped with the reason printed instead of quietly missing, and the check beside it fails the moment a reference lands and the name is still there. @@ -65,7 +65,7 @@ The browser sets are recorded when a branch lands, not beside it (`golden_web.sh {{golden3 shadow-teapot | One scene, three sets: a GPU through Metal, a rasteriser written in Dart, and a browser. The pictures on this site are the Impeller set.}}

    -

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps eighty-six scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    +

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps eighty-seven scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    `cross_backend_test.dart` compares them with per-scene budgets, and any new backend has to pass `flutter3d_conformance` before it counts as one. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 005ff6f06..65d9de6af 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -316,15 +316,13 @@ and the published web editor needs it checked from its own address. | Id | Item | Theirs | Ours today | Effort | |---|---|---|---|---| -| P1 | SMAA 1x | `render/smaa_pass.dart` | FXAA, TAA/TAAU | M | -| P2 | Lens flare, lens distortion, `.cube` LUT import | `bloom_pass.dart` flares, `screen_distortion.dart`, `post_process/color_lut.dart` | halation, strip LUT grading (`doc/postprocessing-parity.md`, `gfx-30n` partial) | S–M | +| P1 | SMAA 1x | `render/smaa_pass.dart` | done, off by default; see below | M | +| P2 | Lens flare, lens distortion, `.cube` LUT import | `bloom_pass.dart` flares, `screen_distortion.dart`, `post_process/color_lut.dart` | done, off by default; see below | S–M | | P3 | Projected box decals painting a material | `decal.dart` (0.24) | done, off by default; see below | M | -| P4 | Planar reflections; public render-to-texture camera | `components/planar_reflector_component.dart`, `render_texture.dart` | SSR, probes; no public RTT | M | -| P3 | Projected box decals painting a material | `decal.dart` (0.24) | none | M | | P4 | Planar reflections; public render-to-texture camera | `components/planar_reflector_component.dart`, `render_texture.dart` | done, reflections off by default; see below | M | -| P5 | Physical sky (atmospheric scattering), stars, height fog | `sky_sources.dart` `PhysicalSkySource` | gradient sky, cubemap, `Atmosphere` presets | M | -| P6 | Debug views (material channels, UV, NaN, split wipe) and render stats | `render/debug_view.dart`, `render_stats.dart` (0.24) | viewport shading, NaN scan in `sim_mcp` | S | -| P7 | Orthographic everywhere, per-draw ranges, alpha to coverage, render order | 0.24 commits | orthographic exists; check every effect | S | +| P5 | Physical sky (atmospheric scattering), stars, height fog | `sky_sources.dart` `PhysicalSkySource` | done, off by default; see below | M | +| P6 | Debug views (material channels, UV, NaN, split wipe) and render stats | `render/debug_view.dart`, `render_stats.dart` (0.24) | done, off by default; see below | S | +| P7 | Orthographic everywhere, per-draw ranges, alpha to coverage, render order | 0.24 commits | per-draw ranges done; orthography, alpha to coverage and order left | S | **P1 done.** `AntiAliasSettings(method: EdgeSmoothing.smaa)`: edges, area weights and neighbourhood blend as three stages on all four backends, the @@ -428,9 +426,6 @@ pays per view; a render texture has no post chain, no multisampling and no mips, and its aspect is the texture's; reflectors and render textures in the level format, the editor and the MCP tools; the comparison page row, with the 0.9.0 revision (decision 19). -| P5 | Physical sky (atmospheric scattering), stars, height fog | `sky_sources.dart` `PhysicalSkySource` | done, off by default; see below | M | -| P6 | Debug views (material channels, UV, NaN, split wipe) and render stats | `render/debug_view.dart`, `render_stats.dart` (0.24) | viewport shading, NaN scan in `sim_mcp` | S | -| P7 | Orthographic everywhere, per-draw ranges, alpha to coverage, render order | 0.24 commits | orthographic exists; check every effect | S | **P5. Physical sky, stars, height fog.** *Done:* `SkySettings.physical` takes a `PhysicalSky` (molecular and haze scattering with their scale @@ -474,10 +469,42 @@ the sky; height fog on particles and splats, which fog as a flat fog at the camera's density; an HDR environment from the sky (`fromSky` is eight bits, so a bright horizon clips); the sky and fog in the level format, the editor and the MCP tools; timing on the Galaxy A55; the -comparison page row, with the 0.9.0 revision (decision 19). The unit tests -were written and analysed but not run in the session that wrote them; run -`flutter test test/sky_physical_test.dart` in `packages/flutter3d` and the -whole suite before relying on this row. +comparison page row, with the 0.9.0 revision (decision 19). Two claims +needed fixing once the tests ran: towards and away from a setting sun are now +compared at one height above the horizon, and the drawn frames are odd-sized +so the centre pixel looks along the camera's axis. + +**P6. Debug views and render stats.** *Done:* `RenderSettings.debugView` +takes a `DebugViewSettings` — a `DebugView` (albedo, the shading normal, +roughness, metalness, occlusion, emission, the base map's UV, and +`nonFinite`, magenta where the light is NaN or infinite over its luminance in +grey) and a `split`, the share of the width left lit. The lit models ask +`WriteDebugView` in `surface.glsl` before writing their light, so a channel +shows what the maps did, not what the surface buffer kept; `FragInfo` grew +`debug_view` (code, split, the scene target's width) at its end. The +composite passes the debug side through its encode alone, decided at the end +of `main` so WGSL's uniform control flow holds (`CompositeInfo.lens` y and +z). Reflections and probes clear it, so neither bakes a channel in. NaN is +found by comparison: `floatBitsToUint` took `impellerc` down in its GLSL ES +output. `FrameResult.targetBytes` counts every texture the frame's graph +touched once, base level times slices times samples (`textureBytes`); the +draws, triangles, instances, pipeline switches and per-pass costs were +already on `FrameResult`. CPU mirrors in every lit model, the impostor and +the composite. Tests: `debug_view_test.dart` (off to the bit, each channel +as the material's number under a doubled exposure, the UV gradient, the +split, NaN magenta and a sound surface grey; mutation-checked on the +composite's flag) and `render_stats_test.dart` (the byte law, a frame's +targets, the glow's chain counted once, draws and triangles growing with +cubes); `debug-view-split` (the teapot lit left, its normal right) in all +four sets, the software set and WebGPU at 0 of 172800 pixels from Impeller, +WebGL at 0.359% on the silhouette. +*Left:* whether Metal, compiled with fast math, keeps the NaN comparison is +not checked — no golden draws a NaN; stages that are not lit materials (sky, +particles, splats, glass's own pass) draw as ever and reach the debug side +untoned; effects between the scene and the composite (reflections, depth of +field, motion blur, a temporal resolve) still run over the channel; a mip +chain is not in `targetBytes`; the view in the editor's viewport and the MCP +tools; the comparison page row (decision 19). Already ours and not theirs, to keep: volumetric fog, weighted blended OIT, motion blur, local exposure and HDR out, EVSM, impostors, Hi-Z and software diff --git a/tool/ci.sh b/tool/ci.sh index 41d33239f..5d673f5f8 100755 --- a/tool/ci.sh +++ b/tool/ci.sh @@ -9,7 +9,7 @@ # # What it does NOT do, stated so the gap is not mistaken for coverage: # -# * The Impeller half of the golden set. Those eighty-six scenes render through +# * The Impeller half of the golden set. Those eighty-seven scenes render through # flutter_gpu and need a real device, so they run from # packages/flutter3d/tool/golden.sh on a machine with a GPU. The software # half runs here, and cross_backend_test.dart compares the two committed From 2379a5538bd20574550abdf680d82f5ff6f11d67 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 14:35:03 +0300 Subject: [PATCH 053/141] An orthographic camera stops treating its eye as a point Through an orthographic lens the rays are parallel, and the eye is only where the camera was put along its axis. Highlights, Fresnel and reflections are now measured against the view axis, so they no longer slide across the frame as the camera pans; fog thickens with depth from the eye's plane instead of in rings round it; and the sky is seen through a sixty-degree lens turned with the camera instead of as one colour. FogInfo.projection carries which lens it is, and isOrthographic reads it off a matrix for the views that have no camera. Light shafts start their march on the eye's plane, as the volumetric fog already did; through a perspective lens that moves nothing. The orthographic-metal scene is in all four golden sets. --- ARCHITECTURE.md | 8 +- README.md | 4 +- .../example/lib/src/spike/backend_web.dart | 2 +- .../example/lib/src/spike/golden_runner.dart | 2 +- .../example/lib/src/spike/golden_scenes.dart | 12 + .../example/lib/src/spike/golden_stages.dart | 45 + .../lib/src/spike/golden_store_web.dart | 4 +- packages/flutter3d/test/ambient_test.dart | 2 +- packages/flutter3d/test/anisotropy_test.dart | 4 +- packages/flutter3d/test/auto_batch_test.dart | 4 +- packages/flutter3d/test/fog_test.dart | 2 +- .../test/goldens/orthographic-metal.png | Bin 0 -> 47623 bytes packages/flutter3d/test/grade_test.dart | 2 +- packages/flutter3d/test/halation_test.dart | 2 +- .../test/irradiance_bounce_test.dart | 2 +- .../flutter3d/test/masked_shadow_test.dart | 2 +- .../test/material_vertex_stage_test.dart | 2 +- .../test/per_view_exposure_test.dart | 2 +- .../flutter3d/test/render_scale_test.dart | 2 +- packages/flutter3d/test/sharpen_test.dart | 2 +- .../flutter3d/test/sky_procedural_test.dart | 2 +- packages/flutter3d/test/ssao_blur_test.dart | 2 +- packages/flutter3d_core/CHANGELOG.md | 10 + .../src/engine/render/render_settings.dart | 4 +- .../lib/src/engine/render/renderer.dart | 12 +- .../engine/render/renderer_planar_pass.dart | 2 + .../src/engine/render/renderer_post_pass.dart | 4 +- .../engine/render/renderer_probe_pass.dart | 2 + .../engine/render/renderer_scene_pass.dart | 5 + .../engine/render/renderer_shadow_pass.dart | 2 +- .../src/engine/render/renderer_sky_pass.dart | 58 +- .../render/renderer_transparency_pass.dart | 2 + .../lib/src/engine/scene/projection.dart | 18 + .../lib/src/engine/scene/scene.dart | 1 + .../formats/ktx2_supercompression_test.dart | 2 +- packages/flutter3d_cpu/CHANGELOG.md | 3 + .../lib/src/cpu_shaders_color.dart | 27 +- .../lib/src/cpu_shaders_planar.dart | 7 +- .../lib/src/cpu_shaders_post.dart | 2 +- .../lib/src/cpu_shaders_reflections.dart | 10 +- .../lib/src/cpu_shaders_surface.dart | 7 +- .../flutter3d_cpu/lib/src/cpu_texture.dart | 2 +- .../flutter3d_cpu/lib/src/portable_log2.dart | 2 +- .../test/cross_backend_test.dart | 1 + .../test/goldens/orthographic-metal.png | Bin 0 -> 47623 bytes .../flutter3d_cpu/test/orthographic_test.dart | 179 + .../test/gpu_formats_test.dart | 2 +- packages/flutter3d_shaders/CHANGELOG.md | 5 + .../flutter3d_shaders/lib/typed_blocks.dart | 4 + .../flutter3d_shaders/lib/uniform_blocks.dart | 10 + .../flutter3d_shaders/shaders/lib/color.glsl | 40 +- .../shaders/lib/surface.glsl | 8 + .../shaders/lighting/planar_reflection.frag | 2 +- .../shaders/post/light_shafts.frag | 17 +- packages/flutter3d_webgl/CHANGELOG.md | 3 + .../flutter3d_webgl/lib/engine_shaders.dart | 683 +++- .../test/cross_backend_test.dart | 4 + .../test/goldens/orthographic-metal.png | Bin 0 -> 46948 bytes .../test/lighting_models_test.dart | 2 +- packages/flutter3d_webgl/tool/golden_web.sh | 2 +- packages/flutter3d_webgpu/CHANGELOG.md | 3 + .../flutter3d_webgpu/lib/engine_shaders.dart | 3055 +++++++++-------- .../test/cross_backend_test.dart | 1 + .../test/goldens/orthographic-metal.png | Bin 0 -> 47636 bytes site/content/core/architecture.md | 2 +- site/content/core/index.md | 2 +- site/content/core/rendering.md | 2 +- site/content/quickstart.md | 2 +- site/content/reference/packages.md | 2 +- site/content/reference/testing.md | 12 +- tasks/0.9-engine-roadmap.md | 29 + tool/ci.sh | 2 +- 72 files changed, 2853 insertions(+), 1512 deletions(-) create mode 100644 packages/flutter3d/test/goldens/orthographic-metal.png create mode 100644 packages/flutter3d_cpu/test/goldens/orthographic-metal.png create mode 100644 packages/flutter3d_cpu/test/orthographic_test.dart create mode 100644 packages/flutter3d_webgl/test/goldens/orthographic-metal.png create mode 100644 packages/flutter3d_webgpu/test/goldens/orthographic-metal.png diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9c49e63c1..1e4f1d17b 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11243 tests** across 43 packages and 10 applications | +| Unit tests | **11248 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | @@ -2731,7 +2731,7 @@ material parameter — is the case `doc/boundary-0.5.0.md` worked through for set belongs to whoever builds on the package rather than to the package itself. -**Golden render tests.** 87 scenes against **four complete independent +**Golden render tests.** 88 scenes against **four complete independent reference sets** — Impeller, the software rasteriser, WebGL2 and WebGPU — each held to zero differing pixels against its own set, with a per-channel tolerance of 8. @@ -3143,7 +3143,7 @@ metres. The directional light's cascades fit the view up to that distance and nothing beyond it casts — a level whose far end matters visually wants the number raised, and pays for it in texels. -**The web backend draws all eighty-seven golden scenes the way Impeller does**, +**The web backend draws all eighty-eight golden scenes the way Impeller does**, between 0.01% and 0.42% of pixels differing by more than 8 per channel — the silhouette's worth of disagreement two rasterisers always have. Two of those numbers fell when the minification filter learned to read a sampler's @@ -3259,7 +3259,7 @@ and charge each of them the bytes, and neither is a change to make on somebody else's behalf. A build that wants it says so in one flag, and the engine's own example takes the same answer from `?backend=webgpu` in the URL — a query parameter rather than a define, because the browser golden stand's whole saving -is one dart2js run serving eighty-seven scenes and both browser backends. +is one dart2js run serving eighty-eight scenes and both browser backends. **`flutter3d_shaders` is one text and no two backends take it the same way.** Impeller compiles the GLSL with `impellerc`; the WebGL2 generator translates it diff --git a/README.md b/README.md index 6e3060dfd..5d81b03f5 100644 --- a/README.md +++ b/README.md @@ -158,9 +158,9 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11243 tests across forty-three packages and nine applications. The +There are 11248 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other -half is rendered by the software backend, which is what makes 87 scenes +half is rendered by the software backend, which is what makes 88 scenes checkable in a headless run. Several steps run in a browser. `flutter test --platform chrome` covers the two diff --git a/packages/flutter3d/example/lib/src/spike/backend_web.dart b/packages/flutter3d/example/lib/src/spike/backend_web.dart index 7ddfdcad3..2199a35ba 100644 --- a/packages/flutter3d/example/lib/src/spike/backend_web.dart +++ b/packages/flutter3d/example/lib/src/spike/backend_web.dart @@ -13,7 +13,7 @@ import 'golden_store.dart'; /// /// Read from the URL, exactly the way the golden scene and the record/compare /// direction are read, and for exactly the same arithmetic: one dart2js run -/// serves eighty-seven scenes because the scene is a query parameter, and one +/// serves eighty-eight scenes because the scene is a query parameter, and one /// dart2js run serves the browser backends because this is one too. A /// `--dart-define` here would be a build per backend, which is the whole saving /// of the browser golden stand spent on a single word. diff --git a/packages/flutter3d/example/lib/src/spike/golden_runner.dart b/packages/flutter3d/example/lib/src/spike/golden_runner.dart index 923b290c3..db859322d 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_runner.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_runner.dart @@ -122,7 +122,7 @@ final class GoldenRunner { scene, // The store's answer wins where it has one. A desktop run takes the // direction from the environment and a browser run from the URL, for the - // same reason on both: one build has to serve eighty-seven scenes in both + // same reason on both: one build has to serve eighty-eight scenes in both // directions, and anything the compiler sees is another build. update: updateOverride ?? diff --git a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart index b29e2451f..b7870b3f9 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_scenes.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_scenes.dart @@ -1222,6 +1222,18 @@ final List kGoldenScenes = [ configure: GoldenStages.skyPhysicalNightSettings, ), + // P7. Metal spheres on a floor under a sky in light fog, through an + // orthographic camera. + const GoldenScene( + name: 'orthographic-metal', + source: 'Cube', + shadows: false, + bloom: false, + ground: false, + stage: GoldenStages.orthographicMetal, + configure: GoldenStages.orthographicMetalSettings, + ), + // P6. `shadow-teapot` wiped at the middle: lit on the left, the shading // normal on the right. const GoldenScene( diff --git a/packages/flutter3d/example/lib/src/spike/golden_stages.dart b/packages/flutter3d/example/lib/src/spike/golden_stages.dart index 30721de57..8e6a7f5ed 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_stages.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_stages.dart @@ -1170,6 +1170,51 @@ abstract final class GoldenStages { static RenderSettings lensDistortion(RenderSettings settings) => settings.copyWith(look: settings.look.copyWith(distortion: 0.3)); + // ------------------------------------------------------------------ P7 + + /// `orthographic-metal`: three metal spheres, rough to smooth, on a floor + /// that ends under a sky, in light fog, through an orthographic camera + /// looking down at them from the corner — the three things that read the + /// eye's position as a point the light travels to. Their highlights sit + /// in the same place on each sphere rather than sliding towards where the + /// eye's point projects, the fog lies flat rather than in rings round it, + /// and the sky is a gradient seen through a sixty-degree lens rather than + /// one colour. + static Future orthographicMetal(GoldenStage stage) async { + final device = stage.device; + stage.sun.setLocalForward(Vector3(-1.0, -2.0, -1.5).normalized()); + return GoldenStaged( + nodes: [ + _slab( + device, + Vector3(6.0, 0.2, 6.0), + Vector3(0.0, -0.1, 0.0), + Material(baseColor: Vector4(0.6, 0.6, 0.58, 1.0), roughness: 0.9), + ), + for (final (i, roughness) in [0.6, 0.35, 0.15].indexed) + _sphere( + device, + Vector3(-1.5 + i * 1.5, 0.5, 0.0), + Material( + baseColor: Vector4(0.9, 0.75, 0.5, 1.0), + metallic: 1.0, + roughness: roughness, + ), + ), + ], + everyFrame: (_, _) => stage.camera + ..projection = const OrthographicProjection(height: 5.0, far: 100.0) + ..setPosition(7.0, 6.0, 9.0) + ..lookAt(Vector3(0.0, 0.3, 0.0)), + ); + } + + static RenderSettings orthographicMetalSettings(RenderSettings settings) => + settings.copyWith( + sky: const SkySettings(enabled: true), + fog: FogSettings(color: Vector3(0.7, 0.75, 0.8), density: 0.03), + ); + // ------------------------------------------------------------------ P6 /// `debug-view-split`: the lit teapot left of the middle and its shading diff --git a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart index e62327b92..54e8023a9 100644 --- a/packages/flutter3d/example/lib/src/spike/golden_store_web.dart +++ b/packages/flutter3d/example/lib/src/spike/golden_store_web.dart @@ -40,7 +40,7 @@ void reportLine(String message) { /// The scene named in the page's URL, if any. /// /// A run-time choice here, where the desktop path takes a compile-time define. -/// The reason is arithmetic: the suite is eighty-seven scenes, and rebuilding +/// The reason is arithmetic: the suite is eighty-eight scenes, and rebuilding /// the bundle for each is seventy-eight dart2js runs to compare seventy-eight /// pictures. One build and seventy-eight navigations is the same information /// in a fraction of the time. @@ -58,7 +58,7 @@ String? get sceneOverride { /// Whether this run records rather than compares, from the page's URL. /// /// A run-time choice for the same reason [sceneOverride] is one: the suite is -/// eighty-seven scenes and rebuilding for each would be seventy-eight dart2js +/// eighty-eight scenes and rebuilding for each would be seventy-eight dart2js /// runs. One build serves both directions, and the URL says which. bool get updateOverride => Uri.base.queryParameters['update'] == '1'; diff --git a/packages/flutter3d/test/ambient_test.dart b/packages/flutter3d/test/ambient_test.dart index e25a66e6b..c174d130a 100644 --- a/packages/flutter3d/test/ambient_test.dart +++ b/packages/flutter3d/test/ambient_test.dart @@ -141,7 +141,7 @@ void main() { // approximately, bit for bit. // // Mutation: default either end of the hemisphere to anything but white. - // Eighty-seven goldens move at once. + // Eighty-eight goldens move at once. final lit = await _pixels(_ball(), _noSky); final top = _band(lit, 0.15, 0.35); final bottom = _band(lit, 0.65, 0.85); diff --git a/packages/flutter3d/test/anisotropy_test.dart b/packages/flutter3d/test/anisotropy_test.dart index 0666050c6..f933c25a7 100644 --- a/packages/flutter3d/test/anisotropy_test.dart +++ b/packages/flutter3d/test/anisotropy_test.dart @@ -24,7 +24,7 @@ /// which spends the taps and throws away what they bought — contrast drops /// *below* the trilinear baseline and the first assertion fails; and letting /// a sampler that asked for one reach the new path at all, which would move a -/// backend eighty-seven golden scenes are recorded on. +/// backend eighty-eight golden scenes are recorded on. /// /// **One they do not catch, said rather than hidden:** taking the taps along /// the *short* axis instead of the long one passes every line here. The @@ -213,7 +213,7 @@ void main() { }); test('asking for one tap is the old sampler exactly', () async { - // What makes this safe to add to the backend eighty-seven golden scenes are + // What makes this safe to add to the backend eighty-eight golden scenes are // recorded on. Not "close": the same bytes, because with `anisotropy` at // one the new path is not reached at all. expect(await _floorAt(1), await _floorAt(1)); diff --git a/packages/flutter3d/test/auto_batch_test.dart b/packages/flutter3d/test/auto_batch_test.dart index 9abd00adf..118613680 100644 --- a/packages/flutter3d/test/auto_batch_test.dart +++ b/packages/flutter3d/test/auto_batch_test.dart @@ -22,7 +22,7 @@ /// three backends.** It computes in Dart doubles; Impeller, WebGL and WebGPU /// compute in 32-bit floats, where those two expressions have far less room /// before they part. Nothing here can run them, so the setting stays off by -/// default — which is also what keeps the eighty-seven goldens where they are. +/// default — which is also what keeps the eighty-eight goldens where they are. library; import 'dart:math' as math; @@ -191,7 +191,7 @@ void main() { }); test('nothing is batched unless it is asked for', () async { - // Which is what keeps the eighty-seven goldens where they are: the default + // Which is what keeps the eighty-eight goldens where they are: the default // frame is the frame it always was, to the byte. final device = _device(); final scene = _field(device, count: 100); diff --git a/packages/flutter3d/test/fog_test.dart b/packages/flutter3d/test/fog_test.dart index 73bc1b6d7..b4e439a41 100644 --- a/packages/flutter3d/test/fog_test.dart +++ b/packages/flutter3d/test/fog_test.dart @@ -151,7 +151,7 @@ void main() { test('no fog is byte-identical to the fog nobody asked for', () async { // The early return at zero density is not an optimisation. The golden sets - // — eighty-seven scenes, zero-pixel threshold, three backends — are all + // — eighty-eight scenes, zero-pixel threshold, three backends — are all // recorded with the default `FogSettings()`, and this is the property that // lets them stay recorded. final none = FogSettings(); diff --git a/packages/flutter3d/test/goldens/orthographic-metal.png b/packages/flutter3d/test/goldens/orthographic-metal.png new file mode 100644 index 0000000000000000000000000000000000000000..6185c32e89efc9ee8d750b9e01c4b5963901d68b GIT binary patch literal 47623 zcmb4qby(9;_%ER-At*>GDIJ1zgNSq~DcvC5J($uU(lt_)PU#$@JEWV@lN>N=Ft&@o zxWD_{=l*l|Y~SzMc}~5b_dVylaZdCH4aFw}F9@)(u%0L@z1PCR!d}1seSVC0pA#>_ z&T#*F;Q3Bj=kfgq@Yp&E3yT3u`Ms=;U-lu`Aeni<|Fz!=6MtPJc_+ zfs#sl>n6B>eV9E?*m$UbZ&7mm78QzQyHekbHBosXko2lVf|Nz8Kq>Amsp>D;kOdJ- zM=XuM zxZgk8KaHaAT1Xxa$M|4HJb z2i2L-=ewg;);|O?Xw_n)nBqh?gF^I=cqt?F$6N^zx&xvvBJF3HX8%!Mz1;!m|A)`a zUw!+F`TKtmL5uum(Y_GNOZdbpLo&$r+dW19+K8fzRww#o7zrx=&vO6pd}y4dS%@>2 z)$!?na>S?{DXT^Ir@CNNiBy z{_E~vpF3Oslh$4TpY;EKz9^;Fe+{Y{YkI|-2%A4<0-G$5!d@)Gp3proI69u5}NotmA--ibl z-dAnMBc5(Er?KjMR*hHPTJ+MrH~FLt5+CJCII7==dIsI~n-yA(V#*EEzYBT~2wVq3 zfo`@Fh}f@#K-+FX81jB9@Sdzt+!9UL>)&Ut5vVt{~jo=sM9zLwC!J!pE|fT zHw1zGo3uI%p19EA&K0ST4qyI~8a3?y))Cyh+B+#+cTeX%H=2&_ zGjY83l1$>)hAj;y0+peBuv5A;3Lb@L)6XUE5t?@l2xj9;1KdSZJ)#jAiw`;p){lbS zbrk2woCbVH&ElY}$Rv5`M;DXc@M2QwFg*wGWJys!gOT28r|B({DEV~&F=r!1Ze1RE z_uO1*#Dg|HLg2?J$dOvI({q+m%i<1Nz+sIT#9LUR~cs7S9&A zQW{%LFY1S9(LpQ$XCFc&;`x}VtQ;k=qE1joTjK@1n_{Q+m9vO@HuF&E-;U_s!iSoj zgB>jkadLIE*-Q5XE0rr-i$r8xig(u@#E77h$76N@e~)TEwQSFEoCLyF*a<)SOQ;=wiOf>Mm*zGitm91p0oAOdu6?>Z1A!ABo>azS`)+N0j&pQE%t=y)7td~AmClAc&TSQM_)qZm z_|ho5?LL+pt}~2jisna~a}BhKAoQkRo^B2+F7LF}HI~X4QV=)U--4YR0D`Kj*tb^) zA|+%(IdNP)_8_U(2>sGS0>4>$1I2r6@yBUwSo_Pt<*bv!r6 zAJ9KEM}ql|B?1&DpU zo&SKuOvU~9?*iH-W#7SVn4c&x(C?h5d(|#LkD8ZJ=DzA-*|Z3ksgl2O=hgiBZQeYD zdqgH?_zrP%CFZ^i`1E!0UXT1?D}>wP&AD5eT-sE%8E0tdr$|jpW+E-^{l4E{I}H>z ziDTLByqOg!^``&in}|Z!-!QlOZgjT^J4xNfjt`AdX3lP&-VyJCrFjjC9~U~iqfN(u zr&FQclR5Fuio(TI$J~Kd+jBG~D8cS^i2n5#Yj(* ztVaekMP+yFP|a<&-vn9rTbej6?kouMxn1)RC3NbO(k)m>cAOXIxbQxFK zbtU|htI-_i7I+gu?b`z529}m?XF0iyTud@n zMhNU4oh8rxy$azjoUDJ^X#Edx z7+;m`@BIh!-fJ%Lw+ta#{Q;5V^G_Aqo))*WOImQ_$4bgcV%*X)IQ^0o3BF`b;Z^2l#^%Rf2Ea!Mt+%RD}68Js*nsTgkji13d6%2{4l zbCh{TBi>+@8loMkUG3{h`FvZpAxEpfym91)PNmTv>UetVG5`!im((@l@*OX76|xLp zKbq)J9MR9MBTuvMtMh^sZd{ClCk#6v*k2kSwn+-#8ILNTZ(T~ z7W;bRV1<&4P$RB}gEPl$w1(ry@Y08Y6N~qPq+qh1{aMwCRqN1}W&(Vy7HS`Z_<15- zLs${3R{nnB%Ij^a*D@Mn?HE6isZVilnnE;JbsEtFqJg_JbCrG0&_wiRianXlWbb0O z^wPI2|0~cKd)Kij^Hz|C&y$nd6dya~+Siy*n|Z}s0cc2FYRhlQyYbsAhgYh# z-(sT2$+nDxY>G_^`9}u41`-A1U{Y9@yQcK9*c0H!CYqforNu$7{kyT8ASwO@kzrso zZH8@S7|pec;umM4=FP%A9d4cIV*xE!P5ZeINw;x_#S&S8`+#(&UeZ3>Sia8R{;$B6Oib`O@QF~V9UnCSgl9j7pvm9)UCmINN z5Sj&Fppn@Yzs@$-Z^ubOPTMExbc}TR$2cohJ@-9RV4TJ`V8NTUeX*<14+dDaWThp>$$$l=Ok?EP>aDCTUIHRwStNL;6La9y_p4ArZ<(qU>qq7xFp_?Jp zP4uE}kBXuWCn9pgQms29O1)K;FVo`g1t<5QOvm%|c1;~i4}$y#K|(!nmW+3}Maf_~ z!4Au26wBISyxZ}oI7MC(H~6CL&{frlE_tpk*LdE#>xWjzQr_4t*bllI2KU{(GL4aY zd6Q0kr~UUz#idBF>CrltoI^20J3^J>@iUKgG_~M^Dfl${I`Iu-*E$2g+*wLK83UJ#V|+x zG)mhp#YY>nvA#PTfZ?#aTPuUw4Jz*VJyQYQEjRe?y-m=c6?SAa5sg;A9voXPjbE;e zodKHp(x|(`_#z%G0xHArJuaVG49PZH=C2N~jsew$&CfuBcPnknu@aLqf+b$um85*t zz}&qcUs#a+Xzf{KTtcSPR;DVK>}d07u9*a@ z8NGBIyG^~slDzlMGF#nI=ymev&urHtyDDv!R}I!XtG}(%tG`IRiy%|37ShVrY#Dza zBaU6EYq~pvF*pMKlFF7yt2Sno4-CB5Ih)iuSBVSS+XdZH8ECQt{as_LiQ?)h*Qk$Pi8d9pR!&p8be@TJgJk`y6n{vGoE9YzDq^ z$*&ccXqjW1wnzVbE>cU#I*DIkcy*`7G%cS1Gafcr2DV-D%mS(CHJMuqlTC4xxJviN z6kQOytPZ+7*#oo4mz#@cfdS}T>A8U%MUY8e)5{K#%_ymMspGBwF(L?>feQd7J5?)~ zp|BTXD=dRUo~K_YBe@<44V72Auh9;{i<;wP>|~lR=^B(-$%cOI$<=Et?fA6t%^(L` z^n&M0_U6anx4ab(xBL$bhJBt;1TKePbv=>;RiO>W@iMnd6IcdV6*4QY>}-md=j^X;D-<(3x%NGj0}sdMuZccP1p`$9e0I3d-sNxwZnT%Xi|zW1q{fT|#84 zN2`H>yR?Q8T1QTGmC4bJRr;kFdFQN)K9+MAX9vmpcv`3XP}P*Gg=K}tw(mJ4YuMPA zcj?A8oI!49iwVMFi{(5$VmP!GqN%xi0kpMK9vtl!Wu*}mfa!LGhUW7X6={$s=e z8gZ~dqZ1D+oFuM|U+b?VfeIBEDKxqr3n)`J6Hqa~mc_Uz8<^$rF>DY;YM8yN_4k-G zeY3PY>T+~}9q|Ly>MXG;6Amqoe97Juo_P2GINvd4Y7!e@k(yKsm?mhR%{WL(ar#`I za>S`r`w{(`1K2$v6e+_Qv5C)H2Pkv)?#*)ed zT>@-nTlgIk6UAlYOkMbJT~8{Ee5sVGjCB?AY9(*(rPtk{4!Snp78;ZB_oHSudw0`t z<_g-DX2fLn7vMfLZc0eK_usfz5Hi@o-1j*aZIP3M0lEYD$ObGA=^(Xu*cc~CffMB~ z65u`R(D0|1{$lzX;X}`d<8;2Bc~5T{>wDIYyS_pqqrSw3v)V9zD{9LVz#ISi%dYy> zqUfBKGxO5tZ$6I915{bI`$_OTKD`l0O72=r&d&nJ>n$x{y;73%dVCg4pEA``2>2T= zgU}dK`*b80=y7ILEDH&H$}3kY9Am&+>hVHU~^J;F5$X>z<=9K3?mBte^2U~~k#Bnvc zKQ+eI`hH!R_FaB&ve95CwINCZfS8SnA_%&e%Q~>T-MwH~S-PMG;Gig2eU7 ztYguyR8~#Acbn{{>fJm@kBe88XWr0e&DQ+o8kL14o(#QiBIbgzRMK%>0WTgUVRZ?n z1+sDXO_Xjy*1*yk!K8xG@~%>iKw3}cuxi@{@6ByOpULg7rvd{OY3stNUeQ3F3p0Vm zG|QQ9%!~M_`Y70yAM92WBF9o_9g-8ze84gLFzHwF9;0x@pGK{m6TTKcz54TZ64RB= z-y-}%i^_aFd`n%5)uwW3S(a?6Jh-I#Nd8r#*9H8SDLm#fR+u31zC~3IvBgSdDnq1r zfz$Kf4gurgu_Ul=PK%(*FoEoLH<5erhGE}DH|4jEPO%u0h8Iq8ZAmlv3&qA(>$UVV zs_K`SZudyGSF2KCIWnwZHI^rDg=O2S+%4736AL6OS#^}BUMC=CV9p;wnk=xGOphnG zoqgU;mjmDV*F!1Mzp?=hXFFITd}be=f_;>zwg*C-eHJqtjrRBo2%!YEX~tYw`6<6y zEi5vB;gZIwh%OQT(GhAtAIBaB8|1Uem+}~>j%K9P zo%wl7Z(qD6_BnRtPEN%s;v(D+F0kw|;5j)ipRjTbblgWM(wzQ0wRH^}LsLQAg}Or1 zg-<%ey(fU59~$q(6QFCp;9|^wmz>0FqcqKnkxfHrAVFTrn#lRQyyPNAbK{$qQJi`_@Q=o0ydxG{is}d%fH+y_yBjs@6KOa z!+2)|L<1jrvWHK0u%njD-bnz?`6i?eGQG$J+luC>-+0#WncubPbkMwa5fD_=U`}<@ z7{nj+v=INiN25s=AyFkwM5k{Q(-`U#xbo<$+fiyxba20GV^z2AaDZ^(UbE%|P`STk z4`Zk|j*yIZwTo5eRTA@r*lIV~zNu9B>cCPy;7Q@YDo*|L=dO0oe&)ly$k%h*1<99%1LK@7`2mBjq>r%Z=_F0H-DdE-Qchj*>xfpP(w8DF%-%0yn6g9 z*7NJV0#KPsf3K%GwNEbh*aBlUxoiNi8q+;rUP%nO!ZiVoSN?OPr z1HMn2`Q^q?#V5x-op12XrCRUiPRHZ<=~ z*Ewdo3pJIA(7rw=Y@(;sVN7Mat}%D9>}p9!$=l(_t8nHCiy@N}Di+9=^rSdo8=pYF z`}N&N%*SzM;CmM6g%!~Ry?@qA3WVD_)L}^rqq8$%TaQMF68{K4PnXejYe_IG`&*cn zW1`%K8Xn2WZt;7U6`&-}-cyxaS(~qAa2(;l**>c5D`GP4P}xbG=Og3XFu`#0w)>ASfQH%a5$liJ{Nkfjk1}e_ z_eERi%?)wNyGvxWDibP6ZS&7`uXc?4Dr_n{UlwybkbT@8=nBRj_OTnUj$$%pT{}G5 z=*Iu5X=>7QKPyyFHR9@z>GhFK;^@L*8S0(IibA)a81B<1^(lJJAuSA45N`~1jze}L z*iqtutQ@4jyeo!sn2`7tWsX0{F9UuuCTjdt=a7x=MsRBkExj(m7k`N@8feJ?bIz@m zcBRd71xLPgZ4cv~-zH$j$Gv*~o4KWzQEK|uz;|W}NvyR^AlJ<=lc7z#l38bVZ5{lf zZ$OuZk~@l;g*#Ag*>tjnA+~{Z+*#lSjEHR_yXRyEsllH8`Ij?lRV!wdZ(y3&d92_) zn~SbW0Nsw$I*#SkBL=)m43i(hCujjD-pftWaz|1Y=S%U6Z{NC>&Xcbs1&n1Xkg2^o zUJ+q@nWgi*Gsf~8qFR^vs&d&fX-u+uTH20ruI;CnAk#5-g+ zy9iZm#lp?d?RY@kPx^F=R3Lm7epW=oz)Hwc+HK!2uf2gbwq>CD@Zk_ETNjjll}xr4 zgz&aD64@Px+++VxBf;WVQ{=?j@<1r1$Vq}$KiQp{vKqnGvk(7$7NF_8`31UURO1r( zElQaS*s)Dj-%Ad+`zilW53PSyh(u4jl8F&jLL;EI(y~iF1}zanHK3{T#t@H&tPal2rOs3I$XXWZFlDpr zOZ9S>jasz~w0QejRyN<;s%!LPH<1`0IWC?DIpO$2P`++J5-}cwx)_h-IcV-oU^7bp zdy|6Hn>XdLIq-@%Pn-eHJ%G8)fG?QZ*6(gfg5=XAt9m8tIBs5#HIS zQ_;xZ9^Y4ZY>7It1TWnT&nHyl53?qH@yGcRdioseQ3j%|IEow;DyH(OaN?U-avt%8 zaE!_)7?;`;@sWq`8|Mv!6=+V&?)QS&{PI}%i?rLiklI{H;+8}iT+}VO8$Kjuoimt2 z6K;*6FLLG2?ba;Zt3-Ct;FP!0-L6f$CXcK%P(uoK*RRkHvWs&cY8z6WqtA#%Jd{fH z0*cueTlb^_+vDp7l0p55xte?J-0!M^Quk0_gBRo$7lvC(cAH8-HNH(HynB%Bx{x2k z)i=`V_sVsoxmD^);+RiqN3|fc?ZI}z#Dky4yKQb+T~uMMNeuoI#(g24Li%2;+drRd zWhW3SH@5zwmL+Mh6b(9-6t?0PUBY!Su`ynVg-#Bnb4+lkv<_o*7dSCxq}JD6nr38$ zH0u$2S=nnb=ivbUUs`$uei0cJDmoS`Ghyz(E5^N59i^?a-9LGj(S{1`Rnpm2Op8c? z$@$`vw#xfY_wGncRyxDCw&P*Wi=?Uv5(Pzs1hUr`2i3B% zJ``yalM2fZ+ZJDrWSmL`NqX5w)v@7`q4MYvIlI-8K>d>ma`XVw<=bs2Rec5%p0j16 z#m)YXQ45A-x;Rep?!eF}R;&&#bs_Ap7WWFFJstDu>))$*j~qQR?jlWRS$X>@1+4L$ zPd$rPMEDaU{1v*WxjGrXZ#V zF5I?k91vR)M>)4BTNi<+1*v z;*5@|^RyZ3GPitN!Jg)ocLDF^Z*T%Hd$IC5>~LZ6Fr`nXx%96$0S6{_nF`=#`v{Me zpC7a1gj94O`nt_y-cQeiZS}BUhL05R3y;l=(z6k*8XpO|9=u@6KGQ2&*ys&Df>&JU z&KOU+c3keiLr82!wZ(`8S{aMxZcSeipZ+P#Uy^#=FVTldiZ8xV;)4rZ49bj_*bA%#?$#~d!1RG?icFdb5Wt>n1zF%DTlo%S$}b9?<;Yw zT-=n0SH8+MCX#EbLf_`Ke^`TR7^683j0cDeHunL?2^@$aN;lT!53Xr@Q$y4YVKiTv zm4vLyO=ro5(~+yMw=?U9in0VEvXKH__WW!~F&PSN=64N++rZvuzE9XvEIhGLyHZly ze=Y1rPexUjXVC)Zb}EyJ$kvfmvzD!yy*Ko~VUdnpb~!vV+T%8;%|f;hMY@ez9O*@_ z;_<2DVY>E?z1EIqno>Xu~vvASmehYgD`zg@$SePk9zXjc9R^o=1JqDPUxJ z>zD*`w0Zk#(00#=zN4)k6)(EUV-?3I9B2jD&84I3Xy4Sq$ zyO(neHyKg88NTe%)WpX$3vZJgect^pR;)W}x{POJFc!+XSeq&LgvH3f(>{TYYRYZ5 z(WRmw^pS@P;J7z)e^rum=KUh+Z&Q}RF$%co^dXm1@aNHOT+k3-{n_l|SdfQzR=iDJ zonuZKF-K62mm4+WBBi?aXU2rBE{mHs5env;QZxOPa6iEOt%ef^lHR@d7M~>0>I*i+ z-<5vZYSH~*k1{8BCKrgfdy};(Vt~tH(n^kUMBdqHPbH^%+TWaeb4_V$D)$7`B2V>(nye@=9#)vYR`H?pg;0EqnYRp!7vdQl)#3 z!Clm!o;4D1$#Z89C*q?$ylGvMBtFDt@=vxj?mC_(>}*w&+JX`Er z6z9D|y&Can4ZblF`CE)C^t03Rbg^K0zxuj|vhQpfR(KyUD(kfRUw5orRl-8_CLB}B zu;gATo6tU*PE?`Sj6pSaV+f-uz0s8p{yiF`4TO zXMLTBt?XI|FDB5=-S|Gg)+x#+d2#MpmfbC>e{toviW=8Bzzoo}lh{qHVJqN-ZrHNr z@^0RJ6A1`3H@`jv2d%ugI$cw=YCm18f{{p--xZ(Hzgsw&W`;Q3C29K;qk>+a30_4` z@D5`>qf5_a=<*hBQx`pg36L`oncI0bC_hfYl%8pOE9>NY<%kP*Ab3NWMBfLqj_u&2 zeoUDJM`|B5BdGMjSvfD#d91m5t7Q;Yv7Slc2_vmmnQz2!^|}*17&a2NzP2!H+*_ex z#`~I5ywfTfT|BP`ZCveqTb21Co_0K+UCg5F8;5PGz8Q~uvaFnTzWe^D(NpfA<;PG( z%mX3f4B0QG?wzBtxO8^iS_&J898O+QO8 zhp^pEE>W#(n9fzV7!TvY!fv1#=IbNUG$tT#n+_QJr7B%)jiVGRN-dsGJQqkcXn-gdLA}c z(@kQ5mR~&C&d(FAk6{ucvzD%_W*6BZkE4f4JOW-4CcSqeY|6AJaOr3kQe_;P+cx4E zzyu6~xN2v|*53#?CjL?R#ASXFs2nUnwp9bJP=3o{^y%SYc5!jOS9P&+M);0zIGl?< z{&cw9b-lE>s+`!9aS`wJ``xW45MImyMcMA_Fz$&KhQ|umms=Quaq5oqHVidB8C3;| zgtjTu8pQ9HNcV|oPtHKZ!iZ=tV56vqvZInzz($YqbD#`Hg5(ZUaBI;WM8ItDVSGoW z1%+89;f&wOkggHx`#3)>t(s}67Cjm5$&fGg3NFlN+Qvdp`x zsK|2Gp%hOy;&al7FoDvj3*$cllYy)RvGNyUC&Qz8%f!Mebg4HGj3Ut$<1!U56>N>sS8q_E@%e_1?a@fL|*52YQ1 zQ)R?_Eb;ydVl|5lV;S-Q%Ho01ycyaV20yk_&Yl$KbF*71BGc65N-3=em4)RFc46H5 zV-qoeaEB)hb_&0zDUZ8B2$=p^u&MtkIqIGm^`knA zUA~=f<*yp@AAOY{Nn){OFtdtur5Xh-1pJl1vh5s>iIZld+QDh1natx~ytH1ZOz8jy zOSg?w4e_!Ln2T1cjq!Ev*|=k5!9A5%nJ|%6-l0jpTf0= z^r21o_!I9sUdKA_HtwqREAvrmcHo>XHdy)LQA7i2pmJFjJKAz==JO__BitqpMTcEq{@ z5P93Jz3&@+eSi3ER7{Mv9FAqX;f?=Di>)Vg_zQXO{d=qIN&vPG5Lhko3>pw?ftv9= z@Q}Tp^X-Vw$yp^%fku&V-UnH0CSPte9UBewq?2qUaz#o3;ir%4&0bMb+=W>+VCGVd z^)}!m*8`?9+q?q~X#5+ej9dMJk+!@W^pWwO{BBU0(^z!cLpG;i^#To}cC+czcHO$I9U`aF0Su*1E1VWNRrp<09 z7IyU>m#W1RV?;V91P$c?T7D9T)-!t1nD0X@Psjc+RemuT67;?CGcuUA2B3QJt4pmv z!sWKbOoNd^C{!s7wQGCvDR;9nyL=hHRHAR?HRO^>Kb2=z>&JbB06uxQddK74aveE< zd6?(79cibfuL_Pn zd_vIdxM@od3fUbK=QBLuE`oMJI=Y@znW06yE~Ml;-Ow}QJh=>>59{cTq=8!gg3YdNE)Mk<4;so6E%C#_?{;@vs~Go*4QzSz zY(lAB51%p@MKBEID~4>Fu|9tHCidRvXucCNlV_Tg!0=$S?8E!cyZB(dte2tTyO2Qv zz?KoF;P`0nPtLA&YTjstT}80n$@2TYY(rb$50&?)f(Pszg~+r&ji1kpibPqO1Pk(e{B<+;aBEsubQ8zFs?c^4QoejvK!( zy`>1W{?*j*M+qA(4bb{wq_l=lyJf0v}#oopmvKzFI6jdI`N@+xDM} zKBGB33Ke;DZ))+J+)w}0!P%Z>w}A*QL0WkuX0um$GB`tqoFipJP4C3o06OWSBUPBy zJg_W#DWwiy&}$o`o7kP9O!>b<_bpO zt@-WPe6HHg{)#)r@o3!iG7hi#MG~`BQ~@``BlO!K=5diiB6hO8iVU!#Uqv1>2KsS{KH54SfRGIWW{eh3)$K4IopKEp5_E^Ht35{;?mdkqVD; z<>gMWv>QF$u9oJssCWg^yTy>R?{r_}OJ?nkHC8{0bLGR}rlE`g6h~-u2N*8tyCqJ17Ro+L; zF}wFmyWUfs7SGh`kDWzUsRWPQTvDsGn!zdg(5=74M4@y8KL%>%G=I;*KEc^O9VkmE zQP17b;CdTR=#Hx37f0)K8rpjvCqSwM#U}D0s#@Rz2$BR7D@pHL22i}e5c7aq_|^(= zj1~f1qUHRy_ct6o-utH1B6C%$()E1(YE|SV^zFl8fHC9YR#rauW9#ert18&qK)k(r z8{K^v!Ez(fJ>Qo1F{K8HZnm$}xNp1+yPq?HD$H+j$W>V-wj8kIfkW8;#W*=MflzQe zVJqSx{qb-#pm=4K#7uajjGbn`ldIy)fN$AhkSQT)Ts* zBgj33C`&2=+6sKq)F1Tp*}W#m^w@$cnGraM7`eua*)AVkvLx%^PX)C7Q5VY7HIu3A zFp%2WvpQ@sfo2ls@YEGKqPxFFh$q_bU>!7_@cnT+p76RPz#GgBIH zD1LiNX4%@3P3y@$2xE1>Jte?4ZuYQYM1t#r2sNcD10*DbtUEc?SF@cWDN1)SVN4$_ zzNQEiaEN&Nu&s=nr@fJ4Yif5@>S$S?V(k>A9P|(2PERd~RpuWLJA6{z3K_YoGE{43 zv>Avlp}a*DS8W|GPJc2pJ9v+b6SeHmcv0DX(sDarBXx{h#^$|ojRSd(R6<1c&JG(S z1N`BKq;u9^q0{q7SYAg}%d$kvA3XXMfVhEvav9k4+6{eys$(_dmFxnK_f5-XOXV`y zfQAaH&M%DwgKq|8euwwbnh_}4v#5yy#pLwLhQ7*PM5HavYN(bf(-tLJyU!X;jlBsz zbsM7mvUhEh%DWw_U({m20nN_NsIGrE2#v+@88k?qIsUXhop<}%sp86L48Ce)bbTp< z&WaXlqi3@UR?{Ew(;lCx|B+<9x(gHhIzOo*tZnqL0-;-?qFQxkkv-B*WrJhDV~6(= z#yljN{u(b}Nn@igV%2?<2f6gU^c=V|0txN931A!c?R}=dVYDUvxHB1Y^9PoS!$xV< zZNuzbW!kJko~I6>^W`}JxNO%Oi0CwiAE;|J+6X;KR~=H;eYhvSt;(`}`&UZQ{q(`m zY>d}2h^nV+pqSV6GE;8wW7@mEpl~_E+AY$QpY<`?)za3MwHDkNd;-!m9W9q<5)%X0Eh(RaN2rSF}FYQgoe)m})K{?1Ju%68-^)@-kCJVc2);Thr~&;9<{LdGGg@Bv@H|2qc_0DzjYZ%3YeGatWQH%`+1Y1Q>d z6%X%a`dErF*lc_|AQ*GMm9)*Ii@4}(k%pbL0ejA zkL$@DO&#NETcT#ep@1{6+7LyRzB{d_RG{Q=mGo~1u!U@ff3b(I3z1(Th z$%0+%WPgiSwu_%mDLWQ&+PuQmR-LzL3Dz8Udd3N95lBvQQ|e5kuNsu?w+HR{^*w1E zPR~B=i!seU9e(##qm|KoP=wM=X+Ejq!?&Psq&r+noVz)Roq53*kdgs=HlU(J)5*1O zhbx4%7;ij#j}e?vZJ5frQwwUzD_a|rio1{vktHa=Pk3WNF=#&8$oLl}L<>~{Mv9TX z*$!c}SP7%LHg&Q5<3Uy%&Pb`$oZS`cX?u(F_aOQVZt!$ts5r03#5X5B*~_b>{XPsX zD0%q(jpUHqX;>^ZDJ!SQf(FiEZC3+M&JQ8{GU1FJZrGpQP~pXfqor!rJ#3NA>adfs|f zm$RqBBlcIMIC)yuJ{!NiDaYc?--b*`JZ9@^R?Ubd^`ox;TNlYxorGHV?EB=aqO zmyMAn`OEfp_@S1DkJfWyx7;u8#JL6L+!x$1n(PtfqkFSgW%w~Z*aXgfz&14+-V(^ zZG)%v;}=f7%;@h=F{m+!qYsZd&|a~a?%ihOR6 zMOZthgZE<>z-mN2xHHV*|HBWl;?w*}C}&fYt-URELm=;lD!_iq5FLC#kERB-RvWp2 zXQMLc;EsV=S7c&C{XdGujh|>$Tu|LaGmnLlHG|r+zScd6Wm}hg=tq!NU~Y`wyGZZK z&|M?ClhAT`ClS-Mn`?Mv~T*-Wd0i3{jIhi0yJj z%Ii6$rc#4>F7b(@@UxeIy($00@8R}yECNhvho2Za)dpDP9AZ%YwuAA;CvP<|8K8v< ztFFR#dMXB7wjp}sda^JZ7*}5==yw0KlK)92KwW%QtTJ4#<2T<{C734U%SHgC0kyK+ zVnZS9CWE!R^yA7?^tdUVzv6F>G=efUZx|_EjL0HneHuNKTr{68tFWVkv>r&Kt zFV>0gzRV@@3>KwjF=pI7?+7D9+^vKvCc1&EDuObrdY`w(h&|HCZX*;~^P>jO^n2_u z6O~c&SuwEY$ar8+@tN5?v|E2CVpL{26T>aI_lBH_NO|a->laZ<{EqZtDV#U?y$TNg)>AbGb!*;(kNteREw9Yv zFW+Slf8xLMm?FM2F|6K_WBj6T8cgeJ(lS$eMt6VS&1yCIriuQArK6^v7w~4}<-Uab z%^NWQESE&@P%1~S36m!DsK>Zn$5vm}p6w&?Uc}fidC1ELfc=iG+kl`op7l1~ye*$= z*8e^WFyLzz@GMKM>Ic)zO-E}_3d3TZqOct2fw?R{D@Qh?+KSh8BJZ%t#M}%~1NrZQ za!>0z3Z4>F;;8Uv{7S6q$QG35mTU(4XXDfu!UAn?#d}$9=nQe;k5?VZdO`#DwXM zkd-+7+P9wLg|*hfM4ysfWpE(ve3^P-eiv0|a#kS*r4GM@4hyu9{&NoE>Q$3R~sPmzNX23;NYWjVg z)Rbb4#v1Ec@NK@jQ+xo{dP2=a`WwLK;5WT#0pOM|!|n~%DIAhETq^xLX&gp6oXeJQ zJ73AUzUeGcU~T04@30F8)@LH!%H8hAQzm}54e7Gc*+d~T&E}QH%1`7O6&xDh39;2{ zso*)D>CPopyD2qOSxt^Lwvw{FcggH3^&+R2NUtmZ0@e)<+496u0CiR-eqrb~p@cgL z$tX%JueG+__*M-IS&jCt<#P(TrD3Uw{x6=sJCN=F`QIM3s35k^kC0?!dpD+*j140bB9Z>{j>nKUQZ0ePFz&XcqofK< zwtPWZH?EfZzQvCpZ>1u@X?A7_)miyr?p7-m5>%@ThqR<4Er%@-JnBQ9^XyVOXY?%G zc`d%Ijsq0@u}0{-&hN1;_NWl7*uBZ&D$w{8!41`y3JTbUd^NcFgxTWJW7?g$cRXd) zlliJ&Ewilu$_h4Hf@lPWNhH`hhZdOvFDGw8u&^?DP5ayGo%_>dv%xxyPjdy{6mzv^ z-4fXN@`Fi~Gj{T&*eezhene6Pg5HO_JQ9)1njHq zeL1CRV3v^l9`8I=Tv*JF2RDOiYkevp0AIJl$mn1G)m|IwpwIMQS$kI;vwZU@CiNwQ zM3q^25}IBh)LQ%akA5}p^N&O2O;0rdvrTX?qOip4x7uG%P{Un^+l5Epsx6;(YKqSIvPloMfc|4cbd3ldHHG2m9Vv6`QQ!_ z^=ga+r()r#D`VoqenM%NfTfBc6#1tqDuB9nPZZn+G0}*J4zD{Y^9Fi465ObHMRMDPy?5##K`*t z7Leb{pikGycu3VXAO|DDaNXw?AG2?YuVU9dS;S+^9BYr39Elkm%TR~O{5Obd8%#yG9(5D`TH_nK=fugQ7GlgYX#Q0v)BEY?maSVB%?LNlPmzWd%hZnU$7 z*(+=P1fp7b>tVDRxD+Up)-UV*3I;!j`0<(W2WTi!U1mD{h}rqKaVM#e9i`CqS*XJG zl!DU1wp0(fdLi<+9Wy1W$j2k8Tv$up39~6dHDHA|J7ZL&Vt-9cjn&9`AB|eMtZ8en zGNY2>d^$Mz{B6mszWa)Em4sPa_J`=;$J0Y2h4k@Ul$V$}@5FTKH|p6POdN>kMvT$q z)2K=2+U`c9PpdFP?9JxR>4FDlaSpn965#Q?I4BC+G*yYx;fJ-T(5}?x)R?ab8s))%S}Vp~zNBxOn;( z>b_$p$#<_lKyAA8Q!M4g+O#gfCaW9S^?uHMc^&8jg`YI|dhJss8-`m!@R zlxK9r^4N8j3j^a1@li&-2=UqNFl*p5cDqeVEe( zb;`_%&UF7P|Gkp8Y@-Zbn%XVQT)UthJF)KrIvRevvXyNYi(Ur@u-vw7H*)mpW|@8R z2g;wyz1--ntiJAPB%d8#9bTBZ40w&lB>qS!2%ZolUQD{K&DbKIjq{j1y;&zMT!?8b zLhw?~t<(Ov=2DHKujKQaMfysm&aVhhln^=aolUvDW_e{tqWD?P-aIFgv>ANh1KN9Sh2Y*%U-V>xVd#T|v!_of zsrdbGQw{Dr(8lOeNd+Y&JzKQ(Vx z#--WOj-0soLTTF?mS^`kVPoKJ&=Gmew_qa?DXr|FDuUla?*3-?c{Z!rQhH^dXo;l) zN-=88xhj^1^w0cQ4RCj>2cTVwx1ZMryWTC+QB03N%tMv7yH zm?XbDL5tmiqjoR0%@3i{a2B04 z!)X=~(bs=Bt>z#q0Pr&`i762Gc%k;_2(czf zS+P-<0)7h!Sq&Oh7jLK(HW8$ZG8v?)zb+BNUVdL{hl7Sti+Qwx-Hfj$+_ez^5s2Bm zXhgi_;0$0BukBUs?)X?HhQG=7Xc^ziH;6v;x1NZ6sda#7h5e%U@NllZ#aw3?ald)5 znMxWs>~qv+-5neKSeqPQT$xL%Qa9t58fDi*W7^wRtMFhC#k1#3r*n7(7w41kJG0| z)5Y%_+0826hRY3|=l275;tR5}O!_N+%Zoc&_fEZXmxG8KNVt|i*z6?7%ZsUOO;w0j z#V!yDJenM_X!rwA4v=z1(wSUD?BG~Yk=sPLCR;cB{rEKKqX0W8@CQ>ACgAN`LUN*h zdc8Z3*ecnz)&po9pYlWs#_tD5egDl4N+I-8k+k;ao7R4?@i7QAGE*EjzYj{jB&2U^ z$MV{Gi05G)IIzd-m(b?dm1})b(XuriNWjs!&0%7W2@D{4cBM(p>1TC8eJI@?ce73S zoI@8Kw}h7vUQ7y;83C!EpM(|(Aih!8qwDPVV-D(<&nt{NfcP-Ekburn6;RYwxqfKNHdPNhC1wDia_xct?!1K-X~R}O|n1ZZ0> zX|q7Nl%l2s*}PMq;5WH9U_-UO#`SdlgL}VQZB{kxR2nng0}Wej6~ydi-h@y&TD_%- zIAuw7rL$Emi+CzGWM!9`t1b5|3SBLG*ok!7bh~>GulIGVGauY-nD+7Z?mR<9N{)%UaeQZqFQdQ0){9#g{F zq$juKZUP8b#iLzl2sKy%25na32@m%^GhKD}bA{on@yyuAdicsouz%eCRL|b*Zc7EY zx?F$0XqH3Jb_92P>>a^w7{QsfpM=lJ47f5Z?!7sxTJCYO7>&Zd#JD$8``p?#R`9V2 zy6S4t&|a8nwZDQU6tU{V8hD*NvcBJTVJsf?COh<5j}M@tWL#we z4}b8v`LCr>pDt4Z9WmE$iGSTyDqW>^-^sm62YrOuN15TU_;0W*1ded9e+k&Mg2y?C z0C&hfj1BJm4k4tb?~AqbS#aSKt2QznWH{@Ve@|9Cb$VZ3I`iQX`)kVXx9+3LpwFD? z{ptw-)=eDJNn6mMPn&pOv2YC&y;VLfSRk=zqcQvBbs9WzXe>W_Ibb=av9%2iXP@o! z{k+dM&Rm9BXJ`xWvj5^d<|t{P=baXEdOX;@9*9p5RSt zLq1;OJ6$YL*O|&gjvgx3Mpw;Cuw5^TLT$6Es?o1Ss`|G+&%U*{l|v^+=z=Jtua!kdr8)h| zgFjMD%X_M-QhL9BGsqCwBp@;?EKVBt|C?TJDokFoN@B9^wEKPuvHN%CVtCz$3N<6XrE<{z|Kt3b&K z-a+NR?>_@RF%JGCrnIHXT4HlgxKx?++)}0E_^;6;sn3!AdsZ8P*`(K~HcZ6$pxyY6 zqg+Qup&ug)K!pKT&OV!~5_mDQuX4{sK0tBaJ~QJ9%+E#>ak@?Sr?jat-9~N}IVDya zIp3AZh<`nq<4-y-;X+!`c7?qrKk7g5O+J>e{#p40>=yf`DzOOuFlP2I_vQgjnM@1}>=HB{3Up&jl&EJ$1Nmba}xGeWU zPo5y>VJF)Wq>~kV38GvtR(mI7ej%K0;RE&Ft6m{Fu|Ujl_}0zY86W1&sY?Z7(GHSe zz#MX-uGhu8z?$$fGjeqhX`h%kepLsS*?YF?fW3@Ojh?L!JeT||&sR=GWa+2)4gOw_ z5+&KH=yzrIm_(x4qUHK;rq@~@ynoINQBC{F#)OtSaikp8~#dpA#H-GQ*W+JtQN419jH0k@4=a`Nr-f+f%ILO`^1N>Y5 z*07h?zN38EAJv!UNQv0noEc!hh!wg>-Sk-{pS&(J-w!kOw^AonScE}kT6f^+P_Q2^ z>>Eb@DDWzUefhJp=*OR%%DM&3qqucU66yv{g%FM(Gq|N1@0|JY@uaVcfd>Z)p0~HI zEk>XwnWlWk=c}dgxH(FlUf$4MZbiTOwJ>2j$}6vJsEP~Wlg-{!NHZt!XF0{j!`}{E zFWwJyi&ht-3^`cJiVGDMg*HB_4ZR2_8lYKJM7(!>k=UA`ZQU2E&8}vSeCZ z73|t=>XW)l|0}ysS!7puc3hkj@lCtTlgFYpFJ|JeR;QtTf7Ug@XnE7wCEw14znone zt94)3*|E%sqTI!U{^;slA>L;IM)$Vp8vQz=Nx@D3 z^og+CJzFE#m_APj?rAtLFm$Uok)MXBpT`7ZFJi+{>jr9{P# z?Rd9dS**Dm301_9@;|zHvd6a-@NJBLL3fzB11^*MO8?^i(5JjsUi$=>qSX53c|&eS zX5klnbI;yAtN!RJM>cz1I<1}}l}PH#_Sly@&IJ+UVu!TGretSV&YrW1_9=&0q*Tw| zHktu~a#@%gs>J4N=68fGsiZZ0HAo~vHLf@*A9ayAQ(!+(& zI_~<^w~Bp>MelwhZw-0VtPzS&=}!vNzOmBsbaPH1)B6vdO-E5ZAXBrM!`=n+v**WYv7xn^;f>362$$}yQSvGKoDw;neK%`W@$<-$phf1 z_{9omkNTUTGyx0p9Lz@O`8n3*e_~xk5)`pC;blM&JYYY;1|;+Gu{ZndAWWcuKB&NBvlA5q*Es0q_1fG?n6VV5_L=Xg9SY> zi%Wj0oLj0_*yBV~T7n7JD~-));n_$XAKoq0vL9N4qcyAUxEdu&>Sp(uht4HKLp1B5 z>x?_7z-2RNudAZ3*tLCpnE^R6!jhhnncJAB8_;ufR@%ll*u0(trJ_GV%dR#W+ z0NzOZgqoo6y)2|hrBFTsK_@@cC-TDry60scXODk$n&S$>hn=#t*biSr8|z8~o{r4H z29m&u{Demw;G>_%-^DL_(*fRNDJx6eIw_0wk0{4Ywngz9p=C9J<8VaSYJkzkrvXG*%Q4@NT zNjm@iizSbKnR#<{YK<%CD~HXn*D}u|i^g_5?h_x6@jihlztxEi29_%re{DwFe7(nE zAR6h}4?IYNX)DWbmTLY&xeBw#1mjx=lg}ck@S;C7~pjpDV4Ke&q zo{#T|K1FoS3Rot~a8=VbRbSKkoXVSIqU@?3^mf=fSYMklB=m$k5np9n!>!(Y-+v%eRfc zWcz%j8-Ki>zqq=N2G2eqOzg1QFV7p+D zY2f8f#@!n2xmPlJY0_I3^D!pbU~+Pq%eW#dU|B4-^GO%lIQpHW>(70x2Ki2VgKiuX2_bKAOHbk- z8|oJC5)8mOX5b{rnL9`d>z*n|eA|DaFS)*vx3qAT8dI-WH7%&spX#0X!zmwR7T=%T zI_1NJ9qj3r9qwusT+!QiEkn_`s`hV&aqVe>0vtb!0yGATzRpznGJ&@KVheq<%}t55 zO;elx$KcxA{hV3{U1T=6+t!~-Emxkix;E4RJcGuDI;;)eDiv>O78qz!bRTZvd8}8ChAZB9pR~h93a+r;`kQWL-o}&$N?Y4Nzg%#bM8+Br`Ynm1&=Z?IuML2Z5S6 z$v|ka>irb2j~8T}aoJvlG+6)WTAM%)kvM`hv5RvhDI6Wh52}EBUBc4=tK7MruhErv zXh;J1b1?B?r7@>WqX{77xuI-1?*4c7v^+N{J8t~^Y*-=VmY1l<=P!FMqTT82ak&!y z=42NaFKrgU0oj_HDjauau8b4G2qeg**elNN#A$Wnl^XNfHYc7e0fGiQ*WU zW2j^=_WqhSdwha-?*+da&Fcr?Ul?dV59MOoLk!`MvSt2~kN1QLWacn4s z?a0M^7<>L0O2oX_+~y!nsExC9BI#Qp8N)nHVDET@T^R62%soGDsE#1?gUrubuwJqPzMyN-HmE$j{ybhuj5ek2{Rk?SR&@NGoaId?K(HmKTG)K&UA*Xx8_ zlAO52=lfnRaNr^vjC=0b!q$6tCJ|gF-zh6#8W+8AqKyiOsAq-Hy|BTS<=^B&5|wo+=Us7hb4WAdcAKol z2Aef$up5%mF~D7a$)f{w1TC4Ms=v-J$x!F|SJo;OE`-uRGCpugI-2 z`{WpRA-VGj&OP0Yc?V3mph>IuPBFdGz=V5CtYH%@BO@j+Rp3OdT@O!03@?Azw3Xs6 zYtU&N4x>%G$VfbqVuO%3)H0+-K zs&i)%ftYU1uEU$!8J9dbz}J0{5D3r`28c6Q9r1sJzCNJM^QKJNXnqB#z$1+Bm7RCQ zn|BO>m=M!gJX*wIWJ<{gRp7Q#D2_3jN)i&AmFJ`>3Z*6bwH1WA;o~jj^o?wFx_T?m zXkV+fyOxVS=6LxGiy$Wh{=5X*_B=_UtlCs9MM{_T$+9% zxK!<2TZsQ%CNJ?(v0qe>%uOCYAJ2^&N5>wd4XFG_J7JWM*NacLkqpm6NqnhIyxW9m z!_e_j;6B6XkgnWCh1P6Pc}yDj6)*m!jmy~1b2DS0Qw{bNWZ>e`pUwUpx%d55LD#Vj zjAGqE*lom$pnRq{^eSCE7e^V2(TcP@(ui~4u>2!OQP=~mN>^y3#jl1F=M=mCBrvE` zvk*0Mv*Bq4h7)c8vZE7B1}k`<10HN}<8hF35@m0OEmpNRvwl0kh*Sy_RUfh(+0pf) zWY_I04|yN4;wItF^38ZLCLwXyA4L|Ee(3B|UfC?lap+o%eR;8qovvFN9o}-AFtGIg z=2iEfbXF~mf*R~RHYHEmGh^_xdAlu*ce$s{$#wVX3VMWZF3tA0O!+Lt`VEGEag1Ns z1fD21&;7wWvbAyi7$cXL#u}pyr|CZ{HCb3bO|4lYVl!Qyhf znohAND3LaA{U9~{mKJy)Y&iw>U=E^tqxu+uNl(% zyJ=umTVez|p5|le83=qF%(Xf+a_Ac-R0`UoplLjk_C|?|7UVF_9eQtTr=C z)8NhBBf%Q$WMOE^zg`{XQ%g8^@$$D4KQ3!4LDPlMCph!Qy0PW&s#an<?6ek4AK{PEa{-C-v;MPg5Po(%(>{`4Si`P zUpQ!?P5tgySZ)+)fL+%cM3|giGD+AwrlvY1)eqLHn@{t5It&Dg5jgi`U(@0}jF;2+ zNy2W{`!QKrQ@c1np@iH*jPL&c%>pFHX8=(qtvlI^fgsm26I*26*%Ex|YN@$+4u0&N zgPvlTo`Wx-7u#CT8d3dQ%?UPeZd4u#&e$GA-|@rx{N&jv1=OS3|7BlC$}7`M$B_)% zrQ4!B=BM-XH0m-`WQeSSfH+(KR7M`9C~7otnus zxt=xVk0A~+Q^Lrma6Y@Vy55&<6TeP?8i zemif5bog_(%|6!%TtYUkz4L_}vs_zE;269v9n2p+B`jgsyQTt-T-x4qE5@ zvwDM+W_zgD%N!1y3U#D zv-)Wn<)$t^EU|++0#b%Idq;k441Fv6X6s^ay-sDMC7W{c!w#ndeXOLU zM_j9vZ1_1D1&gDN5$16d(ue2T2PJ@O{qrk+G^6ZsDADQ2d|jIlftyJ6ePz?HcQ@V2 zo&2+Dx&c}{!(=$A7@$o@O}4km@ggkRQ0BqKEZ?=1$(?l_IS3dhH#mrBSjx@l%6JZ0 zYUTmOv-KUVN52x4oCn??@O20F)&gGP2}tNFczQtj;B%~HoSn-lGKjh6XZsyO-K33Ag3jrJ*D+; zf~R!o;|0F{)iGAf1CE@a@MZi8ur~EWJsht|{+1cIPwEd7a-3j(*+WtF`$27RraWna2T-&wDAnaxdhidxa)>jIQxZ_B_kolMr=;-kOk^<* z)+l1TUE%N}+PN+16!)3uU;7e2*s`t&G9U=1c%^Wh7`(^HOmSjw?3i2(EPk&Y?A*3* zI9-H$1FgMse{Sb1O1Qbp8N^WUKguNFKlzy{dm0-8XW}aY-XCty<2xcz&4_R$VCsvW zXNR|wr4OZtuPXoRl4SgiAXv5i zdOO`GDYP1bWuno@f;>(7(%%e6RCW1Mq3uCZ5#hoP+P@>O0BAVX_pGHe~ucjH@p|lE-fHZvI`Td|0=fGm5xiUB| zcoLpP+QV8O!nIfsr(R1jIt!yftckC9$S`!S2ArV0uHI8FR?bd3I919AkS^0d$Je=l-j~oSR&_5tlx?i>Wai@u;Er!IUe+V|- zYPHCink`#}jZ}+i=*h)yh+~)#4ta`fGAH*udgt<~4OuTlI@NH5vL2IJQ${#H7ZhOA zxVWsdaqZ=0&KoupCX`Q_tBhb57Fw@27QAO8b0<~k%4k|bOT@%VnUKX}9EpeIP zt?(U~Ec^;8mBUe5%-U|hzJ7>#R+GbcRibMmErKeVA+xAi#(%Rec>5F4t&wL|BLL1o zHXXwwK=2ceUy%DK@Q%UD^+97uG#vkn@6CsXj*Aa_)B7tB*1(1SswSB(me$3Mpbna* ze?p|TL$^EYe%{-t`7wXG_aeZvzSFTAZpNWm{q~jUomp;e7pBVHP@OTwuVlRln};K0 ze2pZn@**r)LGtSLDd~Uw_bZ(ykin6N?#7h^+h-q!YkB9!aqAYf+7}>A z`z}WBKZYF44taTqR#1HU(dKbFs}dmdwe>(`02lw(llaawXW?M+B7NieA(OSM8ZQ}_ zSG8fv4Xqh6&^q3l3lBe*eFhDQ1}LdA2IF|QC==asg36mAvrT1kx^{>%iSy9%0r`1!Ai-k`0UZnVKIes+6GRdiE);w!i5%ff;m03kOy z?jKOJ+V1bQ`&3Vl8Iey#_}IDrZ3TcBrFX)#EnKmJR(kZ|Vn5%-!hMxb6Uzo0460Xd z^NdLrkdZY$r-aS*Ta)#wqg$7)-_W;gC8zRxqv?DP5!q%IWB-|I;>;H8_wI#~a#DVN z*dneh9nG~ch`CH8KR=**`HIkX_1#7$xEmfo>l=*NDPH8w2u}E)jfSDF<7lU2WF`*( zU%PEYmuSF5#lR&oF-P-a=A7|^HPK@{oRt!803K&ne-T zdPzAzf&*#X+Pd-;@8hOj8a0Qf*T!?vIj(<%y9=SwtzsT+)lQ$xGvGl3nkm&uR@W)t zGWURnQ;rXoYbabg8-@%Ar^^( z7~E`pzJCFJ$erW+=fY|Bcsi;k#+_qDp(` z&gRpqulxsSPp6RZG%!6~97Vs{Qv~-7X|d76;(znZx|Ph%Ybj2qq%luE6>;-JR5;uG z{Q|PUo2LpO|D#>#z=d(r)o((l(y;am_B6vv+q z_@L1l6kzrC9G5tQZA)2fTtq%r_XAWCepL$g7v}*?8LrH64QTRVRTv(EArO+xVrE)2 zF^_RX{6PPKO3zewT1Lc(|EiDBH8(F)grJz`bB^9us2E?46938VpJdaW)mD0f-*mBp zh|SO4v4)qvIZT)b%P1-0rWWv6c|YODCJ-m2`*P%-u+X-(d;?^cSJ&8Eu@A+vIpEh8 zQp{F^^K~ql$Rx&jANT6NhvvLJd&gN8GHr5^Fz4XvRF>q@tW)J(jHDuI8s@G=SF22Q zfLk&5383p5|6m?E;%nIy43>+x&ta72Uiw-6Y^54Ye3S+{U18Ni_apM~GdB|!olpEt zd@w#kR;fk=bbtn;lw_m*56vH^Iv0!xg==c|NCk@JzQ~dOFZ%O_vq2f*BPOVCDJ46k zI>vM<8^Yb~1tS1rIvHDDp51hgxHc?yHk0bO9{;VZHcDaockmW=>1i3bIKNzfQ#O@% zo~rzaNxl~Zfj~-DdIAyVdY-EpLd_S?0GH@HPvZtl`cvj{L0OCP^zDPH$f^ff!!M|8 zZlTAV_5B%^s?iG^%<{jWfvhDc@)#D_lD8|;l?87Oq_f7*gJq=Vfo^X>X#SXsueARxYgxA%K1x6gAm2+)woS4rMsRJ%NJV>YGK}3t# zgE4Yt;d%ZJk~<-%m%PoUC=A;_26PKbd>*#CQ=Qj-gyUn=O4g~>_X5s=K(k|fUn~s# zmjpE*w49^7TRl!;V_(PKZ~5U;stCU4DW zmt=;Xi0~ei+GL|ezE^2~b(Fq)V@oxTDL0PA51WI1hA9;KNEDq3m}QuknM0bvbG z;=ih}c4!v**aCcNy$U5pbz1m7UEG(izuPnF4paTfe<_O_HySy+~~UA!bhE?w*en(^>8ysZ{&t=<1<-T!AG z?85^*tat~9QM=3Vt^=|p$Jl#B=B(Kn;rk0sFxI$aFe9X}5%7V*_Kx1wLlLbc=UZgJ z!edP8rTgY$tO33-yIL;z+c+M~Ot zIPDLtu+;cgX%;fZ5r+tzfRLj}7pUq5OExIHoC*=bx|em*QxB$nSq4FH@*MiauYkZo zQdJzy(|$4~q{OYaPJR1z6OCq!|B_Dw!-Dni(y6^4Ve>9M!}w0{);O9-{8~RZLgv^r zyjLYLLmR*OGE0M8`+vs#@2k;PBZHSH?|I(pqvRw35|3{+3P76`!+HmBa>?raj;<*M z`6@OASH4~X;+bCoaFp3{OEl3nyXKzl_K_!DS!C=5j9~3rA1)Xj8O7qJ)2(d7p3XMwVS>4 z*rBNKvuzG2)~o55)z+SrIi3Lj?K2wksh9be!LWpicOGlt>?|NGaa=%3N(o{JkwL%V zengX-kED`pp~IkwQLtfSV0#E$%Ze9gRh#gEanX_qm>8$>RMc>Co>w+}%CHtH6|f<` zvL|k#)wZ-z(qi(z@aQ7K+19b>eTY^a$ti0Ad{<-WsNrc!!C%77!>FSQUiTFJd_*R&O2eapK0jtFn4L4RjtX28T>@&e}6V{@x%>#Du z{pBirmPKv2cPMFRY?e^i;iXvE&ZWm(O`E~`4W~2;JOsu!Y`89(3a)#g7SLa^n#9+3 ze%BLL`^e!3BDCrsv%&T)dOfg#4;|Uv!TwRs5D-bVAAXw2swkGnIXb0sQ1GHFE0{is zdC}NR`tTyKKzt>m%c+aZ)vRT-fU4TYPm#R+=zm(K%^jml0?MCRvT8yXx(~?g@rfvz zr11!)w`yLO#6#Fvc-&Xr=o(W`XoQa2_r z#Z?RVqD!C0(y8I$RgY%{TGW32k11%zs(j{cUya&nS)31_iKL560aB{N3a?l~vzjUK zBil4T?)S7Yg#rXJXi}bjQOs4D&Q$l?yL^L^&kLH54&`jZ*4tjIiU%8sIvrm(AIW>t zim%AIblY!w?dSfZ!qHNN5PPV!i;o9ibzt_<<|b;|5var>Lo1scKqox9`Xv&K8hwMI z-ju)zlkq@`^6Y#v-PA{^_tL4ZqpD4g#z!Jp#XobD8O4I$R8k_t+nR|oa=(n1(bq63 z{g-M6AmNw9G`;)BK82JzBG|E%4&%yzakUX*3aFP939MV;nT7q&l-eb~>p4d~a{W61 zDyu=wJ61tt)7eC&BjSYEf#nie>Nu!nW!y~b|H9SR4iV7iokJ%aE&H(&H$i+^0a&!U z=gS(N703Eo;FOLsfLM&krXD|>SNidiOhL>nQDKnG6@NoEiBX@=HCyNFnJKk8@pZBs z=T~LYa9XiVuF_W>LJm}(sCngIZJABttMn%X|A662ud=ki^Y=vUqY@3@EG?n8tded7 zB8#YJlGm`%4xV+7oAXpNg0lO8^{Jio3c4n7z`Rr4RQjL-5I8Pn${1&pXelj#AH8FP zPe0yH|D84K)2iLXv7Ej(%M~kMb_Ev3}<;Nijf~8&)sXK<3cA`}lbi&QBTYUY!?}8?fIG{W4D( zXc{E_s69UFF$zrko#p&tuc5a*^kJxG>c?d331uR!_7G z`=2^lwua6g$1BS9yi_dGc%F;LBv>opW1IaKuX<;k+=hMTqJNAcAxlQb>4A_F&L8xa zf~ta+f_`0S1-+>Q#j=`DdU!Y{7~zr7BIye+y{Z`Pr%h3?t)Ov_ybZ?YXL>2IA(g9hncq13w`%4W_#Ix2+wWg;xblU zR=<8p_bFiH5hAGyxIEH&?tM_pnWF)lS!Q-y!JCN+r_zd38L9bqVs!Hm$cGl;V_u3- zCUE`ynTR?mtC$!e;O5NuNefXZe=Cm(SZ>Pk4Q%+1-=WOIq=Hr3<3DXzO_M9T_xZy= z|AEFaw1wQl|DgXDtIGyI@ zdI9m5Lv!l{mJBBBSM9l64YmWYgHa3O$n)<{1^Sj{lgVuAEh?b{0x$IAm_}D((8QId zm?Ouwh+ieJ`&1H)rhvKCv!3Msl@htsvL4jkIO6HuoW;)b4I1k5&#b0Z_TzrD#1YQ7 zEeuVTZ&s+;p_U}|z?Bu+1@95;gYvF~BHS%te@MDXmfQl%Hoh}r{|aii!d+9YVCv`>B9D5fmCFxFIRH4X9w&&LPHn ziJ3WTr9n1wvulyq(1$@kBX_2sG5)?UZDh|NV@IYgUA8nu#%Pi{z3!#g8ccZnT|F@Q zt^PfxT4LWj-N01A!I?_ed1fVM?M8B(6PC<$K?jv2F`Z^H)`uEY*ebt*`p zYRt@u3f@v`M+j5jezRmkdts#0*IO6>W{(nibfUEQj<-K^-0^>5aTlT%N zI6$Ay)Ar)p2R=R;4_>=$(9{7INIbJbh?>Zp_;#^kCUkkk6b-?@lZz<$EgHT$b&qzT z*k84PBoL~}4t!zrr)?)=11q1^#YuH41e`Ztjo$OV6ZL)|8&^Q;1$mzI^}nJ(e9i^B z3@k2Yn0j94$WrWYPhSQ}_D9+-jVvP2(3$Ajn5 zA6j)|*4d_k2TfbBz~lR+9du=7rimKX0$n|DwX(ki09*WC&1VCLQm9VX6$abkpmFtp zdrcDUkA4ZccJyZpjw;l^E9Gz7X~u{Tvqx7gjT~zPRk)N)8DSKj`$5E>d+q`~Zb!{| zr_?0tr@XJZ?6|_JG|#TCIBr_kj7Z?!KnYfyErpy*q1-w1Ve4+re66lvF2hw$D#h(f zGEN)kn)7VmRdFXwhVX8mA-jg&qfANTpY|D#zeu|;5}W)Z+Kb4Nk_?nLT!VXez6j!N zXO^f5ZMdF7iFBMPoeAmf06cQ#289I**cIq)<5Q+>pt`D4uJlw702HQpoJ9Hw$J$A} z&_42Nw?Imn`XV5Y^tVF0#Y+Ng%JD?&`JEnu{btG9gM!D^y~lpul0pgZQXtFM+w!6S zp*iOfl6>XAB$os(3#)gSCtYs!&jfy_3;3B(`r^Fa5hFhh{xglx8g~p9{jX zrTk9;fl+^wGMlOkb~7`18YKb<)sn6+&;w*5b$3+-IMmmgFB5M* zbu99z7ZK(Rfj&Z5DrlRSD(IV-emY~L?5UGqMyDvzN`KB3sQ`{scd_Syo%;D{7+W z@B4{8q^hZ3r8)n)f4|49Y%?sK?U!&ndg$s6uj?u24RzH!Af-vK4?$`!fa6{gZ@{Y; zR@`l=AYy<$L?l)vI!_9d3OJ;3>L1T0;7&nZd-QiJ)~gIRBo#=Cs9avPfSO=j%f1j~ z7^CBC3FWf20zEjcQw5koc;T0!nS!>eB!ZPFinZNyG`#5(J3yYV0Q~<%9CX~>Be5ai zqWW-0m*LB{-?KNK0l1wRZBIbXOoISklR9B)qaK+&uuoTDSYi%_tc1ENy6bEctY8q#qn~qq6i59#dc;wc!jz(#Uh}Wr7S6-m>Pgy4g2@9f`L0VpO6bvH z`&uOTqFc-3!S%zCI;TF@sm=@A;V@zKzXJ+w|d6_3{y zcZZ0j4+-U!?r^%%2qp=TO9Vs_Mev2w1Ah_ewy;QPz3bYWw(Zvm6$p0F?8BdHTk`u) znvt*?HQ$mzv0fnV>C;#9onmfpKA-Q?*T*W{f)eLfdv3BCB(}IMbGz8tJws$)u|zNi zIH}8vYg#wcyva=#XDTn>&rfm|W(ajw$_jGVsY2MOt;;vPtOvj50%MsX+Snsalm4vM)N@au+kDQw&nKwmpK|oU-8a%X}%io zEEt`t@Do&Oz{Jk!dEf6O3Gx&(-lm4+YJm38RSSJT(%J!@dPSw$4y-ur`@dNLB;$VQ z73;#HX5T)J8KrOoMpjvn2D zQ>^1Q>_C869kz@;%=6BGV>rlBzhM+ohM?YYfoR?Xaa2k1qdTb~_2=U<5dI?2ia3Xt zSzd2mU8_BqHQ#lW53x3BTJFyG`+WK4aMz9n^r=)jQphq1JhGm|8{t5pL*GPRK|9P6 zVPJIamAL)F^yV<&p_})0#BY@z;Nc}!I7QR)@R|{?hfVN}&rvg-Vkad1*~P3PD5I#H z%UeXtgOn4dWtF!{3o*r7BQ<)|QCq&OA^U!T!pKnDe(HKr2Y)*;14CA={(z`Xv63VO zdZlSNrBbvrMbXL7l}tbuvXK1NKXPU&T`2SAtyD}^@nx^BCU-zDk&>VKSri_?L{C_I zqS2F6z(XjepRR*TJy593s8Q8&*9iimp|n?FIr2!$2o$>|uCWzmc9YlA`=H@Z6D;Cl z$|4aFb(Q<=W({X5qEQ-8h?_<(?pDg%^lJ4B&OjUv)#Oq^0!SkPWuUX*4Gg`e%({%j z8t95a`6F0Kcr9SVAH;%z5tOSWAgPz@8c%kq$t5=ZlmzWqjsc-glpuGGn+O0V{b4tE ziu?9DpWWw-9Q7m_#H|+*bHU-Im;NSaf0a!wW>(MS#nDS>;L4fAUg(vFq*_GoPES2X zJ^OqDF@pfEmTtasbtyrxQbQl+zdZj!Q}5!^`2^wm7`dj*?SBl7Lx&pETRr18^b1*a zfo}U)B-acFJF@ectvVZ>wH&X8uu9Plze0abXZ_pZ(w@tkgy9O$4!*P%z@1Lz!a{!5 zXT#_=Y0c0wAo(YeFRZvYXUt;c^$rbm5Tuu6x&_5baB`NwLTSglF!~6ZVBXNWUp&Qa z!8bGf2>7FY22ah)C{TkT>vOAPy6NlI;wL(Pv?@7O4d-#&n`I6e5XaPSZp-geWthp7UQqMaIDsl1n-NU5Z z7j{F$PR1Kw^G_z4UrLu-4W;6tR@(e}#NKm7PPDt57tR=WzmK2lT2l-aJC zknBeB-nxD(Y<=Ld`0%Yd-0PQ68B^%c;ZzE13vv#X9?#+w&WE9;D&<@VyUXY0gcmLz zf4l_%^WKks&Z4$oXxs%zu^gv^M*_OI@y0OIhGV%MA9fa?8;b{1P8@db(23BX8~C3) zQA~O7SyJ*syn1{Fe&sbk3jhYrSrTPTAIf&=_3Ga*E;-{;9?hi%3$wHR`h8y9sWyWt zU2NaIWjlQHraQs6{_Pe?nYFu780+xL9FA_!%psjr%052kAUnL?AVZGD6^Y-wIf7w# zF&j}Zt+%288_6eb=r+AuD+Z4QK+^;l6;f7`tjNUFo=K;uR4+4B8V zr5uHDyi*~@6Vxw_5BHJGpa12xgeoE=2UNBTr3|(G{B?Oe)RrLS;~k_^5sL@>b#f+H z;d&G9M*v$*$>Zzd4>jArjptXdx}t$`57-V1P`bx6YPZAG){CowIN2Yxn zj<$QqwY%rfe%`b1Al$nC9=PZJN0s4OZP*sCj}1q+jxvA!S^?5-M`!sH$06VOLH1>$Z<2yN{RQ}0$+BJXvm)9ae#T)ge(Pj|Rl%%Ev-808wr6}I+%8>d)hiN%% zSUk=T)byu@pS5^8&TbCQC;A^zx{3P)vG|nX^q;u&S#ZIRo(JcieG08N0zuqKU?q7N z2e*BF)poDftA}iszD+GO~c8317KzJqY{?OR~$1CI%f`ydd;>rou3 zVO#>E+zC%HXbHp699lSDJb?@QuR@sH`hNKnxRhaO9FGt;hOqgwk9^i@N{X^V$VsAR zJ%3_S7wGE4uK798dOtOX?H!H#Eew~+aE-+?NX{Cs=l*L7U03edzW{>XO|aI&f4uZr zaKSUrgLBV1bwtgQdwM(C^n+mVuQj=Vie2obx83jaPS3i>n6=Ha(#^;LZ`!gQuK)HO z^t`U|7ode<7J-xoX$?x+aIU5p2`uResJ|kl0mZ8iTchyY0s3%s_ju^)#ISWDKJ2&o z^N(C>^Kzmgd~+IkqJ%pQH)lX8p$yYXJrlm5TrU<6K}ZNU9kRNz2jXuIe6=|8?iY6d z*I)lquwdc5vri?*k2(j;m2b@M^_s`HDC1I&K{?;AU~+0k$qDC|xYaNKuxH;vxaIaO zuw~oMgmO$Y`fM24!GgfaZ2%5u2~u)SI6iFK;>UfSmHnK5f&YnNX?A{jKYe0W5D)3h z#D=9aUTNYGe$R(LWAh<3=&-DB>v1Em!3aNoc@vVn19Inq`Hk=x+fP%rVSM@T?c)7{ zkq7bk!IUn!9|@(a#zXw~+kfK?F9Wau=bk;NGNvCA-OgWn4TTL98kcf$OY%N1TCdp{ zW-;Zvci$nn`L@k)&$gY-$3sz8t~K`%(UoFuPE~^%g9*Aox8d6iNGFwYY}i_eXOIb6 zL@AFx=WYJ%!=JTsju$nBfifO&7#2e)fO5E>IBXqJhH3cX^YcyvhEqzp<9flJ0VSTG zv%=$PkEiiqD97U(mD1(8c71*8?{B=}gx#ygg*ZK@-=PTnD#zh&Ia$(B~4L1{^ zM&Q8hyz=YU{{Xh!_eeN=OP==(r6|HFF^r4;ArrP#XyNrE_!5k$QdWkmM;Z_7Bq<6w z9bXK>&3WJRq0d+qx*Sp(bTKR^V`gm_~2$M;AjfBx$mUk(5O=bn99W-~@VWV${0Qj~EiM_pR5 zFgrUB)3bBFO-0*vrRy7GP5Iun`F{Aux7Ldg3mbK9eF!tZ9{br%DQl3`VVtKdb6_0! zFYMH-iz4BOGt# zP#rG5+~MMyk5`gy6Xu_T~fL-e6?4h0RGz>Uk<0A`gB;eYWbq&gj05}hkCf< z(UzC;30C&|bhvp~rQ1Sa@4kcZgIn*0E%)t|Atk^ogjGj~byCV4S{P=ip1LUF+*f}1 z`7QO6#<}pr(gVjK+#mDqtN)Hj#X)ks8iaA?*X{^woS14-bV;zvY-`(EL|V$k0C2U zIi?r~gfBWj3v6g5N#YS9O&rCibAGEiZ&l$B*okQ2YYdoof9UUQS~(7o<5i&5tcW)5 zq7sZvIARJgYhqFm$0&xC>3$TlIq&UqKXJV>|1<87dY=TP8^oQd;Pp<03AX>#3oe3n z7p|=*Z*^?F23NlAXMox0`RW-1y&jt#TFO#3pB?``FK&!^i9XK^Zd!Tgrfu*q|9+dQ zwDKT~*T*3&Lkq!^eEhmh%nFs{@Uy4+dEJf)2n+ED5KhO(Vap(l6?gV*Y6fL%!Tjmf ze`}MCI&NGC$niES1bF!vyFoVwv+l~!`zOr)MeBLIhM%87NQ=+=V`%*;aL!Zg`RhZd zCgRCdx)O|Cd@P=y{PBxn-34plymL+ic0h)`FpDX_V_eFnbidErPOu_y%CiZaQM+>2 z=KJC6|8{HG$P;)Vs3_3I&uuoOvLQ|3)oD0dRg5}}aeSK6mmwC5Cu71{R($+^us8^G z@rBaL-n{$jzqL_lWx_XX)I->Er5Y$+u~GLAw+3l&=jXGRWAIXxd#+N)(82Ku@Wq`5 ze10v5t;IwA&siV49};}E{q;_U{EAd+J8kWL^G&Y+0D$w(Ic*Vh!t}$>?e+&EiZVX- z%KClYXfQ*ge+ar)A-xvH{bO0 zV2sIB#*4GGzh7J{W3d%3_EBKF+08R^sL^H$5q%7E?Y;XB!uM{u3pQ_kID{2eNSboQ zFvIZDlyQdg0(@pg0SEpJs3cYRW-4N`K8UBwZ0g}AXbt^dv~XX{pMLPucByl`qO!$` zoGIBnSn)WpGL9%k?625~;GKg=4pfD2EWR{+wfiM0*S!1T*Qav2>;~wa3Nb!#w?t1vi#&Gtqr;yTM7D8#z+0(NB>3HPp2N34h3*z%T zgu~Z7j>!Gt^%Thwn|FQSZwO6e!_0WegRTH83|ZcIy92_;dZ5|aioX~_!z*_{>l1!w z!8Ou}5IQM2our`Ng8LCtJ04FLrAy`!#^3ahGNr$Ke#oK@x}%`qbMqhjT;a8?Gk~yX#Xp ziOLc~mceQG+0?ufp~NF9dqMFJsO(3PBR22);NPG&eH?PUs3}e=$8N9I@HmE5?okEE z+2q#nP6LK-?ns2PUhA9$ogo3r?tWzCbg{A@Qt4`)4?m1fl_>zgZ~xjaz!_(p3@cYG zU$mUCZr6VIdd&nbu4GC%mSz1u?-8o}a!d4h^PKPAy$9gCH{S)D?s%RbJo_-3fT)7;^W$E>NqoK6g#a<%2p4~j({8~|lltstK zcHgt-0DSM3JK^pv59%-xl~z8K!}ST}1?03DxmFHYe1;4qiQ|YtE7O1Kw+3k*4}zwm zoI5Fbx1Z37$juQ)Iu+(!ANUk%(>bJ?wF{Kv&4Vw78-*_@{sr(28uPfn7`Duz5UQ@T z5mSD7v$G}Wi17rSoeuEDoujDI%~P_hf&_mWU990gut6eK&GfoUTCT%|4JdpBLl<>y8?uCE4{$?FcLgOpJR$DJN{%(I=0rSRQ zQ^e|e3}F$rvT}5e#2`#=5V~GAgwqq8_-(4Oa=@BeGtLKiQxvJ#Yy z3NZmz7_utV@)fS;Z?ndCCNliRaf-^_?|_c)(s^L@rofrRBS0q_C%=9G)o`{pKAxQ* za{tnCk-mV~*`TeZ7O%9O!j9foumCUm$>-HDKJN?|m&KT-#|wtUJyw)DO6{4sxmxiO zXxPwZ&`j;VXYT>H=2Kt6&9=MOh>7e#9l9E{Mm|iPF?*V>X2Yxj?DFDELkq_r#)JJC z9LBs85$jawFb@6gqV&DL=AG~VlwI5$Zw%6d!5916UYWu-+%F2LSf_&cN8v)@Cn(e; zd=;#UQLT`ZT8;ZTyps zeda_i?0sxlT8Jl>I}YF_Kv*1BXPp@mCmZ%vA&lY);!8rA-Vgo$WI$N;x49kfc;6>U z>qQ$|4&glLy2G=TqF=h@cwvTAuF|#`1GhTd-H$060Ehb=Dagu@fPxN4{ZNy zO%TRW%7ID?K;P%pFzucfW)o<5d(YneaMSlU!rk{g$Y4}2{shkKP{y&5Rsz^8UE}h(m%Oa*o)%<9&Z^ zb72<1*TG9@#4&7RN|80@i~`MiDaVU;i@}OD!ftl~zAg?=w|HD?9HW4*()l4xh}3xq zm9n1m<@bl)|5Q8@^jW~s&P5(ngWcKv45^Ll$JYn#e7yNLuLQ8*hN%}TCmge#GA=pe zf$aKy-udQzUS(vXd62p);XCiX7ry+@-}CniH{O&^GPDv9CjJPGx;|{}8~1SkTu4y` znyn-S2nWg$Uytv{p&Y>4LwI3x#LE2;{YBTq{wDknI~R@p_P_X~)f)4`p&Nr3ZS5(< z{E}3>G3aVajWx6T;GKAzDZhUx|cYauXNb8kO1%d6bN|(SV zAEKDj60~_EOD<13A+(e7mf!m2u~x=1Jc^C&x%oa!PE9wpN1hDyJS1$a?3&GccJGJp z-nB$R4%vkNu9M+*5aNlz4Z}AV z#w;*aoyvVcoeZ(_z?F97ewcUs#V4)$s*%vByS9Qg@*H0E4!qIlkQ!Bvm%|rBOovsv zQ=vnZ`7=eJAhtZJSq4N3guLs6s}6nJbav@+e4@Z?x=7E_MFh?g$;MJ=jtuL z^(r{y^iyC}^IB7&jJxK5xy{1Ju}#kijR9fCF>Cj$`h8xaTm)Ft5`z2B%X+x^4!Cpk z4su^8gwx81`xRA^xyq6B9TbQSHG7)+$7qY7*AL~B);q%vEM(qA2EN0 z&X)*fT+iw&N*2tYy!Wq=0!}C{3?F0%jVfzIqYw@hDY*TN04uI|u}$Hs!A$Qo$o}H} z6gady4m9i>gRsyEVz9yY3ArhBQt*2H{*d#gl&-)!ygrOWnovcztIAb@aZK4t4#xK9 zosYM+FkZC+Mz@fUcL%#Jdj>bU35p!WrL8LW^^tzjx-&}29~5I?_wIf0-5+d(jhi0u zjV6Js04;U?`&Ty1k?Qab;T2O_T@<8mdt-3R=`0KmU+0lK58EL67gvTEP}HEp zJ1Hz4=f9T2?>KQxi;u>W$mz01DZX9lWW=CK9YXQvD&ctBF75x~7d{^@UUxpMMnlwE zQ@@zD=^5w=6Mv3o+gdA5u*w~_jw#_g?z{)S^tJCc6|5;l+UUFcp(tazU4fLQ92Ww@ z-k~Uj2b(>e39Bfqm3Rc@iT%c%j6onwo{Nz}7Jw zi_m2d(jlCNRZ`goaU@_9QMP&U@Ww`}A7un15nmXOkke%nGEkB@J|%uCSR}}%;2MLh zydSxJ1Hkxq{`;!|jLDcuO+LOu*y*y@D&nS8dK+U~S^$`xooj}hbCyKCpa<)VU-_m7 zFK!%#VaTBIOby!(3gwQ&(b}+fR9UK!4dW8whT{pKh2hpME`I`UmpLS@Vw`xJcha=aL#vj2dt7y3WAzwW>UeDzMm zV8i4z9^yZ*FF~tnE`>jP*D zN;f!vLOE)%CQ!ri#o`L;ITlaY`Ou*3{1cR@7+1t!az6+4pF^4Z|Hps!F$=u$7jnAT z_DW(f=xUG^VI@FUgjHvFh5p9;r%Dku{(%xiaq#*Hpp)BK5OU!HTV{nZ@S%I^FcKF@jehq*DSaaT4&}|{jd&KTu za%G$cuf?(F$@{#yOCs)OcJqzj+W>dmwJm@yZCpbrhm9{ky_{1G8hPh#TuJ(@S9H=7g=7|1p>e{^{{4%AO79^PDk(e1;+(*ni>vm_Pork6S(* zDJ#hi@MROB-1@AXsiGhS_=>}+Bgyf~FzOCpc_NhxUoAJ@bv84BY-GY5XmQZ>p%SK@ zB^uY@!_3ll3BF z`d(_HfXMT^@V2!U4jrC?U||8~!n`ea@7WJGes?|GargbC5f-5pY>z;g`4>VMLx97` z3XL|}PEL=mOWca(kQK!xbQbV_+61Sd9s+;8W2s?kF)mh*_=4d`M5>da52L%|g`BbO ze;$-O`H$=GkKS{QO+kx57(H0t=mS^^<>7PW z2Cbg}QgJ^u2vfz5>q~*EG>n^2x)^p6Yy-Hl_`*(wMlk`h(ay#Y%C^J)+@TyL%nt_x z@Vo!xDmd%RQ=2dRFv119+QXOaFg1t7LY7rk!Q5OQre|j9^EuE$9*rlOg);c69J% z0XYBg4(U$C;P4IoPuF{}pD|$m=sh2^c{yDjRx$XtjWvZ5p!K00z=O43(cxxy0tOwl zB>j&nLP2qH#T$XDyq^wL9v`VEB_0XB=}yFuN>}qIh%YtKN``I>j)N*&t$w1=MIUPE zc=>#xQftf0f96uSc-@b}+SO-~=yL8E52-Rp`=Tczi(*Ek!vp2zfR}6l4&m z5T6J!3Oh;3^Wk2xQ<0k^#&}!yg41Uiq+%4KFiCYW)J(000?+NklXNA^6q_nh=#Qy)@n_mYO zEIecN*#c^iO*%Cing+G0GVYlg$k6_dG0$NpY(Q_|#qk3uOA0bve*F}@JOxbs5%&-Gh@u&9vv^>h49 zbRvlVYG*8QAEkKy@4G*0#l|<16XmuBF@Ih~K{92?A%Z&t>M%2h96ByOOy13Z3?0#b z+{lY?65>!h6KM!zxN7kP_cfyA$^9i2pv8mlgP!A-U@L8xIuWuGmLMCr6Fr|^KL+2J zB$U|FgmFISF1P@TG4NIJw4;w${zjEO#H>o&#a_WQfWbogb~&m2nKV8SBaQ zaD5=?^{A~Rjk*L~>Ob~3=6|}Apef$Seb8pBOYqC^ zpTI8Gxe7WF8g%jTGHhw5LWgZi33H_tK5qhyQ9zOs#vh(2Tw_q?(2V9pgT6|q<@et5 zI)J58#!A`3xZkgu_j&R2V(<+PAepo5+Px2MxaoGdedE2f@spsWD8zQ@4&Bstx?YD> zxITbOZtgT|J*o#*1Fiqqv80mJ;D%uw%Mr8sQWa*jbAh1QWdfJ*Ty*P92%UzQa#!lb z?b8An5)f-vWWhI3xCoMZW6K-+5K?(C^GmLB)Zsg5j+guEJPJAuLH$TMP60B0|FAP5 z!#Ayj-TjPqCgKyxVoEm`zQOgR?F?=r%SNnhL&Sxh7!KW0!B$?pM{j$}>)^~YPJ`o) zTM-rF!`b;tx&H2EfqJQhb#~hN+r4`qeCMXyVExAX=zZ%1;gm8C`vfUS6D1}oEkv^`nDuIxZoe7=*dv7ULD5MqrDtrw{W6otE5T>MZ}|`p@fB(E1JQ8^!Z~-t`gE z90ai8jT~wGP$P_>8#KzUG0uZn*a&BI4zT~s4jW|(AqpqG6M=6GYn@(E>&cZ{!( z#1nLa{Q3nOUe1Iqb4ah>*!CD?MI|i2G^S`fDq+5KfwD2QemSi9<#O$}z4i5Q))}Y5 zs#PmsY&;R4UcYtmJPH4L{OiLjo~o+TxFf8O4}WU);1BPbBgXKJ#WzHbn87y|AHGj4Fy{_-fcuZ@HMcW?m`lz(XlH@| zSXD$>%8Nryfo%+47&fGlPvmq_V@($1m)|i1RzvtNcNQ|?tKEkI;hTaijtk$#9(WLKwxz?69+f>OD)T$8yH0&I7F%RU!Z3yFP5S93Zzz zqboN4v~hQ~FdQvHi$OOVVx5M`0bQRfrIi2Nk3n+0x^kDGAr6m6hRw; z(B=N;=5%qKdE0|d8SwCOp^Q5tIm$K$)ofuLc&tI2E5(>n?qXl@;>+OK7e5o$u0D&` z+=7&?0uJJ1&$qPf+Px3HbK|XW`yKbzDBUw}aAEpc@+|j_)6YA1s}etb7LQf%{NOP}kr&Y#E;aZEtx! z7y}u~nA^EZ8DL`ah*l};vTOHV_|A>D!EJYJ4I$zjN-1Aqzl`lZ-bGNpBY`kpAAzt| z53%*6x-)acB7~_uRE{;`U+6EoZ^3gCb`E0xix7^*!|Fwca%2ACJ3nk80o`VWZ!{dV zbQoZ0ab+38Erc#^H1U!*9Ih$HuyY_ni^Ur%$Znm6{9JCVezDTy^%>9MYWv4wn~o<7 zwgH29ju!nR!c$PJ*dGD1aU~qf(Mm9t>L(6k+;-sek^7okGH4HNv-0=f`g#B`uy#$R zGS0kS`%PEAF{Jf}9w-1~VEu+o@cA!(lU6;ud^hE*DM6{R^^Ln9i3k`tawwDa7!D3z z93~h_VSJiWPC_g?K}%L{#34l)iZH|b2{1;?)rYIKPDG}^F&I;&8}ncAi{GCZjN3E+ z;X6NUJ3&ln^i{>HL1!5FX84&@@G*!X4|qHtwhR`z@FnX9fN#*5@bO?}JN(2dO{x1h z2UoiMYVqv>RE0%W!uSGG{DUjmEclYk4+u4h=EK+tDF6OjUk_jmtX+N9&^h7U9PqB) zd*RzR)WglZbA%UhBq9P~bvxTHRLHbZ_HFOzlrdh9I|JH!--xsN5ENvnES36@lq9K- z5$1>$h{fX(pd~5Dj!=&JA3R6Mf8lxH@gzG7yuQJL`QPvO5NSnY%8}baBkUS~Ss@ZQ zs}p=u!^+_ILycUhtU5rKbs9YU4fUVJljlE%QzmqoG8bW-RJPb(ZG9{b1Y5QK2T{U9 zDqU$9Sy&{cs_ZF1H3!ZLMDcyqH^*6t>m#sJW$O6I^-=lzZ+!y*wC@;4IuA)*NG{4^M~*FkR3)jHvc#~sf0P? zh>~PVPRUuyFyqh)?Enruo(QBEMg0CKzQN%et8abe0f?VD@YNw3!bFcpf-E~Bz0WM} zu1Yr-zVfhg8opBf$?%OqCn01-`09{#joFCoJV@&>KKrcGVdcsdtlGQhfLq0T+lH-z z5=V<`>k<%Vj_VM{=L_0U9a?TXZS?(NX$VUf;*E3&F9u-@TQzqY&k;L#vGa+YlZYZF z@r#{_SSMomVQPEDoGBfxc^I$;|+(+!TV#l{|plR`oaF>gvjp^C|#Xy!TlGN zx0u6~6smMSS!sryk5I{q%2hgEJYQmS3fdfwUG2#A;$?Wi@Bhvl;Ow(bgHHP5jz4keq02`q5mAliXs;7XO?nj@tgnsj;j%qWF?sk zUko+e_{WBIrAA*IUQQ}=1-3Ex=1vHU!7Ri7Z1@WGorEt9K=xPboM@dM=XVHM>M*NE z7O$jqQ`=3 z!P`G*T_YvJA*CFLftg=oO3oh`Q*fc3pQD!JYyH%Ql>>isp&M1|v@<07t<^h=hc;4@ zzb5c2=v=XQ@O7EP^!y|aLFo#^#mVi|Asw<^p`5LZbx29aryz-8=Hdg$x?Xn4!{_J6 zjYe*cnL+!d|M|!3cZ?etLz4sEy?Y;g>xP@*w)I=p;{(_V0Z99c!_jSYqg2M7a>f)c z-Jy%_kGejgd_|?4nLwS8|&Bxex(3ueYQx!5hZ!VN^{Rj6$=$s6ybQ8nM zI)rt&;l>oVPRU_g0lFc)y7?9N6Tv7qR~_>=+9^=#BY;dS9=|@7!(|81`;6m}psT?) zmD5d5n8I;O!^sjA2UoT^O4ctq%Gi>|kba(WOV)^Yw!4=;JO&M|OkOWk&a%?X-`_z; zfV1SF1aB2eNoA})M9LrDAhIIOxUWqcpRc*yy57!!EK{0}-Y2);t6 zMf5itzPyuxKE=$&=jHKWKV!qy8jO*9V8B2xXtaRBhQb#ZeCbo-P5#j1o@zdPK^wjW zw4T9?K9gPH3&2i@6k*sX>F`DCdE+02FRot!zChsd$zp~z66Y-z;M?MU#zwzsn`V^0E{Xy`>{)^{LmleN0j?duhY4=|! ztz3Vue%bTF??1gBJwEVPI6sj4ht?aqKdwJg-{g7~eZErn4?TZ*_0{?V-nQ6Z+tCg` zkHPs9ezi4Xns~zYK>GZppKrDO2<|_fe%vqA^P9k@8JF_@$pjiS9(5_`^($Q zNIVs~AGx3BpuWlVYrBUPCG9~NrW{eOz}b=#1~2>DhaM{-zoNED$Ej6VRB-|7dAObh zvaLVB_uyDh+4nes`H4TAts5+7B;`gU{)@^TH)huNjM@u}^^J4u&cSwg1buiS$~I@c z*rTVCLg>-6`|Ijg&guFbc3kNOIo+Thar{{6;^8SCA0COJ^&l(TP(YbLp(zTFK6b?I zm{O(^Ts{G>hm;*Spk9yrgX5NVPmK_uQdP=AJu3SXrRP4M{{E}=)(Q}7KBJk%WP|7?W;1%DT9(_*Oh(N4ez|UFGPPF?O|2f9> zXM_<>ZsX6jcbK&UgR(zZ@NFy$W3b-7Jtf?o?q#Dp)ylO4&3t*`}|4QW9LWDd+@wS^{v*g73z!K zk1SQSU+UZz4=stv>7i#y2;$>Z4s@PaA0FSM6CD|-(qkvXW#VGc0 z{0_cH&y_jLnUbDQn6vghM&1(+b3!;E7Y{0f9--#HE8?@7T|Mv~ty|CQSLo4-xe=-U zA=*=qDqUY){(5MAyW}cenp2_MiK38{Fxk&Nu7MIxE8B#U1-`$mT(MuFGNk7t@mM0D z$Owp(E9LhiZOi8)^%@nzF{LXe&m+QHR=hFg8`dKpabphi^Cy%qum2onjGt6o-@)e$ z@CX?j(ZlVAI^sl?6aSo9J^@H<2k8TkN6hqg#UDm;mMGpJXH2-DD0_(uY=nbOXu|CT zx<917W$j=vmjypR{5ioRCv;?m_%A8<k-L~fr ziXH25>!o%+BPfMPPFbpN{`pqxALXQ#_Q7cWOZ64opWWZ!d5exz$T?$NTH*7heIEH7 z3io`F&%3Ua#W@#R`-1CNYfn&)Sd9;G&RrTAQreeT`_JyD+J5D>m-v2>coRADSbN4x z7EgA2iFhU|U!nebB=?AvV$ie1J#3Owjr17W<4Mnp=17T>gs_LuK2&J3HM&0YQ|ZxJ zA5eQjs(1k_LlY=N=f7n=K952*re~8liYswbqyaFXFC{^mHmIOvUW&9@=AD!-m^CG4*t zmFay5HWTHXQp%y?PTVKl=n4ustbmTnSl#ZLiLjmHpF?Rxtts8BD6Scha>yuWXz<;U zK8M5Ph=YoyBOJh$FN!Dj{SUl7LH>txN0fFk7zvEemZt55KBCJiK=z;14~qxiZju`Y z`uz9@LTLBALhsc*hsYRtV$3`}yE&dC!ntSXM`X-A=($w%I$_SoviPJN6IYV)*?e97 zC;JbPv#~83IMP7=t$9a9} z&yQ2i{CSC7ZxnWWeiPKcR^M9v+qjaJ&L7;r^!<+IgysEX?Uz4~sEmc2vEOddcIERY ze13w@UzYM!a;ZG$I*J^z`uWJ4BNOZweKs zBy-k?w-)O6fGr`smP3W6XTW=esoBkZ>>dDh36Nr>xnHqIXTu&1@5%AmuHJ7z=L1)H zRH*WAwvm*g3H^5pQShHgT-w79d!!KdNKr0>N(QXVk^kB(MV`Y4p+122;DOY?kJHkQ zExA7fDP6anD=VsqWCf+?a3y7%H+wl)5m&C*EiM>IE*sqkW5{zGQ_3T=+4U^BFVZY@ zHrqZ>y7(&m#<1ru6w=+5GS5k?`=^w&Z$G$FG{Suk?`yl=SFfmy@#iw~UNLTm3|k+z zJA4FAnPbu=N6g+1Xe&HuH{I@hq_AoC#o5s&h|9M_Yy>sZ?x&R|Iz7n#hR+A`i*pap zj>ZoFYbVV?6;`3B+f?Sz;?aA)q$k?U^|W0-K0GL77li z&VM7kCrqwq!k*g@@9pvU0DVYeIVS3_f0(5BXwTFH^^?lkNblWRVLw3IrTWu*%7QYI z;!~doAxA4cAHdJ=@0aDrXDD$X^@q+cDKFGmrr+bu+@D>qeF9p4NjjJ;>tO# zl!Y8ASIT_Oh}%E(Po7;J))y%L>g@^ryo&9o`g!c$9^v-R`)?HG#{Es?h_m9w_dBkn zrI87-Ju?~p3#HtC3h^TU==BBvh5B&!$I|+z*VAW92m2f0_@+L3#^8(SN|D>GQfv=2 zjO=vm8>kW%m86ghh$wNE6b^f)xJT-GM6_oa_9&@Bw}3uuyoVX_-&dDqJvetpHtEsg z-J6GiD`7pyYf;aZ^q_G+UQ)v9d&xK+-Xn#^f1Got@k)w^ND%i>hB+opu}I351_=0s z!AL-uvke?X$~F|j!N`hme5gF>g4fE*5-U%xL^Ye2iT#z&d6>hOJV*C0s+0rjyU=r zvDjV-aiPy^cDoeKxpL*p+F2KN?rdq+&fxZiJvZUo5ux%W^-tmf`uRtGC+9+ukz!x3 Z{~z(wh{^6wWorNc002ovPDHLkV1lqBt%Cpn literal 0 HcmV?d00001 diff --git a/packages/flutter3d/test/grade_test.dart b/packages/flutter3d/test/grade_test.dart index 0a5884e1c..a5b2135ec 100644 --- a/packages/flutter3d/test/grade_test.dart +++ b/packages/flutter3d/test/grade_test.dart @@ -63,7 +63,7 @@ Future> _ramp(LookSettings look, {int width = 256}) async { void main() { test('every default is an exact identity', () async { - // The promise eighty-seven goldens rest on. Neutral for these three is + // The promise eighty-eight goldens rest on. Neutral for these three is // (0,0,0) added, (1,1,1) as an exponent and (1,1,1) multiplied, and the // composite has to take all three without moving a byte. final plain = await _ramp(const LookSettings()); diff --git a/packages/flutter3d/test/halation_test.dart b/packages/flutter3d/test/halation_test.dart index bfb5940e6..1668e1939 100644 --- a/packages/flutter3d/test/halation_test.dart +++ b/packages/flutter3d/test/halation_test.dart @@ -87,7 +87,7 @@ void main() { test('zero is an exact identity, and is the default', () async { // The multiplier is one on every channel at zero, so the chain comes out - // byte for byte as it did — which is what eighty-seven goldens need. + // byte for byte as it did — which is what eighty-eight goldens need. expect(const BloomSettings().halation, 0.0); final a = await _glow(halation: 0.0); final b = await _glow(halation: 0.0); diff --git a/packages/flutter3d/test/irradiance_bounce_test.dart b/packages/flutter3d/test/irradiance_bounce_test.dart index c383c26f4..0c4cd4419 100644 --- a/packages/flutter3d/test/irradiance_bounce_test.dart +++ b/packages/flutter3d/test/irradiance_bounce_test.dart @@ -136,7 +136,7 @@ double _tint(Uint8List rgba) { void main() { test('off, the picture is the bytes it was', () async { // The clause every recorded frame depends on. A field is null by default, - // so this is the path eighty-seven goldens take. + // so this is the path eighty-eight goldens take. final a = await _draw(field: false, redWall: true); final b = await _draw(field: false, redWall: true); expect(a, orderedEquals(b)); diff --git a/packages/flutter3d/test/masked_shadow_test.dart b/packages/flutter3d/test/masked_shadow_test.dart index 6074eadd4..818c773c2 100644 --- a/packages/flutter3d/test/masked_shadow_test.dart +++ b/packages/flutter3d/test/masked_shadow_test.dart @@ -223,7 +223,7 @@ void main() { test('a caster with no map keeps the shadow it always cast', () async { // **The half that lets this land.** A material that is not cut out goes // through the stage it has always gone through, with no sampler in the - // pipeline and no texture bound per draw, so the eighty-seven goldens + // pipeline and no texture bound per draw, so the eighty-eight goldens // recorded against that stage cannot move. Asking for MASK with no map to // read is the same case: there is nothing to cut out. final plain = await _frame(masked: false, withTexture: false); diff --git a/packages/flutter3d/test/material_vertex_stage_test.dart b/packages/flutter3d/test/material_vertex_stage_test.dart index da90b63ce..6d4daf07f 100644 --- a/packages/flutter3d/test/material_vertex_stage_test.dart +++ b/packages/flutter3d/test/material_vertex_stage_test.dart @@ -172,7 +172,7 @@ void main() { // **The half that lets this land**, and the reason the seam is a null field // rather than a new required one: a material saying nothing about its vertex // stage goes through the pipeline it has always gone through, which is also - // why the eighty-seven goldens cannot move. + // why the eighty-eight goldens cannot move. final withSeam = await _draw(lighting: _plain); final device = CpuDevice( diff --git a/packages/flutter3d/test/per_view_exposure_test.dart b/packages/flutter3d/test/per_view_exposure_test.dart index ab4b8fe3b..1ce146cc9 100644 --- a/packages/flutter3d/test/per_view_exposure_test.dart +++ b/packages/flutter3d/test/per_view_exposure_test.dart @@ -157,7 +157,7 @@ void main() { // **The strongest form of "this costs nothing where it is not wanted".** // One view is the whole frame, so its rectangle is the whole histogram // and its exposure is the frame's. The composite takes its single-draw - // path, and eighty-seven goldens stay where they are. + // path, and eighty-eight goldens stay where they are. final off = await frame( const RenderSettings(autoExposure: AutoExposureSettings(enabled: true)), ); diff --git a/packages/flutter3d/test/render_scale_test.dart b/packages/flutter3d/test/render_scale_test.dart index 4f9434d9e..6597f7a45 100644 --- a/packages/flutter3d/test/render_scale_test.dart +++ b/packages/flutter3d/test/render_scale_test.dart @@ -36,7 +36,7 @@ import 'package:vector_math/vector_math.dart'; void main() { test('one is the whole resolution, and is the default', () { - // Eighty-seven goldens are recorded at the size they asked for. A default + // Eighty-eight goldens are recorded at the size they asked for. A default // that shrank anything would move every one of them. expect(const RenderSettings().renderScale, 1.0); expect(_drawn(1.0), (width: 64, height: 64)); diff --git a/packages/flutter3d/test/sharpen_test.dart b/packages/flutter3d/test/sharpen_test.dart index d519fcd73..0c38ecc37 100644 --- a/packages/flutter3d/test/sharpen_test.dart +++ b/packages/flutter3d/test/sharpen_test.dart @@ -95,7 +95,7 @@ int _steepest(List row) { void main() { test('zero is an exact identity, and is the default', () async { - // Eighty-seven goldens go through this pass. The shader returns the centre + // Eighty-eight goldens go through this pass. The shader returns the centre // untouched at zero rather than running a kernel that rounds to nothing, // because "rounds to nothing" is a claim about the target's bit depth. expect(const AntiAliasSettings().sharpen, 0.0); diff --git a/packages/flutter3d/test/sky_procedural_test.dart b/packages/flutter3d/test/sky_procedural_test.dart index 53e485e12..7e103c8ed 100644 --- a/packages/flutter3d/test/sky_procedural_test.dart +++ b/packages/flutter3d/test/sky_procedural_test.dart @@ -167,7 +167,7 @@ double _mean(Uint8List rgba, {required int channel}) { void main() { _hardEdgedSun(); test('no sky is byte-identical to the sky nobody asked for', () async { - // The property eighty-seven goldens rest on: a frame with no sky in it is + // The property eighty-eight goldens rest on: a frame with no sky in it is // the frame this renderer has always drawn. // // Mutation: drop the early return. The sky is drawn into every scene that diff --git a/packages/flutter3d/test/ssao_blur_test.dart b/packages/flutter3d/test/ssao_blur_test.dart index 64b236e92..3eac36ade 100644 --- a/packages/flutter3d/test/ssao_blur_test.dart +++ b/packages/flutter3d/test/ssao_blur_test.dart @@ -75,7 +75,7 @@ void main() { test('no taps is no pass, and is the default', () async { // The occlusion is already off by default, and this is a second // full-screen pass over it — so nothing pays for either until two things - // are switched on. Eighty-seven goldens depend on the frame being what it + // are switched on. Eighty-eight goldens depend on the frame being what it // was. expect(const AmbientOcclusionSettings().blurTaps, 0); expect(await _frame(blurTaps: 0), await _frame(blurTaps: 0)); diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index 415e07cfc..118d89c42 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -1,5 +1,15 @@ ## Unreleased +- **An orthographic camera no longer reads its eye as a point the light + travels to.** Highlights, Fresnel and reflections are measured against + the view axis rather than from the eye's position, so they stop sliding + across the frame as an orthographic camera pans; fog thickens with depth + from the eye's plane rather than in rings round it; and the sky is seen + through a sixty-degree lens turned as the camera is, rather than as one + colour. `isOrthographic` tells a view-projection matrix's kind. Light + shafts march from the eye's plane, as the volumetric fog already did. + `orthographic-metal` is in all four golden sets. + - **A material channel in place of the light, over all or part of the frame.** `RenderSettings.debugView` takes a `DebugViewSettings`: a `DebugView` — albedo, the shading normal, roughness, metalness, diff --git a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart index d57645d4d..3c8baeb4b 100644 --- a/packages/flutter3d_core/lib/src/engine/render/render_settings.dart +++ b/packages/flutter3d_core/lib/src/engine/render/render_settings.dart @@ -1462,7 +1462,7 @@ final class RenderSettings { /// eight-bit answer — `auto_batch_test.dart` holds a hundred cubes, turned and /// scaled, to byte equality. Impeller, WebGL and WebGPU compute in 32-bit /// floats, where those expressions have far less room before they part, and - /// nothing headless can run them. So the eighty-seven goldens keep the frame + /// nothing headless can run them. So the eighty-eight goldens keep the frame /// they have, and an application that wants the draw calls back asks. /// /// Shadows and picking are unaffected: both walk the scene themselves and @@ -2246,7 +2246,7 @@ final class DisplayTransform { /// **Everything here defaults to doing nothing, exactly.** Not nearly nothing: /// a vignette of zero multiplies by one and grain of zero adds zero, so a scene /// that asks for none of it composites to the same bytes it did before this -/// existed. Eighty-seven goldens depend on that being exact, and the composite +/// existed. Eighty-eight goldens depend on that being exact, and the composite /// pass already keeps the same promise for ambient occlusion. /// /// Applied in the composite rather than as passes of their own, which is the diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer.dart b/packages/flutter3d_core/lib/src/engine/render/renderer.dart index 53fcea565..b8df8b6ef 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer.dart @@ -1565,7 +1565,7 @@ final class Renderer implements RenderServices { /// `gfx-76n`'s strength, in x. Neutral is zero, which the composite reads as /// a multiplier of exactly one — the same arrangement the occlusion's - /// strength has, and for the same reason: eighty-seven goldens go through this + /// strength has, and for the same reason: eighty-eight goldens go through this /// block and "off" has to be a number the shader cancels, not one it nearly /// cancels. Float32List get _compositeContact => _compositeInfo.contact; @@ -3173,6 +3173,13 @@ final class Renderer implements RenderServices { final Float32List _skyVertexData = Float32List(3 * _kSkyVertexFloats); final vm.Vector3 _skyRay = vm.Vector3.zero(); + /// The lens the sky is seen through when the camera's is orthographic — + /// `P7`, set by `_encodeSky` for the one draw: the view axis, the world + /// directions of the frame's right and top edges, and the frame's aspect. + /// Null through a perspective lens, whose own rays the sky takes. + ({vm.Vector3 forward, vm.Vector3 right, vm.Vector3 up, double aspect})? + _skyOrthoLens; + /// Pipelines for full-screen stages this class does not have a field for. /// /// The engine's own effects each keep theirs in a named field, which is fine @@ -3515,6 +3522,9 @@ final class Renderer implements RenderServices { _forwardData[0] = _forward.x; _forwardData[1] = _forward.y; _forwardData[2] = _forward.z; + // `P7`: whether the eye is a point the rays meet at or only where an + // orthographic camera was put — see `Orthographic` in `color.glsl`. + _fogInfo.projection[0] = isOrthographic(viewProjection) ? 1.0 : 0.0; for (final node in scene.meshes) { if (!node.visibleInHierarchy) continue; diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart index e37f72b54..ad6dcccc4 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_planar_pass.dart @@ -114,6 +114,8 @@ extension _PlanarPasses on Renderer { _forwardData[0] = _forward.x; _forwardData[1] = _forward.y; _forwardData[2] = _forward.z; + // A mirror of an orthographic view is orthographic too. + _fogInfo.projection[0] = isOrthographic(skyViewProjection) ? 1.0 : 0.0; final frustum = vm.Frustum.matrix(viewProjection); void encodeHalf({required bool blended}) { diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart index 1f0745afb..f436b568e 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_post_pass.dart @@ -205,7 +205,7 @@ extension _PostPasses on Renderer { _fxaaParams[3] = settings.blend.clamp(0.0, 1.0); // `gfx-29n`. Zero exactly when nobody asked: the shader returns the // centre untouched at zero rather than running a kernel that rounds to - // nothing, and eighty-seven goldens depend on that being the same bytes. + // nothing, and eighty-eight goldens depend on that being the same bytes. // // After a temporal resolve the robust kernel, at the resolve's own // strength — `R2`: what softens a resolved picture is the history, and @@ -1678,7 +1678,7 @@ extension _PostPasses on Renderer { // itself — then one draw per view, scissored to its own rectangle, so the // exposure in the uniform is the one that view metered. With per-view // metering off, or with a single view, this is the one full-frame draw it - // has always been and the bytes are the bytes eighty-seven goldens hold. + // has always been and the bytes are the bytes eighty-eight goldens hold. final perView = settings.autoExposure.enabled && settings.autoExposure.perView && diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_probe_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_probe_pass.dart index cc2d078cc..5798f3d36 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_probe_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_probe_pass.dart @@ -324,6 +324,8 @@ extension _ProbePasses on Renderer { _forwardData[0] = _forward.x; _forwardData[1] = _forward.y; _forwardData[2] = _forward.z; + // A probe's faces are perspective, whatever the camera that asked is. + _fogInfo.projection[0] = 0.0; void encodeHalf({required bool blended}) { for (final node in scene.meshes) { diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart index 56e40af73..7ee0bcbda 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_scene_pass.dart @@ -306,6 +306,11 @@ extension _ScenePasses on Renderer { _forwardData[0] = _forward.x; _forwardData[1] = _forward.y; _forwardData[2] = _forward.z; + // `P7`: whether the eye is a point the rays meet at or only where an + // orthographic camera was put — see `Orthographic` in `color.glsl`. + _fogInfo.projection[0] = camera.projection is OrthographicProjection + ? 1.0 + : 0.0; developer.Timeline.startSync('Renderer.encodeDraws'); void encodeOne(MeshNode node) => _encodeNode( diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart index 46ef3ead1..dd86ce239 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart @@ -1105,7 +1105,7 @@ extension _ShadowPasses on Renderer { final skinned = skeleton != null; // `gfx-60n`. A cut-out caster goes through a stage with a sampler in // it; everything else keeps the stage it has always had, which is why - // the masked half costs the common path nothing and why eighty-seven + // the masked half costs the common path nothing and why eighty-eight // goldens recorded against the plain stage cannot move. final masked = maskedShadowShader != shadowShader && _castsMasked(node); final kind = diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_sky_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_sky_pass.dart index 6e866ec4f..5f47617b8 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_sky_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_sky_pass.dart @@ -78,6 +78,9 @@ extension _SkyPass on Renderer { pass.setDepthWrite(false); final inverse = vm.Matrix4.copy(viewProjection)..invert(); + _skyOrthoLens = isOrthographic(viewProjection) + ? _orthoSkyLens(inverse) + : null; pass.bindPipeline( textured @@ -286,12 +289,23 @@ extension _SkyPass on Renderer { /// zero-to-one on Impeller and on the software rasteriser and minus-one-to-one /// on WebGL, and the difference of two points on one ray is the same direction /// wherever the two points sit. - static void _skyCornerRay( - vm.Matrix4 inverse, - double x, - double y, - vm.Vector3 out, - ) { + /// + /// **Through an orthographic lens every corner's ray is the view axis**, so + /// the sky would be one colour, a cube map one texel, and the sun's disc the + /// whole frame or nothing — `P7`. The sky is then seen as a perspective + /// camera of [_kOrthoSkyFieldOfView] standing where the orthographic one + /// does would see it: an orthographic view is a way of drawing the near + /// world, not a claim that the sky is infinitely far in one direction only. + void _skyCornerRay(vm.Matrix4 inverse, double x, double y, vm.Vector3 out) { + final lens = _skyOrthoLens; + if (lens != null) { + final spread = math.tan(_kOrthoSkyFieldOfView / 2.0); + out + ..setFrom(lens.forward) + ..addScaled(lens.right, x * spread * lens.aspect) + ..addScaled(lens.up, y * spread); + return; + } final near = inverse.transform(vm.Vector4(x, y, 0.5, 1.0)); final far = inverse.transform(vm.Vector4(x, y, 1.0, 1.0)); out.setValues( @@ -300,4 +314,36 @@ extension _SkyPass on Renderer { far.z / far.w - near.z / near.w, ); } + + /// The view axis and the world directions of the frame's right and top + /// edges, out of an orthographic [inverse] view-projection, and the frame's + /// aspect — the lens [_skyCornerRay] sees the sky through. + /// + /// Read off the matrix at the frame's middle and edges rather than off the + /// camera, so whatever the backend did to its y — the top edge is the one + /// clip y of one points at on every backend — the sky turns with it. + static ({vm.Vector3 forward, vm.Vector3 right, vm.Vector3 up, double aspect}) + _orthoSkyLens(vm.Matrix4 inverse) { + vm.Vector3 at(double x, double y, double z) { + final p = inverse.transform(vm.Vector4(x, y, z, 1.0)); + return vm.Vector3(p.x / p.w, p.y / p.w, p.z / p.w); + } + + final centre = at(0.0, 0.0, 0.5); + final forward = (at(0.0, 0.0, 1.0) - centre)..normalize(); + final right = at(1.0, 0.0, 0.5) - centre; + final up = at(0.0, 1.0, 0.5) - centre; + final halfHeight = up.length; + final aspect = halfHeight > 0.0 ? right.length / halfHeight : 1.0; + return ( + forward: forward, + right: right..normalize(), + up: up..normalize(), + aspect: aspect, + ); + } } + +/// The vertical field of view the sky is seen with through an orthographic +/// lens, in radians: sixty degrees, a camera's ordinary width — `P7`. +const double _kOrthoSkyFieldOfView = math.pi / 3.0; diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_transparency_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_transparency_pass.dart index 749cf1c5f..06f0eae11 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_transparency_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_transparency_pass.dart @@ -128,6 +128,8 @@ extension _TransparencyPasses on Renderer { _forwardData[0] = deferred.forward.x; _forwardData[1] = deferred.forward.y; _forwardData[2] = deferred.forward.z; + _fogInfo.projection[0] = + deferred.view.camera.projection is OrthographicProjection ? 1.0 : 0.0; _clustersActive = deferred.clustered; _aimSceneColour(deferred); if (deferred.clustered && rebuildClusters) { diff --git a/packages/flutter3d_core/lib/src/engine/scene/projection.dart b/packages/flutter3d_core/lib/src/engine/scene/projection.dart index f73baf8d2..5747df580 100644 --- a/packages/flutter3d_core/lib/src/engine/scene/projection.dart +++ b/packages/flutter3d_core/lib/src/engine/scene/projection.dart @@ -626,3 +626,21 @@ Vector3 viewAxisOf(Matrix4 viewProjection, [Vector3? out]) { if (result.length2 > 0.0) result.normalize(); return result; } + +/// Whether [viewProjection] projects orthographically — `P7`. +/// +/// Read out of the matrix for the reason [viewAxisOf] is: a probe face and a +/// mirrored view have no camera to ask. An orthographic projection leaves w +/// alone, so the matrix's bottom row is the view's own, nought nought nought +/// and a number; a perspective one puts the depth there. What +/// [toDepthRange] and [toFramebufferOrigin] do touches the depth and the +/// height rows and leaves that one. +bool isOrthographic(Matrix4 viewProjection) { + final m = viewProjection.storage; + final w = m[15].abs(); + const tiny = 1e-9; + return w > tiny && + m[3].abs() <= tiny * w && + m[7].abs() <= tiny * w && + m[11].abs() <= tiny * w; +} diff --git a/packages/flutter3d_core/lib/src/engine/scene/scene.dart b/packages/flutter3d_core/lib/src/engine/scene/scene.dart index 471a6dcfd..44b1bc257 100644 --- a/packages/flutter3d_core/lib/src/engine/scene/scene.dart +++ b/packages/flutter3d_core/lib/src/engine/scene/scene.dart @@ -77,6 +77,7 @@ final class Scene { /// frame, and the order they were attached in breaks ties between equal /// [DecalNode.order]s. final List _decals = []; + /// Planar reflectors, in attachment order — `P4`. A registry for the /// probes' reason: the renderer draws each of them a picture every frame. final List _reflectors = []; diff --git a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart index 519537ba7..768f62c0b 100644 --- a/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart +++ b/packages/flutter3d_core/test/formats/ktx2_supercompression_test.dart @@ -157,7 +157,7 @@ void main() { }); test('an uncompressed file is untouched by any of this', () { - // The path eighty-seven goldens and every existing asset take. A level with + // The path eighty-eight goldens and every existing asset take. A level with // no supercompression is handed back as a view on the file's own bytes, // with nothing copied and nothing decoded. final file = buildKtx2( diff --git a/packages/flutter3d_cpu/CHANGELOG.md b/packages/flutter3d_cpu/CHANGELOG.md index f99798321..c2348473f 100644 --- a/packages/flutter3d_cpu/CHANGELOG.md +++ b/packages/flutter3d_cpu/CHANGELOG.md @@ -1,5 +1,8 @@ ## Unreleased +- **The orthographic camera in software**: `towardsEye`, `eyeDistance` and + the light shafts' start follow the GLSL. + - **Debug views in software.** `writeDebugView` mirrors `WriteDebugView` for every lit model and the impostor, and the composite passes the debug side of the split through its encode alone, as the GLSL does. diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_color.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_color.dart index 6c4c88690..5c047c7a4 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_color.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_color.dart @@ -68,11 +68,20 @@ Vector3 decodeOctahedral(double ex, double ey) { return n..normalize(); } +/// `Orthographic` — `P7`: whether `FogInfo.projection.x` says the camera +/// projects orthographically. False where the block is not bound, which is +/// what the GLSL's `F3D_NO_FOG` stages are. +bool orthographic(ShaderBindings b) => + b.vec4('FogInfo', 'projection', Vector4.zero()).x > 0.5; + /// `EyeDistance`: how far this fragment is from the camera, in world metres. /// /// What the fog fades by. A distance, because fog is a property of the air -/// between two points and does not care which way the camera faces. +/// between two points and does not care which way the camera faces — through +/// a perspective lens. Through an orthographic one, the depth from the eye's +/// plane, as the GLSL has it. double eyeDistance(Float32List v, ShaderBindings b) { + if (orthographic(b)) return math.max(viewDepth(v, b), 0.0); final eye = b.vec4('FogInfo', 'eye', Vector4.zero()); final dx = v[kVWorld] - eye.x; final dy = v[kVWorld + 1] - eye.y; @@ -80,6 +89,22 @@ double eyeDistance(Float32List v, ShaderBindings b) { return math.sqrt(dx * dx + dy * dy + dz * dz); } +/// `TowardsEye` — `P7`: back along the ray that reached this fragment, to +/// the eye through a perspective lens and against the view axis through an +/// orthographic one. +Vector3 towardsEye(Float32List v, ShaderBindings b) { + if (orthographic(b)) { + final forward = b.vec4('FogInfo', 'forward', Vector4.zero()); + return Vector3(-forward.x, -forward.y, -forward.z); + } + final eye = b.vec4('FogInfo', 'eye', Vector4.zero()); + return Vector3( + eye.x - v[kVWorld], + eye.y - v[kVWorld + 1], + eye.z - v[kVWorld + 2], + )..normalize(); +} + /// `ViewDepth`: how far this fragment is along the view axis, in world metres. /// /// What the surface buffer's alpha holds. A depth rather than a distance, and diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_planar.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_planar.dart index 03d8fc7de..0dcddfdc1 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_planar.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_planar.dart @@ -44,12 +44,7 @@ final class PlanarReflectionShader implements CpuFragmentShader { final normal = Vector3(v[kVNormal], v[kVNormal + 1], v[kVNormal + 2]) ..normalize(); if (!c.frontFacing) normal.negate(); - final eye = b.vec4('FogInfo', 'eye', Vector4.zero()); - final toEye = Vector3( - eye.x - v[kVWorld], - eye.y - v[kVWorld + 1], - eye.z - v[kVWorld + 2], - )..normalize(); + final toEye = towardsEye(v, b); final cosine = normal.dot(toEye).clamp(0.0, 1.0); final f0 = params.x; final grazing = 1.0 - cosine; diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart index 9dafbc251..1197a5abc 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_post.dart @@ -180,7 +180,7 @@ final class CompositeShader implements CpuFragmentShader { // Skipped at exactly one, which is what both settings off comes to: a // multiply by one is exact, so this is a shortcut rather than a difference, - // and it keeps the frames eighty-seven goldens hold untouched by arithmetic + // and it keeps the frames eighty-eight goldens hold untouched by arithmetic // they never used to go through. if (shade != 1.0) colour.scale(shade); diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_reflections.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_reflections.dart index 7dedc3679..e31f9af42 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_reflections.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_reflections.dart @@ -258,13 +258,16 @@ final class LightShaftsShader implements CpuFragmentShader { final ndcY = 1.0 - v[1] * 2.0; final nearH = inverse.transformed(Vector4(ndcX, ndcY, 0.0, 1.0)); final farH = inverse.transformed(Vector4(ndcX, ndcY, 1.0, 1.0)); - final origin = Vector3(nearH.x, nearH.y, nearH.z)..scale(1.0 / nearH.w); + final nearPoint = Vector3(nearH.x, nearH.y, nearH.z)..scale(1.0 / nearH.w); final farPoint = Vector3(farH.x, farH.y, farH.z)..scale(1.0 / farH.w); - final along = (farPoint - origin)..normalize(); + final along = (farPoint - nearPoint)..normalize(); final axis = Vector3(forward.x, forward.y, forward.z); - final surfaceDepth = surfaceTexture.sample(v[0], v[1]).w; final cosine = math.max(along.dot(axis), 1e-4); + // From the eye's plane, not the near plane — `P7`, as the GLSL has it. + final eye = Vector3(camera.x, camera.y, camera.z); + final origin = nearPoint - along * ((nearPoint - eye).dot(axis) / cosine); + final surfaceDepth = surfaceTexture.sample(v[0], v[1]).w; final toSurface = surfaceDepth > 0.0 ? surfaceDepth / cosine : 1e9; final distance = math.min(camera.w, toSurface); if (distance <= 0.0) return scene; @@ -314,7 +317,6 @@ final class LightShaftsShader implements CpuFragmentShader { final sigma = math.max(scatter.w, 0.0); final stepTransmittance = math.exp(-sigma * stride); var transmittance = math.exp(-sigma * offset); - final eye = Vector3(camera.x, camera.y, camera.z); var inscatter = 0.0; for (var i = 0; i < steps && i < 64; i++) { final travelled = offset + i * stride; diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_surface.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_surface.dart index 6d3dd78a3..b6e162d48 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_surface.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_surface.dart @@ -269,7 +269,12 @@ Surface? readSurface( final material = bindings.vec4('FragInfo', 'material', Vector4.zero()); final camera = bindings.vec4('FragInfo', 'camera_position', Vector4.zero()); final world = Vector3(v[kVWorld], v[kVWorld + 1], v[kVWorld + 2]); - final view = Vector3(camera.x, camera.y, camera.z) - world; + // `P7`: against the view axis through an orthographic lens — `TowardsEye`. + // The stages without the fog block keep the eye's point, as the GLSL's + // `F3D_NO_FOG` ones do, and read as perspective here. + final view = orthographic(bindings) + ? towardsEye(v, bindings) + : Vector3(camera.x, camera.y, camera.z) - world; final viewLength = view.length; if (viewLength > 1e-6) view.scale(1.0 / viewLength); diff --git a/packages/flutter3d_cpu/lib/src/cpu_texture.dart b/packages/flutter3d_cpu/lib/src/cpu_texture.dart index 596a7ba55..7ff031655 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_texture.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_texture.dart @@ -300,7 +300,7 @@ final class BoundTexture { // from the short one. // // **A sampler that did not ask is untouched**, which is what makes this - // safe to add to a backend eighty-seven golden scenes are recorded on: with + // safe to add to a backend eighty-eight golden scenes are recorded on: with // `anisotropy` at one — the default everywhere in this engine — the // arithmetic below is not reached and the bytes are the ones that were // recorded. `anisotropic-floor` is the one scene that asks. diff --git a/packages/flutter3d_cpu/lib/src/portable_log2.dart b/packages/flutter3d_cpu/lib/src/portable_log2.dart index 9cad43934..796801490 100644 --- a/packages/flutter3d_cpu/lib/src/portable_log2.dart +++ b/packages/flutter3d_cpu/lib/src/portable_log2.dart @@ -10,7 +10,7 @@ /// twenty-five five. `anisotropic-floor` is the scene built to sit exactly /// there: 2059 of its 172800 pixels differed between a reference recorded on /// macOS and the same code run on a Linux runner, and nothing else in -/// eighty-seven scenes differed at all. +/// eighty-eight scenes differed at all. /// /// **The same rule the simulation already keeps.** `flutter3d_sim`'s /// `Portable` exists for this, and a step in that package is forbidden from diff --git a/packages/flutter3d_cpu/test/cross_backend_test.dart b/packages/flutter3d_cpu/test/cross_backend_test.dart index d1112c396..9ddd1836d 100644 --- a/packages/flutter3d_cpu/test/cross_backend_test.dart +++ b/packages/flutter3d_cpu/test/cross_backend_test.dart @@ -54,6 +54,7 @@ import 'package:test/test.dart'; /// multisampling on silhouettes, and an additive quad's edge deposits too /// little to cross a channel threshold of eight. const Map _budgets = { + 'orthographic-metal': 0.01, 'debug-view-split': 0.01, // 0.025% measured, down from 0.633%. Screen-space reflections are a march, // and what is left is the two rasterisers disagreeing about where a ray ends diff --git a/packages/flutter3d_cpu/test/goldens/orthographic-metal.png b/packages/flutter3d_cpu/test/goldens/orthographic-metal.png new file mode 100644 index 0000000000000000000000000000000000000000..6185c32e89efc9ee8d750b9e01c4b5963901d68b GIT binary patch literal 47623 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 Z{~z(wh{^6wWorNc002ovPDHLkV1lqBt%Cpn literal 0 HcmV?d00001 diff --git a/packages/flutter3d_cpu/test/orthographic_test.dart b/packages/flutter3d_cpu/test/orthographic_test.dart new file mode 100644 index 000000000..74d564973 --- /dev/null +++ b/packages/flutter3d_cpu/test/orthographic_test.dart @@ -0,0 +1,179 @@ +/// An orthographic camera through the effects that used to assume the rays +/// meet at the eye — `P7`. +/// +/// dart test test/orthographic_test.dart +/// +/// Through an orthographic lens the rays are parallel, and the eye's position +/// along the axis is only where the camera was put: moving it changes nothing +/// in the picture. Each test here holds one thing that read that position as a +/// point the light travels to — a highlight, the fog, the sky — to the +/// parallel rays instead. +library; + +import 'dart:typed_data'; + +import 'package:flutter3d_core/flutter3d_core.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:test/test.dart'; +import 'package:vector_math/vector_math.dart'; + +const int _width = 64; +const int _height = 48; + +/// A wall facing the camera and filling the frame, of [material], seen from +/// [distance] metres through [projection], with one light from the front +/// and above. +Float32List _render( + Projection projection, { + double distance = 4.0, + RenderSettings settings = const RenderSettings( + bloom: BloomSettings(enabled: false), + look: LookSettings(dither: 0.0), + ), + Material? material, + bool wall = true, +}) { + final device = CpuDevice( + width: _width, + height: _height, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final scene = Scene(); + if (wall) { + scene.add( + MeshNode( + DeviceMesh.upload(device, CuboidShape().build()), + material ?? + Material( + baseColor: Vector4(0.5, 0.5, 0.5, 1.0), + roughness: 0.2, + metallic: 1.0, + ), + ) + ..setPosition(0.0, 0.0, -distance) + ..setScale(40.0, 40.0, 0.1), + ); + } + scene.add( + LightNode(intensity: 3.0)..lookAt(Vector3(0.0, -0.3, -1.0).normalized()), + ); + final camera = CameraNode(projection: projection); + scene.add(camera); + final result = Renderer.create(device: device).render( + width: _width, + height: _height, + scene: scene, + views: [ + RenderView(camera: camera, clearColor: Vector4(0.0, 0.0, 0.0, 1.0)), + ], + settings: settings, + ); + return Float32List.fromList(device.readHdrPixels(result.frame)); +} + +Vector3 _at(Float32List frame, int x, int y) { + final i = (y * _width + x) * 4; + return Vector3(frame[i], frame[i + 1], frame[i + 2]); +} + +/// How far apart the brightest and darkest of five points across the frame +/// are, in the red channel. +double _spread(Float32List frame) { + final reds = [ + for (final (x, y) in <(int, int)>[ + (_width ~/ 2, _height ~/ 2), + (4, 4), + (_width - 5, 4), + (4, _height - 5), + (_width - 5, _height - 5), + ]) + _at(frame, x, y).x, + ]; + return reds.reduce((a, b) => a > b ? a : b) - + reds.reduce((a, b) => a < b ? a : b); +} + +const OrthographicProjection _ortho = OrthographicProjection(height: 6.0); +const PerspectiveProjection _perspective = PerspectiveProjection( + fovYRadians: 1.2, +); + +void main() { + test('an orthographic matrix is told from a perspective one', () { + // Mutation: test the bottom row's last entry alone, and a perspective + // matrix that happens to keep one there reads as orthographic. + final view = Matrix4.identity()..setTranslationRaw(0.0, -1.0, -5.0); + for (final range in DepthRange.values) { + expect( + isOrthographic( + toDepthRange(_ortho.toMatrix(1.5), range) * view as Matrix4, + ), + isTrue, + ); + expect( + isOrthographic( + toDepthRange(_perspective.toMatrix(1.5), range) * view as Matrix4, + ), + isFalse, + ); + } + }); + + test('a highlight does not slide across a flat wall', () { + // Mutation: read `FragInfo.camera_position` for the view direction in + // `readSurface`, and the metal wall brightens towards wherever the eye's + // point projects — the very slide a pan would show. + expect(_spread(_render(_ortho)), lessThan(0.02)); + // The same wall through a perspective lens does vary: the test can see a + // slide when there is one. + expect(_spread(_render(_perspective)), greaterThan(0.05)); + }); + + test('where the camera stands along its axis changes nothing lit', () { + // Mutation: as above — a view direction from the eye's point moves with + // the eye, and a highlight with it. + final near = _render(_ortho, distance: 3.0); + final far = _render(_ortho, distance: 12.0); + for (final (x, y) in <(int, int)>[(8, 8), (_width - 9, _height - 9)]) { + final a = _at(near, x, y); + final b = _at(far, x, y); + expect((a - b).length, lessThan(0.01), reason: 'at ($x, $y)'); + } + }); + + test('fog lies flat on a wall the camera faces', () { + // Mutation: keep the radial `EyeDistance` through an orthographic lens, + // and the fog rings round the middle of the frame. + final fogged = _render( + _ortho, + distance: 20.0, + material: Material( + lighting: LightingModel.unlit, + baseColor: Vector4(1.0, 1.0, 1.0, 1.0), + ), + settings: RenderSettings( + bloom: const BloomSettings(enabled: false), + look: const LookSettings(dither: 0.0), + fog: FogSettings(color: Vector3.zero(), density: 0.05), + ), + ); + expect(_spread(fogged), lessThan(0.01)); + }); + + test('the sky is a gradient, not one colour', () { + // Mutation: hand the sky the orthographic matrix's own corner rays, which + // are all the view axis, and the frame is a single colour. + final sky = _render( + _ortho, + wall: false, + settings: const RenderSettings( + bloom: BloomSettings(enabled: false), + look: LookSettings(dither: 0.0), + sky: SkySettings(enabled: true), + ), + ); + final top = _at(sky, _width ~/ 2, 1); + final bottom = _at(sky, _width ~/ 2, _height - 2); + expect((top - bottom).length, greaterThan(0.05)); + }); +} diff --git a/packages/flutter3d_impeller/test/gpu_formats_test.dart b/packages/flutter3d_impeller/test/gpu_formats_test.dart index 01b0a5d4d..857d5e080 100644 --- a/packages/flutter3d_impeller/test/gpu_formats_test.dart +++ b/packages/flutter3d_impeller/test/gpu_formats_test.dart @@ -2,7 +2,7 @@ /// /// The hazard this exists for: a wrong mapping compiles, runs, and renders /// wrong only for the values a scene happens to use. The goldens cover the -/// values the eighty-seven scenes exercise and nothing else — `BlendFactor` has +/// values the eighty-eight scenes exercise and nothing else — `BlendFactor` has /// fifteen members and the engine draws with two of them — so a mistake in the /// other thirteen would be found by a user rather than by us. /// diff --git a/packages/flutter3d_shaders/CHANGELOG.md b/packages/flutter3d_shaders/CHANGELOG.md index 7d42330f2..a23b733f5 100644 --- a/packages/flutter3d_shaders/CHANGELOG.md +++ b/packages/flutter3d_shaders/CHANGELOG.md @@ -1,5 +1,10 @@ ## Unreleased +- **`FogInfo.projection`, `Orthographic`, `TowardsEye`** — `P7`: the lit + models, the planar reflection's Fresnel and `EyeDistance` ask whether the + camera is orthographic. `light_shafts.frag` starts its march on the eye's + plane. + - **`FragInfo.debug_view` and `WriteDebugView`** in `surface.glsl` — `P6`: every lit model asks it before writing its light. NaN is found by comparison, because `impellerc`'s GLSL ES output has no bit casts. diff --git a/packages/flutter3d_shaders/lib/typed_blocks.dart b/packages/flutter3d_shaders/lib/typed_blocks.dart index e5d3ee2f5..e0ca08994 100644 --- a/packages/flutter3d_shaders/lib/typed_blocks.dart +++ b/packages/flutter3d_shaders/lib/typed_blocks.dart @@ -391,11 +391,15 @@ final class FogInfoBlock extends UniformBlock { /// `forward`: Vector4, at byte 32. final Float32List forward = Float32List(4); + /// `projection`: Vector4, at byte 48. + final Float32List projection = Float32List(4); + @override late final Map members = { 'fog': fog, 'eye': eye, 'forward': forward, + 'projection': projection, }; } diff --git a/packages/flutter3d_shaders/lib/uniform_blocks.dart b/packages/flutter3d_shaders/lib/uniform_blocks.dart index 8a6ac8c77..da6d48520 100644 --- a/packages/flutter3d_shaders/lib/uniform_blocks.dart +++ b/packages/flutter3d_shaders/lib/uniform_blocks.dart @@ -23,6 +23,7 @@ uniformBlocks = >>{ 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + 'projection': (offset: 48, byteLength: 16, elements: 1, type: 'Vector4'), }, 'FragInfo': { 'light_position': ( @@ -339,6 +340,7 @@ uniformBlocks = >>{ 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + 'projection': (offset: 48, byteLength: 16, elements: 1, type: 'Vector4'), }, 'FragInfo': { 'light_position': ( @@ -494,6 +496,7 @@ uniformBlocks = >>{ 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + 'projection': (offset: 48, byteLength: 16, elements: 1, type: 'Vector4'), }, 'FragInfo': { 'light_position': ( @@ -833,6 +836,7 @@ uniformBlocks = >>{ 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + 'projection': (offset: 48, byteLength: 16, elements: 1, type: 'Vector4'), }, }, 'ObjectId': >{ @@ -1039,6 +1043,7 @@ uniformBlocks = >>{ 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + 'projection': (offset: 48, byteLength: 16, elements: 1, type: 'Vector4'), }, 'FragInfo': { 'light_position': ( @@ -1182,6 +1187,7 @@ uniformBlocks = >>{ 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + 'projection': (offset: 48, byteLength: 16, elements: 1, type: 'Vector4'), }, 'FragInfo': { 'light_position': ( @@ -1369,6 +1375,7 @@ uniformBlocks = >>{ 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + 'projection': (offset: 48, byteLength: 16, elements: 1, type: 'Vector4'), }, 'PlanarReflectionInfo': { 'view': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), @@ -1557,6 +1564,7 @@ uniformBlocks = >>{ 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + 'projection': (offset: 48, byteLength: 16, elements: 1, type: 'Vector4'), }, 'FragInfo': { 'light_position': ( @@ -1700,6 +1708,7 @@ uniformBlocks = >>{ 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + 'projection': (offset: 48, byteLength: 16, elements: 1, type: 'Vector4'), }, 'FragInfo': { 'light_position': ( @@ -2036,6 +2045,7 @@ uniformBlocks = >>{ 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), 'eye': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), 'forward': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + 'projection': (offset: 48, byteLength: 16, elements: 1, type: 'Vector4'), }, 'FragInfo': { 'light_position': ( diff --git a/packages/flutter3d_shaders/shaders/lib/color.glsl b/packages/flutter3d_shaders/shaders/lib/color.glsl index d569388a8..6d6de6fb2 100644 --- a/packages/flutter3d_shaders/shaders/lib/color.glsl +++ b/packages/flutter3d_shaders/shaders/lib/color.glsl @@ -133,15 +133,24 @@ uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -156,6 +165,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no diff --git a/packages/flutter3d_shaders/shaders/lib/surface.glsl b/packages/flutter3d_shaders/shaders/lib/surface.glsl index 1ed3833c2..63916a6b3 100644 --- a/packages/flutter3d_shaders/shaders/lib/surface.glsl +++ b/packages/flutter3d_shaders/shaders/lib/surface.glsl @@ -291,7 +291,15 @@ Surface ReadSurface() { // double-sided material ever draws a back face, since everything else has // them culled. if (!gl_FrontFacing) s.n = -s.n; +#ifdef F3D_NO_FOG + // The stages without the fog block — shadows and the id pass — light + // nothing, and keep the eye's point. s.v = normalize(frag_info.camera_position.xyz - v_world_position); +#else + // `P7`: against the view axis through an orthographic lens, where the + // eye's point is only where the camera was put. + s.v = TowardsEye(); +#endif // Clamped away from zero: a grazing view direction otherwise divides by zero // in the specular visibility term. s.n_dot_v = max(dot(s.n, s.v), 1e-4); diff --git a/packages/flutter3d_shaders/shaders/lighting/planar_reflection.frag b/packages/flutter3d_shaders/shaders/lighting/planar_reflection.frag index 849298381..ff3f2ff89 100644 --- a/packages/flutter3d_shaders/shaders/lighting/planar_reflection.frag +++ b/packages/flutter3d_shaders/shaders/lighting/planar_reflection.frag @@ -70,7 +70,7 @@ void main() { // and a mirror's F0 of one makes the term one everywhere. vec3 n = normalize(v_normal); if (!gl_FrontFacing) n = -n; - vec3 v = normalize(fog_info.eye.xyz - v_world_position); + vec3 v = TowardsEye(); float cosine = clamp(dot(n, v), 0.0, 1.0); float f0 = planar_info.params.x; float grazing = 1.0 - cosine; diff --git a/packages/flutter3d_shaders/shaders/post/light_shafts.frag b/packages/flutter3d_shaders/shaders/post/light_shafts.frag index 99cf2e290..a8112f0d5 100644 --- a/packages/flutter3d_shaders/shaders/post/light_shafts.frag +++ b/packages/flutter3d_shaders/shaders/post/light_shafts.frag @@ -141,15 +141,26 @@ void main() { vec2 xy = vec2(v_uv.x * 2.0 - 1.0, 1.0 - v_uv.y * 2.0); vec4 nearH = shaft_info.inverse_view_projection * vec4(xy, 0.0, 1.0); vec4 farH = shaft_info.inverse_view_projection * vec4(xy, 1.0, 1.0); - vec3 origin = nearH.xyz / nearH.w; - vec3 along = normalize(farH.xyz / farH.w - origin); + vec3 nearPoint = nearH.xyz / nearH.w; + vec3 along = normalize(farH.xyz / farH.w - nearPoint); + float cosine = max(dot(along, shaft_info.forward.xyz), 1e-4); + + // **From the eye's plane, not the near plane** — `P7`, as + // `volumetric_fog.frag` already starts. The surface buffer's depth is + // measured from the eye, so a march from the near plane ran the near + // distance past the surface. Through a perspective lens that is a tenth of + // a metre; through an orthographic one the near plane can stand anywhere, + // behind the eye included, and the march ended that far off the wall. + vec3 origin = nearPoint - + along * (dot(nearPoint - shaft_info.camera.xyz, + shaft_info.forward.xyz) / + cosine); // How far there is air. The surface buffer holds depth along the view axis // in metres, so the distance along *this* ray is that over the cosine // between the two — a ray at the corner of the frame travels further than // the axis does to reach the same plane. float surfaceDepth = texture(surface_texture, v_uv).a; - float cosine = max(dot(along, shaft_info.forward.xyz), 1e-4); float toSurface = surfaceDepth > 0.0 ? surfaceDepth / cosine : 1e9; float distance = min(shaft_info.camera.w, toSurface); if (distance <= 0.0) { diff --git a/packages/flutter3d_webgl/CHANGELOG.md b/packages/flutter3d_webgl/CHANGELOG.md index ee4ed099d..54b061f66 100644 --- a/packages/flutter3d_webgl/CHANGELOG.md +++ b/packages/flutter3d_webgl/CHANGELOG.md @@ -1,5 +1,8 @@ ## Unreleased +- **The generated tables carry the orthographic camera's paths**, and + `orthographic-metal` is in the browser reference set. + - **The generated tables carry the debug views**, and `debug-view-split` is in the browser reference set. diff --git a/packages/flutter3d_webgl/lib/engine_shaders.dart b/packages/flutter3d_webgl/lib/engine_shaders.dart index 9127317d5..d73cc65d1 100644 --- a/packages/flutter3d_webgl/lib/engine_shaders.dart +++ b/packages/flutter3d_webgl/lib/engine_shaders.dart @@ -2741,15 +2741,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -2764,6 +2773,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -3414,7 +3444,15 @@ Surface ReadSurface() { // double-sided material ever draws a back face, since everything else has // them culled. if (!gl_FrontFacing) s.n = -s.n; +#ifdef F3D_NO_FOG + // The stages without the fog block — shadows and the id pass — light + // nothing, and keep the eye's point. s.v = normalize(frag_info.camera_position.xyz - v_world_position); +#else + // `P7`: against the view axis through an orthographic lens, where the + // eye's point is only where the camera was put. + s.v = TowardsEye(); +#endif // Clamped away from zero: a grazing view direction otherwise divides by zero // in the specular visibility term. s.n_dot_v = max(dot(s.n, s.v), 1e-4); @@ -4611,15 +4649,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -4634,6 +4681,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -5284,7 +5352,15 @@ Surface ReadSurface() { // double-sided material ever draws a back face, since everything else has // them culled. if (!gl_FrontFacing) s.n = -s.n; +#ifdef F3D_NO_FOG + // The stages without the fog block — shadows and the id pass — light + // nothing, and keep the eye's point. s.v = normalize(frag_info.camera_position.xyz - v_world_position); +#else + // `P7`: against the view axis through an orthographic lens, where the + // eye's point is only where the camera was put. + s.v = TowardsEye(); +#endif // Clamped away from zero: a grazing view direction otherwise divides by zero // in the specular visibility term. s.n_dot_v = max(dot(s.n, s.v), 1e-4); @@ -6451,15 +6527,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -6474,6 +6559,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -6737,7 +6843,7 @@ void main() { // and a mirror's F0 of one makes the term one everywhere. vec3 n = normalize(v_normal); if (!gl_FrontFacing) n = -n; - vec3 v = normalize(fog_info.eye.xyz - v_world_position); + vec3 v = TowardsEye(); float cosine = clamp(dot(n, v), 0.0, 1.0); float f0 = planar_info.params.x; float grazing = 1.0 - cosine; @@ -6937,15 +7043,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -6960,6 +7075,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -7610,7 +7746,15 @@ Surface ReadSurface() { // double-sided material ever draws a back face, since everything else has // them culled. if (!gl_FrontFacing) s.n = -s.n; +#ifdef F3D_NO_FOG + // The stages without the fog block — shadows and the id pass — light + // nothing, and keep the eye's point. s.v = normalize(frag_info.camera_position.xyz - v_world_position); +#else + // `P7`: against the view axis through an orthographic lens, where the + // eye's point is only where the camera was put. + s.v = TowardsEye(); +#endif // Clamped away from zero: a grazing view direction otherwise divides by zero // in the specular visibility term. s.n_dot_v = max(dot(s.n, s.v), 1e-4); @@ -9501,15 +9645,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -9524,6 +9677,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -10174,7 +10348,15 @@ Surface ReadSurface() { // double-sided material ever draws a back face, since everything else has // them culled. if (!gl_FrontFacing) s.n = -s.n; +#ifdef F3D_NO_FOG + // The stages without the fog block — shadows and the id pass — light + // nothing, and keep the eye's point. s.v = normalize(frag_info.camera_position.xyz - v_world_position); +#else + // `P7`: against the view axis through an orthographic lens, where the + // eye's point is only where the camera was put. + s.v = TowardsEye(); +#endif // Clamped away from zero: a grazing view direction otherwise divides by zero // in the specular visibility term. s.n_dot_v = max(dot(s.n, s.v), 1e-4); @@ -12100,15 +12282,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -12123,6 +12314,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -12773,7 +12985,15 @@ Surface ReadSurface() { // double-sided material ever draws a back face, since everything else has // them culled. if (!gl_FrontFacing) s.n = -s.n; +#ifdef F3D_NO_FOG + // The stages without the fog block — shadows and the id pass — light + // nothing, and keep the eye's point. s.v = normalize(frag_info.camera_position.xyz - v_world_position); +#else + // `P7`: against the view axis through an orthographic lens, where the + // eye's point is only where the camera was put. + s.v = TowardsEye(); +#endif // Clamped away from zero: a grazing view direction otherwise divides by zero // in the specular visibility term. s.n_dot_v = max(dot(s.n, s.v), 1e-4); @@ -15528,15 +15748,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -15551,6 +15780,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -16201,7 +16451,15 @@ Surface ReadSurface() { // double-sided material ever draws a back face, since everything else has // them culled. if (!gl_FrontFacing) s.n = -s.n; +#ifdef F3D_NO_FOG + // The stages without the fog block — shadows and the id pass — light + // nothing, and keep the eye's point. s.v = normalize(frag_info.camera_position.xyz - v_world_position); +#else + // `P7`: against the view axis through an orthographic lens, where the + // eye's point is only where the camera was put. + s.v = TowardsEye(); +#endif // Clamped away from zero: a grazing view direction otherwise divides by zero // in the specular visibility term. s.n_dot_v = max(dot(s.n, s.v), 1e-4); @@ -18924,15 +19182,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -18947,6 +19214,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -19597,7 +19885,15 @@ Surface ReadSurface() { // double-sided material ever draws a back face, since everything else has // them culled. if (!gl_FrontFacing) s.n = -s.n; +#ifdef F3D_NO_FOG + // The stages without the fog block — shadows and the id pass — light + // nothing, and keep the eye's point. s.v = normalize(frag_info.camera_position.xyz - v_world_position); +#else + // `P7`: against the view axis through an orthographic lens, where the + // eye's point is only where the camera was put. + s.v = TowardsEye(); +#endif // Clamped away from zero: a grazing view direction otherwise divides by zero // in the specular visibility term. s.n_dot_v = max(dot(s.n, s.v), 1e-4); @@ -21471,15 +21767,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -21494,6 +21799,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -23815,15 +24141,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -23838,6 +24173,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -28725,15 +29081,26 @@ void main() { vec2 xy = vec2(v_uv.x * 2.0 - 1.0, 1.0 - v_uv.y * 2.0); vec4 nearH = shaft_info.inverse_view_projection * vec4(xy, 0.0, 1.0); vec4 farH = shaft_info.inverse_view_projection * vec4(xy, 1.0, 1.0); - vec3 origin = nearH.xyz / nearH.w; - vec3 along = normalize(farH.xyz / farH.w - origin); + vec3 nearPoint = nearH.xyz / nearH.w; + vec3 along = normalize(farH.xyz / farH.w - nearPoint); + float cosine = max(dot(along, shaft_info.forward.xyz), 1e-4); + + // **From the eye's plane, not the near plane** — `P7`, as + // `volumetric_fog.frag` already starts. The surface buffer's depth is + // measured from the eye, so a march from the near plane ran the near + // distance past the surface. Through a perspective lens that is a tenth of + // a metre; through an orthographic one the near plane can stand anywhere, + // behind the eye included, and the march ended that far off the wall. + vec3 origin = nearPoint - + along * (dot(nearPoint - shaft_info.camera.xyz, + shaft_info.forward.xyz) / + cosine); // How far there is air. The surface buffer holds depth along the view axis // in metres, so the distance along *this* ray is that over the cosine // between the two — a ray at the corner of the frame travels further than // the axis does to reach the same plane. float surfaceDepth = texture(surface_texture, v_uv).a; - float cosine = max(dot(along, shaft_info.forward.xyz), 1e-4); float toSurface = surfaceDepth > 0.0 ? surfaceDepth / cosine : 1e9; float distance = min(shaft_info.camera.w, toSurface); if (distance <= 0.0) { @@ -30973,15 +31340,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -30996,6 +31372,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -31373,15 +31770,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -31396,6 +31802,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -31768,15 +32195,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -31791,6 +32227,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -32952,15 +33409,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -32975,6 +33441,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -33865,15 +34352,24 @@ layout(std140) uniform FogInfo { /// question [eye] does — where the camera is and which way it faces — and /// this is the block `color.glsl` can see. vec4 forward; + + /// x: one when the camera's projection is orthographic, nought when it is + /// perspective — `P7`, see [Orthographic]. y, z, w unused. Appended, so + /// every offset above stays where four backends agree on it. + vec4 projection; } fog_info; -/// How far this fragment is from the eye, in world metres. +/// Whether the camera is orthographic — `P7`. /// -/// What the fog fades by. Distance rather than depth, because fog is a -/// property of the air between two points and does not care which way the -/// camera happens to face. -float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } +/// **What every eye-relative quantity has to ask first.** Through a +/// perspective lens the rays meet at the eye, so the way to the eye and the +/// distance to it are the fragment's own; through an orthographic one the +/// rays are parallel and the eye is only where the camera was put along its +/// axis, which moves nothing in the picture. Reading the eye's position as if +/// it were a point the light travels to slides highlights across the frame +/// as the camera pans and lays fog in rings round a point nobody sees. +bool Orthographic() { return fog_info.projection.x > 0.5; } /// How far this fragment is *along the view axis*, in world metres. /// @@ -33888,6 +34384,27 @@ float ViewDepth() { return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); } +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face — through a perspective lens. Through an +/// orthographic one every ray starts on the eye's plane, so the air a ray +/// crosses is its depth from that plane, never less than nought. +float EyeDistance() { + return Orthographic() ? max(ViewDepth(), 0.0) + : distance(v_world_position, fog_info.eye.xyz); +} + +/// The unit direction from this fragment back along the ray that reached +/// it: to the eye through a perspective lens, against the view axis through +/// an orthographic one — `P7`. What a highlight, a Fresnel term and a +/// reflection are measured from. +vec3 TowardsEye() { + return Orthographic() ? -fog_info.forward.xyz + : normalize(fog_info.eye.xyz - v_world_position); +} + #else // F3D_NO_FOG // The same two questions, answered without the block: a stage that declares no @@ -34538,7 +35055,15 @@ Surface ReadSurface() { // double-sided material ever draws a back face, since everything else has // them culled. if (!gl_FrontFacing) s.n = -s.n; +#ifdef F3D_NO_FOG + // The stages without the fog block — shadows and the id pass — light + // nothing, and keep the eye's point. s.v = normalize(frag_info.camera_position.xyz - v_world_position); +#else + // `P7`: against the view axis through an orthographic lens, where the + // eye's point is only where the camera was put. + s.v = TowardsEye(); +#endif // Clamped away from zero: a grazing view direction otherwise divides by zero // in the specular visibility term. s.n_dot_v = max(dot(s.n, s.v), 1e-4); diff --git a/packages/flutter3d_webgl/test/cross_backend_test.dart b/packages/flutter3d_webgl/test/cross_backend_test.dart index a4bf3b100..3f7746147 100644 --- a/packages/flutter3d_webgl/test/cross_backend_test.dart +++ b/packages/flutter3d_webgl/test/cross_backend_test.dart @@ -98,6 +98,10 @@ const int _channel = 8; /// pointed at and agreed with Impeller to the pixel for six sessions. /// const Map _budgets = { + // 0.394% measured, on the spheres' and the floor's edges, which WebGL2 + // does not multisample as Impeller does; the highlights, the fog and the + // sky agree. + 'orthographic-metal': 0.5, // 0.359% measured, on the silhouette: `shadow-teapot`'s edge between // WebGL2 and Impeller, and the normals' half shows it in brighter colours // than the lit half's. diff --git a/packages/flutter3d_webgl/test/goldens/orthographic-metal.png b/packages/flutter3d_webgl/test/goldens/orthographic-metal.png new file mode 100644 index 0000000000000000000000000000000000000000..3b380f9b63453fd65560801c92c0c02779636053 GIT binary patch literal 46948 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≥M^Xw6P*k%PV2H@^FPCT!h)j>$}Q)LH5qA15r+(3 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, eye: vec4, forward: vec4, + projection: vec4, } struct FragInfo { @@ -4225,9 +4226,9 @@ var g_albedo: vec3; var g_debug_surface: vec3; var g_debug_surface_on: bool; var g_premultiply: bool; -var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; +var v_world_position_1: vec3; var frag_albedo: vec4; var frag_surface: vec4; var v_normal_1: vec3; @@ -4383,10 +4384,25 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { return; } +fn Orthographic_u0028_() -> bool { + let _e89 = fog_info.projection[0u]; + return (_e89 > 0.5f); +} + fn EyeDistance_u0028_() -> f32 { - let _e87 = v_world_position_1; - let _e89 = fog_info.eye; - return distance(_e87, _e89.xyz); + var local_1: f32; + + let _e88 = Orthographic_u0028_(); + if _e88 { + let _e89 = ViewDepth_u0028_(); + local_1 = max(_e89, 0f); + } else { + let _e91 = v_world_position_1; + let _e93 = fog_info.eye; + local_1 = distance(_e91, _e93.xyz); + } + let _e96 = local_1; + return _e96; } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -4394,7 +4410,7 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var d: f32; var falloff: f32; var k: f32; - var local_1: f32; + var local_2: f32; let _e95 = fog_info.fog[3u]; density = _e95; @@ -4417,12 +4433,12 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { if (abs(_e113) > 0.001f) { let _e116 = k; let _e120 = k; - local_1 = ((1f - exp(-(_e116))) / _e120); + local_2 = ((1f - exp(-(_e116))) / _e120); } else { let _e122 = k; - local_1 = (1f - (0.5f * _e122)); + local_2 = (1f - (0.5f * _e122)); } - let _e125 = local_1; + let _e125 = local_2; let _e126 = density; density = (_e126 * _e125); } @@ -4477,17 +4493,17 @@ fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { } fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { - var phi_602_: bool; + var phi_634_: bool; let _e88 = (*c); let _e89 = (*c); let _e91 = any((_e88 != _e89)); - phi_602_ = _e91; + phi_634_ = _e91; if !(_e91) { let _e93 = (*c); - phi_602_ = any((abs(_e93) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + phi_634_ = any((abs(_e93) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); } - let _e98 = phi_602_; + let _e98 = phi_634_; return _e98; } @@ -4500,9 +4516,9 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var grey: f32; var param_10: vec3; var param_11: vec3; - var local_2: vec3; + var local_3: vec3; var weight_2: f32; - var local_3: f32; + var local_4: f32; var param_12: vec3; var param_13: f32; @@ -4571,12 +4587,12 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ param_11 = _e169; let _e170 = NonFinite_u0028_vf3_u003b((¶m_11)); if _e170 { - local_2 = vec3(1f, 0f, 1f); + local_3 = vec3(1f, 0f, 1f); } else { let _e171 = grey; - local_2 = vec3((_e171 * 0.5f)); + local_3 = vec3((_e171 * 0.5f)); } - let _e174 = local_2; + let _e174 = local_3; shown = _e174; } } @@ -4589,11 +4605,11 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e175 = g_premultiply; if _e175 { let _e177 = (*s).alpha; - local_3 = _e177; + local_4 = _e177; } else { - local_3 = 1f; + local_4 = 1f; } - let _e178 = local_3; + let _e178 = local_4; weight_2 = _e178; let _e179 = shown; param_12 = _e179; @@ -4609,6 +4625,22 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ return true; } +fn TowardsEye_u0028_() -> vec3 { + var local_5: vec3; + + let _e88 = Orthographic_u0028_(); + if _e88 { + let _e90 = fog_info.forward; + local_5 = -(_e90.xyz); + } else { + let _e94 = fog_info.eye; + let _e96 = v_world_position_1; + local_5 = normalize((_e94.xyz - _e96)); + } + let _e99 = local_5; + return _e99; +} + fn MaterialLodBias_u0028_() -> f32 { let _e89 = frag_info.target_origin[1u]; return _e89; @@ -4674,27 +4706,26 @@ fn ReadSurface_u0028_() -> Surface { let _e155 = s_1.n; s_1.n = -(_e155); } - let _e159 = frag_info.camera_position; - let _e161 = v_world_position_1; - s_1.v = normalize((_e159.xyz - _e161)); - let _e166 = s_1.n; - let _e168 = s_1.v; - s_1.n_dot_v = max(dot(_e166, _e168), 0.0001f); - let _e174 = frag_info.material[0u]; - s_1.metallic = clamp(_e174, 0f, 1f); - let _e179 = frag_info.material[1u]; - s_1.roughness = clamp(_e179, 0.02f, 1f); - let _e183 = frag_info.ambient_ground; - let _e186 = frag_info.ambient_sky; - let _e190 = s_1.n[1u]; - let _e197 = frag_info.material[2u]; - s_1.ambient = (mix(_e183.xyz, _e186.xyz, vec3(((_e190 * 0.5f) + 0.5f))) * _e197); - let _e202 = frag_info.frame_params[0u]; - s_1.exposure = max(_e202, 0f); + let _e158 = TowardsEye_u0028_(); + s_1.v = _e158; + let _e161 = s_1.n; + let _e163 = s_1.v; + s_1.n_dot_v = max(dot(_e161, _e163), 0.0001f); + let _e169 = frag_info.material[0u]; + s_1.metallic = clamp(_e169, 0f, 1f); + let _e174 = frag_info.material[1u]; + s_1.roughness = clamp(_e174, 0.02f, 1f); + let _e178 = frag_info.ambient_ground; + let _e181 = frag_info.ambient_sky; + let _e185 = s_1.n[1u]; + let _e192 = frag_info.material[2u]; + s_1.ambient = (mix(_e178.xyz, _e181.xyz, vec3(((_e185 * 0.5f) + 0.5f))) * _e192); + let _e197 = frag_info.frame_params[0u]; + s_1.exposure = max(_e197, 0f); s_1.occlusion = 1f; s_1.emissive = vec3(0f, 0f, 0f); - let _e207 = s_1; - return _e207; + let _e202 = s_1; + return _e202; } fn main_1() { @@ -4750,7 +4781,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), @@ -4759,6 +4790,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, eye: vec4, forward: vec4, + projection: vec4, } struct FragInfo { @@ -4929,9 +4966,9 @@ var g_albedo: vec3; var g_debug_surface: vec3; var g_debug_surface_on: bool; var g_premultiply: bool; -var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; +var v_world_position_1: vec3; var frag_color: vec4; @group(1) @binding(1) var frag_info: FragInfo; @@ -5023,10 +5060,25 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { return; } +fn Orthographic_u0028_() -> bool { + let _e70 = fog_info.projection[0u]; + return (_e70 > 0.5f); +} + fn EyeDistance_u0028_() -> f32 { - let _e68 = v_world_position_1; - let _e70 = fog_info.eye; - return distance(_e68, _e70.xyz); + var local_1: f32; + + let _e69 = Orthographic_u0028_(); + if _e69 { + let _e70 = ViewDepth_u0028_(); + local_1 = max(_e70, 0f); + } else { + let _e72 = v_world_position_1; + let _e74 = fog_info.eye; + local_1 = distance(_e72, _e74.xyz); + } + let _e77 = local_1; + return _e77; } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -5034,7 +5086,7 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var d: f32; var falloff: f32; var k: f32; - var local_1: f32; + var local_2: f32; let _e76 = fog_info.fog[3u]; density = _e76; @@ -5057,12 +5109,12 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { if (abs(_e94) > 0.001f) { let _e97 = k; let _e101 = k; - local_1 = ((1f - exp(-(_e97))) / _e101); + local_2 = ((1f - exp(-(_e97))) / _e101); } else { let _e103 = k; - local_1 = (1f - (0.5f * _e103)); + local_2 = (1f - (0.5f * _e103)); } - let _e106 = local_1; + let _e106 = local_2; let _e107 = density; density = (_e107 * _e106); } @@ -5109,6 +5161,22 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b(linearColor_1: ptr> return; } +fn TowardsEye_u0028_() -> vec3 { + var local_3: vec3; + + let _e69 = Orthographic_u0028_(); + if _e69 { + let _e71 = fog_info.forward; + local_3 = -(_e71.xyz); + } else { + let _e75 = fog_info.eye; + let _e77 = v_world_position_1; + local_3 = normalize((_e75.xyz - _e77)); + } + let _e80 = local_3; + return _e80; +} + fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { let _e69 = (*srgb); let _e72 = (*srgb); @@ -5181,27 +5249,26 @@ fn ReadSurface_u0028_() -> Surface { let _e136 = s.n; s.n = -(_e136); } - let _e140 = frag_info.camera_position; - let _e142 = v_world_position_1; - s.v = normalize((_e140.xyz - _e142)); - let _e147 = s.n; - let _e149 = s.v; - s.n_dot_v = max(dot(_e147, _e149), 0.0001f); - let _e155 = frag_info.material[0u]; - s.metallic = clamp(_e155, 0f, 1f); - let _e160 = frag_info.material[1u]; - s.roughness = clamp(_e160, 0.02f, 1f); - let _e164 = frag_info.ambient_ground; - let _e167 = frag_info.ambient_sky; - let _e171 = s.n[1u]; - let _e178 = frag_info.material[2u]; - s.ambient = (mix(_e164.xyz, _e167.xyz, vec3(((_e171 * 0.5f) + 0.5f))) * _e178); - let _e183 = frag_info.frame_params[0u]; - s.exposure = max(_e183, 0f); + let _e139 = TowardsEye_u0028_(); + s.v = _e139; + let _e142 = s.n; + let _e144 = s.v; + s.n_dot_v = max(dot(_e142, _e144), 0.0001f); + let _e150 = frag_info.material[0u]; + s.metallic = clamp(_e150, 0f, 1f); + let _e155 = frag_info.material[1u]; + s.roughness = clamp(_e155, 0.02f, 1f); + let _e159 = frag_info.ambient_ground; + let _e162 = frag_info.ambient_sky; + let _e166 = s.n[1u]; + let _e173 = frag_info.material[2u]; + s.ambient = (mix(_e159.xyz, _e162.xyz, vec3(((_e166 * 0.5f) + 0.5f))) * _e173); + let _e178 = frag_info.frame_params[0u]; + s.exposure = max(_e178, 0f); s.occlusion = 1f; s.emissive = vec3(0f, 0f, 0f); - let _e188 = s; - return _e188; + let _e183 = s; + return _e183; } fn main_1() { @@ -5243,7 +5310,7 @@ fn main(@location(6) v_world_position: vec3, @location(4) v_texcoord: vec2< name: 'FogInfo', group: 1, binding: 0, - sizeInBytes: 48, + sizeInBytes: 64, members: [ WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), @@ -5252,6 +5319,11 @@ fn main(@location(6) v_world_position: vec3, @location(4) v_texcoord: vec2< offsetInBytes: 32, sizeInBytes: 16, ), + WebGpuBlockMember( + name: 'projection', + offsetInBytes: 48, + sizeInBytes: 16, + ), ], ), WebGpuBlock( @@ -5379,6 +5451,7 @@ struct FogInfo { fog: vec4, eye: vec4, forward: vec4, + projection: vec4, } struct PlanarReflectionInfo { @@ -5391,9 +5464,9 @@ var g_albedo: vec3; var g_debug_surface: vec3; var g_debug_surface_on: bool; var g_premultiply: bool; -var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; +var v_world_position_1: vec3; var gl_FragCoord_1: vec4; @group(1) @binding(1) var planar_info: PlanarReflectionInfo; @@ -5409,10 +5482,32 @@ var v_tangent_1: vec4; var v_color_1: vec4; var v_lightmap_uv_1: vec2; +fn ViewDepth_u0028_() -> f32 { + let _e32 = v_world_position_1; + let _e34 = fog_info.eye; + let _e38 = fog_info.forward; + return dot((_e32 - _e34.xyz), _e38.xyz); +} + +fn Orthographic_u0028_() -> bool { + let _e34 = fog_info.projection[0u]; + return (_e34 > 0.5f); +} + fn EyeDistance_u0028_() -> f32 { - let _e31 = v_world_position_1; - let _e33 = fog_info.eye; - return distance(_e31, _e33.xyz); + var local: f32; + + let _e33 = Orthographic_u0028_(); + if _e33 { + let _e34 = ViewDepth_u0028_(); + local = max(_e34, 0f); + } else { + let _e36 = v_world_position_1; + let _e38 = fog_info.eye; + local = distance(_e36, _e38.xyz); + } + let _e41 = local; + return _e41; } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -5420,62 +5515,78 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var d: f32; var falloff: f32; var k: f32; - var local: f32; + var local_1: f32; - let _e39 = fog_info.fog[3u]; - density = _e39; - let _e40 = density; - if (_e40 <= 0f) { - let _e42 = (*color); - return _e42; - } - let _e43 = EyeDistance_u0028_(); - d = _e43; - let _e46 = fog_info.eye[3u]; - falloff = _e46; - let _e47 = falloff; - if (_e47 > 0f) { - let _e49 = falloff; - let _e51 = v_world_position_1[1u]; - let _e54 = fog_info.eye[1u]; - k = (_e49 * (_e51 - _e54)); - let _e57 = k; - if (abs(_e57) > 0.001f) { - let _e60 = k; - let _e64 = k; - local = ((1f - exp(-(_e60))) / _e64); + let _e40 = fog_info.fog[3u]; + density = _e40; + let _e41 = density; + if (_e41 <= 0f) { + let _e43 = (*color); + return _e43; + } + let _e44 = EyeDistance_u0028_(); + d = _e44; + let _e47 = fog_info.eye[3u]; + falloff = _e47; + let _e48 = falloff; + if (_e48 > 0f) { + let _e50 = falloff; + let _e52 = v_world_position_1[1u]; + let _e55 = fog_info.eye[1u]; + k = (_e50 * (_e52 - _e55)); + let _e58 = k; + if (abs(_e58) > 0.001f) { + let _e61 = k; + let _e65 = k; + local_1 = ((1f - exp(-(_e61))) / _e65); } else { - let _e66 = k; - local = (1f - (0.5f * _e66)); + let _e67 = k; + local_1 = (1f - (0.5f * _e67)); } - let _e69 = local; - let _e70 = density; - density = (_e70 * _e69); + let _e70 = local_1; + let _e71 = density; + density = (_e71 * _e70); } - let _e73 = fog_info.fog; - let _e75 = (*color); - let _e76 = density; - let _e78 = d; - return mix(_e73.xyz, _e75, vec3(clamp(exp((-(_e76) * _e78)), 0f, 1f))); + let _e74 = fog_info.fog; + let _e76 = (*color); + let _e77 = density; + let _e79 = d; + return mix(_e74.xyz, _e76, vec3(clamp(exp((-(_e77) * _e79)), 0f, 1f))); } -fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_1: vec2; +fn TowardsEye_u0028_() -> vec3 { + var local_2: vec3; - let _e33 = (*rows); - if (_e33 > 0f) { - let _e36 = gl_FragCoord_1[0u]; - let _e37 = (*rows); - let _e39 = gl_FragCoord_1[1u]; - local_1 = vec2(_e36, (_e37 - _e39)); + let _e33 = Orthographic_u0028_(); + if _e33 { + let _e35 = fog_info.forward; + local_2 = -(_e35.xyz); } else { - let _e42 = gl_FragCoord_1; - local_1 = _e42.xy; + let _e39 = fog_info.eye; + let _e41 = v_world_position_1; + local_2 = normalize((_e39.xyz - _e41)); } - let _e44 = local_1; + let _e44 = local_2; return _e44; } +fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { + var local_3: vec2; + + let _e34 = (*rows); + if (_e34 > 0f) { + let _e37 = gl_FragCoord_1[0u]; + let _e38 = (*rows); + let _e40 = gl_FragCoord_1[1u]; + local_3 = vec2(_e37, (_e38 - _e40)); + } else { + let _e43 = gl_FragCoord_1; + local_3 = _e43.xy; + } + let _e45 = local_3; + return _e45; +} + fn main_1() { var rows_1: f32; var pixel: vec2; @@ -5496,61 +5607,60 @@ fn main_1() { g_debug_surface = vec3(0f, 0f, 0f); g_debug_surface_on = false; g_premultiply = false; - let _e47 = planar_info.params[2u]; - rows_1 = _e47; - let _e48 = rows_1; - param = _e48; - let _e49 = FragCoordFromTop_u0028_f1_u003b((¶m)); - pixel = _e49; - let _e50 = pixel; - let _e52 = planar_info.view; - let _e56 = planar_info.view; - uv = ((_e50 - _e52.xy) / _e56.zw); - let _e59 = rows_1; - if (_e59 > 0f) { - let _e62 = uv[1u]; - uv[1u] = (1f - _e62); - } - let _e65 = uv; - let _e66 = textureSampleLevel(reflection_texture_tex, reflection_texture_smp, _e65, 0f); - let _e69 = planar_info.tint; - reflected = (_e66.xyz * _e69.xyz); - let _e72 = v_normal_1; - n = normalize(_e72); - let _e74 = gl_FrontFacing_1; - if !(_e74) { - let _e76 = n; - n = -(_e76); - } - let _e79 = fog_info.eye; - let _e81 = v_world_position_1; - v = normalize((_e79.xyz - _e81)); - let _e84 = n; - let _e85 = v; - cosine = clamp(dot(_e84, _e85), 0f, 1f); - let _e90 = planar_info.params[0u]; - f0_ = _e90; - let _e91 = cosine; - grazing = (1f - _e91); - let _e93 = grazing; - let _e94 = grazing; - grazing2_ = (_e93 * _e94); - let _e96 = f0_; - let _e97 = f0_; - let _e99 = grazing2_; - let _e101 = grazing2_; - let _e103 = grazing; - fresnel = (_e96 + ((((1f - _e97) * _e99) * _e101) * _e103)); - let _e106 = fresnel; - let _e109 = planar_info.params[1u]; - alpha = clamp((_e106 * _e109), 0f, 1f); - let _e112 = reflected; - param_1 = _e112; - let _e113 = ApplyFog_u0028_vf3_u003b((¶m_1)); - let _e114 = alpha; - let _e115 = (_e113 * _e114); - let _e116 = alpha; - frag_color = vec4(_e115.x, _e115.y, _e115.z, _e116); + let _e48 = planar_info.params[2u]; + rows_1 = _e48; + let _e49 = rows_1; + param = _e49; + let _e50 = FragCoordFromTop_u0028_f1_u003b((¶m)); + pixel = _e50; + let _e51 = pixel; + let _e53 = planar_info.view; + let _e57 = planar_info.view; + uv = ((_e51 - _e53.xy) / _e57.zw); + let _e60 = rows_1; + if (_e60 > 0f) { + let _e63 = uv[1u]; + uv[1u] = (1f - _e63); + } + let _e66 = uv; + let _e67 = textureSampleLevel(reflection_texture_tex, reflection_texture_smp, _e66, 0f); + let _e70 = planar_info.tint; + reflected = (_e67.xyz * _e70.xyz); + let _e73 = v_normal_1; + n = normalize(_e73); + let _e75 = gl_FrontFacing_1; + if !(_e75) { + let _e77 = n; + n = -(_e77); + } + let _e79 = TowardsEye_u0028_(); + v = _e79; + let _e80 = n; + let _e81 = v; + cosine = clamp(dot(_e80, _e81), 0f, 1f); + let _e86 = planar_info.params[0u]; + f0_ = _e86; + let _e87 = cosine; + grazing = (1f - _e87); + let _e89 = grazing; + let _e90 = grazing; + grazing2_ = (_e89 * _e90); + let _e92 = f0_; + let _e93 = f0_; + let _e95 = grazing2_; + let _e97 = grazing2_; + let _e99 = grazing; + fresnel = (_e92 + ((((1f - _e93) * _e95) * _e97) * _e99)); + let _e102 = fresnel; + let _e105 = planar_info.params[1u]; + alpha = clamp((_e102 * _e105), 0f, 1f); + let _e108 = reflected; + param_1 = _e108; + let _e109 = ApplyFog_u0028_vf3_u003b((¶m_1)); + let _e110 = alpha; + let _e111 = (_e109 * _e110); + let _e112 = alpha; + frag_color = vec4(_e111.x, _e111.y, _e111.z, _e112); return; } @@ -5575,7 +5685,7 @@ fn main(@location(6) v_world_position: vec3, @builtin(position) gl_FragCoor name: 'FogInfo', group: 1, binding: 0, - sizeInBytes: 48, + sizeInBytes: 64, members: [ WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), @@ -5584,6 +5694,11 @@ fn main(@location(6) v_world_position: vec3, @builtin(position) gl_FragCoor offsetInBytes: 32, sizeInBytes: 16, ), + WebGpuBlockMember( + name: 'projection', + offsetInBytes: 48, + sizeInBytes: 16, + ), ], ), WebGpuBlock( @@ -5643,6 +5758,7 @@ struct FogInfo { fog: vec4, eye: vec4, forward: vec4, + projection: vec4, } struct LightListInfo { @@ -5707,9 +5823,9 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; -var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; +var v_world_position_1: vec3; var frag_albedo: vec4; var frag_surface: vec4; var v_normal_1: vec3; @@ -5911,10 +6027,25 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { return; } +fn Orthographic_u0028_() -> bool { + let _e186 = fog_info.projection[0u]; + return (_e186 > 0.5f); +} + fn EyeDistance_u0028_() -> f32 { - let _e184 = v_world_position_1; - let _e186 = fog_info.eye; - return distance(_e184, _e186.xyz); + var local_1: f32; + + let _e185 = Orthographic_u0028_(); + if _e185 { + let _e186 = ViewDepth_u0028_(); + local_1 = max(_e186, 0f); + } else { + let _e188 = v_world_position_1; + let _e190 = fog_info.eye; + local_1 = distance(_e188, _e190.xyz); + } + let _e193 = local_1; + return _e193; } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -5922,7 +6053,7 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var d: f32; var falloff: f32; var k: f32; - var local_1: f32; + var local_2: f32; let _e192 = fog_info.fog[3u]; density = _e192; @@ -5945,12 +6076,12 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { if (abs(_e210) > 0.001f) { let _e213 = k; let _e217 = k; - local_1 = ((1f - exp(-(_e213))) / _e217); + local_2 = ((1f - exp(-(_e213))) / _e217); } else { let _e219 = k; - local_1 = (1f - (0.5f * _e219)); + local_2 = (1f - (0.5f * _e219)); } - let _e222 = local_1; + let _e222 = local_2; let _e223 = density; density = (_e223 * _e222); } @@ -5991,17 +6122,17 @@ fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { } fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { - var phi_2501_: bool; + var phi_2531_: bool; let _e185 = (*c); let _e186 = (*c); let _e188 = any((_e185 != _e186)); - phi_2501_ = _e188; + phi_2531_ = _e188; if !(_e188) { let _e190 = (*c); - phi_2501_ = any((abs(_e190) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + phi_2531_ = any((abs(_e190) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); } - let _e195 = phi_2501_; + let _e195 = phi_2531_; return _e195; } @@ -6014,9 +6145,9 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var grey: f32; var param_7: vec3; var param_8: vec3; - var local_2: vec3; + var local_3: vec3; var weight_2: f32; - var local_3: f32; + var local_4: f32; var param_9: vec3; var param_10: f32; @@ -6085,12 +6216,12 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ param_8 = _e266; let _e267 = NonFinite_u0028_vf3_u003b((¶m_8)); if _e267 { - local_2 = vec3(1f, 0f, 1f); + local_3 = vec3(1f, 0f, 1f); } else { let _e268 = grey; - local_2 = vec3((_e268 * 0.5f)); + local_3 = vec3((_e268 * 0.5f)); } - let _e271 = local_2; + let _e271 = local_3; shown = _e271; } } @@ -6103,11 +6234,11 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e272 = g_premultiply; if _e272 { let _e274 = (*s).alpha; - local_3 = _e274; + local_4 = _e274; } else { - local_3 = 1f; + local_4 = 1f; } - let _e275 = local_3; + let _e275 = local_4; weight_2 = _e275; let _e276 = shown; param_9 = _e276; @@ -6182,7 +6313,7 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_4: vec2; + var local_5: vec2; var atlas: vec2; let _e193 = point_shadow.params[0u]; @@ -6191,8 +6322,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr(0.16666667f, 0.16666667f)); let _e209 = point_shadow.params3_[0u]; @@ -6350,19 +6481,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_5: vec2; + var local_6: vec2; let _e186 = (*rows); if (_e186 > 0f) { let _e189 = gl_FragCoord_1[0u]; let _e190 = (*rows); let _e192 = gl_FragCoord_1[1u]; - local_5 = vec2(_e189, (_e190 - _e192)); + local_6 = vec2(_e189, (_e190 - _e192)); } else { let _e195 = gl_FragCoord_1; - local_5 = _e195.xy; + local_6 = _e195.xy; } - let _e197 = local_5; + let _e197 = local_6; return _e197; } @@ -6401,7 +6532,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_7: vec3; var lit_1: f32; var param_37: vec2; var param_38: vec2; @@ -6418,8 +6549,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_49: f32; var param_50: f32; - var phi_2190_: bool; - var phi_2251_: bool; + var phi_2220_: bool; + var phi_2281_: bool; let _e238 = (*lightIndex); let _e242 = point_shadow.slots[_e238][0u]; @@ -6484,13 +6615,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e338); - phi_2190_ = _e339; + phi_2220_ = _e339; if _e339 { let _e341 = a[0u]; let _e343 = a[2u]; - phi_2190_ = (_e341 >= _e343); + phi_2220_ = (_e341 >= _e343); } - let _e346 = phi_2190_; + let _e346 = phi_2220_; if _e346 { let _e348 = toFragment[0u]; face = select(1i, 0i, (_e348 > 0f)); @@ -6520,12 +6651,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2251_ = _e389; + phi_2281_ = _e389; if !(_e389) { let _e392 = ndc[1u]; - phi_2251_ = (abs(_e392) > 1f); + phi_2281_ = (abs(_e392) > 1f); } - let _e396 = phi_2251_; + let _e396 = phi_2281_; if _e396 { return 1f; } @@ -6575,15 +6706,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_7 = vec3(0f, 0f, 1f); } else { let _e451 = radius_1; let _e454 = point_shadow.params2_[2u]; let _e458 = blocker_1; let _e459 = range_3; - local_6 = vec3(clamp((_e451 / max(_e454, 0.000001f)), 0f, 1f), clamp((_e458 / _e459), 0f, 1f), 0f); + local_7 = vec3(clamp((_e451 / max(_e454, 0.000001f)), 0f, 1f), clamp((_e458 / _e459), 0f, 1f), 0f); } - let _e463 = local_6; + let _e463 = local_7; g_debug_surface = _e463; } let _e464 = radius_1; @@ -6775,8 +6906,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_7: mat4x4; var local_8: mat4x4; + var local_9: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -6790,8 +6921,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_9: f32; var local_10: f32; + var local_11: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -6825,11 +6956,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_69: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3446_: bool; - var phi_3457_: bool; - var phi_3618_: bool; - var phi_3625_: bool; - var phi_3632_: bool; + var phi_3476_: bool; + var phi_3487_: bool; + var phi_3648_: bool; + var phi_3655_: bool; + var phi_3662_: bool; let _e252 = frag_info.shadow_params[3u]; strength_1 = _e252; @@ -6850,25 +6981,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e273 = cascadeCount; let _e274 = (_e273 > 1i); - phi_3446_ = _e274; + phi_3476_ = _e274; if _e274 { let _e275 = viewDistance; let _e278 = frag_info.shadow_cascades[0u]; - phi_3446_ = (_e275 > _e278); + phi_3476_ = (_e275 > _e278); } - let _e281 = phi_3446_; + let _e281 = phi_3476_; if _e281 { cascade = 1i; } let _e282 = cascadeCount; let _e283 = (_e282 > 2i); - phi_3457_ = _e283; + phi_3487_ = _e283; if _e283 { let _e284 = viewDistance; let _e287 = frag_info.shadow_cascades[1u]; - phi_3457_ = (_e284 > _e287); + phi_3487_ = (_e284 > _e287); } - let _e290 = phi_3457_; + let _e290 = phi_3487_; if _e290 { cascade = 2i; } @@ -6893,20 +7024,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e299 = which; if (_e299 == 0i) { let _e302 = frag_info.shadow_matrix; - local_7 = _e302; + local_8 = _e302; } else { let _e303 = which; if (_e303 == 1i) { let _e306 = frag_info.shadow_matrix_far; - local_8 = _e306; + local_9 = _e306; } else { let _e308 = frag_info.shadow_matrix_farthest; - local_8 = _e308; + local_9 = _e308; } - let _e309 = local_8; - local_7 = _e309; + let _e309 = local_9; + local_8 = _e309; } - let _e310 = local_7; + let _e310 = local_8; matrix = _e310; let _e313 = matrix[0][0u]; let _e316 = matrix[1][0u]; @@ -6956,24 +7087,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e397 * 0.5f) + 0.5f), (0.5f - (_e401 * 0.5f))); let _e406 = inTile[0u]; let _e407 = (_e406 < 0f); - phi_3618_ = _e407; + phi_3648_ = _e407; if !(_e407) { let _e410 = inTile[0u]; - phi_3618_ = (_e410 > 1f); + phi_3648_ = (_e410 > 1f); } - let _e413 = phi_3618_; - phi_3625_ = _e413; + let _e413 = phi_3648_; + phi_3655_ = _e413; if !(_e413) { let _e416 = inTile[1u]; - phi_3625_ = (_e416 < 0f); + phi_3655_ = (_e416 < 0f); } - let _e419 = phi_3625_; - phi_3632_ = _e419; + let _e419 = phi_3655_; + phi_3662_ = _e419; if !(_e419) { let _e422 = inTile[1u]; - phi_3632_ = (_e422 > 1f); + phi_3662_ = (_e422 > 1f); } - let _e425 = phi_3632_; + let _e425 = phi_3662_; if _e425 { continue; } @@ -7030,20 +7161,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e494 = cascade; if (_e494 == 0i) { let _e498 = frag_info.shadow_bias[0u]; - local_9 = _e498; + local_10 = _e498; } else { let _e499 = cascade; if (_e499 == 1i) { let _e503 = frag_info.shadow_bias[1u]; - local_10 = _e503; + local_11 = _e503; } else { let _e506 = frag_info.shadow_bias[2u]; - local_10 = _e506; + local_11 = _e506; } - let _e507 = local_10; - local_9 = _e507; + let _e507 = local_11; + local_10 = _e507; } - let _e508 = local_9; + let _e508 = local_10; bias = _e508; let _e511 = frag_info.shadow_params[0u]; let _e514 = frag_info.shadow_cascades[3u]; @@ -7288,7 +7419,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_11: vec3; + var local_12: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -7330,15 +7461,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e234 > 0f) { let _e236 = (*mirror); let _e237 = t_1; - local_11 = (_e236 * _e237); + local_12 = (_e236 * _e237); } else { let _e239 = toPlane; let _e240 = n_2; let _e241 = toPlane; let _e242 = n_2; - local_11 = (_e239 - (_e240 * dot(_e241, _e242))); + local_12 = (_e239 - (_e240 * dot(_e241, _e242))); } - let _e246 = local_11; + let _e246 = local_12; onPlane = _e246; } let _e247 = onPlane; @@ -7372,7 +7503,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_12: f32; + var local_13: f32; let _e193 = (*a_1); let _e194 = (*a_1); @@ -7394,11 +7525,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> let _e218 = axis; let _e219 = (*n_3); let _e222 = len; - local_12 = ((_e217 * dot(_e218, _e219)) / _e222); + local_13 = ((_e217 * dot(_e218, _e219)) / _e222); } else { - local_12 = 0f; + local_13 = 0f; } - let _e224 = local_12; + let _e224 = local_13; return _e224; } @@ -7409,15 +7540,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_13: i32; + var local_14: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_14: f32; + var local_15: f32; var aAbove: bool; var bAbove: bool; - var local_15: f32; + var local_16: f32; var param_73: vec3; var param_74: vec3; var param_75: vec3; @@ -7437,12 +7568,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr 0.000000000001f) { let _e235 = ha; let _e236 = d_3; - local_14 = (_e235 / _e236); + local_15 = (_e235 / _e236); } else { - local_14 = 0f; + local_15 = 0f; } - let _e238 = local_14; + let _e238 = local_15; q = (_e228 + ((_e229 - _e230) * _e238)); let _e241 = ha; aAbove = (_e241 > 0f); @@ -7485,11 +7616,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_16: f32; var local_17: f32; var local_18: f32; + var local_19: f32; let _e190 = (*slot_1); let _e191 = (*slot_1); @@ -7536,28 +7667,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 let _e195 = lane; if (_e195 == 0i) { let _e198 = (*four)[0u]; - local_16 = _e198; + local_17 = _e198; } else { let _e199 = lane; if (_e199 == 1i) { let _e202 = (*four)[1u]; - local_17 = _e202; + local_18 = _e202; } else { let _e203 = lane; if (_e203 == 2i) { let _e206 = (*four)[2u]; - local_18 = _e206; + local_19 = _e206; } else { let _e208 = (*four)[3u]; - local_18 = _e208; + local_19 = _e208; } - let _e209 = local_18; - local_17 = _e209; + let _e209 = local_19; + local_18 = _e209; } - let _e210 = local_17; - local_16 = _e210; + let _e210 = local_18; + local_17 = _e210; } - let _e211 = local_16; + let _e211 = local_17; return _e211; } @@ -7668,12 +7799,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_19: f32; + var local_20: f32; var param_87: i32; var param_88: i32; var v_1: f32; var u_1: f32; - var local_20: f32; + var local_21: f32; var param_89: f32; var param_90: i32; var type_39: f32; @@ -7683,12 +7814,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_21: f32; + var local_22: f32; var param_91: array, 4>; var param_92: vec3; var area: f32; var radiance: f32; - var local_22: f32; + var local_23: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -7701,7 +7832,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_97: vec3; var toPoint: vec3; var pointDistance: f32; - var local_23: vec3; + var local_24: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; @@ -7736,14 +7867,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e259 = slot_5; param_87 = _e259; let _e260 = ClusterRow_u0028_i1_u003b((¶m_87)); - local_19 = _e260; + local_20 = _e260; } else { let _e261 = slot_5; param_88 = _e261; let _e262 = LightListRow_u0028_i1_u003b((¶m_88)); - local_19 = _e262; + local_20 = _e262; } - let _e263 = local_19; + let _e263 = local_20; listRow = _e263; let _e264 = listRow; let _e268 = light_list_info.list[2u]; @@ -7771,14 +7902,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e294 = listRow; param_89 = _e294; let _e295 = InSlots_u0028_f1_u003b((¶m_89)); - local_20 = select(1f, 0f, _e295); + local_21 = select(1f, 0f, _e295); } else { let _e297 = slot_5; param_90 = _e297; let _e298 = LightListScale_u0028_i1_u003b((¶m_90)); - local_20 = _e298; + local_21 = _e298; } - let _e299 = local_20; + let _e299 = local_21; let _e301 = color_1[3u]; color_1[3u] = (_e301 * _e299); } @@ -7815,16 +7946,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e340, cross(_e341, _e342)) >= 0f); let _e346 = behind; if _e346 { - local_21 = 0f; + local_22 = 0f; } else { let _e347 = corners_1; param_91 = _e347; let _e349 = (*s_4).n; param_92 = _e349; let _e350 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_91), (¶m_92)); - local_21 = _e350; + local_22 = _e350; } - let _e351 = local_21; + let _e351 = local_22; formFactor = _e351; let _e352 = halfWidth_1; let _e353 = halfHeight_1; @@ -7832,11 +7963,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e357 = area; if (_e357 > 0.000000001f) { let _e359 = area; - local_22 = (1f / _e359); + local_23 = (1f / _e359); } else { - local_22 = 0f; + local_23 = 0f; } - let _e361 = local_22; + let _e361 = local_23; radiance = _e361; let _e362 = toCentre; distance_2 = length(_e362); @@ -7879,12 +8010,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e403 > 0.000001f) { let _e405 = toPoint; let _e406 = pointDistance; - local_23 = (_e405 / vec3(_e406)); + local_24 = (_e405 / vec3(_e406)); } else { let _e410 = (*s_4).n; - local_23 = _e410; + local_24 = _e410; } - let _e411 = local_23; + let _e411 = local_24; light_3.l = _e411; let _e414 = light_3.l; let _e416 = (*s_4).v; @@ -7980,7 +8111,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_24: f32; + var local_25: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -8008,15 +8139,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e244 = clip_1[3u]; let _e245 = near_1; if (_e244 <= _e245) { - local_24 = 0f; + local_25 = 0f; } else { let _e248 = clip_1[3u]; let _e249 = near_1; let _e254 = light_list_info.cluster_depth[1u]; let _e258 = grid[2u]; - local_24 = clamp(floor((log((_e248 / _e249)) * _e254)), 0f, (_e258 - 1f)); + local_25 = clamp(floor((log((_e248 / _e249)) * _e254)), 0f, (_e258 - 1f)); } - let _e261 = local_24; + let _e261 = local_25; tz = _e261; let _e262 = tx; let _e263 = ty; @@ -8070,7 +8201,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_104: Surface; var visibility: f32; var param_105: i32; - var local_25: f32; + var local_26: f32; var param_106: Surface; var param_107: LightSample; var param_108: i32; @@ -8120,11 +8251,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e223 = i_8; param_111 = _e223; let _e224 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_109), (¶m_110), (¶m_111)); - local_25 = (_e219 * _e224); + local_26 = (_e219 * _e224); } else { - local_25 = 1f; + local_26 = 1f; } - let _e226 = local_25; + let _e226 = local_26; visibility = _e226; let _e227 = visibility; if (_e227 <= 0f) { @@ -8389,7 +8520,7 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var param_126: f32; var param_127: vec2; - var local_26: vec3; + var local_27: vec3; let _e231 = irradiance_info.origin; origin_2 = _e231.xyz; @@ -8564,11 +8695,11 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr 0f) { let _e459 = total_2; let _e460 = weights; - local_26 = (_e459 / vec3(_e460)); + local_27 = (_e459 / vec3(_e460)); } else { - local_26 = vec3(0f, 0f, 0f); + local_27 = vec3(0f, 0f, 0f); } - let _e463 = local_26; + let _e463 = local_27; return _e463; } @@ -8615,6 +8746,22 @@ fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d return; } +fn TowardsEye_u0028_() -> vec3 { + var local_28: vec3; + + let _e185 = Orthographic_u0028_(); + if _e185 { + let _e187 = fog_info.forward; + local_28 = -(_e187.xyz); + } else { + let _e191 = fog_info.eye; + let _e193 = v_world_position_1; + local_28 = normalize((_e191.xyz - _e193)); + } + let _e196 = local_28; + return _e196; +} + fn ReadSurface_u0028_() -> Surface { var texel_6: vec4; var s_10: Surface; @@ -8675,27 +8822,26 @@ fn ReadSurface_u0028_() -> Surface { let _e252 = s_10.n; s_10.n = -(_e252); } - let _e256 = frag_info.camera_position; - let _e258 = v_world_position_1; - s_10.v = normalize((_e256.xyz - _e258)); - let _e263 = s_10.n; - let _e265 = s_10.v; - s_10.n_dot_v = max(dot(_e263, _e265), 0.0001f); - let _e271 = frag_info.material[0u]; - s_10.metallic = clamp(_e271, 0f, 1f); - let _e276 = frag_info.material[1u]; - s_10.roughness = clamp(_e276, 0.02f, 1f); - let _e280 = frag_info.ambient_ground; - let _e283 = frag_info.ambient_sky; - let _e287 = s_10.n[1u]; - let _e294 = frag_info.material[2u]; - s_10.ambient = (mix(_e280.xyz, _e283.xyz, vec3(((_e287 * 0.5f) + 0.5f))) * _e294); - let _e299 = frag_info.frame_params[0u]; - s_10.exposure = max(_e299, 0f); + let _e255 = TowardsEye_u0028_(); + s_10.v = _e255; + let _e258 = s_10.n; + let _e260 = s_10.v; + s_10.n_dot_v = max(dot(_e258, _e260), 0.0001f); + let _e266 = frag_info.material[0u]; + s_10.metallic = clamp(_e266, 0f, 1f); + let _e271 = frag_info.material[1u]; + s_10.roughness = clamp(_e271, 0.02f, 1f); + let _e275 = frag_info.ambient_ground; + let _e278 = frag_info.ambient_sky; + let _e282 = s_10.n[1u]; + let _e289 = frag_info.material[2u]; + s_10.ambient = (mix(_e275.xyz, _e278.xyz, vec3(((_e282 * 0.5f) + 0.5f))) * _e289); + let _e294 = frag_info.frame_params[0u]; + s_10.exposure = max(_e294, 0f); s_10.occlusion = 1f; s_10.emissive = vec3(0f, 0f, 0f); - let _e304 = s_10; - return _e304; + let _e299 = s_10; + return _e299; } fn main_1() { @@ -8776,7 +8922,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), @@ -8785,6 +8931,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, eye: vec4, forward: vec4, + projection: vec4, } struct LightListInfo { @@ -9181,9 +9333,9 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; -var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; +var v_world_position_1: vec3; var frag_albedo: vec4; var frag_surface: vec4; var v_normal_1: vec3; @@ -9385,10 +9537,25 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { return; } +fn Orthographic_u0028_() -> bool { + let _e187 = fog_info.projection[0u]; + return (_e187 > 0.5f); +} + fn EyeDistance_u0028_() -> f32 { - let _e185 = v_world_position_1; - let _e187 = fog_info.eye; - return distance(_e185, _e187.xyz); + var local_1: f32; + + let _e186 = Orthographic_u0028_(); + if _e186 { + let _e187 = ViewDepth_u0028_(); + local_1 = max(_e187, 0f); + } else { + let _e189 = v_world_position_1; + let _e191 = fog_info.eye; + local_1 = distance(_e189, _e191.xyz); + } + let _e194 = local_1; + return _e194; } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -9396,7 +9563,7 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var d: f32; var falloff: f32; var k: f32; - var local_1: f32; + var local_2: f32; let _e193 = fog_info.fog[3u]; density = _e193; @@ -9419,12 +9586,12 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { if (abs(_e211) > 0.001f) { let _e214 = k; let _e218 = k; - local_1 = ((1f - exp(-(_e214))) / _e218); + local_2 = ((1f - exp(-(_e214))) / _e218); } else { let _e220 = k; - local_1 = (1f - (0.5f * _e220)); + local_2 = (1f - (0.5f * _e220)); } - let _e223 = local_1; + let _e223 = local_2; let _e224 = density; density = (_e224 * _e223); } @@ -9465,17 +9632,17 @@ fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { } fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { - var phi_2504_: bool; + var phi_2534_: bool; let _e186 = (*c); let _e187 = (*c); let _e189 = any((_e186 != _e187)); - phi_2504_ = _e189; + phi_2534_ = _e189; if !(_e189) { let _e191 = (*c); - phi_2504_ = any((abs(_e191) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + phi_2534_ = any((abs(_e191) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); } - let _e196 = phi_2504_; + let _e196 = phi_2534_; return _e196; } @@ -9488,9 +9655,9 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var grey: f32; var param_7: vec3; var param_8: vec3; - var local_2: vec3; + var local_3: vec3; var weight_2: f32; - var local_3: f32; + var local_4: f32; var param_9: vec3; var param_10: f32; @@ -9559,12 +9726,12 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ param_8 = _e267; let _e268 = NonFinite_u0028_vf3_u003b((¶m_8)); if _e268 { - local_2 = vec3(1f, 0f, 1f); + local_3 = vec3(1f, 0f, 1f); } else { let _e269 = grey; - local_2 = vec3((_e269 * 0.5f)); + local_3 = vec3((_e269 * 0.5f)); } - let _e272 = local_2; + let _e272 = local_3; shown = _e272; } } @@ -9577,11 +9744,11 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e273 = g_premultiply; if _e273 { let _e275 = (*s).alpha; - local_3 = _e275; + local_4 = _e275; } else { - local_3 = 1f; + local_4 = 1f; } - let _e276 = local_3; + let _e276 = local_4; weight_2 = _e276; let _e277 = shown; param_9 = _e277; @@ -9666,7 +9833,7 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_4: vec2; + var local_5: vec2; var atlas: vec2; let _e194 = point_shadow.params[0u]; @@ -9675,8 +9842,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr(0.16666667f, 0.16666667f)); let _e210 = point_shadow.params3_[0u]; @@ -9834,19 +10001,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_5: vec2; + var local_6: vec2; let _e187 = (*rows); if (_e187 > 0f) { let _e190 = gl_FragCoord_1[0u]; let _e191 = (*rows); let _e193 = gl_FragCoord_1[1u]; - local_5 = vec2(_e190, (_e191 - _e193)); + local_6 = vec2(_e190, (_e191 - _e193)); } else { let _e196 = gl_FragCoord_1; - local_5 = _e196.xy; + local_6 = _e196.xy; } - let _e198 = local_5; + let _e198 = local_6; return _e198; } @@ -9885,7 +10052,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_7: vec3; var lit_1: f32; var param_37: vec2; var param_38: vec2; @@ -9902,8 +10069,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_49: f32; var param_50: f32; - var phi_2193_: bool; - var phi_2254_: bool; + var phi_2223_: bool; + var phi_2284_: bool; let _e239 = (*lightIndex); let _e243 = point_shadow.slots[_e239][0u]; @@ -9968,13 +10135,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e339); - phi_2193_ = _e340; + phi_2223_ = _e340; if _e340 { let _e342 = a[0u]; let _e344 = a[2u]; - phi_2193_ = (_e342 >= _e344); + phi_2223_ = (_e342 >= _e344); } - let _e347 = phi_2193_; + let _e347 = phi_2223_; if _e347 { let _e349 = toFragment[0u]; face = select(1i, 0i, (_e349 > 0f)); @@ -10004,12 +10171,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2254_ = _e390; + phi_2284_ = _e390; if !(_e390) { let _e393 = ndc[1u]; - phi_2254_ = (abs(_e393) > 1f); + phi_2284_ = (abs(_e393) > 1f); } - let _e397 = phi_2254_; + let _e397 = phi_2284_; if _e397 { return 1f; } @@ -10059,15 +10226,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_7 = vec3(0f, 0f, 1f); } else { let _e452 = radius_1; let _e455 = point_shadow.params2_[2u]; let _e459 = blocker_1; let _e460 = range_3; - local_6 = vec3(clamp((_e452 / max(_e455, 0.000001f)), 0f, 1f), clamp((_e459 / _e460), 0f, 1f), 0f); + local_7 = vec3(clamp((_e452 / max(_e455, 0.000001f)), 0f, 1f), clamp((_e459 / _e460), 0f, 1f), 0f); } - let _e464 = local_6; + let _e464 = local_7; g_debug_surface = _e464; } let _e465 = radius_1; @@ -10259,8 +10426,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_7: mat4x4; var local_8: mat4x4; + var local_9: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -10274,8 +10441,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_9: f32; var local_10: f32; + var local_11: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -10309,11 +10476,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_69: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3473_: bool; - var phi_3484_: bool; - var phi_3645_: bool; - var phi_3652_: bool; - var phi_3659_: bool; + var phi_3503_: bool; + var phi_3514_: bool; + var phi_3675_: bool; + var phi_3682_: bool; + var phi_3689_: bool; let _e253 = frag_info.shadow_params[3u]; strength_1 = _e253; @@ -10334,25 +10501,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e274 = cascadeCount; let _e275 = (_e274 > 1i); - phi_3473_ = _e275; + phi_3503_ = _e275; if _e275 { let _e276 = viewDistance; let _e279 = frag_info.shadow_cascades[0u]; - phi_3473_ = (_e276 > _e279); + phi_3503_ = (_e276 > _e279); } - let _e282 = phi_3473_; + let _e282 = phi_3503_; if _e282 { cascade = 1i; } let _e283 = cascadeCount; let _e284 = (_e283 > 2i); - phi_3484_ = _e284; + phi_3514_ = _e284; if _e284 { let _e285 = viewDistance; let _e288 = frag_info.shadow_cascades[1u]; - phi_3484_ = (_e285 > _e288); + phi_3514_ = (_e285 > _e288); } - let _e291 = phi_3484_; + let _e291 = phi_3514_; if _e291 { cascade = 2i; } @@ -10377,20 +10544,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e300 = which; if (_e300 == 0i) { let _e303 = frag_info.shadow_matrix; - local_7 = _e303; + local_8 = _e303; } else { let _e304 = which; if (_e304 == 1i) { let _e307 = frag_info.shadow_matrix_far; - local_8 = _e307; + local_9 = _e307; } else { let _e309 = frag_info.shadow_matrix_farthest; - local_8 = _e309; + local_9 = _e309; } - let _e310 = local_8; - local_7 = _e310; + let _e310 = local_9; + local_8 = _e310; } - let _e311 = local_7; + let _e311 = local_8; matrix = _e311; let _e314 = matrix[0][0u]; let _e317 = matrix[1][0u]; @@ -10440,24 +10607,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e398 * 0.5f) + 0.5f), (0.5f - (_e402 * 0.5f))); let _e407 = inTile[0u]; let _e408 = (_e407 < 0f); - phi_3645_ = _e408; + phi_3675_ = _e408; if !(_e408) { let _e411 = inTile[0u]; - phi_3645_ = (_e411 > 1f); + phi_3675_ = (_e411 > 1f); } - let _e414 = phi_3645_; - phi_3652_ = _e414; + let _e414 = phi_3675_; + phi_3682_ = _e414; if !(_e414) { let _e417 = inTile[1u]; - phi_3652_ = (_e417 < 0f); + phi_3682_ = (_e417 < 0f); } - let _e420 = phi_3652_; - phi_3659_ = _e420; + let _e420 = phi_3682_; + phi_3689_ = _e420; if !(_e420) { let _e423 = inTile[1u]; - phi_3659_ = (_e423 > 1f); + phi_3689_ = (_e423 > 1f); } - let _e426 = phi_3659_; + let _e426 = phi_3689_; if _e426 { continue; } @@ -10514,20 +10681,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e495 = cascade; if (_e495 == 0i) { let _e499 = frag_info.shadow_bias[0u]; - local_9 = _e499; + local_10 = _e499; } else { let _e500 = cascade; if (_e500 == 1i) { let _e504 = frag_info.shadow_bias[1u]; - local_10 = _e504; + local_11 = _e504; } else { let _e507 = frag_info.shadow_bias[2u]; - local_10 = _e507; + local_11 = _e507; } - let _e508 = local_10; - local_9 = _e508; + let _e508 = local_11; + local_10 = _e508; } - let _e509 = local_9; + let _e509 = local_10; bias = _e509; let _e512 = frag_info.shadow_params[0u]; let _e515 = frag_info.shadow_cascades[3u]; @@ -10772,7 +10939,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_11: vec3; + var local_12: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -10814,15 +10981,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e235 > 0f) { let _e237 = (*mirror); let _e238 = t_1; - local_11 = (_e237 * _e238); + local_12 = (_e237 * _e238); } else { let _e240 = toPlane; let _e241 = n_2; let _e242 = toPlane; let _e243 = n_2; - local_11 = (_e240 - (_e241 * dot(_e242, _e243))); + local_12 = (_e240 - (_e241 * dot(_e242, _e243))); } - let _e247 = local_11; + let _e247 = local_12; onPlane = _e247; } let _e248 = onPlane; @@ -10856,7 +11023,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_12: f32; + var local_13: f32; let _e194 = (*a_1); let _e195 = (*a_1); @@ -10878,11 +11045,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> let _e219 = axis; let _e220 = (*n_3); let _e223 = len; - local_12 = ((_e218 * dot(_e219, _e220)) / _e223); + local_13 = ((_e218 * dot(_e219, _e220)) / _e223); } else { - local_12 = 0f; + local_13 = 0f; } - let _e225 = local_12; + let _e225 = local_13; return _e225; } @@ -10893,15 +11060,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_13: i32; + var local_14: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_14: f32; + var local_15: f32; var aAbove: bool; var bAbove: bool; - var local_15: f32; + var local_16: f32; var param_73: vec3; var param_74: vec3; var param_75: vec3; @@ -10921,12 +11088,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr 0.000000000001f) { let _e236 = ha; let _e237 = d_3; - local_14 = (_e236 / _e237); + local_15 = (_e236 / _e237); } else { - local_14 = 0f; + local_15 = 0f; } - let _e239 = local_14; + let _e239 = local_15; q = (_e229 + ((_e230 - _e231) * _e239)); let _e242 = ha; aAbove = (_e242 > 0f); @@ -10969,11 +11136,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_16: f32; var local_17: f32; var local_18: f32; + var local_19: f32; let _e191 = (*slot_1); let _e192 = (*slot_1); @@ -11020,28 +11187,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 let _e196 = lane; if (_e196 == 0i) { let _e199 = (*four)[0u]; - local_16 = _e199; + local_17 = _e199; } else { let _e200 = lane; if (_e200 == 1i) { let _e203 = (*four)[1u]; - local_17 = _e203; + local_18 = _e203; } else { let _e204 = lane; if (_e204 == 2i) { let _e207 = (*four)[2u]; - local_18 = _e207; + local_19 = _e207; } else { let _e209 = (*four)[3u]; - local_18 = _e209; + local_19 = _e209; } - let _e210 = local_18; - local_17 = _e210; + let _e210 = local_19; + local_18 = _e210; } - let _e211 = local_17; - local_16 = _e211; + let _e211 = local_18; + local_17 = _e211; } - let _e212 = local_16; + let _e212 = local_17; return _e212; } @@ -11152,12 +11319,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_19: f32; + var local_20: f32; var param_87: i32; var param_88: i32; var v_1: f32; var u_1: f32; - var local_20: f32; + var local_21: f32; var param_89: f32; var param_90: i32; var type_39: f32; @@ -11167,12 +11334,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_21: f32; + var local_22: f32; var param_91: array, 4>; var param_92: vec3; var area: f32; var radiance: f32; - var local_22: f32; + var local_23: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -11185,7 +11352,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_97: vec3; var toPoint: vec3; var pointDistance: f32; - var local_23: vec3; + var local_24: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; @@ -11220,14 +11387,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e260 = slot_5; param_87 = _e260; let _e261 = ClusterRow_u0028_i1_u003b((¶m_87)); - local_19 = _e261; + local_20 = _e261; } else { let _e262 = slot_5; param_88 = _e262; let _e263 = LightListRow_u0028_i1_u003b((¶m_88)); - local_19 = _e263; + local_20 = _e263; } - let _e264 = local_19; + let _e264 = local_20; listRow = _e264; let _e265 = listRow; let _e269 = light_list_info.list[2u]; @@ -11255,14 +11422,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e295 = listRow; param_89 = _e295; let _e296 = InSlots_u0028_f1_u003b((¶m_89)); - local_20 = select(1f, 0f, _e296); + local_21 = select(1f, 0f, _e296); } else { let _e298 = slot_5; param_90 = _e298; let _e299 = LightListScale_u0028_i1_u003b((¶m_90)); - local_20 = _e299; + local_21 = _e299; } - let _e300 = local_20; + let _e300 = local_21; let _e302 = color_1[3u]; color_1[3u] = (_e302 * _e300); } @@ -11299,16 +11466,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e341, cross(_e342, _e343)) >= 0f); let _e347 = behind; if _e347 { - local_21 = 0f; + local_22 = 0f; } else { let _e348 = corners_1; param_91 = _e348; let _e350 = (*s_4).n; param_92 = _e350; let _e351 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_91), (¶m_92)); - local_21 = _e351; + local_22 = _e351; } - let _e352 = local_21; + let _e352 = local_22; formFactor = _e352; let _e353 = halfWidth_1; let _e354 = halfHeight_1; @@ -11316,11 +11483,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e358 = area; if (_e358 > 0.000000001f) { let _e360 = area; - local_22 = (1f / _e360); + local_23 = (1f / _e360); } else { - local_22 = 0f; + local_23 = 0f; } - let _e362 = local_22; + let _e362 = local_23; radiance = _e362; let _e363 = toCentre; distance_2 = length(_e363); @@ -11363,12 +11530,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e404 > 0.000001f) { let _e406 = toPoint; let _e407 = pointDistance; - local_23 = (_e406 / vec3(_e407)); + local_24 = (_e406 / vec3(_e407)); } else { let _e411 = (*s_4).n; - local_23 = _e411; + local_24 = _e411; } - let _e412 = local_23; + let _e412 = local_24; light_3.l = _e412; let _e415 = light_3.l; let _e417 = (*s_4).v; @@ -11464,7 +11631,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_24: f32; + var local_25: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -11492,15 +11659,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e245 = clip_1[3u]; let _e246 = near_1; if (_e245 <= _e246) { - local_24 = 0f; + local_25 = 0f; } else { let _e249 = clip_1[3u]; let _e250 = near_1; let _e255 = light_list_info.cluster_depth[1u]; let _e259 = grid[2u]; - local_24 = clamp(floor((log((_e249 / _e250)) * _e255)), 0f, (_e259 - 1f)); + local_25 = clamp(floor((log((_e249 / _e250)) * _e255)), 0f, (_e259 - 1f)); } - let _e262 = local_24; + let _e262 = local_25; tz = _e262; let _e263 = tx; let _e264 = ty; @@ -11554,7 +11721,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_104: Surface; var visibility: f32; var param_105: i32; - var local_25: f32; + var local_26: f32; var param_106: Surface; var param_107: LightSample; var param_108: i32; @@ -11604,11 +11771,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e224 = i_8; param_111 = _e224; let _e225 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_109), (¶m_110), (¶m_111)); - local_25 = (_e220 * _e225); + local_26 = (_e220 * _e225); } else { - local_25 = 1f; + local_26 = 1f; } - let _e227 = local_25; + let _e227 = local_26; visibility = _e227; let _e228 = visibility; if (_e228 <= 0f) { @@ -11889,7 +12056,7 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var param_126: f32; var param_127: vec2; - var local_26: vec3; + var local_27: vec3; let _e232 = irradiance_info.origin; origin_2 = _e232.xyz; @@ -12064,11 +12231,11 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr 0f) { let _e460 = total_2; let _e461 = weights; - local_26 = (_e460 / vec3(_e461)); + local_27 = (_e460 / vec3(_e461)); } else { - local_26 = vec3(0f, 0f, 0f); + local_27 = vec3(0f, 0f, 0f); } - let _e464 = local_26; + let _e464 = local_27; return _e464; } @@ -12115,6 +12282,22 @@ fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d return; } +fn TowardsEye_u0028_() -> vec3 { + var local_28: vec3; + + let _e186 = Orthographic_u0028_(); + if _e186 { + let _e188 = fog_info.forward; + local_28 = -(_e188.xyz); + } else { + let _e192 = fog_info.eye; + let _e194 = v_world_position_1; + local_28 = normalize((_e192.xyz - _e194)); + } + let _e197 = local_28; + return _e197; +} + fn ReadSurface_u0028_() -> Surface { var texel_6: vec4; var s_11: Surface; @@ -12175,27 +12358,26 @@ fn ReadSurface_u0028_() -> Surface { let _e253 = s_11.n; s_11.n = -(_e253); } - let _e257 = frag_info.camera_position; - let _e259 = v_world_position_1; - s_11.v = normalize((_e257.xyz - _e259)); - let _e264 = s_11.n; - let _e266 = s_11.v; - s_11.n_dot_v = max(dot(_e264, _e266), 0.0001f); - let _e272 = frag_info.material[0u]; - s_11.metallic = clamp(_e272, 0f, 1f); - let _e277 = frag_info.material[1u]; - s_11.roughness = clamp(_e277, 0.02f, 1f); - let _e281 = frag_info.ambient_ground; - let _e284 = frag_info.ambient_sky; - let _e288 = s_11.n[1u]; - let _e295 = frag_info.material[2u]; - s_11.ambient = (mix(_e281.xyz, _e284.xyz, vec3(((_e288 * 0.5f) + 0.5f))) * _e295); - let _e300 = frag_info.frame_params[0u]; - s_11.exposure = max(_e300, 0f); + let _e256 = TowardsEye_u0028_(); + s_11.v = _e256; + let _e259 = s_11.n; + let _e261 = s_11.v; + s_11.n_dot_v = max(dot(_e259, _e261), 0.0001f); + let _e267 = frag_info.material[0u]; + s_11.metallic = clamp(_e267, 0f, 1f); + let _e272 = frag_info.material[1u]; + s_11.roughness = clamp(_e272, 0.02f, 1f); + let _e276 = frag_info.ambient_ground; + let _e279 = frag_info.ambient_sky; + let _e283 = s_11.n[1u]; + let _e290 = frag_info.material[2u]; + s_11.ambient = (mix(_e276.xyz, _e279.xyz, vec3(((_e283 * 0.5f) + 0.5f))) * _e290); + let _e295 = frag_info.frame_params[0u]; + s_11.exposure = max(_e295, 0f); s_11.occlusion = 1f; s_11.emissive = vec3(0f, 0f, 0f); - let _e305 = s_11; - return _e305; + let _e300 = s_11; + return _e300; } fn main_1() { @@ -12282,7 +12464,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), @@ -12291,6 +12473,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, eye: vec4, forward: vec4, + projection: vec4, } struct LightListInfo { @@ -12687,9 +12875,9 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; -var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; +var v_world_position_1: vec3; var frag_albedo: vec4; var frag_surface: vec4; var v_normal_1: vec3; @@ -12899,10 +13087,25 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { return; } +fn Orthographic_u0028_() -> bool { + let _e206 = fog_info.projection[0u]; + return (_e206 > 0.5f); +} + fn EyeDistance_u0028_() -> f32 { - let _e204 = v_world_position_1; - let _e206 = fog_info.eye; - return distance(_e204, _e206.xyz); + var local_1: f32; + + let _e205 = Orthographic_u0028_(); + if _e205 { + let _e206 = ViewDepth_u0028_(); + local_1 = max(_e206, 0f); + } else { + let _e208 = v_world_position_1; + let _e210 = fog_info.eye; + local_1 = distance(_e208, _e210.xyz); + } + let _e213 = local_1; + return _e213; } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -12910,7 +13113,7 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var d: f32; var falloff: f32; var k: f32; - var local_1: f32; + var local_2: f32; let _e212 = fog_info.fog[3u]; density = _e212; @@ -12933,12 +13136,12 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { if (abs(_e230) > 0.001f) { let _e233 = k; let _e237 = k; - local_1 = ((1f - exp(-(_e233))) / _e237); + local_2 = ((1f - exp(-(_e233))) / _e237); } else { let _e239 = k; - local_1 = (1f - (0.5f * _e239)); + local_2 = (1f - (0.5f * _e239)); } - let _e242 = local_1; + let _e242 = local_2; let _e243 = density; density = (_e243 * _e242); } @@ -12979,17 +13182,17 @@ fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { } fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { - var phi_2843_: bool; + var phi_2873_: bool; let _e205 = (*c); let _e206 = (*c); let _e208 = any((_e205 != _e206)); - phi_2843_ = _e208; + phi_2873_ = _e208; if !(_e208) { let _e210 = (*c); - phi_2843_ = any((abs(_e210) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + phi_2873_ = any((abs(_e210) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); } - let _e215 = phi_2843_; + let _e215 = phi_2873_; return _e215; } @@ -13002,9 +13205,9 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var grey: f32; var param_7: vec3; var param_8: vec3; - var local_2: vec3; + var local_3: vec3; var weight_2: f32; - var local_3: f32; + var local_4: f32; var param_9: vec3; var param_10: f32; @@ -13073,12 +13276,12 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ param_8 = _e286; let _e287 = NonFinite_u0028_vf3_u003b((¶m_8)); if _e287 { - local_2 = vec3(1f, 0f, 1f); + local_3 = vec3(1f, 0f, 1f); } else { let _e288 = grey; - local_2 = vec3((_e288 * 0.5f)); + local_3 = vec3((_e288 * 0.5f)); } - let _e291 = local_2; + let _e291 = local_3; shown = _e291; } } @@ -13091,11 +13294,11 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e292 = g_premultiply; if _e292 { let _e294 = (*s).alpha; - local_3 = _e294; + local_4 = _e294; } else { - local_3 = 1f; + local_4 = 1f; } - let _e295 = local_3; + let _e295 = local_4; weight_2 = _e295; let _e296 = shown; param_9 = _e296; @@ -13146,7 +13349,7 @@ fn FonAverage_u0028_f1_u003b(r_1: ptr) -> f32 { fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, r_2: ptr, mu_i: ptr, mu_o: ptr, l_dot_v: ptr) -> vec3 { var s_1: f32; var s_over_t: f32; - var local_4: f32; + var local_5: f32; var af: f32; var single: vec3; var average_1: f32; @@ -13168,12 +13371,12 @@ fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_5: vec2; + var local_6: vec2; var atlas: vec2; let _e213 = point_shadow.params[0u]; @@ -13495,8 +13698,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr(0.16666667f, 0.16666667f)); let _e229 = point_shadow.params3_[0u]; @@ -13654,19 +13857,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_6: vec2; + var local_7: vec2; let _e206 = (*rows); if (_e206 > 0f) { let _e209 = gl_FragCoord_1[0u]; let _e210 = (*rows); let _e212 = gl_FragCoord_1[1u]; - local_6 = vec2(_e209, (_e210 - _e212)); + local_7 = vec2(_e209, (_e210 - _e212)); } else { let _e215 = gl_FragCoord_1; - local_6 = _e215.xy; + local_7 = _e215.xy; } - let _e217 = local_6; + let _e217 = local_7; return _e217; } @@ -13705,7 +13908,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_8: vec3; var lit_1: f32; var param_63: vec2; var param_64: vec2; @@ -13722,8 +13925,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_75: f32; var param_76: f32; - var phi_2534_: bool; - var phi_2595_: bool; + var phi_2564_: bool; + var phi_2625_: bool; let _e258 = (*lightIndex); let _e262 = point_shadow.slots[_e258][0u]; @@ -13788,13 +13991,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e358); - phi_2534_ = _e359; + phi_2564_ = _e359; if _e359 { let _e361 = a_1[0u]; let _e363 = a_1[2u]; - phi_2534_ = (_e361 >= _e363); + phi_2564_ = (_e361 >= _e363); } - let _e366 = phi_2534_; + let _e366 = phi_2564_; if _e366 { let _e368 = toFragment[0u]; face = select(1i, 0i, (_e368 > 0f)); @@ -13824,12 +14027,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2595_ = _e409; + phi_2625_ = _e409; if !(_e409) { let _e412 = ndc[1u]; - phi_2595_ = (abs(_e412) > 1f); + phi_2625_ = (abs(_e412) > 1f); } - let _e416 = phi_2595_; + let _e416 = phi_2625_; if _e416 { return 1f; } @@ -13879,15 +14082,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_8 = vec3(0f, 0f, 1f); } else { let _e471 = radius_1; let _e474 = point_shadow.params2_[2u]; let _e478 = blocker_1; let _e479 = range_3; - local_7 = vec3(clamp((_e471 / max(_e474, 0.000001f)), 0f, 1f), clamp((_e478 / _e479), 0f, 1f), 0f); + local_8 = vec3(clamp((_e471 / max(_e474, 0.000001f)), 0f, 1f), clamp((_e478 / _e479), 0f, 1f), 0f); } - let _e483 = local_7; + let _e483 = local_8; g_debug_surface = _e483; } let _e484 = radius_1; @@ -14079,8 +14282,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_8: mat4x4; var local_9: mat4x4; + var local_10: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -14094,8 +14297,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_10: f32; var local_11: f32; + var local_12: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -14129,11 +14332,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_95: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3812_: bool; - var phi_3823_: bool; - var phi_3984_: bool; - var phi_3991_: bool; - var phi_3998_: bool; + var phi_3842_: bool; + var phi_3853_: bool; + var phi_4014_: bool; + var phi_4021_: bool; + var phi_4028_: bool; let _e272 = frag_info.shadow_params[3u]; strength_1 = _e272; @@ -14154,25 +14357,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e293 = cascadeCount; let _e294 = (_e293 > 1i); - phi_3812_ = _e294; + phi_3842_ = _e294; if _e294 { let _e295 = viewDistance; let _e298 = frag_info.shadow_cascades[0u]; - phi_3812_ = (_e295 > _e298); + phi_3842_ = (_e295 > _e298); } - let _e301 = phi_3812_; + let _e301 = phi_3842_; if _e301 { cascade = 1i; } let _e302 = cascadeCount; let _e303 = (_e302 > 2i); - phi_3823_ = _e303; + phi_3853_ = _e303; if _e303 { let _e304 = viewDistance; let _e307 = frag_info.shadow_cascades[1u]; - phi_3823_ = (_e304 > _e307); + phi_3853_ = (_e304 > _e307); } - let _e310 = phi_3823_; + let _e310 = phi_3853_; if _e310 { cascade = 2i; } @@ -14197,20 +14400,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e319 = which; if (_e319 == 0i) { let _e322 = frag_info.shadow_matrix; - local_8 = _e322; + local_9 = _e322; } else { let _e323 = which; if (_e323 == 1i) { let _e326 = frag_info.shadow_matrix_far; - local_9 = _e326; + local_10 = _e326; } else { let _e328 = frag_info.shadow_matrix_farthest; - local_9 = _e328; + local_10 = _e328; } - let _e329 = local_9; - local_8 = _e329; + let _e329 = local_10; + local_9 = _e329; } - let _e330 = local_8; + let _e330 = local_9; matrix = _e330; let _e333 = matrix[0][0u]; let _e336 = matrix[1][0u]; @@ -14260,24 +14463,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e417 * 0.5f) + 0.5f), (0.5f - (_e421 * 0.5f))); let _e426 = inTile[0u]; let _e427 = (_e426 < 0f); - phi_3984_ = _e427; + phi_4014_ = _e427; if !(_e427) { let _e430 = inTile[0u]; - phi_3984_ = (_e430 > 1f); + phi_4014_ = (_e430 > 1f); } - let _e433 = phi_3984_; - phi_3991_ = _e433; + let _e433 = phi_4014_; + phi_4021_ = _e433; if !(_e433) { let _e436 = inTile[1u]; - phi_3991_ = (_e436 < 0f); + phi_4021_ = (_e436 < 0f); } - let _e439 = phi_3991_; - phi_3998_ = _e439; + let _e439 = phi_4021_; + phi_4028_ = _e439; if !(_e439) { let _e442 = inTile[1u]; - phi_3998_ = (_e442 > 1f); + phi_4028_ = (_e442 > 1f); } - let _e445 = phi_3998_; + let _e445 = phi_4028_; if _e445 { continue; } @@ -14334,20 +14537,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e514 = cascade; if (_e514 == 0i) { let _e518 = frag_info.shadow_bias[0u]; - local_10 = _e518; + local_11 = _e518; } else { let _e519 = cascade; if (_e519 == 1i) { let _e523 = frag_info.shadow_bias[1u]; - local_11 = _e523; + local_12 = _e523; } else { let _e526 = frag_info.shadow_bias[2u]; - local_11 = _e526; + local_12 = _e526; } - let _e527 = local_11; - local_10 = _e527; + let _e527 = local_12; + local_11 = _e527; } - let _e528 = local_10; + let _e528 = local_11; bias = _e528; let _e531 = frag_info.shadow_params[0u]; let _e534 = frag_info.shadow_cascades[3u]; @@ -14589,7 +14792,7 @@ fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_2: ptr>, b: ptr; var len: f32; var angle_1: f32; - var local_12: vec3; + var local_13: vec3; let _e210 = (*a_2); let _e211 = (*b); @@ -14604,11 +14807,11 @@ fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_2: ptr>, b: ptr(0f, 0f, 0f); + local_13 = vec3(0f, 0f, 0f); } - let _e228 = local_12; + let _e228 = local_13; return _e228; } @@ -14625,7 +14828,7 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: var along: vec3; var alongLength: f32; var t1_: vec3; - var local_13: vec3; + var local_14: vec3; var t2_: vec3; var minv: mat3x3; var i_7: i32; @@ -14642,7 +14845,7 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: var param_112: vec3; var len_1: f32; var z_1: f32; - var local_14: f32; + var local_15: f32; var sphere: f32; var param_113: f32; var param_114: f32; @@ -14679,13 +14882,13 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: if (_e272 > 0.00001f) { let _e274 = along; let _e275 = alongLength; - local_13 = (_e274 / vec3(_e275)); + local_14 = (_e274 / vec3(_e275)); } else { let _e278 = (*n_2); let _e280 = (*n_2)[2u]; - local_13 = normalize(cross(_e278, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e280) < 0.999f))))); + local_14 = normalize(cross(_e278, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e280) < 0.999f))))); } - let _e287 = local_13; + let _e287 = local_14; t1_ = _e287; let _e288 = (*n_2); let _e289 = t1_; @@ -14749,11 +14952,11 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: if (_e362 > 0.000000001f) { let _e365 = f_2[2u]; let _e366 = len_1; - local_14 = (_e365 / _e366); + local_15 = (_e365 / _e366); } else { - local_14 = 0f; + local_15 = 0f; } - let _e368 = local_14; + let _e368 = local_15; z_1 = _e368; let _e369 = z_1; let _e372 = len_1; @@ -14777,7 +14980,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_15: vec3; + var local_16: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -14819,15 +15022,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e254 > 0f) { let _e256 = (*mirror); let _e257 = t_1; - local_15 = (_e256 * _e257); + local_16 = (_e256 * _e257); } else { let _e259 = toPlane; let _e260 = n_3; let _e261 = toPlane; let _e262 = n_3; - local_15 = (_e259 - (_e260 * dot(_e261, _e262))); + local_16 = (_e259 - (_e260 * dot(_e261, _e262))); } - let _e266 = local_15; + let _e266 = local_16; onPlane = _e266; } let _e267 = onPlane; @@ -14861,7 +15064,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> var angle_2: f32; var axis_1: vec3; var len_2: f32; - var local_16: f32; + var local_17: f32; let _e213 = (*a_3); let _e214 = (*a_3); @@ -14883,11 +15086,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> let _e238 = axis_1; let _e239 = (*n_4); let _e242 = len_2; - local_16 = ((_e237 * dot(_e238, _e239)) / _e242); + local_17 = ((_e237 * dot(_e238, _e239)) / _e242); } else { - local_16 = 0f; + local_17 = 0f; } - let _e244 = local_16; + let _e244 = local_17; return _e244; } @@ -14898,15 +15101,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr; var b_2: vec3; - var local_17: i32; + var local_18: i32; var ha: f32; var hb: f32; var d_4: f32; var q: vec3; - var local_18: f32; + var local_19: f32; var aAbove: bool; var bAbove: bool; - var local_19: f32; + var local_20: f32; var param_116: vec3; var param_117: vec3; var param_118: vec3; @@ -14926,12 +15129,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr 0.000000000001f) { let _e255 = ha; let _e256 = d_4; - local_18 = (_e255 / _e256); + local_19 = (_e255 / _e256); } else { - local_18 = 0f; + local_19 = 0f; } - let _e258 = local_18; + let _e258 = local_19; q = (_e248 + ((_e249 - _e250) * _e258)); let _e261 = ha; aAbove = (_e261 > 0f); @@ -14974,11 +15177,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_20: f32; var local_21: f32; var local_22: f32; + var local_23: f32; let _e210 = (*slot_1); let _e211 = (*slot_1); @@ -15025,28 +15228,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 let _e215 = lane; if (_e215 == 0i) { let _e218 = (*four)[0u]; - local_20 = _e218; + local_21 = _e218; } else { let _e219 = lane; if (_e219 == 1i) { let _e222 = (*four)[1u]; - local_21 = _e222; + local_22 = _e222; } else { let _e223 = lane; if (_e223 == 2i) { let _e226 = (*four)[2u]; - local_22 = _e226; + local_23 = _e226; } else { let _e228 = (*four)[3u]; - local_22 = _e228; + local_23 = _e228; } - let _e229 = local_22; - local_21 = _e229; + let _e229 = local_23; + local_22 = _e229; } - let _e230 = local_21; - local_20 = _e230; + let _e230 = local_22; + local_21 = _e230; } - let _e231 = local_20; + let _e231 = local_21; return _e231; } @@ -15157,12 +15360,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_23: f32; + var local_24: f32; var param_130: i32; var param_131: i32; var v_2: f32; var u_1: f32; - var local_24: f32; + var local_25: f32; var param_132: f32; var param_133: i32; var type_42: f32; @@ -15172,12 +15375,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_2: array, 4>; var behind: bool; var formFactor: f32; - var local_25: f32; + var local_26: f32; var param_134: array, 4>; var param_135: vec3; var area: f32; var radiance: f32; - var local_26: f32; + var local_27: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -15190,7 +15393,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_140: vec3; var toPoint: vec3; var pointDistance: f32; - var local_27: vec3; + var local_28: vec3; var param_141: vec3; var param_142: vec3; var param_143: f32; @@ -15229,14 +15432,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e283 = slot_5; param_130 = _e283; let _e284 = ClusterRow_u0028_i1_u003b((¶m_130)); - local_23 = _e284; + local_24 = _e284; } else { let _e285 = slot_5; param_131 = _e285; let _e286 = LightListRow_u0028_i1_u003b((¶m_131)); - local_23 = _e286; + local_24 = _e286; } - let _e287 = local_23; + let _e287 = local_24; listRow = _e287; let _e288 = listRow; let _e292 = light_list_info.list[2u]; @@ -15264,14 +15467,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e318 = listRow; param_132 = _e318; let _e319 = InSlots_u0028_f1_u003b((¶m_132)); - local_24 = select(1f, 0f, _e319); + local_25 = select(1f, 0f, _e319); } else { let _e321 = slot_5; param_133 = _e321; let _e322 = LightListScale_u0028_i1_u003b((¶m_133)); - local_24 = _e322; + local_25 = _e322; } - let _e323 = local_24; + let _e323 = local_25; let _e325 = color_1[3u]; color_1[3u] = (_e325 * _e323); } @@ -15308,16 +15511,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e364, cross(_e365, _e366)) >= 0f); let _e370 = behind; if _e370 { - local_25 = 0f; + local_26 = 0f; } else { let _e371 = corners_2; param_134 = _e371; let _e373 = (*s_6).n; param_135 = _e373; let _e374 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_134), (¶m_135)); - local_25 = _e374; + local_26 = _e374; } - let _e375 = local_25; + let _e375 = local_26; formFactor = _e375; let _e376 = halfWidth_1; let _e377 = halfHeight_1; @@ -15325,11 +15528,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e381 = area; if (_e381 > 0.000000001f) { let _e383 = area; - local_26 = (1f / _e383); + local_27 = (1f / _e383); } else { - local_26 = 0f; + local_27 = 0f; } - let _e385 = local_26; + let _e385 = local_27; radiance = _e385; let _e386 = toCentre; distance_2 = length(_e386); @@ -15372,12 +15575,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e427 > 0.000001f) { let _e429 = toPoint; let _e430 = pointDistance; - local_27 = (_e429 / vec3(_e430)); + local_28 = (_e429 / vec3(_e430)); } else { let _e434 = (*s_6).n; - local_27 = _e434; + local_28 = _e434; } - let _e435 = local_27; + let _e435 = local_28; light_3.l = _e435; let _e438 = light_3.l; let _e440 = (*s_6).v; @@ -15484,7 +15687,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_28: f32; + var local_29: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -15512,15 +15715,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e264 = clip_1[3u]; let _e265 = near_1; if (_e264 <= _e265) { - local_28 = 0f; + local_29 = 0f; } else { let _e268 = clip_1[3u]; let _e269 = near_1; let _e274 = light_list_info.cluster_depth[1u]; let _e278 = grid[2u]; - local_28 = clamp(floor((log((_e268 / _e269)) * _e274)), 0f, (_e278 - 1f)); + local_29 = clamp(floor((log((_e268 / _e269)) * _e274)), 0f, (_e278 - 1f)); } - let _e281 = local_28; + let _e281 = local_29; tz = _e281; let _e282 = tx; let _e283 = ty; @@ -15574,7 +15777,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_151: Surface; var visibility: f32; var param_152: i32; - var local_29: f32; + var local_30: f32; var param_153: Surface; var param_154: LightSample; var param_155: i32; @@ -15624,11 +15827,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e243 = i_9; param_158 = _e243; let _e244 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_156), (¶m_157), (¶m_158)); - local_29 = (_e239 * _e244); + local_30 = (_e239 * _e244); } else { - local_29 = 1f; + local_30 = 1f; } - let _e246 = local_29; + let _e246 = local_30; visibility = _e246; let _e247 = visibility; if (_e247 <= 0f) { @@ -15936,7 +16139,7 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var param_178: f32; var param_179: vec2; - var local_30: vec3; + var local_31: vec3; let _e251 = irradiance_info.origin; origin_2 = _e251.xyz; @@ -16111,11 +16314,11 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr 0f) { let _e479 = total_2; let _e480 = weights; - local_30 = (_e479 / vec3(_e480)); + local_31 = (_e479 / vec3(_e480)); } else { - local_30 = vec3(0f, 0f, 0f); + local_31 = vec3(0f, 0f, 0f); } - let _e483 = local_30; + let _e483 = local_31; return _e483; } @@ -16162,6 +16365,22 @@ fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d return; } +fn TowardsEye_u0028_() -> vec3 { + var local_32: vec3; + + let _e205 = Orthographic_u0028_(); + if _e205 { + let _e207 = fog_info.forward; + local_32 = -(_e207.xyz); + } else { + let _e211 = fog_info.eye; + let _e213 = v_world_position_1; + local_32 = normalize((_e211.xyz - _e213)); + } + let _e216 = local_32; + return _e216; +} + fn ReadSurface_u0028_() -> Surface { var texel_6: vec4; var s_13: Surface; @@ -16222,27 +16441,26 @@ fn ReadSurface_u0028_() -> Surface { let _e272 = s_13.n; s_13.n = -(_e272); } - let _e276 = frag_info.camera_position; - let _e278 = v_world_position_1; - s_13.v = normalize((_e276.xyz - _e278)); - let _e283 = s_13.n; - let _e285 = s_13.v; - s_13.n_dot_v = max(dot(_e283, _e285), 0.0001f); - let _e291 = frag_info.material[0u]; - s_13.metallic = clamp(_e291, 0f, 1f); - let _e296 = frag_info.material[1u]; - s_13.roughness = clamp(_e296, 0.02f, 1f); - let _e300 = frag_info.ambient_ground; - let _e303 = frag_info.ambient_sky; - let _e307 = s_13.n[1u]; - let _e314 = frag_info.material[2u]; - s_13.ambient = (mix(_e300.xyz, _e303.xyz, vec3(((_e307 * 0.5f) + 0.5f))) * _e314); - let _e319 = frag_info.frame_params[0u]; - s_13.exposure = max(_e319, 0f); + let _e275 = TowardsEye_u0028_(); + s_13.v = _e275; + let _e278 = s_13.n; + let _e280 = s_13.v; + s_13.n_dot_v = max(dot(_e278, _e280), 0.0001f); + let _e286 = frag_info.material[0u]; + s_13.metallic = clamp(_e286, 0f, 1f); + let _e291 = frag_info.material[1u]; + s_13.roughness = clamp(_e291, 0.02f, 1f); + let _e295 = frag_info.ambient_ground; + let _e298 = frag_info.ambient_sky; + let _e302 = s_13.n[1u]; + let _e309 = frag_info.material[2u]; + s_13.ambient = (mix(_e295.xyz, _e298.xyz, vec3(((_e302 * 0.5f) + 0.5f))) * _e309); + let _e314 = frag_info.frame_params[0u]; + s_13.exposure = max(_e314, 0f); s_13.occlusion = 1f; s_13.emissive = vec3(0f, 0f, 0f); - let _e324 = s_13; - return _e324; + let _e319 = s_13; + return _e319; } fn main_1() { @@ -16425,7 +16643,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), @@ -16434,6 +16652,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, eye: vec4, forward: vec4, + projection: vec4, } struct LightListInfo { @@ -16878,9 +17102,9 @@ var g_thickness: f32; var g_transmittance: vec3; var g_iridescence: f32; var g_irid_fresnel: vec3; -var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; +var v_world_position_1: vec3; var frag_albedo: vec4; var frag_surface: vec4; var v_normal_1: vec3; @@ -17105,10 +17329,25 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { return; } +fn Orthographic_u0028_() -> bool { + let _e272 = fog_info.projection[0u]; + return (_e272 > 0.5f); +} + fn EyeDistance_u0028_() -> f32 { - let _e270 = v_world_position_1; - let _e272 = fog_info.eye; - return distance(_e270, _e272.xyz); + var local_1: f32; + + let _e271 = Orthographic_u0028_(); + if _e271 { + let _e272 = ViewDepth_u0028_(); + local_1 = max(_e272, 0f); + } else { + let _e274 = v_world_position_1; + let _e276 = fog_info.eye; + local_1 = distance(_e274, _e276.xyz); + } + let _e279 = local_1; + return _e279; } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -17116,7 +17355,7 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var d: f32; var falloff: f32; var k: f32; - var local_1: f32; + var local_2: f32; let _e278 = fog_info.fog[3u]; density = _e278; @@ -17139,12 +17378,12 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { if (abs(_e296) > 0.001f) { let _e299 = k; let _e303 = k; - local_1 = ((1f - exp(-(_e299))) / _e303); + local_2 = ((1f - exp(-(_e299))) / _e303); } else { let _e305 = k; - local_1 = (1f - (0.5f * _e305)); + local_2 = (1f - (0.5f * _e305)); } - let _e308 = local_1; + let _e308 = local_2; let _e309 = density; density = (_e309 * _e308); } @@ -17185,17 +17424,17 @@ fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { } fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { - var phi_2955_: bool; + var phi_2985_: bool; let _e271 = (*c); let _e272 = (*c); let _e274 = any((_e271 != _e272)); - phi_2955_ = _e274; + phi_2985_ = _e274; if !(_e274) { let _e276 = (*c); - phi_2955_ = any((abs(_e276) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + phi_2985_ = any((abs(_e276) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); } - let _e281 = phi_2955_; + let _e281 = phi_2985_; return _e281; } @@ -17223,9 +17462,9 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var grey: f32; var param_8: vec3; var param_9: vec3; - var local_2: vec3; + var local_3: vec3; var weight_2: f32; - var local_3: f32; + var local_4: f32; var param_10: vec3; var param_11: f32; @@ -17295,12 +17534,12 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ param_9 = _e353; let _e354 = NonFinite_u0028_vf3_u003b((¶m_9)); if _e354 { - local_2 = vec3(1f, 0f, 1f); + local_3 = vec3(1f, 0f, 1f); } else { let _e355 = grey; - local_2 = vec3((_e355 * 0.5f)); + local_3 = vec3((_e355 * 0.5f)); } - let _e358 = local_2; + let _e358 = local_3; shown = _e358; } } @@ -17313,11 +17552,11 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e359 = g_premultiply; if _e359 { let _e361 = (*s).alpha; - local_3 = _e361; + local_4 = _e361; } else { - local_3 = 1f; + local_4 = 1f; } - let _e362 = local_3; + let _e362 = local_4; weight_2 = _e362; let _e363 = shown; param_10 = _e363; @@ -17380,7 +17619,7 @@ fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr; var len: f32; var angle: f32; - var local_4: vec3; + var local_5: vec3; let _e276 = (*a_1); let _e277 = (*b); @@ -17395,11 +17634,11 @@ fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr(0f, 0f, 0f); + local_5 = vec3(0f, 0f, 0f); } - let _e294 = local_4; + let _e294 = local_5; return _e294; } @@ -17428,7 +17667,7 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: var along: vec3; var alongLength: f32; var t1_: vec3; - var local_5: vec3; + var local_6: vec3; var t2_: vec3; var minv: mat3x3; var i: i32; @@ -17445,7 +17684,7 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: var param_25: vec3; var len_1: f32; var z_1: f32; - var local_6: f32; + var local_7: f32; var sphere: f32; var param_26: f32; var param_27: f32; @@ -17482,13 +17721,13 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: if (_e338 > 0.00001f) { let _e340 = along; let _e341 = alongLength; - local_5 = (_e340 / vec3(_e341)); + local_6 = (_e340 / vec3(_e341)); } else { let _e344 = (*n_1); let _e346 = (*n_1)[2u]; - local_5 = normalize(cross(_e344, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e346) < 0.999f))))); + local_6 = normalize(cross(_e344, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e346) < 0.999f))))); } - let _e353 = local_5; + let _e353 = local_6; t1_ = _e353; let _e354 = (*n_1); let _e355 = t1_; @@ -17552,11 +17791,11 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: if (_e428 > 0.000000001f) { let _e431 = f[2u]; let _e432 = len_1; - local_6 = (_e431 / _e432); + local_7 = (_e431 / _e432); } else { - local_6 = 0f; + local_7 = 0f; } - let _e434 = local_6; + let _e434 = local_7; z_1 = _e434; let _e435 = z_1; let _e438 = len_1; @@ -17706,7 +17945,7 @@ fn FonAverage_u0028_f1_u003b(r_1: ptr) -> f32 { fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, r_2: ptr, mu_i: ptr, mu_o: ptr, l_dot_v: ptr) -> vec3 { var s_2: f32; var s_over_t: f32; - var local_7: f32; + var local_8: f32; var af: f32; var single: vec3; var average_1: f32; @@ -17728,12 +17967,12 @@ fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_8: vec2; + var local_9: vec2; var atlas: vec2; let _e279 = point_shadow.params[0u]; @@ -18154,8 +18393,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv_1: ptr(0.16666667f, 0.16666667f)); let _e295 = point_shadow.params3_[0u]; @@ -18313,19 +18552,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_9: vec2; + var local_10: vec2; let _e272 = (*rows); if (_e272 > 0f) { let _e275 = gl_FragCoord_1[0u]; let _e276 = (*rows); let _e278 = gl_FragCoord_1[1u]; - local_9 = vec2(_e275, (_e276 - _e278)); + local_10 = vec2(_e275, (_e276 - _e278)); } else { let _e281 = gl_FragCoord_1; - local_9 = _e281.xy; + local_10 = _e281.xy; } - let _e283 = local_9; + let _e283 = local_10; return _e283; } @@ -18364,7 +18603,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_11: vec3; var lit_1: f32; var param_110: vec2; var param_111: vec2; @@ -18381,8 +18620,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_122: f32; var param_123: f32; - var phi_2646_: bool; - var phi_2707_: bool; + var phi_2676_: bool; + var phi_2737_: bool; let _e324 = (*lightIndex); let _e328 = point_shadow.slots[_e324][0u]; @@ -18447,13 +18686,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e424); - phi_2646_ = _e425; + phi_2676_ = _e425; if _e425 { let _e427 = a_2[0u]; let _e429 = a_2[2u]; - phi_2646_ = (_e427 >= _e429); + phi_2676_ = (_e427 >= _e429); } - let _e432 = phi_2646_; + let _e432 = phi_2676_; if _e432 { let _e434 = toFragment[0u]; face = select(1i, 0i, (_e434 > 0f)); @@ -18483,12 +18722,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2707_ = _e475; + phi_2737_ = _e475; if !(_e475) { let _e478 = ndc[1u]; - phi_2707_ = (abs(_e478) > 1f); + phi_2737_ = (abs(_e478) > 1f); } - let _e482 = phi_2707_; + let _e482 = phi_2737_; if _e482 { return 1f; } @@ -18538,15 +18777,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_11 = vec3(0f, 0f, 1f); } else { let _e537 = radius_1; let _e540 = point_shadow.params2_[2u]; let _e544 = blocker_1; let _e545 = range_3; - local_10 = vec3(clamp((_e537 / max(_e540, 0.000001f)), 0f, 1f), clamp((_e544 / _e545), 0f, 1f), 0f); + local_11 = vec3(clamp((_e537 / max(_e540, 0.000001f)), 0f, 1f), clamp((_e544 / _e545), 0f, 1f), 0f); } - let _e549 = local_10; + let _e549 = local_11; g_debug_surface = _e549; } let _e550 = radius_1; @@ -18738,8 +18977,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_11: mat4x4; var local_12: mat4x4; + var local_13: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -18753,8 +18992,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_13: f32; var local_14: f32; + var local_15: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -18788,11 +19027,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_142: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_4028_: bool; - var phi_4039_: bool; - var phi_4200_: bool; - var phi_4207_: bool; - var phi_4214_: bool; + var phi_4058_: bool; + var phi_4069_: bool; + var phi_4230_: bool; + var phi_4237_: bool; + var phi_4244_: bool; let _e338 = frag_info.shadow_params[3u]; strength_1 = _e338; @@ -18813,25 +19052,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e359 = cascadeCount; let _e360 = (_e359 > 1i); - phi_4028_ = _e360; + phi_4058_ = _e360; if _e360 { let _e361 = viewDistance; let _e364 = frag_info.shadow_cascades[0u]; - phi_4028_ = (_e361 > _e364); + phi_4058_ = (_e361 > _e364); } - let _e367 = phi_4028_; + let _e367 = phi_4058_; if _e367 { cascade = 1i; } let _e368 = cascadeCount; let _e369 = (_e368 > 2i); - phi_4039_ = _e369; + phi_4069_ = _e369; if _e369 { let _e370 = viewDistance; let _e373 = frag_info.shadow_cascades[1u]; - phi_4039_ = (_e370 > _e373); + phi_4069_ = (_e370 > _e373); } - let _e376 = phi_4039_; + let _e376 = phi_4069_; if _e376 { cascade = 2i; } @@ -18856,20 +19095,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e385 = which; if (_e385 == 0i) { let _e388 = frag_info.shadow_matrix; - local_11 = _e388; + local_12 = _e388; } else { let _e389 = which; if (_e389 == 1i) { let _e392 = frag_info.shadow_matrix_far; - local_12 = _e392; + local_13 = _e392; } else { let _e394 = frag_info.shadow_matrix_farthest; - local_12 = _e394; + local_13 = _e394; } - let _e395 = local_12; - local_11 = _e395; + let _e395 = local_13; + local_12 = _e395; } - let _e396 = local_11; + let _e396 = local_12; matrix = _e396; let _e399 = matrix[0][0u]; let _e402 = matrix[1][0u]; @@ -18919,24 +19158,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e483 * 0.5f) + 0.5f), (0.5f - (_e487 * 0.5f))); let _e492 = inTile[0u]; let _e493 = (_e492 < 0f); - phi_4200_ = _e493; + phi_4230_ = _e493; if !(_e493) { let _e496 = inTile[0u]; - phi_4200_ = (_e496 > 1f); + phi_4230_ = (_e496 > 1f); } - let _e499 = phi_4200_; - phi_4207_ = _e499; + let _e499 = phi_4230_; + phi_4237_ = _e499; if !(_e499) { let _e502 = inTile[1u]; - phi_4207_ = (_e502 < 0f); + phi_4237_ = (_e502 < 0f); } - let _e505 = phi_4207_; - phi_4214_ = _e505; + let _e505 = phi_4237_; + phi_4244_ = _e505; if !(_e505) { let _e508 = inTile[1u]; - phi_4214_ = (_e508 > 1f); + phi_4244_ = (_e508 > 1f); } - let _e511 = phi_4214_; + let _e511 = phi_4244_; if _e511 { continue; } @@ -18993,20 +19232,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e580 = cascade; if (_e580 == 0i) { let _e584 = frag_info.shadow_bias[0u]; - local_13 = _e584; + local_14 = _e584; } else { let _e585 = cascade; if (_e585 == 1i) { let _e589 = frag_info.shadow_bias[1u]; - local_14 = _e589; + local_15 = _e589; } else { let _e592 = frag_info.shadow_bias[2u]; - local_14 = _e592; + local_15 = _e592; } - let _e593 = local_14; - local_13 = _e593; + let _e593 = local_15; + local_14 = _e593; } - let _e594 = local_13; + let _e594 = local_14; bias = _e594; let _e597 = frag_info.shadow_params[0u]; let _e600 = frag_info.shadow_cascades[3u]; @@ -19251,7 +19490,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_15: vec3; + var local_16: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -19293,15 +19532,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e320 > 0f) { let _e322 = (*mirror); let _e323 = t_1; - local_15 = (_e322 * _e323); + local_16 = (_e322 * _e323); } else { let _e325 = toPlane; let _e326 = n_3; let _e327 = toPlane; let _e328 = n_3; - local_15 = (_e325 - (_e326 * dot(_e327, _e328))); + local_16 = (_e325 - (_e326 * dot(_e327, _e328))); } - let _e332 = local_15; + let _e332 = local_16; onPlane = _e332; } let _e333 = onPlane; @@ -19335,7 +19574,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> var angle_2: f32; var axis_1: vec3; var len_2: f32; - var local_16: f32; + var local_17: f32; let _e279 = (*a_3); let _e280 = (*a_3); @@ -19357,11 +19596,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> let _e304 = axis_1; let _e305 = (*n_4); let _e308 = len_2; - local_16 = ((_e303 * dot(_e304, _e305)) / _e308); + local_17 = ((_e303 * dot(_e304, _e305)) / _e308); } else { - local_16 = 0f; + local_17 = 0f; } - let _e310 = local_16; + let _e310 = local_17; return _e310; } @@ -19372,15 +19611,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr; var b_2: vec3; - var local_17: i32; + var local_18: i32; var ha: f32; var hb: f32; var d_5: f32; var q: vec3; - var local_18: f32; + var local_19: f32; var aAbove: bool; var bAbove: bool; - var local_19: f32; + var local_20: f32; var param_146: vec3; var param_147: vec3; var param_148: vec3; @@ -19400,12 +19639,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr 0.000000000001f) { let _e321 = ha; let _e322 = d_5; - local_18 = (_e321 / _e322); + local_19 = (_e321 / _e322); } else { - local_18 = 0f; + local_19 = 0f; } - let _e324 = local_18; + let _e324 = local_19; q = (_e314 + ((_e315 - _e316) * _e324)); let _e327 = ha; aAbove = (_e327 > 0f); @@ -19448,11 +19687,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr>, slot_2: ptr) -> f32 { var lane: i32; - var local_20: f32; var local_21: f32; var local_22: f32; + var local_23: f32; let _e276 = (*slot_2); let _e277 = (*slot_2); @@ -19499,28 +19738,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_2 let _e281 = lane; if (_e281 == 0i) { let _e284 = (*four)[0u]; - local_20 = _e284; + local_21 = _e284; } else { let _e285 = lane; if (_e285 == 1i) { let _e288 = (*four)[1u]; - local_21 = _e288; + local_22 = _e288; } else { let _e289 = lane; if (_e289 == 2i) { let _e292 = (*four)[2u]; - local_22 = _e292; + local_23 = _e292; } else { let _e294 = (*four)[3u]; - local_22 = _e294; + local_23 = _e294; } - let _e295 = local_22; - local_21 = _e295; + let _e295 = local_23; + local_22 = _e295; } - let _e296 = local_21; - local_20 = _e296; + let _e296 = local_22; + local_21 = _e296; } - let _e297 = local_20; + let _e297 = local_21; return _e297; } @@ -19631,12 +19870,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_6: i32; var clustered: bool; var listRow: f32; - var local_23: f32; + var local_24: f32; var param_160: i32; var param_161: i32; var v_2: f32; var u_1: f32; - var local_24: f32; + var local_25: f32; var param_162: f32; var param_163: i32; var type_45: f32; @@ -19646,12 +19885,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_2: array, 4>; var behind: bool; var formFactor: f32; - var local_25: f32; + var local_26: f32; var param_164: array, 4>; var param_165: vec3; var area: f32; var radiance: f32; - var local_26: f32; + var local_27: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -19664,7 +19903,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_170: vec3; var toPoint: vec3; var pointDistance: f32; - var local_27: vec3; + var local_28: vec3; var param_171: vec3; var param_172: vec3; var param_173: f32; @@ -19703,14 +19942,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e349 = slot_6; param_160 = _e349; let _e350 = ClusterRow_u0028_i1_u003b((¶m_160)); - local_23 = _e350; + local_24 = _e350; } else { let _e351 = slot_6; param_161 = _e351; let _e352 = LightListRow_u0028_i1_u003b((¶m_161)); - local_23 = _e352; + local_24 = _e352; } - let _e353 = local_23; + let _e353 = local_24; listRow = _e353; let _e354 = listRow; let _e358 = light_list_info.list[2u]; @@ -19738,14 +19977,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e384 = listRow; param_162 = _e384; let _e385 = InSlots_u0028_f1_u003b((¶m_162)); - local_24 = select(1f, 0f, _e385); + local_25 = select(1f, 0f, _e385); } else { let _e387 = slot_6; param_163 = _e387; let _e388 = LightListScale_u0028_i1_u003b((¶m_163)); - local_24 = _e388; + local_25 = _e388; } - let _e389 = local_24; + let _e389 = local_25; let _e391 = color_1[3u]; color_1[3u] = (_e391 * _e389); } @@ -19782,16 +20021,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e430, cross(_e431, _e432)) >= 0f); let _e436 = behind; if _e436 { - local_25 = 0f; + local_26 = 0f; } else { let _e437 = corners_2; param_164 = _e437; let _e439 = (*s_7).n; param_165 = _e439; let _e440 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_164), (¶m_165)); - local_25 = _e440; + local_26 = _e440; } - let _e441 = local_25; + let _e441 = local_26; formFactor = _e441; let _e442 = halfWidth_1; let _e443 = halfHeight_1; @@ -19799,11 +20038,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e447 = area; if (_e447 > 0.000000001f) { let _e449 = area; - local_26 = (1f / _e449); + local_27 = (1f / _e449); } else { - local_26 = 0f; + local_27 = 0f; } - let _e451 = local_26; + let _e451 = local_27; radiance = _e451; let _e452 = toCentre; distance_2 = length(_e452); @@ -19846,12 +20085,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e493 > 0.000001f) { let _e495 = toPoint; let _e496 = pointDistance; - local_27 = (_e495 / vec3(_e496)); + local_28 = (_e495 / vec3(_e496)); } else { let _e500 = (*s_7).n; - local_27 = _e500; + local_28 = _e500; } - let _e501 = local_27; + let _e501 = local_28; light_4.l = _e501; let _e504 = light_4.l; let _e506 = (*s_7).v; @@ -19960,7 +20199,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_28: f32; + var local_29: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -19988,15 +20227,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e330 = clip_1[3u]; let _e331 = near_1; if (_e330 <= _e331) { - local_28 = 0f; + local_29 = 0f; } else { let _e334 = clip_1[3u]; let _e335 = near_1; let _e340 = light_list_info.cluster_depth[1u]; let _e344 = grid[2u]; - local_28 = clamp(floor((log((_e334 / _e335)) * _e340)), 0f, (_e344 - 1f)); + local_29 = clamp(floor((log((_e334 / _e335)) * _e340)), 0f, (_e344 - 1f)); } - let _e347 = local_28; + let _e347 = local_29; tz = _e347; let _e348 = tx; let _e349 = ty; @@ -20050,7 +20289,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_181: Surface; var visibility: f32; var param_182: i32; - var local_29: f32; + var local_30: f32; var param_183: Surface; var param_184: LightSample; var param_185: i32; @@ -20100,11 +20339,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e309 = i_9; param_188 = _e309; let _e310 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_186), (¶m_187), (¶m_188)); - local_29 = (_e305 * _e310); + local_30 = (_e305 * _e310); } else { - local_29 = 1f; + local_30 = 1f; } - let _e312 = local_29; + let _e312 = local_30; visibility = _e312; let _e313 = visibility; if (_e313 <= 0f) { @@ -20228,31 +20467,31 @@ fn IorInfinite_u0028_() -> bool { } fn DispersionSpread_u0028_f1_u003b(ior: ptr) -> f32 { - var local_30: f32; + var local_31: f32; let _e272 = IorInfinite_u0028_(); if _e272 { - local_30 = 0f; + local_31 = 0f; } else { let _e273 = (*ior); let _e278 = layer_info.transmission[3u]; - local_30 = (((_e273 - 1f) * 0.025f) * _e278); + local_31 = (((_e273 - 1f) * 0.025f) * _e278); } - let _e280 = local_30; + let _e280 = local_31; return _e280; } fn RefractionIor_u0028_() -> f32 { - var local_31: f32; + var local_32: f32; let _e271 = IorInfinite_u0028_(); if _e271 { - local_31 = 10000f; + local_32 = 10000f; } else { let _e274 = layer_info.coat[2u]; - local_31 = max(_e274, 1f); + local_32 = max(_e274, 1f); } - let _e276 = local_31; + let _e276 = local_32; return _e276; } @@ -20264,11 +20503,11 @@ fn SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3 var lod_1: f32; var thin: bool; var red: vec3; - var local_32: vec3; - var green: vec3; var local_33: vec3; - var blue: vec3; + var green: vec3; var local_34: vec3; + var blue: vec3; + var local_35: vec3; var param_196: vec3; var param_197: f32; var param_198: vec3; @@ -20294,40 +20533,40 @@ fn SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3 let _e314 = thin; if _e314 { let _e316 = (*s_9).v; - local_32 = -(_e316); + local_33 = -(_e316); } else { let _e319 = (*s_9).v; let _e322 = (*s_9).n; let _e323 = ior_1; let _e324 = spread_1; - local_32 = refract(-(_e319), _e322, (1f / max((_e323 - _e324), 1f))); + local_33 = refract(-(_e319), _e322, (1f / max((_e323 - _e324), 1f))); } - let _e329 = local_32; + let _e329 = local_33; red = _e329; let _e330 = thin; if _e330 { let _e332 = (*s_9).v; - local_33 = -(_e332); + local_34 = -(_e332); } else { let _e335 = (*s_9).v; let _e338 = (*s_9).n; let _e339 = ior_1; - local_33 = refract(-(_e335), _e338, (1f / _e339)); + local_34 = refract(-(_e335), _e338, (1f / _e339)); } - let _e342 = local_33; + let _e342 = local_34; green = _e342; let _e343 = thin; if _e343 { let _e345 = (*s_9).v; - local_34 = -(_e345); + local_35 = -(_e345); } else { let _e348 = (*s_9).v; let _e351 = (*s_9).n; let _e352 = ior_1; let _e353 = spread_1; - local_34 = refract(-(_e348), _e351, (1f / (_e352 + _e353))); + local_35 = refract(-(_e348), _e351, (1f / (_e352 + _e353))); } - let _e357 = local_34; + let _e357 = local_35; blue = _e357; let _e358 = v_world_position_1; let _e359 = red; @@ -20360,11 +20599,11 @@ fn TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u var lod_2: f32; var thin_1: bool; var red_1: vec3; - var local_35: vec3; - var green_1: vec3; var local_36: vec3; - var blue_1: vec3; + var green_1: vec3; var local_37: vec3; + var blue_1: vec3; + var local_38: vec3; let _e283 = RefractionIor_u0028_(); ior_2 = _e283; @@ -20381,40 +20620,40 @@ fn TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u let _e297 = thin_1; if _e297 { let _e299 = (*s_10).v; - local_35 = -(_e299); + local_36 = -(_e299); } else { let _e302 = (*s_10).v; let _e305 = (*s_10).n; let _e306 = ior_2; let _e307 = spread_2; - local_35 = refract(-(_e302), _e305, (1f / max((_e306 - _e307), 1f))); + local_36 = refract(-(_e302), _e305, (1f / max((_e306 - _e307), 1f))); } - let _e312 = local_35; + let _e312 = local_36; red_1 = _e312; let _e313 = thin_1; if _e313 { let _e315 = (*s_10).v; - local_36 = -(_e315); + local_37 = -(_e315); } else { let _e318 = (*s_10).v; let _e321 = (*s_10).n; let _e322 = ior_2; - local_36 = refract(-(_e318), _e321, (1f / _e322)); + local_37 = refract(-(_e318), _e321, (1f / _e322)); } - let _e325 = local_36; + let _e325 = local_37; green_1 = _e325; let _e326 = thin_1; if _e326 { let _e328 = (*s_10).v; - local_37 = -(_e328); + local_38 = -(_e328); } else { let _e331 = (*s_10).v; let _e334 = (*s_10).n; let _e335 = ior_2; let _e336 = spread_2; - local_37 = refract(-(_e331), _e334, (1f / (_e335 + _e336))); + local_38 = refract(-(_e331), _e334, (1f / (_e335 + _e336))); } - let _e340 = local_37; + let _e340 = local_38; blue_1 = _e340; let _e341 = red_1; let _e342 = lod_2; @@ -20518,7 +20757,7 @@ fn FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b(film: ptr; - var local_38: vec3; + var local_39: vec3; let _e299 = (*film); let _e300 = (*thickness); @@ -20622,12 +20861,12 @@ fn FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b(film: ptr(1f, 1f, 1f); + local_39 = vec3(1f, 1f, 1f); } else { let _e439 = total_2; - local_38 = max(_e439, vec3(0f, 0f, 0f)); + local_39 = max(_e439, vec3(0f, 0f, 0f)); } - let _e441 = local_38; + let _e441 = local_39; return _e441; } @@ -20661,13 +20900,13 @@ fn ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_216: f32; var t_2: vec3; var usable: bool; - var local_39: vec3; + var local_40: vec3; var b_4: vec3; var turn_2: vec2; var along_1: vec3; - var local_40: f32; - var local_41: vec3; - var phi_4888_: bool; + var local_41: f32; + var local_42: vec3; + var phi_4918_: bool; let _e283 = (*s_12).n_dot_v; let _e287 = g_sheen_roughness; @@ -20693,11 +20932,11 @@ fn ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e317 = usable; if _e317 { let _e318 = t_2; - local_39 = normalize(_e318); + local_40 = normalize(_e318); } else { - local_39 = vec3(1f, 0f, 0f); + local_40 = vec3(1f, 0f, 0f); } - let _e320 = local_39; + let _e320 = local_40; t_2 = _e320; let _e322 = (*s_12).n; let _e323 = t_2; @@ -20716,30 +20955,30 @@ fn ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e341 = turn_2[1u]; along_1 = ((_e335 * _e337) + (_e339 * _e341)); let _e344 = usable; - phi_4888_ = _e344; + phi_4918_ = _e344; if _e344 { let _e345 = along_1; let _e346 = along_1; - phi_4888_ = (dot(_e345, _e346) > 0.000000000001f); + phi_4918_ = (dot(_e345, _e346) > 0.000000000001f); } - let _e350 = phi_4888_; + let _e350 = phi_4918_; if _e350 { let _e353 = layer_info.anisotropy[0u]; - local_40 = clamp(_e353, 0f, 1f); + local_41 = clamp(_e353, 0f, 1f); } else { - local_40 = 0f; + local_41 = 0f; } - let _e355 = local_40; + let _e355 = local_41; g_aniso = _e355; let _e356 = g_aniso; if (_e356 > 0f) { let _e358 = along_1; - local_41 = normalize(_e358); + local_42 = normalize(_e358); } else { let _e360 = t_2; - local_41 = _e360; + local_42 = _e360; } - let _e361 = local_41; + let _e361 = local_42; g_aniso_t = _e361; let _e363 = (*s_12).n; let _e364 = g_aniso_t; @@ -20831,16 +21070,16 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var det: f32; var flip: f32; var front: vec3; - var local_42: vec3; + var local_43: vec3; var turned: vec3; var turns: bool; - var local_43: vec3; var local_44: vec3; + var local_45: vec3; var sampled: vec3; - var phi_3707_: bool; - var phi_3714_: bool; - var phi_3721_: bool; - var phi_3728_: bool; + var phi_3737_: bool; + var phi_3744_: bool; + var phi_3751_: bool; + var phi_3758_: bool; param_221 = 2i; let _e286 = MapUv_u0028_i1_u003b((¶m_221)); @@ -20878,12 +21117,12 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e328 = gl_FrontFacing_1; if _e328 { let _e329 = b_5; - local_42 = _e329; + local_43 = _e329; } else { let _e330 = b_5; - local_42 = -(_e330); + local_43 = -(_e330); } - let _e332 = local_42; + let _e332 = local_43; front = _e332; let _e333 = t_3; let _e335 = m_2[3u]; @@ -20893,41 +21132,41 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ turned = (((_e333 * _e335) + (_e337 * _e339)) * _e342); let _e345 = m_2[1u]; let _e346 = (_e345 != 0f); - phi_3707_ = _e346; + phi_3737_ = _e346; if !(_e346) { let _e349 = m_2[2u]; - phi_3707_ = (_e349 != 0f); + phi_3737_ = (_e349 != 0f); } - let _e352 = phi_3707_; - phi_3714_ = _e352; + let _e352 = phi_3737_; + phi_3744_ = _e352; if !(_e352) { let _e355 = m_2[0u]; - phi_3714_ = (_e355 < 0f); + phi_3744_ = (_e355 < 0f); } - let _e358 = phi_3714_; - phi_3721_ = _e358; + let _e358 = phi_3744_; + phi_3751_ = _e358; if !(_e358) { let _e361 = m_2[3u]; - phi_3721_ = (_e361 < 0f); + phi_3751_ = (_e361 < 0f); } - let _e364 = phi_3721_; - phi_3728_ = _e364; + let _e364 = phi_3751_; + phi_3758_ = _e364; if _e364 { let _e365 = turned; let _e366 = turned; - phi_3728_ = (dot(_e365, _e366) > 0.000000000001f); + phi_3758_ = (dot(_e365, _e366) > 0.000000000001f); } - let _e370 = phi_3728_; + let _e370 = phi_3758_; turns = _e370; let _e371 = turns; if _e371 { let _e372 = turned; - local_43 = normalize(_e372); + local_44 = normalize(_e372); } else { let _e374 = t_3; - local_43 = _e374; + local_44 = _e374; } - let _e375 = local_43; + let _e375 = local_44; t_3 = _e375; let _e376 = turns; if _e376 { @@ -20935,12 +21174,12 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e379 = t_3; let _e382 = v_tangent_1[3u]; let _e384 = flip; - local_44 = ((cross(_e378, _e379) * _e382) * _e384); + local_45 = ((cross(_e378, _e379) * _e382) * _e384); } else { let _e386 = b_5; - local_44 = _e386; + local_45 = _e386; } - let _e387 = local_44; + let _e387 = local_45; b_5 = _e387; let _e388 = gl_FrontFacing_1; if !(_e388) { @@ -21104,7 +21343,7 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_5: ptr; var param_234: f32; var param_235: vec2; - var local_45: vec3; + var local_46: vec3; let _e317 = irradiance_info.origin; origin_2 = _e317.xyz; @@ -21279,11 +21518,11 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_5: ptr 0f) { let _e545 = total_3; let _e546 = weights; - local_45 = (_e545 / vec3(_e546)); + local_46 = (_e545 / vec3(_e546)); } else { - local_45 = vec3(0f, 0f, 0f); + local_46 = vec3(0f, 0f, 0f); } - let _e549 = local_45; + let _e549 = local_46; return _e549; } @@ -21336,9 +21575,9 @@ fn ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ var param_242: vec3; var ior_3: f32; var r_5: f32; - var local_46: f32; + var local_47: f32; var distance_5: f32; - var local_47: vec3; + var local_48: vec3; var fc: f32; let _e280 = v_texcoord_1; @@ -21361,13 +21600,13 @@ fn ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ ior_3 = max(_e302, 1f); let _e304 = IorInfinite_u0028_(); if _e304 { - local_46 = 1f; + local_47 = 1f; } else { let _e305 = ior_3; let _e307 = ior_3; - local_46 = ((_e305 - 1f) / (_e307 + 1f)); + local_47 = ((_e305 - 1f) / (_e307 + 1f)); } - let _e310 = local_46; + let _e310 = local_47; r_5 = _e310; let _e311 = r_5; let _e312 = r_5; @@ -21395,11 +21634,11 @@ fn ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ let _e361 = layer_info.attenuation; let _e364 = g_thickness; let _e365 = distance_5; - local_47 = pow(max(_e361.xyz, vec3(0.0001f, 0.0001f, 0.0001f)), vec3((_e364 / _e365))); + local_48 = pow(max(_e361.xyz, vec3(0.0001f, 0.0001f, 0.0001f)), vec3((_e364 / _e365))); } else { - local_47 = vec3(1f, 1f, 1f); + local_48 = vec3(1f, 1f, 1f); } - let _e369 = local_47; + let _e369 = local_48; g_transmittance = _e369; let _e372 = layer_info.iridescence[0u]; g_iridescence = clamp(_e372, 0f, 1f); @@ -21416,6 +21655,22 @@ fn ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ return; } +fn TowardsEye_u0028_() -> vec3 { + var local_49: vec3; + + let _e271 = Orthographic_u0028_(); + if _e271 { + let _e273 = fog_info.forward; + local_49 = -(_e273.xyz); + } else { + let _e277 = fog_info.eye; + let _e279 = v_world_position_1; + local_49 = normalize((_e277.xyz - _e279)); + } + let _e282 = local_49; + return _e282; +} + fn ReadSurface_u0028_() -> Surface { var texel_6: vec4; var param_243: i32; @@ -21478,27 +21733,26 @@ fn ReadSurface_u0028_() -> Surface { let _e339 = s_19.n; s_19.n = -(_e339); } - let _e343 = frag_info.camera_position; - let _e345 = v_world_position_1; - s_19.v = normalize((_e343.xyz - _e345)); - let _e350 = s_19.n; - let _e352 = s_19.v; - s_19.n_dot_v = max(dot(_e350, _e352), 0.0001f); - let _e358 = frag_info.material[0u]; - s_19.metallic = clamp(_e358, 0f, 1f); - let _e363 = frag_info.material[1u]; - s_19.roughness = clamp(_e363, 0.02f, 1f); - let _e367 = frag_info.ambient_ground; - let _e370 = frag_info.ambient_sky; - let _e374 = s_19.n[1u]; - let _e381 = frag_info.material[2u]; - s_19.ambient = (mix(_e367.xyz, _e370.xyz, vec3(((_e374 * 0.5f) + 0.5f))) * _e381); - let _e386 = frag_info.frame_params[0u]; - s_19.exposure = max(_e386, 0f); + let _e342 = TowardsEye_u0028_(); + s_19.v = _e342; + let _e345 = s_19.n; + let _e347 = s_19.v; + s_19.n_dot_v = max(dot(_e345, _e347), 0.0001f); + let _e353 = frag_info.material[0u]; + s_19.metallic = clamp(_e353, 0f, 1f); + let _e358 = frag_info.material[1u]; + s_19.roughness = clamp(_e358, 0.02f, 1f); + let _e362 = frag_info.ambient_ground; + let _e365 = frag_info.ambient_sky; + let _e369 = s_19.n[1u]; + let _e376 = frag_info.material[2u]; + s_19.ambient = (mix(_e362.xyz, _e365.xyz, vec3(((_e369 * 0.5f) + 0.5f))) * _e376); + let _e381 = frag_info.frame_params[0u]; + s_19.exposure = max(_e381, 0f); s_19.occlusion = 1f; s_19.emissive = vec3(0f, 0f, 0f); - let _e391 = s_19; - return _e391; + let _e386 = s_19; + return _e386; } fn main_1() { @@ -21537,7 +21791,7 @@ fn main_1() { var multiple: vec3; var reflects: vec3; var through: vec3; - var local_48: vec3; + var local_50: vec3; var param_256: Surface; var param_257: f32; var coatPrefiltered: vec3; @@ -21728,7 +21982,7 @@ fn main_1() { reflects = ((mix(_e490, _e491, vec3(_e492)) * _e496) + vec3((_e498 * _e500))); let _e504 = SceneColourBound_u0028_(); if _e504 { - local_48 = vec3(0f, 0f, 0f); + local_50 = vec3(0f, 0f, 0f); } else { let _e505 = s_20; param_256 = _e505; @@ -21737,9 +21991,9 @@ fn main_1() { let _e507 = TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_f1_u003b((¶m_256), (¶m_257)); let _e508 = g_transmittance; let _e510 = reflects; - local_48 = ((_e507 * _e508) * (vec3(1f, 1f, 1f) - min(_e510, vec3(1f, 1f, 1f)))); + local_50 = ((_e507 * _e508) * (vec3(1f, 1f, 1f) - min(_e510, vec3(1f, 1f, 1f)))); } - let _e514 = local_48; + let _e514 = local_50; through = _e514; let _e515 = diffuseColor_1; let _e516 = through; @@ -21862,7 +22116,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), @@ -21871,6 +22125,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, eye: vec4, forward: vec4, + projection: vec4, } struct LightListInfo { @@ -22366,9 +22626,9 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; -var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; +var v_world_position_1: vec3; var frag_albedo: vec4; var frag_surface: vec4; var v_normal_1: vec3; @@ -22570,10 +22830,25 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { return; } +fn Orthographic_u0028_() -> bool { + let _e188 = fog_info.projection[0u]; + return (_e188 > 0.5f); +} + fn EyeDistance_u0028_() -> f32 { - let _e186 = v_world_position_1; - let _e188 = fog_info.eye; - return distance(_e186, _e188.xyz); + var local_1: f32; + + let _e187 = Orthographic_u0028_(); + if _e187 { + let _e188 = ViewDepth_u0028_(); + local_1 = max(_e188, 0f); + } else { + let _e190 = v_world_position_1; + let _e192 = fog_info.eye; + local_1 = distance(_e190, _e192.xyz); + } + let _e195 = local_1; + return _e195; } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -22581,7 +22856,7 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var d: f32; var falloff: f32; var k: f32; - var local_1: f32; + var local_2: f32; let _e194 = fog_info.fog[3u]; density = _e194; @@ -22604,12 +22879,12 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { if (abs(_e212) > 0.001f) { let _e215 = k; let _e219 = k; - local_1 = ((1f - exp(-(_e215))) / _e219); + local_2 = ((1f - exp(-(_e215))) / _e219); } else { let _e221 = k; - local_1 = (1f - (0.5f * _e221)); + local_2 = (1f - (0.5f * _e221)); } - let _e224 = local_1; + let _e224 = local_2; let _e225 = density; density = (_e225 * _e224); } @@ -22650,17 +22925,17 @@ fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { } fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { - var phi_2504_: bool; + var phi_2534_: bool; let _e187 = (*c); let _e188 = (*c); let _e190 = any((_e187 != _e188)); - phi_2504_ = _e190; + phi_2534_ = _e190; if !(_e190) { let _e192 = (*c); - phi_2504_ = any((abs(_e192) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + phi_2534_ = any((abs(_e192) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); } - let _e197 = phi_2504_; + let _e197 = phi_2534_; return _e197; } @@ -22673,9 +22948,9 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var grey: f32; var param_7: vec3; var param_8: vec3; - var local_2: vec3; + var local_3: vec3; var weight_2: f32; - var local_3: f32; + var local_4: f32; var param_9: vec3; var param_10: f32; @@ -22744,12 +23019,12 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ param_8 = _e268; let _e269 = NonFinite_u0028_vf3_u003b((¶m_8)); if _e269 { - local_2 = vec3(1f, 0f, 1f); + local_3 = vec3(1f, 0f, 1f); } else { let _e270 = grey; - local_2 = vec3((_e270 * 0.5f)); + local_3 = vec3((_e270 * 0.5f)); } - let _e273 = local_2; + let _e273 = local_3; shown = _e273; } } @@ -22762,11 +23037,11 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e274 = g_premultiply; if _e274 { let _e276 = (*s).alpha; - local_3 = _e276; + local_4 = _e276; } else { - local_3 = 1f; + local_4 = 1f; } - let _e277 = local_3; + let _e277 = local_4; weight_2 = _e277; let _e278 = shown; param_9 = _e278; @@ -22863,7 +23138,7 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_4: vec2; + var local_5: vec2; var atlas: vec2; let _e195 = point_shadow.params[0u]; @@ -22872,8 +23147,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr(0.16666667f, 0.16666667f)); let _e211 = point_shadow.params3_[0u]; @@ -23031,19 +23306,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_5: vec2; + var local_6: vec2; let _e188 = (*rows); if (_e188 > 0f) { let _e191 = gl_FragCoord_1[0u]; let _e192 = (*rows); let _e194 = gl_FragCoord_1[1u]; - local_5 = vec2(_e191, (_e192 - _e194)); + local_6 = vec2(_e191, (_e192 - _e194)); } else { let _e197 = gl_FragCoord_1; - local_5 = _e197.xy; + local_6 = _e197.xy; } - let _e199 = local_5; + let _e199 = local_6; return _e199; } @@ -23082,7 +23357,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_7: vec3; var lit_1: f32; var param_37: vec2; var param_38: vec2; @@ -23099,8 +23374,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_49: f32; var param_50: f32; - var phi_2193_: bool; - var phi_2254_: bool; + var phi_2223_: bool; + var phi_2284_: bool; let _e240 = (*lightIndex); let _e244 = point_shadow.slots[_e240][0u]; @@ -23165,13 +23440,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e340); - phi_2193_ = _e341; + phi_2223_ = _e341; if _e341 { let _e343 = a[0u]; let _e345 = a[2u]; - phi_2193_ = (_e343 >= _e345); + phi_2223_ = (_e343 >= _e345); } - let _e348 = phi_2193_; + let _e348 = phi_2223_; if _e348 { let _e350 = toFragment[0u]; face = select(1i, 0i, (_e350 > 0f)); @@ -23201,12 +23476,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2254_ = _e391; + phi_2284_ = _e391; if !(_e391) { let _e394 = ndc[1u]; - phi_2254_ = (abs(_e394) > 1f); + phi_2284_ = (abs(_e394) > 1f); } - let _e398 = phi_2254_; + let _e398 = phi_2284_; if _e398 { return 1f; } @@ -23256,15 +23531,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_7 = vec3(0f, 0f, 1f); } else { let _e453 = radius_1; let _e456 = point_shadow.params2_[2u]; let _e460 = blocker_1; let _e461 = range_3; - local_6 = vec3(clamp((_e453 / max(_e456, 0.000001f)), 0f, 1f), clamp((_e460 / _e461), 0f, 1f), 0f); + local_7 = vec3(clamp((_e453 / max(_e456, 0.000001f)), 0f, 1f), clamp((_e460 / _e461), 0f, 1f), 0f); } - let _e465 = local_6; + let _e465 = local_7; g_debug_surface = _e465; } let _e466 = radius_1; @@ -23456,8 +23731,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_7: mat4x4; var local_8: mat4x4; + var local_9: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -23471,8 +23746,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_9: f32; var local_10: f32; + var local_11: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -23506,11 +23781,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_69: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3473_: bool; - var phi_3484_: bool; - var phi_3645_: bool; - var phi_3652_: bool; - var phi_3659_: bool; + var phi_3503_: bool; + var phi_3514_: bool; + var phi_3675_: bool; + var phi_3682_: bool; + var phi_3689_: bool; let _e254 = frag_info.shadow_params[3u]; strength_1 = _e254; @@ -23531,25 +23806,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e275 = cascadeCount; let _e276 = (_e275 > 1i); - phi_3473_ = _e276; + phi_3503_ = _e276; if _e276 { let _e277 = viewDistance; let _e280 = frag_info.shadow_cascades[0u]; - phi_3473_ = (_e277 > _e280); + phi_3503_ = (_e277 > _e280); } - let _e283 = phi_3473_; + let _e283 = phi_3503_; if _e283 { cascade = 1i; } let _e284 = cascadeCount; let _e285 = (_e284 > 2i); - phi_3484_ = _e285; + phi_3514_ = _e285; if _e285 { let _e286 = viewDistance; let _e289 = frag_info.shadow_cascades[1u]; - phi_3484_ = (_e286 > _e289); + phi_3514_ = (_e286 > _e289); } - let _e292 = phi_3484_; + let _e292 = phi_3514_; if _e292 { cascade = 2i; } @@ -23574,20 +23849,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e301 = which; if (_e301 == 0i) { let _e304 = frag_info.shadow_matrix; - local_7 = _e304; + local_8 = _e304; } else { let _e305 = which; if (_e305 == 1i) { let _e308 = frag_info.shadow_matrix_far; - local_8 = _e308; + local_9 = _e308; } else { let _e310 = frag_info.shadow_matrix_farthest; - local_8 = _e310; + local_9 = _e310; } - let _e311 = local_8; - local_7 = _e311; + let _e311 = local_9; + local_8 = _e311; } - let _e312 = local_7; + let _e312 = local_8; matrix = _e312; let _e315 = matrix[0][0u]; let _e318 = matrix[1][0u]; @@ -23637,24 +23912,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e399 * 0.5f) + 0.5f), (0.5f - (_e403 * 0.5f))); let _e408 = inTile[0u]; let _e409 = (_e408 < 0f); - phi_3645_ = _e409; + phi_3675_ = _e409; if !(_e409) { let _e412 = inTile[0u]; - phi_3645_ = (_e412 > 1f); + phi_3675_ = (_e412 > 1f); } - let _e415 = phi_3645_; - phi_3652_ = _e415; + let _e415 = phi_3675_; + phi_3682_ = _e415; if !(_e415) { let _e418 = inTile[1u]; - phi_3652_ = (_e418 < 0f); + phi_3682_ = (_e418 < 0f); } - let _e421 = phi_3652_; - phi_3659_ = _e421; + let _e421 = phi_3682_; + phi_3689_ = _e421; if !(_e421) { let _e424 = inTile[1u]; - phi_3659_ = (_e424 > 1f); + phi_3689_ = (_e424 > 1f); } - let _e427 = phi_3659_; + let _e427 = phi_3689_; if _e427 { continue; } @@ -23711,20 +23986,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e496 = cascade; if (_e496 == 0i) { let _e500 = frag_info.shadow_bias[0u]; - local_9 = _e500; + local_10 = _e500; } else { let _e501 = cascade; if (_e501 == 1i) { let _e505 = frag_info.shadow_bias[1u]; - local_10 = _e505; + local_11 = _e505; } else { let _e508 = frag_info.shadow_bias[2u]; - local_10 = _e508; + local_11 = _e508; } - let _e509 = local_10; - local_9 = _e509; + let _e509 = local_11; + local_10 = _e509; } - let _e510 = local_9; + let _e510 = local_10; bias = _e510; let _e513 = frag_info.shadow_params[0u]; let _e516 = frag_info.shadow_cascades[3u]; @@ -23969,7 +24244,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_11: vec3; + var local_12: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -24011,15 +24286,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e236 > 0f) { let _e238 = (*mirror); let _e239 = t_1; - local_11 = (_e238 * _e239); + local_12 = (_e238 * _e239); } else { let _e241 = toPlane; let _e242 = n_2; let _e243 = toPlane; let _e244 = n_2; - local_11 = (_e241 - (_e242 * dot(_e243, _e244))); + local_12 = (_e241 - (_e242 * dot(_e243, _e244))); } - let _e248 = local_11; + let _e248 = local_12; onPlane = _e248; } let _e249 = onPlane; @@ -24053,7 +24328,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_12: f32; + var local_13: f32; let _e195 = (*a_1); let _e196 = (*a_1); @@ -24075,11 +24350,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> let _e220 = axis; let _e221 = (*n_3); let _e224 = len; - local_12 = ((_e219 * dot(_e220, _e221)) / _e224); + local_13 = ((_e219 * dot(_e220, _e221)) / _e224); } else { - local_12 = 0f; + local_13 = 0f; } - let _e226 = local_12; + let _e226 = local_13; return _e226; } @@ -24090,15 +24365,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_13: i32; + var local_14: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_14: f32; + var local_15: f32; var aAbove: bool; var bAbove: bool; - var local_15: f32; + var local_16: f32; var param_73: vec3; var param_74: vec3; var param_75: vec3; @@ -24118,12 +24393,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr 0.000000000001f) { let _e237 = ha; let _e238 = d_3; - local_14 = (_e237 / _e238); + local_15 = (_e237 / _e238); } else { - local_14 = 0f; + local_15 = 0f; } - let _e240 = local_14; + let _e240 = local_15; q = (_e230 + ((_e231 - _e232) * _e240)); let _e243 = ha; aAbove = (_e243 > 0f); @@ -24166,11 +24441,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_16: f32; var local_17: f32; var local_18: f32; + var local_19: f32; let _e192 = (*slot_1); let _e193 = (*slot_1); @@ -24217,28 +24492,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 let _e197 = lane; if (_e197 == 0i) { let _e200 = (*four)[0u]; - local_16 = _e200; + local_17 = _e200; } else { let _e201 = lane; if (_e201 == 1i) { let _e204 = (*four)[1u]; - local_17 = _e204; + local_18 = _e204; } else { let _e205 = lane; if (_e205 == 2i) { let _e208 = (*four)[2u]; - local_18 = _e208; + local_19 = _e208; } else { let _e210 = (*four)[3u]; - local_18 = _e210; + local_19 = _e210; } - let _e211 = local_18; - local_17 = _e211; + let _e211 = local_19; + local_18 = _e211; } - let _e212 = local_17; - local_16 = _e212; + let _e212 = local_18; + local_17 = _e212; } - let _e213 = local_16; + let _e213 = local_17; return _e213; } @@ -24349,12 +24624,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_19: f32; + var local_20: f32; var param_87: i32; var param_88: i32; var v_1: f32; var u_1: f32; - var local_20: f32; + var local_21: f32; var param_89: f32; var param_90: i32; var type_39: f32; @@ -24364,12 +24639,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_21: f32; + var local_22: f32; var param_91: array, 4>; var param_92: vec3; var area: f32; var radiance: f32; - var local_22: f32; + var local_23: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -24382,7 +24657,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_97: vec3; var toPoint: vec3; var pointDistance: f32; - var local_23: vec3; + var local_24: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; @@ -24417,14 +24692,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e261 = slot_5; param_87 = _e261; let _e262 = ClusterRow_u0028_i1_u003b((¶m_87)); - local_19 = _e262; + local_20 = _e262; } else { let _e263 = slot_5; param_88 = _e263; let _e264 = LightListRow_u0028_i1_u003b((¶m_88)); - local_19 = _e264; + local_20 = _e264; } - let _e265 = local_19; + let _e265 = local_20; listRow = _e265; let _e266 = listRow; let _e270 = light_list_info.list[2u]; @@ -24452,14 +24727,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e296 = listRow; param_89 = _e296; let _e297 = InSlots_u0028_f1_u003b((¶m_89)); - local_20 = select(1f, 0f, _e297); + local_21 = select(1f, 0f, _e297); } else { let _e299 = slot_5; param_90 = _e299; let _e300 = LightListScale_u0028_i1_u003b((¶m_90)); - local_20 = _e300; + local_21 = _e300; } - let _e301 = local_20; + let _e301 = local_21; let _e303 = color_1[3u]; color_1[3u] = (_e303 * _e301); } @@ -24496,16 +24771,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e342, cross(_e343, _e344)) >= 0f); let _e348 = behind; if _e348 { - local_21 = 0f; + local_22 = 0f; } else { let _e349 = corners_1; param_91 = _e349; let _e351 = (*s_4).n; param_92 = _e351; let _e352 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_91), (¶m_92)); - local_21 = _e352; + local_22 = _e352; } - let _e353 = local_21; + let _e353 = local_22; formFactor = _e353; let _e354 = halfWidth_1; let _e355 = halfHeight_1; @@ -24513,11 +24788,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e359 = area; if (_e359 > 0.000000001f) { let _e361 = area; - local_22 = (1f / _e361); + local_23 = (1f / _e361); } else { - local_22 = 0f; + local_23 = 0f; } - let _e363 = local_22; + let _e363 = local_23; radiance = _e363; let _e364 = toCentre; distance_2 = length(_e364); @@ -24560,12 +24835,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e405 > 0.000001f) { let _e407 = toPoint; let _e408 = pointDistance; - local_23 = (_e407 / vec3(_e408)); + local_24 = (_e407 / vec3(_e408)); } else { let _e412 = (*s_4).n; - local_23 = _e412; + local_24 = _e412; } - let _e413 = local_23; + let _e413 = local_24; light_3.l = _e413; let _e416 = light_3.l; let _e418 = (*s_4).v; @@ -24661,7 +24936,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_24: f32; + var local_25: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -24689,15 +24964,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e246 = clip_1[3u]; let _e247 = near_1; if (_e246 <= _e247) { - local_24 = 0f; + local_25 = 0f; } else { let _e250 = clip_1[3u]; let _e251 = near_1; let _e256 = light_list_info.cluster_depth[1u]; let _e260 = grid[2u]; - local_24 = clamp(floor((log((_e250 / _e251)) * _e256)), 0f, (_e260 - 1f)); + local_25 = clamp(floor((log((_e250 / _e251)) * _e256)), 0f, (_e260 - 1f)); } - let _e263 = local_24; + let _e263 = local_25; tz = _e263; let _e264 = tx; let _e265 = ty; @@ -24751,7 +25026,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_104: Surface; var visibility: f32; var param_105: i32; - var local_25: f32; + var local_26: f32; var param_106: Surface; var param_107: LightSample; var param_108: i32; @@ -24801,11 +25076,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e225 = i_8; param_111 = _e225; let _e226 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_109), (¶m_110), (¶m_111)); - local_25 = (_e221 * _e226); + local_26 = (_e221 * _e226); } else { - local_25 = 1f; + local_26 = 1f; } - let _e228 = local_25; + let _e228 = local_26; visibility = _e228; let _e229 = visibility; if (_e229 <= 0f) { @@ -25086,7 +25361,7 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var param_126: f32; var param_127: vec2; - var local_26: vec3; + var local_27: vec3; let _e233 = irradiance_info.origin; origin_2 = _e233.xyz; @@ -25261,11 +25536,11 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr 0f) { let _e461 = total_2; let _e462 = weights; - local_26 = (_e461 / vec3(_e462)); + local_27 = (_e461 / vec3(_e462)); } else { - local_26 = vec3(0f, 0f, 0f); + local_27 = vec3(0f, 0f, 0f); } - let _e465 = local_26; + let _e465 = local_27; return _e465; } @@ -25312,6 +25587,22 @@ fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d return; } +fn TowardsEye_u0028_() -> vec3 { + var local_28: vec3; + + let _e187 = Orthographic_u0028_(); + if _e187 { + let _e189 = fog_info.forward; + local_28 = -(_e189.xyz); + } else { + let _e193 = fog_info.eye; + let _e195 = v_world_position_1; + local_28 = normalize((_e193.xyz - _e195)); + } + let _e198 = local_28; + return _e198; +} + fn ReadSurface_u0028_() -> Surface { var texel_6: vec4; var s_11: Surface; @@ -25372,27 +25663,26 @@ fn ReadSurface_u0028_() -> Surface { let _e254 = s_11.n; s_11.n = -(_e254); } - let _e258 = frag_info.camera_position; - let _e260 = v_world_position_1; - s_11.v = normalize((_e258.xyz - _e260)); - let _e265 = s_11.n; - let _e267 = s_11.v; - s_11.n_dot_v = max(dot(_e265, _e267), 0.0001f); - let _e273 = frag_info.material[0u]; - s_11.metallic = clamp(_e273, 0f, 1f); - let _e278 = frag_info.material[1u]; - s_11.roughness = clamp(_e278, 0.02f, 1f); - let _e282 = frag_info.ambient_ground; - let _e285 = frag_info.ambient_sky; - let _e289 = s_11.n[1u]; - let _e296 = frag_info.material[2u]; - s_11.ambient = (mix(_e282.xyz, _e285.xyz, vec3(((_e289 * 0.5f) + 0.5f))) * _e296); - let _e301 = frag_info.frame_params[0u]; - s_11.exposure = max(_e301, 0f); + let _e257 = TowardsEye_u0028_(); + s_11.v = _e257; + let _e260 = s_11.n; + let _e262 = s_11.v; + s_11.n_dot_v = max(dot(_e260, _e262), 0.0001f); + let _e268 = frag_info.material[0u]; + s_11.metallic = clamp(_e268, 0f, 1f); + let _e273 = frag_info.material[1u]; + s_11.roughness = clamp(_e273, 0.02f, 1f); + let _e277 = frag_info.ambient_ground; + let _e280 = frag_info.ambient_sky; + let _e284 = s_11.n[1u]; + let _e291 = frag_info.material[2u]; + s_11.ambient = (mix(_e277.xyz, _e280.xyz, vec3(((_e284 * 0.5f) + 0.5f))) * _e291); + let _e296 = frag_info.frame_params[0u]; + s_11.exposure = max(_e296, 0f); s_11.occlusion = 1f; s_11.emissive = vec3(0f, 0f, 0f); - let _e306 = s_11; - return _e306; + let _e301 = s_11; + return _e301; } fn main_1() { @@ -25484,7 +25774,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), @@ -25493,6 +25783,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3, eye: vec4, forward: vec4, + projection: vec4, } struct FragmentOutput { @@ -25953,7 +26249,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), @@ -25962,6 +26258,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3; var nearH: vec4; var farH: vec4; - var origin: vec3; + var nearPoint: vec3; var along: vec3; - var surfaceDepth: f32; var cosine_1: f32; + var origin: vec3; + var surfaceDepth: f32; var toSurface: f32; var local_6: f32; var distance_: f32; @@ -37717,134 +38019,141 @@ fn main_1() { var param_7: f32; var shaft: vec3; - let _e90 = v_uv_1; - let _e91 = textureSample(scene_texture_tex, scene_texture_smp, _e90); - scene = _e91; - let _e94 = shaft_info.forward[3u]; - steps = i32((_e94 + 0.5f)); - let _e97 = steps; - if (_e97 < 1i) { - let _e99 = scene; - frag_color = _e99; + let _e91 = v_uv_1; + let _e92 = textureSample(scene_texture_tex, scene_texture_smp, _e91); + scene = _e92; + let _e95 = shaft_info.forward[3u]; + steps = i32((_e95 + 0.5f)); + let _e98 = steps; + if (_e98 < 1i) { + let _e100 = scene; + frag_color = _e100; return; } - let _e101 = v_uv_1[0u]; - let _e105 = v_uv_1[1u]; - xy = vec2(((_e101 * 2f) - 1f), (1f - (_e105 * 2f))); - let _e110 = shaft_info.inverse_view_projection; - let _e111 = xy; - nearH = (_e110 * vec4(_e111.x, _e111.y, 0f, 1f)); - let _e117 = shaft_info.inverse_view_projection; - let _e118 = xy; - farH = (_e117 * vec4(_e118.x, _e118.y, 1f, 1f)); - let _e123 = nearH; - let _e126 = nearH[3u]; - origin = (_e123.xyz / vec3(_e126)); - let _e129 = farH; - let _e132 = farH[3u]; - let _e135 = origin; - along = normalize(((_e129.xyz / vec3(_e132)) - _e135)); - let _e138 = v_uv_1; - let _e139 = textureSample(surface_texture_tex, surface_texture_smp, _e138); - surfaceDepth = _e139.w; - let _e141 = along; - let _e143 = shaft_info.forward; - cosine_1 = max(dot(_e141, _e143.xyz), 0.0001f); - let _e147 = surfaceDepth; - if (_e147 > 0f) { - let _e149 = surfaceDepth; - let _e150 = cosine_1; - local_6 = (_e149 / _e150); + let _e102 = v_uv_1[0u]; + let _e106 = v_uv_1[1u]; + xy = vec2(((_e102 * 2f) - 1f), (1f - (_e106 * 2f))); + let _e111 = shaft_info.inverse_view_projection; + let _e112 = xy; + nearH = (_e111 * vec4(_e112.x, _e112.y, 0f, 1f)); + let _e118 = shaft_info.inverse_view_projection; + let _e119 = xy; + farH = (_e118 * vec4(_e119.x, _e119.y, 1f, 1f)); + let _e124 = nearH; + let _e127 = nearH[3u]; + nearPoint = (_e124.xyz / vec3(_e127)); + let _e130 = farH; + let _e133 = farH[3u]; + let _e136 = nearPoint; + along = normalize(((_e130.xyz / vec3(_e133)) - _e136)); + let _e139 = along; + let _e141 = shaft_info.forward; + cosine_1 = max(dot(_e139, _e141.xyz), 0.0001f); + let _e145 = nearPoint; + let _e146 = along; + let _e147 = nearPoint; + let _e149 = shaft_info.camera; + let _e153 = shaft_info.forward; + let _e156 = cosine_1; + origin = (_e145 - (_e146 * (dot((_e147 - _e149.xyz), _e153.xyz) / _e156))); + let _e160 = v_uv_1; + let _e161 = textureSample(surface_texture_tex, surface_texture_smp, _e160); + surfaceDepth = _e161.w; + let _e163 = surfaceDepth; + if (_e163 > 0f) { + let _e165 = surfaceDepth; + let _e166 = cosine_1; + local_6 = (_e165 / _e166); } else { local_6 = 1000000000f; } - let _e152 = local_6; - toSurface = _e152; - let _e155 = shaft_info.camera[3u]; - let _e156 = toSurface; - distance_ = min(_e155, _e156); - let _e158 = distance_; - if (_e158 <= 0f) { - let _e160 = scene; - frag_color = _e160; + let _e168 = local_6; + toSurface = _e168; + let _e171 = shaft_info.camera[3u]; + let _e172 = toSurface; + distance_ = min(_e171, _e172); + let _e174 = distance_; + if (_e174 <= 0f) { + let _e176 = scene; + frag_color = _e176; return; } - let _e161 = distance_; - let _e162 = steps; - stride = (_e161 / f32(_e162)); - let _e165 = TargetFragCoord_u0028_(); - param_3 = _e165; - let _e166 = PixelNoise_u0028_vf2_u003b((¶m_3)); - let _e167 = stride; - offset = (_e166 * _e167); - let _e171 = shaft_info.scatter[3u]; - sigma = max(_e171, 0f); - let _e173 = sigma; - let _e175 = stride; - stepTransmittance = exp((-(_e173) * _e175)); - let _e178 = sigma; - let _e180 = offset; - transmittance = exp((-(_e178) * _e180)); - let _e184 = shaft_info.camera; - eye = _e184.xyz; + let _e177 = distance_; + let _e178 = steps; + stride = (_e177 / f32(_e178)); + let _e181 = TargetFragCoord_u0028_(); + param_3 = _e181; + let _e182 = PixelNoise_u0028_vf2_u003b((¶m_3)); + let _e183 = stride; + offset = (_e182 * _e183); + let _e187 = shaft_info.scatter[3u]; + sigma = max(_e187, 0f); + let _e189 = sigma; + let _e191 = stride; + stepTransmittance = exp((-(_e189) * _e191)); + let _e194 = sigma; + let _e196 = offset; + transmittance = exp((-(_e194) * _e196)); + let _e200 = shaft_info.camera; + eye = _e200.xyz; inscatter = 0f; i = 0i; loop { - let _e186 = i; - if (_e186 < 64i) { - let _e188 = i; - let _e189 = steps; - if (_e188 >= _e189) { + let _e202 = i; + if (_e202 < 64i) { + let _e204 = i; + let _e205 = steps; + if (_e204 >= _e205) { break; } - let _e191 = offset; - let _e192 = i; - let _e194 = stride; - travelled = (_e191 + (f32(_e192) * _e194)); - let _e197 = origin; - let _e198 = along; - let _e199 = travelled; - at_3 = (_e197 + (_e198 * _e199)); - let _e202 = at_3; - let _e203 = eye; - let _e206 = at_3; - param_4 = _e206; - param_5 = length((_e202 - _e203)); - let _e207 = LitAt_u0028_vf3_u003b_f1_u003b((¶m_4), (¶m_5)); - lit = _e207; - let _e208 = transmittance; - let _e209 = stepTransmittance; - let _e212 = lit; - let _e214 = inscatter; - inscatter = (_e214 + ((_e208 * (1f - _e209)) * _e212)); - let _e216 = stepTransmittance; - let _e217 = transmittance; - transmittance = (_e217 * _e216); + let _e207 = offset; + let _e208 = i; + let _e210 = stride; + travelled = (_e207 + (f32(_e208) * _e210)); + let _e213 = origin; + let _e214 = along; + let _e215 = travelled; + at_3 = (_e213 + (_e214 * _e215)); + let _e218 = at_3; + let _e219 = eye; + let _e222 = at_3; + param_4 = _e222; + param_5 = length((_e218 - _e219)); + let _e223 = LitAt_u0028_vf3_u003b_f1_u003b((¶m_4), (¶m_5)); + lit = _e223; + let _e224 = transmittance; + let _e225 = stepTransmittance; + let _e228 = lit; + let _e230 = inscatter; + inscatter = (_e230 + ((_e224 * (1f - _e225)) * _e228)); + let _e232 = stepTransmittance; + let _e233 = transmittance; + transmittance = (_e233 * _e232); continue; } else { break; } continuing { - let _e219 = i; - i = (_e219 + 1i); - } - } - let _e221 = along; - let _e223 = shaft_info.sun; - param_6 = dot(_e221, _e223.xyz); - let _e228 = shaft_info.sun[3u]; - param_7 = _e228; - let _e229 = HenyeyGreenstein_u0028_f1_u003b_f1_u003b((¶m_6), (¶m_7)); - phase = _e229; - let _e231 = shaft_info.scatter; - let _e233 = phase; - let _e234 = inscatter; - shaft = (_e231.xyz * (_e233 * _e234)); - let _e237 = scene; - let _e239 = shaft; - let _e240 = (_e237.xyz + _e239); - let _e242 = scene[3u]; - frag_color = vec4(_e240.x, _e240.y, _e240.z, _e242); + let _e235 = i; + i = (_e235 + 1i); + } + } + let _e237 = along; + let _e239 = shaft_info.sun; + param_6 = dot(_e237, _e239.xyz); + let _e244 = shaft_info.sun[3u]; + param_7 = _e244; + let _e245 = HenyeyGreenstein_u0028_f1_u003b_f1_u003b((¶m_6), (¶m_7)); + phase = _e245; + let _e247 = shaft_info.scatter; + let _e249 = phase; + let _e250 = inscatter; + shaft = (_e247.xyz * (_e249 * _e250)); + let _e253 = scene; + let _e255 = shaft; + let _e256 = (_e253.xyz + _e255); + let _e258 = scene[3u]; + frag_color = vec4(_e256.x, _e256.y, _e256.z, _e258); return; } @@ -44691,6 +45000,7 @@ struct FogInfo { fog: vec4, eye: vec4, forward: vec4, + projection: vec4, } struct LightListInfo { @@ -44747,9 +45057,9 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; -var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; +var v_world_position_1: vec3; var frag_albedo: vec4; var frag_surface: vec4; var v_normal_1: vec3; @@ -44929,10 +45239,25 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { return; } +fn Orthographic_u0028_() -> bool { + let _e178 = fog_info.projection[0u]; + return (_e178 > 0.5f); +} + fn EyeDistance_u0028_() -> f32 { - let _e176 = v_world_position_1; - let _e178 = fog_info.eye; - return distance(_e176, _e178.xyz); + var local_1: f32; + + let _e177 = Orthographic_u0028_(); + if _e177 { + let _e178 = ViewDepth_u0028_(); + local_1 = max(_e178, 0f); + } else { + let _e180 = v_world_position_1; + let _e182 = fog_info.eye; + local_1 = distance(_e180, _e182.xyz); + } + let _e185 = local_1; + return _e185; } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { @@ -44940,7 +45265,7 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var d: f32; var falloff: f32; var k: f32; - var local_1: f32; + var local_2: f32; let _e184 = fog_info.fog[3u]; density = _e184; @@ -44963,12 +45288,12 @@ fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { if (abs(_e202) > 0.001f) { let _e205 = k; let _e209 = k; - local_1 = ((1f - exp(-(_e205))) / _e209); + local_2 = ((1f - exp(-(_e205))) / _e209); } else { let _e211 = k; - local_1 = (1f - (0.5f * _e211)); + local_2 = (1f - (0.5f * _e211)); } - let _e214 = local_1; + let _e214 = local_2; let _e215 = density; density = (_e215 * _e214); } @@ -45009,17 +45334,17 @@ fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { } fn NonFinite_u0028_vf3_u003b(c: ptr>) -> bool { - var phi_2694_: bool; + var phi_2725_: bool; let _e177 = (*c); let _e178 = (*c); let _e180 = any((_e177 != _e178)); - phi_2694_ = _e180; + phi_2725_ = _e180; if !(_e180) { let _e182 = (*c); - phi_2694_ = any((abs(_e182) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); + phi_2725_ = any((abs(_e182) > vec3(300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f, 300000000000000000000000000000000000000f))); } - let _e187 = phi_2694_; + let _e187 = phi_2725_; return _e187; } @@ -45032,9 +45357,9 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var grey: f32; var param_7: vec3; var param_8: vec3; - var local_2: vec3; + var local_3: vec3; var weight_2: f32; - var local_3: f32; + var local_4: f32; var param_9: vec3; var param_10: f32; @@ -45103,12 +45428,12 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ param_8 = _e258; let _e259 = NonFinite_u0028_vf3_u003b((¶m_8)); if _e259 { - local_2 = vec3(1f, 0f, 1f); + local_3 = vec3(1f, 0f, 1f); } else { let _e260 = grey; - local_2 = vec3((_e260 * 0.5f)); + local_3 = vec3((_e260 * 0.5f)); } - let _e263 = local_2; + let _e263 = local_3; shown = _e263; } } @@ -45121,11 +45446,11 @@ fn WriteDebugView_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ let _e264 = g_premultiply; if _e264 { let _e266 = (*s).alpha; - local_3 = _e266; + local_4 = _e266; } else { - local_3 = 1f; + local_4 = 1f; } - let _e267 = local_3; + let _e267 = local_4; weight_2 = _e267; let _e268 = shown; param_9 = _e268; @@ -45200,7 +45525,7 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { var inset: f32; - var local_4: vec2; + var local_5: vec2; var atlas: vec2; let _e185 = point_shadow.params[0u]; @@ -45209,8 +45534,8 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr(0.16666667f, 0.16666667f)); let _e201 = point_shadow.params3_[0u]; @@ -45368,19 +45693,19 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { - var local_5: vec2; + var local_6: vec2; let _e178 = (*rows); if (_e178 > 0f) { let _e181 = gl_FragCoord_1[0u]; let _e182 = (*rows); let _e184 = gl_FragCoord_1[1u]; - local_5 = vec2(_e181, (_e182 - _e184)); + local_6 = vec2(_e181, (_e182 - _e184)); } else { let _e187 = gl_FragCoord_1; - local_5 = _e187.xy; + local_6 = _e187.xy; } - let _e189 = local_5; + let _e189 = local_6; return _e189; } @@ -45419,7 +45744,7 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; + var local_7: vec3; var lit_1: f32; var param_37: vec2; var param_38: vec2; @@ -45436,8 +45761,8 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_49: f32; var param_50: f32; - var phi_2383_: bool; - var phi_2444_: bool; + var phi_2414_: bool; + var phi_2475_: bool; let _e230 = (*lightIndex); let _e234 = point_shadow.slots[_e230][0u]; @@ -45502,13 +45827,13 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr= _e330); - phi_2383_ = _e331; + phi_2414_ = _e331; if _e331 { let _e333 = a[0u]; let _e335 = a[2u]; - phi_2383_ = (_e333 >= _e335); + phi_2414_ = (_e333 >= _e335); } - let _e338 = phi_2383_; + let _e338 = phi_2414_; if _e338 { let _e340 = toFragment[0u]; face = select(1i, 0i, (_e340 > 0f)); @@ -45538,12 +45863,12 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr 1f); - phi_2444_ = _e381; + phi_2475_ = _e381; if !(_e381) { let _e384 = ndc[1u]; - phi_2444_ = (abs(_e384) > 1f); + phi_2475_ = (abs(_e384) > 1f); } - let _e388 = phi_2444_; + let _e388 = phi_2475_; if _e388 { return 1f; } @@ -45593,15 +45918,15 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr(0f, 0f, 1f); + local_7 = vec3(0f, 0f, 1f); } else { let _e443 = radius_1; let _e446 = point_shadow.params2_[2u]; let _e450 = blocker_1; let _e451 = range_3; - local_6 = vec3(clamp((_e443 / max(_e446, 0.000001f)), 0f, 1f), clamp((_e450 / _e451), 0f, 1f), 0f); + local_7 = vec3(clamp((_e443 / max(_e446, 0.000001f)), 0f, 1f), clamp((_e450 / _e451), 0f, 1f), 0f); } - let _e455 = local_6; + let _e455 = local_7; g_debug_surface = _e455; } let _e456 = radius_1; @@ -45793,8 +46118,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var attempt: i32; var which: i32; var matrix: mat4x4; - var local_7: mat4x4; var local_8: mat4x4; + var local_9: mat4x4; var axisX: vec3; var axisY: vec3; var rowX: f32; @@ -45808,8 +46133,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var inTile: vec2; var cosSlope: f32; var bias: f32; - var local_9: f32; var local_10: f32; + var local_11: f32; var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; @@ -45843,11 +46168,11 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_69: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_2924_: bool; - var phi_2935_: bool; - var phi_3096_: bool; - var phi_3103_: bool; - var phi_3110_: bool; + var phi_2955_: bool; + var phi_2966_: bool; + var phi_3127_: bool; + var phi_3134_: bool; + var phi_3141_: bool; let _e244 = frag_info.shadow_params[3u]; strength_1 = _e244; @@ -45868,25 +46193,25 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf cascade = 0i; let _e265 = cascadeCount; let _e266 = (_e265 > 1i); - phi_2924_ = _e266; + phi_2955_ = _e266; if _e266 { let _e267 = viewDistance; let _e270 = frag_info.shadow_cascades[0u]; - phi_2924_ = (_e267 > _e270); + phi_2955_ = (_e267 > _e270); } - let _e273 = phi_2924_; + let _e273 = phi_2955_; if _e273 { cascade = 1i; } let _e274 = cascadeCount; let _e275 = (_e274 > 2i); - phi_2935_ = _e275; + phi_2966_ = _e275; if _e275 { let _e276 = viewDistance; let _e279 = frag_info.shadow_cascades[1u]; - phi_2935_ = (_e276 > _e279); + phi_2966_ = (_e276 > _e279); } - let _e282 = phi_2935_; + let _e282 = phi_2966_; if _e282 { cascade = 2i; } @@ -45911,20 +46236,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e291 = which; if (_e291 == 0i) { let _e294 = frag_info.shadow_matrix; - local_7 = _e294; + local_8 = _e294; } else { let _e295 = which; if (_e295 == 1i) { let _e298 = frag_info.shadow_matrix_far; - local_8 = _e298; + local_9 = _e298; } else { let _e300 = frag_info.shadow_matrix_farthest; - local_8 = _e300; + local_9 = _e300; } - let _e301 = local_8; - local_7 = _e301; + let _e301 = local_9; + local_8 = _e301; } - let _e302 = local_7; + let _e302 = local_8; matrix = _e302; let _e305 = matrix[0][0u]; let _e308 = matrix[1][0u]; @@ -45974,24 +46299,24 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf inTile = vec2(((_e389 * 0.5f) + 0.5f), (0.5f - (_e393 * 0.5f))); let _e398 = inTile[0u]; let _e399 = (_e398 < 0f); - phi_3096_ = _e399; + phi_3127_ = _e399; if !(_e399) { let _e402 = inTile[0u]; - phi_3096_ = (_e402 > 1f); + phi_3127_ = (_e402 > 1f); } - let _e405 = phi_3096_; - phi_3103_ = _e405; + let _e405 = phi_3127_; + phi_3134_ = _e405; if !(_e405) { let _e408 = inTile[1u]; - phi_3103_ = (_e408 < 0f); + phi_3134_ = (_e408 < 0f); } - let _e411 = phi_3103_; - phi_3110_ = _e411; + let _e411 = phi_3134_; + phi_3141_ = _e411; if !(_e411) { let _e414 = inTile[1u]; - phi_3110_ = (_e414 > 1f); + phi_3141_ = (_e414 > 1f); } - let _e417 = phi_3110_; + let _e417 = phi_3141_; if _e417 { continue; } @@ -46048,20 +46373,20 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf let _e486 = cascade; if (_e486 == 0i) { let _e490 = frag_info.shadow_bias[0u]; - local_9 = _e490; + local_10 = _e490; } else { let _e491 = cascade; if (_e491 == 1i) { let _e495 = frag_info.shadow_bias[1u]; - local_10 = _e495; + local_11 = _e495; } else { let _e498 = frag_info.shadow_bias[2u]; - local_10 = _e498; + local_11 = _e498; } - let _e499 = local_10; - local_9 = _e499; + let _e499 = local_11; + local_10 = _e499; } - let _e500 = local_9; + let _e500 = local_10; bias = _e500; let _e503 = frag_info.shadow_params[0u]; let _e506 = frag_info.shadow_cascades[3u]; @@ -46306,7 +46631,7 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var denom: f32; var onPlane: vec3; var t_1: f32; - var local_11: vec3; + var local_12: vec3; var offset_2: vec3; var wLen2_: f32; var hLen2_: f32; @@ -46348,15 +46673,15 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b if (_e226 > 0f) { let _e228 = (*mirror); let _e229 = t_1; - local_11 = (_e228 * _e229); + local_12 = (_e228 * _e229); } else { let _e231 = toPlane; let _e232 = n_2; let _e233 = toPlane; let _e234 = n_2; - local_11 = (_e231 - (_e232 * dot(_e233, _e234))); + local_12 = (_e231 - (_e232 * dot(_e233, _e234))); } - let _e238 = local_11; + let _e238 = local_12; onPlane = _e238; } let _e239 = onPlane; @@ -46390,7 +46715,7 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var angle_1: f32; var axis: vec3; var len: f32; - var local_12: f32; + var local_13: f32; let _e185 = (*a_1); let _e186 = (*a_1); @@ -46412,11 +46737,11 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> let _e210 = axis; let _e211 = (*n_3); let _e214 = len; - local_12 = ((_e209 * dot(_e210, _e211)) / _e214); + local_13 = ((_e209 * dot(_e210, _e211)) / _e214); } else { - local_12 = 0f; + local_13 = 0f; } - let _e216 = local_12; + let _e216 = local_13; return _e216; } @@ -46427,15 +46752,15 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr; var b_1: vec3; - var local_13: i32; + var local_14: i32; var ha: f32; var hb: f32; var d_3: f32; var q: vec3; - var local_14: f32; + var local_15: f32; var aAbove: bool; var bAbove: bool; - var local_15: f32; + var local_16: f32; var param_73: vec3; var param_74: vec3; var param_75: vec3; @@ -46455,12 +46780,12 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr 0.000000000001f) { let _e227 = ha; let _e228 = d_3; - local_14 = (_e227 / _e228); + local_15 = (_e227 / _e228); } else { - local_14 = 0f; + local_15 = 0f; } - let _e230 = local_14; + let _e230 = local_15; q = (_e220 + ((_e221 - _e222) * _e230)); let _e233 = ha; aAbove = (_e233 > 0f); @@ -46503,11 +46828,11 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr>, slot_1: ptr) -> f32 { var lane: i32; - var local_16: f32; var local_17: f32; var local_18: f32; + var local_19: f32; let _e182 = (*slot_1); let _e183 = (*slot_1); @@ -46554,28 +46879,28 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 let _e187 = lane; if (_e187 == 0i) { let _e190 = (*four)[0u]; - local_16 = _e190; + local_17 = _e190; } else { let _e191 = lane; if (_e191 == 1i) { let _e194 = (*four)[1u]; - local_17 = _e194; + local_18 = _e194; } else { let _e195 = lane; if (_e195 == 2i) { let _e198 = (*four)[2u]; - local_18 = _e198; + local_19 = _e198; } else { let _e200 = (*four)[3u]; - local_18 = _e200; + local_19 = _e200; } - let _e201 = local_18; - local_17 = _e201; + let _e201 = local_19; + local_18 = _e201; } - let _e202 = local_17; - local_16 = _e202; + let _e202 = local_18; + local_17 = _e202; } - let _e203 = local_16; + let _e203 = local_17; return _e203; } @@ -46686,12 +47011,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var slot_5: i32; var clustered: bool; var listRow: f32; - var local_19: f32; + var local_20: f32; var param_87: i32; var param_88: i32; var v_1: f32; var u_1: f32; - var local_20: f32; + var local_21: f32; var param_89: f32; var param_90: i32; var type_39: f32; @@ -46701,12 +47026,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var corners_1: array, 4>; var behind: bool; var formFactor: f32; - var local_21: f32; + var local_22: f32; var param_91: array, 4>; var param_92: vec3; var area: f32; var radiance: f32; - var local_22: f32; + var local_23: f32; var distance_2: f32; var ratio_1: f32; var window_1: f32; @@ -46719,7 +47044,7 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ var param_97: vec3; var toPoint: vec3; var pointDistance: f32; - var local_23: vec3; + var local_24: vec3; var aim: vec3; var attenuation_1: f32; var toLight_1: vec3; @@ -46754,14 +47079,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e251 = slot_5; param_87 = _e251; let _e252 = ClusterRow_u0028_i1_u003b((¶m_87)); - local_19 = _e252; + local_20 = _e252; } else { let _e253 = slot_5; param_88 = _e253; let _e254 = LightListRow_u0028_i1_u003b((¶m_88)); - local_19 = _e254; + local_20 = _e254; } - let _e255 = local_19; + let _e255 = local_20; listRow = _e255; let _e256 = listRow; let _e260 = light_list_info.list[2u]; @@ -46789,14 +47114,14 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e286 = listRow; param_89 = _e286; let _e287 = InSlots_u0028_f1_u003b((¶m_89)); - local_20 = select(1f, 0f, _e287); + local_21 = select(1f, 0f, _e287); } else { let _e289 = slot_5; param_90 = _e289; let _e290 = LightListScale_u0028_i1_u003b((¶m_90)); - local_20 = _e290; + local_21 = _e290; } - let _e291 = local_20; + let _e291 = local_21; let _e293 = color_1[3u]; color_1[3u] = (_e293 * _e291); } @@ -46833,16 +47158,16 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ behind = (dot(_e332, cross(_e333, _e334)) >= 0f); let _e338 = behind; if _e338 { - local_21 = 0f; + local_22 = 0f; } else { let _e339 = corners_1; param_91 = _e339; let _e341 = (*s_4).n; param_92 = _e341; let _e342 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_91), (¶m_92)); - local_21 = _e342; + local_22 = _e342; } - let _e343 = local_21; + let _e343 = local_22; formFactor = _e343; let _e344 = halfWidth_1; let _e345 = halfHeight_1; @@ -46850,11 +47175,11 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ let _e349 = area; if (_e349 > 0.000000001f) { let _e351 = area; - local_22 = (1f / _e351); + local_23 = (1f / _e351); } else { - local_22 = 0f; + local_23 = 0f; } - let _e353 = local_22; + let _e353 = local_23; radiance = _e353; let _e354 = toCentre; distance_2 = length(_e354); @@ -46897,12 +47222,12 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ if (_e395 > 0.000001f) { let _e397 = toPoint; let _e398 = pointDistance; - local_23 = (_e397 / vec3(_e398)); + local_24 = (_e397 / vec3(_e398)); } else { let _e402 = (*s_4).n; - local_23 = _e402; + local_24 = _e402; } - let _e403 = local_23; + let _e403 = local_24; light_3.l = _e403; let _e406 = light_3.l; let _e408 = (*s_4).v; @@ -46998,7 +47323,7 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var tx: f32; var ty: f32; var tz: f32; - var local_24: f32; + var local_25: f32; var cell: f32; var row_3: f32; var header: vec4; @@ -47026,15 +47351,15 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { let _e236 = clip_1[3u]; let _e237 = near_1; if (_e236 <= _e237) { - local_24 = 0f; + local_25 = 0f; } else { let _e240 = clip_1[3u]; let _e241 = near_1; let _e246 = light_list_info.cluster_depth[1u]; let _e250 = grid[2u]; - local_24 = clamp(floor((log((_e240 / _e241)) * _e246)), 0f, (_e250 - 1f)); + local_25 = clamp(floor((log((_e240 / _e241)) * _e246)), 0f, (_e250 - 1f)); } - let _e253 = local_24; + let _e253 = local_25; tz = _e253; let _e254 = tx; let _e255 = ty; @@ -47088,7 +47413,7 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_104: Surface; var visibility: f32; var param_105: i32; - var local_25: f32; + var local_26: f32; var param_106: Surface; var param_107: LightSample; var param_108: i32; @@ -47138,11 +47463,11 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 let _e215 = i_8; param_111 = _e215; let _e216 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_109), (¶m_110), (¶m_111)); - local_25 = (_e211 * _e216); + local_26 = (_e211 * _e216); } else { - local_25 = 1f; + local_26 = 1f; } - let _e218 = local_25; + let _e218 = local_26; visibility = _e218; let _e219 = visibility; if (_e219 <= 0f) { @@ -47215,7 +47540,7 @@ fn ImpostorViewUv_u0028_vf2_u003b_vf3_u003b(cell_1: ptr>, of var right: vec3; var param_115: vec3; var up_1: vec3; - var local_26: vec2; + var local_27: vec2; let _e184 = (*cell_1); param_114 = (_e184 / vec2(7f)); @@ -47232,9 +47557,9 @@ fn ImpostorViewUv_u0028_vf2_u003b_vf3_u003b(cell_1: ptr>, of let _e194 = right; let _e198 = (*offset_3); let _e199 = up_1; - local_26 = vec2(((dot(_e193, _e194) * 0.5f) + 0.5f), (0.5f - (dot(_e198, _e199) * 0.5f))); + local_27 = vec2(((dot(_e193, _e194) * 0.5f) + 0.5f), (0.5f - (dot(_e198, _e199) * 0.5f))); let _e204 = (*cell_1); - let _e205 = local_26; + let _e205 = local_27; return ((_e204 + clamp(_e205, vec2(0f, 0f), vec2(1f, 1f))) / vec2(8f)); } @@ -47263,6 +47588,22 @@ fn ImpostorEncode_u0028_vf3_u003b(d_6: ptr>) -> vec2 { return ((select(_e215, _e214, vec2((_e212 >= 0f))) * 0.5f) + vec2(0.5f, 0.5f)); } +fn TowardsEye_u0028_() -> vec3 { + var local_28: vec3; + + let _e177 = Orthographic_u0028_(); + if _e177 { + let _e179 = fog_info.forward; + local_28 = -(_e179.xyz); + } else { + let _e183 = fog_info.eye; + let _e185 = v_world_position_1; + local_28 = normalize((_e183.xyz - _e185)); + } + let _e188 = local_28; + return _e188; +} + fn MaterialLodBias_u0028_() -> f32 { let _e178 = frag_info.target_origin[1u]; return _e178; @@ -47328,27 +47669,26 @@ fn ReadSurface_u0028_() -> Surface { let _e244 = s_8.n; s_8.n = -(_e244); } - let _e248 = frag_info.camera_position; - let _e250 = v_world_position_1; - s_8.v = normalize((_e248.xyz - _e250)); - let _e255 = s_8.n; - let _e257 = s_8.v; - s_8.n_dot_v = max(dot(_e255, _e257), 0.0001f); - let _e263 = frag_info.material[0u]; - s_8.metallic = clamp(_e263, 0f, 1f); - let _e268 = frag_info.material[1u]; - s_8.roughness = clamp(_e268, 0.02f, 1f); - let _e272 = frag_info.ambient_ground; - let _e275 = frag_info.ambient_sky; - let _e279 = s_8.n[1u]; - let _e286 = frag_info.material[2u]; - s_8.ambient = (mix(_e272.xyz, _e275.xyz, vec3(((_e279 * 0.5f) + 0.5f))) * _e286); - let _e291 = frag_info.frame_params[0u]; - s_8.exposure = max(_e291, 0f); + let _e247 = TowardsEye_u0028_(); + s_8.v = _e247; + let _e250 = s_8.n; + let _e252 = s_8.v; + s_8.n_dot_v = max(dot(_e250, _e252), 0.0001f); + let _e258 = frag_info.material[0u]; + s_8.metallic = clamp(_e258, 0f, 1f); + let _e263 = frag_info.material[1u]; + s_8.roughness = clamp(_e263, 0.02f, 1f); + let _e267 = frag_info.ambient_ground; + let _e270 = frag_info.ambient_sky; + let _e274 = s_8.n[1u]; + let _e281 = frag_info.material[2u]; + s_8.ambient = (mix(_e267.xyz, _e270.xyz, vec3(((_e274 * 0.5f) + 0.5f))) * _e281); + let _e286 = frag_info.frame_params[0u]; + s_8.exposure = max(_e286, 0f); s_8.occlusion = 1f; s_8.emissive = vec3(0f, 0f, 0f); - let _e296 = s_8; - return _e296; + let _e291 = s_8; + return _e291; } fn main_1() { @@ -47364,11 +47704,11 @@ fn main_1() { var f: vec2; var lower: bool; var c0_: vec2; - var local_27: vec2; + var local_29: vec2; var c1_: vec2; var c2_: vec2; var w: vec3; - var local_28: vec3; + var local_30: vec3; var uv0_: vec2; var param_120: vec2; var param_121: vec3; @@ -47387,7 +47727,7 @@ fn main_1() { var wa: vec3; var alpha_3: f32; var srgb_1: vec3; - var local_29: vec3; + var local_31: vec3; var worldRight: vec3; var worldFacing: vec3; var worldUp: vec3; @@ -47439,12 +47779,12 @@ fn main_1() { let _e262 = lower; if _e262 { let _e263 = base; - local_27 = _e263; + local_29 = _e263; } else { let _e264 = base; - local_27 = (_e264 + vec2(1f, 1f)); + local_29 = (_e264 + vec2(1f, 1f)); } - let _e266 = local_27; + let _e266 = local_29; c0_ = _e266; let _e267 = base; c1_ = (_e267 + vec2(1f, 0f)); @@ -47456,15 +47796,15 @@ fn main_1() { let _e276 = f[1u]; let _e279 = f[0u]; let _e281 = f[1u]; - local_28 = vec3(((1f - _e273) - _e276), _e279, _e281); + local_30 = vec3(((1f - _e273) - _e276), _e279, _e281); } else { let _e284 = f[0u]; let _e286 = f[1u]; let _e290 = f[1u]; let _e293 = f[0u]; - local_28 = vec3(((_e284 + _e286) - 1f), (1f - _e290), (1f - _e293)); + local_30 = vec3(((_e284 + _e286) - 1f), (1f - _e290), (1f - _e293)); } - let _e296 = local_28; + let _e296 = local_30; w = _e296; let _e297 = c0_; param_120 = _e297; @@ -47529,12 +47869,12 @@ fn main_1() { let _e369 = wa[1u]; let _e372 = n2_; let _e377 = wa[2u]; - local_29 = (((((_e357.xyz * 2f) - vec3(1f, 1f, 1f)) * _e362) + (((_e364.xyz * 2f) - vec3(1f, 1f, 1f)) * _e369)) + (((_e372.xyz * 2f) - vec3(1f, 1f, 1f)) * _e377)); - let _e380 = local_29; - let _e381 = local_29; - let _e384 = local_29; + local_31 = (((((_e357.xyz * 2f) - vec3(1f, 1f, 1f)) * _e362) + (((_e364.xyz * 2f) - vec3(1f, 1f, 1f)) * _e369)) + (((_e372.xyz * 2f) - vec3(1f, 1f, 1f)) * _e377)); + let _e380 = local_31; + let _e381 = local_31; + let _e384 = local_31; let _e385 = d_7; - local_29 = normalize(select(_e385, _e384, vec3((dot(_e380, _e381) > 0.000000000001f)))); + local_31 = normalize(select(_e385, _e384, vec3((dot(_e380, _e381) > 0.000000000001f)))); let _e389 = v_tangent_1; worldRight = normalize(_e389.xyz); let _e392 = v_normal_1; @@ -47543,13 +47883,13 @@ fn main_1() { let _e395 = worldRight; worldUp = cross(_e394, _e395); let _e397 = worldRight; - let _e398 = local_29; + let _e398 = local_31; let _e399 = right_1; let _e402 = worldUp; - let _e403 = local_29; + let _e403 = local_31; let _e404 = up_2; let _e408 = worldFacing; - let _e409 = local_29; + let _e409 = local_31; let _e410 = d_7; s_9.n = normalize((((_e397 * dot(_e398, _e399)) + (_e402 * dot(_e403, _e404))) + (_e408 * dot(_e409, _e410)))); let _e417 = s_9.n; @@ -47617,7 +47957,7 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3[ WebGpuBlockMember(name: 'fog', offsetInBytes: 0, sizeInBytes: 16), WebGpuBlockMember(name: 'eye', offsetInBytes: 16, sizeInBytes: 16), @@ -47626,6 +47966,11 @@ fn main(@location(6) v_world_position: vec3, @location(2) v_normal: vec3 _budgets = { + 'orthographic-metal': 0.01, 'debug-view-split': 0.01, 'teapot-generated-normals': 0.01, 'shadow-teapot': 0.01, diff --git a/packages/flutter3d_webgpu/test/goldens/orthographic-metal.png b/packages/flutter3d_webgpu/test/goldens/orthographic-metal.png new file mode 100644 index 0000000000000000000000000000000000000000..8959e154e9e74f44ed7e5b978e8554184e683fbb GIT binary patch literal 47636 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
    @mUk*1_bn2&#NgX1I_hpAC;n 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 m0s8qzekbQbk&$9wum2zM(}>CLPGxHV0000 -

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps eighty-seven scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    +

    Independently written implementations agreeing is evidence; one implementation agreeing with itself is not. The software set also keeps eighty-eight scenes checkable in a headless run: recording the other two takes a GPU or a browser, but comparing the committed sets takes neither.

    `cross_backend_test.dart` compares them with per-scene budgets, and any new backend has to pass `flutter3d_conformance` before it counts as one. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 65d9de6af..00e4a7afe 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -506,6 +506,35 @@ field, motion blur, a temporal resolve) still run over the channel; a mip chain is not in `targetBytes`; the view in the editor's viewport and the MCP tools; the comparison page row (decision 19). +**P7. Orthographic everywhere, per-draw ranges, alpha to coverage, render +order.** *Done:* per-draw index ranges (`PassEncoder.draw` takes +`firstIndex` and `indexCount`, refused past the binding by `indexWindow` on +all four backends). The orthographic camera, after an audit of every pass for +a perspective assumption: `FogInfo.projection` says which lens it is +(`isOrthographic` reads it off the matrix, so a mirror and a probe face need +no camera), and `TowardsEye` in `color.glsl` is the view axis through an +orthographic lens — the lit models' `s.v`, the planar reflection's Fresnel — +so highlights stop sliding across the frame; `EyeDistance` is the depth from +the eye's plane there, so fog lies flat; the sky is seen through a +sixty-degree lens built from the matrix's own axes, so it is a gradient +rather than one colour; the light shafts start on the eye's plane, as the +volumetric fog already did. Already right and left alone: SSAO, SSR, +contact shadows, decals, camera velocity, the volumetric fog, weighted +blended transparency, soft particles, impostors, splats' lens, LOD, Hi-Z, +mirrors. Tests: `orthographic_test.dart` (the matrix told apart in both depth +ranges, a metal wall even across the frame and the same wherever the camera +stands along its axis, fog flat on a wall, the sky a gradient; +mutation-checked on the sky) and `orthographic-metal` in all four sets, the +software set and WebGPU at 0 of 172800 pixels from Impeller, WebGL at +0.394% on the edges. +*Left:* alpha to coverage; an explicit render order; under an orthographic +camera, the shadow cascades (split by distance from the eye, so the near +ones cover air and everything falls to the last and softest), particles' +and splats' fog and facing (their stages declare a shortened `FogInfo` +without the axis), a near plane at or behind the eye (the surface buffer +reads depth nought or less as sky), splat sorting by distance rather than +depth, and the example's overlay passing a perspective to `lookFrom`. + Already ours and not theirs, to keep: volumetric fog, weighted blended OIT, motion blur, local exposure and HDR out, EVSM, impostors, Hi-Z and software occlusion, generated LOD, WebGPU, the CPU backend, retargeting. diff --git a/tool/ci.sh b/tool/ci.sh index 5d673f5f8..5e7d7a2f6 100755 --- a/tool/ci.sh +++ b/tool/ci.sh @@ -9,7 +9,7 @@ # # What it does NOT do, stated so the gap is not mistaken for coverage: # -# * The Impeller half of the golden set. Those eighty-seven scenes render through +# * The Impeller half of the golden set. Those eighty-eight scenes render through # flutter_gpu and need a real device, so they run from # packages/flutter3d/tool/golden.sh on a machine with a GPU. The software # half runs here, and cross_backend_test.dart compares the two committed From 2d692bf110c38198e3a82f5690d4c426b1705483 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 14:41:42 +0300 Subject: [PATCH 054/141] Nodes that share a material can be put in an order MeshNode.drawOrder adds to the material's drawBucket in the leading field of the sort key, so it outranks depth and state in both the opaque and the transparent half. Before this the bucket was per material, and two nodes sharing one could not be ordered without a copy of it. The batching no longer merges a run across a boundary of two orders, which would have drawn the later nodes at the earlier ones' place. --- ARCHITECTURE.md | 2 +- README.md | 2 +- packages/flutter3d/README.md | 4 +- packages/flutter3d/test/draw_order_test.dart | 134 ++++++++++++++++++ packages/flutter3d_core/CHANGELOG.md | 5 + .../lib/src/engine/render/material.dart | 2 + .../lib/src/engine/render/render_list.dart | 4 +- .../lib/src/engine/render/renderer_batch.dart | 3 + .../lib/src/engine/scene/mesh_node.dart | 16 +++ site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- tasks/0.9-engine-roadmap.md | 7 +- 12 files changed, 178 insertions(+), 11 deletions(-) create mode 100644 packages/flutter3d/test/draw_order_test.dart diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 1e4f1d17b..d1409d28f 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2691,7 +2691,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **11248 tests** across 43 packages and 10 applications | +| Unit tests | **11252 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 5d81b03f5..69fce2f38 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11248 tests across forty-three packages and nine applications. The +There are 11252 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 88 scenes checkable in a headless run. diff --git a/packages/flutter3d/README.md b/packages/flutter3d/README.md index f6ee90e7c..d695119d4 100644 --- a/packages/flutter3d/README.md +++ b/packages/flutter3d/README.md @@ -104,7 +104,7 @@ What works today: `CUBICSPLINE` with authored tangents), slerped rotations, an `AnimationPlayer` with play/pause/seek/speed and once/loop/ping-pong, and the decoded node hierarchy rebuilt on instantiation so an animated parent carries its subtree; -- 1642 tests covering projection, scene, sorting, debug draw, raycasting, +- 1646 tests covering projection, scene, sorting, debug draw, raycasting, animation, skinning, lighting, render targets, BVH, LOD, glTF, OBJ and `.f3d`, plus a real frame drawn through `flutter3d_cpu`'s software rasteriser for the ones that need one, all without a GPU. The geometry the engine is written in @@ -322,7 +322,7 @@ lib/src/engine/assets/ glTF, OBJ and .f3d decoders, isolate loading, ca example/lib/ the demo, and the frame capture hook skills/ the conventions, as agent skills — see below bin/skills.dart what copies them into a project that uses this -test/ 1642 tests, all runnable without a GPU +test/ 1646 tests, all runnable without a GPU ``` The GLSL is not here. Every shader this package draws with lives in diff --git a/packages/flutter3d/test/draw_order_test.dart b/packages/flutter3d/test/draw_order_test.dart new file mode 100644 index 000000000..9a71431a6 --- /dev/null +++ b/packages/flutter3d/test/draw_order_test.dart @@ -0,0 +1,134 @@ +/// An order between nodes that share a material — `P7`, `MeshNode.drawOrder`. +/// +/// flutter test test/draw_order_test.dart +/// +/// The material's bucket was the only explicit order there was, and two nodes +/// sharing a material could not be put in one without a copy of it. These +/// claims are about the sort key and about the batching that reads the sorted +/// list: neither may let a node's order be undone. +library; + +import 'package:flutter3d/flutter3d.dart' hide Material; +import 'package:flutter3d/flutter3d.dart' as engine show Material; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +/// Boxes in a row along the view axis, nearest first, all of [material], +/// each named by its order in [orders]. +({Scene scene, CameraNode camera}) _row( + List orders, + engine.Material material, { + MeshGeometry? geometry, +}) { + final scene = Scene(); + final mesh = + geometry ?? CpuMesh(CuboidShape(size: Vector3(1.0, 1.0, 1.0)).build()); + for (var i = 0; i < orders.length; i++) { + scene.add( + MeshNode(mesh, material, name: 'node $i') + ..drawOrder = orders[i] + // Spread along the view axis, so a depth term that outranked the + // order would put them in another order. + ..setPosition(0.0, 0.0, 10.0 + i * 5.0), + ); + } + final camera = scene.add(CameraNode())..lookAt(Vector3(0.0, 0.0, 1.0)); + return (scene: scene, camera: camera); +} + +/// The names of the drawn nodes in the order the list sorted them, opaque +/// half or transparent half. +List _sorted( + List orders, { + engine.Material? material, + bool transparent = false, +}) { + final world = _row( + orders, + material ?? + engine.Material( + lighting: LightingModel.unlit, + baseColor: Vector4(1.0, 1.0, 1.0, transparent ? 0.5 : 1.0), + alphaMode: transparent + ? MaterialAlphaMode.blend + : MaterialAlphaMode.opaque, + ), + ); + final view = RenderView(camera: world.camera); + final list = RenderList() + ..build( + world.scene, + view, + viewMatrix: world.camera.viewMatrix, + frustum: Frustum.matrix(world.camera.viewProjection(1.0)), + ) + ..sort(view); + return [ + for (final index in transparent ? list.transparent : list.opaque) + list.itemAt(index).requireNode.name ?? '?', + ]; +} + +void main() { + test('nodes sharing a material are drawn in their order, lowest first', () { + // Mutation: leave `drawOrder` out of the bucket in `_sortKey`, and the + // shared material sorts them by depth, nearest first. + expect(_sorted([2, -1, 0]), ['node 1', 'node 2', 'node 0']); + }); + + test('the order holds in the transparent half too', () { + // Far to near is the transparent half's own sort, so without the order + // the farthest node would come first. + expect(_sorted([-1, 0, 1], transparent: true), [ + 'node 0', + 'node 1', + 'node 2', + ]); + }); + + test("a node's order adds to its material's bucket", () { + final material = engine.Material( + lighting: LightingModel.unlit, + drawBucket: 3, + ); + // Bucket three plus minus four is minus one: before an ordinary node of + // the same material at nought, which is three. + expect(_sorted([0, -4], material: material), [ + 'node 1', + 'node 0', + ]); + }); + + test('nodes of different orders are never one instanced draw', () { + // Mutation: drop the order from the batching's run test, and a run that + // crosses from one order to the next is drawn at the first one's place. + // Six on each side, enough for a run either side to batch on its own. + const size = 48; + final device = CpuDevice( + width: size, + height: size, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final mesh = DeviceMesh.upload(device, CuboidShape().build()); + final world = _row( + [for (var i = 0; i < 12; i++) i < 6 ? 0 : 1], + engine.Material(lighting: LightingModel.unlit), + geometry: mesh, + ); + FrameResult draw({required bool batched}) => + Renderer.create(device: device).render( + width: size, + height: size, + scene: world.scene, + views: [RenderView(camera: world.camera)], + settings: RenderSettings(batchIdenticalDraws: batched), + ); + final batched = draw(batched: true); + final plain = draw(batched: false); + expect(batched.batchedDraws, 12); + // Two instanced draws stand for twelve: ten calls fewer. One run of + // twelve across the boundary would be eleven fewer. + expect(plain.drawCalls - batched.drawCalls, 10); + }); +} diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index 118d89c42..3d0e20150 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -1,5 +1,10 @@ ## Unreleased +- **`MeshNode.drawOrder`** puts nodes that share a material in an order: + it adds to the material's `drawBucket`, outranks every other sort term in + both halves of the list, and is never merged across by the batching. Nought + by default. + - **An orthographic camera no longer reads its eye as a point the light travels to.** Highlights, Fresnel and reflections are measured against the view axis rather than from the eye's position, so they stop sliding diff --git a/packages/flutter3d_core/lib/src/engine/render/material.dart b/packages/flutter3d_core/lib/src/engine/render/material.dart index 4aeb828a4..db13a9002 100644 --- a/packages/flutter3d_core/lib/src/engine/render/material.dart +++ b/packages/flutter3d_core/lib/src/engine/render/material.dart @@ -316,6 +316,8 @@ final class Material { /// Signed, and negative is the useful half: ordinary materials sit at zero, /// so the only way to be drawn *before* the scene is to ask for less than it. /// The usable range is −128 to 127 and values outside it are clamped. + /// [MeshNode.drawOrder] adds to it, for an order between nodes that share + /// this material. int drawBucket; /// Overrides whether this surface writes depth. Null lets transparency diff --git a/packages/flutter3d_core/lib/src/engine/render/render_list.dart b/packages/flutter3d_core/lib/src/engine/render/render_list.dart index 5aa03c911..12c037f82 100644 --- a/packages/flutter3d_core/lib/src/engine/render/render_list.dart +++ b/packages/flutter3d_core/lib/src/engine/render/render_list.dart @@ -378,7 +378,9 @@ final class RenderList { // bucket. `& 0xFF` sent −1 to 255 and drew the thing dead last, silently: // a sky asked to go first went behind nothing and in front of everything. // Clamping rather than wrapping for the same reason at the other end. - final bucket = (material.drawBucket + 128).clamp(0, 255); + // `P7`: the node's own order on top of its material's. + final bucket = (material.drawBucket + item.requireNode.drawOrder + 128) + .clamp(0, 255); switch (mode) { case SortMode.stateThenDepth: diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_batch.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_batch.dart index 6175abfce..08a64dd49 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_batch.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_batch.dart @@ -45,6 +45,9 @@ extension _BatchedDraws on Renderer { if (!_isBatchable(next) || !identical(next.mesh, first.mesh) || !identical(next.material, first.material) || + // Adjacent across a boundary of two orders, and the merge would + // draw the later one at the earlier one's place — `P7`. + next.drawOrder != first.drawOrder || next.worldIsMirrored != first.worldIsMirrored || !identical(probes.nearest(next.worldBoundsCentre), probe)) { break; diff --git a/packages/flutter3d_core/lib/src/engine/scene/mesh_node.dart b/packages/flutter3d_core/lib/src/engine/scene/mesh_node.dart index 7f21a3a48..7c648d39f 100644 --- a/packages/flutter3d_core/lib/src/engine/scene/mesh_node.dart +++ b/packages/flutter3d_core/lib/src/engine/scene/mesh_node.dart @@ -218,6 +218,22 @@ base class MeshNode extends SceneNode { /// looks for. A static thing wrongly left dynamic merely costs a redraw. bool shadowIsStatic = false; + /// Where this node is drawn among the others, on top of its material's + /// [Material.drawBucket] — `P7`. + /// + /// **Per node, because the bucket is per material.** Two nodes sharing a + /// material — a decal card and the wall it marks, a ghost and the player it + /// replays — could not be put in an order without giving one a copy of the + /// material, and a copy breaks the batching, the material's sort id and + /// every edit made to the original afterwards. The two are added, and the + /// sum outranks every other sort term in both halves of the list: lower + /// first, the same within a half in the order the view's sort mode gives. + /// The usable sum is −128 to 127 and is clamped beyond it. + /// + /// Nought by default, which leaves every scene drawn as it was. Nodes of + /// different orders are never merged into one instanced draw. + int drawOrder = 0; + /// Skips frustum culling for this node. /// /// Worth setting on anything that follows the camera, such as a skybox, whose diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 8987786ac..f163bc85a 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 11248 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-eight scenes stay checkable in a headless run. +There are 11252 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so eighty-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index 4a3101bdd..88ec37183 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,14 +1,14 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11248 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 11252 tests need a GPU. --- # Testing -11248 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +11252 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| -| `flutter3d` | 1642 | | | | +| `flutter3d` | 1646 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | | `flutter3d_sim` | 691 | | `pad_input` | 67 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 11222 rather than 11248: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 11226 rather than 11252: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. diff --git a/tasks/0.9-engine-roadmap.md b/tasks/0.9-engine-roadmap.md index 00e4a7afe..667e338d8 100644 --- a/tasks/0.9-engine-roadmap.md +++ b/tasks/0.9-engine-roadmap.md @@ -527,7 +527,12 @@ stands along its axis, fog flat on a wall, the sky a gradient; mutation-checked on the sky) and `orthographic-metal` in all four sets, the software set and WebGPU at 0 of 172800 pixels from Impeller, WebGL at 0.394% on the edges. -*Left:* alpha to coverage; an explicit render order; under an orthographic +An explicit render order between nodes: `MeshNode.drawOrder`, added to the +material's `drawBucket` in the sort key's leading field, honoured in both +halves, and a boundary the batching does not merge across +(`draw_order_test.dart`, mutation-checked on the batching). +*Left:* alpha to coverage; the order in the level format and the editor; +under an orthographic camera, the shadow cascades (split by distance from the eye, so the near ones cover air and everything falls to the last and softest), particles' and splats' fog and facing (their stages declare a shortened `FogInfo` From 2657152f3f6502ffe2ae7d288dee5b980c9bda08 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 14:51:43 +0300 Subject: [PATCH 055/141] See-through casters shade the sun by what their material lets through A shadow map holds one depth per texel, so glass and liquid cast shadows as dark as stone. ShadowSettings.translucentCasters, off by default, draws a blended or transmissive caster into the sun's atlas after the opaque ones, into the three channels that held nothing: what it takes from red and green, and what it leaves of blue, from its opacity, its transmission tinted by its colour, and the Fresnel loss of its index. Clear glass casts a faint shadow with darker edges, a coloured liquid a shadow of its colour, and layers combine through the blend. The layout keeps every existing stage writing what it wrote, so with the setting off every frame is byte for byte as before; the goldens on all four backends hold. ShadowFactor sets light_transmittance, the light loop multiplies the sun by it, and a see-through surface that casts is not shaded by it. The software backend mirrors both stages, and the WebGL and WebGPU tables are regenerated. --- ARCHITECTURE.md | 2 +- README.md | 2 +- packages/flutter3d/README.md | 4 +- .../test/translucent_shadow_test.dart | 190 + packages/flutter3d_conformance/CHANGELOG.md | 4 + .../lib/src/shader_link_checks.dart | 3 + packages/flutter3d_core/CHANGELOG.md | 10 + .../lib/src/engine/render/renderer.dart | 37 + .../engine/render/renderer_mesh_encode.dart | 20 + .../engine/render/renderer_shadow_pass.dart | 267 +- .../src/engine/render/shadow_settings.dart | 29 + packages/flutter3d_cpu/CHANGELOG.md | 6 + .../lib/src/cpu_shaders_builtin.dart | 1 + .../lib/src/cpu_shaders_lighting.dart | 7 +- .../src/cpu_shaders_shadow_directional.dart | 25 +- .../lib/src/cpu_shaders_shadow_passes.dart | 38 + packages/flutter3d_shaders/CHANGELOG.md | 7 + .../lib/flutter3d_shaders.dart | 1 + .../flutter3d_shaders/lib/stage_bindings.dart | 4 + .../flutter3d_shaders/lib/typed_blocks.dart | 21 + .../flutter3d_shaders/lib/uniform_blocks.dart | 7 + .../shaders/flutter3d.shaderbundle.json | 4 + .../flutter3d_shaders/shaders/lib/shadow.glsl | 12 + .../shaders/lib/surface.glsl | 17 +- .../lighting/shadow_transmittance.frag | 64 + packages/flutter3d_webgl/CHANGELOG.md | 5 + .../flutter3d_webgl/lib/engine_shaders.dart | 614 +- packages/flutter3d_webgpu/CHANGELOG.md | 5 + .../flutter3d_webgpu/lib/engine_shaders.dart | 26958 ++++++++-------- site/content/core/backends.md | 2 +- site/content/quickstart.md | 2 +- site/content/reference/testing.md | 8 +- 32 files changed, 14917 insertions(+), 13459 deletions(-) create mode 100644 packages/flutter3d/test/translucent_shadow_test.dart create mode 100644 packages/flutter3d_shaders/shaders/lighting/shadow_transmittance.frag diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0c26831d1..e21203f28 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2690,7 +2690,7 @@ storage. |---|---| | Style | `dart format` | | Analysis | `flutter analyze` clean across the workspace, no warnings | -| Unit tests | **10883 tests** across 43 packages and 10 applications | +| Unit tests | **10887 tests** across 43 packages and 10 applications | | Structure rules | 35, `dart run tool/structure.dart`, the first CI step | | CI | GitHub Actions over `tool/ci.sh`, on `ubuntu-latest`, with no graphics card | diff --git a/README.md b/README.md index 00e114aa8..a40ae05fe 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ Or one package at a time: (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10883 tests across forty-three packages and nine applications. The +There are 10887 tests across forty-three packages and nine applications. The only ones that need a GPU are the Impeller half of the golden set. The other half is rendered by the software backend, which is what makes 78 scenes checkable in a headless run. diff --git a/packages/flutter3d/README.md b/packages/flutter3d/README.md index fe9ee3f4e..ce4e0975c 100644 --- a/packages/flutter3d/README.md +++ b/packages/flutter3d/README.md @@ -104,7 +104,7 @@ What works today: `CUBICSPLINE` with authored tangents), slerped rotations, an `AnimationPlayer` with play/pause/seek/speed and once/loop/ping-pong, and the decoded node hierarchy rebuilt on instantiation so an animated parent carries its subtree; -- 1620 tests covering projection, scene, sorting, debug draw, raycasting, +- 1624 tests covering projection, scene, sorting, debug draw, raycasting, animation, skinning, lighting, render targets, BVH, LOD, glTF, OBJ and `.f3d`, plus a real frame drawn through `flutter3d_cpu`'s software rasteriser for the ones that need one, all without a GPU. The geometry the engine is written in @@ -322,7 +322,7 @@ lib/src/engine/assets/ glTF, OBJ and .f3d decoders, isolate loading, ca example/lib/ the demo, and the frame capture hook skills/ the conventions, as agent skills — see below bin/skills.dart what copies them into a project that uses this -test/ 1620 tests, all runnable without a GPU +test/ 1624 tests, all runnable without a GPU ``` The GLSL is not here. Every shader this package draws with lives in diff --git a/packages/flutter3d/test/translucent_shadow_test.dart b/packages/flutter3d/test/translucent_shadow_test.dart new file mode 100644 index 000000000..0a076ebb3 --- /dev/null +++ b/packages/flutter3d/test/translucent_shadow_test.dart @@ -0,0 +1,190 @@ +/// `ShadowSettings.translucentCasters`: a see-through caster shades the sun's +/// light by what its material lets through. +/// +/// flutter test test/translucent_shadow_test.dart +/// +/// **A shadow map holds one depth per texel**, so until this a pane of clear +/// glass cast a shadow as dark as a wall, and a blue liquid a grey one. The +/// claims, each against the same floor and the same card over it: +/// +/// * clear glass still darkens the floor, and much less than an opaque card; +/// * a blue liquid darkens it to blue: what reaches the floor is bluer than +/// the light that fell; +/// * with nothing see-through in the scene, turning it on changes no pixel. +library; + +import 'package:flutter3d/flutter3d.dart'; +import 'package:flutter3d_cpu/flutter3d_cpu.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:vector_math/vector_math.dart'; + +const int _size = 64; + +/// A floor and a card over it, lit at forty-five degrees so the shadow lands +/// clear of the card's footprint, seen from above. +Future> _frame({ + required Material? card, + required bool translucent, + bool casting = true, +}) async { + final device = CpuDevice( + width: _size, + height: _size, + shaders: CpuShaderLibrary(builtinCpuShaders()), + ); + final renderer = Renderer.create(device: device); + final scene = Scene() + ..add( + MeshNode( + DeviceMesh.upload( + device, + CuboidShape(size: Vector3(6, 0.1, 6)).build(), + ), + Material(name: 'floor', baseColor: Vector4(0.9, 0.9, 0.9, 1.0)), + )..setPosition(0.0, -1.0, 0.0), + ) + ..add( + LightNode(intensity: 1.0, castsShadow: true) + ..setPosition(4.0, 5.0, 0.01) + ..lookAt(Vector3.zero()), + ) + ..add( + CameraNode() + ..setPosition(0.0, 5.0, 0.01) + ..lookAt(Vector3.zero()), + ); + if (card != null) { + scene.add( + MeshNode( + DeviceMesh.upload( + device, + CuboidShape(size: Vector3(2, 0.05, 2)).build(), + ), + card, + ) + ..setPosition(0.0, 1.0, 0.0) + ..shadowCasting = casting + ? ShadowCastingMode.on + : ShadowCastingMode.off, + ); + } + final frame = renderer.render( + width: _size, + height: _size, + scene: scene, + views: [ + RenderView( + camera: scene.cameras.single, + clearColor: Vector4(0.0, 0.0, 0.0, 1.0), + ), + ], + settings: RenderSettings( + shadows: ShadowSettings(translucentCasters: translucent), + look: const LookSettings(dither: 0), + ), + ); + final bytes = await device.readPixels(frame.frame); + return [for (var i = 0; i < _size * _size * 4; i++) bytes!.getUint8(i)]; +} + +Material _opaque() => + Material(name: 'wall', baseColor: Vector4(0.8, 0.8, 0.8, 1.0)); + +Material _glass() => Material( + name: 'glass', + lighting: LightingModel.pbrLayered, + baseColor: Vector4(0.95, 0.97, 1.0, 0.12), + alphaMode: MaterialAlphaMode.blend, + doubleSided: true, + extensions: MaterialExtensions(transmission: 0.95, ior: 1.5), +); + +Material _blueLiquid() => Material( + name: 'liquid', + lighting: LightingModel.pbrLayered, + baseColor: Vector4(0.15, 0.3, 0.95, 0.8), + alphaMode: MaterialAlphaMode.blend, + extensions: MaterialExtensions(transmission: 0.6, ior: 1.33), +); + +/// The floor pixels a solid card shadows: where [solid] is darker than [lit]. +List _shadowed(List solid, List lit) => [ + for (var i = 0; i < solid.length; i += 4) + if (solid[i] < lit[i] - 8) i, +]; + +/// The mean red, green and blue over [pixels] of [frame]. +(double, double, double) _mean(List frame, List pixels) { + var r = 0.0, g = 0.0, b = 0.0; + for (final i in pixels) { + r += frame[i]; + g += frame[i + 1]; + b += frame[i + 2]; + } + final n = pixels.length.toDouble(); + return (r / n, g / n, b / n); +} + +void main() { + test('clear glass casts a faint shadow, an opaque card a dark one', () async { + final lit = await _frame( + card: _opaque(), + translucent: true, + casting: false, + ); + final solid = await _frame(card: _opaque(), translucent: true); + final asBefore = await _frame(card: _glass(), translucent: false); + final glass = await _frame(card: _glass(), translucent: true); + final region = _shadowed(solid, lit); + expect(region.length, greaterThan(40), reason: 'the fixture casts nothing'); + + final (litR, _, _) = _mean(lit, region); + final (solidR, _, _) = _mean(solid, region); + final (beforeR, _, _) = _mean(asBefore, region); + final (glassR, _, _) = _mean(glass, region); + + // Off, the glass is a wall — the defect. Mutation: draw see-through + // casters into the atlas regardless of the setting, and this moves. + expect(beforeR, closeTo(solidR, 2.0)); + // On, it lets most of the light through, but not all of it: four + // surfaces each reflect some away. Mutation: skip the Fresnel loss, and + // the shadow is gone; draw the glass as opaque, and it is a wall again. + expect(glassR, greaterThan(solidR + 0.5 * (litR - solidR))); + expect(glassR, lessThan(litR - 1.0)); + }); + + test('a blue liquid casts a blue shadow', () async { + final lit = await _frame( + card: _opaque(), + translucent: true, + casting: false, + ); + final solid = await _frame(card: _opaque(), translucent: true); + final liquid = await _frame(card: _blueLiquid(), translucent: true); + final region = _shadowed(solid, lit); + + final (litR, _, litB) = _mean(lit, region); + final (r, _, b) = _mean(liquid, region); + // Bluer than the light that fell: the red is taken more than the blue. + // Mutation: write the same number into all three channels, and the + // ratio stays the floor's. + expect(b / r, greaterThan(1.3 * litB / litR)); + // And darker than no shadow at all. + expect(r, lessThan(litR - 8.0)); + }); + + test('with nothing see-through, turning it on changes no pixel', () async { + final off = await _frame(card: _opaque(), translucent: false); + final on = await _frame(card: _opaque(), translucent: true); + // Mutation: clear the atlas's free channels to ones, or set the flag on + // a draw whose atlas carries none, and the floor goes black. + expect(on, off); + }); + + test('copyWith keeps the setting', () { + // `copyWith` has dropped fields before, and `settingsFrom` calls it every + // frame, so a field it forgets is a setting nobody can turn on. + const settings = ShadowSettings(translucentCasters: true); + expect(settings.copyWith(strength: 0.5).translucentCasters, isTrue); + }); +} diff --git a/packages/flutter3d_conformance/CHANGELOG.md b/packages/flutter3d_conformance/CHANGELOG.md index 7357f9088..9acbf898b 100644 --- a/packages/flutter3d_conformance/CHANGELOG.md +++ b/packages/flutter3d_conformance/CHANGELOG.md @@ -1,3 +1,7 @@ +## Unreleased + +- Links every mesh vertex stage with `ShadowTransmittance`. + ## 0.8.1 **The linking check names `ContactShadowResolve`**, the stage diff --git a/packages/flutter3d_conformance/lib/src/shader_link_checks.dart b/packages/flutter3d_conformance/lib/src/shader_link_checks.dart index f9ffd75b2..e171adddd 100644 --- a/packages/flutter3d_conformance/lib/src/shader_link_checks.dart +++ b/packages/flutter3d_conformance/lib/src/shader_link_checks.dart @@ -81,6 +81,9 @@ Future checkLinking(GraphicsDevice device) async { // plain ones do not, so they are a different link. (vertex, 'ShadowDepthMasked'), (vertex, 'ShadowDistanceMasked'), + // `ShadowSettings.translucentCasters`: a see-through mesh into the + // sun's atlas, reading the normal the others ignore. + (vertex, 'ShadowTransmittance'), ], // The picking pass draws every mesh again through the stage its layout // needs — plain, skinned or instanced; a lightmapped mesh has the plain diff --git a/packages/flutter3d_core/CHANGELOG.md b/packages/flutter3d_core/CHANGELOG.md index cce175b2b..f25ba01c4 100644 --- a/packages/flutter3d_core/CHANGELOG.md +++ b/packages/flutter3d_core/CHANGELOG.md @@ -1,5 +1,15 @@ ## Unreleased +- **See-through casters shade the sun by what they let through.** + `ShadowSettings.translucentCasters`, off by default. A blended or + transmissive material is drawn into the sun's atlas after the opaque + casters, into the three channels that held nothing, as how much of red, + green and blue it lets through: its opacity, its transmission tinted by its + colour, and the Fresnel loss of its index at that angle. Clear glass casts + a faint shadow with darker edges, a coloured liquid a shadow of its colour, + and layers combine. A see-through surface that casts is not shaded by it, + and it does nothing under the `evsm` filter. Off, every frame is drawn byte for byte as + before. - **A reload reaches the contributors too.** `Renderer.relinkShaders` asks every `PassContributor` to drop what it linked, through the new `PassContributor.relinkShaders`, which does nothing by default. diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer.dart b/packages/flutter3d_core/lib/src/engine/render/renderer.dart index 6b51491a8..6126627f1 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer.dart @@ -1129,6 +1129,22 @@ final class Renderer implements RenderServices { PipelineHandle? _skinnedMaskedCubeShadowPipeline; PipelineHandle? _instancedMaskedCubeShadowPipeline; + /// The same three once more, against `ShadowTransmittance`: see-through + /// casters writing what they let through into the sun's atlas — + /// `ShadowSettings.translucentCasters`. Never built while it is off. + PipelineHandle? _transmittanceShadowPipeline; + PipelineHandle? _skinnedTransmittanceShadowPipeline; + PipelineHandle? _instancedTransmittanceShadowPipeline; + + /// A see-through caster's colour, opacity, transmission and head-on + /// reflectance, and the way to the light, for `ShadowTransmittance`. + final TransmittanceInfoBlock _transmittanceInfo = TransmittanceInfoBlock(); + + /// Whether the sun's atlas, as last drawn, carries what see-through + /// casters let through — read by the draws that light with it, which + /// otherwise would take an atlas's empty channels for a black filter. + bool _shadowTransmits = false; + /// `gfx-60n`: the cutoff and the base alpha, packed for the shadow stages. Float32List get _shadowMask => _maskInfo.mask; PipelineHandle? _instancedShadowPipeline; @@ -3195,6 +3211,27 @@ final class Renderer implements RenderServices { depthCompare: CompareFunction.less, ); + /// How a see-through caster is drawn into the sun's atlas: tested against + /// the opaque casters' depth and writing none, so a pane behind a wall adds + /// nothing and two panes both count; both faces, since the stage counts a + /// closed body as crossed by its two surfaces; and blended so that layers + /// combine — what is taken from red and green as `src + dst·(1 − src)`, + /// which keeps the depth in red because the stage writes nought there, + /// and what is left of blue, in alpha, multiplied. See + /// `shadow_transmittance.frag` for the layout. + static const PassState _kShadowTransmittanceState = PassState( + primitiveType: PrimitiveType.triangle, + cullMode: CullMode.none, + blend: BlendState( + sourceColorFactor: BlendFactor.one, + destinationColorFactor: BlendFactor.oneMinusSourceColor, + sourceAlphaFactor: BlendFactor.destinationAlpha, + destinationAlphaFactor: BlendFactor.zero, + ), + depthWrite: false, + depthCompare: CompareFunction.less, + ); + /// Blanking one tile of the atlas by drawing over it. /// /// The pass loads rather than clears — clearing is attachment-wide and would diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart index f4be28a5d..08621e16d 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_mesh_encode.dart @@ -563,6 +563,26 @@ extension _MeshEncode on Renderer { ? settings.shadows.directionalLightRadius : ShadowSettings.sunAngularRadius; + // `ShadowSettings.translucentCasters`: whether this draw is shaded by + // what the see-through casters let through, in the one spare + // component of `shadow_bias`. Only when the atlas the shader reads is + // the one that carries it — not the moments — and never for a surface + // that is itself one of those casters, which would otherwise darken by + // its own colour wherever it stands in its own shadow. A see-through + // surface that casts nothing — a glazed floor, a water plane — is + // shaded like any other. + final castsThrough = + node.shadowCasting.casts && + (node.material.isTransparent || + (node.material.extensions?.transmission ?? 0.0) > 0.0); + _shadowCascadeBias[3] = + _shadowTransmits && + shadows.directional != null && + shadows.directionalMoments == null && + !castsThrough + ? 1.0 + : 0.0; + // Gated on the model, like every other block and sampler here. Unlit // declares FragInfo but reaches no lighting loop, so the compiler drops // all three of these — and binding a block the compiled shader does not diff --git a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart index 95fb4cd80..5f4de8b88 100644 --- a/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart +++ b/packages/flutter3d_core/lib/src/engine/render/renderer_shadow_pass.dart @@ -508,6 +508,7 @@ extension _ShadowPasses on Renderer { vm.Matrix4? shaderMatrix, vm.Frustum? frustum, { bool? only, + bool Function(MeshNode node)? seeThrough, }) { int mix(int key, int value) => 0x1fffffff & (key * 31 + value); var key = mix( @@ -518,9 +519,24 @@ extension _ShadowPasses on Renderer { key = mix(key, value.hashCode); } for (final node in scene.meshes) { - if (only != null && node.shadowIsStatic != only) continue; + // A see-through caster is never kept in the static atlas — its share + // of the map is blended into the frame's, after the walls — so it + // belongs to the frame's key whatever it is marked as. + final translucent = seeThrough?.call(node) ?? false; + if (only != null && (translucent ? only : node.shadowIsStatic != only)) { + continue; + } if (!_drawsIntoCascade(node, frustum)) continue; final material = node.material; + if (translucent) { + // What it lets through is read off the material, which no version + // follows: its colour, its transmission and its index. + key = mix(key, material.baseColor.x.hashCode); + key = mix(key, material.baseColor.y.hashCode); + key = mix(key, material.baseColor.z.hashCode); + key = mix(key, (material.extensions?.transmission ?? 0.0).hashCode); + key = mix(key, (material.extensions?.ior ?? 1.5).hashCode); + } key = mix(key, identityHashCode(node)); key = mix(key, node.worldVersion); key = mix(key, identityHashCode(material)); @@ -599,6 +615,21 @@ extension _ShadowPasses on Renderer { // row, which is the shadow a cut-out caster used to get rather than no // shadow at all, and `masked` below then never fires. final maskedShadowShader = shaders['ShadowDepthMasked'] ?? shadowShader; + // `ShadowSettings.translucentCasters`: null when it is off, under the + // moments filter whose blur takes over the channels it writes, or in a + // bundle without the stage — and then every caster is drawn as before. + final transmittanceShader = + settings.translucentCasters && + settings.directionalFilter != ShadowFilter.evsm + ? shaders['ShadowTransmittance'] + : null; + final transmits = transmittanceShader != null; + bool seeThrough(MeshNode node) { + if (!transmits) return false; + final material = node.material; + return material.isTransparent || + (material.extensions?.transmission ?? 0.0) > 0.0; + } // Casters only. The last cascade is fitted to this, so anything counted // here that cannot cast a shadow spends texels on nothing: a sky dome or a @@ -865,16 +896,26 @@ extension _ShadowPasses on Renderer { // atlas holds nothing, so every tile is drawn and the pass clears; // otherwise the pass keeps the atlas and redraws only the rest. A frame // tile's key takes in its static tile's, so a static change redraws both. + // A change of layout is a fresh atlas too: one drawn without the + // see-through channels holds ones where this frame reads "all taken". final fresh = _shadowMap == null || _shadowResolution != resolution || - _shadowCascadeCount != count; + _shadowCascadeCount != count || + _shadowTransmits != transmits; final staticFresh = fresh || _shadowMapStatic == null; // `S1`: the static casters keyed as a whole rather than per tile, since // a tile that scrolls gains the casters its new strip holds without any // of them having changed; and each tile's key is that and its matrix. final staticScene = split - ? _cascadeBakeKey(scene, settings, null, null, only: true) + ? _cascadeBakeKey( + scene, + settings, + null, + null, + only: true, + seeThrough: transmits ? seeThrough : null, + ) : 0; final staticKeys = [ for (var i = 0; i < count; i++) @@ -891,6 +932,7 @@ extension _ShadowPasses on Renderer { shaderMatrices[i], cascadeFrusta[i], only: split ? false : null, + seeThrough: transmits ? seeThrough : null, ) * 31 + (split ? staticKeys[i] + 1 : 0)), @@ -904,7 +946,14 @@ extension _ShadowPasses on Renderer { } // The tile reset is what lets a pass keep the others; a bundle without it // draws every tile from a clear, as the pass always did. - final keep = !fresh && resetShader != null && resetVertexShader != null; + // Not with see-through casters: their share is blended over what is + // there, so a redrawn tile has to start from the clear, and a clear takes + // the whole atlas. + final keep = + !fresh && + !transmits && + resetShader != null && + resetVertexShader != null; for (var i = 0; i < count; i++) { if (!keep) dirty[i] = true; if (dirty[i]) _directionalBaked[i] = keys[i]; @@ -961,6 +1010,7 @@ extension _ShadowPasses on Renderer { ); _shadowResolution = resolution; _shadowCascadeCount = count; + _shadowTransmits = transmits; } if (split && staticModes.any((mode) => mode != _StaticTile.keep)) { _shadowMapStaticSpare ??= device.createTexture(atlasSpec()); @@ -986,6 +1036,57 @@ extension _ShadowPasses on Renderer { ) : full; + /// Binds [node]'s geometry, matrices, morph, instances and joints for the + /// vertex stage [kind] picks (0 static, 1 skinned, 2 instanced, plus + /// three for a masked fragment stage), and draws it once. + void drawNode( + CommandEncoder pass, + MeshNode node, + int kind, + vm.Matrix4 drawMatrix, + ) { + final mesh = node.mesh as DrawableGeometry; + final instanced = node is InstancedMeshNode ? node : null; + final skeleton = node.skeleton; + pass.setWindingOrder( + node.worldIsMirrored + ? WindingOrder.clockwise + : WindingOrder.counterClockwise, + ); + pass.bindVertexBuffer(mesh.vertices, mesh.vertexCount); + pass.bindIndexBuffer(mesh.indices, mesh.indexType, mesh.indexCount); + + mvp + ..setFrom(drawMatrix) + ..multiply(node.worldMatrix); + final stage = switch (kind) { + 2 => instancedVertexShader, + 1 => skinnedVertexShader, + _ => vertexShader, + }; + _frameInfo.mvp.setAll(0, mvp.storage); + _frameInfo.model.setAll(0, node.worldMatrix.storage); + _frameInfo.normalMatrix.setAll(0, node.worldNormalMatrix.storage); + pass.bindBlock(stage, _frameInfo); + _bindMorph(pass, stage, node.morph); + if (instanced != null) { + _bindInstanceMorph(pass, stage, instanced); + } + if (instanced != null) { + pass.bindVertexData(instanced.instanceBytes, instanced.count, slot: 1); + } + if (skeleton != null) { + skeleton.update(node.worldMatrix); + _skinInfo.jointMatrices.setAll(0, skeleton.matrices); + pass.bindBlock(skinnedVertexShader, _skinInfo); + } + pass.draw(instanceCount: instanced?.count ?? 1); + // A caster in three cascades is three draws, and a frame that + // reported one caster and no draws at all was hiding the cost of the + // cascade count from every measurement made of it — `gfx-01n`. + _frameCounters?.drawCalls++; + } + /// Draws the casters of [cascade] into [pass] — every caster when [only] /// is null, and the static or the dynamic ones otherwise. void drawCasters( @@ -1016,11 +1117,12 @@ extension _ShadowPasses on Renderer { // vertex layout, so it needs the skinned stage here too; drawing it with // the static one would read joints and weights as position and normal. int? boundKind; - final drawMatrix = drawMatrices[cascade]; final casterFrustum = cascadeFrusta[cascade]; for (var i = 0; i < meshes.length; i++) { final node = meshes[i]; + // Drawn after the walls, by [drawSeeThrough], in the frame's atlas. + if (seeThrough(node)) continue; if (only != null && node.shadowIsStatic != only) continue; // **A caster outside this cascade is not drawn into it — `gfx-63n`.** // Every cascade used to walk the whole scene, so a level larger than @@ -1039,7 +1141,6 @@ extension _ShadowPasses on Renderer { !within.any((f) => f.intersectsWithAabb3(node.worldBounds))) { continue; } - final mesh = node.mesh as DrawableGeometry; final instanced = node is InstancedMeshNode ? node : null; // Both sides recorded for a surface that has only one, so the sun does @@ -1106,48 +1207,77 @@ extension _ShadowPasses on Renderer { boundKind = kind; } if (masked) _bindShadowMask(pass, maskedShadowShader, node.material); + drawNode(pass, node, kind, drawMatrices[cascade]); + } + } - pass.setWindingOrder( - node.worldIsMirrored - ? WindingOrder.clockwise - : WindingOrder.counterClockwise, - ); - pass.bindVertexBuffer(mesh.vertices, mesh.vertexCount); - pass.bindIndexBuffer(mesh.indices, mesh.indexType, mesh.indexCount); - - mvp - ..setFrom(drawMatrix) - ..multiply(node.worldMatrix); - final stage = switch (kind) { - 2 => instancedVertexShader, - 1 => skinnedVertexShader, - _ => vertexShader, - }; - _frameInfo.mvp.setAll(0, mvp.storage); - _frameInfo.model.setAll(0, node.worldMatrix.storage); - _frameInfo.normalMatrix.setAll(0, node.worldNormalMatrix.storage); - pass.bindBlock(stage, _frameInfo); - _bindMorph(pass, stage, node.morph); - if (instanced != null) { - _bindInstanceMorph(pass, stage, instanced); + /// Draws [cascade]'s see-through casters into [pass], over the walls + /// [drawCasters] left there — `ShadowSettings.translucentCasters`. + void drawSeeThrough(CommandEncoder pass, int cascade) { + final shader = transmittanceShader; + if (shader == null) return; + final tile = tileOf(cascade); + final casterFrustum = cascadeFrusta[cascade]; + var stated = false; + int? boundKind; + for (final node in meshes) { + if (!seeThrough(node) || !_drawsIntoCascade(node, casterFrustum)) { + continue; } - if (instanced != null) { - pass.bindVertexData( - instanced.instanceBytes, - instanced.count, - slot: 1, + if (!stated) { + pass.setState( + Renderer._kShadowTransmittanceState.copyWith( + viewport: tile, + scissor: tile, + ), ); + stated = true; } - if (skeleton != null) { - skeleton.update(node.worldMatrix); - _skinInfo.jointMatrices.setAll(0, skeleton.matrices); - pass.bindBlock(skinnedVertexShader, _skinInfo); + final kind = node is InstancedMeshNode + ? 2 + : node.skeleton != null + ? 1 + : 0; + if (boundKind != kind) { + pass.bindPipeline(switch (kind) { + 2 => + _instancedTransmittanceShadowPipeline ??= device.createPipeline( + instancedVertexShader, + shader, + layout: _kInstancedLayout, + ), + 1 => _skinnedTransmittanceShadowPipeline ??= device.createPipeline( + skinnedVertexShader, + shader, + ), + _ => _transmittanceShadowPipeline ??= device.createPipeline( + vertexShader, + shader, + ), + }); + boundKind = kind; } - pass.draw(instanceCount: instanced?.count ?? 1); - // A caster in three cascades is three draws, and a frame that - // reported one caster and no draws at all was hiding the cost of the - // cascade count from every measurement made of it — `gfx-01n`. - _frameCounters?.drawCalls++; + final material = node.material; + final colour = material.baseColor; + final ior = material.extensions?.ior ?? 1.5; + final f0 = (ior - 1.0) / (ior + 1.0); + _transmittanceInfo.color + ..[0] = colour.x + ..[1] = colour.y + ..[2] = colour.z + ..[3] = material.isTransparent ? colour.w : 1.0; + _transmittanceInfo.light + ..[0] = -aim.x + ..[1] = -aim.y + ..[2] = -aim.z + ..[3] = 0.0; + _transmittanceInfo.params + ..[0] = material.extensions?.transmission ?? 0.0 + ..[1] = f0 * f0 + ..[2] = 0.0 + ..[3] = 0.0; + pass.bindBlock(shader, _transmittanceInfo); + drawNode(pass, node, kind, drawMatrices[cascade]); } } @@ -1171,26 +1301,36 @@ extension _ShadowPasses on Renderer { _frameCounters?.drawCalls++; } - CommandEncoder open(TextureHandle target, {required bool load}) => - device.beginRenderPass( - RenderPassDescriptor( - label: _passLabel, - colors: [ - ColorTarget( - texture: target, - // Cleared to the far plane, so anything the pass does not draw - // reads as "nothing between here and the light" — or, since - // `S1`, kept, when some tiles still hold this frame's - // picture. The depth is cleared either way: it lives only as - // long as the pass, and a kept tile draws nothing that would - // test against it. - clearValue: vm.Vector4(1.0, 1.0, 1.0, 1.0), - loadAction: load ? LoadAction.load : LoadAction.clear, - ), - ], - depth: DepthTarget(texture: depth), + CommandEncoder open( + TextureHandle target, { + required bool load, + bool transmittance = false, + }) => device.beginRenderPass( + RenderPassDescriptor( + label: _passLabel, + colors: [ + ColorTarget( + texture: target, + // Cleared to the far plane, so anything the pass does not draw + // reads as "nothing between here and the light" — or, since + // `S1`, kept, when some tiles still hold this frame's + // picture. The depth is cleared either way: it lives only as + // long as the pass, and a kept tile draws nothing that would + // test against it. + // + // With see-through casters the frame's atlas clears to + // (1, 0, 0, 1) instead: the far plane, nothing taken from red + // or green, all of blue left — the layout + // `shadow_transmittance.frag` explains. + clearValue: transmittance + ? vm.Vector4(1.0, 0.0, 0.0, 1.0) + : vm.Vector4(1.0, 1.0, 1.0, 1.0), + loadAction: load ? LoadAction.load : LoadAction.clear, ), - ); + ], + depth: DepthTarget(texture: depth), + ), + ); developer.Timeline.startSync('Renderer.shadowPass'); @@ -1268,7 +1408,7 @@ extension _ShadowPasses on Renderer { _staticSceneKey = staticScene; } - final pass = open(_shadowMap!, load: keep); + final pass = open(_shadowMap!, load: keep, transmittance: transmits); for (var cascade = 0; cascade < count; cascade++) { if (!dirty[cascade]) continue; if (split) { @@ -1280,6 +1420,7 @@ extension _ShadowPasses on Renderer { if (keep) resetTile(pass, cascade); drawCasters(pass, cascade); } + drawSeeThrough(pass, cascade); } pass.submit(); developer.Timeline.finishSync(); diff --git a/packages/flutter3d_core/lib/src/engine/render/shadow_settings.dart b/packages/flutter3d_core/lib/src/engine/render/shadow_settings.dart index e792c00e3..f0d77501e 100644 --- a/packages/flutter3d_core/lib/src/engine/render/shadow_settings.dart +++ b/packages/flutter3d_core/lib/src/engine/render/shadow_settings.dart @@ -105,6 +105,7 @@ final class ShadowSettings { this.filter, this.evsmBlurRadius = 2, this.evsmBleedReduction = 0.2, + this.translucentCasters = false, }); /// How the directional map is filtered — `S2`. Null picks from @@ -146,6 +147,32 @@ final class ShadowSettings { /// darkens the soft edge with it; a fifth is the usual compromise. final double evsmBleedReduction; + /// Whether glass, liquid and other see-through casters shade the sun's + /// light by what their material lets through, instead of blocking it like + /// a wall. + /// + /// **A shadow map holds one depth per texel**, and a half-transparent caster + /// has no single depth to give it, so until this every blended or + /// transmissive material cast a shadow as dark as stone. With this on, such + /// a caster (`MaterialAlphaMode.blend`, or a transmission above nought) is + /// drawn after the opaque ones into the three channels of the atlas that + /// held nothing: how much of each of red, green and blue it lets through, + /// from its opacity, its transmission, its colour and the Fresnel loss its + /// index of refraction gives at that angle — so a clear glass casts a faint + /// shadow with darker edges, and a coloured liquid a shadow of its colour. + /// Layers combine: light through two walls and a liquid is what all three + /// let through. + /// + /// What it does not do: a receiver standing between the light and the + /// translucent caster is shaded as if it were behind it, since the atlas + /// keeps the transmittance and not where along the light it happened; and + /// it does nothing with [ShadowFilter.evsm], whose moments take over the + /// channels this uses. A see-through surface that casts is not shaded by + /// it, so a glass does not darken itself; one that casts nothing, a glazed + /// floor, is shaded like any other. Off by default, which draws every frame + /// exactly as before. + final bool translucentCasters; + /// How many cascades the directional map is split into, one to three. /// /// **Three by default, at a thousand-pixel tile.** It was one for a while, @@ -491,6 +518,7 @@ final class ShadowSettings { ShadowFilter? filter, int? evsmBlurRadius, double? evsmBleedReduction, + bool? translucentCasters, }) => // Every field, and that is not bookkeeping. This method already dropped // the point-shadow settings on the floor: `settingsFrom` calls it once a @@ -524,5 +552,6 @@ final class ShadowSettings { filter: filter ?? this.filter, evsmBlurRadius: evsmBlurRadius ?? this.evsmBlurRadius, evsmBleedReduction: evsmBleedReduction ?? this.evsmBleedReduction, + translucentCasters: translucentCasters ?? this.translucentCasters, ); } diff --git a/packages/flutter3d_cpu/CHANGELOG.md b/packages/flutter3d_cpu/CHANGELOG.md index 6715f67a3..bb5ba623e 100644 --- a/packages/flutter3d_cpu/CHANGELOG.md +++ b/packages/flutter3d_cpu/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +- **`ShadowTransmittance`** and the coloured shadow it feeds, the software + twin of the stage and of the light loop's new term + (`ShadowSettings.translucentCasters`). + ## 0.8.2+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart index df140fd70..b1e98ee25 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_builtin.dart @@ -238,6 +238,7 @@ Map builtinCpuShaders() { 'ShadowDepth': const CpuStage.fragment(ShadowDepthShader()), 'ShadowDistance': const CpuStage.fragment(ShadowDistanceShader()), 'ShadowCopy': const CpuStage.fragment(ShadowCopyShader()), + 'ShadowTransmittance': const CpuStage.fragment(ShadowTransmittanceShader()), 'EvsmFilter': const CpuStage.fragment(EvsmFilterShader()), 'ShadowTileReset': const CpuStage.fragment(ShadowTileResetShader()), 'ShadowTileResetVertex': const CpuStage.vertex( diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_lighting.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_lighting.dart index 1ccd787a5..bf71f30fa 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_lighting.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_lighting.dart @@ -421,6 +421,7 @@ Vector3 accumulateLights( // has six rows and the slot table eight entries, so a light from the list // has no row to read and asking would index past the table. var visibility = 1.0; + shadowTransmittance.setValues(1.0, 1.0, 1.0); if (i < kMaxLights) { visibility = shadowed ? shadowFactor(s, b, i, light.nDotL, c) : 1.0; visibility *= pointShadowFactor(b, s.world, s.normal, i, c); @@ -428,9 +429,9 @@ Vector3 accumulateLights( if (visibility <= 0.0) continue; final response = shade(s, light); total += Vector3( - response.x * light.radiance.x, - response.y * light.radiance.y, - response.z * light.radiance.z, + response.x * light.radiance.x * shadowTransmittance.x, + response.y * light.radiance.y * shadowTransmittance.y, + response.z * light.radiance.z * shadowTransmittance.z, )..scale(light.nDotL * visibility); } return total; diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_shadow_directional.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_shadow_directional.dart index c5b1173f6..86eef6033 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_shadow_directional.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_shadow_directional.dart @@ -24,6 +24,13 @@ import 'cpu_shaders_surface.dart'; /// [lightNDotL] is the light's `n_dot_l`, which `ShadowFactor` is handed with /// the light and no longer reads: the offset measures the slope along the /// map's own axes instead. +/// What the see-through casters between the sun and the fragment last given +/// to [shadowFactor] let through — `light_transmittance` in `surface.glsl`, +/// and the same arrangement: set as a side effect, white whenever the call +/// does not reach the atlas, read by `accumulateLights` beside the +/// visibility. +final Vector3 shadowTransmittance = Vector3.all(1.0); + double shadowFactor( Surface s, ShaderBindings b, @@ -31,6 +38,7 @@ double shadowFactor( double lightNDotL, [ FragmentContext? c, ]) { + shadowTransmittance.setValues(1.0, 1.0, 1.0); final params = b.vec4('FragInfo', 'shadow_params', Vector4.zero()); final strength = params.w; if (strength <= 0.0) return 1.0; @@ -159,7 +167,8 @@ double shadowFactor( if (projected == null) return 1.0; // Each cascade's own bias — `shadow_bias` in `surface.glsl`. - final bias = b.vec4('FragInfo', 'shadow_bias', Vector4.zero())[cascadeIndex]; + final biases = b.vec4('FragInfo', 'shadow_bias', Vector4.zero()); + final bias = biases[cascadeIndex]; // Horizontally a texel of the atlas, vertically a texel of a tile. final texelU = params.x; final texelV = cascades.w > 0.0 ? cascades.w : params.x; @@ -171,6 +180,20 @@ double shadowFactor( double tap(double du, double dv) => map.sample((u + du).clamp(loU, hiU), (vv + dv).clamp(loV, hiV)).x; + // What the see-through casters let through, before the filter, whose soft + // path leaves early — `ShadowSettings.translucentCasters`; `shadow.glsl` + // has the layout. + if (biases.w > 0.5) { + final stored = map.sample(u.clamp(loU, hiU), vv.clamp(loV, hiV)); + final k = strength.clamp(0.0, 1.0); + double through(double value) => 1.0 + (value.clamp(0.0, 1.0) - 1.0) * k; + shadowTransmittance.setValues( + through(1.0 - stored.y), + through(1.0 - stored.z), + through(stored.w), + ); + } + // `gfx-15n`: the directional light's apparent size, riding in // `ambient_ground.w` for the reason `surface.glsl` gives. Zero is the 3×3 // kernel this has always had. diff --git a/packages/flutter3d_cpu/lib/src/cpu_shaders_shadow_passes.dart b/packages/flutter3d_cpu/lib/src/cpu_shaders_shadow_passes.dart index 04cbf243a..ef68127b1 100644 --- a/packages/flutter3d_cpu/lib/src/cpu_shaders_shadow_passes.dart +++ b/packages/flutter3d_cpu/lib/src/cpu_shaders_shadow_passes.dart @@ -94,6 +94,44 @@ final class ShadowDistanceMaskedShader implements CpuFragmentShader { } } +/// `shadow_transmittance.frag`: what a see-through caster lets through to +/// the sun — `ShadowSettings.translucentCasters`. +/// +/// Green and blue are what it takes from red and green, alpha what it leaves +/// of blue, red nought so the depth beneath survives the blend; the GLSL +/// stage has the reasons, and this is the same arithmetic. +final class ShadowTransmittanceShader implements CpuFragmentShader { + const ShadowTransmittanceShader(); + + @override + Vector4? run(Float32List v, ShaderBindings bindings, FragmentContext c) { + final colour = bindings.vec4('TransmittanceInfo', 'color', Vector4.zero()); + final light = bindings.vec4('TransmittanceInfo', 'light', Vector4.zero()); + final params = bindings.vec4('TransmittanceInfo', 'params', Vector4.zero()); + final opacity = colour.w.clamp(0.0, 1.0); + final transmission = params.x.clamp(0.0, 1.0); + double body(double channel) => + (1.0 - opacity) + opacity * transmission * channel.clamp(0.0, 1.0); + + final normal = Vector3(v[kVNormal], v[kVNormal + 1], v[kVNormal + 2]); + final length = normal.length; + final facing = length > 0.0 + ? (normal.dot(Vector3(light.x, light.y, light.z)) / length).abs() + : 1.0; + final f0 = params.y.clamp(0.0, 1.0); + final fresnel = + f0 + (1.0 - f0) * math.pow(1.0 - facing.clamp(0.0, 1.0), 5.0); + double through(double channel) => + math.sqrt(math.max(body(channel), 0.0)) * (1.0 - fresnel); + return Vector4( + 0.0, + 1.0 - through(colour.x), + 1.0 - through(colour.y), + through(colour.z), + ); + } +} + /// `shadow_copy.frag`: one cascade's tile of the static atlas, into colour /// and depth — `S1`. final class ShadowCopyShader implements CpuFragmentShader { diff --git a/packages/flutter3d_shaders/CHANGELOG.md b/packages/flutter3d_shaders/CHANGELOG.md index db4f338c5..b40d0a904 100644 --- a/packages/flutter3d_shaders/CHANGELOG.md +++ b/packages/flutter3d_shaders/CHANGELOG.md @@ -1,3 +1,10 @@ +## Unreleased + +- **`ShadowTransmittance`**, the stage a see-through caster is drawn into the + sun's atlas with (`ShadowSettings.translucentCasters`), and + `light_transmittance` in `surface.glsl`, which `ShadowFactor` sets and the + light loop multiplies the sun by. + ## 0.8.2+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_shaders/lib/flutter3d_shaders.dart b/packages/flutter3d_shaders/lib/flutter3d_shaders.dart index 17ed19cf1..befa3bf9f 100644 --- a/packages/flutter3d_shaders/lib/flutter3d_shaders.dart +++ b/packages/flutter3d_shaders/lib/flutter3d_shaders.dart @@ -96,6 +96,7 @@ const List kRequiredShaders = [ (name: 'ShadowDistance', fragment: true), (name: 'ShadowDepthMasked', fragment: true), (name: 'ShadowDistanceMasked', fragment: true), + (name: 'ShadowTransmittance', fragment: true), (name: 'ShadowCopy', fragment: true), (name: 'EvsmFilter', fragment: true), (name: 'ShadowTileReset', fragment: true), diff --git a/packages/flutter3d_shaders/lib/stage_bindings.dart b/packages/flutter3d_shaders/lib/stage_bindings.dart index acb4ee93d..d3eb18f08 100644 --- a/packages/flutter3d_shaders/lib/stage_bindings.dart +++ b/packages/flutter3d_shaders/lib/stage_bindings.dart @@ -336,6 +336,10 @@ stageBindings = blocks, Set samplers})>{ ), 'ShadowTileReset': (blocks: {}, samplers: {}), 'ShadowTileResetVertex': (blocks: {}, samplers: {}), + 'ShadowTransmittance': ( + blocks: {'TransmittanceInfo'}, + samplers: {}, + ), 'Sky': (blocks: {}, samplers: {}), 'SkyCube': (blocks: {}, samplers: {'sky_texture'}), 'SkyCubeVertex': (blocks: {}, samplers: {}), diff --git a/packages/flutter3d_shaders/lib/typed_blocks.dart b/packages/flutter3d_shaders/lib/typed_blocks.dart index 87b4624dc..a62f20cd4 100644 --- a/packages/flutter3d_shaders/lib/typed_blocks.dart +++ b/packages/flutter3d_shaders/lib/typed_blocks.dart @@ -1250,6 +1250,27 @@ final class TileMaxInfoBlock extends UniformBlock { }; } +/// `TransmittanceInfo`. +final class TransmittanceInfoBlock extends UniformBlock { + TransmittanceInfoBlock() : super('TransmittanceInfo'); + + /// `color`: Vector4, at byte 0. + final Float32List color = Float32List(4); + + /// `light`: Vector4, at byte 16. + final Float32List light = Float32List(4); + + /// `params`: Vector4, at byte 32. + final Float32List params = Float32List(4); + + @override + late final Map members = { + 'color': color, + 'light': light, + 'params': params, + }; +} + /// `VelocityInfo`. final class VelocityInfoBlock extends UniformBlock { VelocityInfoBlock() : super('VelocityInfo'); diff --git a/packages/flutter3d_shaders/lib/uniform_blocks.dart b/packages/flutter3d_shaders/lib/uniform_blocks.dart index bc5e0f742..1ca3933a2 100644 --- a/packages/flutter3d_shaders/lib/uniform_blocks.dart +++ b/packages/flutter3d_shaders/lib/uniform_blocks.dart @@ -1422,6 +1422,13 @@ uniformBlocks = >>{ 'light': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), }, }, + 'ShadowTransmittance': >{ + 'TransmittanceInfo': { + 'color': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), + 'light': (offset: 16, byteLength: 16, elements: 1, type: 'Vector4'), + 'params': (offset: 32, byteLength: 16, elements: 1, type: 'Vector4'), + }, + }, 'Splat': >{ 'FogInfo': { 'fog': (offset: 0, byteLength: 16, elements: 1, type: 'Vector4'), diff --git a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json index e0181a153..ed9d7b0c6 100644 --- a/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json +++ b/packages/flutter3d_shaders/shaders/flutter3d.shaderbundle.json @@ -255,6 +255,10 @@ "type": "fragment", "file": "shaders/lighting/shadow_distance_masked.frag" }, + "ShadowTransmittance": { + "type": "fragment", + "file": "shaders/lighting/shadow_transmittance.frag" + }, "ShadowCopy": { "type": "fragment", "file": "shaders/lighting/shadow_copy.frag" diff --git a/packages/flutter3d_shaders/shaders/lib/shadow.glsl b/packages/flutter3d_shaders/shaders/lib/shadow.glsl index 50981ea4a..a063ae452 100644 --- a/packages/flutter3d_shaders/shaders/lib/shadow.glsl +++ b/packages/flutter3d_shaders/shaders/lib/shadow.glsl @@ -219,6 +219,18 @@ float ShadowFactor(Surface s, LightSample light, int lightIndex) { // kernel, and how far under minus one the value sits is the light-bleeding // cut. A sign rather than another uniform, for the reason the softness // itself rides here. + // **What the see-through casters let through** — before the filter, whose + // soft path leaves early where it finds no blocker. One bilinear tap: a + // translucent caster's shadow is light shaded rather than light stopped, + // and its edge is softened by the filtering the tap already gets. Green + // and blue hold what was taken from red and green, alpha what was left of + // blue — the layout `shadow_transmittance.frag` explains. + if (frag_info.shadow_bias.w > 0.5) { + vec4 stored = textureLod(shadow_texture, clamp(uv, tileLo, tileHi), 0.0); + vec3 through = clamp(vec3(1.0 - stored.g, 1.0 - stored.b, stored.a), 0.0, 1.0); + light_transmittance = mix(vec3(1.0), through, clamp(strength, 0.0, 1.0)); + } + float softness = frag_info.ambient_ground.w; float lit = 0.0; if (softness < 0.0) { diff --git a/packages/flutter3d_shaders/shaders/lib/surface.glsl b/packages/flutter3d_shaders/shaders/lib/surface.glsl index 5303f0ddf..174d43b2a 100644 --- a/packages/flutter3d_shaders/shaders/lib/surface.glsl +++ b/packages/flutter3d_shaders/shaders/lib/surface.glsl @@ -133,7 +133,9 @@ uniform FragInfo { vec4 ambient_ground; /// x, y, z: the depth bias of each cascade, in that cascade's own normalized - /// depth. w unused. + /// depth. w: one when the atlas carries what see-through casters let + /// through and this draw is to be shaded by it, nought otherwise — + /// `ShadowSettings.translucentCasters`; see `ShadowFactor`. /// /// `ShadowSettings.bias` is one number and a cascade's depth range is not: /// a near cascade is stretched towards the light when a caster stands @@ -672,6 +674,15 @@ LightSample SampleLight(int index, Surface s) { /// returns `ShadowFactor(...)`; an unlit one returns 1. float LightVisibility(Surface s, LightSample light, int index); +/// What the see-through casters between the sun and this fragment let +/// through, per channel — `ShadowSettings.translucentCasters`. Set by +/// `ShadowFactor` for the light it shadows and reset to one before every +/// light, so a model that samples no shadow map, and every light but the +/// sun, leaves it white. A colour beside the visibility rather than folded +/// into it, because visibility is one number in every model and coloured +/// light is not. +vec3 light_transmittance = vec3(1.0); + /// A model's per-light term, defined by each fragment shader. /// /// A prototype here and the definition in the model is what lets the loop below @@ -1090,6 +1101,7 @@ vec3 AccumulateLights(Surface s) { if (i >= count) break; LightSample light = SampleLight(i, s); if (light.n_dot_l <= 0.0) continue; + light_transmittance = vec3(1.0); // A light from the list has no shadow row to read — see `LightHasShadow`. // A branch rather than something folded into the two calls, because both // index tables eight entries wide and the ninth light would read past them @@ -1099,7 +1111,8 @@ vec3 AccumulateLights(Surface s) { PointShadowFactor(v_world_position, s.n, i) : 1.0; if (visibility <= 0.0) continue; - total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility; + total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility * + light_transmittance; } return total; diff --git a/packages/flutter3d_shaders/shaders/lighting/shadow_transmittance.frag b/packages/flutter3d_shaders/shaders/lighting/shadow_transmittance.frag new file mode 100644 index 000000000..0800515a4 --- /dev/null +++ b/packages/flutter3d_shaders/shaders/lighting/shadow_transmittance.frag @@ -0,0 +1,64 @@ +#version 460 core + +// What a see-through caster lets through to the sun's shadow map — +// `ShadowSettings.translucentCasters`. +// +// Drawn after the opaque casters into the same atlas, depth-tested against +// them and writing no depth of its own, so a pane behind a wall adds nothing +// and two panes in a row both count. +// +// **The atlas's other three channels, in a layout chosen so that every other +// stage keeps writing exactly what it wrote.** Red stays the depth. Green and +// blue hold how much of red and green is *taken away*, and alpha how much of +// blue is *let through*: an opaque caster writes (z, 0, 0, 1) and so says +// "nothing taken" without knowing this stage exists, and so does the static +// tile's copy. The pass blends green and blue as `src + dst·(1 − src)`, which +// is what absorption does over layers — 1 − (1 − a)(1 − b) — and leaves red +// alone because red comes in as nought; alpha it multiplies. +// +// What one surface lets through: +// +// * the share of it that is not there at all, 1 − opacity; +// * of the share that is, what its transmission passes, tinted by its +// colour; +// * and of that, what is not reflected away at its surface, from the +// Schlick term of its index — which is what makes the rim of a glass, met +// at a grazing angle, darker than its middle. +// +// The body's share is taken as a square root because a closed caster is +// recorded from both of its sides: light crosses its body once, and its two +// surfaces between them give it once. A caster recorded from one side only +// comes out lighter than it is, never darker. + +#define F3D_NO_SURFACE_BUFFER +#define F3D_NO_FOG + +#include + +uniform TransmittanceInfo { + /// rgb: the material's base colour. a: its opacity, one for a material + /// that is not blended. + vec4 color; + + /// xyz: the direction towards the light, in the world. w unused. + vec4 light; + + /// x: the transmission, nought for a material that has none. y: the + /// reflectance head-on, ((n − 1) / (n + 1))² of its index. zw unused. + vec4 params; +} +transmittance_info; + +void main() { + float opacity = clamp(transmittance_info.color.a, 0.0, 1.0); + float transmission = clamp(transmittance_info.params.x, 0.0, 1.0); + vec3 body = vec3(1.0 - opacity) + + opacity * transmission * clamp(transmittance_info.color.rgb, 0.0, 1.0); + + float facing = abs(dot(normalize(v_normal), transmittance_info.light.xyz)); + float f0 = clamp(transmittance_info.params.y, 0.0, 1.0); + float fresnel = f0 + (1.0 - f0) * pow(1.0 - clamp(facing, 0.0, 1.0), 5.0); + + vec3 through = sqrt(max(body, vec3(0.0))) * (1.0 - fresnel); + frag_color = vec4(0.0, 1.0 - through.r, 1.0 - through.g, through.b); +} diff --git a/packages/flutter3d_webgl/CHANGELOG.md b/packages/flutter3d_webgl/CHANGELOG.md index 3072daaa4..aed3bdd3a 100644 --- a/packages/flutter3d_webgl/CHANGELOG.md +++ b/packages/flutter3d_webgl/CHANGELOG.md @@ -1,3 +1,8 @@ +## Unreleased + +- The shader table regenerated for `ShadowTransmittance` and the coloured + sun shadow (`ShadowSettings.translucentCasters`). + ## 0.8.2+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_webgl/lib/engine_shaders.dart b/packages/flutter3d_webgl/lib/engine_shaders.dart index 7c1464f7e..1427c4bce 100644 --- a/packages/flutter3d_webgl/lib/engine_shaders.dart +++ b/packages/flutter3d_webgl/lib/engine_shaders.dart @@ -3174,7 +3174,9 @@ layout(std140) uniform FragInfo { vec4 ambient_ground; /// x, y, z: the depth bias of each cascade, in that cascade's own normalized - /// depth. w unused. + /// depth. w: one when the atlas carries what see-through casters let + /// through and this draw is to be shaded by it, nought otherwise — + /// `ShadowSettings.translucentCasters`; see `ShadowFactor`. /// /// `ShadowSettings.bias` is one number and a cascade's depth range is not: /// a near cascade is stretched towards the light when a caster stands @@ -3804,6 +3806,15 @@ LightSample SampleLight(int index, Surface s) { /// returns `ShadowFactor(...)`; an unlit one returns 1. float LightVisibility(Surface s, LightSample light, int index); +/// What the see-through casters between the sun and this fragment let +/// through, per channel — `ShadowSettings.translucentCasters`. Set by +/// `ShadowFactor` for the light it shadows and reset to one before every +/// light, so a model that samples no shadow map, and every light but the +/// sun, leaves it white. A colour beside the visibility rather than folded +/// into it, because visibility is one number in every model and coloured +/// light is not. +vec3 light_transmittance = vec3(1.0); + /// A model's per-light term, defined by each fragment shader. /// /// A prototype here and the definition in the model is what lets the loop below @@ -4222,6 +4233,7 @@ vec3 AccumulateLights(Surface s) { if (i >= count) break; LightSample light = SampleLight(i, s); if (light.n_dot_l <= 0.0) continue; + light_transmittance = vec3(1.0); // A light from the list has no shadow row to read — see `LightHasShadow`. // A branch rather than something folded into the two calls, because both // index tables eight entries wide and the ninth light would read past them @@ -4231,7 +4243,8 @@ vec3 AccumulateLights(Surface s) { PointShadowFactor(v_world_position, s.n, i) : 1.0; if (visibility <= 0.0) continue; - total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility; + total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility * + light_transmittance; } return total; @@ -4957,7 +4970,9 @@ layout(std140) uniform FragInfo { vec4 ambient_ground; /// x, y, z: the depth bias of each cascade, in that cascade's own normalized - /// depth. w unused. + /// depth. w: one when the atlas carries what see-through casters let + /// through and this draw is to be shaded by it, nought otherwise — + /// `ShadowSettings.translucentCasters`; see `ShadowFactor`. /// /// `ShadowSettings.bias` is one number and a cascade's depth range is not: /// a near cascade is stretched towards the light when a caster stands @@ -5587,6 +5602,15 @@ LightSample SampleLight(int index, Surface s) { /// returns `ShadowFactor(...)`; an unlit one returns 1. float LightVisibility(Surface s, LightSample light, int index); +/// What the see-through casters between the sun and this fragment let +/// through, per channel — `ShadowSettings.translucentCasters`. Set by +/// `ShadowFactor` for the light it shadows and reset to one before every +/// light, so a model that samples no shadow map, and every light but the +/// sun, leaves it white. A colour beside the visibility rather than folded +/// into it, because visibility is one number in every model and coloured +/// light is not. +vec3 light_transmittance = vec3(1.0); + /// A model's per-light term, defined by each fragment shader. /// /// A prototype here and the definition in the model is what lets the loop below @@ -6005,6 +6029,7 @@ vec3 AccumulateLights(Surface s) { if (i >= count) break; LightSample light = SampleLight(i, s); if (light.n_dot_l <= 0.0) continue; + light_transmittance = vec3(1.0); // A light from the list has no shadow row to read — see `LightHasShadow`. // A branch rather than something folded into the two calls, because both // index tables eight entries wide and the ninth light would read past them @@ -6014,7 +6039,8 @@ vec3 AccumulateLights(Surface s) { PointShadowFactor(v_world_position, s.n, i) : 1.0; if (visibility <= 0.0) continue; - total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility; + total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility * + light_transmittance; } return total; @@ -6726,7 +6752,9 @@ layout(std140) uniform FragInfo { vec4 ambient_ground; /// x, y, z: the depth bias of each cascade, in that cascade's own normalized - /// depth. w unused. + /// depth. w: one when the atlas carries what see-through casters let + /// through and this draw is to be shaded by it, nought otherwise — + /// `ShadowSettings.translucentCasters`; see `ShadowFactor`. /// /// `ShadowSettings.bias` is one number and a cascade's depth range is not: /// a near cascade is stretched towards the light when a caster stands @@ -7356,6 +7384,15 @@ LightSample SampleLight(int index, Surface s) { /// returns `ShadowFactor(...)`; an unlit one returns 1. float LightVisibility(Surface s, LightSample light, int index); +/// What the see-through casters between the sun and this fragment let +/// through, per channel — `ShadowSettings.translucentCasters`. Set by +/// `ShadowFactor` for the light it shadows and reset to one before every +/// light, so a model that samples no shadow map, and every light but the +/// sun, leaves it white. A colour beside the visibility rather than folded +/// into it, because visibility is one number in every model and coloured +/// light is not. +vec3 light_transmittance = vec3(1.0); + /// A model's per-light term, defined by each fragment shader. /// /// A prototype here and the definition in the model is what lets the loop below @@ -7774,6 +7811,7 @@ vec3 AccumulateLights(Surface s) { if (i >= count) break; LightSample light = SampleLight(i, s); if (light.n_dot_l <= 0.0) continue; + light_transmittance = vec3(1.0); // A light from the list has no shadow row to read — see `LightHasShadow`. // A branch rather than something folded into the two calls, because both // index tables eight entries wide and the ninth light would read past them @@ -7783,7 +7821,8 @@ vec3 AccumulateLights(Surface s) { PointShadowFactor(v_world_position, s.n, i) : 1.0; if (visibility <= 0.0) continue; - total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility; + total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility * + light_transmittance; } return total; @@ -8394,6 +8433,18 @@ float ShadowFactor(Surface s, LightSample light, int lightIndex) { // kernel, and how far under minus one the value sits is the light-bleeding // cut. A sign rather than another uniform, for the reason the softness // itself rides here. + // **What the see-through casters let through** — before the filter, whose + // soft path leaves early where it finds no blocker. One bilinear tap: a + // translucent caster's shadow is light shaded rather than light stopped, + // and its edge is softened by the filtering the tap already gets. Green + // and blue hold what was taken from red and green, alpha what was left of + // blue — the layout `shadow_transmittance.frag` explains. + if (frag_info.shadow_bias.w > 0.5) { + vec4 stored = textureLod(shadow_texture, clamp(uv, tileLo, tileHi), 0.0); + vec3 through = clamp(vec3(1.0 - stored.g, 1.0 - stored.b, stored.a), 0.0, 1.0); + light_transmittance = mix(vec3(1.0), through, clamp(strength, 0.0, 1.0)); + } + float softness = frag_info.ambient_ground.w; float lit = 0.0; if (softness < 0.0) { @@ -9205,7 +9256,9 @@ layout(std140) uniform FragInfo { vec4 ambient_ground; /// x, y, z: the depth bias of each cascade, in that cascade's own normalized - /// depth. w unused. + /// depth. w: one when the atlas carries what see-through casters let + /// through and this draw is to be shaded by it, nought otherwise — + /// `ShadowSettings.translucentCasters`; see `ShadowFactor`. /// /// `ShadowSettings.bias` is one number and a cascade's depth range is not: /// a near cascade is stretched towards the light when a caster stands @@ -9835,6 +9888,15 @@ LightSample SampleLight(int index, Surface s) { /// returns `ShadowFactor(...)`; an unlit one returns 1. float LightVisibility(Surface s, LightSample light, int index); +/// What the see-through casters between the sun and this fragment let +/// through, per channel — `ShadowSettings.translucentCasters`. Set by +/// `ShadowFactor` for the light it shadows and reset to one before every +/// light, so a model that samples no shadow map, and every light but the +/// sun, leaves it white. A colour beside the visibility rather than folded +/// into it, because visibility is one number in every model and coloured +/// light is not. +vec3 light_transmittance = vec3(1.0); + /// A model's per-light term, defined by each fragment shader. /// /// A prototype here and the definition in the model is what lets the loop below @@ -10253,6 +10315,7 @@ vec3 AccumulateLights(Surface s) { if (i >= count) break; LightSample light = SampleLight(i, s); if (light.n_dot_l <= 0.0) continue; + light_transmittance = vec3(1.0); // A light from the list has no shadow row to read — see `LightHasShadow`. // A branch rather than something folded into the two calls, because both // index tables eight entries wide and the ninth light would read past them @@ -10262,7 +10325,8 @@ vec3 AccumulateLights(Surface s) { PointShadowFactor(v_world_position, s.n, i) : 1.0; if (visibility <= 0.0) continue; - total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility; + total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility * + light_transmittance; } return total; @@ -10873,6 +10937,18 @@ float ShadowFactor(Surface s, LightSample light, int lightIndex) { // kernel, and how far under minus one the value sits is the light-bleeding // cut. A sign rather than another uniform, for the reason the softness // itself rides here. + // **What the see-through casters let through** — before the filter, whose + // soft path leaves early where it finds no blocker. One bilinear tap: a + // translucent caster's shadow is light shaded rather than light stopped, + // and its edge is softened by the filtering the tap already gets. Green + // and blue hold what was taken from red and green, alpha what was left of + // blue — the layout `shadow_transmittance.frag` explains. + if (frag_info.shadow_bias.w > 0.5) { + vec4 stored = textureLod(shadow_texture, clamp(uv, tileLo, tileHi), 0.0); + vec3 through = clamp(vec3(1.0 - stored.g, 1.0 - stored.b, stored.a), 0.0, 1.0); + light_transmittance = mix(vec3(1.0), through, clamp(strength, 0.0, 1.0)); + } + float softness = frag_info.ambient_ground.w; float lit = 0.0; if (softness < 0.0) { @@ -11719,7 +11795,9 @@ layout(std140) uniform FragInfo { vec4 ambient_ground; /// x, y, z: the depth bias of each cascade, in that cascade's own normalized - /// depth. w unused. + /// depth. w: one when the atlas carries what see-through casters let + /// through and this draw is to be shaded by it, nought otherwise — + /// `ShadowSettings.translucentCasters`; see `ShadowFactor`. /// /// `ShadowSettings.bias` is one number and a cascade's depth range is not: /// a near cascade is stretched towards the light when a caster stands @@ -12349,6 +12427,15 @@ LightSample SampleLight(int index, Surface s) { /// returns `ShadowFactor(...)`; an unlit one returns 1. float LightVisibility(Surface s, LightSample light, int index); +/// What the see-through casters between the sun and this fragment let +/// through, per channel — `ShadowSettings.translucentCasters`. Set by +/// `ShadowFactor` for the light it shadows and reset to one before every +/// light, so a model that samples no shadow map, and every light but the +/// sun, leaves it white. A colour beside the visibility rather than folded +/// into it, because visibility is one number in every model and coloured +/// light is not. +vec3 light_transmittance = vec3(1.0); + /// A model's per-light term, defined by each fragment shader. /// /// A prototype here and the definition in the model is what lets the loop below @@ -12767,6 +12854,7 @@ vec3 AccumulateLights(Surface s) { if (i >= count) break; LightSample light = SampleLight(i, s); if (light.n_dot_l <= 0.0) continue; + light_transmittance = vec3(1.0); // A light from the list has no shadow row to read — see `LightHasShadow`. // A branch rather than something folded into the two calls, because both // index tables eight entries wide and the ninth light would read past them @@ -12776,7 +12864,8 @@ vec3 AccumulateLights(Surface s) { PointShadowFactor(v_world_position, s.n, i) : 1.0; if (visibility <= 0.0) continue; - total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility; + total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility * + light_transmittance; } return total; @@ -13387,6 +13476,18 @@ float ShadowFactor(Surface s, LightSample light, int lightIndex) { // kernel, and how far under minus one the value sits is the light-bleeding // cut. A sign rather than another uniform, for the reason the softness // itself rides here. + // **What the see-through casters let through** — before the filter, whose + // soft path leaves early where it finds no blocker. One bilinear tap: a + // translucent caster's shadow is light shaded rather than light stopped, + // and its edge is softened by the filtering the tap already gets. Green + // and blue hold what was taken from red and green, alpha what was left of + // blue — the layout `shadow_transmittance.frag` explains. + if (frag_info.shadow_bias.w > 0.5) { + vec4 stored = textureLod(shadow_texture, clamp(uv, tileLo, tileHi), 0.0); + vec3 through = clamp(vec3(1.0 - stored.g, 1.0 - stored.b, stored.a), 0.0, 1.0); + light_transmittance = mix(vec3(1.0), through, clamp(strength, 0.0, 1.0)); + } + float softness = frag_info.ambient_ground.w; float lit = 0.0; if (softness < 0.0) { @@ -15065,7 +15166,9 @@ layout(std140) uniform FragInfo { vec4 ambient_ground; /// x, y, z: the depth bias of each cascade, in that cascade's own normalized - /// depth. w unused. + /// depth. w: one when the atlas carries what see-through casters let + /// through and this draw is to be shaded by it, nought otherwise — + /// `ShadowSettings.translucentCasters`; see `ShadowFactor`. /// /// `ShadowSettings.bias` is one number and a cascade's depth range is not: /// a near cascade is stretched towards the light when a caster stands @@ -15695,6 +15798,15 @@ LightSample SampleLight(int index, Surface s) { /// returns `ShadowFactor(...)`; an unlit one returns 1. float LightVisibility(Surface s, LightSample light, int index); +/// What the see-through casters between the sun and this fragment let +/// through, per channel — `ShadowSettings.translucentCasters`. Set by +/// `ShadowFactor` for the light it shadows and reset to one before every +/// light, so a model that samples no shadow map, and every light but the +/// sun, leaves it white. A colour beside the visibility rather than folded +/// into it, because visibility is one number in every model and coloured +/// light is not. +vec3 light_transmittance = vec3(1.0); + /// A model's per-light term, defined by each fragment shader. /// /// A prototype here and the definition in the model is what lets the loop below @@ -16113,6 +16225,7 @@ vec3 AccumulateLights(Surface s) { if (i >= count) break; LightSample light = SampleLight(i, s); if (light.n_dot_l <= 0.0) continue; + light_transmittance = vec3(1.0); // A light from the list has no shadow row to read — see `LightHasShadow`. // A branch rather than something folded into the two calls, because both // index tables eight entries wide and the ninth light would read past them @@ -16122,7 +16235,8 @@ vec3 AccumulateLights(Surface s) { PointShadowFactor(v_world_position, s.n, i) : 1.0; if (visibility <= 0.0) continue; - total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility; + total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility * + light_transmittance; } return total; @@ -16733,6 +16847,18 @@ float ShadowFactor(Surface s, LightSample light, int lightIndex) { // kernel, and how far under minus one the value sits is the light-bleeding // cut. A sign rather than another uniform, for the reason the softness // itself rides here. + // **What the see-through casters let through** — before the filter, whose + // soft path leaves early where it finds no blocker. One bilinear tap: a + // translucent caster's shadow is light shaded rather than light stopped, + // and its edge is softened by the filtering the tap already gets. Green + // and blue hold what was taken from red and green, alpha what was left of + // blue — the layout `shadow_transmittance.frag` explains. + if (frag_info.shadow_bias.w > 0.5) { + vec4 stored = textureLod(shadow_texture, clamp(uv, tileLo, tileHi), 0.0); + vec3 through = clamp(vec3(1.0 - stored.g, 1.0 - stored.b, stored.a), 0.0, 1.0); + light_transmittance = mix(vec3(1.0), through, clamp(strength, 0.0, 1.0)); + } + float softness = frag_info.ambient_ground.w; float lit = 0.0; if (softness < 0.0) { @@ -18379,7 +18505,9 @@ layout(std140) uniform FragInfo { vec4 ambient_ground; /// x, y, z: the depth bias of each cascade, in that cascade's own normalized - /// depth. w unused. + /// depth. w: one when the atlas carries what see-through casters let + /// through and this draw is to be shaded by it, nought otherwise — + /// `ShadowSettings.translucentCasters`; see `ShadowFactor`. /// /// `ShadowSettings.bias` is one number and a cascade's depth range is not: /// a near cascade is stretched towards the light when a caster stands @@ -19009,6 +19137,15 @@ LightSample SampleLight(int index, Surface s) { /// returns `ShadowFactor(...)`; an unlit one returns 1. float LightVisibility(Surface s, LightSample light, int index); +/// What the see-through casters between the sun and this fragment let +/// through, per channel — `ShadowSettings.translucentCasters`. Set by +/// `ShadowFactor` for the light it shadows and reset to one before every +/// light, so a model that samples no shadow map, and every light but the +/// sun, leaves it white. A colour beside the visibility rather than folded +/// into it, because visibility is one number in every model and coloured +/// light is not. +vec3 light_transmittance = vec3(1.0); + /// A model's per-light term, defined by each fragment shader. /// /// A prototype here and the definition in the model is what lets the loop below @@ -19427,6 +19564,7 @@ vec3 AccumulateLights(Surface s) { if (i >= count) break; LightSample light = SampleLight(i, s); if (light.n_dot_l <= 0.0) continue; + light_transmittance = vec3(1.0); // A light from the list has no shadow row to read — see `LightHasShadow`. // A branch rather than something folded into the two calls, because both // index tables eight entries wide and the ninth light would read past them @@ -19436,7 +19574,8 @@ vec3 AccumulateLights(Surface s) { PointShadowFactor(v_world_position, s.n, i) : 1.0; if (visibility <= 0.0) continue; - total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility; + total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility * + light_transmittance; } return total; @@ -20047,6 +20186,18 @@ float ShadowFactor(Surface s, LightSample light, int lightIndex) { // kernel, and how far under minus one the value sits is the light-bleeding // cut. A sign rather than another uniform, for the reason the softness // itself rides here. + // **What the see-through casters let through** — before the filter, whose + // soft path leaves early where it finds no blocker. One bilinear tap: a + // translucent caster's shadow is light shaded rather than light stopped, + // and its edge is softened by the filtering the tap already gets. Green + // and blue hold what was taken from red and green, alpha what was left of + // blue — the layout `shadow_transmittance.frag` explains. + if (frag_info.shadow_bias.w > 0.5) { + vec4 stored = textureLod(shadow_texture, clamp(uv, tileLo, tileHi), 0.0); + vec3 through = clamp(vec3(1.0 - stored.g, 1.0 - stored.b, stored.a), 0.0, 1.0); + light_transmittance = mix(vec3(1.0), through, clamp(strength, 0.0, 1.0)); + } + float softness = frag_info.ambient_ground.w; float lit = 0.0; if (softness < 0.0) { @@ -30101,6 +30252,412 @@ void main() { frag_color = vec4(clamp(distance / range, 0.0, 1.0), 0.0, 0.0, 1.0); } +''', + 'ShadowTransmittance': r'''#version 300 es +precision highp float; +precision highp int; +precision highp sampler2D; +precision highp samplerCube; + +// What a see-through caster lets through to the sun's shadow map — +// `ShadowSettings.translucentCasters`. +// +// Drawn after the opaque casters into the same atlas, depth-tested against +// them and writing no depth of its own, so a pane behind a wall adds nothing +// and two panes in a row both count. +// +// **The atlas's other three channels, in a layout chosen so that every other +// stage keeps writing exactly what it wrote.** Red stays the depth. Green and +// blue hold how much of red and green is *taken away*, and alpha how much of +// blue is *let through*: an opaque caster writes (z, 0, 0, 1) and so says +// "nothing taken" without knowing this stage exists, and so does the static +// tile's copy. The pass blends green and blue as `src + dst·(1 − src)`, which +// is what absorption does over layers — 1 − (1 − a)(1 − b) — and leaves red +// alone because red comes in as nought; alpha it multiplies. +// +// What one surface lets through: +// +// * the share of it that is not there at all, 1 − opacity; +// * of the share that is, what its transmission passes, tinted by its +// colour; +// * and of that, what is not reflected away at its surface, from the +// Schlick term of its index — which is what makes the rim of a glass, met +// at a grazing angle, darker than its middle. +// +// The body's share is taken as a square root because a closed caster is +// recorded from both of its sides: light crosses its body once, and its two +// surfaces between them give it once. A caster recorded from one side only +// comes out lighter than it is, never darker. + +#define F3D_NO_SURFACE_BUFFER +#define F3D_NO_FOG + +// --- lib/color.glsl --- +// Colour space helpers and the fragment output interface. +// +// Split out of surface.glsl so a shader that needs no material inputs — the +// normals debug view — can avoid DECLARING the FragInfo uniform block at all. +// That matters more than it looks: reflection metadata reports a block as +// present merely because it was declared, even when the compiled shader binds +// no such buffer, so a declared-but-unused block is indistinguishable from a +// used one until Metal crashes on the bind. + +#ifndef COLOR_GLSL_ +#define COLOR_GLSL_ + +precision highp float; + +const float kPi = 3.14159265359; + +// One varying set shared by every fragment shader, matching mesh.vert. +// +// All five are declared here, including the two the debug models never read: a +// fragment shader whose `in` block disagrees with the vertex shader's `out` +// block fails to link, and there is no partial-match rule to lean on. +in vec3 v_world_position; +in vec3 v_normal; +in vec2 v_texcoord; +in vec4 v_tangent; +in vec4 v_color; + +/// Where this fragment is in the level's lightmap. Zero from every vertex +/// stage but `mesh_lightmapped.vert`, and read only by the lit models, which +/// sample a one-texel black there when a material has no map. +in vec2 v_lightmap_uv; + +layout(location = 0) out vec4 frag_color; + +// The second attachment: what a screen-space effect needs to know about the +// surface it is looking at. World-space normal in rgb, and in a the depth along +// the view axis in world metres — not a window depth; `WriteSurfaceGeometry` +// says at length why not. +// +// Depth travels here rather than in a depth texture because flutter_gpu cannot +// sample one — the same reason the shadow pass writes its depth into a colour +// target. See ARCHITECTURE.md §2. +// +// Guarded, because not every stage that includes this header draws into a +// two-attachment target. The shadow pass draws into one, and a pipeline +// declaring an output its target has no slot for is a mismatch worth avoiding +// rather than discovering. +#ifndef F3D_NO_SURFACE_BUFFER +layout(location = 1) out vec4 frag_surface; + +/// The surface's own colour, sRGB-encoded, alpha one where a surface was +/// drawn — `L5`. The third attachment, present only when a pass reads it (the +/// indirect light does) and the device opens three; like the surface buffer, +/// written unconditionally and discarded when absent. Stored in the surface +/// buffer's format rather than eight bits a channel, and `Renderer` says why. +layout(location = 2) out vec4 frag_albedo; +#endif + +/// What [frag_albedo] carries: the lit models set it in `ReadSurface`, and a +/// stage that reflects nothing — unlit, the debug views — leaves it black, +/// which is what light bounced onto it would come to. +vec3 g_albedo = vec3(0.0); + +/// Octahedral encoding: a unit vector in two channels instead of three. +/// +/// Worth the arithmetic because the fourth channel is already spent on depth, +/// and without a free channel there is nowhere to put roughness — which is the +/// difference between a reflection that knows stone from a mirror and one that +/// does not. The error is well under a degree, far below anything a reflection +/// off rough stone would show. +vec2 EncodeOctahedral(vec3 n) { + n /= abs(n.x) + abs(n.y) + abs(n.z); + vec2 e = n.xy; + if (n.z < 0.0) { + e = (1.0 - abs(n.yx)) * vec2(n.x >= 0.0 ? 1.0 : -1.0, + n.y >= 0.0 ? 1.0 : -1.0); + } + return e * 0.5 + 0.5; +} + +/// Where a debug pass leaves the picture it wants shown instead of the normal. +/// +/// Declared here, in the header every lit shader includes **first**, and +/// written from surface.glsl, which is included after. The alternative was a +/// new member on a shared uniform block; a global costs nothing and moves no +/// offsets. It is read at the moment the surface buffer is written, which +/// happens after the lighting loop has run, so the value is there by then. +vec3 g_debug_surface = vec3(0.0); +bool g_debug_surface_on = false; + +/// Whether [WriteSurface] weights the colour by its alpha: set by +/// `ReadSurface` for a material that blends, and false for everything else. +/// +/// **The blend takes its source as premultiplied**, so a blended surface has +/// to hand it the colour times the alpha — a pane at a fifth of opaque adds a +/// fifth of its light, not all of it. glTF's blend mode is Porter and Duff's +/// over on straight colour, and this is the one place that turns the lit +/// radiance into what that means. An opaque or masked surface keeps its +/// colour whole: its alpha is not a coverage, and nothing blends it. +/// A global for the reason [g_debug_surface] is one. +bool g_premultiply = false; + +// **A stage that needs none of this must be able to declare none of it.** On +// Vulkan both stages' descriptors are merged into one set layout, and two +// bindings with the same number in it is not a layout the specification +// allows. A driver may accept it anyway; a Galaxy A55's refuses the pipeline +// with `ErrorUnknown` and no other word, which is how the shadow pass came to +// build everywhere except there — its only uniform block was this one, and it +// landed on the same binding as the vertex stage's first. +#ifndef F3D_NO_FOG + +/// Distance fog, in its own block rather than folded into FragInfo. +/// +/// Its own because color.glsl is included before FragInfo is declared, and +/// because appending to a block that half a dozen shaders already share is a +/// way to move offsets nobody expected to move. Three vec4s is a cheap price +/// for not touching any of that. +layout(std140) uniform FogInfo { + /// rgb: linear fog colour. w: density per metre, zero for no fog. + vec4 fog; + + /// xyz: camera position in world space. Duplicated from FragInfo so this + /// block stands alone; a vec3 is cheaper than a coupling. + vec4 eye; + + /// xyz: the direction the camera looks, as a unit vector in world space. + /// w: what a transparent draw writes under weighted blended transparency — + /// `R8`, see `WriteWeightedBlended`. Zero for every other draw. + /// + /// Here rather than in a block of its own because it answers the same + /// question [eye] does — where the camera is and which way it faces — and + /// this is the block `color.glsl` can see. + vec4 forward; +} +fog_info; + +/// How far this fragment is from the eye, in world metres. +/// +/// What the fog fades by. Distance rather than depth, because fog is a +/// property of the air between two points and does not care which way the +/// camera happens to face. +float EyeDistance() { return distance(v_world_position, fog_info.eye.xyz); } + +/// How far this fragment is *along the view axis*, in world metres. +/// +/// What the surface buffer's alpha holds. Depth rather than distance, and the +/// difference only shows on an orthographic camera — where the rays through +/// the pixels are parallel instead of meeting at the eye, so a distance from +/// the eye names a sphere that the pixel's ray crosses somewhere the reader +/// cannot solve for. A depth along the axis names a plane, which every ray +/// crosses exactly once. See `WorldAtDepth` in `post/ssao.frag` for the +/// reconstruction both projections share. +float ViewDepth() { + return dot(v_world_position - fog_info.eye.xyz, fog_info.forward.xyz); +} + +#else // F3D_NO_FOG + +// The same two questions, answered without the block: a stage that declares no +// fog has no eye position to measure from either. Stubs rather than a guard at +// every call site, so that what includes this file reads the same whichever +// way it was compiled. +float EyeDistance() { return 0.0; } +float ViewDepth() { return 0.0; } + +#endif // F3D_NO_FOG + +/// sRGB to linear. Textures are authored in sRGB, but lighting is only correct +/// in linear space; skipping this is what makes naive renderers look muddy. +vec3 SrgbToLinear(vec3 srgb) { + return mix( + srgb / 12.92, + pow((srgb + vec3(0.055)) / 1.055, vec3(2.4)), + step(vec3(0.04045), srgb)); +} + +/// Linear to sRGB. The render target is a plain UNorm format rather than an +/// sRGB one, so the encode has to happen here. +vec3 LinearToSrgb(vec3 linear) { + return mix( + linear * 12.92, + 1.055 * pow(linear, vec3(1.0 / 2.4)) - vec3(0.055), + step(vec3(0.0031308), linear)); +} + +/// Writes scene-referred linear light into the HDR target. +/// +/// No tone map and no sRGB encode: those moved into the composite pass, which +/// is the entire point of rendering into `r16g16b16a16Float` first. Applying +/// them here meant every model wrote display-referred colour into an 8-bit +/// buffer, so anything above display white was gone before post-processing +/// could see it — and bloom is a function of exactly that. +/// +/// Exposure moved with them, for the same reason: it belongs on the same side +/// of the display transform as the tone map. +/// Records the geometry of this fragment for whatever runs after the scene. +/// +/// Called from the same place that writes colour, so a surface cannot be lit +/// into the frame without also describing itself — which is the failure that +/// leaves a screen-space effect reflecting whatever was in the buffer before. +/// +/// rg: octahedral normal. b: perceptual roughness. a: **depth along the view +/// axis, in world metres** — see [ViewDepth]. +/// +/// **Not `gl_FragCoord.z`, and that is a defect this channel carried until it +/// was looked at.** Window depth crowds every distant surface into the top of +/// its range — with a near plane of a tenth of a metre, everything past twenty +/// metres lives in the last half a hundredth of `[0, 1]` — and this attachment +/// is a half float, whose steps up there are about five ten-thousandths. So two +/// surfaces half a metre apart at twenty metres stored the *same* number, and +/// every screen-space pass that compares against this channel decided whole +/// bands of pixels by rounding. The occlusion pass drew them: vertical stripes +/// along the lines of constant depth on any wall receding from the camera, on +/// both GPU backends. The software rasteriser kept the channel at full +/// precision and drew the effect correctly, so it was the one that looked +/// wrong against the other two. +/// +/// A depth in metres has none of that: the exponent carries the range and the +/// mantissa carries the same relative precision everywhere, which at twenty +/// metres is a centimetre. Both numbers are measured in +/// `flutter3d/test/surface_depth_test.dart`. +/// +/// Zero still means nothing was drawn. The attachment is cleared to zero and +/// nothing is drawn in front of the near plane. +void WriteSurfaceGeometry(float roughness) { +#ifndef F3D_NO_SURFACE_BUFFER + // `L5`: the surface's colour, whatever the surface buffer ends up holding. + frag_albedo = vec4(LinearToSrgb(clamp(g_albedo, vec3(0.0), vec3(1.0))), 1.0); + // A debug pass takes the buffer over rather than getting one of its own. + // The surface buffer already has an attachment, a viewer and a golden; a + // second one would need all three built before it could answer anything. + if (g_debug_surface_on) { + frag_surface = vec4(g_debug_surface, ViewDepth()); + return; + } + // Reversed on a back face, as the lit normal is, so the occlusion and + // reflection passes see the side of a double-sided surface that faces them. + vec3 geometric = normalize(v_normal); + if (!gl_FrontFacing) geometric = -geometric; + frag_surface = vec4(EncodeOctahedral(geometric), + clamp(roughness, 0.0, 1.0), ViewDepth()); +#endif +} + +/// Fades [color] toward the fog with distance from the eye. +/// +/// Exponential rather than linear, because linear fog has a visible plane +/// where it starts and a dungeon corridor is exactly where that shows. +vec3 ApplyFog(vec3 color) { +#ifdef F3D_NO_FOG + return color; +#else + float density = fog_info.fog.w; + if (density <= 0.0) return color; + float d = EyeDistance(); + return mix(fog_info.fog.rgb, color, clamp(exp(-density * d), 0.0, 1.0)); +#endif +} + +/// How much a transparent fragment counts for against the others over its +/// pixel — `R8`. McGuire and Bavoil's depth weight (their equation 9): a near +/// layer outweighs a far one, which is all the ordering a weighted average +/// can keep. [alpha] multiplies it, as theirs does, so a faint layer counts +/// faintly. Depth along the view axis, in metres, the surface buffer's. +float WeightedBlendedWeight(float alpha) { + float z = abs(ViewDepth()); + float near = z / 5.0; + float far = z / 200.0; + float far3 = far * far * far; + return alpha * + clamp(10.0 / (1e-5 + near * near + far3 * far3), 1e-2, 3e3); +} + +/// What a transparent draw writes when the frame composites transparency +/// order-independently — `R8`. `fog_info.forward.w` says which: +/// +/// - 0: [frag_color] as it stands, the sorted blend's source. Every opaque +/// draw, and every draw in a frame that sorts. +/// - 1: the accumulation target's share — the colour, which the engine keeps +/// premultiplied, and the alpha, both times the weight. Added. +/// - 2: the revealage target's — the alpha alone, in every channel, which the +/// blend multiplies the target by one minus of. +/// - 3: both at once, the second into attachment one, where the surface +/// buffer would be; the pass that asks has no surface buffer attached. +/// +/// Selects rather than returns, because a phi of constants is what +/// SPIRV-Cross refuses. At nought the branch is not taken and [frag_color] +/// is untouched, which is what keeps a sorting frame byte-identical. +void WriteWeightedBlended() { +#ifndef F3D_NO_FOG + float mode = fog_info.forward.w; + if (mode > 0.5) { + float alpha = frag_color.a; + float weight = WeightedBlendedWeight(alpha); + vec4 accumulate = vec4(frag_color.rgb * weight, alpha * weight); + bool revealage = mode > 1.5 && mode < 2.5; + frag_color = revealage ? vec4(alpha) : accumulate; +#ifndef F3D_NO_SURFACE_BUFFER + if (mode > 2.5) frag_surface = vec4(alpha); +#endif + } +#endif +} + +/// The fog is mixed in before the weight, so a thin distant pane adds a thin +/// share of the fog too rather than all of it. Times one when nothing blends, +/// which is exact, so an opaque draw writes what it always wrote. +void WriteSurface(vec3 linearColor, float alpha, float roughness) { + float weight = g_premultiply ? alpha : 1.0; + frag_color = vec4(ApplyFog(linearColor) * weight, alpha); + WriteSurfaceGeometry(roughness); + WriteWeightedBlended(); +} + +/// For a stage with no material to speak of. +/// +/// Fully rough, which is the honest default: a surface that cannot say how +/// polished it is should not be reflected off. +void WriteSurface(vec3 linearColor, float alpha) { + WriteSurface(linearColor, alpha, 1.0); +} + +/// Writes a value that is already display-referred. +/// +/// For debug output, where the colour is not a light value at all: a normal +/// encoded as RGB means nothing after a tone curve. Converting to linear here +/// means the composite pass's sRGB encode hands the original back unchanged, +/// provided the view also turns tone mapping and exposure off — which is what +/// `RenderSettings.tonemap` is for. +void WriteDisplayColor(vec3 displayColor, float alpha) { + frag_color = vec4(SrgbToLinear(displayColor), alpha); + WriteSurfaceGeometry(1.0); +} + +#endif // COLOR_GLSL_ + + +layout(std140) uniform TransmittanceInfo { + /// rgb: the material's base colour. a: its opacity, one for a material + /// that is not blended. + vec4 color; + + /// xyz: the direction towards the light, in the world. w unused. + vec4 light; + + /// x: the transmission, nought for a material that has none. y: the + /// reflectance head-on, ((n − 1) / (n + 1))² of its index. zw unused. + vec4 params; +} +transmittance_info; + +void main() { + float opacity = clamp(transmittance_info.color.a, 0.0, 1.0); + float transmission = clamp(transmittance_info.params.x, 0.0, 1.0); + vec3 body = vec3(1.0 - opacity) + + opacity * transmission * clamp(transmittance_info.color.rgb, 0.0, 1.0); + + float facing = abs(dot(normalize(v_normal), transmittance_info.light.xyz)); + float f0 = clamp(transmittance_info.params.y, 0.0, 1.0); + float fresnel = f0 + (1.0 - f0) * pow(1.0 - clamp(facing, 0.0, 1.0), 5.0); + + vec3 through = sqrt(max(body, vec3(0.0))) * (1.0 - fresnel); + frag_color = vec4(0.0, 1.0 - through.r, 1.0 - through.g, through.b); +} + ''', 'ShadowCopy': r'''#version 300 es precision highp float; @@ -32227,7 +32784,9 @@ layout(std140) uniform FragInfo { vec4 ambient_ground; /// x, y, z: the depth bias of each cascade, in that cascade's own normalized - /// depth. w unused. + /// depth. w: one when the atlas carries what see-through casters let + /// through and this draw is to be shaded by it, nought otherwise — + /// `ShadowSettings.translucentCasters`; see `ShadowFactor`. /// /// `ShadowSettings.bias` is one number and a cascade's depth range is not: /// a near cascade is stretched towards the light when a caster stands @@ -32857,6 +33416,15 @@ LightSample SampleLight(int index, Surface s) { /// returns `ShadowFactor(...)`; an unlit one returns 1. float LightVisibility(Surface s, LightSample light, int index); +/// What the see-through casters between the sun and this fragment let +/// through, per channel — `ShadowSettings.translucentCasters`. Set by +/// `ShadowFactor` for the light it shadows and reset to one before every +/// light, so a model that samples no shadow map, and every light but the +/// sun, leaves it white. A colour beside the visibility rather than folded +/// into it, because visibility is one number in every model and coloured +/// light is not. +vec3 light_transmittance = vec3(1.0); + /// A model's per-light term, defined by each fragment shader. /// /// A prototype here and the definition in the model is what lets the loop below @@ -33275,6 +33843,7 @@ vec3 AccumulateLights(Surface s) { if (i >= count) break; LightSample light = SampleLight(i, s); if (light.n_dot_l <= 0.0) continue; + light_transmittance = vec3(1.0); // A light from the list has no shadow row to read — see `LightHasShadow`. // A branch rather than something folded into the two calls, because both // index tables eight entries wide and the ninth light would read past them @@ -33284,7 +33853,8 @@ vec3 AccumulateLights(Surface s) { PointShadowFactor(v_world_position, s.n, i) : 1.0; if (visibility <= 0.0) continue; - total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility; + total += ShadeLight(s, light) * light.radiance * light.n_dot_l * visibility * + light_transmittance; } return total; @@ -33502,6 +34072,18 @@ float ShadowFactor(Surface s, LightSample light, int lightIndex) { // kernel, and how far under minus one the value sits is the light-bleeding // cut. A sign rather than another uniform, for the reason the softness // itself rides here. + // **What the see-through casters let through** — before the filter, whose + // soft path leaves early where it finds no blocker. One bilinear tap: a + // translucent caster's shadow is light shaded rather than light stopped, + // and its edge is softened by the filtering the tap already gets. Green + // and blue hold what was taken from red and green, alpha what was left of + // blue — the layout `shadow_transmittance.frag` explains. + if (frag_info.shadow_bias.w > 0.5) { + vec4 stored = textureLod(shadow_texture, clamp(uv, tileLo, tileHi), 0.0); + vec3 through = clamp(vec3(1.0 - stored.g, 1.0 - stored.b, stored.a), 0.0, 1.0); + light_transmittance = mix(vec3(1.0), through, clamp(strength, 0.0, 1.0)); + } + float softness = frag_info.ambient_ground.w; float lit = 0.0; if (softness < 0.0) { diff --git a/packages/flutter3d_webgpu/CHANGELOG.md b/packages/flutter3d_webgpu/CHANGELOG.md index ad3ae5e31..b6a9e8c71 100644 --- a/packages/flutter3d_webgpu/CHANGELOG.md +++ b/packages/flutter3d_webgpu/CHANGELOG.md @@ -1,3 +1,8 @@ +## Unreleased + +- The shader table regenerated for `ShadowTransmittance` and the coloured + sun shadow (`ShadowSettings.translucentCasters`). + ## 0.8.2+1 **Resolves on Flutter 3.44 and Dart 3.12.0.** The constraints asked for Dart diff --git a/packages/flutter3d_webgpu/lib/engine_shaders.dart b/packages/flutter3d_webgpu/lib/engine_shaders.dart index dd3f1f32f..fd0404a78 100644 --- a/packages/flutter3d_webgpu/lib/engine_shaders.dart +++ b/packages/flutter3d_webgpu/lib/engine_shaders.dart @@ -4100,6 +4100,7 @@ var g_albedo: vec3; var g_debug_surface: vec3; var g_debug_surface_on: bool; var g_premultiply: bool; +var light_transmittance: vec3; var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; @@ -4120,10 +4121,10 @@ var v_tangent_1: vec4; var v_lightmap_uv_1: vec2; fn ViewDepth_u0028_() -> f32 { - let _e75 = v_world_position_1; - let _e77 = fog_info.eye; - let _e81 = fog_info.forward; - return dot((_e75 - _e77.xyz), _e81.xyz); + let _e76 = v_world_position_1; + let _e78 = fog_info.eye; + let _e82 = fog_info.forward; + return dot((_e76 - _e78.xyz), _e82.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -4132,22 +4133,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e80 = ViewDepth_u0028_(); - z = abs(_e80); - let _e82 = z; - near = (_e82 / 5f); - let _e84 = z; - far = (_e84 / 200f); - let _e86 = far; + let _e81 = ViewDepth_u0028_(); + z = abs(_e81); + let _e83 = z; + near = (_e83 / 5f); + let _e85 = z; + far = (_e85 / 200f); let _e87 = far; - let _e89 = far; - far3_ = ((_e86 * _e87) * _e89); - let _e91 = (*alpha); - let _e92 = near; + let _e88 = far; + let _e90 = far; + far3_ = ((_e87 * _e88) * _e90); + let _e92 = (*alpha); let _e93 = near; - let _e96 = far3_; + let _e94 = near; let _e97 = far3_; - return (_e91 * clamp((10f / ((0.00001f + (_e92 * _e93)) + (_e96 * _e97))), 0.01f, 3000f)); + let _e98 = far3_; + return (_e92 * clamp((10f / ((0.00001f + (_e93 * _e94)) + (_e97 * _e98))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -4159,39 +4160,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e84 = fog_info.forward[3u]; - mode = _e84; - let _e85 = mode; - if (_e85 > 0.5f) { - let _e88 = frag_color[3u]; - alpha_1 = _e88; - let _e89 = alpha_1; - param = _e89; - let _e90 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e90; - let _e91 = frag_color; - let _e93 = weight; - let _e94 = (_e91.xyz * _e93); - let _e95 = alpha_1; - let _e96 = weight; - accumulate = vec4(_e94.x, _e94.y, _e94.z, (_e95 * _e96)); - let _e102 = mode; - let _e104 = mode; - revealage = ((_e102 > 1.5f) && (_e104 < 2.5f)); - let _e107 = revealage; - if _e107 { - let _e108 = alpha_1; - local = vec4(_e108); + let _e85 = fog_info.forward[3u]; + mode = _e85; + let _e86 = mode; + if (_e86 > 0.5f) { + let _e89 = frag_color[3u]; + alpha_1 = _e89; + let _e90 = alpha_1; + param = _e90; + let _e91 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e91; + let _e92 = frag_color; + let _e94 = weight; + let _e95 = (_e92.xyz * _e94); + let _e96 = alpha_1; + let _e97 = weight; + accumulate = vec4(_e95.x, _e95.y, _e95.z, (_e96 * _e97)); + let _e103 = mode; + let _e105 = mode; + revealage = ((_e103 > 1.5f) && (_e105 < 2.5f)); + let _e108 = revealage; + if _e108 { + let _e109 = alpha_1; + local = vec4(_e109); } else { - let _e110 = accumulate; - local = _e110; + let _e111 = accumulate; + local = _e111; } - let _e111 = local; - frag_color = _e111; - let _e112 = mode; - if (_e112 > 2.5f) { - let _e114 = alpha_1; - frag_surface = vec4(_e114); + let _e112 = local; + frag_color = _e112; + let _e113 = mode; + if (_e113 > 2.5f) { + let _e115 = alpha_1; + frag_surface = vec4(_e115); } } return; @@ -4200,29 +4201,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e78 = (*n)[0u]; - let _e81 = (*n)[1u]; - let _e85 = (*n)[2u]; - let _e88 = (*n); - (*n) = (_e88 / vec3(((abs(_e78) + abs(_e81)) + abs(_e85)))); - let _e91 = (*n); - e = _e91.xy; - let _e94 = (*n)[2u]; - if (_e94 < 0f) { - let _e96 = (*n); - let _e102 = (*n)[0u]; - let _e106 = (*n)[1u]; - e = ((vec2(1f) - abs(_e96.yx)) * vec2(select(-1f, 1f, (_e102 >= 0f)), select(-1f, 1f, (_e106 >= 0f)))); + let _e79 = (*n)[0u]; + let _e82 = (*n)[1u]; + let _e86 = (*n)[2u]; + let _e89 = (*n); + (*n) = (_e89 / vec3(((abs(_e79) + abs(_e82)) + abs(_e86)))); + let _e92 = (*n); + e = _e92.xy; + let _e95 = (*n)[2u]; + if (_e95 < 0f) { + let _e97 = (*n); + let _e103 = (*n)[0u]; + let _e107 = (*n)[1u]; + e = ((vec2(1f) - abs(_e97.yx)) * vec2(select(-1f, 1f, (_e103 >= 0f)), select(-1f, 1f, (_e107 >= 0f)))); } - let _e111 = e; - return ((_e111 * 0.5f) + vec2(0.5f)); + let _e112 = e; + return ((_e112 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e76 = (*linear); - let _e78 = (*linear); - let _e82 = (*linear); - return mix((_e76 * 12.92f), ((pow(_e78, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e82)); + let _e77 = (*linear); + let _e79 = (*linear); + let _e83 = (*linear); + return mix((_e77 * 12.92f), ((pow(_e79, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e83)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -4230,57 +4231,57 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e79 = g_albedo; - param_1 = clamp(_e79, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e81 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e81.x, _e81.y, _e81.z, 1f); - let _e86 = g_debug_surface_on; - if _e86 { - let _e87 = g_debug_surface; - let _e88 = ViewDepth_u0028_(); - frag_surface = vec4(_e87.x, _e87.y, _e87.z, _e88); + let _e80 = g_albedo; + param_1 = clamp(_e80, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e82 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e82.x, _e82.y, _e82.z, 1f); + let _e87 = g_debug_surface_on; + if _e87 { + let _e88 = g_debug_surface; + let _e89 = ViewDepth_u0028_(); + frag_surface = vec4(_e88.x, _e88.y, _e88.z, _e89); return; } - let _e93 = v_normal_1; - geometric = normalize(_e93); - let _e95 = gl_FrontFacing_1; - if !(_e95) { - let _e97 = geometric; - geometric = -(_e97); - } - let _e99 = geometric; - param_2 = _e99; - let _e100 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e101 = (*roughness); - let _e103 = ViewDepth_u0028_(); - frag_surface = vec4(_e100.x, _e100.y, clamp(_e101, 0f, 1f), _e103); + let _e94 = v_normal_1; + geometric = normalize(_e94); + let _e96 = gl_FrontFacing_1; + if !(_e96) { + let _e98 = geometric; + geometric = -(_e98); + } + let _e100 = geometric; + param_2 = _e100; + let _e101 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e102 = (*roughness); + let _e104 = ViewDepth_u0028_(); + frag_surface = vec4(_e101.x, _e101.y, clamp(_e102, 0f, 1f), _e104); return; } fn EyeDistance_u0028_() -> f32 { - let _e75 = v_world_position_1; - let _e77 = fog_info.eye; - return distance(_e75, _e77.xyz); + let _e76 = v_world_position_1; + let _e78 = fog_info.eye; + return distance(_e76, _e78.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; - let _e80 = fog_info.fog[3u]; - density = _e80; - let _e81 = density; - if (_e81 <= 0f) { - let _e83 = (*color); - return _e83; + let _e81 = fog_info.fog[3u]; + density = _e81; + let _e82 = density; + if (_e82 <= 0f) { + let _e84 = (*color); + return _e84; } - let _e84 = EyeDistance_u0028_(); - d = _e84; - let _e86 = fog_info.fog; - let _e88 = (*color); - let _e89 = density; - let _e91 = d; - return mix(_e86.xyz, _e88, vec3(clamp(exp((-(_e89) * _e91)), 0f, 1f))); + let _e85 = EyeDistance_u0028_(); + d = _e85; + let _e87 = fog_info.fog; + let _e89 = (*color); + let _e90 = density; + let _e92 = d; + return mix(_e87.xyz, _e89, vec3(clamp(exp((-(_e90) * _e92)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -4288,18 +4289,18 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e81 = g_premultiply; - let _e82 = (*alpha_2); - weight_1 = select(1f, _e82, _e81); - let _e84 = (*linearColor); - param_3 = _e84; - let _e85 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e86 = weight_1; - let _e87 = (_e85 * _e86); - let _e88 = (*alpha_2); - frag_color = vec4(_e87.x, _e87.y, _e87.z, _e88); - let _e93 = (*roughness_1); - param_4 = _e93; + let _e82 = g_premultiply; + let _e83 = (*alpha_2); + weight_1 = select(1f, _e83, _e82); + let _e85 = (*linearColor); + param_3 = _e85; + let _e86 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e87 = weight_1; + let _e88 = (_e86 * _e87); + let _e89 = (*alpha_2); + frag_color = vec4(_e88.x, _e88.y, _e88.z, _e89); + let _e94 = (*roughness_1); + param_4 = _e94; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; @@ -4310,25 +4311,25 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b(linearColor_1: ptr> var param_6: f32; var param_7: f32; - let _e80 = (*linearColor_1); - param_5 = _e80; - let _e81 = (*alpha_3); - param_6 = _e81; + let _e81 = (*linearColor_1); + param_5 = _e81; + let _e82 = (*alpha_3); + param_6 = _e82; param_7 = 1f; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_5), (¶m_6), (¶m_7)); return; } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e76 = (*srgb); - let _e79 = (*srgb); - let _e84 = (*srgb); - return mix((_e76 / vec3(12.92f)), pow(((_e79 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e84)); + let _e77 = (*srgb); + let _e80 = (*srgb); + let _e85 = (*srgb); + return mix((_e77 / vec3(12.92f)), pow(((_e80 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e85)); } fn MaterialLodBias_u0028_() -> f32 { - let _e77 = frag_info.target_origin[1u]; - return _e77; + let _e78 = frag_info.target_origin[1u]; + return _e78; } fn ReadSurface_u0028_() -> Surface { @@ -4340,78 +4341,78 @@ fn ReadSurface_u0028_() -> Surface { var anchored: vec3; var noise: f32; - let _e82 = v_texcoord_1; - let _e83 = MaterialLodBias_u0028_(); - let _e84 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e82, _e83); - texel = _e84; - let _e85 = texel; - param_8 = _e85.xyz; - let _e87 = SrgbToLinear_u0028_vf3_u003b((¶m_8)); - let _e89 = frag_info.base_color; - param_9 = _e89.xyz; - let _e91 = SrgbToLinear_u0028_vf3_u003b((¶m_9)); - let _e93 = v_color_1; - s.albedo = ((_e87 * _e91) * _e93.xyz); - let _e98 = texel[3u]; - let _e101 = frag_info.base_color[3u]; - let _e104 = v_color_1[3u]; - s.alpha = ((_e98 * _e101) * _e104); - let _e108 = s.albedo; - g_albedo = _e108; - let _e111 = frag_info.material2_[0u]; - cutoff = _e111; - let _e112 = cutoff; - if (_e112 >= 0f) { - let _e115 = s.alpha; - let _e116 = cutoff; - if (_e115 < _e116) { + let _e83 = v_texcoord_1; + let _e84 = MaterialLodBias_u0028_(); + let _e85 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e83, _e84); + texel = _e85; + let _e86 = texel; + param_8 = _e86.xyz; + let _e88 = SrgbToLinear_u0028_vf3_u003b((¶m_8)); + let _e90 = frag_info.base_color; + param_9 = _e90.xyz; + let _e92 = SrgbToLinear_u0028_vf3_u003b((¶m_9)); + let _e94 = v_color_1; + s.albedo = ((_e88 * _e92) * _e94.xyz); + let _e99 = texel[3u]; + let _e102 = frag_info.base_color[3u]; + let _e105 = v_color_1[3u]; + s.alpha = ((_e99 * _e102) * _e105); + let _e109 = s.albedo; + g_albedo = _e109; + let _e112 = frag_info.material2_[0u]; + cutoff = _e112; + let _e113 = cutoff; + if (_e113 >= 0f) { + let _e116 = s.alpha; + let _e117 = cutoff; + if (_e116 < _e117) { discard; } } else { - let _e118 = cutoff; - if (_e118 < -1.5f) { - let _e120 = v_world_position_1; - anchored = floor((_e120 * 16f)); - let _e123 = anchored; - noise = fract((sin(dot(_e123, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e129 = s.alpha; - let _e130 = noise; - if (_e129 < _e130) { + let _e119 = cutoff; + if (_e119 < -1.5f) { + let _e121 = v_world_position_1; + anchored = floor((_e121 * 16f)); + let _e124 = anchored; + noise = fract((sin(dot(_e124, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e130 = s.alpha; + let _e131 = noise; + if (_e130 < _e131) { discard; } } } - let _e132 = cutoff; - let _e134 = cutoff; - g_premultiply = ((_e132 < 0f) && (_e134 > -0.75f)); - let _e137 = v_normal_1; - s.n = normalize(_e137); - let _e140 = gl_FrontFacing_1; - if !(_e140) { - let _e143 = s.n; - s.n = -(_e143); - } - let _e147 = frag_info.camera_position; - let _e149 = v_world_position_1; - s.v = normalize((_e147.xyz - _e149)); - let _e154 = s.n; - let _e156 = s.v; - s.n_dot_v = max(dot(_e154, _e156), 0.0001f); - let _e162 = frag_info.material[0u]; - s.metallic = clamp(_e162, 0f, 1f); - let _e167 = frag_info.material[1u]; - s.roughness = clamp(_e167, 0.02f, 1f); - let _e171 = frag_info.ambient_ground; - let _e174 = frag_info.ambient_sky; - let _e178 = s.n[1u]; - let _e185 = frag_info.material[2u]; - s.ambient = (mix(_e171.xyz, _e174.xyz, vec3(((_e178 * 0.5f) + 0.5f))) * _e185); - let _e190 = frag_info.frame_params[0u]; - s.exposure = max(_e190, 0f); + let _e133 = cutoff; + let _e135 = cutoff; + g_premultiply = ((_e133 < 0f) && (_e135 > -0.75f)); + let _e138 = v_normal_1; + s.n = normalize(_e138); + let _e141 = gl_FrontFacing_1; + if !(_e141) { + let _e144 = s.n; + s.n = -(_e144); + } + let _e148 = frag_info.camera_position; + let _e150 = v_world_position_1; + s.v = normalize((_e148.xyz - _e150)); + let _e155 = s.n; + let _e157 = s.v; + s.n_dot_v = max(dot(_e155, _e157), 0.0001f); + let _e163 = frag_info.material[0u]; + s.metallic = clamp(_e163, 0f, 1f); + let _e168 = frag_info.material[1u]; + s.roughness = clamp(_e168, 0.02f, 1f); + let _e172 = frag_info.ambient_ground; + let _e175 = frag_info.ambient_sky; + let _e179 = s.n[1u]; + let _e186 = frag_info.material[2u]; + s.ambient = (mix(_e172.xyz, _e175.xyz, vec3(((_e179 * 0.5f) + 0.5f))) * _e186); + let _e191 = frag_info.frame_params[0u]; + s.exposure = max(_e191, 0f); s.occlusion = 1f; s.emissive = vec3(0f, 0f, 0f); - let _e195 = s; - return _e195; + let _e196 = s; + return _e196; } fn main_1() { @@ -4423,13 +4424,14 @@ fn main_1() { g_debug_surface = vec3(0f, 0f, 0f); g_debug_surface_on = false; g_premultiply = false; - let _e78 = ReadSurface_u0028_(); - s_1 = _e78; + light_transmittance = vec3(1f, 1f, 1f); + let _e79 = ReadSurface_u0028_(); + s_1 = _e79; g_albedo = vec3(0f, 0f, 0f); - let _e80 = s_1.albedo; - param_10 = _e80; - let _e82 = s_1.alpha; - param_11 = _e82; + let _e81 = s_1.albedo; + param_10 = _e81; + let _e83 = s_1.alpha; + param_11 = _e83; WriteSurface_u0028_vf3_u003b_f1_u003b((¶m_10), (¶m_11)); return; } @@ -4629,6 +4631,7 @@ var g_albedo: vec3; var g_debug_surface: vec3; var g_debug_surface_on: bool; var g_premultiply: bool; +var light_transmittance: vec3; var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; @@ -4647,10 +4650,10 @@ var v_tangent_1: vec4; var v_lightmap_uv_1: vec2; fn ViewDepth_u0028_() -> f32 { - let _e67 = v_world_position_1; - let _e69 = fog_info.eye; - let _e73 = fog_info.forward; - return dot((_e67 - _e69.xyz), _e73.xyz); + let _e69 = v_world_position_1; + let _e71 = fog_info.eye; + let _e75 = fog_info.forward; + return dot((_e69 - _e71.xyz), _e75.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -4659,22 +4662,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e72 = ViewDepth_u0028_(); - z = abs(_e72); - let _e74 = z; - near = (_e74 / 5f); + let _e74 = ViewDepth_u0028_(); + z = abs(_e74); let _e76 = z; - far = (_e76 / 200f); - let _e78 = far; - let _e79 = far; + near = (_e76 / 5f); + let _e78 = z; + far = (_e78 / 200f); + let _e80 = far; let _e81 = far; - far3_ = ((_e78 * _e79) * _e81); - let _e83 = (*alpha); - let _e84 = near; - let _e85 = near; - let _e88 = far3_; - let _e89 = far3_; - return (_e83 * clamp((10f / ((0.00001f + (_e84 * _e85)) + (_e88 * _e89))), 0.01f, 3000f)); + let _e83 = far; + far3_ = ((_e80 * _e81) * _e83); + let _e85 = (*alpha); + let _e86 = near; + let _e87 = near; + let _e90 = far3_; + let _e91 = far3_; + return (_e85 * clamp((10f / ((0.00001f + (_e86 * _e87)) + (_e90 * _e91))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -4686,35 +4689,35 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e76 = fog_info.forward[3u]; - mode = _e76; - let _e77 = mode; - if (_e77 > 0.5f) { - let _e80 = frag_color[3u]; - alpha_1 = _e80; - let _e81 = alpha_1; - param = _e81; - let _e82 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e82; - let _e83 = frag_color; - let _e85 = weight; - let _e86 = (_e83.xyz * _e85); - let _e87 = alpha_1; - let _e88 = weight; - accumulate = vec4(_e86.x, _e86.y, _e86.z, (_e87 * _e88)); - let _e94 = mode; + let _e78 = fog_info.forward[3u]; + mode = _e78; + let _e79 = mode; + if (_e79 > 0.5f) { + let _e82 = frag_color[3u]; + alpha_1 = _e82; + let _e83 = alpha_1; + param = _e83; + let _e84 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e84; + let _e85 = frag_color; + let _e87 = weight; + let _e88 = (_e85.xyz * _e87); + let _e89 = alpha_1; + let _e90 = weight; + accumulate = vec4(_e88.x, _e88.y, _e88.z, (_e89 * _e90)); let _e96 = mode; - revealage = ((_e94 > 1.5f) && (_e96 < 2.5f)); - let _e99 = revealage; - if _e99 { - let _e100 = alpha_1; - local = vec4(_e100); + let _e98 = mode; + revealage = ((_e96 > 1.5f) && (_e98 < 2.5f)); + let _e101 = revealage; + if _e101 { + let _e102 = alpha_1; + local = vec4(_e102); } else { - let _e102 = accumulate; - local = _e102; + let _e104 = accumulate; + local = _e104; } - let _e103 = local; - frag_color = _e103; + let _e105 = local; + frag_color = _e105; } return; } @@ -4724,29 +4727,29 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { } fn EyeDistance_u0028_() -> f32 { - let _e67 = v_world_position_1; - let _e69 = fog_info.eye; - return distance(_e67, _e69.xyz); + let _e69 = v_world_position_1; + let _e71 = fog_info.eye; + return distance(_e69, _e71.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; - let _e72 = fog_info.fog[3u]; - density = _e72; - let _e73 = density; - if (_e73 <= 0f) { - let _e75 = (*color); - return _e75; + let _e74 = fog_info.fog[3u]; + density = _e74; + let _e75 = density; + if (_e75 <= 0f) { + let _e77 = (*color); + return _e77; } - let _e76 = EyeDistance_u0028_(); - d = _e76; - let _e78 = fog_info.fog; - let _e80 = (*color); - let _e81 = density; - let _e83 = d; - return mix(_e78.xyz, _e80, vec3(clamp(exp((-(_e81) * _e83)), 0f, 1f))); + let _e78 = EyeDistance_u0028_(); + d = _e78; + let _e80 = fog_info.fog; + let _e82 = (*color); + let _e83 = density; + let _e85 = d; + return mix(_e80.xyz, _e82, vec3(clamp(exp((-(_e83) * _e85)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -4754,18 +4757,18 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_2: f32; - let _e73 = g_premultiply; - let _e74 = (*alpha_2); - weight_1 = select(1f, _e74, _e73); - let _e76 = (*linearColor); - param_1 = _e76; - let _e77 = ApplyFog_u0028_vf3_u003b((¶m_1)); - let _e78 = weight_1; - let _e79 = (_e77 * _e78); - let _e80 = (*alpha_2); - frag_color = vec4(_e79.x, _e79.y, _e79.z, _e80); - let _e85 = (*roughness_1); - param_2 = _e85; + let _e75 = g_premultiply; + let _e76 = (*alpha_2); + weight_1 = select(1f, _e76, _e75); + let _e78 = (*linearColor); + param_1 = _e78; + let _e79 = ApplyFog_u0028_vf3_u003b((¶m_1)); + let _e80 = weight_1; + let _e81 = (_e79 * _e80); + let _e82 = (*alpha_2); + frag_color = vec4(_e81.x, _e81.y, _e81.z, _e82); + let _e87 = (*roughness_1); + param_2 = _e87; WriteSurfaceGeometry_u0028_f1_u003b((¶m_2)); WriteWeightedBlended_u0028_(); return; @@ -4776,25 +4779,25 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b(linearColor_1: ptr> var param_4: f32; var param_5: f32; - let _e72 = (*linearColor_1); - param_3 = _e72; - let _e73 = (*alpha_3); - param_4 = _e73; + let _e74 = (*linearColor_1); + param_3 = _e74; + let _e75 = (*alpha_3); + param_4 = _e75; param_5 = 1f; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_3), (¶m_4), (¶m_5)); return; } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e68 = (*srgb); - let _e71 = (*srgb); - let _e76 = (*srgb); - return mix((_e68 / vec3(12.92f)), pow(((_e71 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e76)); + let _e70 = (*srgb); + let _e73 = (*srgb); + let _e78 = (*srgb); + return mix((_e70 / vec3(12.92f)), pow(((_e73 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e78)); } fn MaterialLodBias_u0028_() -> f32 { - let _e69 = frag_info.target_origin[1u]; - return _e69; + let _e71 = frag_info.target_origin[1u]; + return _e71; } fn ReadSurface_u0028_() -> Surface { @@ -4806,78 +4809,78 @@ fn ReadSurface_u0028_() -> Surface { var anchored: vec3; var noise: f32; - let _e74 = v_texcoord_1; - let _e75 = MaterialLodBias_u0028_(); - let _e76 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e74, _e75); - texel = _e76; - let _e77 = texel; - param_6 = _e77.xyz; - let _e79 = SrgbToLinear_u0028_vf3_u003b((¶m_6)); - let _e81 = frag_info.base_color; - param_7 = _e81.xyz; - let _e83 = SrgbToLinear_u0028_vf3_u003b((¶m_7)); - let _e85 = v_color_1; - s.albedo = ((_e79 * _e83) * _e85.xyz); - let _e90 = texel[3u]; - let _e93 = frag_info.base_color[3u]; - let _e96 = v_color_1[3u]; - s.alpha = ((_e90 * _e93) * _e96); - let _e100 = s.albedo; - g_albedo = _e100; - let _e103 = frag_info.material2_[0u]; - cutoff = _e103; - let _e104 = cutoff; - if (_e104 >= 0f) { - let _e107 = s.alpha; - let _e108 = cutoff; - if (_e107 < _e108) { + let _e76 = v_texcoord_1; + let _e77 = MaterialLodBias_u0028_(); + let _e78 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e76, _e77); + texel = _e78; + let _e79 = texel; + param_6 = _e79.xyz; + let _e81 = SrgbToLinear_u0028_vf3_u003b((¶m_6)); + let _e83 = frag_info.base_color; + param_7 = _e83.xyz; + let _e85 = SrgbToLinear_u0028_vf3_u003b((¶m_7)); + let _e87 = v_color_1; + s.albedo = ((_e81 * _e85) * _e87.xyz); + let _e92 = texel[3u]; + let _e95 = frag_info.base_color[3u]; + let _e98 = v_color_1[3u]; + s.alpha = ((_e92 * _e95) * _e98); + let _e102 = s.albedo; + g_albedo = _e102; + let _e105 = frag_info.material2_[0u]; + cutoff = _e105; + let _e106 = cutoff; + if (_e106 >= 0f) { + let _e109 = s.alpha; + let _e110 = cutoff; + if (_e109 < _e110) { discard; } } else { - let _e110 = cutoff; - if (_e110 < -1.5f) { - let _e112 = v_world_position_1; - anchored = floor((_e112 * 16f)); - let _e115 = anchored; - noise = fract((sin(dot(_e115, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e121 = s.alpha; - let _e122 = noise; - if (_e121 < _e122) { + let _e112 = cutoff; + if (_e112 < -1.5f) { + let _e114 = v_world_position_1; + anchored = floor((_e114 * 16f)); + let _e117 = anchored; + noise = fract((sin(dot(_e117, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e123 = s.alpha; + let _e124 = noise; + if (_e123 < _e124) { discard; } } } - let _e124 = cutoff; let _e126 = cutoff; - g_premultiply = ((_e124 < 0f) && (_e126 > -0.75f)); - let _e129 = v_normal_1; - s.n = normalize(_e129); - let _e132 = gl_FrontFacing_1; - if !(_e132) { - let _e135 = s.n; - s.n = -(_e135); - } - let _e139 = frag_info.camera_position; - let _e141 = v_world_position_1; - s.v = normalize((_e139.xyz - _e141)); - let _e146 = s.n; - let _e148 = s.v; - s.n_dot_v = max(dot(_e146, _e148), 0.0001f); - let _e154 = frag_info.material[0u]; - s.metallic = clamp(_e154, 0f, 1f); - let _e159 = frag_info.material[1u]; - s.roughness = clamp(_e159, 0.02f, 1f); - let _e163 = frag_info.ambient_ground; - let _e166 = frag_info.ambient_sky; - let _e170 = s.n[1u]; - let _e177 = frag_info.material[2u]; - s.ambient = (mix(_e163.xyz, _e166.xyz, vec3(((_e170 * 0.5f) + 0.5f))) * _e177); - let _e182 = frag_info.frame_params[0u]; - s.exposure = max(_e182, 0f); + let _e128 = cutoff; + g_premultiply = ((_e126 < 0f) && (_e128 > -0.75f)); + let _e131 = v_normal_1; + s.n = normalize(_e131); + let _e134 = gl_FrontFacing_1; + if !(_e134) { + let _e137 = s.n; + s.n = -(_e137); + } + let _e141 = frag_info.camera_position; + let _e143 = v_world_position_1; + s.v = normalize((_e141.xyz - _e143)); + let _e148 = s.n; + let _e150 = s.v; + s.n_dot_v = max(dot(_e148, _e150), 0.0001f); + let _e156 = frag_info.material[0u]; + s.metallic = clamp(_e156, 0f, 1f); + let _e161 = frag_info.material[1u]; + s.roughness = clamp(_e161, 0.02f, 1f); + let _e165 = frag_info.ambient_ground; + let _e168 = frag_info.ambient_sky; + let _e172 = s.n[1u]; + let _e179 = frag_info.material[2u]; + s.ambient = (mix(_e165.xyz, _e168.xyz, vec3(((_e172 * 0.5f) + 0.5f))) * _e179); + let _e184 = frag_info.frame_params[0u]; + s.exposure = max(_e184, 0f); s.occlusion = 1f; s.emissive = vec3(0f, 0f, 0f); - let _e187 = s; - return _e187; + let _e189 = s; + return _e189; } fn main_1() { @@ -4889,12 +4892,13 @@ fn main_1() { g_debug_surface = vec3(0f, 0f, 0f); g_debug_surface_on = false; g_premultiply = false; - let _e70 = ReadSurface_u0028_(); - s_1 = _e70; - let _e72 = s_1.albedo; - param_8 = _e72; - let _e74 = s_1.alpha; - param_9 = _e74; + light_transmittance = vec3(1f, 1f, 1f); + let _e72 = ReadSurface_u0028_(); + s_1 = _e72; + let _e74 = s_1.albedo; + param_8 = _e74; + let _e76 = s_1.alpha; + param_9 = _e76; WriteSurface_u0028_vf3_u003b_f1_u003b((¶m_8), (¶m_9)); return; } @@ -5138,6 +5142,7 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; +var light_transmittance: vec3; var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; @@ -5205,10 +5210,10 @@ var metallic_roughness_texture_tex: texture_2d; var metallic_roughness_texture_smp: sampler; fn ViewDepth_u0028_() -> f32 { - let _e175 = v_world_position_1; - let _e177 = fog_info.eye; - let _e181 = fog_info.forward; - return dot((_e175 - _e177.xyz), _e181.xyz); + let _e176 = v_world_position_1; + let _e178 = fog_info.eye; + let _e182 = fog_info.forward; + return dot((_e176 - _e178.xyz), _e182.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -5217,22 +5222,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e180 = ViewDepth_u0028_(); - z = abs(_e180); - let _e182 = z; - near = (_e182 / 5f); - let _e184 = z; - far = (_e184 / 200f); - let _e186 = far; + let _e181 = ViewDepth_u0028_(); + z = abs(_e181); + let _e183 = z; + near = (_e183 / 5f); + let _e185 = z; + far = (_e185 / 200f); let _e187 = far; - let _e189 = far; - far3_ = ((_e186 * _e187) * _e189); - let _e191 = (*alpha); - let _e192 = near; + let _e188 = far; + let _e190 = far; + far3_ = ((_e187 * _e188) * _e190); + let _e192 = (*alpha); let _e193 = near; - let _e196 = far3_; + let _e194 = near; let _e197 = far3_; - return (_e191 * clamp((10f / ((0.00001f + (_e192 * _e193)) + (_e196 * _e197))), 0.01f, 3000f)); + let _e198 = far3_; + return (_e192 * clamp((10f / ((0.00001f + (_e193 * _e194)) + (_e197 * _e198))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -5244,39 +5249,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e184 = fog_info.forward[3u]; - mode = _e184; - let _e185 = mode; - if (_e185 > 0.5f) { - let _e188 = frag_color[3u]; - alpha_1 = _e188; - let _e189 = alpha_1; - param = _e189; - let _e190 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e190; - let _e191 = frag_color; - let _e193 = weight; - let _e194 = (_e191.xyz * _e193); - let _e195 = alpha_1; - let _e196 = weight; - accumulate = vec4(_e194.x, _e194.y, _e194.z, (_e195 * _e196)); - let _e202 = mode; - let _e204 = mode; - revealage = ((_e202 > 1.5f) && (_e204 < 2.5f)); - let _e207 = revealage; - if _e207 { - let _e208 = alpha_1; - local = vec4(_e208); + let _e185 = fog_info.forward[3u]; + mode = _e185; + let _e186 = mode; + if (_e186 > 0.5f) { + let _e189 = frag_color[3u]; + alpha_1 = _e189; + let _e190 = alpha_1; + param = _e190; + let _e191 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e191; + let _e192 = frag_color; + let _e194 = weight; + let _e195 = (_e192.xyz * _e194); + let _e196 = alpha_1; + let _e197 = weight; + accumulate = vec4(_e195.x, _e195.y, _e195.z, (_e196 * _e197)); + let _e203 = mode; + let _e205 = mode; + revealage = ((_e203 > 1.5f) && (_e205 < 2.5f)); + let _e208 = revealage; + if _e208 { + let _e209 = alpha_1; + local = vec4(_e209); } else { - let _e210 = accumulate; - local = _e210; + let _e211 = accumulate; + local = _e211; } - let _e211 = local; - frag_color = _e211; - let _e212 = mode; - if (_e212 > 2.5f) { - let _e214 = alpha_1; - frag_surface = vec4(_e214); + let _e212 = local; + frag_color = _e212; + let _e213 = mode; + if (_e213 > 2.5f) { + let _e215 = alpha_1; + frag_surface = vec4(_e215); } } return; @@ -5285,29 +5290,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e178 = (*n)[0u]; - let _e181 = (*n)[1u]; - let _e185 = (*n)[2u]; - let _e188 = (*n); - (*n) = (_e188 / vec3(((abs(_e178) + abs(_e181)) + abs(_e185)))); - let _e191 = (*n); - e = _e191.xy; - let _e194 = (*n)[2u]; - if (_e194 < 0f) { - let _e196 = (*n); - let _e202 = (*n)[0u]; - let _e206 = (*n)[1u]; - e = ((vec2(1f) - abs(_e196.yx)) * vec2(select(-1f, 1f, (_e202 >= 0f)), select(-1f, 1f, (_e206 >= 0f)))); + let _e179 = (*n)[0u]; + let _e182 = (*n)[1u]; + let _e186 = (*n)[2u]; + let _e189 = (*n); + (*n) = (_e189 / vec3(((abs(_e179) + abs(_e182)) + abs(_e186)))); + let _e192 = (*n); + e = _e192.xy; + let _e195 = (*n)[2u]; + if (_e195 < 0f) { + let _e197 = (*n); + let _e203 = (*n)[0u]; + let _e207 = (*n)[1u]; + e = ((vec2(1f) - abs(_e197.yx)) * vec2(select(-1f, 1f, (_e203 >= 0f)), select(-1f, 1f, (_e207 >= 0f)))); } - let _e211 = e; - return ((_e211 * 0.5f) + vec2(0.5f)); + let _e212 = e; + return ((_e212 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e176 = (*linear); - let _e178 = (*linear); - let _e182 = (*linear); - return mix((_e176 * 12.92f), ((pow(_e178, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e182)); + let _e177 = (*linear); + let _e179 = (*linear); + let _e183 = (*linear); + return mix((_e177 * 12.92f), ((pow(_e179, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e183)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -5315,57 +5320,57 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e179 = g_albedo; - param_1 = clamp(_e179, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e181 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e181.x, _e181.y, _e181.z, 1f); - let _e186 = g_debug_surface_on; - if _e186 { - let _e187 = g_debug_surface; - let _e188 = ViewDepth_u0028_(); - frag_surface = vec4(_e187.x, _e187.y, _e187.z, _e188); + let _e180 = g_albedo; + param_1 = clamp(_e180, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e182 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e182.x, _e182.y, _e182.z, 1f); + let _e187 = g_debug_surface_on; + if _e187 { + let _e188 = g_debug_surface; + let _e189 = ViewDepth_u0028_(); + frag_surface = vec4(_e188.x, _e188.y, _e188.z, _e189); return; } - let _e193 = v_normal_1; - geometric = normalize(_e193); - let _e195 = gl_FrontFacing_1; - if !(_e195) { - let _e197 = geometric; - geometric = -(_e197); - } - let _e199 = geometric; - param_2 = _e199; - let _e200 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e201 = (*roughness); - let _e203 = ViewDepth_u0028_(); - frag_surface = vec4(_e200.x, _e200.y, clamp(_e201, 0f, 1f), _e203); + let _e194 = v_normal_1; + geometric = normalize(_e194); + let _e196 = gl_FrontFacing_1; + if !(_e196) { + let _e198 = geometric; + geometric = -(_e198); + } + let _e200 = geometric; + param_2 = _e200; + let _e201 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e202 = (*roughness); + let _e204 = ViewDepth_u0028_(); + frag_surface = vec4(_e201.x, _e201.y, clamp(_e202, 0f, 1f), _e204); return; } fn EyeDistance_u0028_() -> f32 { - let _e175 = v_world_position_1; - let _e177 = fog_info.eye; - return distance(_e175, _e177.xyz); + let _e176 = v_world_position_1; + let _e178 = fog_info.eye; + return distance(_e176, _e178.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; - let _e180 = fog_info.fog[3u]; - density = _e180; - let _e181 = density; - if (_e181 <= 0f) { - let _e183 = (*color); - return _e183; + let _e181 = fog_info.fog[3u]; + density = _e181; + let _e182 = density; + if (_e182 <= 0f) { + let _e184 = (*color); + return _e184; } - let _e184 = EyeDistance_u0028_(); - d = _e184; - let _e186 = fog_info.fog; - let _e188 = (*color); - let _e189 = density; - let _e191 = d; - return mix(_e186.xyz, _e188, vec3(clamp(exp((-(_e189) * _e191)), 0f, 1f))); + let _e185 = EyeDistance_u0028_(); + d = _e185; + let _e187 = fog_info.fog; + let _e189 = (*color); + let _e190 = density; + let _e192 = d; + return mix(_e187.xyz, _e189, vec3(clamp(exp((-(_e190) * _e192)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -5373,55 +5378,55 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e181 = g_premultiply; - let _e182 = (*alpha_2); - weight_1 = select(1f, _e182, _e181); - let _e184 = (*linearColor); - param_3 = _e184; - let _e185 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e186 = weight_1; - let _e187 = (_e185 * _e186); - let _e188 = (*alpha_2); - frag_color = vec4(_e187.x, _e187.y, _e187.z, _e188); - let _e193 = (*roughness_1); - param_4 = _e193; + let _e182 = g_premultiply; + let _e183 = (*alpha_2); + weight_1 = select(1f, _e183, _e182); + let _e185 = (*linearColor); + param_3 = _e185; + let _e186 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e187 = weight_1; + let _e188 = (_e186 * _e187); + let _e189 = (*alpha_2); + frag_color = vec4(_e188.x, _e188.y, _e188.z, _e189); + let _e194 = (*roughness_1); + param_4 = _e194; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; } fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s: ptr, light: ptr) -> vec3 { - let _e178 = (*s).albedo; - return _e178; + let _e179 = (*s).albedo; + return _e179; } fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e176 = (*i); - if (_e176 == 0i) { + let _e177 = (*i); + if (_e177 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e178 = (*i); - if (_e178 == 1i) { + let _e179 = (*i); + if (_e179 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e180 = (*i); - if (_e180 == 2i) { + let _e181 = (*i); + if (_e181 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e182 = (*i); - if (_e182 == 3i) { + let _e183 = (*i); + if (_e183 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e184 = (*i); - if (_e184 == 4i) { + let _e185 = (*i); + if (_e185 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e186 = (*i); - if (_e186 == 5i) { + let _e187 = (*i); + if (_e187 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e188 = (*i); - if (_e188 == 6i) { + let _e189 = (*i); + if (_e189 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -5431,20 +5436,20 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr; var param_5: i32; - let _e181 = (*i_1); - param_5 = _e181; - let _e182 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); - p = _e182; - let _e184 = p[0u]; - let _e185 = (*ca); - let _e188 = p[1u]; - let _e189 = (*sa); - let _e193 = p[0u]; - let _e194 = (*sa); - let _e197 = p[1u]; - let _e198 = (*ca); - let _e202 = (*radius); - return (vec2(((_e184 * _e185) - (_e188 * _e189)), ((_e193 * _e194) + (_e197 * _e198))) * _e202); + let _e182 = (*i_1); + param_5 = _e182; + let _e183 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); + p = _e183; + let _e185 = p[0u]; + let _e186 = (*ca); + let _e189 = p[1u]; + let _e190 = (*sa); + let _e194 = p[0u]; + let _e195 = (*sa); + let _e198 = p[1u]; + let _e199 = (*ca); + let _e203 = (*radius); + return (vec2(((_e185 * _e186) - (_e189 * _e190)), ((_e194 * _e195) + (_e198 * _e199))) * _e203); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { @@ -5452,27 +5457,27 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr; var atlas: vec2; - let _e184 = point_shadow.params[0u]; - inset = _e184; - let _e185 = (*uv); - let _e186 = (*offset); - let _e188 = inset; + let _e185 = point_shadow.params[0u]; + inset = _e185; + let _e186 = (*uv); + let _e187 = (*offset); let _e189 = inset; - local_1 = clamp((_e185 + _e186), vec2(_e188), vec2((1f - _e189))); - let _e194 = local_1; - let _e195 = (*tile); - atlas = ((_e194 + _e195) * vec2(0.16666667f, 0.16666667f)); - let _e200 = point_shadow.params3_[0u]; - if (_e200 > 0.5f) { - let _e203 = atlas[1u]; - atlas[1u] = (1f - _e203); - } - let _e206 = atlas; - let _e207 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e206, 0f); - let _e209 = atlas; - let _e210 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e209, 0f); - let _e213 = (*range); - return (min(_e207.x, _e210.x) * _e213); + let _e190 = inset; + local_1 = clamp((_e186 + _e187), vec2(_e189), vec2((1f - _e190))); + let _e195 = local_1; + let _e196 = (*tile); + atlas = ((_e195 + _e196) * vec2(0.16666667f, 0.16666667f)); + let _e201 = point_shadow.params3_[0u]; + if (_e201 > 0.5f) { + let _e204 = atlas[1u]; + atlas[1u] = (1f - _e204); + } + let _e207 = atlas; + let _e208 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e207, 0f); + let _e210 = atlas; + let _e211 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e210, 0f); + let _e214 = (*range); + return (min(_e208.x, _e211.x) * _e214); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { @@ -5482,24 +5487,24 @@ fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: pt var param_8: vec2; var param_9: f32; - let _e185 = (*uv_1); - param_6 = _e185; - let _e186 = (*offset_1); - param_7 = _e186; - let _e187 = (*tile_1); - param_8 = _e187; - let _e188 = (*range_1); - param_9 = _e188; - let _e189 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); - stored = _e189; - let _e190 = stored; - let _e191 = (*range_1); - if (_e190 >= (_e191 * 0.999f)) { + let _e186 = (*uv_1); + param_6 = _e186; + let _e187 = (*offset_1); + param_7 = _e187; + let _e188 = (*tile_1); + param_8 = _e188; + let _e189 = (*range_1); + param_9 = _e189; + let _e190 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); + stored = _e190; + let _e191 = stored; + let _e192 = (*range_1); + if (_e191 >= (_e192 * 0.999f)) { return 1f; } - let _e194 = (*receiver); - let _e195 = stored; - return select(1f, 0f, (_e194 > _e195)); + let _e195 = (*receiver); + let _e196 = stored; + return select(1f, 0f, (_e195 > _e196)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { @@ -5526,111 +5531,111 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u var world: f32; (*blockerOut) = -1f; - let _e206 = point_shadow.params2_[1u]; - lightRadius = _e206; - let _e209 = point_shadow.params2_[0u]; - minRadius = _e209; - let _e212 = point_shadow.params2_[2u]; - maxRadius = _e212; - let _e213 = lightRadius; - if (_e213 <= 0f) { - let _e215 = (*uv_2); - param_10 = _e215; + let _e207 = point_shadow.params2_[1u]; + lightRadius = _e207; + let _e210 = point_shadow.params2_[0u]; + minRadius = _e210; + let _e213 = point_shadow.params2_[2u]; + maxRadius = _e213; + let _e214 = lightRadius; + if (_e214 <= 0f) { + let _e216 = (*uv_2); + param_10 = _e216; param_11 = vec2(0f, 0f); - let _e216 = (*tile_2); - param_12 = _e216; - let _e217 = (*range_2); - param_13 = _e217; - let _e218 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); - (*blockerOut) = _e218; - let _e219 = minRadius; - return _e219; + let _e217 = (*tile_2); + param_12 = _e217; + let _e218 = (*range_2); + param_13 = _e218; + let _e219 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); + (*blockerOut) = _e219; + let _e220 = minRadius; + return _e220; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e220 = i_2; - if (_e220 < 8i) { - let _e222 = i_2; - param_14 = _e222; - let _e223 = (*ca_1); - param_15 = _e223; - let _e224 = (*sa_1); - param_16 = _e224; - let _e225 = maxRadius; - param_17 = _e225; - let _e226 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); - let _e227 = (*uv_2); - param_18 = _e227; - param_19 = _e226; - let _e228 = (*tile_2); - param_20 = _e228; - let _e229 = (*range_2); - param_21 = _e229; - let _e230 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); - stored_1 = _e230; - let _e231 = stored_1; - let _e232 = (*range_2); - if (_e231 >= (_e232 * 0.999f)) { - continue; - } - let _e235 = stored_1; - let _e236 = (*receiver_1); - if (_e235 >= _e236) { - continue; - } - let _e238 = stored_1; - let _e239 = sum; - sum = (_e239 + _e238); - let _e241 = count; - count = (_e241 + 1f); - continue; - } else { - break; - } - continuing { - let _e243 = i_2; - i_2 = (_e243 + 1i); - } - } - let _e245 = count; - if (_e245 < 0.5f) { - return -1f; - } - let _e247 = sum; - let _e248 = count; - blocker = max((_e247 / _e248), 0.0001f); - let _e251 = blocker; - (*blockerOut) = _e251; - let _e252 = lightRadius; - let _e253 = (*receiver_1); - let _e254 = blocker; - let _e258 = blocker; - world = ((_e252 * max((_e253 - _e254), 0f)) / _e258); - let _e260 = world; - let _e261 = (*receiver_1); - let _e263 = (*tanHalf); - let _e266 = minRadius; - let _e267 = maxRadius; - return clamp((_e260 / ((2f * _e261) * _e263)), _e266, _e267); -} - + let _e221 = i_2; + if (_e221 < 8i) { + let _e223 = i_2; + param_14 = _e223; + let _e224 = (*ca_1); + param_15 = _e224; + let _e225 = (*sa_1); + param_16 = _e225; + let _e226 = maxRadius; + param_17 = _e226; + let _e227 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); + let _e228 = (*uv_2); + param_18 = _e228; + param_19 = _e227; + let _e229 = (*tile_2); + param_20 = _e229; + let _e230 = (*range_2); + param_21 = _e230; + let _e231 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); + stored_1 = _e231; + let _e232 = stored_1; + let _e233 = (*range_2); + if (_e232 >= (_e233 * 0.999f)) { + continue; + } + let _e236 = stored_1; + let _e237 = (*receiver_1); + if (_e236 >= _e237) { + continue; + } + let _e239 = stored_1; + let _e240 = sum; + sum = (_e240 + _e239); + let _e242 = count; + count = (_e242 + 1f); + continue; + } else { + break; + } + continuing { + let _e244 = i_2; + i_2 = (_e244 + 1i); + } + } + let _e246 = count; + if (_e246 < 0.5f) { + return -1f; + } + let _e248 = sum; + let _e249 = count; + blocker = max((_e248 / _e249), 0.0001f); + let _e252 = blocker; + (*blockerOut) = _e252; + let _e253 = lightRadius; + let _e254 = (*receiver_1); + let _e255 = blocker; + let _e259 = blocker; + world = ((_e253 * max((_e254 - _e255), 0f)) / _e259); + let _e261 = world; + let _e262 = (*receiver_1); + let _e264 = (*tanHalf); + let _e267 = minRadius; + let _e268 = maxRadius; + return clamp((_e261 / ((2f * _e262) * _e264)), _e267, _e268); +} + fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { var local_2: vec2; - let _e177 = (*rows); - if (_e177 > 0f) { - let _e180 = gl_FragCoord_1[0u]; - let _e181 = (*rows); - let _e183 = gl_FragCoord_1[1u]; - local_2 = vec2(_e180, (_e181 - _e183)); + let _e178 = (*rows); + if (_e178 > 0f) { + let _e181 = gl_FragCoord_1[0u]; + let _e182 = (*rows); + let _e184 = gl_FragCoord_1[1u]; + local_2 = vec2(_e181, (_e182 - _e184)); } else { - let _e186 = gl_FragCoord_1; - local_2 = _e186.xy; + let _e187 = gl_FragCoord_1; + local_2 = _e187.xy; } - let _e188 = local_2; - return _e188; + let _e189 = local_2; + return _e189; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -5685,260 +5690,260 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_43: f32; var param_44: f32; - var phi_2146_: bool; - var phi_2207_: bool; - - let _e229 = (*lightIndex); - let _e233 = point_shadow.slots[_e229][0u]; - slot = i32((_e233 + 0.5f)); - let _e236 = (*lightIndex); - let _e240 = point_shadow.slots[_e236][0u]; - if (_e240 < 0f) { + var phi_2147_: bool; + var phi_2208_: bool; + + let _e230 = (*lightIndex); + let _e234 = point_shadow.slots[_e230][0u]; + slot = i32((_e234 + 0.5f)); + let _e237 = (*lightIndex); + let _e241 = point_shadow.slots[_e237][0u]; + if (_e241 < 0f) { return 1f; } - let _e244 = point_shadow.params[2u]; - strength = _e244; - let _e245 = strength; - if (_e245 <= 0f) { + let _e245 = point_shadow.params[2u]; + strength = _e245; + let _e246 = strength; + if (_e246 <= 0f) { return 1f; } - let _e247 = slot; - let _e250 = point_shadow.lights[_e247]; - let _e252 = (*world_1); - toLight = (_e250.xyz - _e252); - let _e254 = toLight; - toLightLength = max(length(_e254), 0.000001f); - let _e257 = (*normal); - let _e258 = toLight; - let _e259 = toLightLength; - nDotL = max(dot(_e257, (_e258 / vec3(_e259))), 0.15f); - let _e264 = nDotL; + let _e248 = slot; + let _e251 = point_shadow.lights[_e248]; + let _e253 = (*world_1); + toLight = (_e251.xyz - _e253); + let _e255 = toLight; + toLightLength = max(length(_e255), 0.000001f); + let _e258 = (*normal); + let _e259 = toLight; + let _e260 = toLightLength; + nDotL = max(dot(_e258, (_e259 / vec3(_e260))), 0.15f); let _e265 = nDotL; - let _e270 = nDotL; + let _e266 = nDotL; let _e271 = nDotL; - slope = min((sqrt(max((1f - (_e264 * _e265)), 0f)) / (_e270 * _e271)), 8f); - let _e275 = toLightLength; - let _e277 = (*lightIndex); - let _e281 = point_shadow.slots[_e277][2u]; - let _e286 = point_shadow.params3_[1u]; - texel = (((2f * _e275) * max(_e281, 0.0001f)) * _e286); - let _e288 = (*world_1); - let _e289 = (*normal); - let _e290 = texel; - let _e294 = point_shadow.params[3u]; - let _e296 = slope; - origin = (_e288 + (((_e289 * _e290) * _e294) * (1f + _e296))); - let _e300 = origin; - let _e301 = slot; - let _e304 = point_shadow.lights[_e301]; - toFragment = (_e300 - _e304.xyz); - let _e307 = toFragment; - distance_ = length(_e307); - let _e309 = slot; - let _e313 = point_shadow.lights[_e309][3u]; - range_3 = max(_e313, 0.0001f); - let _e315 = distance_; - let _e316 = range_3; - if (_e315 >= _e316) { + let _e272 = nDotL; + slope = min((sqrt(max((1f - (_e265 * _e266)), 0f)) / (_e271 * _e272)), 8f); + let _e276 = toLightLength; + let _e278 = (*lightIndex); + let _e282 = point_shadow.slots[_e278][2u]; + let _e287 = point_shadow.params3_[1u]; + texel = (((2f * _e276) * max(_e282, 0.0001f)) * _e287); + let _e289 = (*world_1); + let _e290 = (*normal); + let _e291 = texel; + let _e295 = point_shadow.params[3u]; + let _e297 = slope; + origin = (_e289 + (((_e290 * _e291) * _e295) * (1f + _e297))); + let _e301 = origin; + let _e302 = slot; + let _e305 = point_shadow.lights[_e302]; + toFragment = (_e301 - _e305.xyz); + let _e308 = toFragment; + distance_ = length(_e308); + let _e310 = slot; + let _e314 = point_shadow.lights[_e310][3u]; + range_3 = max(_e314, 0.0001f); + let _e316 = distance_; + let _e317 = range_3; + if (_e316 >= _e317) { return 1f; } face = 0i; - let _e318 = (*lightIndex); - let _e322 = point_shadow.slots[_e318][1u]; - if (_e322 < 0.5f) { - let _e324 = toFragment; - a = abs(_e324); - let _e327 = a[0u]; - let _e329 = a[1u]; - let _e330 = (_e327 >= _e329); - phi_2146_ = _e330; - if _e330 { - let _e332 = a[0u]; - let _e334 = a[2u]; - phi_2146_ = (_e332 >= _e334); - } - let _e337 = phi_2146_; - if _e337 { - let _e339 = toFragment[0u]; - face = select(1i, 0i, (_e339 > 0f)); + let _e319 = (*lightIndex); + let _e323 = point_shadow.slots[_e319][1u]; + if (_e323 < 0.5f) { + let _e325 = toFragment; + a = abs(_e325); + let _e328 = a[0u]; + let _e330 = a[1u]; + let _e331 = (_e328 >= _e330); + phi_2147_ = _e331; + if _e331 { + let _e333 = a[0u]; + let _e335 = a[2u]; + phi_2147_ = (_e333 >= _e335); + } + let _e338 = phi_2147_; + if _e338 { + let _e340 = toFragment[0u]; + face = select(1i, 0i, (_e340 > 0f)); } else { - let _e343 = a[1u]; - let _e345 = a[2u]; - if (_e343 >= _e345) { - let _e348 = toFragment[1u]; - face = select(3i, 2i, (_e348 > 0f)); + let _e344 = a[1u]; + let _e346 = a[2u]; + if (_e344 >= _e346) { + let _e349 = toFragment[1u]; + face = select(3i, 2i, (_e349 > 0f)); } else { - let _e352 = toFragment[2u]; - face = select(5i, 4i, (_e352 > 0f)); + let _e353 = toFragment[2u]; + face = select(5i, 4i, (_e353 > 0f)); } } } - let _e355 = slot; - let _e357 = face; - let _e361 = point_shadow.faces[((_e355 * 6i) + _e357)]; - let _e362 = origin; - clip = (_e361 * vec4(_e362.x, _e362.y, _e362.z, 1f)); - let _e369 = clip[3u]; - if (_e369 <= 0f) { + let _e356 = slot; + let _e358 = face; + let _e362 = point_shadow.faces[((_e356 * 6i) + _e358)]; + let _e363 = origin; + clip = (_e362 * vec4(_e363.x, _e363.y, _e363.z, 1f)); + let _e370 = clip[3u]; + if (_e370 <= 0f) { return 1f; } - let _e371 = clip; - let _e374 = clip[3u]; - ndc = (_e371.xy / vec2(_e374)); - let _e378 = ndc[0u]; - let _e380 = (abs(_e378) > 1f); - phi_2207_ = _e380; - if !(_e380) { - let _e383 = ndc[1u]; - phi_2207_ = (abs(_e383) > 1f); + let _e372 = clip; + let _e375 = clip[3u]; + ndc = (_e372.xy / vec2(_e375)); + let _e379 = ndc[0u]; + let _e381 = (abs(_e379) > 1f); + phi_2208_ = _e381; + if !(_e381) { + let _e384 = ndc[1u]; + phi_2208_ = (abs(_e384) > 1f); } - let _e387 = phi_2207_; - if _e387 { + let _e388 = phi_2208_; + if _e388 { return 1f; } - let _e389 = ndc[0u]; - let _e393 = ndc[1u]; - uv_3 = vec2(((_e389 * 0.5f) + 0.5f), (0.5f - (_e393 * 0.5f))); - let _e397 = face; - let _e399 = slot; - tile_3 = vec2(f32(_e397), f32(_e399)); - let _e402 = distance_; - let _e405 = point_shadow.params[1u]; - receiver_2 = (_e402 - _e405); - let _e409 = frag_info.target_origin[0u]; - param_22 = _e409; - let _e410 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); - noise = fract((52.982918f * fract(dot(_e410, vec2(0.06711056f, 0.00583715f))))); - let _e415 = noise; - angle = (_e415 * 6.2831855f); - let _e417 = angle; - ca_2 = cos(_e417); - let _e419 = angle; - sa_2 = sin(_e419); - let _e421 = (*lightIndex); - let _e425 = point_shadow.slots[_e421][2u]; - tanHalf_1 = max(_e425, 0.0001f); + let _e390 = ndc[0u]; + let _e394 = ndc[1u]; + uv_3 = vec2(((_e390 * 0.5f) + 0.5f), (0.5f - (_e394 * 0.5f))); + let _e398 = face; + let _e400 = slot; + tile_3 = vec2(f32(_e398), f32(_e400)); + let _e403 = distance_; + let _e406 = point_shadow.params[1u]; + receiver_2 = (_e403 - _e406); + let _e410 = frag_info.target_origin[0u]; + param_22 = _e410; + let _e411 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); + noise = fract((52.982918f * fract(dot(_e411, vec2(0.06711056f, 0.00583715f))))); + let _e416 = noise; + angle = (_e416 * 6.2831855f); + let _e418 = angle; + ca_2 = cos(_e418); + let _e420 = angle; + sa_2 = sin(_e420); + let _e422 = (*lightIndex); + let _e426 = point_shadow.slots[_e422][2u]; + tanHalf_1 = max(_e426, 0.0001f); blocker_1 = -1f; - let _e427 = uv_3; - param_23 = _e427; - let _e428 = tile_3; - param_24 = _e428; - let _e429 = range_3; - param_25 = _e429; - let _e430 = receiver_2; - param_26 = _e430; - let _e431 = ca_2; - param_27 = _e431; - let _e432 = sa_2; - param_28 = _e432; - let _e433 = tanHalf_1; - param_29 = _e433; - let _e434 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e435 = param_30; - blocker_1 = _e435; - radius_1 = _e434; - let _e438 = point_shadow.params2_[3u]; - if (_e438 > 0.5f) { + let _e428 = uv_3; + param_23 = _e428; + let _e429 = tile_3; + param_24 = _e429; + let _e430 = range_3; + param_25 = _e430; + let _e431 = receiver_2; + param_26 = _e431; + let _e432 = ca_2; + param_27 = _e432; + let _e433 = sa_2; + param_28 = _e433; + let _e434 = tanHalf_1; + param_29 = _e434; + let _e435 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); + let _e436 = param_30; + blocker_1 = _e436; + radius_1 = _e435; + let _e439 = point_shadow.params2_[3u]; + if (_e439 > 0.5f) { g_debug_surface_on = true; - let _e440 = radius_1; - if (_e440 < 0f) { + let _e441 = radius_1; + if (_e441 < 0f) { local_3 = vec3(0f, 0f, 1f); } else { - let _e442 = radius_1; - let _e445 = point_shadow.params2_[2u]; - let _e449 = blocker_1; - let _e450 = range_3; - local_3 = vec3(clamp((_e442 / max(_e445, 0.000001f)), 0f, 1f), clamp((_e449 / _e450), 0f, 1f), 0f); + let _e443 = radius_1; + let _e446 = point_shadow.params2_[2u]; + let _e450 = blocker_1; + let _e451 = range_3; + local_3 = vec3(clamp((_e443 / max(_e446, 0.000001f)), 0f, 1f), clamp((_e450 / _e451), 0f, 1f), 0f); } - let _e454 = local_3; - g_debug_surface = _e454; + let _e455 = local_3; + g_debug_surface = _e455; } - let _e455 = radius_1; - if (_e455 < 0f) { + let _e456 = radius_1; + if (_e456 < 0f) { return 1f; } - let _e457 = uv_3; - param_31 = _e457; + let _e458 = uv_3; + param_31 = _e458; param_32 = vec2(0f, 0f); - let _e458 = tile_3; - param_33 = _e458; - let _e459 = range_3; - param_34 = _e459; - let _e460 = receiver_2; - param_35 = _e460; - let _e461 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); - lit = _e461; - let _e462 = radius_1; - if (_e462 > 0f) { + let _e459 = tile_3; + param_33 = _e459; + let _e460 = range_3; + param_34 = _e460; + let _e461 = receiver_2; + param_35 = _e461; + let _e462 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); + lit = _e462; + let _e463 = radius_1; + if (_e463 > 0f) { i_3 = 0i; loop { - let _e464 = i_3; - if (_e464 < 8i) { - let _e466 = i_3; - param_36 = _e466; - let _e467 = ca_2; - param_37 = _e467; - let _e468 = sa_2; - param_38 = _e468; - let _e469 = radius_1; - param_39 = _e469; - let _e470 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - let _e471 = uv_3; - param_40 = _e471; - param_41 = _e470; - let _e472 = tile_3; - param_42 = _e472; - let _e473 = range_3; - param_43 = _e473; - let _e474 = receiver_2; - param_44 = _e474; - let _e475 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); - let _e476 = lit; - lit = (_e476 + _e475); + let _e465 = i_3; + if (_e465 < 8i) { + let _e467 = i_3; + param_36 = _e467; + let _e468 = ca_2; + param_37 = _e468; + let _e469 = sa_2; + param_38 = _e469; + let _e470 = radius_1; + param_39 = _e470; + let _e471 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); + let _e472 = uv_3; + param_40 = _e472; + param_41 = _e471; + let _e473 = tile_3; + param_42 = _e473; + let _e474 = range_3; + param_43 = _e474; + let _e475 = receiver_2; + param_44 = _e475; + let _e476 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); + let _e477 = lit; + lit = (_e477 + _e476); continue; } else { break; } continuing { - let _e478 = i_3; - i_3 = (_e478 + 1i); + let _e479 = i_3; + i_3 = (_e479 + 1i); } } - let _e480 = lit; - lit = (_e480 * 0.11111111f); - } - let _e482 = lit; - let _e483 = strength; - return mix(1f, _e482, clamp(_e483, 0f, 1f)); + let _e481 = lit; + lit = (_e481 * 0.11111111f); + } + let _e483 = lit; + let _e484 = strength; + return mix(1f, _e483, clamp(_e484, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r: f32; var theta: f32; - let _e180 = (*i_4); - let _e183 = (*n_1); - r = sqrt(((f32(_e180) + 0.5f) / f32(_e183))); - let _e187 = (*i_4); - let _e190 = (*turn); - theta = ((f32(_e187) * 2.3999631f) + _e190); - let _e192 = r; - let _e193 = theta; - let _e195 = theta; - return (vec2(cos(_e193), sin(_e195)) * _e192); + let _e181 = (*i_4); + let _e184 = (*n_1); + r = sqrt(((f32(_e181) + 0.5f) / f32(_e184))); + let _e188 = (*i_4); + let _e191 = (*turn); + theta = ((f32(_e188) * 2.3999631f) + _e191); + let _e193 = r; + let _e194 = theta; + let _e196 = theta; + return (vec2(cos(_e194), sin(_e196)) * _e193); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; var param_45: f32; - let _e179 = frag_info.target_origin[0u]; - param_45 = _e179; - let _e180 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); - let _e183 = frag_info.target_origin[3u]; - at = (_e180 + vec2((5.588238f * max(_e183, 0f)))); - let _e188 = at; - return fract((52.982918f * fract(dot(_e188, vec2(0.06711056f, 0.00583715f))))); + let _e180 = frag_info.target_origin[0u]; + param_45 = _e180; + let _e181 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); + let _e184 = frag_info.target_origin[3u]; + at = (_e181 + vec2((5.588238f * max(_e184, 0f)))); + let _e189 = at; + return fract((52.982918f * fract(dot(_e189, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -5947,37 +5952,37 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_2: f32; - let _e177 = (*depth); - d_2 = ((2f * clamp(_e177, 0f, 1f)) - 1f); - let _e181 = d_2; - let _e184 = d_2; - return vec2(exp((40f * _e181)), -(exp((-5f * _e184)))); + let _e178 = (*depth); + d_2 = ((2f * clamp(_e178, 0f, 1f)) - 1f); + let _e182 = d_2; + let _e185 = d_2; + return vec2(exp((40f * _e182)), -(exp((-5f * _e185)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { @@ -5995,37 +6000,37 @@ fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr(0.004f, 0.0005f) * _e193); - let _e196 = scale[0u]; - let _e198 = scale[0u]; - let _e200 = (*moments_1); - param_47 = _e200.xy; - let _e203 = warped[0u]; - param_48 = _e203; - param_49 = (_e196 * _e198); - let _e204 = (*bleed_1); - param_50 = _e204; - let _e205 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); - positive = _e205; - let _e207 = scale[1u]; - let _e209 = scale[1u]; - let _e211 = (*moments_1); - param_51 = _e211.zw; - let _e214 = warped[1u]; - param_52 = _e214; - param_53 = (_e207 * _e209); - let _e215 = (*bleed_1); - param_54 = _e215; - let _e216 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); - negative = _e216; - let _e217 = positive; - let _e218 = negative; - return min(_e217, _e218); + let _e192 = (*depth_1); + param_46 = _e192; + let _e193 = EvsmWarp_u0028_f1_u003b((¶m_46)); + warped = _e193; + let _e194 = warped; + scale = (vec2(0.004f, 0.0005f) * _e194); + let _e197 = scale[0u]; + let _e199 = scale[0u]; + let _e201 = (*moments_1); + param_47 = _e201.xy; + let _e204 = warped[0u]; + param_48 = _e204; + param_49 = (_e197 * _e199); + let _e205 = (*bleed_1); + param_50 = _e205; + let _e206 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); + positive = _e206; + let _e208 = scale[1u]; + let _e210 = scale[1u]; + let _e212 = (*moments_1); + param_51 = _e212.zw; + let _e215 = warped[1u]; + param_52 = _e215; + param_53 = (_e208 * _e210); + let _e216 = (*bleed_1); + param_54 = _e216; + let _e217 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); + negative = _e217; + let _e218 = positive; + let _e219 = negative; + return min(_e218, _e219); } fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_1: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { @@ -6062,6 +6067,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; + var stored_2: vec4; + var through: vec3; var softness: f32; var lit_1: f32; var moments_2: vec4; @@ -6092,51 +6099,51 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_63: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3242_: bool; - var phi_3253_: bool; - var phi_3414_: bool; - var phi_3421_: bool; - var phi_3428_: bool; - - let _e243 = frag_info.shadow_params[3u]; - strength_1 = _e243; - let _e244 = strength_1; - if (_e244 <= 0f) { + var phi_3245_: bool; + var phi_3256_: bool; + var phi_3417_: bool; + var phi_3424_: bool; + var phi_3431_: bool; + + let _e246 = frag_info.shadow_params[3u]; + strength_1 = _e246; + let _e247 = strength_1; + if (_e247 <= 0f) { return 1f; } - let _e246 = (*lightIndex_1); - let _e249 = frag_info.frame_params[2u]; - if (_e246 != i32((_e249 + 0.5f))) { + let _e249 = (*lightIndex_1); + let _e252 = frag_info.frame_params[2u]; + if (_e249 != i32((_e252 + 0.5f))) { return 1f; } - let _e255 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e255 + 0.5f)); - let _e258 = v_world_position_1; - let _e260 = frag_info.camera_position; - viewDistance = length((_e258 - _e260.xyz)); + let _e258 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e258 + 0.5f)); + let _e261 = v_world_position_1; + let _e263 = frag_info.camera_position; + viewDistance = length((_e261 - _e263.xyz)); cascade = 0i; - let _e264 = cascadeCount; - let _e265 = (_e264 > 1i); - phi_3242_ = _e265; - if _e265 { - let _e266 = viewDistance; - let _e269 = frag_info.shadow_cascades[0u]; - phi_3242_ = (_e266 > _e269); - } - let _e272 = phi_3242_; - if _e272 { + let _e267 = cascadeCount; + let _e268 = (_e267 > 1i); + phi_3245_ = _e268; + if _e268 { + let _e269 = viewDistance; + let _e272 = frag_info.shadow_cascades[0u]; + phi_3245_ = (_e269 > _e272); + } + let _e275 = phi_3245_; + if _e275 { cascade = 1i; } - let _e273 = cascadeCount; - let _e274 = (_e273 > 2i); - phi_3253_ = _e274; - if _e274 { - let _e275 = viewDistance; - let _e278 = frag_info.shadow_cascades[1u]; - phi_3253_ = (_e275 > _e278); + let _e276 = cascadeCount; + let _e277 = (_e276 > 2i); + phi_3256_ = _e277; + if _e277 { + let _e278 = viewDistance; + let _e281 = frag_info.shadow_cascades[1u]; + phi_3256_ = (_e278 > _e281); } - let _e281 = phi_3253_; - if _e281 { + let _e284 = phi_3256_; + if _e284 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -6147,232 +6154,247 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e282 = attempt; - if (_e282 < 3i) { - let _e284 = cascade; - let _e285 = attempt; - which = (_e284 + _e285); - let _e287 = which; - let _e288 = cascadeCount; - if (_e287 >= _e288) { + let _e285 = attempt; + if (_e285 < 3i) { + let _e287 = cascade; + let _e288 = attempt; + which = (_e287 + _e288); + let _e290 = which; + let _e291 = cascadeCount; + if (_e290 >= _e291) { break; } - let _e290 = which; - if (_e290 == 0i) { - let _e293 = frag_info.shadow_matrix; - local_4 = _e293; + let _e293 = which; + if (_e293 == 0i) { + let _e296 = frag_info.shadow_matrix; + local_4 = _e296; } else { - let _e294 = which; - if (_e294 == 1i) { - let _e297 = frag_info.shadow_matrix_far; - local_5 = _e297; + let _e297 = which; + if (_e297 == 1i) { + let _e300 = frag_info.shadow_matrix_far; + local_5 = _e300; } else { - let _e299 = frag_info.shadow_matrix_farthest; - local_5 = _e299; + let _e302 = frag_info.shadow_matrix_farthest; + local_5 = _e302; } - let _e300 = local_5; - local_4 = _e300; - } - let _e301 = local_4; - matrix = _e301; - let _e304 = matrix[0][0u]; - let _e307 = matrix[1][0u]; - let _e310 = matrix[2][0u]; - axisX = vec3(_e304, _e307, _e310); - let _e314 = matrix[0][1u]; - let _e317 = matrix[1][1u]; - let _e320 = matrix[2][1u]; - axisY = vec3(_e314, _e317, _e320); - let _e322 = axisX; - rowX = max(length(_e322), 0.000001f); - let _e325 = axisY; - rowY = max(length(_e325), 0.000001f); - let _e330 = frag_info.shadow_cascades[3u]; - let _e332 = rowX; - texelMetres = ((2f * _e330) / _e332); - let _e336 = frag_info.shadow_cascades[3u]; - let _e339 = (*s_1).n; - let _e340 = axisX; - let _e343 = rowX; - let _e344 = rowX; - let _e348 = (*s_1).n; - let _e349 = axisY; - let _e352 = rowY; - let _e353 = rowY; - reach = ((3f * _e336) * ((abs(dot(_e339, _e340)) / (_e343 * _e344)) + (abs(dot(_e348, _e349)) / (_e352 * _e353)))); - let _e360 = frag_info.shadow_params[2u]; - let _e361 = texelMetres; - flatOffset = min(_e360, _e361); - let _e363 = v_world_position_1; - let _e365 = (*s_1).n; - let _e366 = flatOffset; - let _e367 = reach; - origin_1 = (_e363 + (_e365 * (_e366 + _e367))); - let _e371 = matrix; - let _e372 = origin_1; - lightSpace = (_e371 * vec4(_e372.x, _e372.y, _e372.z, 1f)); - let _e379 = lightSpace[3u]; - if (_e379 <= 0f) { + let _e303 = local_5; + local_4 = _e303; + } + let _e304 = local_4; + matrix = _e304; + let _e307 = matrix[0][0u]; + let _e310 = matrix[1][0u]; + let _e313 = matrix[2][0u]; + axisX = vec3(_e307, _e310, _e313); + let _e317 = matrix[0][1u]; + let _e320 = matrix[1][1u]; + let _e323 = matrix[2][1u]; + axisY = vec3(_e317, _e320, _e323); + let _e325 = axisX; + rowX = max(length(_e325), 0.000001f); + let _e328 = axisY; + rowY = max(length(_e328), 0.000001f); + let _e333 = frag_info.shadow_cascades[3u]; + let _e335 = rowX; + texelMetres = ((2f * _e333) / _e335); + let _e339 = frag_info.shadow_cascades[3u]; + let _e342 = (*s_1).n; + let _e343 = axisX; + let _e346 = rowX; + let _e347 = rowX; + let _e351 = (*s_1).n; + let _e352 = axisY; + let _e355 = rowY; + let _e356 = rowY; + reach = ((3f * _e339) * ((abs(dot(_e342, _e343)) / (_e346 * _e347)) + (abs(dot(_e351, _e352)) / (_e355 * _e356)))); + let _e363 = frag_info.shadow_params[2u]; + let _e364 = texelMetres; + flatOffset = min(_e363, _e364); + let _e366 = v_world_position_1; + let _e368 = (*s_1).n; + let _e369 = flatOffset; + let _e370 = reach; + origin_1 = (_e366 + (_e368 * (_e369 + _e370))); + let _e374 = matrix; + let _e375 = origin_1; + lightSpace = (_e374 * vec4(_e375.x, _e375.y, _e375.z, 1f)); + let _e382 = lightSpace[3u]; + if (_e382 <= 0f) { continue; } - let _e381 = lightSpace; - let _e384 = lightSpace[3u]; - candidate = (_e381.xyz / vec3(_e384)); - let _e388 = candidate[0u]; - let _e392 = candidate[1u]; - inTile = vec2(((_e388 * 0.5f) + 0.5f), (0.5f - (_e392 * 0.5f))); - let _e397 = inTile[0u]; - let _e398 = (_e397 < 0f); - phi_3414_ = _e398; - if !(_e398) { - let _e401 = inTile[0u]; - phi_3414_ = (_e401 > 1f); - } - let _e404 = phi_3414_; - phi_3421_ = _e404; - if !(_e404) { - let _e407 = inTile[1u]; - phi_3421_ = (_e407 < 0f); - } - let _e410 = phi_3421_; - phi_3428_ = _e410; - if !(_e410) { - let _e413 = inTile[1u]; - phi_3428_ = (_e413 > 1f); - } - let _e416 = phi_3428_; - if _e416 { + let _e384 = lightSpace; + let _e387 = lightSpace[3u]; + candidate = (_e384.xyz / vec3(_e387)); + let _e391 = candidate[0u]; + let _e395 = candidate[1u]; + inTile = vec2(((_e391 * 0.5f) + 0.5f), (0.5f - (_e395 * 0.5f))); + let _e400 = inTile[0u]; + let _e401 = (_e400 < 0f); + phi_3417_ = _e401; + if !(_e401) { + let _e404 = inTile[0u]; + phi_3417_ = (_e404 > 1f); + } + let _e407 = phi_3417_; + phi_3424_ = _e407; + if !(_e407) { + let _e410 = inTile[1u]; + phi_3424_ = (_e410 < 0f); + } + let _e413 = phi_3424_; + phi_3431_ = _e413; + if !(_e413) { + let _e416 = inTile[1u]; + phi_3431_ = (_e416 > 1f); + } + let _e419 = phi_3431_; + if _e419 { continue; } - let _e418 = candidate[2u]; - if (_e418 > 1f) { - let _e420 = which; - let _e421 = cascadeCount; - if (_e420 < (_e421 - 1i)) { + let _e421 = candidate[2u]; + if (_e421 > 1f) { + let _e423 = which; + let _e424 = cascadeCount; + if (_e423 < (_e424 - 1i)) { continue; } candidate[2u] = 1f; } - let _e426 = inTile[0u]; - let _e427 = which; - let _e430 = cascadeCount; - let _e434 = inTile[1u]; - uv_4 = vec2(((_e426 + f32(_e427)) / f32(_e430)), _e434); - let _e436 = candidate; - projected = _e436; - let _e437 = which; - cascade = _e437; - let _e438 = texelMetres; - cascadeTexel = _e438; - let _e441 = matrix[0][2u]; - let _e444 = matrix[1][2u]; - let _e447 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e441, _e444, _e447)), 0.000001f)); - let _e453 = (*s_1).n; - let _e456 = matrix[0][2u]; - let _e459 = matrix[1][2u]; - let _e462 = matrix[2][2u]; - let _e466 = cascadeDepth; - cosSlope = clamp((abs(dot(_e453, vec3(_e456, _e459, _e462))) * _e466), 0.1f, 1f); - let _e469 = texelMetres; - let _e470 = cosSlope; - let _e471 = cosSlope; - let _e477 = cosSlope; - let _e479 = cascadeDepth; - receiverSlope = (((_e469 * sqrt(max((1f - (_e470 * _e471)), 0f))) / _e477) / _e479); + let _e429 = inTile[0u]; + let _e430 = which; + let _e433 = cascadeCount; + let _e437 = inTile[1u]; + uv_4 = vec2(((_e429 + f32(_e430)) / f32(_e433)), _e437); + let _e439 = candidate; + projected = _e439; + let _e440 = which; + cascade = _e440; + let _e441 = texelMetres; + cascadeTexel = _e441; + let _e444 = matrix[0][2u]; + let _e447 = matrix[1][2u]; + let _e450 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e444, _e447, _e450)), 0.000001f)); + let _e456 = (*s_1).n; + let _e459 = matrix[0][2u]; + let _e462 = matrix[1][2u]; + let _e465 = matrix[2][2u]; + let _e469 = cascadeDepth; + cosSlope = clamp((abs(dot(_e456, vec3(_e459, _e462, _e465))) * _e469), 0.1f, 1f); + let _e472 = texelMetres; + let _e473 = cosSlope; + let _e474 = cosSlope; + let _e480 = cosSlope; + let _e482 = cascadeDepth; + receiverSlope = (((_e472 * sqrt(max((1f - (_e473 * _e474)), 0f))) / _e480) / _e482); found = true; break; } else { break; } continuing { - let _e481 = attempt; - attempt = (_e481 + 1i); + let _e484 = attempt; + attempt = (_e484 + 1i); } } - let _e483 = found; - if !(_e483) { + let _e486 = found; + if !(_e486) { return 1f; } - let _e485 = cascade; - if (_e485 == 0i) { - let _e489 = frag_info.shadow_bias[0u]; - local_6 = _e489; + let _e488 = cascade; + if (_e488 == 0i) { + let _e492 = frag_info.shadow_bias[0u]; + local_6 = _e492; } else { - let _e490 = cascade; - if (_e490 == 1i) { - let _e494 = frag_info.shadow_bias[1u]; - local_7 = _e494; - } else { - let _e497 = frag_info.shadow_bias[2u]; + let _e493 = cascade; + if (_e493 == 1i) { + let _e497 = frag_info.shadow_bias[1u]; local_7 = _e497; - } - let _e498 = local_7; - local_6 = _e498; - } - let _e499 = local_6; - bias = _e499; - let _e502 = frag_info.shadow_params[0u]; - let _e505 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e502, _e505); - let _e507 = cascade; - let _e509 = cascadeCount; - let _e513 = texel_1; - tileLo = (vec2((f32(_e507) / f32(_e509)), 0f) + (_e513 * 0.5f)); - let _e516 = cascade; - let _e519 = cascadeCount; - let _e523 = texel_1; - tileHi = (vec2((f32((_e516 + 1i)) / f32(_e519)), 1f) - (_e523 * 0.5f)); - let _e528 = frag_info.ambient_ground[3u]; - softness = _e528; + } else { + let _e500 = frag_info.shadow_bias[2u]; + local_7 = _e500; + } + let _e501 = local_7; + local_6 = _e501; + } + let _e502 = local_6; + bias = _e502; + let _e505 = frag_info.shadow_params[0u]; + let _e508 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e505, _e508); + let _e510 = cascade; + let _e512 = cascadeCount; + let _e516 = texel_1; + tileLo = (vec2((f32(_e510) / f32(_e512)), 0f) + (_e516 * 0.5f)); + let _e519 = cascade; + let _e522 = cascadeCount; + let _e526 = texel_1; + tileHi = (vec2((f32((_e519 + 1i)) / f32(_e522)), 1f) - (_e526 * 0.5f)); + let _e531 = frag_info.shadow_bias[3u]; + if (_e531 > 0.5f) { + let _e533 = uv_4; + let _e534 = tileLo; + let _e535 = tileHi; + let _e537 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e533, _e534, _e535), 0f); + stored_2 = _e537; + let _e539 = stored_2[1u]; + let _e542 = stored_2[2u]; + let _e545 = stored_2[3u]; + through = clamp(vec3((1f - _e539), (1f - _e542), _e545), vec3(0f), vec3(1f)); + let _e550 = through; + let _e551 = strength_1; + light_transmittance = mix(vec3(1f, 1f, 1f), _e550, vec3(clamp(_e551, 0f, 1f))); + } + let _e557 = frag_info.ambient_ground[3u]; + softness = _e557; lit_1 = 0f; - let _e529 = softness; - if (_e529 < 0f) { - let _e531 = uv_4; - let _e532 = tileLo; - let _e533 = tileHi; - let _e535 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e531, _e532, _e533), 0f); - moments_2 = _e535; - let _e537 = projected[2u]; - let _e538 = bias; - let _e540 = softness; - let _e544 = moments_2; - param_55 = _e544; - param_56 = (_e537 - _e538); - param_57 = clamp((-(_e540) - 1f), 0f, 0.95f); - let _e545 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); - lit_1 = _e545; + let _e558 = softness; + if (_e558 < 0f) { + let _e560 = uv_4; + let _e561 = tileLo; + let _e562 = tileHi; + let _e564 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e560, _e561, _e562), 0f); + moments_2 = _e564; + let _e566 = projected[2u]; + let _e567 = bias; + let _e569 = softness; + let _e573 = moments_2; + param_55 = _e573; + param_56 = (_e566 - _e567); + param_57 = clamp((-(_e569) - 1f), 0f, 0.95f); + let _e574 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); + lit_1 = _e574; } else { - let _e546 = softness; - if (_e546 <= 0f) { + let _e575 = softness; + if (_e575 <= 0f) { y = -1i; loop { - let _e548 = y; - if (_e548 <= 1i) { + let _e577 = y; + if (_e577 <= 1i) { x = -1i; loop { - let _e550 = x; - if (_e550 <= 1i) { - let _e552 = uv_4; - let _e553 = x; - let _e555 = y; - let _e558 = texel_1; - let _e561 = tileLo; - let _e562 = tileHi; - let _e564 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e552 + (vec2(f32(_e553), f32(_e555)) * _e558)), _e561, _e562), 0f); - occluder = _e564.x; - let _e567 = projected[2u]; - let _e568 = bias; - let _e570 = occluder; - let _e573 = lit_1; - lit_1 = (_e573 + select(1f, 0f, ((_e567 - _e568) > _e570))); + let _e579 = x; + if (_e579 <= 1i) { + let _e581 = uv_4; + let _e582 = x; + let _e584 = y; + let _e587 = texel_1; + let _e590 = tileLo; + let _e591 = tileHi; + let _e593 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e581 + (vec2(f32(_e582), f32(_e584)) * _e587)), _e590, _e591), 0f); + occluder = _e593.x; + let _e596 = projected[2u]; + let _e597 = bias; + let _e599 = occluder; + let _e602 = lit_1; + lit_1 = (_e602 + select(1f, 0f, ((_e596 - _e597) > _e599))); continue; } else { break; } continuing { - let _e575 = x; - x = (_e575 + 1i); + let _e604 = x; + x = (_e604 + 1i); } } continue; @@ -6380,125 +6402,125 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e577 = y; - y = (_e577 + 1i); + let _e606 = y; + y = (_e606 + 1i); } } - let _e579 = lit_1; - lit_1 = (_e579 * 0.11111111f); + let _e608 = lit_1; + lit_1 = (_e608 * 0.11111111f); } else { - let _e581 = softness; - spread = tan(min(_e581, 0.5f)); - let _e584 = ShadowNoise_u0028_(); - turn_1 = (_e584 * 6.2831855f); - let _e586 = spread; - let _e588 = projected[2u]; - let _e590 = cascadeDepth; - let _e592 = cascadeTexel; - searchRadius = clamp((((_e586 * _e588) * _e590) / _e592), 1f, 16f); + let _e610 = softness; + spread = tan(min(_e610, 0.5f)); + let _e613 = ShadowNoise_u0028_(); + turn_1 = (_e613 * 6.2831855f); + let _e615 = spread; + let _e617 = projected[2u]; + let _e619 = cascadeDepth; + let _e621 = cascadeTexel; + searchRadius = clamp((((_e615 * _e617) * _e619) / _e621), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e595 = i_5; - if (_e595 < 16i) { - let _e597 = i_5; - param_58 = _e597; + let _e624 = i_5; + if (_e624 < 16i) { + let _e626 = i_5; + param_58 = _e626; param_59 = 16i; - let _e598 = turn_1; - param_60 = _e598; - let _e599 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); - disc = _e599; - let _e600 = bias; - let _e601 = disc; - let _e603 = searchRadius; - let _e607 = receiverSlope; - tapBias = (_e600 + (max(((length(_e601) * _e603) - 1.5f), 0f) * _e607)); - let _e610 = uv_4; - let _e611 = disc; - let _e612 = texel_1; - let _e614 = searchRadius; - let _e617 = tileLo; - let _e618 = tileHi; - let _e620 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e610 + ((_e611 * _e612) * _e614)), _e617, _e618), 0f); - occluder_1 = _e620.x; - let _e623 = projected[2u]; - let _e624 = tapBias; - let _e626 = occluder_1; - if ((_e623 - _e624) > _e626) { - let _e628 = occluder_1; - let _e629 = blockerSum; - blockerSum = (_e629 + _e628); - let _e631 = blockerCount; - blockerCount = (_e631 + 1f); + let _e627 = turn_1; + param_60 = _e627; + let _e628 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); + disc = _e628; + let _e629 = bias; + let _e630 = disc; + let _e632 = searchRadius; + let _e636 = receiverSlope; + tapBias = (_e629 + (max(((length(_e630) * _e632) - 1.5f), 0f) * _e636)); + let _e639 = uv_4; + let _e640 = disc; + let _e641 = texel_1; + let _e643 = searchRadius; + let _e646 = tileLo; + let _e647 = tileHi; + let _e649 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e639 + ((_e640 * _e641) * _e643)), _e646, _e647), 0f); + occluder_1 = _e649.x; + let _e652 = projected[2u]; + let _e653 = tapBias; + let _e655 = occluder_1; + if ((_e652 - _e653) > _e655) { + let _e657 = occluder_1; + let _e658 = blockerSum; + blockerSum = (_e658 + _e657); + let _e660 = blockerCount; + blockerCount = (_e660 + 1f); } continue; } else { break; } continuing { - let _e633 = i_5; - i_5 = (_e633 + 1i); + let _e662 = i_5; + i_5 = (_e662 + 1i); } } - let _e635 = blockerCount; - if (_e635 <= 0f) { + let _e664 = blockerCount; + if (_e664 <= 0f) { return 1f; } - let _e638 = projected[2u]; - let _e639 = blockerSum; - let _e640 = blockerCount; - let _e644 = cascadeDepth; - gap = (max((_e638 - (_e639 / _e640)), 0f) * _e644); - let _e646 = spread; - let _e647 = gap; - let _e649 = cascadeTexel; - radius_2 = clamp(((_e646 * _e647) / _e649), 1f, 16f); + let _e667 = projected[2u]; + let _e668 = blockerSum; + let _e669 = blockerCount; + let _e673 = cascadeDepth; + gap = (max((_e667 - (_e668 / _e669)), 0f) * _e673); + let _e675 = spread; + let _e676 = gap; + let _e678 = cascadeTexel; + radius_2 = clamp(((_e675 * _e676) / _e678), 1f, 16f); i_6 = 0i; loop { - let _e652 = i_6; - if (_e652 < 16i) { - let _e654 = turn_1; - let _e656 = i_6; - param_61 = _e656; + let _e681 = i_6; + if (_e681 < 16i) { + let _e683 = turn_1; + let _e685 = i_6; + param_61 = _e685; param_62 = 16i; - param_63 = (_e654 + 1f); - let _e657 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - disc_1 = _e657; - let _e658 = bias; - let _e659 = disc_1; - let _e661 = radius_2; - let _e665 = receiverSlope; - tapBias_1 = (_e658 + (max(((length(_e659) * _e661) - 1.5f), 0f) * _e665)); - let _e668 = uv_4; - let _e669 = disc_1; - let _e670 = texel_1; - let _e672 = radius_2; - let _e675 = tileLo; - let _e676 = tileHi; - let _e678 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e668 + ((_e669 * _e670) * _e672)), _e675, _e676), 0f); - occluder_2 = _e678.x; - let _e681 = projected[2u]; - let _e682 = tapBias_1; - let _e684 = occluder_2; - let _e687 = lit_1; - lit_1 = (_e687 + select(1f, 0f, ((_e681 - _e682) > _e684))); + param_63 = (_e683 + 1f); + let _e686 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); + disc_1 = _e686; + let _e687 = bias; + let _e688 = disc_1; + let _e690 = radius_2; + let _e694 = receiverSlope; + tapBias_1 = (_e687 + (max(((length(_e688) * _e690) - 1.5f), 0f) * _e694)); + let _e697 = uv_4; + let _e698 = disc_1; + let _e699 = texel_1; + let _e701 = radius_2; + let _e704 = tileLo; + let _e705 = tileHi; + let _e707 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e697 + ((_e698 * _e699) * _e701)), _e704, _e705), 0f); + occluder_2 = _e707.x; + let _e710 = projected[2u]; + let _e711 = tapBias_1; + let _e713 = occluder_2; + let _e716 = lit_1; + lit_1 = (_e716 + select(1f, 0f, ((_e710 - _e711) > _e713))); continue; } else { break; } continuing { - let _e689 = i_6; - i_6 = (_e689 + 1i); + let _e718 = i_6; + i_6 = (_e718 + 1i); } } - let _e691 = lit_1; - lit_1 = (_e691 * 0.0625f); + let _e720 = lit_1; + lit_1 = (_e720 * 0.0625f); } } - let _e693 = lit_1; - let _e694 = strength_1; - return mix(1f, _e693, clamp(_e694, 0f, 1f)); + let _e722 = lit_1; + let _e723 = strength_1; + return mix(1f, _e722, clamp(_e723, 0f, 1f)); } fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_2: ptr, index: ptr) -> f32 { @@ -6506,19 +6528,19 @@ fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_65: LightSample; var param_66: i32; - let _e181 = (*s_2); - param_64 = _e181; - let _e182 = (*light_2); - param_65 = _e182; - let _e183 = (*index); - param_66 = _e183; - let _e184 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); - return _e184; + let _e182 = (*s_2); + param_64 = _e182; + let _e183 = (*light_2); + param_65 = _e183; + let _e184 = (*index); + param_66 = _e184; + let _e185 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); + return _e185; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e176 = (*index_1); - return (_e176 < 8i); + let _e177 = (*index_1); + return (_e177 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -6526,26 +6548,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e180 = (*distance_1); let _e181 = (*distance_1); - attenuation = (1f / max((_e180 * _e181), 0.0001f)); - let _e185 = (*range_4); - if (_e185 > 0f) { - let _e187 = (*distance_1); - let _e188 = (*range_4); - ratio = (_e187 / _e188); - let _e190 = ratio; + let _e182 = (*distance_1); + attenuation = (1f / max((_e181 * _e182), 0.0001f)); + let _e186 = (*range_4); + if (_e186 > 0f) { + let _e188 = (*distance_1); + let _e189 = (*range_4); + ratio = (_e188 / _e189); let _e191 = ratio; - let _e193 = ratio; - let _e195 = ratio; - window = clamp((1f - (((_e190 * _e191) * _e193) * _e195)), 0f, 1f); - let _e199 = window; + let _e192 = ratio; + let _e194 = ratio; + let _e196 = ratio; + window = clamp((1f - (((_e191 * _e192) * _e194) * _e196)), 0f, 1f); let _e200 = window; - let _e202 = attenuation; - attenuation = (_e202 * (_e199 * _e200)); + let _e201 = window; + let _e203 = attenuation; + attenuation = (_e203 * (_e200 * _e201)); } - let _e204 = attenuation; - return _e204; + let _e205 = attenuation; + return _e205; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -6562,75 +6584,75 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var u: f32; var v: f32; - let _e192 = (*halfWidth); - let _e193 = (*halfHeight); - n_2 = cross(_e192, _e193); - let _e195 = n_2; - nLen = length(_e195); - let _e197 = nLen; - if (_e197 < 0.000000000001f) { - let _e199 = (*centre); - return _e199; + let _e193 = (*halfWidth); + let _e194 = (*halfHeight); + n_2 = cross(_e193, _e194); + let _e196 = n_2; + nLen = length(_e196); + let _e198 = nLen; + if (_e198 < 0.000000000001f) { + let _e200 = (*centre); + return _e200; } - let _e200 = nLen; - let _e201 = n_2; - n_2 = (_e201 / vec3(_e200)); - let _e204 = (*centre); - let _e205 = (*world_2); - toPlane = (_e204 - _e205); - let _e207 = (*mirror); - let _e208 = n_2; - denom = dot(_e207, _e208); - let _e210 = denom; - if (abs(_e210) < 0.00001f) { - let _e213 = toPlane; - let _e214 = n_2; - let _e215 = toPlane; - let _e216 = n_2; - onPlane = (_e213 - (_e214 * dot(_e215, _e216))); + let _e201 = nLen; + let _e202 = n_2; + n_2 = (_e202 / vec3(_e201)); + let _e205 = (*centre); + let _e206 = (*world_2); + toPlane = (_e205 - _e206); + let _e208 = (*mirror); + let _e209 = n_2; + denom = dot(_e208, _e209); + let _e211 = denom; + if (abs(_e211) < 0.00001f) { + let _e214 = toPlane; + let _e215 = n_2; + let _e216 = toPlane; + let _e217 = n_2; + onPlane = (_e214 - (_e215 * dot(_e216, _e217))); } else { - let _e220 = toPlane; - let _e221 = n_2; - let _e223 = denom; - t_1 = (dot(_e220, _e221) / _e223); - let _e225 = t_1; - if (_e225 > 0f) { - let _e227 = (*mirror); - let _e228 = t_1; - local_8 = (_e227 * _e228); + let _e221 = toPlane; + let _e222 = n_2; + let _e224 = denom; + t_1 = (dot(_e221, _e222) / _e224); + let _e226 = t_1; + if (_e226 > 0f) { + let _e228 = (*mirror); + let _e229 = t_1; + local_8 = (_e228 * _e229); } else { - let _e230 = toPlane; - let _e231 = n_2; - let _e232 = toPlane; - let _e233 = n_2; - local_8 = (_e230 - (_e231 * dot(_e232, _e233))); + let _e231 = toPlane; + let _e232 = n_2; + let _e233 = toPlane; + let _e234 = n_2; + local_8 = (_e231 - (_e232 * dot(_e233, _e234))); } - let _e237 = local_8; - onPlane = _e237; + let _e238 = local_8; + onPlane = _e238; } - let _e238 = onPlane; - let _e239 = toPlane; - offset_2 = (_e238 - _e239); - let _e241 = (*halfWidth); + let _e239 = onPlane; + let _e240 = toPlane; + offset_2 = (_e239 - _e240); let _e242 = (*halfWidth); - wLen2_ = max(dot(_e241, _e242), 0.000000000001f); - let _e245 = (*halfHeight); + let _e243 = (*halfWidth); + wLen2_ = max(dot(_e242, _e243), 0.000000000001f); let _e246 = (*halfHeight); - hLen2_ = max(dot(_e245, _e246), 0.000000000001f); - let _e249 = offset_2; - let _e250 = (*halfWidth); - let _e252 = wLen2_; - u = clamp((dot(_e249, _e250) / _e252), -1f, 1f); - let _e255 = offset_2; - let _e256 = (*halfHeight); - let _e258 = hLen2_; - v = clamp((dot(_e255, _e256) / _e258), -1f, 1f); - let _e261 = (*centre); - let _e262 = (*halfWidth); - let _e263 = u; - let _e266 = (*halfHeight); - let _e267 = v; - return ((_e261 + (_e262 * _e263)) + (_e266 * _e267)); + let _e247 = (*halfHeight); + hLen2_ = max(dot(_e246, _e247), 0.000000000001f); + let _e250 = offset_2; + let _e251 = (*halfWidth); + let _e253 = wLen2_; + u = clamp((dot(_e250, _e251) / _e253), -1f, 1f); + let _e256 = offset_2; + let _e257 = (*halfHeight); + let _e259 = hLen2_; + v = clamp((dot(_e256, _e257) / _e259), -1f, 1f); + let _e262 = (*centre); + let _e263 = (*halfWidth); + let _e264 = u; + let _e267 = (*halfHeight); + let _e268 = v; + return ((_e262 + (_e263 * _e264)) + (_e267 * _e268)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>, n_3: ptr>) -> f32 { @@ -6641,32 +6663,32 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var len: f32; var local_9: f32; - let _e184 = (*a_1); let _e185 = (*a_1); - ua = (_e184 / vec3(max(length(_e185), 0.000000000001f))); - let _e190 = (*b); + let _e186 = (*a_1); + ua = (_e185 / vec3(max(length(_e186), 0.000000000001f))); let _e191 = (*b); - ub = (_e190 / vec3(max(length(_e191), 0.000000000001f))); - let _e196 = ua; - let _e197 = ub; - angle_1 = acos(clamp(dot(_e196, _e197), -1f, 1f)); - let _e201 = ua; - let _e202 = ub; - axis = cross(_e201, _e202); - let _e204 = axis; - len = length(_e204); - let _e206 = len; - if (_e206 > 0.000001f) { - let _e208 = angle_1; - let _e209 = axis; - let _e210 = (*n_3); - let _e213 = len; - local_9 = ((_e208 * dot(_e209, _e210)) / _e213); + let _e192 = (*b); + ub = (_e191 / vec3(max(length(_e192), 0.000000000001f))); + let _e197 = ua; + let _e198 = ub; + angle_1 = acos(clamp(dot(_e197, _e198), -1f, 1f)); + let _e202 = ua; + let _e203 = ub; + axis = cross(_e202, _e203); + let _e205 = axis; + len = length(_e205); + let _e207 = len; + if (_e207 > 0.000001f) { + let _e209 = angle_1; + let _e210 = axis; + let _e211 = (*n_3); + let _e214 = len; + local_9 = ((_e209 * dot(_e210, _e211)) / _e214); } else { local_9 = 0f; } - let _e215 = local_9; - return _e215; + let _e216 = local_9; + return _e216; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr, 4>>, n_4: ptr>) -> f32 { @@ -6697,98 +6719,98 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr(0f, 0f, 0f); i_7 = 0i; loop { - let _e198 = i_7; - if (_e198 < 4i) { - let _e200 = i_7; - let _e202 = (*corners)[_e200]; - a_2 = _e202; - let _e203 = i_7; - if (_e203 == 3i) { + let _e199 = i_7; + if (_e199 < 4i) { + let _e201 = i_7; + let _e203 = (*corners)[_e201]; + a_2 = _e203; + let _e204 = i_7; + if (_e204 == 3i) { local_10 = 0i; } else { - let _e205 = i_7; - local_10 = (_e205 + 1i); - } - let _e207 = local_10; - let _e209 = (*corners)[_e207]; - b_1 = _e209; - let _e210 = a_2; - let _e211 = (*n_4); - ha = dot(_e210, _e211); - let _e213 = b_1; - let _e214 = (*n_4); - hb = dot(_e213, _e214); - let _e216 = ha; - let _e217 = hb; - d_3 = (_e216 - _e217); - let _e219 = a_2; - let _e220 = b_1; - let _e221 = a_2; - let _e223 = d_3; - if (abs(_e223) > 0.000000000001f) { - let _e226 = ha; - let _e227 = d_3; - local_11 = (_e226 / _e227); - } else { - local_11 = 0f; - } - let _e229 = local_11; - q = (_e219 + ((_e220 - _e221) * _e229)); - let _e232 = ha; - aAbove = (_e232 > 0f); - let _e234 = hb; - bAbove = (_e234 > 0f); - let _e236 = aAbove; - let _e237 = bAbove; - if (_e236 || _e237) { - let _e239 = aAbove; - let _e240 = a_2; - let _e241 = q; - let _e244 = bAbove; - let _e245 = b_1; - let _e246 = q; - param_67 = select(_e241, _e240, vec3(_e239)); - param_68 = select(_e246, _e245, vec3(_e244)); - let _e249 = (*n_4); - param_69 = _e249; - let _e250 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); - local_12 = _e250; - } else { - local_12 = 0f; + let _e206 = i_7; + local_10 = (_e206 + 1i); } - let _e251 = local_12; - let _e252 = total; - total = (_e252 + _e251); - let _e254 = aAbove; - let _e255 = bAbove; - let _e258 = q; - let _e259 = exit; - exit = select(_e259, _e258, vec3((_e254 && !(_e255)))); - let _e262 = aAbove; - let _e264 = bAbove; - let _e266 = q; - let _e267 = entry; - entry = select(_e267, _e266, vec3((!(_e262) && _e264))); + let _e208 = local_10; + let _e210 = (*corners)[_e208]; + b_1 = _e210; + let _e211 = a_2; + let _e212 = (*n_4); + ha = dot(_e211, _e212); + let _e214 = b_1; + let _e215 = (*n_4); + hb = dot(_e214, _e215); + let _e217 = ha; + let _e218 = hb; + d_3 = (_e217 - _e218); + let _e220 = a_2; + let _e221 = b_1; + let _e222 = a_2; + let _e224 = d_3; + if (abs(_e224) > 0.000000000001f) { + let _e227 = ha; + let _e228 = d_3; + local_11 = (_e227 / _e228); + } else { + local_11 = 0f; + } + let _e230 = local_11; + q = (_e220 + ((_e221 - _e222) * _e230)); + let _e233 = ha; + aAbove = (_e233 > 0f); + let _e235 = hb; + bAbove = (_e235 > 0f); + let _e237 = aAbove; + let _e238 = bAbove; + if (_e237 || _e238) { + let _e240 = aAbove; + let _e241 = a_2; + let _e242 = q; + let _e245 = bAbove; + let _e246 = b_1; + let _e247 = q; + param_67 = select(_e242, _e241, vec3(_e240)); + param_68 = select(_e247, _e246, vec3(_e245)); + let _e250 = (*n_4); + param_69 = _e250; + let _e251 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); + local_12 = _e251; + } else { + local_12 = 0f; + } + let _e252 = local_12; + let _e253 = total; + total = (_e253 + _e252); + let _e255 = aAbove; + let _e256 = bAbove; + let _e259 = q; + let _e260 = exit; + exit = select(_e260, _e259, vec3((_e255 && !(_e256)))); + let _e263 = aAbove; + let _e265 = bAbove; + let _e267 = q; + let _e268 = entry; + entry = select(_e268, _e267, vec3((!(_e263) && _e265))); continue; } else { break; } continuing { - let _e270 = i_7; - i_7 = (_e270 + 1i); - } - } - let _e272 = exit; - param_70 = _e272; - let _e273 = entry; - param_71 = _e273; - let _e274 = (*n_4); - param_72 = _e274; - let _e275 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); - let _e276 = total; - total = (_e276 + _e275); - let _e278 = total; - return max((-(_e278) * 0.5f), 0f); + let _e271 = i_7; + i_7 = (_e271 + 1i); + } + } + let _e273 = exit; + param_70 = _e273; + let _e274 = entry; + param_71 = _e274; + let _e275 = (*n_4); + param_72 = _e275; + let _e276 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); + let _e277 = total; + total = (_e277 + _e276); + let _e279 = total; + return max((-(_e279) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { @@ -6797,91 +6819,91 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 var local_14: f32; var local_15: f32; - let _e181 = (*slot_1); let _e182 = (*slot_1); - lane = (_e181 - ((_e182 / 4i) * 4i)); - let _e186 = lane; - if (_e186 == 0i) { - let _e189 = (*four)[0u]; - local_13 = _e189; + let _e183 = (*slot_1); + lane = (_e182 - ((_e183 / 4i) * 4i)); + let _e187 = lane; + if (_e187 == 0i) { + let _e190 = (*four)[0u]; + local_13 = _e190; } else { - let _e190 = lane; - if (_e190 == 1i) { - let _e193 = (*four)[1u]; - local_14 = _e193; + let _e191 = lane; + if (_e191 == 1i) { + let _e194 = (*four)[1u]; + local_14 = _e194; } else { - let _e194 = lane; - if (_e194 == 2i) { - let _e197 = (*four)[2u]; - local_15 = _e197; + let _e195 = lane; + if (_e195 == 2i) { + let _e198 = (*four)[2u]; + local_15 = _e198; } else { - let _e199 = (*four)[3u]; - local_15 = _e199; + let _e200 = (*four)[3u]; + local_15 = _e200; } - let _e200 = local_15; - local_14 = _e200; + let _e201 = local_15; + local_14 = _e201; } - let _e201 = local_14; - local_13 = _e201; + let _e202 = local_14; + local_13 = _e202; } - let _e202 = local_13; - return _e202; + let _e203 = local_13; + return _e203; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { var param_73: vec4; var param_74: i32; - let _e178 = (*slot_2); - let _e182 = light_list_info.scales[(_e178 / 4i)]; - param_73 = _e182; - let _e183 = (*slot_2); - param_74 = _e183; - let _e184 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); - return _e184; + let _e179 = (*slot_2); + let _e183 = light_list_info.scales[(_e179 / 4i)]; + param_73 = _e183; + let _e184 = (*slot_2); + param_74 = _e184; + let _e185 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); + return _e185; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_3: vec4; var b_2: vec4; - let _e180 = light_list_info.slot_rows[0i]; - let _e181 = (*row); - a_3 = abs((_e180 - vec4(_e181))); - let _e187 = light_list_info.slot_rows[1i]; - let _e188 = (*row); - b_2 = abs((_e187 - vec4(_e188))); - let _e193 = a_3[0u]; - let _e195 = a_3[1u]; - let _e198 = a_3[2u]; - let _e200 = a_3[3u]; - let _e204 = b_2[0u]; - let _e206 = b_2[1u]; - let _e209 = b_2[2u]; - let _e211 = b_2[3u]; - return (min(min(min(_e193, _e195), min(_e198, _e200)), min(min(_e204, _e206), min(_e209, _e211))) < 0.5f); + let _e181 = light_list_info.slot_rows[0i]; + let _e182 = (*row); + a_3 = abs((_e181 - vec4(_e182))); + let _e188 = light_list_info.slot_rows[1i]; + let _e189 = (*row); + b_2 = abs((_e188 - vec4(_e189))); + let _e194 = a_3[0u]; + let _e196 = a_3[1u]; + let _e199 = a_3[2u]; + let _e201 = a_3[3u]; + let _e205 = b_2[0u]; + let _e207 = b_2[1u]; + let _e210 = b_2[2u]; + let _e212 = b_2[3u]; + return (min(min(min(_e194, _e196), min(_e199, _e201)), min(min(_e205, _e207), min(_e210, _e212))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { var param_75: vec4; var param_76: i32; - let _e178 = (*slot_3); - let _e182 = light_list_info.indices[(_e178 / 4i)]; - param_75 = _e182; - let _e183 = (*slot_3); - param_76 = _e183; - let _e184 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); - return _e184; + let _e179 = (*slot_3); + let _e183 = light_list_info.indices[(_e179 / 4i)]; + param_75 = _e183; + let _e184 = (*slot_3); + param_76 = _e184; + let _e185 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); + return _e185; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e177 = (*texel_2); - let _e181 = light_list_info.list[1u]; - let _e183 = (*row_1); - let _e187 = light_list_info.list[2u]; - let _e190 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e177 + 0.5f) * _e181), ((_e183 + 0.5f) * _e187)), 0f); - return _e190; + let _e178 = (*texel_2); + let _e182 = light_list_info.list[1u]; + let _e184 = (*row_1); + let _e188 = light_list_info.list[2u]; + let _e191 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e178 + 0.5f) * _e182), ((_e184 + 0.5f) * _e188)), 0f); + return _e191; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -6895,35 +6917,35 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var param_79: vec4; var param_80: i32; - let _e185 = g_cluster_offset; - let _e186 = (*slot_4); - entry_1 = (_e185 + f32(_e186)); - let _e189 = entry_1; - row_2 = floor((_e189 / 16f)); - let _e192 = entry_1; - let _e193 = row_2; - within = (_e192 - (_e193 * 16f)); - let _e196 = within; - texel_3 = floor((_e196 / 4f)); - let _e201 = light_list_info.cluster_depth[3u]; - let _e202 = row_2; - let _e204 = texel_3; - param_77 = _e204; - param_78 = (_e201 + _e202); - let _e205 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); - four_1 = _e205; - let _e206 = within; - let _e207 = texel_3; - let _e212 = four_1; - param_79 = _e212; - param_80 = i32(((_e206 - (_e207 * 4f)) + 0.5f)); - let _e213 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); - return _e213; + let _e186 = g_cluster_offset; + let _e187 = (*slot_4); + entry_1 = (_e186 + f32(_e187)); + let _e190 = entry_1; + row_2 = floor((_e190 / 16f)); + let _e193 = entry_1; + let _e194 = row_2; + within = (_e193 - (_e194 * 16f)); + let _e197 = within; + texel_3 = floor((_e197 / 4f)); + let _e202 = light_list_info.cluster_depth[3u]; + let _e203 = row_2; + let _e205 = texel_3; + param_77 = _e205; + param_78 = (_e202 + _e203); + let _e206 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); + four_1 = _e206; + let _e207 = within; + let _e208 = texel_3; + let _e213 = four_1; + param_79 = _e213; + param_80 = i32(((_e207 - (_e208 * 4f)) + 0.5f)); + let _e214 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); + return _e214; } fn Clustered_u0028_() -> bool { - let _e177 = light_list_info.cluster_grid[3u]; - return (_e177 > 0.5f); + let _e178 = light_list_info.cluster_grid[3u]; + return (_e178 > 0.5f); } fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_3: ptr) -> LightSample { @@ -6979,270 +7001,270 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e228 = (*index_2); - if (_e228 < 8i) { - let _e230 = (*index_2); - let _e233 = frag_info.light_position[_e230]; - position = _e233; - let _e234 = (*index_2); - let _e237 = frag_info.light_color[_e234]; - color_1 = _e237; - let _e238 = (*index_2); - let _e241 = frag_info.light_direction[_e238]; - direction = _e241; - let _e242 = (*index_2); - let _e245 = frag_info.light_cone[_e242]; - cone = _e245; + let _e229 = (*index_2); + if (_e229 < 8i) { + let _e231 = (*index_2); + let _e234 = frag_info.light_position[_e231]; + position = _e234; + let _e235 = (*index_2); + let _e238 = frag_info.light_color[_e235]; + color_1 = _e238; + let _e239 = (*index_2); + let _e242 = frag_info.light_direction[_e239]; + direction = _e242; + let _e243 = (*index_2); + let _e246 = frag_info.light_cone[_e243]; + cone = _e246; } else { - let _e246 = (*index_2); - slot_5 = (_e246 - 8i); - let _e248 = Clustered_u0028_(); - clustered = _e248; - let _e249 = clustered; - if _e249 { - let _e250 = slot_5; - param_81 = _e250; - let _e251 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_16 = _e251; + let _e247 = (*index_2); + slot_5 = (_e247 - 8i); + let _e249 = Clustered_u0028_(); + clustered = _e249; + let _e250 = clustered; + if _e250 { + let _e251 = slot_5; + param_81 = _e251; + let _e252 = ClusterRow_u0028_i1_u003b((¶m_81)); + local_16 = _e252; } else { - let _e252 = slot_5; - param_82 = _e252; - let _e253 = LightListRow_u0028_i1_u003b((¶m_82)); - local_16 = _e253; + let _e253 = slot_5; + param_82 = _e253; + let _e254 = LightListRow_u0028_i1_u003b((¶m_82)); + local_16 = _e254; } - let _e254 = local_16; - listRow = _e254; - let _e255 = listRow; - let _e259 = light_list_info.list[2u]; - v_1 = ((_e255 + 0.5f) * _e259); - let _e263 = light_list_info.list[1u]; - u_1 = _e263; - let _e264 = u_1; - let _e266 = v_1; - let _e268 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e264), _e266), 0f); - position = _e268; - let _e269 = u_1; - let _e271 = v_1; - let _e273 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e269), _e271), 0f); - color_1 = _e273; - let _e274 = u_1; - let _e276 = v_1; - let _e278 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e274), _e276), 0f); - direction = _e278; - let _e279 = u_1; - let _e281 = v_1; - let _e283 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e279), _e281), 0f); - cone = _e283; - let _e284 = clustered; - if _e284 { - let _e285 = listRow; - param_83 = _e285; - let _e286 = InSlots_u0028_f1_u003b((¶m_83)); - local_17 = select(1f, 0f, _e286); - } else { - let _e288 = slot_5; - param_84 = _e288; - let _e289 = LightListScale_u0028_i1_u003b((¶m_84)); - local_17 = _e289; - } - let _e290 = local_17; - let _e292 = color_1[3u]; - color_1[3u] = (_e292 * _e290); - } - let _e296 = position[3u]; - type_39 = _e296; - let _e297 = type_39; - if (_e297 > 2.5f) { - let _e299 = direction; - halfWidth_1 = _e299.xyz; - let _e301 = cone; - halfHeight_1 = _e301.xyz; - let _e303 = position; - let _e305 = v_world_position_1; - toCentre = (_e303.xyz - _e305); - let _e307 = toCentre; - let _e308 = halfWidth_1; - let _e310 = halfHeight_1; - corners_1[0i] = ((_e307 - _e308) - _e310); - let _e313 = toCentre; - let _e314 = halfWidth_1; - let _e316 = halfHeight_1; - corners_1[1i] = ((_e313 + _e314) - _e316); - let _e319 = toCentre; - let _e320 = halfWidth_1; - let _e322 = halfHeight_1; - corners_1[2i] = ((_e319 + _e320) + _e322); - let _e325 = toCentre; - let _e326 = halfWidth_1; - let _e328 = halfHeight_1; - corners_1[3i] = ((_e325 - _e326) + _e328); - let _e331 = toCentre; - let _e332 = halfWidth_1; - let _e333 = halfHeight_1; - behind = (dot(_e331, cross(_e332, _e333)) >= 0f); - let _e337 = behind; - if _e337 { - local_18 = 0f; - } else { - let _e338 = corners_1; - param_85 = _e338; - let _e340 = (*s_3).n; - param_86 = _e340; - let _e341 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_18 = _e341; - } - let _e342 = local_18; - formFactor = _e342; - let _e343 = halfWidth_1; - let _e344 = halfHeight_1; - area = (length(cross(_e343, _e344)) * 4f); - let _e348 = area; - if (_e348 > 0.000000001f) { - let _e350 = area; - local_19 = (1f / _e350); + let _e255 = local_16; + listRow = _e255; + let _e256 = listRow; + let _e260 = light_list_info.list[2u]; + v_1 = ((_e256 + 0.5f) * _e260); + let _e264 = light_list_info.list[1u]; + u_1 = _e264; + let _e265 = u_1; + let _e267 = v_1; + let _e269 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e265), _e267), 0f); + position = _e269; + let _e270 = u_1; + let _e272 = v_1; + let _e274 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e270), _e272), 0f); + color_1 = _e274; + let _e275 = u_1; + let _e277 = v_1; + let _e279 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e275), _e277), 0f); + direction = _e279; + let _e280 = u_1; + let _e282 = v_1; + let _e284 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e280), _e282), 0f); + cone = _e284; + let _e285 = clustered; + if _e285 { + let _e286 = listRow; + param_83 = _e286; + let _e287 = InSlots_u0028_f1_u003b((¶m_83)); + local_17 = select(1f, 0f, _e287); } else { - local_19 = 0f; + let _e289 = slot_5; + param_84 = _e289; + let _e290 = LightListScale_u0028_i1_u003b((¶m_84)); + local_17 = _e290; } - let _e352 = local_19; - radiance = _e352; - let _e353 = toCentre; - distance_2 = length(_e353); - let _e356 = direction[3u]; - if (_e356 > 0f) { - let _e358 = distance_2; - let _e360 = direction[3u]; - ratio_1 = (_e358 / _e360); - let _e362 = ratio_1; - let _e363 = ratio_1; - let _e365 = ratio_1; - let _e367 = ratio_1; - window_1 = clamp((1f - (((_e362 * _e363) * _e365) * _e367)), 0f, 1f); - let _e371 = window_1; - let _e372 = window_1; - let _e374 = radiance; - radiance = (_e374 * (_e371 * _e372)); - } - let _e377 = (*s_3).v; - let _e380 = (*s_3).n; - mirror_1 = reflect(-(_e377), _e380); - let _e382 = position; - param_87 = _e382.xyz; - let _e384 = halfWidth_1; - param_88 = _e384; - let _e385 = halfHeight_1; - param_89 = _e385; - let _e386 = v_world_position_1; - param_90 = _e386; - let _e387 = mirror_1; - param_91 = _e387; - let _e388 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); - representative = _e388; - let _e389 = representative; - let _e390 = v_world_position_1; - toPoint = (_e389 - _e390); - let _e392 = toPoint; - pointDistance = length(_e392); - let _e394 = pointDistance; - if (_e394 > 0.000001f) { - let _e396 = toPoint; - let _e397 = pointDistance; - local_20 = (_e396 / vec3(_e397)); - } else { - let _e401 = (*s_3).n; - local_20 = _e401; - } - let _e402 = local_20; - light_3.l = _e402; - let _e405 = light_3.l; - let _e407 = (*s_3).v; - light_3.h = normalize((_e405 + _e407)); - let _e411 = formFactor; - light_3.n_dot_l = _e411; - let _e414 = (*s_3).n; - let _e416 = light_3.h; - light_3.n_dot_h = max(dot(_e414, _e416), 0f); - let _e421 = (*s_3).v; - let _e423 = light_3.h; - light_3.v_dot_h = max(dot(_e421, _e423), 0f); - let _e427 = color_1; - let _e430 = color_1[3u]; - let _e432 = radiance; - light_3.radiance = ((_e427.xyz * _e430) * _e432); - let _e435 = light_3; - return _e435; - } - let _e436 = direction; - aim = normalize(_e436.xyz); - attenuation_1 = 1f; - let _e439 = type_39; - if (_e439 < 0.5f) { - let _e441 = aim; - light_3.l = -(_e441); - } else { - let _e444 = position; - let _e446 = v_world_position_1; - toLight_1 = (_e444.xyz - _e446); - let _e448 = toLight_1; - distance_3 = length(_e448); - let _e450 = distance_3; - if (_e450 < 0.000001f) { - let _e453 = (*s_3).n; - light_3.l = _e453; - let _e456 = (*s_3).n; - light_3.h = _e456; - light_3.radiance = vec3(0f, 0f, 0f); - light_3.n_dot_l = 0f; - light_3.n_dot_h = 0f; - light_3.v_dot_h = 0f; - let _e462 = light_3; - return _e462; - } - let _e463 = toLight_1; - let _e464 = distance_3; - light_3.l = (_e463 / vec3(_e464)); - let _e468 = distance_3; - param_92 = _e468; - let _e470 = direction[3u]; - param_93 = _e470; - let _e471 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); - attenuation_1 = _e471; - let _e472 = type_39; - if (_e472 > 1.5f) { - let _e474 = aim; - let _e476 = light_3.l; - cosAngle = dot(_e474, -(_e476)); - let _e479 = cosAngle; - let _e481 = cone[1u]; - let _e484 = cone[0u]; - let _e486 = cone[1u]; - let _e490 = attenuation_1; - attenuation_1 = (_e490 * clamp(((_e479 - _e481) / (_e484 - _e486)), 0f, 1f)); - } - } - let _e493 = light_3.l; - let _e495 = (*s_3).v; - light_3.h = normalize((_e493 + _e495)); - let _e500 = (*s_3).n; - let _e502 = light_3.l; - light_3.n_dot_l = max(dot(_e500, _e502), 0f); - let _e507 = (*s_3).n; - let _e509 = light_3.h; - light_3.n_dot_h = max(dot(_e507, _e509), 0f); - let _e514 = (*s_3).v; - let _e516 = light_3.h; - light_3.v_dot_h = max(dot(_e514, _e516), 0f); - let _e520 = color_1; - let _e523 = color_1[3u]; - let _e525 = attenuation_1; - light_3.radiance = ((_e520.xyz * _e523) * _e525); - let _e528 = light_3; - return _e528; -} - -fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { - var clip_1: vec4; - var ndc_1: vec2; - var grid: vec3; + let _e291 = local_17; + let _e293 = color_1[3u]; + color_1[3u] = (_e293 * _e291); + } + let _e297 = position[3u]; + type_39 = _e297; + let _e298 = type_39; + if (_e298 > 2.5f) { + let _e300 = direction; + halfWidth_1 = _e300.xyz; + let _e302 = cone; + halfHeight_1 = _e302.xyz; + let _e304 = position; + let _e306 = v_world_position_1; + toCentre = (_e304.xyz - _e306); + let _e308 = toCentre; + let _e309 = halfWidth_1; + let _e311 = halfHeight_1; + corners_1[0i] = ((_e308 - _e309) - _e311); + let _e314 = toCentre; + let _e315 = halfWidth_1; + let _e317 = halfHeight_1; + corners_1[1i] = ((_e314 + _e315) - _e317); + let _e320 = toCentre; + let _e321 = halfWidth_1; + let _e323 = halfHeight_1; + corners_1[2i] = ((_e320 + _e321) + _e323); + let _e326 = toCentre; + let _e327 = halfWidth_1; + let _e329 = halfHeight_1; + corners_1[3i] = ((_e326 - _e327) + _e329); + let _e332 = toCentre; + let _e333 = halfWidth_1; + let _e334 = halfHeight_1; + behind = (dot(_e332, cross(_e333, _e334)) >= 0f); + let _e338 = behind; + if _e338 { + local_18 = 0f; + } else { + let _e339 = corners_1; + param_85 = _e339; + let _e341 = (*s_3).n; + param_86 = _e341; + let _e342 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); + local_18 = _e342; + } + let _e343 = local_18; + formFactor = _e343; + let _e344 = halfWidth_1; + let _e345 = halfHeight_1; + area = (length(cross(_e344, _e345)) * 4f); + let _e349 = area; + if (_e349 > 0.000000001f) { + let _e351 = area; + local_19 = (1f / _e351); + } else { + local_19 = 0f; + } + let _e353 = local_19; + radiance = _e353; + let _e354 = toCentre; + distance_2 = length(_e354); + let _e357 = direction[3u]; + if (_e357 > 0f) { + let _e359 = distance_2; + let _e361 = direction[3u]; + ratio_1 = (_e359 / _e361); + let _e363 = ratio_1; + let _e364 = ratio_1; + let _e366 = ratio_1; + let _e368 = ratio_1; + window_1 = clamp((1f - (((_e363 * _e364) * _e366) * _e368)), 0f, 1f); + let _e372 = window_1; + let _e373 = window_1; + let _e375 = radiance; + radiance = (_e375 * (_e372 * _e373)); + } + let _e378 = (*s_3).v; + let _e381 = (*s_3).n; + mirror_1 = reflect(-(_e378), _e381); + let _e383 = position; + param_87 = _e383.xyz; + let _e385 = halfWidth_1; + param_88 = _e385; + let _e386 = halfHeight_1; + param_89 = _e386; + let _e387 = v_world_position_1; + param_90 = _e387; + let _e388 = mirror_1; + param_91 = _e388; + let _e389 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); + representative = _e389; + let _e390 = representative; + let _e391 = v_world_position_1; + toPoint = (_e390 - _e391); + let _e393 = toPoint; + pointDistance = length(_e393); + let _e395 = pointDistance; + if (_e395 > 0.000001f) { + let _e397 = toPoint; + let _e398 = pointDistance; + local_20 = (_e397 / vec3(_e398)); + } else { + let _e402 = (*s_3).n; + local_20 = _e402; + } + let _e403 = local_20; + light_3.l = _e403; + let _e406 = light_3.l; + let _e408 = (*s_3).v; + light_3.h = normalize((_e406 + _e408)); + let _e412 = formFactor; + light_3.n_dot_l = _e412; + let _e415 = (*s_3).n; + let _e417 = light_3.h; + light_3.n_dot_h = max(dot(_e415, _e417), 0f); + let _e422 = (*s_3).v; + let _e424 = light_3.h; + light_3.v_dot_h = max(dot(_e422, _e424), 0f); + let _e428 = color_1; + let _e431 = color_1[3u]; + let _e433 = radiance; + light_3.radiance = ((_e428.xyz * _e431) * _e433); + let _e436 = light_3; + return _e436; + } + let _e437 = direction; + aim = normalize(_e437.xyz); + attenuation_1 = 1f; + let _e440 = type_39; + if (_e440 < 0.5f) { + let _e442 = aim; + light_3.l = -(_e442); + } else { + let _e445 = position; + let _e447 = v_world_position_1; + toLight_1 = (_e445.xyz - _e447); + let _e449 = toLight_1; + distance_3 = length(_e449); + let _e451 = distance_3; + if (_e451 < 0.000001f) { + let _e454 = (*s_3).n; + light_3.l = _e454; + let _e457 = (*s_3).n; + light_3.h = _e457; + light_3.radiance = vec3(0f, 0f, 0f); + light_3.n_dot_l = 0f; + light_3.n_dot_h = 0f; + light_3.v_dot_h = 0f; + let _e463 = light_3; + return _e463; + } + let _e464 = toLight_1; + let _e465 = distance_3; + light_3.l = (_e464 / vec3(_e465)); + let _e469 = distance_3; + param_92 = _e469; + let _e471 = direction[3u]; + param_93 = _e471; + let _e472 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); + attenuation_1 = _e472; + let _e473 = type_39; + if (_e473 > 1.5f) { + let _e475 = aim; + let _e477 = light_3.l; + cosAngle = dot(_e475, -(_e477)); + let _e480 = cosAngle; + let _e482 = cone[1u]; + let _e485 = cone[0u]; + let _e487 = cone[1u]; + let _e491 = attenuation_1; + attenuation_1 = (_e491 * clamp(((_e480 - _e482) / (_e485 - _e487)), 0f, 1f)); + } + } + let _e494 = light_3.l; + let _e496 = (*s_3).v; + light_3.h = normalize((_e494 + _e496)); + let _e501 = (*s_3).n; + let _e503 = light_3.l; + light_3.n_dot_l = max(dot(_e501, _e503), 0f); + let _e508 = (*s_3).n; + let _e510 = light_3.h; + light_3.n_dot_h = max(dot(_e508, _e510), 0f); + let _e515 = (*s_3).v; + let _e517 = light_3.h; + light_3.v_dot_h = max(dot(_e515, _e517), 0f); + let _e521 = color_1; + let _e524 = color_1[3u]; + let _e526 = attenuation_1; + light_3.radiance = ((_e521.xyz * _e524) * _e526); + let _e529 = light_3; + return _e529; +} + +fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { + var clip_1: vec4; + var ndc_1: vec2; + var grid: vec3; var near_1: f32; var tx: f32; var ty: f32; @@ -7254,58 +7276,58 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var param_94: f32; var param_95: f32; - let _e190 = light_list_info.cluster_view_projection; - let _e191 = (*world_3); - clip_1 = (_e190 * vec4(_e191.x, _e191.y, _e191.z, 1f)); - let _e197 = clip_1; - let _e200 = clip_1[3u]; - ndc_1 = (_e197.xy / vec2(max(_e200, 0.000001f))); - let _e205 = light_list_info.cluster_grid; - grid = _e205.xyz; - let _e209 = light_list_info.cluster_depth[0u]; - near_1 = _e209; - let _e211 = ndc_1[0u]; - let _e215 = grid[0u]; - let _e219 = grid[0u]; - tx = clamp(floor((((_e211 * 0.5f) + 0.5f) * _e215)), 0f, (_e219 - 1f)); - let _e223 = ndc_1[1u]; - let _e227 = grid[1u]; - let _e231 = grid[1u]; - ty = clamp(floor((((_e223 * 0.5f) + 0.5f) * _e227)), 0f, (_e231 - 1f)); - let _e235 = clip_1[3u]; - let _e236 = near_1; - if (_e235 <= _e236) { + let _e191 = light_list_info.cluster_view_projection; + let _e192 = (*world_3); + clip_1 = (_e191 * vec4(_e192.x, _e192.y, _e192.z, 1f)); + let _e198 = clip_1; + let _e201 = clip_1[3u]; + ndc_1 = (_e198.xy / vec2(max(_e201, 0.000001f))); + let _e206 = light_list_info.cluster_grid; + grid = _e206.xyz; + let _e210 = light_list_info.cluster_depth[0u]; + near_1 = _e210; + let _e212 = ndc_1[0u]; + let _e216 = grid[0u]; + let _e220 = grid[0u]; + tx = clamp(floor((((_e212 * 0.5f) + 0.5f) * _e216)), 0f, (_e220 - 1f)); + let _e224 = ndc_1[1u]; + let _e228 = grid[1u]; + let _e232 = grid[1u]; + ty = clamp(floor((((_e224 * 0.5f) + 0.5f) * _e228)), 0f, (_e232 - 1f)); + let _e236 = clip_1[3u]; + let _e237 = near_1; + if (_e236 <= _e237) { local_21 = 0f; } else { - let _e239 = clip_1[3u]; - let _e240 = near_1; - let _e245 = light_list_info.cluster_depth[1u]; - let _e249 = grid[2u]; - local_21 = clamp(floor((log((_e239 / _e240)) * _e245)), 0f, (_e249 - 1f)); - } - let _e252 = local_21; - tz = _e252; - let _e253 = tx; - let _e254 = ty; - let _e256 = grid[0u]; - let _e259 = tz; - let _e261 = grid[0u]; - let _e264 = grid[1u]; - cell = ((_e253 + (_e254 * _e256)) + ((_e259 * _e261) * _e264)); - let _e267 = cell; - row_3 = floor((_e267 / 4f)); - let _e270 = cell; - let _e271 = row_3; - let _e276 = light_list_info.cluster_depth[2u]; - let _e277 = row_3; - param_94 = (_e270 - (_e271 * 4f)); - param_95 = (_e276 + _e277); - let _e279 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); - header = _e279; - let _e281 = header[0u]; - g_cluster_offset = _e281; - let _e283 = header[1u]; - g_cluster_count = _e283; + let _e240 = clip_1[3u]; + let _e241 = near_1; + let _e246 = light_list_info.cluster_depth[1u]; + let _e250 = grid[2u]; + local_21 = clamp(floor((log((_e240 / _e241)) * _e246)), 0f, (_e250 - 1f)); + } + let _e253 = local_21; + tz = _e253; + let _e254 = tx; + let _e255 = ty; + let _e257 = grid[0u]; + let _e260 = tz; + let _e262 = grid[0u]; + let _e265 = grid[1u]; + cell = ((_e254 + (_e255 * _e257)) + ((_e260 * _e262) * _e265)); + let _e268 = cell; + row_3 = floor((_e268 / 4f)); + let _e271 = cell; + let _e272 = row_3; + let _e277 = light_list_info.cluster_depth[2u]; + let _e278 = row_3; + param_94 = (_e271 - (_e272 * 4f)); + param_95 = (_e277 + _e278); + let _e280 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); + header = _e280; + let _e282 = header[0u]; + g_cluster_offset = _e282; + let _e284 = header[1u]; + g_cluster_count = _e284; return; } @@ -7313,19 +7335,19 @@ fn LightCount_u0028_() -> i32 { var tail: f32; var param_96: vec3; - let _e179 = light_list_info.list[0u]; - tail = _e179; - let _e180 = Clustered_u0028_(); - if _e180 { - let _e181 = v_world_position_1; - param_96 = _e181; + let _e180 = light_list_info.list[0u]; + tail = _e180; + let _e181 = Clustered_u0028_(); + if _e181 { + let _e182 = v_world_position_1; + param_96 = _e182; FindCluster_u0028_vf3_u003b((¶m_96)); - let _e182 = g_cluster_count; - tail = _e182; - } - let _e185 = frag_info.frame_params[1u]; - let _e189 = tail; - return (clamp(i32((_e185 + 0.5f)), 0i, 8i) + clamp(i32((_e189 + 0.5f)), 0i, 24i)); + let _e183 = g_cluster_count; + tail = _e183; + } + let _e186 = frag_info.frame_params[1u]; + let _e190 = tail; + return (clamp(i32((_e186 + 0.5f)), 0i, 8i) + clamp(i32((_e190 + 0.5f)), 0i, 24i)); } fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_4: ptr) -> vec3 { @@ -7348,128 +7370,130 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_107: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e193 = LightCount_u0028_(); - count_1 = _e193; + let _e194 = LightCount_u0028_(); + count_1 = _e194; i_8 = 0i; loop { - let _e194 = i_8; - if (_e194 < 32i) { - let _e196 = i_8; - let _e197 = count_1; - if (_e196 >= _e197) { + let _e195 = i_8; + if (_e195 < 32i) { + let _e197 = i_8; + let _e198 = count_1; + if (_e197 >= _e198) { break; } - let _e199 = i_8; - param_97 = _e199; - let _e200 = (*s_4); - param_98 = _e200; - let _e201 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); - light_4 = _e201; - let _e203 = light_4.n_dot_l; - if (_e203 <= 0f) { + let _e200 = i_8; + param_97 = _e200; + let _e201 = (*s_4); + param_98 = _e201; + let _e202 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); + light_4 = _e202; + let _e204 = light_4.n_dot_l; + if (_e204 <= 0f) { continue; } - let _e205 = i_8; - param_99 = _e205; - let _e206 = LightHasShadow_u0028_i1_u003b((¶m_99)); - if _e206 { - let _e207 = (*s_4); - param_100 = _e207; - let _e208 = light_4; - param_101 = _e208; - let _e209 = i_8; - param_102 = _e209; - let _e210 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); - let _e211 = v_world_position_1; - param_103 = _e211; - let _e213 = (*s_4).n; - param_104 = _e213; - let _e214 = i_8; - param_105 = _e214; - let _e215 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_22 = (_e210 * _e215); + light_transmittance = vec3(1f, 1f, 1f); + let _e206 = i_8; + param_99 = _e206; + let _e207 = LightHasShadow_u0028_i1_u003b((¶m_99)); + if _e207 { + let _e208 = (*s_4); + param_100 = _e208; + let _e209 = light_4; + param_101 = _e209; + let _e210 = i_8; + param_102 = _e210; + let _e211 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); + let _e212 = v_world_position_1; + param_103 = _e212; + let _e214 = (*s_4).n; + param_104 = _e214; + let _e215 = i_8; + param_105 = _e215; + let _e216 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); + local_22 = (_e211 * _e216); } else { local_22 = 1f; } - let _e217 = local_22; - visibility = _e217; - let _e218 = visibility; - if (_e218 <= 0f) { + let _e218 = local_22; + visibility = _e218; + let _e219 = visibility; + if (_e219 <= 0f) { continue; } - let _e220 = (*s_4); - param_106 = _e220; - let _e221 = light_4; - param_107 = _e221; - let _e222 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); - let _e224 = light_4.radiance; - let _e227 = light_4.n_dot_l; - let _e229 = visibility; - let _e231 = total_1; - total_1 = (_e231 + (((_e222 * _e224) * _e227) * _e229)); + let _e221 = (*s_4); + param_106 = _e221; + let _e222 = light_4; + param_107 = _e222; + let _e223 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); + let _e225 = light_4.radiance; + let _e228 = light_4.n_dot_l; + let _e230 = visibility; + let _e232 = light_transmittance; + let _e234 = total_1; + total_1 = (_e234 + ((((_e223 * _e225) * _e228) * _e230) * _e232)); continue; } else { break; } continuing { - let _e233 = i_8; - i_8 = (_e233 + 1i); + let _e236 = i_8; + i_8 = (_e236 + 1i); } } - let _e235 = total_1; - return _e235; + let _e238 = total_1; + return _e238; } fn SampleLightmap_u0028_() -> vec3 { var texel_4: vec4; - let _e176 = v_lightmap_uv_1; - let _e177 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e176); - texel_4 = _e177; - let _e178 = texel_4; - let _e181 = texel_4[3u]; - return ((_e178.xyz * _e181) * 8f); + let _e177 = v_lightmap_uv_1; + let _e178 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e177); + texel_4 = _e178; + let _e179 = texel_4; + let _e182 = texel_4[3u]; + return ((_e179.xyz * _e182) * 8f); } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e176 = (*srgb); - let _e179 = (*srgb); - let _e184 = (*srgb); - return mix((_e176 / vec3(12.92f)), pow(((_e179 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e184)); + let _e177 = (*srgb); + let _e180 = (*srgb); + let _e185 = (*srgb); + return mix((_e177 / vec3(12.92f)), pow(((_e180 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e185)); } fn MaterialLodBias_u0028_() -> f32 { - let _e177 = frag_info.target_origin[1u]; - return _e177; + let _e178 = frag_info.target_origin[1u]; + return _e178; } fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_5: ptr) { var emissive: vec3; var param_108: vec3; - let _e178 = v_texcoord_1; - let _e179 = MaterialLodBias_u0028_(); - let _e180 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e178, _e179); - param_108 = _e180.xyz; - let _e182 = SrgbToLinear_u0028_vf3_u003b((¶m_108)); - emissive = _e182; - let _e183 = emissive; - let _e185 = frag_info.emissive; - let _e190 = frag_info.material2_[3u]; - (*s_5).emissive = ((_e183 * _e185.xyz) * _e190); + let _e179 = v_texcoord_1; + let _e180 = MaterialLodBias_u0028_(); + let _e181 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e179, _e180); + param_108 = _e181.xyz; + let _e183 = SrgbToLinear_u0028_vf3_u003b((¶m_108)); + emissive = _e183; + let _e184 = emissive; + let _e186 = frag_info.emissive; + let _e191 = frag_info.material2_[3u]; + (*s_5).emissive = ((_e184 * _e186.xyz) * _e191); return; } fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) { var occlusion: f32; - let _e177 = v_texcoord_1; - let _e178 = MaterialLodBias_u0028_(); - let _e179 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e177, _e178); - occlusion = _e179.x; - let _e181 = occlusion; - let _e184 = frag_info.material2_[2u]; - (*s_6).occlusion = mix(1f, _e181, clamp(_e184, 0f, 1f)); + let _e178 = v_texcoord_1; + let _e179 = MaterialLodBias_u0028_(); + let _e180 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e178, _e179); + occlusion = _e180.x; + let _e182 = occlusion; + let _e185 = frag_info.material2_[2u]; + (*s_6).occlusion = mix(1f, _e182, clamp(_e185, 0f, 1f)); return; } @@ -7479,64 +7503,64 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var b_3: vec3; var sampled: vec3; - let _e180 = v_texcoord_1; - let _e181 = MaterialLodBias_u0028_(); - let _e182 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e180, _e181); - sampledTexel = _e182; - let _e183 = v_tangent_1; - t_2 = _e183.xyz; - let _e185 = t_2; - let _e187 = (*s_7).n; - let _e189 = (*s_7).n; - let _e190 = t_2; - t_2 = (_e185 - (_e187 * dot(_e189, _e190))); - let _e194 = t_2; + let _e181 = v_texcoord_1; + let _e182 = MaterialLodBias_u0028_(); + let _e183 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e181, _e182); + sampledTexel = _e183; + let _e184 = v_tangent_1; + t_2 = _e184.xyz; + let _e186 = t_2; + let _e188 = (*s_7).n; + let _e190 = (*s_7).n; + let _e191 = t_2; + t_2 = (_e186 - (_e188 * dot(_e190, _e191))); let _e195 = t_2; - if (dot(_e194, _e195) < 0.000000000001f) { + let _e196 = t_2; + if (dot(_e195, _e196) < 0.000000000001f) { return; } - let _e198 = t_2; - t_2 = normalize(_e198); - let _e201 = (*s_7).n; - let _e202 = t_2; - let _e205 = v_tangent_1[3u]; - b_3 = (cross(_e201, _e202) * _e205); - let _e207 = gl_FrontFacing_1; - if !(_e207) { - let _e209 = t_2; - t_2 = -(_e209); - } - let _e211 = sampledTexel; - sampled = ((_e211.xyz * 2f) - vec3(1f)); - let _e218 = frag_info.emissive[3u]; - if (_e218 > 0.5f) { - let _e220 = sampled; - let _e222 = sampled; - sampled[2u] = sqrt(max((1f - dot(_e220.xy, _e222.xy)), 0f)); - } - let _e231 = frag_info.material2_[1u]; - let _e232 = sampled; - let _e234 = (_e232.xy * _e231); - sampled[0u] = _e234.x; - sampled[1u] = _e234.y; - let _e239 = t_2; - let _e241 = sampled[0u]; - let _e243 = b_3; - let _e245 = sampled[1u]; - let _e249 = (*s_7).n; - let _e251 = sampled[2u]; - (*s_7).n = normalize((((_e239 * _e241) + (_e243 * _e245)) + (_e249 * _e251))); - let _e257 = (*s_7).n; - let _e259 = (*s_7).v; - (*s_7).n_dot_v = max(dot(_e257, _e259), 0.0001f); + let _e199 = t_2; + t_2 = normalize(_e199); + let _e202 = (*s_7).n; + let _e203 = t_2; + let _e206 = v_tangent_1[3u]; + b_3 = (cross(_e202, _e203) * _e206); + let _e208 = gl_FrontFacing_1; + if !(_e208) { + let _e210 = t_2; + t_2 = -(_e210); + } + let _e212 = sampledTexel; + sampled = ((_e212.xyz * 2f) - vec3(1f)); + let _e219 = frag_info.emissive[3u]; + if (_e219 > 0.5f) { + let _e221 = sampled; + let _e223 = sampled; + sampled[2u] = sqrt(max((1f - dot(_e221.xy, _e223.xy)), 0f)); + } + let _e232 = frag_info.material2_[1u]; + let _e233 = sampled; + let _e235 = (_e233.xy * _e232); + sampled[0u] = _e235.x; + sampled[1u] = _e235.y; + let _e240 = t_2; + let _e242 = sampled[0u]; + let _e244 = b_3; + let _e246 = sampled[1u]; + let _e250 = (*s_7).n; + let _e252 = sampled[2u]; + (*s_7).n = normalize((((_e240 * _e242) + (_e244 * _e246)) + (_e250 * _e252))); + let _e258 = (*s_7).n; + let _e260 = (*s_7).v; + (*s_7).n_dot_v = max(dot(_e258, _e260), 0.0001f); return; } fn AtlasTexel_u0028_vf2_u003b(texel_5: ptr>) -> vec4 { - let _e176 = (*texel_5); - let _e180 = irradiance_info.atlas; - let _e183 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e176 + vec2(0.5f)) * _e180.xy), 0f); - return _e183; + let _e177 = (*texel_5); + let _e181 = irradiance_info.atlas; + let _e184 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e177 + vec2(0.5f)) * _e181.xy), 0f); + return _e184; } fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr>, interior: ptr, uv_5: ptr>) -> vec4 { @@ -7552,42 +7576,42 @@ fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr; var param_112: vec2; - let _e189 = (*uv_5); - let _e190 = (*interior); - at_1 = ((vec2(1f) + (_e189 * _e190)) - vec2(0.5f)); - let _e196 = at_1; - low = floor(_e196); - let _e198 = at_1; - let _e199 = low; - f = (_e198 - _e199); - let _e201 = (*corner); - let _e202 = low; - param_109 = (_e201 + _e202); - let _e204 = AtlasTexel_u0028_vf2_u003b((¶m_109)); - a_4 = _e204; - let _e205 = (*corner); - let _e206 = low; - param_110 = ((_e205 + _e206) + vec2(1f, 0f)); - let _e209 = AtlasTexel_u0028_vf2_u003b((¶m_110)); - b_4 = _e209; - let _e210 = (*corner); - let _e211 = low; - param_111 = ((_e210 + _e211) + vec2(0f, 1f)); - let _e214 = AtlasTexel_u0028_vf2_u003b((¶m_111)); - c = _e214; - let _e215 = (*corner); - let _e216 = low; - param_112 = ((_e215 + _e216) + vec2(1f, 1f)); - let _e219 = AtlasTexel_u0028_vf2_u003b((¶m_112)); - d_4 = _e219; - let _e220 = a_4; - let _e221 = b_4; - let _e223 = f[0u]; - let _e226 = c; - let _e227 = d_4; - let _e229 = f[0u]; - let _e233 = f[1u]; - return mix(mix(_e220, _e221, vec4(_e223)), mix(_e226, _e227, vec4(_e229)), vec4(_e233)); + let _e190 = (*uv_5); + let _e191 = (*interior); + at_1 = ((vec2(1f) + (_e190 * _e191)) - vec2(0.5f)); + let _e197 = at_1; + low = floor(_e197); + let _e199 = at_1; + let _e200 = low; + f = (_e199 - _e200); + let _e202 = (*corner); + let _e203 = low; + param_109 = (_e202 + _e203); + let _e205 = AtlasTexel_u0028_vf2_u003b((¶m_109)); + a_4 = _e205; + let _e206 = (*corner); + let _e207 = low; + param_110 = ((_e206 + _e207) + vec2(1f, 0f)); + let _e210 = AtlasTexel_u0028_vf2_u003b((¶m_110)); + b_4 = _e210; + let _e211 = (*corner); + let _e212 = low; + param_111 = ((_e211 + _e212) + vec2(0f, 1f)); + let _e215 = AtlasTexel_u0028_vf2_u003b((¶m_111)); + c = _e215; + let _e216 = (*corner); + let _e217 = low; + param_112 = ((_e216 + _e217) + vec2(1f, 1f)); + let _e220 = AtlasTexel_u0028_vf2_u003b((¶m_112)); + d_4 = _e220; + let _e221 = a_4; + let _e222 = b_4; + let _e224 = f[0u]; + let _e227 = c; + let _e228 = d_4; + let _e230 = f[0u]; + let _e234 = f[1u]; + return mix(mix(_e221, _e222, vec4(_e224)), mix(_e227, _e228, vec4(_e230)), vec4(_e234)); } fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec2 { @@ -7595,29 +7619,29 @@ fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec var n_5: vec3; var xy: vec2; - let _e180 = (*direction_1)[0u]; - let _e183 = (*direction_1)[1u]; - let _e187 = (*direction_1)[2u]; - sum_1 = ((abs(_e180) + abs(_e183)) + abs(_e187)); - let _e190 = sum_1; - if (_e190 <= 0f) { + let _e181 = (*direction_1)[0u]; + let _e184 = (*direction_1)[1u]; + let _e188 = (*direction_1)[2u]; + sum_1 = ((abs(_e181) + abs(_e184)) + abs(_e188)); + let _e191 = sum_1; + if (_e191 <= 0f) { return vec2(0.5f, 0.5f); } - let _e192 = (*direction_1); - let _e193 = sum_1; - n_5 = (_e192 / vec3(_e193)); - let _e196 = n_5; - xy = _e196.xy; - let _e199 = n_5[2u]; - if (_e199 < 0f) { - let _e202 = n_5[1u]; - let _e206 = n_5[0u]; - let _e211 = n_5[0u]; - let _e215 = n_5[1u]; - xy = vec2(((1f - abs(_e202)) * select(-1f, 1f, (_e206 >= 0f))), ((1f - abs(_e211)) * select(-1f, 1f, (_e215 >= 0f)))); + let _e193 = (*direction_1); + let _e194 = sum_1; + n_5 = (_e193 / vec3(_e194)); + let _e197 = n_5; + xy = _e197.xy; + let _e200 = n_5[2u]; + if (_e200 < 0f) { + let _e203 = n_5[1u]; + let _e207 = n_5[0u]; + let _e212 = n_5[0u]; + let _e216 = n_5[1u]; + xy = vec2(((1f - abs(_e203)) * select(-1f, 1f, (_e207 >= 0f))), ((1f - abs(_e212)) * select(-1f, 1f, (_e216 >= 0f)))); } - let _e220 = xy; - return ((_e220 * 0.5f) + vec2(0.5f)); + let _e221 = xy; + return ((_e221 * 0.5f) + vec2(0.5f)); } fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view: ptr>) -> vec3 { @@ -7665,227 +7689,227 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var local_23: vec3; - let _e222 = irradiance_info.origin; - origin_2 = _e222.xyz; - let _e225 = irradiance_info.spacing; - spacing = _e225.xyz; - let _e228 = irradiance_info.counts; - counts = _e228.xyz; - let _e232 = irradiance_info.tiles[0u]; - irradianceTile = _e232; - let _e235 = irradiance_info.tiles[1u]; - depthTile = _e235; - let _e238 = irradiance_info.tiles[2u]; - columns = _e238; - let _e241 = irradiance_info.tiles[3u]; - momentsTop = _e241; - let _e242 = (*normal_1); - unit = normalize(_e242); - let _e244 = (*world_4); - let _e245 = unit; - let _e248 = irradiance_info.spacing[3u]; - let _e251 = (*view); - let _e254 = irradiance_info.counts[3u]; - biased = ((_e244 + (_e245 * _e248)) + (_e251 * _e254)); - let _e257 = biased; - let _e258 = origin_2; - let _e260 = spacing; - grid_1 = ((_e257 - _e258) / _e260); - let _e262 = grid_1; - let _e264 = counts; - base = clamp(floor(_e262), vec3(0f, 0f, 0f), (_e264 - vec3(2f))); - let _e268 = grid_1; - let _e269 = base; - f_1 = clamp((_e268 - _e269), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e223 = irradiance_info.origin; + origin_2 = _e223.xyz; + let _e226 = irradiance_info.spacing; + spacing = _e226.xyz; + let _e229 = irradiance_info.counts; + counts = _e229.xyz; + let _e233 = irradiance_info.tiles[0u]; + irradianceTile = _e233; + let _e236 = irradiance_info.tiles[1u]; + depthTile = _e236; + let _e239 = irradiance_info.tiles[2u]; + columns = _e239; + let _e242 = irradiance_info.tiles[3u]; + momentsTop = _e242; + let _e243 = (*normal_1); + unit = normalize(_e243); + let _e245 = (*world_4); + let _e246 = unit; + let _e249 = irradiance_info.spacing[3u]; + let _e252 = (*view); + let _e255 = irradiance_info.counts[3u]; + biased = ((_e245 + (_e246 * _e249)) + (_e252 * _e255)); + let _e258 = biased; + let _e259 = origin_2; + let _e261 = spacing; + grid_1 = ((_e258 - _e259) / _e261); + let _e263 = grid_1; + let _e265 = counts; + base = clamp(floor(_e263), vec3(0f, 0f, 0f), (_e265 - vec3(2f))); + let _e269 = grid_1; + let _e270 = base; + f_1 = clamp((_e269 - _e270), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); total_2 = vec3(0f, 0f, 0f); weights = 0f; corner_1 = 0i; loop { - let _e272 = corner_1; - if (_e272 < 8i) { - let _e274 = corner_1; - let _e277 = corner_1; - let _e282 = corner_1; - offset_3 = vec3(f32((_e274 & 1i)), f32(((_e277 >> bitcast(1i)) & 1i)), f32(((_e282 >> bitcast(2i)) & 1i))); - let _e288 = base; - let _e289 = offset_3; - cell_1 = (_e288 + _e289); - let _e292 = cell_1[2u]; - let _e294 = counts[1u]; - let _e297 = cell_1[1u]; - let _e300 = counts[0u]; - let _e303 = cell_1[0u]; - probe = ((((_e292 * _e294) + _e297) * _e300) + _e303); - let _e305 = probe; - let _e306 = columns; - let _e311 = probe; - let _e312 = columns; - tile_4 = vec2((_e305 - (floor((_e305 / _e306)) * _e306)), floor((_e311 / _e312))); - let _e316 = tile_4; - let _e317 = irradianceTile; - irradianceCorner = (_e316 * (_e317 + 2f)); - let _e321 = tile_4[0u]; - let _e322 = depthTile; - let _e325 = momentsTop; - let _e327 = tile_4[1u]; - let _e328 = depthTile; - momentCorner = vec2((_e321 * (_e322 + 2f)), (_e325 + (_e327 * (_e328 + 2f)))); - let _e333 = irradianceCorner; - param_113 = (_e333 + vec2(1f)); - let _e336 = AtlasTexel_u0028_vf2_u003b((¶m_113)); - if (_e336.w < 0.5f) { - continue; - } - let _e339 = f_1; - let _e341 = f_1; - let _e342 = offset_3; - trilinear = mix((vec3(1f, 1f, 1f) - _e339), _e341, _e342); - let _e345 = trilinear[0u]; - let _e347 = trilinear[1u]; - let _e350 = trilinear[2u]; - weight_2 = max(((_e345 * _e347) * _e350), 0.001f); - let _e353 = origin_2; - let _e354 = spacing; - let _e355 = cell_1; - probePosition = (_e353 + (_e354 * _e355)); - let _e358 = probePosition; - let _e359 = biased; - toProbe = (_e358 - _e359); - let _e361 = toProbe; - distance_4 = length(_e361); - let _e363 = distance_4; - if (_e363 > 0.000001f) { - let _e365 = toProbe; - let _e366 = distance_4; - direction_2 = (_e365 / vec3(_e366)); - let _e369 = unit; - let _e370 = probePosition; - let _e371 = (*world_4); - facing = ((dot(_e369, normalize((_e370 - _e371))) * 0.5f) + 0.5f); - let _e377 = facing; - let _e378 = facing; - probeWeight = ((_e377 * _e378) + 0.2f); - let _e381 = direction_2; - param_114 = -(_e381); - let _e383 = ProbeOctahedral_u0028_vf3_u003b((¶m_114)); - let _e384 = momentCorner; - param_115 = _e384; - let _e385 = depthTile; - param_116 = _e385; - param_117 = _e383; - let _e386 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_115), (¶m_116), (¶m_117)); - moments_3 = _e386.xy; - chebyshev = 1f; - let _e388 = distance_4; - let _e390 = moments_3[0u]; - if (_e388 > _e390) { - let _e393 = moments_3[1u]; - let _e395 = moments_3[0u]; - let _e397 = moments_3[0u]; - variance_1 = max((_e393 - (_e395 * _e397)), 0.000001f); - let _e401 = distance_4; - let _e403 = moments_3[0u]; - difference = (_e401 - _e403); - let _e405 = variance_1; - let _e406 = variance_1; - let _e407 = difference; - let _e408 = difference; - chebyshev = (_e405 / (_e406 + (_e407 * _e408))); - let _e412 = chebyshev; - let _e413 = chebyshev; - let _e415 = chebyshev; - chebyshev = ((_e412 * _e413) * _e415); - } - let _e417 = probeWeight; - let _e418 = chebyshev; - probeWeight = max((_e417 * max(_e418, 0.05f)), 0.000001f); - let _e422 = probeWeight; - if (_e422 < 0.2f) { - let _e424 = probeWeight; - let _e425 = probeWeight; - let _e428 = probeWeight; - probeWeight = (_e428 * ((_e424 * _e425) * 25f)); - } - let _e430 = probeWeight; - let _e431 = weight_2; - weight_2 = (_e431 * _e430); - } - let _e433 = unit; - param_118 = _e433; - let _e434 = ProbeOctahedral_u0028_vf3_u003b((¶m_118)); - let _e435 = irradianceCorner; - param_119 = _e435; - let _e436 = irradianceTile; - param_120 = _e436; - param_121 = _e434; - let _e437 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_119), (¶m_120), (¶m_121)); - let _e439 = weight_2; - let _e441 = total_2; - total_2 = (_e441 + (_e437.xyz * _e439)); - let _e443 = weight_2; - let _e444 = weights; - weights = (_e444 + _e443); - continue; - } else { - break; - } - continuing { - let _e446 = corner_1; - corner_1 = (_e446 + 1i); - } - } - let _e448 = weights; - if (_e448 > 0f) { - let _e450 = total_2; - let _e451 = weights; - local_23 = (_e450 / vec3(_e451)); - } else { - local_23 = vec3(0f, 0f, 0f); - } - let _e454 = local_23; - return _e454; -} - -fn IrradianceEnabled_u0028_() -> bool { - let _e177 = irradiance_info.origin[3u]; - return (_e177 > 0.5f); -} - -fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { - var param_122: vec3; - var param_123: vec3; + let _e273 = corner_1; + if (_e273 < 8i) { + let _e275 = corner_1; + let _e278 = corner_1; + let _e283 = corner_1; + offset_3 = vec3(f32((_e275 & 1i)), f32(((_e278 >> bitcast(1i)) & 1i)), f32(((_e283 >> bitcast(2i)) & 1i))); + let _e289 = base; + let _e290 = offset_3; + cell_1 = (_e289 + _e290); + let _e293 = cell_1[2u]; + let _e295 = counts[1u]; + let _e298 = cell_1[1u]; + let _e301 = counts[0u]; + let _e304 = cell_1[0u]; + probe = ((((_e293 * _e295) + _e298) * _e301) + _e304); + let _e306 = probe; + let _e307 = columns; + let _e312 = probe; + let _e313 = columns; + tile_4 = vec2((_e306 - (floor((_e306 / _e307)) * _e307)), floor((_e312 / _e313))); + let _e317 = tile_4; + let _e318 = irradianceTile; + irradianceCorner = (_e317 * (_e318 + 2f)); + let _e322 = tile_4[0u]; + let _e323 = depthTile; + let _e326 = momentsTop; + let _e328 = tile_4[1u]; + let _e329 = depthTile; + momentCorner = vec2((_e322 * (_e323 + 2f)), (_e326 + (_e328 * (_e329 + 2f)))); + let _e334 = irradianceCorner; + param_113 = (_e334 + vec2(1f)); + let _e337 = AtlasTexel_u0028_vf2_u003b((¶m_113)); + if (_e337.w < 0.5f) { + continue; + } + let _e340 = f_1; + let _e342 = f_1; + let _e343 = offset_3; + trilinear = mix((vec3(1f, 1f, 1f) - _e340), _e342, _e343); + let _e346 = trilinear[0u]; + let _e348 = trilinear[1u]; + let _e351 = trilinear[2u]; + weight_2 = max(((_e346 * _e348) * _e351), 0.001f); + let _e354 = origin_2; + let _e355 = spacing; + let _e356 = cell_1; + probePosition = (_e354 + (_e355 * _e356)); + let _e359 = probePosition; + let _e360 = biased; + toProbe = (_e359 - _e360); + let _e362 = toProbe; + distance_4 = length(_e362); + let _e364 = distance_4; + if (_e364 > 0.000001f) { + let _e366 = toProbe; + let _e367 = distance_4; + direction_2 = (_e366 / vec3(_e367)); + let _e370 = unit; + let _e371 = probePosition; + let _e372 = (*world_4); + facing = ((dot(_e370, normalize((_e371 - _e372))) * 0.5f) + 0.5f); + let _e378 = facing; + let _e379 = facing; + probeWeight = ((_e378 * _e379) + 0.2f); + let _e382 = direction_2; + param_114 = -(_e382); + let _e384 = ProbeOctahedral_u0028_vf3_u003b((¶m_114)); + let _e385 = momentCorner; + param_115 = _e385; + let _e386 = depthTile; + param_116 = _e386; + param_117 = _e384; + let _e387 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_115), (¶m_116), (¶m_117)); + moments_3 = _e387.xy; + chebyshev = 1f; + let _e389 = distance_4; + let _e391 = moments_3[0u]; + if (_e389 > _e391) { + let _e394 = moments_3[1u]; + let _e396 = moments_3[0u]; + let _e398 = moments_3[0u]; + variance_1 = max((_e394 - (_e396 * _e398)), 0.000001f); + let _e402 = distance_4; + let _e404 = moments_3[0u]; + difference = (_e402 - _e404); + let _e406 = variance_1; + let _e407 = variance_1; + let _e408 = difference; + let _e409 = difference; + chebyshev = (_e406 / (_e407 + (_e408 * _e409))); + let _e413 = chebyshev; + let _e414 = chebyshev; + let _e416 = chebyshev; + chebyshev = ((_e413 * _e414) * _e416); + } + let _e418 = probeWeight; + let _e419 = chebyshev; + probeWeight = max((_e418 * max(_e419, 0.05f)), 0.000001f); + let _e423 = probeWeight; + if (_e423 < 0.2f) { + let _e425 = probeWeight; + let _e426 = probeWeight; + let _e429 = probeWeight; + probeWeight = (_e429 * ((_e425 * _e426) * 25f)); + } + let _e431 = probeWeight; + let _e432 = weight_2; + weight_2 = (_e432 * _e431); + } + let _e434 = unit; + param_118 = _e434; + let _e435 = ProbeOctahedral_u0028_vf3_u003b((¶m_118)); + let _e436 = irradianceCorner; + param_119 = _e436; + let _e437 = irradianceTile; + param_120 = _e437; + param_121 = _e435; + let _e438 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_119), (¶m_120), (¶m_121)); + let _e440 = weight_2; + let _e442 = total_2; + total_2 = (_e442 + (_e438.xyz * _e440)); + let _e444 = weight_2; + let _e445 = weights; + weights = (_e445 + _e444); + continue; + } else { + break; + } + continuing { + let _e447 = corner_1; + corner_1 = (_e447 + 1i); + } + } + let _e449 = weights; + if (_e449 > 0f) { + let _e451 = total_2; + let _e452 = weights; + local_23 = (_e451 / vec3(_e452)); + } else { + local_23 = vec3(0f, 0f, 0f); + } + let _e455 = local_23; + return _e455; +} + +fn IrradianceEnabled_u0028_() -> bool { + let _e178 = irradiance_info.origin[3u]; + return (_e178 > 0.5f); +} + +fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { + var param_122: vec3; + var param_123: vec3; var param_124: vec3; var param_125: Surface; var param_126: Surface; var param_127: Surface; - let _e182 = IrradianceEnabled_u0028_(); - if _e182 { - let _e183 = v_world_position_1; - param_122 = _e183; - let _e185 = (*s_8).n; - param_123 = _e185; - let _e187 = (*s_8).v; - param_124 = _e187; - let _e188 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_122), (¶m_123), (¶m_124)); - let _e191 = frag_info.material[2u]; - (*s_8).ambient = (_e188 * _e191); - } - let _e194 = (*s_8); - param_125 = _e194; + let _e183 = IrradianceEnabled_u0028_(); + if _e183 { + let _e184 = v_world_position_1; + param_122 = _e184; + let _e186 = (*s_8).n; + param_123 = _e186; + let _e188 = (*s_8).v; + param_124 = _e188; + let _e189 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_122), (¶m_123), (¶m_124)); + let _e192 = frag_info.material[2u]; + (*s_8).ambient = (_e189 * _e192); + } + let _e195 = (*s_8); + param_125 = _e195; ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_125)); - let _e195 = param_125; - (*s_8) = _e195; - let _e196 = (*s_8); - param_126 = _e196; + let _e196 = param_125; + (*s_8) = _e196; + let _e197 = (*s_8); + param_126 = _e197; ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_126)); - let _e197 = param_126; - (*s_8) = _e197; - let _e198 = (*s_8); - param_127 = _e198; + let _e198 = param_126; + (*s_8) = _e198; + let _e199 = (*s_8); + param_127 = _e199; ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_127)); - let _e199 = param_127; - (*s_8) = _e199; + let _e200 = param_127; + (*s_8) = _e200; return; } @@ -7898,78 +7922,78 @@ fn ReadSurface_u0028_() -> Surface { var anchored: vec3; var noise_1: f32; - let _e182 = v_texcoord_1; - let _e183 = MaterialLodBias_u0028_(); - let _e184 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e182, _e183); - texel_6 = _e184; - let _e185 = texel_6; - param_128 = _e185.xyz; - let _e187 = SrgbToLinear_u0028_vf3_u003b((¶m_128)); - let _e189 = frag_info.base_color; - param_129 = _e189.xyz; - let _e191 = SrgbToLinear_u0028_vf3_u003b((¶m_129)); - let _e193 = v_color_1; - s_9.albedo = ((_e187 * _e191) * _e193.xyz); - let _e198 = texel_6[3u]; - let _e201 = frag_info.base_color[3u]; - let _e204 = v_color_1[3u]; - s_9.alpha = ((_e198 * _e201) * _e204); - let _e208 = s_9.albedo; - g_albedo = _e208; - let _e211 = frag_info.material2_[0u]; - cutoff = _e211; - let _e212 = cutoff; - if (_e212 >= 0f) { - let _e215 = s_9.alpha; - let _e216 = cutoff; - if (_e215 < _e216) { + let _e183 = v_texcoord_1; + let _e184 = MaterialLodBias_u0028_(); + let _e185 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e183, _e184); + texel_6 = _e185; + let _e186 = texel_6; + param_128 = _e186.xyz; + let _e188 = SrgbToLinear_u0028_vf3_u003b((¶m_128)); + let _e190 = frag_info.base_color; + param_129 = _e190.xyz; + let _e192 = SrgbToLinear_u0028_vf3_u003b((¶m_129)); + let _e194 = v_color_1; + s_9.albedo = ((_e188 * _e192) * _e194.xyz); + let _e199 = texel_6[3u]; + let _e202 = frag_info.base_color[3u]; + let _e205 = v_color_1[3u]; + s_9.alpha = ((_e199 * _e202) * _e205); + let _e209 = s_9.albedo; + g_albedo = _e209; + let _e212 = frag_info.material2_[0u]; + cutoff = _e212; + let _e213 = cutoff; + if (_e213 >= 0f) { + let _e216 = s_9.alpha; + let _e217 = cutoff; + if (_e216 < _e217) { discard; } } else { - let _e218 = cutoff; - if (_e218 < -1.5f) { - let _e220 = v_world_position_1; - anchored = floor((_e220 * 16f)); - let _e223 = anchored; - noise_1 = fract((sin(dot(_e223, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e229 = s_9.alpha; - let _e230 = noise_1; - if (_e229 < _e230) { + let _e219 = cutoff; + if (_e219 < -1.5f) { + let _e221 = v_world_position_1; + anchored = floor((_e221 * 16f)); + let _e224 = anchored; + noise_1 = fract((sin(dot(_e224, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e230 = s_9.alpha; + let _e231 = noise_1; + if (_e230 < _e231) { discard; } } } - let _e232 = cutoff; - let _e234 = cutoff; - g_premultiply = ((_e232 < 0f) && (_e234 > -0.75f)); - let _e237 = v_normal_1; - s_9.n = normalize(_e237); - let _e240 = gl_FrontFacing_1; - if !(_e240) { - let _e243 = s_9.n; - s_9.n = -(_e243); - } - let _e247 = frag_info.camera_position; - let _e249 = v_world_position_1; - s_9.v = normalize((_e247.xyz - _e249)); - let _e254 = s_9.n; - let _e256 = s_9.v; - s_9.n_dot_v = max(dot(_e254, _e256), 0.0001f); - let _e262 = frag_info.material[0u]; - s_9.metallic = clamp(_e262, 0f, 1f); - let _e267 = frag_info.material[1u]; - s_9.roughness = clamp(_e267, 0.02f, 1f); - let _e271 = frag_info.ambient_ground; - let _e274 = frag_info.ambient_sky; - let _e278 = s_9.n[1u]; - let _e285 = frag_info.material[2u]; - s_9.ambient = (mix(_e271.xyz, _e274.xyz, vec3(((_e278 * 0.5f) + 0.5f))) * _e285); - let _e290 = frag_info.frame_params[0u]; - s_9.exposure = max(_e290, 0f); + let _e233 = cutoff; + let _e235 = cutoff; + g_premultiply = ((_e233 < 0f) && (_e235 > -0.75f)); + let _e238 = v_normal_1; + s_9.n = normalize(_e238); + let _e241 = gl_FrontFacing_1; + if !(_e241) { + let _e244 = s_9.n; + s_9.n = -(_e244); + } + let _e248 = frag_info.camera_position; + let _e250 = v_world_position_1; + s_9.v = normalize((_e248.xyz - _e250)); + let _e255 = s_9.n; + let _e257 = s_9.v; + s_9.n_dot_v = max(dot(_e255, _e257), 0.0001f); + let _e263 = frag_info.material[0u]; + s_9.metallic = clamp(_e263, 0f, 1f); + let _e268 = frag_info.material[1u]; + s_9.roughness = clamp(_e268, 0.02f, 1f); + let _e272 = frag_info.ambient_ground; + let _e275 = frag_info.ambient_sky; + let _e279 = s_9.n[1u]; + let _e286 = frag_info.material[2u]; + s_9.ambient = (mix(_e272.xyz, _e275.xyz, vec3(((_e279 * 0.5f) + 0.5f))) * _e286); + let _e291 = frag_info.frame_params[0u]; + s_9.exposure = max(_e291, 0f); s_9.occlusion = 1f; s_9.emissive = vec3(0f, 0f, 0f); - let _e295 = s_9; - return _e295; + let _e296 = s_9; + return _e296; } fn main_1() { @@ -7987,29 +8011,30 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e182 = ReadSurface_u0028_(); - s_10 = _e182; - let _e183 = s_10; - param_130 = _e183; + light_transmittance = vec3(1f, 1f, 1f); + let _e183 = ReadSurface_u0028_(); + s_10 = _e183; + let _e184 = s_10; + param_130 = _e184; ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_130)); - let _e184 = param_130; - s_10 = _e184; - let _e186 = s_10.albedo; - let _e188 = s_10.ambient; - let _e189 = SampleLightmap_u0028_(); - let _e193 = s_10.occlusion; - ambient = ((_e186 * (_e188 + _e189)) * _e193); - let _e195 = s_10; - param_131 = _e195; - let _e196 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_131)); - let _e198 = s_10.occlusion; - let _e200 = ambient; - let _e203 = s_10.emissive; - param_132 = (((_e196 * _e198) + _e200) + _e203); - let _e206 = s_10.alpha; - param_133 = _e206; - let _e208 = s_10.roughness; - param_134 = _e208; + let _e185 = param_130; + s_10 = _e185; + let _e187 = s_10.albedo; + let _e189 = s_10.ambient; + let _e190 = SampleLightmap_u0028_(); + let _e194 = s_10.occlusion; + ambient = ((_e187 * (_e189 + _e190)) * _e194); + let _e196 = s_10; + param_131 = _e196; + let _e197 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_131)); + let _e199 = s_10.occlusion; + let _e201 = ambient; + let _e204 = s_10.emissive; + param_132 = (((_e197 * _e199) + _e201) + _e204); + let _e207 = s_10.alpha; + param_133 = _e207; + let _e209 = s_10.roughness; + param_134 = _e209; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_132), (¶m_133), (¶m_134)); return; } @@ -8436,6 +8461,7 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; +var light_transmittance: vec3; var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; @@ -8503,10 +8529,10 @@ var shadow_texture_tex: texture_2d; var shadow_texture_smp: sampler; fn ViewDepth_u0028_() -> f32 { - let _e176 = v_world_position_1; - let _e178 = fog_info.eye; - let _e182 = fog_info.forward; - return dot((_e176 - _e178.xyz), _e182.xyz); + let _e177 = v_world_position_1; + let _e179 = fog_info.eye; + let _e183 = fog_info.forward; + return dot((_e177 - _e179.xyz), _e183.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -8515,22 +8541,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e181 = ViewDepth_u0028_(); - z = abs(_e181); - let _e183 = z; - near = (_e183 / 5f); - let _e185 = z; - far = (_e185 / 200f); - let _e187 = far; + let _e182 = ViewDepth_u0028_(); + z = abs(_e182); + let _e184 = z; + near = (_e184 / 5f); + let _e186 = z; + far = (_e186 / 200f); let _e188 = far; - let _e190 = far; - far3_ = ((_e187 * _e188) * _e190); - let _e192 = (*alpha); - let _e193 = near; + let _e189 = far; + let _e191 = far; + far3_ = ((_e188 * _e189) * _e191); + let _e193 = (*alpha); let _e194 = near; - let _e197 = far3_; + let _e195 = near; let _e198 = far3_; - return (_e192 * clamp((10f / ((0.00001f + (_e193 * _e194)) + (_e197 * _e198))), 0.01f, 3000f)); + let _e199 = far3_; + return (_e193 * clamp((10f / ((0.00001f + (_e194 * _e195)) + (_e198 * _e199))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -8542,39 +8568,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e185 = fog_info.forward[3u]; - mode = _e185; - let _e186 = mode; - if (_e186 > 0.5f) { - let _e189 = frag_color[3u]; - alpha_1 = _e189; - let _e190 = alpha_1; - param = _e190; - let _e191 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e191; - let _e192 = frag_color; - let _e194 = weight; - let _e195 = (_e192.xyz * _e194); - let _e196 = alpha_1; - let _e197 = weight; - accumulate = vec4(_e195.x, _e195.y, _e195.z, (_e196 * _e197)); - let _e203 = mode; - let _e205 = mode; - revealage = ((_e203 > 1.5f) && (_e205 < 2.5f)); - let _e208 = revealage; - if _e208 { - let _e209 = alpha_1; - local = vec4(_e209); + let _e186 = fog_info.forward[3u]; + mode = _e186; + let _e187 = mode; + if (_e187 > 0.5f) { + let _e190 = frag_color[3u]; + alpha_1 = _e190; + let _e191 = alpha_1; + param = _e191; + let _e192 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e192; + let _e193 = frag_color; + let _e195 = weight; + let _e196 = (_e193.xyz * _e195); + let _e197 = alpha_1; + let _e198 = weight; + accumulate = vec4(_e196.x, _e196.y, _e196.z, (_e197 * _e198)); + let _e204 = mode; + let _e206 = mode; + revealage = ((_e204 > 1.5f) && (_e206 < 2.5f)); + let _e209 = revealage; + if _e209 { + let _e210 = alpha_1; + local = vec4(_e210); } else { - let _e211 = accumulate; - local = _e211; + let _e212 = accumulate; + local = _e212; } - let _e212 = local; - frag_color = _e212; - let _e213 = mode; - if (_e213 > 2.5f) { - let _e215 = alpha_1; - frag_surface = vec4(_e215); + let _e213 = local; + frag_color = _e213; + let _e214 = mode; + if (_e214 > 2.5f) { + let _e216 = alpha_1; + frag_surface = vec4(_e216); } } return; @@ -8583,29 +8609,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e179 = (*n)[0u]; - let _e182 = (*n)[1u]; - let _e186 = (*n)[2u]; - let _e189 = (*n); - (*n) = (_e189 / vec3(((abs(_e179) + abs(_e182)) + abs(_e186)))); - let _e192 = (*n); - e = _e192.xy; - let _e195 = (*n)[2u]; - if (_e195 < 0f) { - let _e197 = (*n); - let _e203 = (*n)[0u]; - let _e207 = (*n)[1u]; - e = ((vec2(1f) - abs(_e197.yx)) * vec2(select(-1f, 1f, (_e203 >= 0f)), select(-1f, 1f, (_e207 >= 0f)))); + let _e180 = (*n)[0u]; + let _e183 = (*n)[1u]; + let _e187 = (*n)[2u]; + let _e190 = (*n); + (*n) = (_e190 / vec3(((abs(_e180) + abs(_e183)) + abs(_e187)))); + let _e193 = (*n); + e = _e193.xy; + let _e196 = (*n)[2u]; + if (_e196 < 0f) { + let _e198 = (*n); + let _e204 = (*n)[0u]; + let _e208 = (*n)[1u]; + e = ((vec2(1f) - abs(_e198.yx)) * vec2(select(-1f, 1f, (_e204 >= 0f)), select(-1f, 1f, (_e208 >= 0f)))); } - let _e212 = e; - return ((_e212 * 0.5f) + vec2(0.5f)); + let _e213 = e; + return ((_e213 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e177 = (*linear); - let _e179 = (*linear); - let _e183 = (*linear); - return mix((_e177 * 12.92f), ((pow(_e179, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e183)); + let _e178 = (*linear); + let _e180 = (*linear); + let _e184 = (*linear); + return mix((_e178 * 12.92f), ((pow(_e180, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e184)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -8613,57 +8639,57 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e180 = g_albedo; - param_1 = clamp(_e180, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e182 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e182.x, _e182.y, _e182.z, 1f); - let _e187 = g_debug_surface_on; - if _e187 { - let _e188 = g_debug_surface; - let _e189 = ViewDepth_u0028_(); - frag_surface = vec4(_e188.x, _e188.y, _e188.z, _e189); + let _e181 = g_albedo; + param_1 = clamp(_e181, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e183 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e183.x, _e183.y, _e183.z, 1f); + let _e188 = g_debug_surface_on; + if _e188 { + let _e189 = g_debug_surface; + let _e190 = ViewDepth_u0028_(); + frag_surface = vec4(_e189.x, _e189.y, _e189.z, _e190); return; } - let _e194 = v_normal_1; - geometric = normalize(_e194); - let _e196 = gl_FrontFacing_1; - if !(_e196) { - let _e198 = geometric; - geometric = -(_e198); + let _e195 = v_normal_1; + geometric = normalize(_e195); + let _e197 = gl_FrontFacing_1; + if !(_e197) { + let _e199 = geometric; + geometric = -(_e199); } - let _e200 = geometric; - param_2 = _e200; - let _e201 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e202 = (*roughness); - let _e204 = ViewDepth_u0028_(); - frag_surface = vec4(_e201.x, _e201.y, clamp(_e202, 0f, 1f), _e204); + let _e201 = geometric; + param_2 = _e201; + let _e202 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e203 = (*roughness); + let _e205 = ViewDepth_u0028_(); + frag_surface = vec4(_e202.x, _e202.y, clamp(_e203, 0f, 1f), _e205); return; } fn EyeDistance_u0028_() -> f32 { - let _e176 = v_world_position_1; - let _e178 = fog_info.eye; - return distance(_e176, _e178.xyz); + let _e177 = v_world_position_1; + let _e179 = fog_info.eye; + return distance(_e177, _e179.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; - let _e181 = fog_info.fog[3u]; - density = _e181; - let _e182 = density; - if (_e182 <= 0f) { - let _e184 = (*color); - return _e184; + let _e182 = fog_info.fog[3u]; + density = _e182; + let _e183 = density; + if (_e183 <= 0f) { + let _e185 = (*color); + return _e185; } - let _e185 = EyeDistance_u0028_(); - d = _e185; - let _e187 = fog_info.fog; - let _e189 = (*color); - let _e190 = density; - let _e192 = d; - return mix(_e187.xyz, _e189, vec3(clamp(exp((-(_e190) * _e192)), 0f, 1f))); + let _e186 = EyeDistance_u0028_(); + d = _e186; + let _e188 = fog_info.fog; + let _e190 = (*color); + let _e191 = density; + let _e193 = d; + return mix(_e188.xyz, _e190, vec3(clamp(exp((-(_e191) * _e193)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -8671,18 +8697,18 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e182 = g_premultiply; - let _e183 = (*alpha_2); - weight_1 = select(1f, _e183, _e182); - let _e185 = (*linearColor); - param_3 = _e185; - let _e186 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e187 = weight_1; - let _e188 = (_e186 * _e187); - let _e189 = (*alpha_2); - frag_color = vec4(_e188.x, _e188.y, _e188.z, _e189); - let _e194 = (*roughness_1); - param_4 = _e194; + let _e183 = g_premultiply; + let _e184 = (*alpha_2); + weight_1 = select(1f, _e184, _e183); + let _e186 = (*linearColor); + param_3 = _e186; + let _e187 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e188 = weight_1; + let _e189 = (_e187 * _e188); + let _e190 = (*alpha_2); + frag_color = vec4(_e189.x, _e189.y, _e189.z, _e190); + let _e195 = (*roughness_1); + param_4 = _e195; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; @@ -8692,44 +8718,44 @@ fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ var shininess: f32; var specular: f32; - let _e181 = (*s).roughness; - shininess = mix(256f, 4f, _e181); - let _e184 = (*light).n_dot_h; - let _e185 = shininess; - let _e189 = frag_info.material[3u]; - specular = (pow(_e184, _e185) * _e189); - let _e192 = (*s).albedo; - let _e193 = specular; - return (_e192 + vec3(_e193)); + let _e182 = (*s).roughness; + shininess = mix(256f, 4f, _e182); + let _e185 = (*light).n_dot_h; + let _e186 = shininess; + let _e190 = frag_info.material[3u]; + specular = (pow(_e185, _e186) * _e190); + let _e193 = (*s).albedo; + let _e194 = specular; + return (_e193 + vec3(_e194)); } fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e177 = (*i); - if (_e177 == 0i) { + let _e178 = (*i); + if (_e178 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e179 = (*i); - if (_e179 == 1i) { + let _e180 = (*i); + if (_e180 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e181 = (*i); - if (_e181 == 2i) { + let _e182 = (*i); + if (_e182 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e183 = (*i); - if (_e183 == 3i) { + let _e184 = (*i); + if (_e184 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e185 = (*i); - if (_e185 == 4i) { + let _e186 = (*i); + if (_e186 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e187 = (*i); - if (_e187 == 5i) { + let _e188 = (*i); + if (_e188 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e189 = (*i); - if (_e189 == 6i) { + let _e190 = (*i); + if (_e190 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -8739,20 +8765,20 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr; var param_5: i32; - let _e182 = (*i_1); - param_5 = _e182; - let _e183 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); - p = _e183; - let _e185 = p[0u]; - let _e186 = (*ca); - let _e189 = p[1u]; - let _e190 = (*sa); - let _e194 = p[0u]; - let _e195 = (*sa); - let _e198 = p[1u]; - let _e199 = (*ca); - let _e203 = (*radius); - return (vec2(((_e185 * _e186) - (_e189 * _e190)), ((_e194 * _e195) + (_e198 * _e199))) * _e203); + let _e183 = (*i_1); + param_5 = _e183; + let _e184 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); + p = _e184; + let _e186 = p[0u]; + let _e187 = (*ca); + let _e190 = p[1u]; + let _e191 = (*sa); + let _e195 = p[0u]; + let _e196 = (*sa); + let _e199 = p[1u]; + let _e200 = (*ca); + let _e204 = (*radius); + return (vec2(((_e186 * _e187) - (_e190 * _e191)), ((_e195 * _e196) + (_e199 * _e200))) * _e204); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { @@ -8760,27 +8786,27 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr; var atlas: vec2; - let _e185 = point_shadow.params[0u]; - inset = _e185; - let _e186 = (*uv); - let _e187 = (*offset); - let _e189 = inset; + let _e186 = point_shadow.params[0u]; + inset = _e186; + let _e187 = (*uv); + let _e188 = (*offset); let _e190 = inset; - local_1 = clamp((_e186 + _e187), vec2(_e189), vec2((1f - _e190))); - let _e195 = local_1; - let _e196 = (*tile); - atlas = ((_e195 + _e196) * vec2(0.16666667f, 0.16666667f)); - let _e201 = point_shadow.params3_[0u]; - if (_e201 > 0.5f) { - let _e204 = atlas[1u]; - atlas[1u] = (1f - _e204); + let _e191 = inset; + local_1 = clamp((_e187 + _e188), vec2(_e190), vec2((1f - _e191))); + let _e196 = local_1; + let _e197 = (*tile); + atlas = ((_e196 + _e197) * vec2(0.16666667f, 0.16666667f)); + let _e202 = point_shadow.params3_[0u]; + if (_e202 > 0.5f) { + let _e205 = atlas[1u]; + atlas[1u] = (1f - _e205); } - let _e207 = atlas; - let _e208 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e207, 0f); - let _e210 = atlas; - let _e211 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e210, 0f); - let _e214 = (*range); - return (min(_e208.x, _e211.x) * _e214); + let _e208 = atlas; + let _e209 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e208, 0f); + let _e211 = atlas; + let _e212 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e211, 0f); + let _e215 = (*range); + return (min(_e209.x, _e212.x) * _e215); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { @@ -8790,24 +8816,24 @@ fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: pt var param_8: vec2; var param_9: f32; - let _e186 = (*uv_1); - param_6 = _e186; - let _e187 = (*offset_1); - param_7 = _e187; - let _e188 = (*tile_1); - param_8 = _e188; - let _e189 = (*range_1); - param_9 = _e189; - let _e190 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); - stored = _e190; - let _e191 = stored; - let _e192 = (*range_1); - if (_e191 >= (_e192 * 0.999f)) { + let _e187 = (*uv_1); + param_6 = _e187; + let _e188 = (*offset_1); + param_7 = _e188; + let _e189 = (*tile_1); + param_8 = _e189; + let _e190 = (*range_1); + param_9 = _e190; + let _e191 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); + stored = _e191; + let _e192 = stored; + let _e193 = (*range_1); + if (_e192 >= (_e193 * 0.999f)) { return 1f; } - let _e195 = (*receiver); - let _e196 = stored; - return select(1f, 0f, (_e195 > _e196)); + let _e196 = (*receiver); + let _e197 = stored; + return select(1f, 0f, (_e196 > _e197)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { @@ -8834,111 +8860,111 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u var world: f32; (*blockerOut) = -1f; - let _e207 = point_shadow.params2_[1u]; - lightRadius = _e207; - let _e210 = point_shadow.params2_[0u]; - minRadius = _e210; - let _e213 = point_shadow.params2_[2u]; - maxRadius = _e213; - let _e214 = lightRadius; - if (_e214 <= 0f) { - let _e216 = (*uv_2); - param_10 = _e216; + let _e208 = point_shadow.params2_[1u]; + lightRadius = _e208; + let _e211 = point_shadow.params2_[0u]; + minRadius = _e211; + let _e214 = point_shadow.params2_[2u]; + maxRadius = _e214; + let _e215 = lightRadius; + if (_e215 <= 0f) { + let _e217 = (*uv_2); + param_10 = _e217; param_11 = vec2(0f, 0f); - let _e217 = (*tile_2); - param_12 = _e217; - let _e218 = (*range_2); - param_13 = _e218; - let _e219 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); - (*blockerOut) = _e219; - let _e220 = minRadius; - return _e220; + let _e218 = (*tile_2); + param_12 = _e218; + let _e219 = (*range_2); + param_13 = _e219; + let _e220 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); + (*blockerOut) = _e220; + let _e221 = minRadius; + return _e221; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e221 = i_2; - if (_e221 < 8i) { - let _e223 = i_2; - param_14 = _e223; - let _e224 = (*ca_1); - param_15 = _e224; - let _e225 = (*sa_1); - param_16 = _e225; - let _e226 = maxRadius; - param_17 = _e226; - let _e227 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); - let _e228 = (*uv_2); - param_18 = _e228; - param_19 = _e227; - let _e229 = (*tile_2); - param_20 = _e229; - let _e230 = (*range_2); - param_21 = _e230; - let _e231 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); - stored_1 = _e231; - let _e232 = stored_1; - let _e233 = (*range_2); - if (_e232 >= (_e233 * 0.999f)) { + let _e222 = i_2; + if (_e222 < 8i) { + let _e224 = i_2; + param_14 = _e224; + let _e225 = (*ca_1); + param_15 = _e225; + let _e226 = (*sa_1); + param_16 = _e226; + let _e227 = maxRadius; + param_17 = _e227; + let _e228 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); + let _e229 = (*uv_2); + param_18 = _e229; + param_19 = _e228; + let _e230 = (*tile_2); + param_20 = _e230; + let _e231 = (*range_2); + param_21 = _e231; + let _e232 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); + stored_1 = _e232; + let _e233 = stored_1; + let _e234 = (*range_2); + if (_e233 >= (_e234 * 0.999f)) { continue; } - let _e236 = stored_1; - let _e237 = (*receiver_1); - if (_e236 >= _e237) { + let _e237 = stored_1; + let _e238 = (*receiver_1); + if (_e237 >= _e238) { continue; } - let _e239 = stored_1; - let _e240 = sum; - sum = (_e240 + _e239); - let _e242 = count; - count = (_e242 + 1f); + let _e240 = stored_1; + let _e241 = sum; + sum = (_e241 + _e240); + let _e243 = count; + count = (_e243 + 1f); continue; } else { break; } continuing { - let _e244 = i_2; - i_2 = (_e244 + 1i); + let _e245 = i_2; + i_2 = (_e245 + 1i); } } - let _e246 = count; - if (_e246 < 0.5f) { + let _e247 = count; + if (_e247 < 0.5f) { return -1f; } - let _e248 = sum; - let _e249 = count; - blocker = max((_e248 / _e249), 0.0001f); - let _e252 = blocker; - (*blockerOut) = _e252; - let _e253 = lightRadius; - let _e254 = (*receiver_1); - let _e255 = blocker; - let _e259 = blocker; - world = ((_e253 * max((_e254 - _e255), 0f)) / _e259); - let _e261 = world; - let _e262 = (*receiver_1); - let _e264 = (*tanHalf); - let _e267 = minRadius; - let _e268 = maxRadius; - return clamp((_e261 / ((2f * _e262) * _e264)), _e267, _e268); + let _e249 = sum; + let _e250 = count; + blocker = max((_e249 / _e250), 0.0001f); + let _e253 = blocker; + (*blockerOut) = _e253; + let _e254 = lightRadius; + let _e255 = (*receiver_1); + let _e256 = blocker; + let _e260 = blocker; + world = ((_e254 * max((_e255 - _e256), 0f)) / _e260); + let _e262 = world; + let _e263 = (*receiver_1); + let _e265 = (*tanHalf); + let _e268 = minRadius; + let _e269 = maxRadius; + return clamp((_e262 / ((2f * _e263) * _e265)), _e268, _e269); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { var local_2: vec2; - let _e178 = (*rows); - if (_e178 > 0f) { - let _e181 = gl_FragCoord_1[0u]; - let _e182 = (*rows); - let _e184 = gl_FragCoord_1[1u]; - local_2 = vec2(_e181, (_e182 - _e184)); + let _e179 = (*rows); + if (_e179 > 0f) { + let _e182 = gl_FragCoord_1[0u]; + let _e183 = (*rows); + let _e185 = gl_FragCoord_1[1u]; + local_2 = vec2(_e182, (_e183 - _e185)); } else { - let _e187 = gl_FragCoord_1; - local_2 = _e187.xy; + let _e188 = gl_FragCoord_1; + local_2 = _e188.xy; } - let _e189 = local_2; - return _e189; + let _e190 = local_2; + return _e190; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -8993,260 +9019,260 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_43: f32; var param_44: f32; - var phi_2149_: bool; - var phi_2210_: bool; + var phi_2150_: bool; + var phi_2211_: bool; - let _e230 = (*lightIndex); - let _e234 = point_shadow.slots[_e230][0u]; - slot = i32((_e234 + 0.5f)); - let _e237 = (*lightIndex); - let _e241 = point_shadow.slots[_e237][0u]; - if (_e241 < 0f) { + let _e231 = (*lightIndex); + let _e235 = point_shadow.slots[_e231][0u]; + slot = i32((_e235 + 0.5f)); + let _e238 = (*lightIndex); + let _e242 = point_shadow.slots[_e238][0u]; + if (_e242 < 0f) { return 1f; } - let _e245 = point_shadow.params[2u]; - strength = _e245; - let _e246 = strength; - if (_e246 <= 0f) { + let _e246 = point_shadow.params[2u]; + strength = _e246; + let _e247 = strength; + if (_e247 <= 0f) { return 1f; } - let _e248 = slot; - let _e251 = point_shadow.lights[_e248]; - let _e253 = (*world_1); - toLight = (_e251.xyz - _e253); - let _e255 = toLight; - toLightLength = max(length(_e255), 0.000001f); - let _e258 = (*normal); - let _e259 = toLight; - let _e260 = toLightLength; - nDotL = max(dot(_e258, (_e259 / vec3(_e260))), 0.15f); - let _e265 = nDotL; - let _e266 = nDotL; - let _e271 = nDotL; + let _e249 = slot; + let _e252 = point_shadow.lights[_e249]; + let _e254 = (*world_1); + toLight = (_e252.xyz - _e254); + let _e256 = toLight; + toLightLength = max(length(_e256), 0.000001f); + let _e259 = (*normal); + let _e260 = toLight; + let _e261 = toLightLength; + nDotL = max(dot(_e259, (_e260 / vec3(_e261))), 0.15f); + let _e266 = nDotL; + let _e267 = nDotL; let _e272 = nDotL; - slope = min((sqrt(max((1f - (_e265 * _e266)), 0f)) / (_e271 * _e272)), 8f); - let _e276 = toLightLength; - let _e278 = (*lightIndex); - let _e282 = point_shadow.slots[_e278][2u]; - let _e287 = point_shadow.params3_[1u]; - texel = (((2f * _e276) * max(_e282, 0.0001f)) * _e287); - let _e289 = (*world_1); - let _e290 = (*normal); - let _e291 = texel; - let _e295 = point_shadow.params[3u]; - let _e297 = slope; - origin = (_e289 + (((_e290 * _e291) * _e295) * (1f + _e297))); - let _e301 = origin; - let _e302 = slot; - let _e305 = point_shadow.lights[_e302]; - toFragment = (_e301 - _e305.xyz); - let _e308 = toFragment; - distance_ = length(_e308); - let _e310 = slot; - let _e314 = point_shadow.lights[_e310][3u]; - range_3 = max(_e314, 0.0001f); - let _e316 = distance_; - let _e317 = range_3; - if (_e316 >= _e317) { + let _e273 = nDotL; + slope = min((sqrt(max((1f - (_e266 * _e267)), 0f)) / (_e272 * _e273)), 8f); + let _e277 = toLightLength; + let _e279 = (*lightIndex); + let _e283 = point_shadow.slots[_e279][2u]; + let _e288 = point_shadow.params3_[1u]; + texel = (((2f * _e277) * max(_e283, 0.0001f)) * _e288); + let _e290 = (*world_1); + let _e291 = (*normal); + let _e292 = texel; + let _e296 = point_shadow.params[3u]; + let _e298 = slope; + origin = (_e290 + (((_e291 * _e292) * _e296) * (1f + _e298))); + let _e302 = origin; + let _e303 = slot; + let _e306 = point_shadow.lights[_e303]; + toFragment = (_e302 - _e306.xyz); + let _e309 = toFragment; + distance_ = length(_e309); + let _e311 = slot; + let _e315 = point_shadow.lights[_e311][3u]; + range_3 = max(_e315, 0.0001f); + let _e317 = distance_; + let _e318 = range_3; + if (_e317 >= _e318) { return 1f; } face = 0i; - let _e319 = (*lightIndex); - let _e323 = point_shadow.slots[_e319][1u]; - if (_e323 < 0.5f) { - let _e325 = toFragment; - a = abs(_e325); - let _e328 = a[0u]; - let _e330 = a[1u]; - let _e331 = (_e328 >= _e330); - phi_2149_ = _e331; - if _e331 { - let _e333 = a[0u]; - let _e335 = a[2u]; - phi_2149_ = (_e333 >= _e335); + let _e320 = (*lightIndex); + let _e324 = point_shadow.slots[_e320][1u]; + if (_e324 < 0.5f) { + let _e326 = toFragment; + a = abs(_e326); + let _e329 = a[0u]; + let _e331 = a[1u]; + let _e332 = (_e329 >= _e331); + phi_2150_ = _e332; + if _e332 { + let _e334 = a[0u]; + let _e336 = a[2u]; + phi_2150_ = (_e334 >= _e336); } - let _e338 = phi_2149_; - if _e338 { - let _e340 = toFragment[0u]; - face = select(1i, 0i, (_e340 > 0f)); + let _e339 = phi_2150_; + if _e339 { + let _e341 = toFragment[0u]; + face = select(1i, 0i, (_e341 > 0f)); } else { - let _e344 = a[1u]; - let _e346 = a[2u]; - if (_e344 >= _e346) { - let _e349 = toFragment[1u]; - face = select(3i, 2i, (_e349 > 0f)); + let _e345 = a[1u]; + let _e347 = a[2u]; + if (_e345 >= _e347) { + let _e350 = toFragment[1u]; + face = select(3i, 2i, (_e350 > 0f)); } else { - let _e353 = toFragment[2u]; - face = select(5i, 4i, (_e353 > 0f)); + let _e354 = toFragment[2u]; + face = select(5i, 4i, (_e354 > 0f)); } } } - let _e356 = slot; - let _e358 = face; - let _e362 = point_shadow.faces[((_e356 * 6i) + _e358)]; - let _e363 = origin; - clip = (_e362 * vec4(_e363.x, _e363.y, _e363.z, 1f)); - let _e370 = clip[3u]; - if (_e370 <= 0f) { + let _e357 = slot; + let _e359 = face; + let _e363 = point_shadow.faces[((_e357 * 6i) + _e359)]; + let _e364 = origin; + clip = (_e363 * vec4(_e364.x, _e364.y, _e364.z, 1f)); + let _e371 = clip[3u]; + if (_e371 <= 0f) { return 1f; } - let _e372 = clip; - let _e375 = clip[3u]; - ndc = (_e372.xy / vec2(_e375)); - let _e379 = ndc[0u]; - let _e381 = (abs(_e379) > 1f); - phi_2210_ = _e381; - if !(_e381) { - let _e384 = ndc[1u]; - phi_2210_ = (abs(_e384) > 1f); + let _e373 = clip; + let _e376 = clip[3u]; + ndc = (_e373.xy / vec2(_e376)); + let _e380 = ndc[0u]; + let _e382 = (abs(_e380) > 1f); + phi_2211_ = _e382; + if !(_e382) { + let _e385 = ndc[1u]; + phi_2211_ = (abs(_e385) > 1f); } - let _e388 = phi_2210_; - if _e388 { + let _e389 = phi_2211_; + if _e389 { return 1f; } - let _e390 = ndc[0u]; - let _e394 = ndc[1u]; - uv_3 = vec2(((_e390 * 0.5f) + 0.5f), (0.5f - (_e394 * 0.5f))); - let _e398 = face; - let _e400 = slot; - tile_3 = vec2(f32(_e398), f32(_e400)); - let _e403 = distance_; - let _e406 = point_shadow.params[1u]; - receiver_2 = (_e403 - _e406); - let _e410 = frag_info.target_origin[0u]; - param_22 = _e410; - let _e411 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); - noise = fract((52.982918f * fract(dot(_e411, vec2(0.06711056f, 0.00583715f))))); - let _e416 = noise; - angle = (_e416 * 6.2831855f); - let _e418 = angle; - ca_2 = cos(_e418); - let _e420 = angle; - sa_2 = sin(_e420); - let _e422 = (*lightIndex); - let _e426 = point_shadow.slots[_e422][2u]; - tanHalf_1 = max(_e426, 0.0001f); + let _e391 = ndc[0u]; + let _e395 = ndc[1u]; + uv_3 = vec2(((_e391 * 0.5f) + 0.5f), (0.5f - (_e395 * 0.5f))); + let _e399 = face; + let _e401 = slot; + tile_3 = vec2(f32(_e399), f32(_e401)); + let _e404 = distance_; + let _e407 = point_shadow.params[1u]; + receiver_2 = (_e404 - _e407); + let _e411 = frag_info.target_origin[0u]; + param_22 = _e411; + let _e412 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); + noise = fract((52.982918f * fract(dot(_e412, vec2(0.06711056f, 0.00583715f))))); + let _e417 = noise; + angle = (_e417 * 6.2831855f); + let _e419 = angle; + ca_2 = cos(_e419); + let _e421 = angle; + sa_2 = sin(_e421); + let _e423 = (*lightIndex); + let _e427 = point_shadow.slots[_e423][2u]; + tanHalf_1 = max(_e427, 0.0001f); blocker_1 = -1f; - let _e428 = uv_3; - param_23 = _e428; - let _e429 = tile_3; - param_24 = _e429; - let _e430 = range_3; - param_25 = _e430; - let _e431 = receiver_2; - param_26 = _e431; - let _e432 = ca_2; - param_27 = _e432; - let _e433 = sa_2; - param_28 = _e433; - let _e434 = tanHalf_1; - param_29 = _e434; - let _e435 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e436 = param_30; - blocker_1 = _e436; - radius_1 = _e435; - let _e439 = point_shadow.params2_[3u]; - if (_e439 > 0.5f) { + let _e429 = uv_3; + param_23 = _e429; + let _e430 = tile_3; + param_24 = _e430; + let _e431 = range_3; + param_25 = _e431; + let _e432 = receiver_2; + param_26 = _e432; + let _e433 = ca_2; + param_27 = _e433; + let _e434 = sa_2; + param_28 = _e434; + let _e435 = tanHalf_1; + param_29 = _e435; + let _e436 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); + let _e437 = param_30; + blocker_1 = _e437; + radius_1 = _e436; + let _e440 = point_shadow.params2_[3u]; + if (_e440 > 0.5f) { g_debug_surface_on = true; - let _e441 = radius_1; - if (_e441 < 0f) { + let _e442 = radius_1; + if (_e442 < 0f) { local_3 = vec3(0f, 0f, 1f); } else { - let _e443 = radius_1; - let _e446 = point_shadow.params2_[2u]; - let _e450 = blocker_1; - let _e451 = range_3; - local_3 = vec3(clamp((_e443 / max(_e446, 0.000001f)), 0f, 1f), clamp((_e450 / _e451), 0f, 1f), 0f); + let _e444 = radius_1; + let _e447 = point_shadow.params2_[2u]; + let _e451 = blocker_1; + let _e452 = range_3; + local_3 = vec3(clamp((_e444 / max(_e447, 0.000001f)), 0f, 1f), clamp((_e451 / _e452), 0f, 1f), 0f); } - let _e455 = local_3; - g_debug_surface = _e455; + let _e456 = local_3; + g_debug_surface = _e456; } - let _e456 = radius_1; - if (_e456 < 0f) { + let _e457 = radius_1; + if (_e457 < 0f) { return 1f; } - let _e458 = uv_3; - param_31 = _e458; + let _e459 = uv_3; + param_31 = _e459; param_32 = vec2(0f, 0f); - let _e459 = tile_3; - param_33 = _e459; - let _e460 = range_3; - param_34 = _e460; - let _e461 = receiver_2; - param_35 = _e461; - let _e462 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); - lit = _e462; - let _e463 = radius_1; - if (_e463 > 0f) { - i_3 = 0i; - loop { - let _e465 = i_3; - if (_e465 < 8i) { - let _e467 = i_3; - param_36 = _e467; - let _e468 = ca_2; - param_37 = _e468; - let _e469 = sa_2; - param_38 = _e469; - let _e470 = radius_1; - param_39 = _e470; - let _e471 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - let _e472 = uv_3; - param_40 = _e472; - param_41 = _e471; - let _e473 = tile_3; - param_42 = _e473; - let _e474 = range_3; - param_43 = _e474; - let _e475 = receiver_2; - param_44 = _e475; - let _e476 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); - let _e477 = lit; - lit = (_e477 + _e476); + let _e460 = tile_3; + param_33 = _e460; + let _e461 = range_3; + param_34 = _e461; + let _e462 = receiver_2; + param_35 = _e462; + let _e463 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); + lit = _e463; + let _e464 = radius_1; + if (_e464 > 0f) { + i_3 = 0i; + loop { + let _e466 = i_3; + if (_e466 < 8i) { + let _e468 = i_3; + param_36 = _e468; + let _e469 = ca_2; + param_37 = _e469; + let _e470 = sa_2; + param_38 = _e470; + let _e471 = radius_1; + param_39 = _e471; + let _e472 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); + let _e473 = uv_3; + param_40 = _e473; + param_41 = _e472; + let _e474 = tile_3; + param_42 = _e474; + let _e475 = range_3; + param_43 = _e475; + let _e476 = receiver_2; + param_44 = _e476; + let _e477 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); + let _e478 = lit; + lit = (_e478 + _e477); continue; } else { break; } continuing { - let _e479 = i_3; - i_3 = (_e479 + 1i); + let _e480 = i_3; + i_3 = (_e480 + 1i); } } - let _e481 = lit; - lit = (_e481 * 0.11111111f); + let _e482 = lit; + lit = (_e482 * 0.11111111f); } - let _e483 = lit; - let _e484 = strength; - return mix(1f, _e483, clamp(_e484, 0f, 1f)); + let _e484 = lit; + let _e485 = strength; + return mix(1f, _e484, clamp(_e485, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r: f32; var theta: f32; - let _e181 = (*i_4); - let _e184 = (*n_1); - r = sqrt(((f32(_e181) + 0.5f) / f32(_e184))); - let _e188 = (*i_4); - let _e191 = (*turn); - theta = ((f32(_e188) * 2.3999631f) + _e191); - let _e193 = r; - let _e194 = theta; - let _e196 = theta; - return (vec2(cos(_e194), sin(_e196)) * _e193); + let _e182 = (*i_4); + let _e185 = (*n_1); + r = sqrt(((f32(_e182) + 0.5f) / f32(_e185))); + let _e189 = (*i_4); + let _e192 = (*turn); + theta = ((f32(_e189) * 2.3999631f) + _e192); + let _e194 = r; + let _e195 = theta; + let _e197 = theta; + return (vec2(cos(_e195), sin(_e197)) * _e194); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; var param_45: f32; - let _e180 = frag_info.target_origin[0u]; - param_45 = _e180; - let _e181 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); - let _e184 = frag_info.target_origin[3u]; - at = (_e181 + vec2((5.588238f * max(_e184, 0f)))); - let _e189 = at; - return fract((52.982918f * fract(dot(_e189, vec2(0.06711056f, 0.00583715f))))); + let _e181 = frag_info.target_origin[0u]; + param_45 = _e181; + let _e182 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); + let _e185 = frag_info.target_origin[3u]; + at = (_e182 + vec2((5.588238f * max(_e185, 0f)))); + let _e190 = at; + return fract((52.982918f * fract(dot(_e190, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -9255,37 +9281,37 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_2: f32; - let _e178 = (*depth); - d_2 = ((2f * clamp(_e178, 0f, 1f)) - 1f); - let _e182 = d_2; - let _e185 = d_2; - return vec2(exp((40f * _e182)), -(exp((-5f * _e185)))); + let _e179 = (*depth); + d_2 = ((2f * clamp(_e179, 0f, 1f)) - 1f); + let _e183 = d_2; + let _e186 = d_2; + return vec2(exp((40f * _e183)), -(exp((-5f * _e186)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { @@ -9303,37 +9329,37 @@ fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr(0.004f, 0.0005f) * _e194); - let _e197 = scale[0u]; - let _e199 = scale[0u]; - let _e201 = (*moments_1); - param_47 = _e201.xy; - let _e204 = warped[0u]; - param_48 = _e204; - param_49 = (_e197 * _e199); - let _e205 = (*bleed_1); - param_50 = _e205; - let _e206 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); - positive = _e206; - let _e208 = scale[1u]; - let _e210 = scale[1u]; - let _e212 = (*moments_1); - param_51 = _e212.zw; - let _e215 = warped[1u]; - param_52 = _e215; - param_53 = (_e208 * _e210); - let _e216 = (*bleed_1); - param_54 = _e216; - let _e217 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); - negative = _e217; - let _e218 = positive; - let _e219 = negative; - return min(_e218, _e219); + let _e193 = (*depth_1); + param_46 = _e193; + let _e194 = EvsmWarp_u0028_f1_u003b((¶m_46)); + warped = _e194; + let _e195 = warped; + scale = (vec2(0.004f, 0.0005f) * _e195); + let _e198 = scale[0u]; + let _e200 = scale[0u]; + let _e202 = (*moments_1); + param_47 = _e202.xy; + let _e205 = warped[0u]; + param_48 = _e205; + param_49 = (_e198 * _e200); + let _e206 = (*bleed_1); + param_50 = _e206; + let _e207 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); + positive = _e207; + let _e209 = scale[1u]; + let _e211 = scale[1u]; + let _e213 = (*moments_1); + param_51 = _e213.zw; + let _e216 = warped[1u]; + param_52 = _e216; + param_53 = (_e209 * _e211); + let _e217 = (*bleed_1); + param_54 = _e217; + let _e218 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); + negative = _e218; + let _e219 = positive; + let _e220 = negative; + return min(_e219, _e220); } fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_1: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { @@ -9370,6 +9396,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; + var stored_2: vec4; + var through: vec3; var softness: f32; var lit_1: f32; var moments_2: vec4; @@ -9400,51 +9428,51 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_63: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3269_: bool; - var phi_3280_: bool; - var phi_3441_: bool; - var phi_3448_: bool; - var phi_3455_: bool; - - let _e244 = frag_info.shadow_params[3u]; - strength_1 = _e244; - let _e245 = strength_1; - if (_e245 <= 0f) { + var phi_3272_: bool; + var phi_3283_: bool; + var phi_3444_: bool; + var phi_3451_: bool; + var phi_3458_: bool; + + let _e247 = frag_info.shadow_params[3u]; + strength_1 = _e247; + let _e248 = strength_1; + if (_e248 <= 0f) { return 1f; } - let _e247 = (*lightIndex_1); - let _e250 = frag_info.frame_params[2u]; - if (_e247 != i32((_e250 + 0.5f))) { + let _e250 = (*lightIndex_1); + let _e253 = frag_info.frame_params[2u]; + if (_e250 != i32((_e253 + 0.5f))) { return 1f; } - let _e256 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e256 + 0.5f)); - let _e259 = v_world_position_1; - let _e261 = frag_info.camera_position; - viewDistance = length((_e259 - _e261.xyz)); + let _e259 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e259 + 0.5f)); + let _e262 = v_world_position_1; + let _e264 = frag_info.camera_position; + viewDistance = length((_e262 - _e264.xyz)); cascade = 0i; - let _e265 = cascadeCount; - let _e266 = (_e265 > 1i); - phi_3269_ = _e266; - if _e266 { - let _e267 = viewDistance; - let _e270 = frag_info.shadow_cascades[0u]; - phi_3269_ = (_e267 > _e270); - } - let _e273 = phi_3269_; - if _e273 { + let _e268 = cascadeCount; + let _e269 = (_e268 > 1i); + phi_3272_ = _e269; + if _e269 { + let _e270 = viewDistance; + let _e273 = frag_info.shadow_cascades[0u]; + phi_3272_ = (_e270 > _e273); + } + let _e276 = phi_3272_; + if _e276 { cascade = 1i; } - let _e274 = cascadeCount; - let _e275 = (_e274 > 2i); - phi_3280_ = _e275; - if _e275 { - let _e276 = viewDistance; - let _e279 = frag_info.shadow_cascades[1u]; - phi_3280_ = (_e276 > _e279); + let _e277 = cascadeCount; + let _e278 = (_e277 > 2i); + phi_3283_ = _e278; + if _e278 { + let _e279 = viewDistance; + let _e282 = frag_info.shadow_cascades[1u]; + phi_3283_ = (_e279 > _e282); } - let _e282 = phi_3280_; - if _e282 { + let _e285 = phi_3283_; + if _e285 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -9455,232 +9483,247 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e283 = attempt; - if (_e283 < 3i) { - let _e285 = cascade; - let _e286 = attempt; - which = (_e285 + _e286); - let _e288 = which; - let _e289 = cascadeCount; - if (_e288 >= _e289) { + let _e286 = attempt; + if (_e286 < 3i) { + let _e288 = cascade; + let _e289 = attempt; + which = (_e288 + _e289); + let _e291 = which; + let _e292 = cascadeCount; + if (_e291 >= _e292) { break; } - let _e291 = which; - if (_e291 == 0i) { - let _e294 = frag_info.shadow_matrix; - local_4 = _e294; + let _e294 = which; + if (_e294 == 0i) { + let _e297 = frag_info.shadow_matrix; + local_4 = _e297; } else { - let _e295 = which; - if (_e295 == 1i) { - let _e298 = frag_info.shadow_matrix_far; - local_5 = _e298; + let _e298 = which; + if (_e298 == 1i) { + let _e301 = frag_info.shadow_matrix_far; + local_5 = _e301; } else { - let _e300 = frag_info.shadow_matrix_farthest; - local_5 = _e300; + let _e303 = frag_info.shadow_matrix_farthest; + local_5 = _e303; } - let _e301 = local_5; - local_4 = _e301; - } - let _e302 = local_4; - matrix = _e302; - let _e305 = matrix[0][0u]; - let _e308 = matrix[1][0u]; - let _e311 = matrix[2][0u]; - axisX = vec3(_e305, _e308, _e311); - let _e315 = matrix[0][1u]; - let _e318 = matrix[1][1u]; - let _e321 = matrix[2][1u]; - axisY = vec3(_e315, _e318, _e321); - let _e323 = axisX; - rowX = max(length(_e323), 0.000001f); - let _e326 = axisY; - rowY = max(length(_e326), 0.000001f); - let _e331 = frag_info.shadow_cascades[3u]; - let _e333 = rowX; - texelMetres = ((2f * _e331) / _e333); - let _e337 = frag_info.shadow_cascades[3u]; - let _e340 = (*s_1).n; - let _e341 = axisX; - let _e344 = rowX; - let _e345 = rowX; - let _e349 = (*s_1).n; - let _e350 = axisY; - let _e353 = rowY; - let _e354 = rowY; - reach = ((3f * _e337) * ((abs(dot(_e340, _e341)) / (_e344 * _e345)) + (abs(dot(_e349, _e350)) / (_e353 * _e354)))); - let _e361 = frag_info.shadow_params[2u]; - let _e362 = texelMetres; - flatOffset = min(_e361, _e362); - let _e364 = v_world_position_1; - let _e366 = (*s_1).n; - let _e367 = flatOffset; - let _e368 = reach; - origin_1 = (_e364 + (_e366 * (_e367 + _e368))); - let _e372 = matrix; - let _e373 = origin_1; - lightSpace = (_e372 * vec4(_e373.x, _e373.y, _e373.z, 1f)); - let _e380 = lightSpace[3u]; - if (_e380 <= 0f) { + let _e304 = local_5; + local_4 = _e304; + } + let _e305 = local_4; + matrix = _e305; + let _e308 = matrix[0][0u]; + let _e311 = matrix[1][0u]; + let _e314 = matrix[2][0u]; + axisX = vec3(_e308, _e311, _e314); + let _e318 = matrix[0][1u]; + let _e321 = matrix[1][1u]; + let _e324 = matrix[2][1u]; + axisY = vec3(_e318, _e321, _e324); + let _e326 = axisX; + rowX = max(length(_e326), 0.000001f); + let _e329 = axisY; + rowY = max(length(_e329), 0.000001f); + let _e334 = frag_info.shadow_cascades[3u]; + let _e336 = rowX; + texelMetres = ((2f * _e334) / _e336); + let _e340 = frag_info.shadow_cascades[3u]; + let _e343 = (*s_1).n; + let _e344 = axisX; + let _e347 = rowX; + let _e348 = rowX; + let _e352 = (*s_1).n; + let _e353 = axisY; + let _e356 = rowY; + let _e357 = rowY; + reach = ((3f * _e340) * ((abs(dot(_e343, _e344)) / (_e347 * _e348)) + (abs(dot(_e352, _e353)) / (_e356 * _e357)))); + let _e364 = frag_info.shadow_params[2u]; + let _e365 = texelMetres; + flatOffset = min(_e364, _e365); + let _e367 = v_world_position_1; + let _e369 = (*s_1).n; + let _e370 = flatOffset; + let _e371 = reach; + origin_1 = (_e367 + (_e369 * (_e370 + _e371))); + let _e375 = matrix; + let _e376 = origin_1; + lightSpace = (_e375 * vec4(_e376.x, _e376.y, _e376.z, 1f)); + let _e383 = lightSpace[3u]; + if (_e383 <= 0f) { continue; } - let _e382 = lightSpace; - let _e385 = lightSpace[3u]; - candidate = (_e382.xyz / vec3(_e385)); - let _e389 = candidate[0u]; - let _e393 = candidate[1u]; - inTile = vec2(((_e389 * 0.5f) + 0.5f), (0.5f - (_e393 * 0.5f))); - let _e398 = inTile[0u]; - let _e399 = (_e398 < 0f); - phi_3441_ = _e399; - if !(_e399) { - let _e402 = inTile[0u]; - phi_3441_ = (_e402 > 1f); - } - let _e405 = phi_3441_; - phi_3448_ = _e405; - if !(_e405) { - let _e408 = inTile[1u]; - phi_3448_ = (_e408 < 0f); - } - let _e411 = phi_3448_; - phi_3455_ = _e411; - if !(_e411) { - let _e414 = inTile[1u]; - phi_3455_ = (_e414 > 1f); - } - let _e417 = phi_3455_; - if _e417 { + let _e385 = lightSpace; + let _e388 = lightSpace[3u]; + candidate = (_e385.xyz / vec3(_e388)); + let _e392 = candidate[0u]; + let _e396 = candidate[1u]; + inTile = vec2(((_e392 * 0.5f) + 0.5f), (0.5f - (_e396 * 0.5f))); + let _e401 = inTile[0u]; + let _e402 = (_e401 < 0f); + phi_3444_ = _e402; + if !(_e402) { + let _e405 = inTile[0u]; + phi_3444_ = (_e405 > 1f); + } + let _e408 = phi_3444_; + phi_3451_ = _e408; + if !(_e408) { + let _e411 = inTile[1u]; + phi_3451_ = (_e411 < 0f); + } + let _e414 = phi_3451_; + phi_3458_ = _e414; + if !(_e414) { + let _e417 = inTile[1u]; + phi_3458_ = (_e417 > 1f); + } + let _e420 = phi_3458_; + if _e420 { continue; } - let _e419 = candidate[2u]; - if (_e419 > 1f) { - let _e421 = which; - let _e422 = cascadeCount; - if (_e421 < (_e422 - 1i)) { + let _e422 = candidate[2u]; + if (_e422 > 1f) { + let _e424 = which; + let _e425 = cascadeCount; + if (_e424 < (_e425 - 1i)) { continue; } candidate[2u] = 1f; } - let _e427 = inTile[0u]; - let _e428 = which; - let _e431 = cascadeCount; - let _e435 = inTile[1u]; - uv_4 = vec2(((_e427 + f32(_e428)) / f32(_e431)), _e435); - let _e437 = candidate; - projected = _e437; - let _e438 = which; - cascade = _e438; - let _e439 = texelMetres; - cascadeTexel = _e439; - let _e442 = matrix[0][2u]; - let _e445 = matrix[1][2u]; - let _e448 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e442, _e445, _e448)), 0.000001f)); - let _e454 = (*s_1).n; - let _e457 = matrix[0][2u]; - let _e460 = matrix[1][2u]; - let _e463 = matrix[2][2u]; - let _e467 = cascadeDepth; - cosSlope = clamp((abs(dot(_e454, vec3(_e457, _e460, _e463))) * _e467), 0.1f, 1f); - let _e470 = texelMetres; - let _e471 = cosSlope; - let _e472 = cosSlope; - let _e478 = cosSlope; - let _e480 = cascadeDepth; - receiverSlope = (((_e470 * sqrt(max((1f - (_e471 * _e472)), 0f))) / _e478) / _e480); + let _e430 = inTile[0u]; + let _e431 = which; + let _e434 = cascadeCount; + let _e438 = inTile[1u]; + uv_4 = vec2(((_e430 + f32(_e431)) / f32(_e434)), _e438); + let _e440 = candidate; + projected = _e440; + let _e441 = which; + cascade = _e441; + let _e442 = texelMetres; + cascadeTexel = _e442; + let _e445 = matrix[0][2u]; + let _e448 = matrix[1][2u]; + let _e451 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e445, _e448, _e451)), 0.000001f)); + let _e457 = (*s_1).n; + let _e460 = matrix[0][2u]; + let _e463 = matrix[1][2u]; + let _e466 = matrix[2][2u]; + let _e470 = cascadeDepth; + cosSlope = clamp((abs(dot(_e457, vec3(_e460, _e463, _e466))) * _e470), 0.1f, 1f); + let _e473 = texelMetres; + let _e474 = cosSlope; + let _e475 = cosSlope; + let _e481 = cosSlope; + let _e483 = cascadeDepth; + receiverSlope = (((_e473 * sqrt(max((1f - (_e474 * _e475)), 0f))) / _e481) / _e483); found = true; break; } else { break; } continuing { - let _e482 = attempt; - attempt = (_e482 + 1i); + let _e485 = attempt; + attempt = (_e485 + 1i); } } - let _e484 = found; - if !(_e484) { + let _e487 = found; + if !(_e487) { return 1f; } - let _e486 = cascade; - if (_e486 == 0i) { - let _e490 = frag_info.shadow_bias[0u]; - local_6 = _e490; + let _e489 = cascade; + if (_e489 == 0i) { + let _e493 = frag_info.shadow_bias[0u]; + local_6 = _e493; } else { - let _e491 = cascade; - if (_e491 == 1i) { - let _e495 = frag_info.shadow_bias[1u]; - local_7 = _e495; - } else { - let _e498 = frag_info.shadow_bias[2u]; + let _e494 = cascade; + if (_e494 == 1i) { + let _e498 = frag_info.shadow_bias[1u]; local_7 = _e498; - } - let _e499 = local_7; - local_6 = _e499; - } - let _e500 = local_6; - bias = _e500; - let _e503 = frag_info.shadow_params[0u]; - let _e506 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e503, _e506); - let _e508 = cascade; - let _e510 = cascadeCount; - let _e514 = texel_1; - tileLo = (vec2((f32(_e508) / f32(_e510)), 0f) + (_e514 * 0.5f)); - let _e517 = cascade; - let _e520 = cascadeCount; - let _e524 = texel_1; - tileHi = (vec2((f32((_e517 + 1i)) / f32(_e520)), 1f) - (_e524 * 0.5f)); - let _e529 = frag_info.ambient_ground[3u]; - softness = _e529; + } else { + let _e501 = frag_info.shadow_bias[2u]; + local_7 = _e501; + } + let _e502 = local_7; + local_6 = _e502; + } + let _e503 = local_6; + bias = _e503; + let _e506 = frag_info.shadow_params[0u]; + let _e509 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e506, _e509); + let _e511 = cascade; + let _e513 = cascadeCount; + let _e517 = texel_1; + tileLo = (vec2((f32(_e511) / f32(_e513)), 0f) + (_e517 * 0.5f)); + let _e520 = cascade; + let _e523 = cascadeCount; + let _e527 = texel_1; + tileHi = (vec2((f32((_e520 + 1i)) / f32(_e523)), 1f) - (_e527 * 0.5f)); + let _e532 = frag_info.shadow_bias[3u]; + if (_e532 > 0.5f) { + let _e534 = uv_4; + let _e535 = tileLo; + let _e536 = tileHi; + let _e538 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e534, _e535, _e536), 0f); + stored_2 = _e538; + let _e540 = stored_2[1u]; + let _e543 = stored_2[2u]; + let _e546 = stored_2[3u]; + through = clamp(vec3((1f - _e540), (1f - _e543), _e546), vec3(0f), vec3(1f)); + let _e551 = through; + let _e552 = strength_1; + light_transmittance = mix(vec3(1f, 1f, 1f), _e551, vec3(clamp(_e552, 0f, 1f))); + } + let _e558 = frag_info.ambient_ground[3u]; + softness = _e558; lit_1 = 0f; - let _e530 = softness; - if (_e530 < 0f) { - let _e532 = uv_4; - let _e533 = tileLo; - let _e534 = tileHi; - let _e536 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e532, _e533, _e534), 0f); - moments_2 = _e536; - let _e538 = projected[2u]; - let _e539 = bias; - let _e541 = softness; - let _e545 = moments_2; - param_55 = _e545; - param_56 = (_e538 - _e539); - param_57 = clamp((-(_e541) - 1f), 0f, 0.95f); - let _e546 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); - lit_1 = _e546; + let _e559 = softness; + if (_e559 < 0f) { + let _e561 = uv_4; + let _e562 = tileLo; + let _e563 = tileHi; + let _e565 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e561, _e562, _e563), 0f); + moments_2 = _e565; + let _e567 = projected[2u]; + let _e568 = bias; + let _e570 = softness; + let _e574 = moments_2; + param_55 = _e574; + param_56 = (_e567 - _e568); + param_57 = clamp((-(_e570) - 1f), 0f, 0.95f); + let _e575 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); + lit_1 = _e575; } else { - let _e547 = softness; - if (_e547 <= 0f) { + let _e576 = softness; + if (_e576 <= 0f) { y = -1i; loop { - let _e549 = y; - if (_e549 <= 1i) { + let _e578 = y; + if (_e578 <= 1i) { x = -1i; loop { - let _e551 = x; - if (_e551 <= 1i) { - let _e553 = uv_4; - let _e554 = x; - let _e556 = y; - let _e559 = texel_1; - let _e562 = tileLo; - let _e563 = tileHi; - let _e565 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e553 + (vec2(f32(_e554), f32(_e556)) * _e559)), _e562, _e563), 0f); - occluder = _e565.x; - let _e568 = projected[2u]; - let _e569 = bias; - let _e571 = occluder; - let _e574 = lit_1; - lit_1 = (_e574 + select(1f, 0f, ((_e568 - _e569) > _e571))); + let _e580 = x; + if (_e580 <= 1i) { + let _e582 = uv_4; + let _e583 = x; + let _e585 = y; + let _e588 = texel_1; + let _e591 = tileLo; + let _e592 = tileHi; + let _e594 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e582 + (vec2(f32(_e583), f32(_e585)) * _e588)), _e591, _e592), 0f); + occluder = _e594.x; + let _e597 = projected[2u]; + let _e598 = bias; + let _e600 = occluder; + let _e603 = lit_1; + lit_1 = (_e603 + select(1f, 0f, ((_e597 - _e598) > _e600))); continue; } else { break; } continuing { - let _e576 = x; - x = (_e576 + 1i); + let _e605 = x; + x = (_e605 + 1i); } } continue; @@ -9688,125 +9731,125 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e578 = y; - y = (_e578 + 1i); + let _e607 = y; + y = (_e607 + 1i); } } - let _e580 = lit_1; - lit_1 = (_e580 * 0.11111111f); + let _e609 = lit_1; + lit_1 = (_e609 * 0.11111111f); } else { - let _e582 = softness; - spread = tan(min(_e582, 0.5f)); - let _e585 = ShadowNoise_u0028_(); - turn_1 = (_e585 * 6.2831855f); - let _e587 = spread; - let _e589 = projected[2u]; - let _e591 = cascadeDepth; - let _e593 = cascadeTexel; - searchRadius = clamp((((_e587 * _e589) * _e591) / _e593), 1f, 16f); + let _e611 = softness; + spread = tan(min(_e611, 0.5f)); + let _e614 = ShadowNoise_u0028_(); + turn_1 = (_e614 * 6.2831855f); + let _e616 = spread; + let _e618 = projected[2u]; + let _e620 = cascadeDepth; + let _e622 = cascadeTexel; + searchRadius = clamp((((_e616 * _e618) * _e620) / _e622), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e596 = i_5; - if (_e596 < 16i) { - let _e598 = i_5; - param_58 = _e598; + let _e625 = i_5; + if (_e625 < 16i) { + let _e627 = i_5; + param_58 = _e627; param_59 = 16i; - let _e599 = turn_1; - param_60 = _e599; - let _e600 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); - disc = _e600; - let _e601 = bias; - let _e602 = disc; - let _e604 = searchRadius; - let _e608 = receiverSlope; - tapBias = (_e601 + (max(((length(_e602) * _e604) - 1.5f), 0f) * _e608)); - let _e611 = uv_4; - let _e612 = disc; - let _e613 = texel_1; - let _e615 = searchRadius; - let _e618 = tileLo; - let _e619 = tileHi; - let _e621 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e611 + ((_e612 * _e613) * _e615)), _e618, _e619), 0f); - occluder_1 = _e621.x; - let _e624 = projected[2u]; - let _e625 = tapBias; - let _e627 = occluder_1; - if ((_e624 - _e625) > _e627) { - let _e629 = occluder_1; - let _e630 = blockerSum; - blockerSum = (_e630 + _e629); - let _e632 = blockerCount; - blockerCount = (_e632 + 1f); + let _e628 = turn_1; + param_60 = _e628; + let _e629 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); + disc = _e629; + let _e630 = bias; + let _e631 = disc; + let _e633 = searchRadius; + let _e637 = receiverSlope; + tapBias = (_e630 + (max(((length(_e631) * _e633) - 1.5f), 0f) * _e637)); + let _e640 = uv_4; + let _e641 = disc; + let _e642 = texel_1; + let _e644 = searchRadius; + let _e647 = tileLo; + let _e648 = tileHi; + let _e650 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e640 + ((_e641 * _e642) * _e644)), _e647, _e648), 0f); + occluder_1 = _e650.x; + let _e653 = projected[2u]; + let _e654 = tapBias; + let _e656 = occluder_1; + if ((_e653 - _e654) > _e656) { + let _e658 = occluder_1; + let _e659 = blockerSum; + blockerSum = (_e659 + _e658); + let _e661 = blockerCount; + blockerCount = (_e661 + 1f); } continue; } else { break; } continuing { - let _e634 = i_5; - i_5 = (_e634 + 1i); + let _e663 = i_5; + i_5 = (_e663 + 1i); } } - let _e636 = blockerCount; - if (_e636 <= 0f) { + let _e665 = blockerCount; + if (_e665 <= 0f) { return 1f; } - let _e639 = projected[2u]; - let _e640 = blockerSum; - let _e641 = blockerCount; - let _e645 = cascadeDepth; - gap = (max((_e639 - (_e640 / _e641)), 0f) * _e645); - let _e647 = spread; - let _e648 = gap; - let _e650 = cascadeTexel; - radius_2 = clamp(((_e647 * _e648) / _e650), 1f, 16f); + let _e668 = projected[2u]; + let _e669 = blockerSum; + let _e670 = blockerCount; + let _e674 = cascadeDepth; + gap = (max((_e668 - (_e669 / _e670)), 0f) * _e674); + let _e676 = spread; + let _e677 = gap; + let _e679 = cascadeTexel; + radius_2 = clamp(((_e676 * _e677) / _e679), 1f, 16f); i_6 = 0i; loop { - let _e653 = i_6; - if (_e653 < 16i) { - let _e655 = turn_1; - let _e657 = i_6; - param_61 = _e657; + let _e682 = i_6; + if (_e682 < 16i) { + let _e684 = turn_1; + let _e686 = i_6; + param_61 = _e686; param_62 = 16i; - param_63 = (_e655 + 1f); - let _e658 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - disc_1 = _e658; - let _e659 = bias; - let _e660 = disc_1; - let _e662 = radius_2; - let _e666 = receiverSlope; - tapBias_1 = (_e659 + (max(((length(_e660) * _e662) - 1.5f), 0f) * _e666)); - let _e669 = uv_4; - let _e670 = disc_1; - let _e671 = texel_1; - let _e673 = radius_2; - let _e676 = tileLo; - let _e677 = tileHi; - let _e679 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e669 + ((_e670 * _e671) * _e673)), _e676, _e677), 0f); - occluder_2 = _e679.x; - let _e682 = projected[2u]; - let _e683 = tapBias_1; - let _e685 = occluder_2; - let _e688 = lit_1; - lit_1 = (_e688 + select(1f, 0f, ((_e682 - _e683) > _e685))); + param_63 = (_e684 + 1f); + let _e687 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); + disc_1 = _e687; + let _e688 = bias; + let _e689 = disc_1; + let _e691 = radius_2; + let _e695 = receiverSlope; + tapBias_1 = (_e688 + (max(((length(_e689) * _e691) - 1.5f), 0f) * _e695)); + let _e698 = uv_4; + let _e699 = disc_1; + let _e700 = texel_1; + let _e702 = radius_2; + let _e705 = tileLo; + let _e706 = tileHi; + let _e708 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e698 + ((_e699 * _e700) * _e702)), _e705, _e706), 0f); + occluder_2 = _e708.x; + let _e711 = projected[2u]; + let _e712 = tapBias_1; + let _e714 = occluder_2; + let _e717 = lit_1; + lit_1 = (_e717 + select(1f, 0f, ((_e711 - _e712) > _e714))); continue; } else { break; } continuing { - let _e690 = i_6; - i_6 = (_e690 + 1i); + let _e719 = i_6; + i_6 = (_e719 + 1i); } } - let _e692 = lit_1; - lit_1 = (_e692 * 0.0625f); + let _e721 = lit_1; + lit_1 = (_e721 * 0.0625f); } } - let _e694 = lit_1; - let _e695 = strength_1; - return mix(1f, _e694, clamp(_e695, 0f, 1f)); + let _e723 = lit_1; + let _e724 = strength_1; + return mix(1f, _e723, clamp(_e724, 0f, 1f)); } fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_2: ptr, index: ptr) -> f32 { @@ -9814,19 +9857,19 @@ fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_65: LightSample; var param_66: i32; - let _e182 = (*s_2); - param_64 = _e182; - let _e183 = (*light_2); - param_65 = _e183; - let _e184 = (*index); - param_66 = _e184; - let _e185 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); - return _e185; + let _e183 = (*s_2); + param_64 = _e183; + let _e184 = (*light_2); + param_65 = _e184; + let _e185 = (*index); + param_66 = _e185; + let _e186 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); + return _e186; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e177 = (*index_1); - return (_e177 < 8i); + let _e178 = (*index_1); + return (_e178 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -9834,26 +9877,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e181 = (*distance_1); let _e182 = (*distance_1); - attenuation = (1f / max((_e181 * _e182), 0.0001f)); - let _e186 = (*range_4); - if (_e186 > 0f) { - let _e188 = (*distance_1); - let _e189 = (*range_4); - ratio = (_e188 / _e189); - let _e191 = ratio; + let _e183 = (*distance_1); + attenuation = (1f / max((_e182 * _e183), 0.0001f)); + let _e187 = (*range_4); + if (_e187 > 0f) { + let _e189 = (*distance_1); + let _e190 = (*range_4); + ratio = (_e189 / _e190); let _e192 = ratio; - let _e194 = ratio; - let _e196 = ratio; - window = clamp((1f - (((_e191 * _e192) * _e194) * _e196)), 0f, 1f); - let _e200 = window; + let _e193 = ratio; + let _e195 = ratio; + let _e197 = ratio; + window = clamp((1f - (((_e192 * _e193) * _e195) * _e197)), 0f, 1f); let _e201 = window; - let _e203 = attenuation; - attenuation = (_e203 * (_e200 * _e201)); + let _e202 = window; + let _e204 = attenuation; + attenuation = (_e204 * (_e201 * _e202)); } - let _e205 = attenuation; - return _e205; + let _e206 = attenuation; + return _e206; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -9870,75 +9913,75 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var u: f32; var v: f32; - let _e193 = (*halfWidth); - let _e194 = (*halfHeight); - n_2 = cross(_e193, _e194); - let _e196 = n_2; - nLen = length(_e196); - let _e198 = nLen; - if (_e198 < 0.000000000001f) { - let _e200 = (*centre); - return _e200; + let _e194 = (*halfWidth); + let _e195 = (*halfHeight); + n_2 = cross(_e194, _e195); + let _e197 = n_2; + nLen = length(_e197); + let _e199 = nLen; + if (_e199 < 0.000000000001f) { + let _e201 = (*centre); + return _e201; } - let _e201 = nLen; - let _e202 = n_2; - n_2 = (_e202 / vec3(_e201)); - let _e205 = (*centre); - let _e206 = (*world_2); - toPlane = (_e205 - _e206); - let _e208 = (*mirror); - let _e209 = n_2; - denom = dot(_e208, _e209); - let _e211 = denom; - if (abs(_e211) < 0.00001f) { - let _e214 = toPlane; - let _e215 = n_2; - let _e216 = toPlane; - let _e217 = n_2; - onPlane = (_e214 - (_e215 * dot(_e216, _e217))); + let _e202 = nLen; + let _e203 = n_2; + n_2 = (_e203 / vec3(_e202)); + let _e206 = (*centre); + let _e207 = (*world_2); + toPlane = (_e206 - _e207); + let _e209 = (*mirror); + let _e210 = n_2; + denom = dot(_e209, _e210); + let _e212 = denom; + if (abs(_e212) < 0.00001f) { + let _e215 = toPlane; + let _e216 = n_2; + let _e217 = toPlane; + let _e218 = n_2; + onPlane = (_e215 - (_e216 * dot(_e217, _e218))); } else { - let _e221 = toPlane; - let _e222 = n_2; - let _e224 = denom; - t_1 = (dot(_e221, _e222) / _e224); - let _e226 = t_1; - if (_e226 > 0f) { - let _e228 = (*mirror); - let _e229 = t_1; - local_8 = (_e228 * _e229); + let _e222 = toPlane; + let _e223 = n_2; + let _e225 = denom; + t_1 = (dot(_e222, _e223) / _e225); + let _e227 = t_1; + if (_e227 > 0f) { + let _e229 = (*mirror); + let _e230 = t_1; + local_8 = (_e229 * _e230); } else { - let _e231 = toPlane; - let _e232 = n_2; - let _e233 = toPlane; - let _e234 = n_2; - local_8 = (_e231 - (_e232 * dot(_e233, _e234))); + let _e232 = toPlane; + let _e233 = n_2; + let _e234 = toPlane; + let _e235 = n_2; + local_8 = (_e232 - (_e233 * dot(_e234, _e235))); } - let _e238 = local_8; - onPlane = _e238; + let _e239 = local_8; + onPlane = _e239; } - let _e239 = onPlane; - let _e240 = toPlane; - offset_2 = (_e239 - _e240); - let _e242 = (*halfWidth); + let _e240 = onPlane; + let _e241 = toPlane; + offset_2 = (_e240 - _e241); let _e243 = (*halfWidth); - wLen2_ = max(dot(_e242, _e243), 0.000000000001f); - let _e246 = (*halfHeight); + let _e244 = (*halfWidth); + wLen2_ = max(dot(_e243, _e244), 0.000000000001f); let _e247 = (*halfHeight); - hLen2_ = max(dot(_e246, _e247), 0.000000000001f); - let _e250 = offset_2; - let _e251 = (*halfWidth); - let _e253 = wLen2_; - u = clamp((dot(_e250, _e251) / _e253), -1f, 1f); - let _e256 = offset_2; - let _e257 = (*halfHeight); - let _e259 = hLen2_; - v = clamp((dot(_e256, _e257) / _e259), -1f, 1f); - let _e262 = (*centre); - let _e263 = (*halfWidth); - let _e264 = u; - let _e267 = (*halfHeight); - let _e268 = v; - return ((_e262 + (_e263 * _e264)) + (_e267 * _e268)); + let _e248 = (*halfHeight); + hLen2_ = max(dot(_e247, _e248), 0.000000000001f); + let _e251 = offset_2; + let _e252 = (*halfWidth); + let _e254 = wLen2_; + u = clamp((dot(_e251, _e252) / _e254), -1f, 1f); + let _e257 = offset_2; + let _e258 = (*halfHeight); + let _e260 = hLen2_; + v = clamp((dot(_e257, _e258) / _e260), -1f, 1f); + let _e263 = (*centre); + let _e264 = (*halfWidth); + let _e265 = u; + let _e268 = (*halfHeight); + let _e269 = v; + return ((_e263 + (_e264 * _e265)) + (_e268 * _e269)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>, n_3: ptr>) -> f32 { @@ -9949,32 +9992,32 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var len: f32; var local_9: f32; - let _e185 = (*a_1); let _e186 = (*a_1); - ua = (_e185 / vec3(max(length(_e186), 0.000000000001f))); - let _e191 = (*b); + let _e187 = (*a_1); + ua = (_e186 / vec3(max(length(_e187), 0.000000000001f))); let _e192 = (*b); - ub = (_e191 / vec3(max(length(_e192), 0.000000000001f))); - let _e197 = ua; - let _e198 = ub; - angle_1 = acos(clamp(dot(_e197, _e198), -1f, 1f)); - let _e202 = ua; - let _e203 = ub; - axis = cross(_e202, _e203); - let _e205 = axis; - len = length(_e205); - let _e207 = len; - if (_e207 > 0.000001f) { - let _e209 = angle_1; - let _e210 = axis; - let _e211 = (*n_3); - let _e214 = len; - local_9 = ((_e209 * dot(_e210, _e211)) / _e214); + let _e193 = (*b); + ub = (_e192 / vec3(max(length(_e193), 0.000000000001f))); + let _e198 = ua; + let _e199 = ub; + angle_1 = acos(clamp(dot(_e198, _e199), -1f, 1f)); + let _e203 = ua; + let _e204 = ub; + axis = cross(_e203, _e204); + let _e206 = axis; + len = length(_e206); + let _e208 = len; + if (_e208 > 0.000001f) { + let _e210 = angle_1; + let _e211 = axis; + let _e212 = (*n_3); + let _e215 = len; + local_9 = ((_e210 * dot(_e211, _e212)) / _e215); } else { local_9 = 0f; } - let _e216 = local_9; - return _e216; + let _e217 = local_9; + return _e217; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr, 4>>, n_4: ptr>) -> f32 { @@ -10005,98 +10048,98 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr(0f, 0f, 0f); i_7 = 0i; loop { - let _e199 = i_7; - if (_e199 < 4i) { - let _e201 = i_7; - let _e203 = (*corners)[_e201]; - a_2 = _e203; - let _e204 = i_7; - if (_e204 == 3i) { + let _e200 = i_7; + if (_e200 < 4i) { + let _e202 = i_7; + let _e204 = (*corners)[_e202]; + a_2 = _e204; + let _e205 = i_7; + if (_e205 == 3i) { local_10 = 0i; } else { - let _e206 = i_7; - local_10 = (_e206 + 1i); - } - let _e208 = local_10; - let _e210 = (*corners)[_e208]; - b_1 = _e210; - let _e211 = a_2; - let _e212 = (*n_4); - ha = dot(_e211, _e212); - let _e214 = b_1; - let _e215 = (*n_4); - hb = dot(_e214, _e215); - let _e217 = ha; - let _e218 = hb; - d_3 = (_e217 - _e218); - let _e220 = a_2; - let _e221 = b_1; - let _e222 = a_2; - let _e224 = d_3; - if (abs(_e224) > 0.000000000001f) { - let _e227 = ha; - let _e228 = d_3; - local_11 = (_e227 / _e228); - } else { - local_11 = 0f; + let _e207 = i_7; + local_10 = (_e207 + 1i); } - let _e230 = local_11; - q = (_e220 + ((_e221 - _e222) * _e230)); - let _e233 = ha; - aAbove = (_e233 > 0f); - let _e235 = hb; - bAbove = (_e235 > 0f); - let _e237 = aAbove; - let _e238 = bAbove; - if (_e237 || _e238) { - let _e240 = aAbove; - let _e241 = a_2; - let _e242 = q; - let _e245 = bAbove; - let _e246 = b_1; - let _e247 = q; - param_67 = select(_e242, _e241, vec3(_e240)); - param_68 = select(_e247, _e246, vec3(_e245)); - let _e250 = (*n_4); - param_69 = _e250; - let _e251 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); - local_12 = _e251; + let _e209 = local_10; + let _e211 = (*corners)[_e209]; + b_1 = _e211; + let _e212 = a_2; + let _e213 = (*n_4); + ha = dot(_e212, _e213); + let _e215 = b_1; + let _e216 = (*n_4); + hb = dot(_e215, _e216); + let _e218 = ha; + let _e219 = hb; + d_3 = (_e218 - _e219); + let _e221 = a_2; + let _e222 = b_1; + let _e223 = a_2; + let _e225 = d_3; + if (abs(_e225) > 0.000000000001f) { + let _e228 = ha; + let _e229 = d_3; + local_11 = (_e228 / _e229); + } else { + local_11 = 0f; + } + let _e231 = local_11; + q = (_e221 + ((_e222 - _e223) * _e231)); + let _e234 = ha; + aAbove = (_e234 > 0f); + let _e236 = hb; + bAbove = (_e236 > 0f); + let _e238 = aAbove; + let _e239 = bAbove; + if (_e238 || _e239) { + let _e241 = aAbove; + let _e242 = a_2; + let _e243 = q; + let _e246 = bAbove; + let _e247 = b_1; + let _e248 = q; + param_67 = select(_e243, _e242, vec3(_e241)); + param_68 = select(_e248, _e247, vec3(_e246)); + let _e251 = (*n_4); + param_69 = _e251; + let _e252 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); + local_12 = _e252; } else { local_12 = 0f; } - let _e252 = local_12; - let _e253 = total; - total = (_e253 + _e252); - let _e255 = aAbove; - let _e256 = bAbove; - let _e259 = q; - let _e260 = exit; - exit = select(_e260, _e259, vec3((_e255 && !(_e256)))); - let _e263 = aAbove; - let _e265 = bAbove; - let _e267 = q; - let _e268 = entry; - entry = select(_e268, _e267, vec3((!(_e263) && _e265))); + let _e253 = local_12; + let _e254 = total; + total = (_e254 + _e253); + let _e256 = aAbove; + let _e257 = bAbove; + let _e260 = q; + let _e261 = exit; + exit = select(_e261, _e260, vec3((_e256 && !(_e257)))); + let _e264 = aAbove; + let _e266 = bAbove; + let _e268 = q; + let _e269 = entry; + entry = select(_e269, _e268, vec3((!(_e264) && _e266))); continue; } else { break; } continuing { - let _e271 = i_7; - i_7 = (_e271 + 1i); + let _e272 = i_7; + i_7 = (_e272 + 1i); } } - let _e273 = exit; - param_70 = _e273; - let _e274 = entry; - param_71 = _e274; - let _e275 = (*n_4); - param_72 = _e275; - let _e276 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); - let _e277 = total; - total = (_e277 + _e276); - let _e279 = total; - return max((-(_e279) * 0.5f), 0f); + let _e274 = exit; + param_70 = _e274; + let _e275 = entry; + param_71 = _e275; + let _e276 = (*n_4); + param_72 = _e276; + let _e277 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); + let _e278 = total; + total = (_e278 + _e277); + let _e280 = total; + return max((-(_e280) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { @@ -10105,91 +10148,91 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 var local_14: f32; var local_15: f32; - let _e182 = (*slot_1); let _e183 = (*slot_1); - lane = (_e182 - ((_e183 / 4i) * 4i)); - let _e187 = lane; - if (_e187 == 0i) { - let _e190 = (*four)[0u]; - local_13 = _e190; + let _e184 = (*slot_1); + lane = (_e183 - ((_e184 / 4i) * 4i)); + let _e188 = lane; + if (_e188 == 0i) { + let _e191 = (*four)[0u]; + local_13 = _e191; } else { - let _e191 = lane; - if (_e191 == 1i) { - let _e194 = (*four)[1u]; - local_14 = _e194; + let _e192 = lane; + if (_e192 == 1i) { + let _e195 = (*four)[1u]; + local_14 = _e195; } else { - let _e195 = lane; - if (_e195 == 2i) { - let _e198 = (*four)[2u]; - local_15 = _e198; + let _e196 = lane; + if (_e196 == 2i) { + let _e199 = (*four)[2u]; + local_15 = _e199; } else { - let _e200 = (*four)[3u]; - local_15 = _e200; + let _e201 = (*four)[3u]; + local_15 = _e201; } - let _e201 = local_15; - local_14 = _e201; + let _e202 = local_15; + local_14 = _e202; } - let _e202 = local_14; - local_13 = _e202; + let _e203 = local_14; + local_13 = _e203; } - let _e203 = local_13; - return _e203; + let _e204 = local_13; + return _e204; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { var param_73: vec4; var param_74: i32; - let _e179 = (*slot_2); - let _e183 = light_list_info.scales[(_e179 / 4i)]; - param_73 = _e183; - let _e184 = (*slot_2); - param_74 = _e184; - let _e185 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); - return _e185; + let _e180 = (*slot_2); + let _e184 = light_list_info.scales[(_e180 / 4i)]; + param_73 = _e184; + let _e185 = (*slot_2); + param_74 = _e185; + let _e186 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); + return _e186; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_3: vec4; var b_2: vec4; - let _e181 = light_list_info.slot_rows[0i]; - let _e182 = (*row); - a_3 = abs((_e181 - vec4(_e182))); - let _e188 = light_list_info.slot_rows[1i]; - let _e189 = (*row); - b_2 = abs((_e188 - vec4(_e189))); - let _e194 = a_3[0u]; - let _e196 = a_3[1u]; - let _e199 = a_3[2u]; - let _e201 = a_3[3u]; - let _e205 = b_2[0u]; - let _e207 = b_2[1u]; - let _e210 = b_2[2u]; - let _e212 = b_2[3u]; - return (min(min(min(_e194, _e196), min(_e199, _e201)), min(min(_e205, _e207), min(_e210, _e212))) < 0.5f); + let _e182 = light_list_info.slot_rows[0i]; + let _e183 = (*row); + a_3 = abs((_e182 - vec4(_e183))); + let _e189 = light_list_info.slot_rows[1i]; + let _e190 = (*row); + b_2 = abs((_e189 - vec4(_e190))); + let _e195 = a_3[0u]; + let _e197 = a_3[1u]; + let _e200 = a_3[2u]; + let _e202 = a_3[3u]; + let _e206 = b_2[0u]; + let _e208 = b_2[1u]; + let _e211 = b_2[2u]; + let _e213 = b_2[3u]; + return (min(min(min(_e195, _e197), min(_e200, _e202)), min(min(_e206, _e208), min(_e211, _e213))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { var param_75: vec4; var param_76: i32; - let _e179 = (*slot_3); - let _e183 = light_list_info.indices[(_e179 / 4i)]; - param_75 = _e183; - let _e184 = (*slot_3); - param_76 = _e184; - let _e185 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); - return _e185; + let _e180 = (*slot_3); + let _e184 = light_list_info.indices[(_e180 / 4i)]; + param_75 = _e184; + let _e185 = (*slot_3); + param_76 = _e185; + let _e186 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); + return _e186; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e178 = (*texel_2); - let _e182 = light_list_info.list[1u]; - let _e184 = (*row_1); - let _e188 = light_list_info.list[2u]; - let _e191 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e178 + 0.5f) * _e182), ((_e184 + 0.5f) * _e188)), 0f); - return _e191; + let _e179 = (*texel_2); + let _e183 = light_list_info.list[1u]; + let _e185 = (*row_1); + let _e189 = light_list_info.list[2u]; + let _e192 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e179 + 0.5f) * _e183), ((_e185 + 0.5f) * _e189)), 0f); + return _e192; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -10203,45 +10246,45 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var param_79: vec4; var param_80: i32; - let _e186 = g_cluster_offset; - let _e187 = (*slot_4); - entry_1 = (_e186 + f32(_e187)); - let _e190 = entry_1; - row_2 = floor((_e190 / 16f)); - let _e193 = entry_1; - let _e194 = row_2; - within = (_e193 - (_e194 * 16f)); - let _e197 = within; - texel_3 = floor((_e197 / 4f)); - let _e202 = light_list_info.cluster_depth[3u]; - let _e203 = row_2; - let _e205 = texel_3; - param_77 = _e205; - param_78 = (_e202 + _e203); - let _e206 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); - four_1 = _e206; - let _e207 = within; - let _e208 = texel_3; - let _e213 = four_1; - param_79 = _e213; - param_80 = i32(((_e207 - (_e208 * 4f)) + 0.5f)); - let _e214 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); - return _e214; -} - -fn Clustered_u0028_() -> bool { - let _e178 = light_list_info.cluster_grid[3u]; - return (_e178 > 0.5f); -} - -fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_3: ptr) -> LightSample { - var light_3: LightSample; - var position: vec4; - var color_1: vec4; - var direction: vec4; - var cone: vec4; - var slot_5: i32; - var clustered: bool; + let _e187 = g_cluster_offset; + let _e188 = (*slot_4); + entry_1 = (_e187 + f32(_e188)); + let _e191 = entry_1; + row_2 = floor((_e191 / 16f)); + let _e194 = entry_1; + let _e195 = row_2; + within = (_e194 - (_e195 * 16f)); + let _e198 = within; + texel_3 = floor((_e198 / 4f)); + let _e203 = light_list_info.cluster_depth[3u]; + let _e204 = row_2; + let _e206 = texel_3; + param_77 = _e206; + param_78 = (_e203 + _e204); + let _e207 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); + four_1 = _e207; + let _e208 = within; + let _e209 = texel_3; + let _e214 = four_1; + param_79 = _e214; + param_80 = i32(((_e208 - (_e209 * 4f)) + 0.5f)); + let _e215 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); + return _e215; +} + +fn Clustered_u0028_() -> bool { + let _e179 = light_list_info.cluster_grid[3u]; + return (_e179 > 0.5f); +} + +fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_3: ptr) -> LightSample { + var light_3: LightSample; + var position: vec4; + var color_1: vec4; + var direction: vec4; + var cone: vec4; + var slot_5: i32; + var clustered: bool; var listRow: f32; var local_16: f32; var param_81: i32; @@ -10287,264 +10330,264 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e229 = (*index_2); - if (_e229 < 8i) { - let _e231 = (*index_2); - let _e234 = frag_info.light_position[_e231]; - position = _e234; - let _e235 = (*index_2); - let _e238 = frag_info.light_color[_e235]; - color_1 = _e238; - let _e239 = (*index_2); - let _e242 = frag_info.light_direction[_e239]; - direction = _e242; - let _e243 = (*index_2); - let _e246 = frag_info.light_cone[_e243]; - cone = _e246; + let _e230 = (*index_2); + if (_e230 < 8i) { + let _e232 = (*index_2); + let _e235 = frag_info.light_position[_e232]; + position = _e235; + let _e236 = (*index_2); + let _e239 = frag_info.light_color[_e236]; + color_1 = _e239; + let _e240 = (*index_2); + let _e243 = frag_info.light_direction[_e240]; + direction = _e243; + let _e244 = (*index_2); + let _e247 = frag_info.light_cone[_e244]; + cone = _e247; } else { - let _e247 = (*index_2); - slot_5 = (_e247 - 8i); - let _e249 = Clustered_u0028_(); - clustered = _e249; - let _e250 = clustered; - if _e250 { - let _e251 = slot_5; - param_81 = _e251; - let _e252 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_16 = _e252; + let _e248 = (*index_2); + slot_5 = (_e248 - 8i); + let _e250 = Clustered_u0028_(); + clustered = _e250; + let _e251 = clustered; + if _e251 { + let _e252 = slot_5; + param_81 = _e252; + let _e253 = ClusterRow_u0028_i1_u003b((¶m_81)); + local_16 = _e253; } else { - let _e253 = slot_5; - param_82 = _e253; - let _e254 = LightListRow_u0028_i1_u003b((¶m_82)); - local_16 = _e254; + let _e254 = slot_5; + param_82 = _e254; + let _e255 = LightListRow_u0028_i1_u003b((¶m_82)); + local_16 = _e255; } - let _e255 = local_16; - listRow = _e255; - let _e256 = listRow; - let _e260 = light_list_info.list[2u]; - v_1 = ((_e256 + 0.5f) * _e260); - let _e264 = light_list_info.list[1u]; - u_1 = _e264; - let _e265 = u_1; - let _e267 = v_1; - let _e269 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e265), _e267), 0f); - position = _e269; - let _e270 = u_1; - let _e272 = v_1; - let _e274 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e270), _e272), 0f); - color_1 = _e274; - let _e275 = u_1; - let _e277 = v_1; - let _e279 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e275), _e277), 0f); - direction = _e279; - let _e280 = u_1; - let _e282 = v_1; - let _e284 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e280), _e282), 0f); - cone = _e284; - let _e285 = clustered; - if _e285 { - let _e286 = listRow; - param_83 = _e286; - let _e287 = InSlots_u0028_f1_u003b((¶m_83)); - local_17 = select(1f, 0f, _e287); + let _e256 = local_16; + listRow = _e256; + let _e257 = listRow; + let _e261 = light_list_info.list[2u]; + v_1 = ((_e257 + 0.5f) * _e261); + let _e265 = light_list_info.list[1u]; + u_1 = _e265; + let _e266 = u_1; + let _e268 = v_1; + let _e270 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e266), _e268), 0f); + position = _e270; + let _e271 = u_1; + let _e273 = v_1; + let _e275 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e271), _e273), 0f); + color_1 = _e275; + let _e276 = u_1; + let _e278 = v_1; + let _e280 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e276), _e278), 0f); + direction = _e280; + let _e281 = u_1; + let _e283 = v_1; + let _e285 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e281), _e283), 0f); + cone = _e285; + let _e286 = clustered; + if _e286 { + let _e287 = listRow; + param_83 = _e287; + let _e288 = InSlots_u0028_f1_u003b((¶m_83)); + local_17 = select(1f, 0f, _e288); } else { - let _e289 = slot_5; - param_84 = _e289; - let _e290 = LightListScale_u0028_i1_u003b((¶m_84)); - local_17 = _e290; + let _e290 = slot_5; + param_84 = _e290; + let _e291 = LightListScale_u0028_i1_u003b((¶m_84)); + local_17 = _e291; } - let _e291 = local_17; - let _e293 = color_1[3u]; - color_1[3u] = (_e293 * _e291); + let _e292 = local_17; + let _e294 = color_1[3u]; + color_1[3u] = (_e294 * _e292); } - let _e297 = position[3u]; - type_39 = _e297; - let _e298 = type_39; - if (_e298 > 2.5f) { - let _e300 = direction; - halfWidth_1 = _e300.xyz; - let _e302 = cone; - halfHeight_1 = _e302.xyz; - let _e304 = position; - let _e306 = v_world_position_1; - toCentre = (_e304.xyz - _e306); - let _e308 = toCentre; - let _e309 = halfWidth_1; - let _e311 = halfHeight_1; - corners_1[0i] = ((_e308 - _e309) - _e311); - let _e314 = toCentre; - let _e315 = halfWidth_1; - let _e317 = halfHeight_1; - corners_1[1i] = ((_e314 + _e315) - _e317); - let _e320 = toCentre; - let _e321 = halfWidth_1; - let _e323 = halfHeight_1; - corners_1[2i] = ((_e320 + _e321) + _e323); - let _e326 = toCentre; - let _e327 = halfWidth_1; - let _e329 = halfHeight_1; - corners_1[3i] = ((_e326 - _e327) + _e329); - let _e332 = toCentre; - let _e333 = halfWidth_1; - let _e334 = halfHeight_1; - behind = (dot(_e332, cross(_e333, _e334)) >= 0f); - let _e338 = behind; - if _e338 { + let _e298 = position[3u]; + type_39 = _e298; + let _e299 = type_39; + if (_e299 > 2.5f) { + let _e301 = direction; + halfWidth_1 = _e301.xyz; + let _e303 = cone; + halfHeight_1 = _e303.xyz; + let _e305 = position; + let _e307 = v_world_position_1; + toCentre = (_e305.xyz - _e307); + let _e309 = toCentre; + let _e310 = halfWidth_1; + let _e312 = halfHeight_1; + corners_1[0i] = ((_e309 - _e310) - _e312); + let _e315 = toCentre; + let _e316 = halfWidth_1; + let _e318 = halfHeight_1; + corners_1[1i] = ((_e315 + _e316) - _e318); + let _e321 = toCentre; + let _e322 = halfWidth_1; + let _e324 = halfHeight_1; + corners_1[2i] = ((_e321 + _e322) + _e324); + let _e327 = toCentre; + let _e328 = halfWidth_1; + let _e330 = halfHeight_1; + corners_1[3i] = ((_e327 - _e328) + _e330); + let _e333 = toCentre; + let _e334 = halfWidth_1; + let _e335 = halfHeight_1; + behind = (dot(_e333, cross(_e334, _e335)) >= 0f); + let _e339 = behind; + if _e339 { local_18 = 0f; } else { - let _e339 = corners_1; - param_85 = _e339; - let _e341 = (*s_3).n; - param_86 = _e341; - let _e342 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_18 = _e342; + let _e340 = corners_1; + param_85 = _e340; + let _e342 = (*s_3).n; + param_86 = _e342; + let _e343 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); + local_18 = _e343; } - let _e343 = local_18; - formFactor = _e343; - let _e344 = halfWidth_1; - let _e345 = halfHeight_1; - area = (length(cross(_e344, _e345)) * 4f); - let _e349 = area; - if (_e349 > 0.000000001f) { - let _e351 = area; - local_19 = (1f / _e351); + let _e344 = local_18; + formFactor = _e344; + let _e345 = halfWidth_1; + let _e346 = halfHeight_1; + area = (length(cross(_e345, _e346)) * 4f); + let _e350 = area; + if (_e350 > 0.000000001f) { + let _e352 = area; + local_19 = (1f / _e352); } else { local_19 = 0f; } - let _e353 = local_19; - radiance = _e353; - let _e354 = toCentre; - distance_2 = length(_e354); - let _e357 = direction[3u]; - if (_e357 > 0f) { - let _e359 = distance_2; - let _e361 = direction[3u]; - ratio_1 = (_e359 / _e361); - let _e363 = ratio_1; + let _e354 = local_19; + radiance = _e354; + let _e355 = toCentre; + distance_2 = length(_e355); + let _e358 = direction[3u]; + if (_e358 > 0f) { + let _e360 = distance_2; + let _e362 = direction[3u]; + ratio_1 = (_e360 / _e362); let _e364 = ratio_1; - let _e366 = ratio_1; - let _e368 = ratio_1; - window_1 = clamp((1f - (((_e363 * _e364) * _e366) * _e368)), 0f, 1f); - let _e372 = window_1; + let _e365 = ratio_1; + let _e367 = ratio_1; + let _e369 = ratio_1; + window_1 = clamp((1f - (((_e364 * _e365) * _e367) * _e369)), 0f, 1f); let _e373 = window_1; - let _e375 = radiance; - radiance = (_e375 * (_e372 * _e373)); + let _e374 = window_1; + let _e376 = radiance; + radiance = (_e376 * (_e373 * _e374)); } - let _e378 = (*s_3).v; - let _e381 = (*s_3).n; - mirror_1 = reflect(-(_e378), _e381); - let _e383 = position; - param_87 = _e383.xyz; - let _e385 = halfWidth_1; - param_88 = _e385; - let _e386 = halfHeight_1; - param_89 = _e386; - let _e387 = v_world_position_1; - param_90 = _e387; - let _e388 = mirror_1; - param_91 = _e388; - let _e389 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); - representative = _e389; - let _e390 = representative; - let _e391 = v_world_position_1; - toPoint = (_e390 - _e391); - let _e393 = toPoint; - pointDistance = length(_e393); - let _e395 = pointDistance; - if (_e395 > 0.000001f) { - let _e397 = toPoint; - let _e398 = pointDistance; - local_20 = (_e397 / vec3(_e398)); + let _e379 = (*s_3).v; + let _e382 = (*s_3).n; + mirror_1 = reflect(-(_e379), _e382); + let _e384 = position; + param_87 = _e384.xyz; + let _e386 = halfWidth_1; + param_88 = _e386; + let _e387 = halfHeight_1; + param_89 = _e387; + let _e388 = v_world_position_1; + param_90 = _e388; + let _e389 = mirror_1; + param_91 = _e389; + let _e390 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); + representative = _e390; + let _e391 = representative; + let _e392 = v_world_position_1; + toPoint = (_e391 - _e392); + let _e394 = toPoint; + pointDistance = length(_e394); + let _e396 = pointDistance; + if (_e396 > 0.000001f) { + let _e398 = toPoint; + let _e399 = pointDistance; + local_20 = (_e398 / vec3(_e399)); } else { - let _e402 = (*s_3).n; - local_20 = _e402; + let _e403 = (*s_3).n; + local_20 = _e403; } - let _e403 = local_20; - light_3.l = _e403; - let _e406 = light_3.l; - let _e408 = (*s_3).v; - light_3.h = normalize((_e406 + _e408)); - let _e412 = formFactor; - light_3.n_dot_l = _e412; - let _e415 = (*s_3).n; - let _e417 = light_3.h; - light_3.n_dot_h = max(dot(_e415, _e417), 0f); - let _e422 = (*s_3).v; - let _e424 = light_3.h; - light_3.v_dot_h = max(dot(_e422, _e424), 0f); - let _e428 = color_1; - let _e431 = color_1[3u]; - let _e433 = radiance; - light_3.radiance = ((_e428.xyz * _e431) * _e433); - let _e436 = light_3; - return _e436; + let _e404 = local_20; + light_3.l = _e404; + let _e407 = light_3.l; + let _e409 = (*s_3).v; + light_3.h = normalize((_e407 + _e409)); + let _e413 = formFactor; + light_3.n_dot_l = _e413; + let _e416 = (*s_3).n; + let _e418 = light_3.h; + light_3.n_dot_h = max(dot(_e416, _e418), 0f); + let _e423 = (*s_3).v; + let _e425 = light_3.h; + light_3.v_dot_h = max(dot(_e423, _e425), 0f); + let _e429 = color_1; + let _e432 = color_1[3u]; + let _e434 = radiance; + light_3.radiance = ((_e429.xyz * _e432) * _e434); + let _e437 = light_3; + return _e437; } - let _e437 = direction; - aim = normalize(_e437.xyz); + let _e438 = direction; + aim = normalize(_e438.xyz); attenuation_1 = 1f; - let _e440 = type_39; - if (_e440 < 0.5f) { - let _e442 = aim; - light_3.l = -(_e442); + let _e441 = type_39; + if (_e441 < 0.5f) { + let _e443 = aim; + light_3.l = -(_e443); } else { - let _e445 = position; - let _e447 = v_world_position_1; - toLight_1 = (_e445.xyz - _e447); - let _e449 = toLight_1; - distance_3 = length(_e449); - let _e451 = distance_3; - if (_e451 < 0.000001f) { - let _e454 = (*s_3).n; - light_3.l = _e454; - let _e457 = (*s_3).n; - light_3.h = _e457; + let _e446 = position; + let _e448 = v_world_position_1; + toLight_1 = (_e446.xyz - _e448); + let _e450 = toLight_1; + distance_3 = length(_e450); + let _e452 = distance_3; + if (_e452 < 0.000001f) { + let _e455 = (*s_3).n; + light_3.l = _e455; + let _e458 = (*s_3).n; + light_3.h = _e458; light_3.radiance = vec3(0f, 0f, 0f); light_3.n_dot_l = 0f; light_3.n_dot_h = 0f; light_3.v_dot_h = 0f; - let _e463 = light_3; - return _e463; + let _e464 = light_3; + return _e464; } - let _e464 = toLight_1; - let _e465 = distance_3; - light_3.l = (_e464 / vec3(_e465)); - let _e469 = distance_3; - param_92 = _e469; - let _e471 = direction[3u]; - param_93 = _e471; - let _e472 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); - attenuation_1 = _e472; - let _e473 = type_39; - if (_e473 > 1.5f) { - let _e475 = aim; - let _e477 = light_3.l; - cosAngle = dot(_e475, -(_e477)); - let _e480 = cosAngle; - let _e482 = cone[1u]; - let _e485 = cone[0u]; - let _e487 = cone[1u]; - let _e491 = attenuation_1; - attenuation_1 = (_e491 * clamp(((_e480 - _e482) / (_e485 - _e487)), 0f, 1f)); + let _e465 = toLight_1; + let _e466 = distance_3; + light_3.l = (_e465 / vec3(_e466)); + let _e470 = distance_3; + param_92 = _e470; + let _e472 = direction[3u]; + param_93 = _e472; + let _e473 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); + attenuation_1 = _e473; + let _e474 = type_39; + if (_e474 > 1.5f) { + let _e476 = aim; + let _e478 = light_3.l; + cosAngle = dot(_e476, -(_e478)); + let _e481 = cosAngle; + let _e483 = cone[1u]; + let _e486 = cone[0u]; + let _e488 = cone[1u]; + let _e492 = attenuation_1; + attenuation_1 = (_e492 * clamp(((_e481 - _e483) / (_e486 - _e488)), 0f, 1f)); } } - let _e494 = light_3.l; - let _e496 = (*s_3).v; - light_3.h = normalize((_e494 + _e496)); - let _e501 = (*s_3).n; - let _e503 = light_3.l; - light_3.n_dot_l = max(dot(_e501, _e503), 0f); - let _e508 = (*s_3).n; - let _e510 = light_3.h; - light_3.n_dot_h = max(dot(_e508, _e510), 0f); - let _e515 = (*s_3).v; - let _e517 = light_3.h; - light_3.v_dot_h = max(dot(_e515, _e517), 0f); - let _e521 = color_1; - let _e524 = color_1[3u]; - let _e526 = attenuation_1; - light_3.radiance = ((_e521.xyz * _e524) * _e526); - let _e529 = light_3; - return _e529; + let _e495 = light_3.l; + let _e497 = (*s_3).v; + light_3.h = normalize((_e495 + _e497)); + let _e502 = (*s_3).n; + let _e504 = light_3.l; + light_3.n_dot_l = max(dot(_e502, _e504), 0f); + let _e509 = (*s_3).n; + let _e511 = light_3.h; + light_3.n_dot_h = max(dot(_e509, _e511), 0f); + let _e516 = (*s_3).v; + let _e518 = light_3.h; + light_3.v_dot_h = max(dot(_e516, _e518), 0f); + let _e522 = color_1; + let _e525 = color_1[3u]; + let _e527 = attenuation_1; + light_3.radiance = ((_e522.xyz * _e525) * _e527); + let _e530 = light_3; + return _e530; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -10562,58 +10605,58 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var param_94: f32; var param_95: f32; - let _e191 = light_list_info.cluster_view_projection; - let _e192 = (*world_3); - clip_1 = (_e191 * vec4(_e192.x, _e192.y, _e192.z, 1f)); - let _e198 = clip_1; - let _e201 = clip_1[3u]; - ndc_1 = (_e198.xy / vec2(max(_e201, 0.000001f))); - let _e206 = light_list_info.cluster_grid; - grid = _e206.xyz; - let _e210 = light_list_info.cluster_depth[0u]; - near_1 = _e210; - let _e212 = ndc_1[0u]; - let _e216 = grid[0u]; - let _e220 = grid[0u]; - tx = clamp(floor((((_e212 * 0.5f) + 0.5f) * _e216)), 0f, (_e220 - 1f)); - let _e224 = ndc_1[1u]; - let _e228 = grid[1u]; - let _e232 = grid[1u]; - ty = clamp(floor((((_e224 * 0.5f) + 0.5f) * _e228)), 0f, (_e232 - 1f)); - let _e236 = clip_1[3u]; - let _e237 = near_1; - if (_e236 <= _e237) { + let _e192 = light_list_info.cluster_view_projection; + let _e193 = (*world_3); + clip_1 = (_e192 * vec4(_e193.x, _e193.y, _e193.z, 1f)); + let _e199 = clip_1; + let _e202 = clip_1[3u]; + ndc_1 = (_e199.xy / vec2(max(_e202, 0.000001f))); + let _e207 = light_list_info.cluster_grid; + grid = _e207.xyz; + let _e211 = light_list_info.cluster_depth[0u]; + near_1 = _e211; + let _e213 = ndc_1[0u]; + let _e217 = grid[0u]; + let _e221 = grid[0u]; + tx = clamp(floor((((_e213 * 0.5f) + 0.5f) * _e217)), 0f, (_e221 - 1f)); + let _e225 = ndc_1[1u]; + let _e229 = grid[1u]; + let _e233 = grid[1u]; + ty = clamp(floor((((_e225 * 0.5f) + 0.5f) * _e229)), 0f, (_e233 - 1f)); + let _e237 = clip_1[3u]; + let _e238 = near_1; + if (_e237 <= _e238) { local_21 = 0f; } else { - let _e240 = clip_1[3u]; - let _e241 = near_1; - let _e246 = light_list_info.cluster_depth[1u]; - let _e250 = grid[2u]; - local_21 = clamp(floor((log((_e240 / _e241)) * _e246)), 0f, (_e250 - 1f)); + let _e241 = clip_1[3u]; + let _e242 = near_1; + let _e247 = light_list_info.cluster_depth[1u]; + let _e251 = grid[2u]; + local_21 = clamp(floor((log((_e241 / _e242)) * _e247)), 0f, (_e251 - 1f)); } - let _e253 = local_21; - tz = _e253; - let _e254 = tx; - let _e255 = ty; - let _e257 = grid[0u]; - let _e260 = tz; - let _e262 = grid[0u]; - let _e265 = grid[1u]; - cell = ((_e254 + (_e255 * _e257)) + ((_e260 * _e262) * _e265)); - let _e268 = cell; - row_3 = floor((_e268 / 4f)); - let _e271 = cell; - let _e272 = row_3; - let _e277 = light_list_info.cluster_depth[2u]; - let _e278 = row_3; - param_94 = (_e271 - (_e272 * 4f)); - param_95 = (_e277 + _e278); - let _e280 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); - header = _e280; - let _e282 = header[0u]; - g_cluster_offset = _e282; - let _e284 = header[1u]; - g_cluster_count = _e284; + let _e254 = local_21; + tz = _e254; + let _e255 = tx; + let _e256 = ty; + let _e258 = grid[0u]; + let _e261 = tz; + let _e263 = grid[0u]; + let _e266 = grid[1u]; + cell = ((_e255 + (_e256 * _e258)) + ((_e261 * _e263) * _e266)); + let _e269 = cell; + row_3 = floor((_e269 / 4f)); + let _e272 = cell; + let _e273 = row_3; + let _e278 = light_list_info.cluster_depth[2u]; + let _e279 = row_3; + param_94 = (_e272 - (_e273 * 4f)); + param_95 = (_e278 + _e279); + let _e281 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); + header = _e281; + let _e283 = header[0u]; + g_cluster_offset = _e283; + let _e285 = header[1u]; + g_cluster_count = _e285; return; } @@ -10621,19 +10664,19 @@ fn LightCount_u0028_() -> i32 { var tail: f32; var param_96: vec3; - let _e180 = light_list_info.list[0u]; - tail = _e180; - let _e181 = Clustered_u0028_(); - if _e181 { - let _e182 = v_world_position_1; - param_96 = _e182; + let _e181 = light_list_info.list[0u]; + tail = _e181; + let _e182 = Clustered_u0028_(); + if _e182 { + let _e183 = v_world_position_1; + param_96 = _e183; FindCluster_u0028_vf3_u003b((¶m_96)); - let _e183 = g_cluster_count; - tail = _e183; + let _e184 = g_cluster_count; + tail = _e184; } - let _e186 = frag_info.frame_params[1u]; - let _e190 = tail; - return (clamp(i32((_e186 + 0.5f)), 0i, 8i) + clamp(i32((_e190 + 0.5f)), 0i, 24i)); + let _e187 = frag_info.frame_params[1u]; + let _e191 = tail; + return (clamp(i32((_e187 + 0.5f)), 0i, 8i) + clamp(i32((_e191 + 0.5f)), 0i, 24i)); } fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_4: ptr) -> vec3 { @@ -10656,144 +10699,146 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_107: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e194 = LightCount_u0028_(); - count_1 = _e194; + let _e195 = LightCount_u0028_(); + count_1 = _e195; i_8 = 0i; loop { - let _e195 = i_8; - if (_e195 < 32i) { - let _e197 = i_8; - let _e198 = count_1; - if (_e197 >= _e198) { + let _e196 = i_8; + if (_e196 < 32i) { + let _e198 = i_8; + let _e199 = count_1; + if (_e198 >= _e199) { break; } - let _e200 = i_8; - param_97 = _e200; - let _e201 = (*s_4); - param_98 = _e201; - let _e202 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); - light_4 = _e202; - let _e204 = light_4.n_dot_l; - if (_e204 <= 0f) { + let _e201 = i_8; + param_97 = _e201; + let _e202 = (*s_4); + param_98 = _e202; + let _e203 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); + light_4 = _e203; + let _e205 = light_4.n_dot_l; + if (_e205 <= 0f) { continue; } - let _e206 = i_8; - param_99 = _e206; - let _e207 = LightHasShadow_u0028_i1_u003b((¶m_99)); - if _e207 { - let _e208 = (*s_4); - param_100 = _e208; - let _e209 = light_4; - param_101 = _e209; - let _e210 = i_8; - param_102 = _e210; - let _e211 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); - let _e212 = v_world_position_1; - param_103 = _e212; - let _e214 = (*s_4).n; - param_104 = _e214; - let _e215 = i_8; - param_105 = _e215; - let _e216 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_22 = (_e211 * _e216); + light_transmittance = vec3(1f, 1f, 1f); + let _e207 = i_8; + param_99 = _e207; + let _e208 = LightHasShadow_u0028_i1_u003b((¶m_99)); + if _e208 { + let _e209 = (*s_4); + param_100 = _e209; + let _e210 = light_4; + param_101 = _e210; + let _e211 = i_8; + param_102 = _e211; + let _e212 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); + let _e213 = v_world_position_1; + param_103 = _e213; + let _e215 = (*s_4).n; + param_104 = _e215; + let _e216 = i_8; + param_105 = _e216; + let _e217 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); + local_22 = (_e212 * _e217); } else { local_22 = 1f; } - let _e218 = local_22; - visibility = _e218; - let _e219 = visibility; - if (_e219 <= 0f) { + let _e219 = local_22; + visibility = _e219; + let _e220 = visibility; + if (_e220 <= 0f) { continue; } - let _e221 = (*s_4); - param_106 = _e221; - let _e222 = light_4; - param_107 = _e222; - let _e223 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); - let _e225 = light_4.radiance; - let _e228 = light_4.n_dot_l; - let _e230 = visibility; - let _e232 = total_1; - total_1 = (_e232 + (((_e223 * _e225) * _e228) * _e230)); + let _e222 = (*s_4); + param_106 = _e222; + let _e223 = light_4; + param_107 = _e223; + let _e224 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); + let _e226 = light_4.radiance; + let _e229 = light_4.n_dot_l; + let _e231 = visibility; + let _e233 = light_transmittance; + let _e235 = total_1; + total_1 = (_e235 + ((((_e224 * _e226) * _e229) * _e231) * _e233)); continue; } else { break; } continuing { - let _e234 = i_8; - i_8 = (_e234 + 1i); + let _e237 = i_8; + i_8 = (_e237 + 1i); } } - let _e236 = total_1; - return _e236; + let _e239 = total_1; + return _e239; } fn SampleLightmap_u0028_() -> vec3 { var texel_4: vec4; - let _e177 = v_lightmap_uv_1; - let _e178 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e177); - texel_4 = _e178; - let _e179 = texel_4; - let _e182 = texel_4[3u]; - return ((_e179.xyz * _e182) * 8f); + let _e178 = v_lightmap_uv_1; + let _e179 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e178); + texel_4 = _e179; + let _e180 = texel_4; + let _e183 = texel_4[3u]; + return ((_e180.xyz * _e183) * 8f); } fn MaterialLodBias_u0028_() -> f32 { - let _e178 = frag_info.target_origin[1u]; - return _e178; + let _e179 = frag_info.target_origin[1u]; + return _e179; } fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_5: ptr) { var orm: vec3; - let _e178 = v_texcoord_1; - let _e179 = MaterialLodBias_u0028_(); - let _e180 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e178, _e179); - orm = _e180.xyz; - let _e183 = (*s_5).metallic; - let _e185 = orm[2u]; - (*s_5).metallic = clamp((_e183 * _e185), 0f, 1f); - let _e190 = (*s_5).roughness; - let _e192 = orm[1u]; - (*s_5).roughness = clamp((_e190 * _e192), 0.02f, 1f); + let _e179 = v_texcoord_1; + let _e180 = MaterialLodBias_u0028_(); + let _e181 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e179, _e180); + orm = _e181.xyz; + let _e184 = (*s_5).metallic; + let _e186 = orm[2u]; + (*s_5).metallic = clamp((_e184 * _e186), 0f, 1f); + let _e191 = (*s_5).roughness; + let _e193 = orm[1u]; + (*s_5).roughness = clamp((_e191 * _e193), 0.02f, 1f); return; } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e177 = (*srgb); - let _e180 = (*srgb); - let _e185 = (*srgb); - return mix((_e177 / vec3(12.92f)), pow(((_e180 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e185)); + let _e178 = (*srgb); + let _e181 = (*srgb); + let _e186 = (*srgb); + return mix((_e178 / vec3(12.92f)), pow(((_e181 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e186)); } fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) { var emissive: vec3; var param_108: vec3; - let _e179 = v_texcoord_1; - let _e180 = MaterialLodBias_u0028_(); - let _e181 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e179, _e180); - param_108 = _e181.xyz; - let _e183 = SrgbToLinear_u0028_vf3_u003b((¶m_108)); - emissive = _e183; - let _e184 = emissive; - let _e186 = frag_info.emissive; - let _e191 = frag_info.material2_[3u]; - (*s_6).emissive = ((_e184 * _e186.xyz) * _e191); + let _e180 = v_texcoord_1; + let _e181 = MaterialLodBias_u0028_(); + let _e182 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e180, _e181); + param_108 = _e182.xyz; + let _e184 = SrgbToLinear_u0028_vf3_u003b((¶m_108)); + emissive = _e184; + let _e185 = emissive; + let _e187 = frag_info.emissive; + let _e192 = frag_info.material2_[3u]; + (*s_6).emissive = ((_e185 * _e187.xyz) * _e192); return; } fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) { var occlusion: f32; - let _e178 = v_texcoord_1; - let _e179 = MaterialLodBias_u0028_(); - let _e180 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e178, _e179); - occlusion = _e180.x; - let _e182 = occlusion; - let _e185 = frag_info.material2_[2u]; - (*s_7).occlusion = mix(1f, _e182, clamp(_e185, 0f, 1f)); + let _e179 = v_texcoord_1; + let _e180 = MaterialLodBias_u0028_(); + let _e181 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e179, _e180); + occlusion = _e181.x; + let _e183 = occlusion; + let _e186 = frag_info.material2_[2u]; + (*s_7).occlusion = mix(1f, _e183, clamp(_e186, 0f, 1f)); return; } @@ -10803,64 +10848,64 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var b_3: vec3; var sampled: vec3; - let _e181 = v_texcoord_1; - let _e182 = MaterialLodBias_u0028_(); - let _e183 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e181, _e182); - sampledTexel = _e183; - let _e184 = v_tangent_1; - t_2 = _e184.xyz; - let _e186 = t_2; - let _e188 = (*s_8).n; - let _e190 = (*s_8).n; - let _e191 = t_2; - t_2 = (_e186 - (_e188 * dot(_e190, _e191))); - let _e195 = t_2; + let _e182 = v_texcoord_1; + let _e183 = MaterialLodBias_u0028_(); + let _e184 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e182, _e183); + sampledTexel = _e184; + let _e185 = v_tangent_1; + t_2 = _e185.xyz; + let _e187 = t_2; + let _e189 = (*s_8).n; + let _e191 = (*s_8).n; + let _e192 = t_2; + t_2 = (_e187 - (_e189 * dot(_e191, _e192))); let _e196 = t_2; - if (dot(_e195, _e196) < 0.000000000001f) { + let _e197 = t_2; + if (dot(_e196, _e197) < 0.000000000001f) { return; } - let _e199 = t_2; - t_2 = normalize(_e199); - let _e202 = (*s_8).n; - let _e203 = t_2; - let _e206 = v_tangent_1[3u]; - b_3 = (cross(_e202, _e203) * _e206); - let _e208 = gl_FrontFacing_1; - if !(_e208) { - let _e210 = t_2; - t_2 = -(_e210); + let _e200 = t_2; + t_2 = normalize(_e200); + let _e203 = (*s_8).n; + let _e204 = t_2; + let _e207 = v_tangent_1[3u]; + b_3 = (cross(_e203, _e204) * _e207); + let _e209 = gl_FrontFacing_1; + if !(_e209) { + let _e211 = t_2; + t_2 = -(_e211); } - let _e212 = sampledTexel; - sampled = ((_e212.xyz * 2f) - vec3(1f)); - let _e219 = frag_info.emissive[3u]; - if (_e219 > 0.5f) { - let _e221 = sampled; - let _e223 = sampled; - sampled[2u] = sqrt(max((1f - dot(_e221.xy, _e223.xy)), 0f)); + let _e213 = sampledTexel; + sampled = ((_e213.xyz * 2f) - vec3(1f)); + let _e220 = frag_info.emissive[3u]; + if (_e220 > 0.5f) { + let _e222 = sampled; + let _e224 = sampled; + sampled[2u] = sqrt(max((1f - dot(_e222.xy, _e224.xy)), 0f)); } - let _e232 = frag_info.material2_[1u]; - let _e233 = sampled; - let _e235 = (_e233.xy * _e232); - sampled[0u] = _e235.x; - sampled[1u] = _e235.y; - let _e240 = t_2; - let _e242 = sampled[0u]; - let _e244 = b_3; - let _e246 = sampled[1u]; - let _e250 = (*s_8).n; - let _e252 = sampled[2u]; - (*s_8).n = normalize((((_e240 * _e242) + (_e244 * _e246)) + (_e250 * _e252))); - let _e258 = (*s_8).n; - let _e260 = (*s_8).v; - (*s_8).n_dot_v = max(dot(_e258, _e260), 0.0001f); + let _e233 = frag_info.material2_[1u]; + let _e234 = sampled; + let _e236 = (_e234.xy * _e233); + sampled[0u] = _e236.x; + sampled[1u] = _e236.y; + let _e241 = t_2; + let _e243 = sampled[0u]; + let _e245 = b_3; + let _e247 = sampled[1u]; + let _e251 = (*s_8).n; + let _e253 = sampled[2u]; + (*s_8).n = normalize((((_e241 * _e243) + (_e245 * _e247)) + (_e251 * _e253))); + let _e259 = (*s_8).n; + let _e261 = (*s_8).v; + (*s_8).n_dot_v = max(dot(_e259, _e261), 0.0001f); return; } fn AtlasTexel_u0028_vf2_u003b(texel_5: ptr>) -> vec4 { - let _e177 = (*texel_5); - let _e181 = irradiance_info.atlas; - let _e184 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e177 + vec2(0.5f)) * _e181.xy), 0f); - return _e184; + let _e178 = (*texel_5); + let _e182 = irradiance_info.atlas; + let _e185 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e178 + vec2(0.5f)) * _e182.xy), 0f); + return _e185; } fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr>, interior: ptr, uv_5: ptr>) -> vec4 { @@ -10876,42 +10921,42 @@ fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr; var param_112: vec2; - let _e190 = (*uv_5); - let _e191 = (*interior); - at_1 = ((vec2(1f) + (_e190 * _e191)) - vec2(0.5f)); - let _e197 = at_1; - low = floor(_e197); - let _e199 = at_1; - let _e200 = low; - f = (_e199 - _e200); - let _e202 = (*corner); - let _e203 = low; - param_109 = (_e202 + _e203); - let _e205 = AtlasTexel_u0028_vf2_u003b((¶m_109)); - a_4 = _e205; - let _e206 = (*corner); - let _e207 = low; - param_110 = ((_e206 + _e207) + vec2(1f, 0f)); - let _e210 = AtlasTexel_u0028_vf2_u003b((¶m_110)); - b_4 = _e210; - let _e211 = (*corner); - let _e212 = low; - param_111 = ((_e211 + _e212) + vec2(0f, 1f)); - let _e215 = AtlasTexel_u0028_vf2_u003b((¶m_111)); - c = _e215; - let _e216 = (*corner); - let _e217 = low; - param_112 = ((_e216 + _e217) + vec2(1f, 1f)); - let _e220 = AtlasTexel_u0028_vf2_u003b((¶m_112)); - d_4 = _e220; - let _e221 = a_4; - let _e222 = b_4; - let _e224 = f[0u]; - let _e227 = c; - let _e228 = d_4; - let _e230 = f[0u]; - let _e234 = f[1u]; - return mix(mix(_e221, _e222, vec4(_e224)), mix(_e227, _e228, vec4(_e230)), vec4(_e234)); + let _e191 = (*uv_5); + let _e192 = (*interior); + at_1 = ((vec2(1f) + (_e191 * _e192)) - vec2(0.5f)); + let _e198 = at_1; + low = floor(_e198); + let _e200 = at_1; + let _e201 = low; + f = (_e200 - _e201); + let _e203 = (*corner); + let _e204 = low; + param_109 = (_e203 + _e204); + let _e206 = AtlasTexel_u0028_vf2_u003b((¶m_109)); + a_4 = _e206; + let _e207 = (*corner); + let _e208 = low; + param_110 = ((_e207 + _e208) + vec2(1f, 0f)); + let _e211 = AtlasTexel_u0028_vf2_u003b((¶m_110)); + b_4 = _e211; + let _e212 = (*corner); + let _e213 = low; + param_111 = ((_e212 + _e213) + vec2(0f, 1f)); + let _e216 = AtlasTexel_u0028_vf2_u003b((¶m_111)); + c = _e216; + let _e217 = (*corner); + let _e218 = low; + param_112 = ((_e217 + _e218) + vec2(1f, 1f)); + let _e221 = AtlasTexel_u0028_vf2_u003b((¶m_112)); + d_4 = _e221; + let _e222 = a_4; + let _e223 = b_4; + let _e225 = f[0u]; + let _e228 = c; + let _e229 = d_4; + let _e231 = f[0u]; + let _e235 = f[1u]; + return mix(mix(_e222, _e223, vec4(_e225)), mix(_e228, _e229, vec4(_e231)), vec4(_e235)); } fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec2 { @@ -10919,29 +10964,29 @@ fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec var n_5: vec3; var xy: vec2; - let _e181 = (*direction_1)[0u]; - let _e184 = (*direction_1)[1u]; - let _e188 = (*direction_1)[2u]; - sum_1 = ((abs(_e181) + abs(_e184)) + abs(_e188)); - let _e191 = sum_1; - if (_e191 <= 0f) { + let _e182 = (*direction_1)[0u]; + let _e185 = (*direction_1)[1u]; + let _e189 = (*direction_1)[2u]; + sum_1 = ((abs(_e182) + abs(_e185)) + abs(_e189)); + let _e192 = sum_1; + if (_e192 <= 0f) { return vec2(0.5f, 0.5f); } - let _e193 = (*direction_1); - let _e194 = sum_1; - n_5 = (_e193 / vec3(_e194)); - let _e197 = n_5; - xy = _e197.xy; - let _e200 = n_5[2u]; - if (_e200 < 0f) { - let _e203 = n_5[1u]; - let _e207 = n_5[0u]; - let _e212 = n_5[0u]; - let _e216 = n_5[1u]; - xy = vec2(((1f - abs(_e203)) * select(-1f, 1f, (_e207 >= 0f))), ((1f - abs(_e212)) * select(-1f, 1f, (_e216 >= 0f)))); + let _e194 = (*direction_1); + let _e195 = sum_1; + n_5 = (_e194 / vec3(_e195)); + let _e198 = n_5; + xy = _e198.xy; + let _e201 = n_5[2u]; + if (_e201 < 0f) { + let _e204 = n_5[1u]; + let _e208 = n_5[0u]; + let _e213 = n_5[0u]; + let _e217 = n_5[1u]; + xy = vec2(((1f - abs(_e204)) * select(-1f, 1f, (_e208 >= 0f))), ((1f - abs(_e213)) * select(-1f, 1f, (_e217 >= 0f)))); } - let _e221 = xy; - return ((_e221 * 0.5f) + vec2(0.5f)); + let _e222 = xy; + return ((_e222 * 0.5f) + vec2(0.5f)); } fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view: ptr>) -> vec3 { @@ -10989,190 +11034,190 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var local_23: vec3; - let _e223 = irradiance_info.origin; - origin_2 = _e223.xyz; - let _e226 = irradiance_info.spacing; - spacing = _e226.xyz; - let _e229 = irradiance_info.counts; - counts = _e229.xyz; - let _e233 = irradiance_info.tiles[0u]; - irradianceTile = _e233; - let _e236 = irradiance_info.tiles[1u]; - depthTile = _e236; - let _e239 = irradiance_info.tiles[2u]; - columns = _e239; - let _e242 = irradiance_info.tiles[3u]; - momentsTop = _e242; - let _e243 = (*normal_1); - unit = normalize(_e243); - let _e245 = (*world_4); - let _e246 = unit; - let _e249 = irradiance_info.spacing[3u]; - let _e252 = (*view); - let _e255 = irradiance_info.counts[3u]; - biased = ((_e245 + (_e246 * _e249)) + (_e252 * _e255)); - let _e258 = biased; - let _e259 = origin_2; - let _e261 = spacing; - grid_1 = ((_e258 - _e259) / _e261); - let _e263 = grid_1; - let _e265 = counts; - base = clamp(floor(_e263), vec3(0f, 0f, 0f), (_e265 - vec3(2f))); - let _e269 = grid_1; - let _e270 = base; - f_1 = clamp((_e269 - _e270), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e224 = irradiance_info.origin; + origin_2 = _e224.xyz; + let _e227 = irradiance_info.spacing; + spacing = _e227.xyz; + let _e230 = irradiance_info.counts; + counts = _e230.xyz; + let _e234 = irradiance_info.tiles[0u]; + irradianceTile = _e234; + let _e237 = irradiance_info.tiles[1u]; + depthTile = _e237; + let _e240 = irradiance_info.tiles[2u]; + columns = _e240; + let _e243 = irradiance_info.tiles[3u]; + momentsTop = _e243; + let _e244 = (*normal_1); + unit = normalize(_e244); + let _e246 = (*world_4); + let _e247 = unit; + let _e250 = irradiance_info.spacing[3u]; + let _e253 = (*view); + let _e256 = irradiance_info.counts[3u]; + biased = ((_e246 + (_e247 * _e250)) + (_e253 * _e256)); + let _e259 = biased; + let _e260 = origin_2; + let _e262 = spacing; + grid_1 = ((_e259 - _e260) / _e262); + let _e264 = grid_1; + let _e266 = counts; + base = clamp(floor(_e264), vec3(0f, 0f, 0f), (_e266 - vec3(2f))); + let _e270 = grid_1; + let _e271 = base; + f_1 = clamp((_e270 - _e271), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); total_2 = vec3(0f, 0f, 0f); weights = 0f; corner_1 = 0i; loop { - let _e273 = corner_1; - if (_e273 < 8i) { - let _e275 = corner_1; - let _e278 = corner_1; - let _e283 = corner_1; - offset_3 = vec3(f32((_e275 & 1i)), f32(((_e278 >> bitcast(1i)) & 1i)), f32(((_e283 >> bitcast(2i)) & 1i))); - let _e289 = base; - let _e290 = offset_3; - cell_1 = (_e289 + _e290); - let _e293 = cell_1[2u]; - let _e295 = counts[1u]; - let _e298 = cell_1[1u]; - let _e301 = counts[0u]; - let _e304 = cell_1[0u]; - probe = ((((_e293 * _e295) + _e298) * _e301) + _e304); - let _e306 = probe; - let _e307 = columns; - let _e312 = probe; - let _e313 = columns; - tile_4 = vec2((_e306 - (floor((_e306 / _e307)) * _e307)), floor((_e312 / _e313))); - let _e317 = tile_4; - let _e318 = irradianceTile; - irradianceCorner = (_e317 * (_e318 + 2f)); - let _e322 = tile_4[0u]; - let _e323 = depthTile; - let _e326 = momentsTop; - let _e328 = tile_4[1u]; - let _e329 = depthTile; - momentCorner = vec2((_e322 * (_e323 + 2f)), (_e326 + (_e328 * (_e329 + 2f)))); - let _e334 = irradianceCorner; - param_113 = (_e334 + vec2(1f)); - let _e337 = AtlasTexel_u0028_vf2_u003b((¶m_113)); - if (_e337.w < 0.5f) { + let _e274 = corner_1; + if (_e274 < 8i) { + let _e276 = corner_1; + let _e279 = corner_1; + let _e284 = corner_1; + offset_3 = vec3(f32((_e276 & 1i)), f32(((_e279 >> bitcast(1i)) & 1i)), f32(((_e284 >> bitcast(2i)) & 1i))); + let _e290 = base; + let _e291 = offset_3; + cell_1 = (_e290 + _e291); + let _e294 = cell_1[2u]; + let _e296 = counts[1u]; + let _e299 = cell_1[1u]; + let _e302 = counts[0u]; + let _e305 = cell_1[0u]; + probe = ((((_e294 * _e296) + _e299) * _e302) + _e305); + let _e307 = probe; + let _e308 = columns; + let _e313 = probe; + let _e314 = columns; + tile_4 = vec2((_e307 - (floor((_e307 / _e308)) * _e308)), floor((_e313 / _e314))); + let _e318 = tile_4; + let _e319 = irradianceTile; + irradianceCorner = (_e318 * (_e319 + 2f)); + let _e323 = tile_4[0u]; + let _e324 = depthTile; + let _e327 = momentsTop; + let _e329 = tile_4[1u]; + let _e330 = depthTile; + momentCorner = vec2((_e323 * (_e324 + 2f)), (_e327 + (_e329 * (_e330 + 2f)))); + let _e335 = irradianceCorner; + param_113 = (_e335 + vec2(1f)); + let _e338 = AtlasTexel_u0028_vf2_u003b((¶m_113)); + if (_e338.w < 0.5f) { continue; } - let _e340 = f_1; - let _e342 = f_1; - let _e343 = offset_3; - trilinear = mix((vec3(1f, 1f, 1f) - _e340), _e342, _e343); - let _e346 = trilinear[0u]; - let _e348 = trilinear[1u]; - let _e351 = trilinear[2u]; - weight_2 = max(((_e346 * _e348) * _e351), 0.001f); - let _e354 = origin_2; - let _e355 = spacing; - let _e356 = cell_1; - probePosition = (_e354 + (_e355 * _e356)); - let _e359 = probePosition; - let _e360 = biased; - toProbe = (_e359 - _e360); - let _e362 = toProbe; - distance_4 = length(_e362); - let _e364 = distance_4; - if (_e364 > 0.000001f) { - let _e366 = toProbe; - let _e367 = distance_4; - direction_2 = (_e366 / vec3(_e367)); - let _e370 = unit; - let _e371 = probePosition; - let _e372 = (*world_4); - facing = ((dot(_e370, normalize((_e371 - _e372))) * 0.5f) + 0.5f); - let _e378 = facing; + let _e341 = f_1; + let _e343 = f_1; + let _e344 = offset_3; + trilinear = mix((vec3(1f, 1f, 1f) - _e341), _e343, _e344); + let _e347 = trilinear[0u]; + let _e349 = trilinear[1u]; + let _e352 = trilinear[2u]; + weight_2 = max(((_e347 * _e349) * _e352), 0.001f); + let _e355 = origin_2; + let _e356 = spacing; + let _e357 = cell_1; + probePosition = (_e355 + (_e356 * _e357)); + let _e360 = probePosition; + let _e361 = biased; + toProbe = (_e360 - _e361); + let _e363 = toProbe; + distance_4 = length(_e363); + let _e365 = distance_4; + if (_e365 > 0.000001f) { + let _e367 = toProbe; + let _e368 = distance_4; + direction_2 = (_e367 / vec3(_e368)); + let _e371 = unit; + let _e372 = probePosition; + let _e373 = (*world_4); + facing = ((dot(_e371, normalize((_e372 - _e373))) * 0.5f) + 0.5f); let _e379 = facing; - probeWeight = ((_e378 * _e379) + 0.2f); - let _e382 = direction_2; - param_114 = -(_e382); - let _e384 = ProbeOctahedral_u0028_vf3_u003b((¶m_114)); - let _e385 = momentCorner; - param_115 = _e385; - let _e386 = depthTile; - param_116 = _e386; - param_117 = _e384; - let _e387 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_115), (¶m_116), (¶m_117)); - moments_3 = _e387.xy; + let _e380 = facing; + probeWeight = ((_e379 * _e380) + 0.2f); + let _e383 = direction_2; + param_114 = -(_e383); + let _e385 = ProbeOctahedral_u0028_vf3_u003b((¶m_114)); + let _e386 = momentCorner; + param_115 = _e386; + let _e387 = depthTile; + param_116 = _e387; + param_117 = _e385; + let _e388 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_115), (¶m_116), (¶m_117)); + moments_3 = _e388.xy; chebyshev = 1f; - let _e389 = distance_4; - let _e391 = moments_3[0u]; - if (_e389 > _e391) { - let _e394 = moments_3[1u]; - let _e396 = moments_3[0u]; - let _e398 = moments_3[0u]; - variance_1 = max((_e394 - (_e396 * _e398)), 0.000001f); - let _e402 = distance_4; - let _e404 = moments_3[0u]; - difference = (_e402 - _e404); - let _e406 = variance_1; + let _e390 = distance_4; + let _e392 = moments_3[0u]; + if (_e390 > _e392) { + let _e395 = moments_3[1u]; + let _e397 = moments_3[0u]; + let _e399 = moments_3[0u]; + variance_1 = max((_e395 - (_e397 * _e399)), 0.000001f); + let _e403 = distance_4; + let _e405 = moments_3[0u]; + difference = (_e403 - _e405); let _e407 = variance_1; - let _e408 = difference; + let _e408 = variance_1; let _e409 = difference; - chebyshev = (_e406 / (_e407 + (_e408 * _e409))); - let _e413 = chebyshev; + let _e410 = difference; + chebyshev = (_e407 / (_e408 + (_e409 * _e410))); let _e414 = chebyshev; - let _e416 = chebyshev; - chebyshev = ((_e413 * _e414) * _e416); + let _e415 = chebyshev; + let _e417 = chebyshev; + chebyshev = ((_e414 * _e415) * _e417); } - let _e418 = probeWeight; - let _e419 = chebyshev; - probeWeight = max((_e418 * max(_e419, 0.05f)), 0.000001f); - let _e423 = probeWeight; - if (_e423 < 0.2f) { - let _e425 = probeWeight; + let _e419 = probeWeight; + let _e420 = chebyshev; + probeWeight = max((_e419 * max(_e420, 0.05f)), 0.000001f); + let _e424 = probeWeight; + if (_e424 < 0.2f) { let _e426 = probeWeight; - let _e429 = probeWeight; - probeWeight = (_e429 * ((_e425 * _e426) * 25f)); + let _e427 = probeWeight; + let _e430 = probeWeight; + probeWeight = (_e430 * ((_e426 * _e427) * 25f)); } - let _e431 = probeWeight; - let _e432 = weight_2; - weight_2 = (_e432 * _e431); + let _e432 = probeWeight; + let _e433 = weight_2; + weight_2 = (_e433 * _e432); } - let _e434 = unit; - param_118 = _e434; - let _e435 = ProbeOctahedral_u0028_vf3_u003b((¶m_118)); - let _e436 = irradianceCorner; - param_119 = _e436; - let _e437 = irradianceTile; - param_120 = _e437; - param_121 = _e435; - let _e438 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_119), (¶m_120), (¶m_121)); - let _e440 = weight_2; - let _e442 = total_2; - total_2 = (_e442 + (_e438.xyz * _e440)); - let _e444 = weight_2; - let _e445 = weights; - weights = (_e445 + _e444); + let _e435 = unit; + param_118 = _e435; + let _e436 = ProbeOctahedral_u0028_vf3_u003b((¶m_118)); + let _e437 = irradianceCorner; + param_119 = _e437; + let _e438 = irradianceTile; + param_120 = _e438; + param_121 = _e436; + let _e439 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_119), (¶m_120), (¶m_121)); + let _e441 = weight_2; + let _e443 = total_2; + total_2 = (_e443 + (_e439.xyz * _e441)); + let _e445 = weight_2; + let _e446 = weights; + weights = (_e446 + _e445); continue; } else { break; } continuing { - let _e447 = corner_1; - corner_1 = (_e447 + 1i); + let _e448 = corner_1; + corner_1 = (_e448 + 1i); } } - let _e449 = weights; - if (_e449 > 0f) { - let _e451 = total_2; - let _e452 = weights; - local_23 = (_e451 / vec3(_e452)); + let _e450 = weights; + if (_e450 > 0f) { + let _e452 = total_2; + let _e453 = weights; + local_23 = (_e452 / vec3(_e453)); } else { local_23 = vec3(0f, 0f, 0f); } - let _e455 = local_23; - return _e455; + let _e456 = local_23; + return _e456; } fn IrradianceEnabled_u0028_() -> bool { - let _e178 = irradiance_info.origin[3u]; - return (_e178 > 0.5f); + let _e179 = irradiance_info.origin[3u]; + return (_e179 > 0.5f); } fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) { @@ -11183,33 +11228,33 @@ fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_126: Surface; var param_127: Surface; - let _e183 = IrradianceEnabled_u0028_(); - if _e183 { - let _e184 = v_world_position_1; - param_122 = _e184; - let _e186 = (*s_9).n; - param_123 = _e186; - let _e188 = (*s_9).v; - param_124 = _e188; - let _e189 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_122), (¶m_123), (¶m_124)); - let _e192 = frag_info.material[2u]; - (*s_9).ambient = (_e189 * _e192); + let _e184 = IrradianceEnabled_u0028_(); + if _e184 { + let _e185 = v_world_position_1; + param_122 = _e185; + let _e187 = (*s_9).n; + param_123 = _e187; + let _e189 = (*s_9).v; + param_124 = _e189; + let _e190 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_122), (¶m_123), (¶m_124)); + let _e193 = frag_info.material[2u]; + (*s_9).ambient = (_e190 * _e193); } - let _e195 = (*s_9); - param_125 = _e195; + let _e196 = (*s_9); + param_125 = _e196; ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_125)); - let _e196 = param_125; - (*s_9) = _e196; - let _e197 = (*s_9); - param_126 = _e197; + let _e197 = param_125; + (*s_9) = _e197; + let _e198 = (*s_9); + param_126 = _e198; ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_126)); - let _e198 = param_126; - (*s_9) = _e198; - let _e199 = (*s_9); - param_127 = _e199; + let _e199 = param_126; + (*s_9) = _e199; + let _e200 = (*s_9); + param_127 = _e200; ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_127)); - let _e200 = param_127; - (*s_9) = _e200; + let _e201 = param_127; + (*s_9) = _e201; return; } @@ -11222,78 +11267,78 @@ fn ReadSurface_u0028_() -> Surface { var anchored: vec3; var noise_1: f32; - let _e183 = v_texcoord_1; - let _e184 = MaterialLodBias_u0028_(); - let _e185 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e183, _e184); - texel_6 = _e185; - let _e186 = texel_6; - param_128 = _e186.xyz; - let _e188 = SrgbToLinear_u0028_vf3_u003b((¶m_128)); - let _e190 = frag_info.base_color; - param_129 = _e190.xyz; - let _e192 = SrgbToLinear_u0028_vf3_u003b((¶m_129)); - let _e194 = v_color_1; - s_10.albedo = ((_e188 * _e192) * _e194.xyz); - let _e199 = texel_6[3u]; - let _e202 = frag_info.base_color[3u]; - let _e205 = v_color_1[3u]; - s_10.alpha = ((_e199 * _e202) * _e205); - let _e209 = s_10.albedo; - g_albedo = _e209; - let _e212 = frag_info.material2_[0u]; - cutoff = _e212; - let _e213 = cutoff; - if (_e213 >= 0f) { - let _e216 = s_10.alpha; - let _e217 = cutoff; - if (_e216 < _e217) { + let _e184 = v_texcoord_1; + let _e185 = MaterialLodBias_u0028_(); + let _e186 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e184, _e185); + texel_6 = _e186; + let _e187 = texel_6; + param_128 = _e187.xyz; + let _e189 = SrgbToLinear_u0028_vf3_u003b((¶m_128)); + let _e191 = frag_info.base_color; + param_129 = _e191.xyz; + let _e193 = SrgbToLinear_u0028_vf3_u003b((¶m_129)); + let _e195 = v_color_1; + s_10.albedo = ((_e189 * _e193) * _e195.xyz); + let _e200 = texel_6[3u]; + let _e203 = frag_info.base_color[3u]; + let _e206 = v_color_1[3u]; + s_10.alpha = ((_e200 * _e203) * _e206); + let _e210 = s_10.albedo; + g_albedo = _e210; + let _e213 = frag_info.material2_[0u]; + cutoff = _e213; + let _e214 = cutoff; + if (_e214 >= 0f) { + let _e217 = s_10.alpha; + let _e218 = cutoff; + if (_e217 < _e218) { discard; } } else { - let _e219 = cutoff; - if (_e219 < -1.5f) { - let _e221 = v_world_position_1; - anchored = floor((_e221 * 16f)); - let _e224 = anchored; - noise_1 = fract((sin(dot(_e224, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e230 = s_10.alpha; - let _e231 = noise_1; - if (_e230 < _e231) { + let _e220 = cutoff; + if (_e220 < -1.5f) { + let _e222 = v_world_position_1; + anchored = floor((_e222 * 16f)); + let _e225 = anchored; + noise_1 = fract((sin(dot(_e225, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e231 = s_10.alpha; + let _e232 = noise_1; + if (_e231 < _e232) { discard; } } } - let _e233 = cutoff; - let _e235 = cutoff; - g_premultiply = ((_e233 < 0f) && (_e235 > -0.75f)); - let _e238 = v_normal_1; - s_10.n = normalize(_e238); - let _e241 = gl_FrontFacing_1; - if !(_e241) { - let _e244 = s_10.n; - s_10.n = -(_e244); + let _e234 = cutoff; + let _e236 = cutoff; + g_premultiply = ((_e234 < 0f) && (_e236 > -0.75f)); + let _e239 = v_normal_1; + s_10.n = normalize(_e239); + let _e242 = gl_FrontFacing_1; + if !(_e242) { + let _e245 = s_10.n; + s_10.n = -(_e245); } - let _e248 = frag_info.camera_position; - let _e250 = v_world_position_1; - s_10.v = normalize((_e248.xyz - _e250)); - let _e255 = s_10.n; - let _e257 = s_10.v; - s_10.n_dot_v = max(dot(_e255, _e257), 0.0001f); - let _e263 = frag_info.material[0u]; - s_10.metallic = clamp(_e263, 0f, 1f); - let _e268 = frag_info.material[1u]; - s_10.roughness = clamp(_e268, 0.02f, 1f); - let _e272 = frag_info.ambient_ground; - let _e275 = frag_info.ambient_sky; - let _e279 = s_10.n[1u]; - let _e286 = frag_info.material[2u]; - s_10.ambient = (mix(_e272.xyz, _e275.xyz, vec3(((_e279 * 0.5f) + 0.5f))) * _e286); - let _e291 = frag_info.frame_params[0u]; - s_10.exposure = max(_e291, 0f); + let _e249 = frag_info.camera_position; + let _e251 = v_world_position_1; + s_10.v = normalize((_e249.xyz - _e251)); + let _e256 = s_10.n; + let _e258 = s_10.v; + s_10.n_dot_v = max(dot(_e256, _e258), 0.0001f); + let _e264 = frag_info.material[0u]; + s_10.metallic = clamp(_e264, 0f, 1f); + let _e269 = frag_info.material[1u]; + s_10.roughness = clamp(_e269, 0.02f, 1f); + let _e273 = frag_info.ambient_ground; + let _e276 = frag_info.ambient_sky; + let _e280 = s_10.n[1u]; + let _e287 = frag_info.material[2u]; + s_10.ambient = (mix(_e273.xyz, _e276.xyz, vec3(((_e280 * 0.5f) + 0.5f))) * _e287); + let _e292 = frag_info.frame_params[0u]; + s_10.exposure = max(_e292, 0f); s_10.occlusion = 1f; s_10.emissive = vec3(0f, 0f, 0f); - let _e296 = s_10; - return _e296; + let _e297 = s_10; + return _e297; } fn main_1() { @@ -11312,34 +11357,35 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e184 = ReadSurface_u0028_(); - s_11 = _e184; - let _e185 = s_11; - param_130 = _e185; + light_transmittance = vec3(1f, 1f, 1f); + let _e185 = ReadSurface_u0028_(); + s_11 = _e185; + let _e186 = s_11; + param_130 = _e186; ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_130)); - let _e186 = param_130; - s_11 = _e186; - let _e187 = s_11; - param_131 = _e187; + let _e187 = param_130; + s_11 = _e187; + let _e188 = s_11; + param_131 = _e188; ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_131)); - let _e188 = param_131; - s_11 = _e188; - let _e190 = s_11.albedo; - let _e192 = s_11.ambient; - let _e193 = SampleLightmap_u0028_(); - let _e197 = s_11.occlusion; - ambient = ((_e190 * (_e192 + _e193)) * _e197); - let _e199 = s_11; - param_132 = _e199; - let _e200 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_132)); - let _e202 = s_11.occlusion; - let _e204 = ambient; - let _e207 = s_11.emissive; - param_133 = (((_e200 * _e202) + _e204) + _e207); - let _e210 = s_11.alpha; - param_134 = _e210; - let _e212 = s_11.roughness; - param_135 = _e212; + let _e189 = param_131; + s_11 = _e189; + let _e191 = s_11.albedo; + let _e193 = s_11.ambient; + let _e194 = SampleLightmap_u0028_(); + let _e198 = s_11.occlusion; + ambient = ((_e191 * (_e193 + _e194)) * _e198); + let _e200 = s_11; + param_132 = _e200; + let _e201 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_132)); + let _e203 = s_11.occlusion; + let _e205 = ambient; + let _e208 = s_11.emissive; + param_133 = (((_e201 * _e203) + _e205) + _e208); + let _e211 = s_11.alpha; + param_134 = _e211; + let _e213 = s_11.roughness; + param_135 = _e213; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_133), (¶m_134), (¶m_135)); return; } @@ -11766,6 +11812,7 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; +var light_transmittance: vec3; var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; @@ -11841,10 +11888,10 @@ var environment_texture_tex: texture_cube; var environment_texture_smp: sampler; fn ViewDepth_u0028_() -> f32 { - let _e195 = v_world_position_1; - let _e197 = fog_info.eye; - let _e201 = fog_info.forward; - return dot((_e195 - _e197.xyz), _e201.xyz); + let _e196 = v_world_position_1; + let _e198 = fog_info.eye; + let _e202 = fog_info.forward; + return dot((_e196 - _e198.xyz), _e202.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -11853,22 +11900,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e200 = ViewDepth_u0028_(); - z = abs(_e200); - let _e202 = z; - near = (_e202 / 5f); - let _e204 = z; - far = (_e204 / 200f); - let _e206 = far; + let _e201 = ViewDepth_u0028_(); + z = abs(_e201); + let _e203 = z; + near = (_e203 / 5f); + let _e205 = z; + far = (_e205 / 200f); let _e207 = far; - let _e209 = far; - far3_ = ((_e206 * _e207) * _e209); - let _e211 = (*alpha); - let _e212 = near; + let _e208 = far; + let _e210 = far; + far3_ = ((_e207 * _e208) * _e210); + let _e212 = (*alpha); let _e213 = near; - let _e216 = far3_; + let _e214 = near; let _e217 = far3_; - return (_e211 * clamp((10f / ((0.00001f + (_e212 * _e213)) + (_e216 * _e217))), 0.01f, 3000f)); + let _e218 = far3_; + return (_e212 * clamp((10f / ((0.00001f + (_e213 * _e214)) + (_e217 * _e218))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -11880,39 +11927,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e204 = fog_info.forward[3u]; - mode = _e204; - let _e205 = mode; - if (_e205 > 0.5f) { - let _e208 = frag_color[3u]; - alpha_1 = _e208; - let _e209 = alpha_1; - param = _e209; - let _e210 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e210; - let _e211 = frag_color; - let _e213 = weight; - let _e214 = (_e211.xyz * _e213); - let _e215 = alpha_1; - let _e216 = weight; - accumulate = vec4(_e214.x, _e214.y, _e214.z, (_e215 * _e216)); - let _e222 = mode; - let _e224 = mode; - revealage = ((_e222 > 1.5f) && (_e224 < 2.5f)); - let _e227 = revealage; - if _e227 { - let _e228 = alpha_1; - local = vec4(_e228); + let _e205 = fog_info.forward[3u]; + mode = _e205; + let _e206 = mode; + if (_e206 > 0.5f) { + let _e209 = frag_color[3u]; + alpha_1 = _e209; + let _e210 = alpha_1; + param = _e210; + let _e211 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e211; + let _e212 = frag_color; + let _e214 = weight; + let _e215 = (_e212.xyz * _e214); + let _e216 = alpha_1; + let _e217 = weight; + accumulate = vec4(_e215.x, _e215.y, _e215.z, (_e216 * _e217)); + let _e223 = mode; + let _e225 = mode; + revealage = ((_e223 > 1.5f) && (_e225 < 2.5f)); + let _e228 = revealage; + if _e228 { + let _e229 = alpha_1; + local = vec4(_e229); } else { - let _e230 = accumulate; - local = _e230; + let _e231 = accumulate; + local = _e231; } - let _e231 = local; - frag_color = _e231; - let _e232 = mode; - if (_e232 > 2.5f) { - let _e234 = alpha_1; - frag_surface = vec4(_e234); + let _e232 = local; + frag_color = _e232; + let _e233 = mode; + if (_e233 > 2.5f) { + let _e235 = alpha_1; + frag_surface = vec4(_e235); } } return; @@ -11921,29 +11968,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e198 = (*n)[0u]; - let _e201 = (*n)[1u]; - let _e205 = (*n)[2u]; - let _e208 = (*n); - (*n) = (_e208 / vec3(((abs(_e198) + abs(_e201)) + abs(_e205)))); - let _e211 = (*n); - e = _e211.xy; - let _e214 = (*n)[2u]; - if (_e214 < 0f) { - let _e216 = (*n); - let _e222 = (*n)[0u]; - let _e226 = (*n)[1u]; - e = ((vec2(1f) - abs(_e216.yx)) * vec2(select(-1f, 1f, (_e222 >= 0f)), select(-1f, 1f, (_e226 >= 0f)))); + let _e199 = (*n)[0u]; + let _e202 = (*n)[1u]; + let _e206 = (*n)[2u]; + let _e209 = (*n); + (*n) = (_e209 / vec3(((abs(_e199) + abs(_e202)) + abs(_e206)))); + let _e212 = (*n); + e = _e212.xy; + let _e215 = (*n)[2u]; + if (_e215 < 0f) { + let _e217 = (*n); + let _e223 = (*n)[0u]; + let _e227 = (*n)[1u]; + e = ((vec2(1f) - abs(_e217.yx)) * vec2(select(-1f, 1f, (_e223 >= 0f)), select(-1f, 1f, (_e227 >= 0f)))); } - let _e231 = e; - return ((_e231 * 0.5f) + vec2(0.5f)); + let _e232 = e; + return ((_e232 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e196 = (*linear); - let _e198 = (*linear); - let _e202 = (*linear); - return mix((_e196 * 12.92f), ((pow(_e198, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e202)); + let _e197 = (*linear); + let _e199 = (*linear); + let _e203 = (*linear); + return mix((_e197 * 12.92f), ((pow(_e199, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e203)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -11951,57 +11998,57 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e199 = g_albedo; - param_1 = clamp(_e199, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e201 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e201.x, _e201.y, _e201.z, 1f); - let _e206 = g_debug_surface_on; - if _e206 { - let _e207 = g_debug_surface; - let _e208 = ViewDepth_u0028_(); - frag_surface = vec4(_e207.x, _e207.y, _e207.z, _e208); + let _e200 = g_albedo; + param_1 = clamp(_e200, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e202 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e202.x, _e202.y, _e202.z, 1f); + let _e207 = g_debug_surface_on; + if _e207 { + let _e208 = g_debug_surface; + let _e209 = ViewDepth_u0028_(); + frag_surface = vec4(_e208.x, _e208.y, _e208.z, _e209); return; } - let _e213 = v_normal_1; - geometric = normalize(_e213); - let _e215 = gl_FrontFacing_1; - if !(_e215) { - let _e217 = geometric; - geometric = -(_e217); - } - let _e219 = geometric; - param_2 = _e219; - let _e220 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e221 = (*roughness); - let _e223 = ViewDepth_u0028_(); - frag_surface = vec4(_e220.x, _e220.y, clamp(_e221, 0f, 1f), _e223); + let _e214 = v_normal_1; + geometric = normalize(_e214); + let _e216 = gl_FrontFacing_1; + if !(_e216) { + let _e218 = geometric; + geometric = -(_e218); + } + let _e220 = geometric; + param_2 = _e220; + let _e221 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e222 = (*roughness); + let _e224 = ViewDepth_u0028_(); + frag_surface = vec4(_e221.x, _e221.y, clamp(_e222, 0f, 1f), _e224); return; } fn EyeDistance_u0028_() -> f32 { - let _e195 = v_world_position_1; - let _e197 = fog_info.eye; - return distance(_e195, _e197.xyz); + let _e196 = v_world_position_1; + let _e198 = fog_info.eye; + return distance(_e196, _e198.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; - let _e200 = fog_info.fog[3u]; - density = _e200; - let _e201 = density; - if (_e201 <= 0f) { - let _e203 = (*color); - return _e203; + let _e201 = fog_info.fog[3u]; + density = _e201; + let _e202 = density; + if (_e202 <= 0f) { + let _e204 = (*color); + return _e204; } - let _e204 = EyeDistance_u0028_(); - d = _e204; - let _e206 = fog_info.fog; - let _e208 = (*color); - let _e209 = density; - let _e211 = d; - return mix(_e206.xyz, _e208, vec3(clamp(exp((-(_e209) * _e211)), 0f, 1f))); + let _e205 = EyeDistance_u0028_(); + d = _e205; + let _e207 = fog_info.fog; + let _e209 = (*color); + let _e210 = density; + let _e212 = d; + return mix(_e207.xyz, _e209, vec3(clamp(exp((-(_e210) * _e212)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -12009,18 +12056,18 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e201 = g_premultiply; - let _e202 = (*alpha_2); - weight_1 = select(1f, _e202, _e201); - let _e204 = (*linearColor); - param_3 = _e204; - let _e205 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e206 = weight_1; - let _e207 = (_e205 * _e206); - let _e208 = (*alpha_2); - frag_color = vec4(_e207.x, _e207.y, _e207.z, _e208); - let _e213 = (*roughness_1); - param_4 = _e213; + let _e202 = g_premultiply; + let _e203 = (*alpha_2); + weight_1 = select(1f, _e203, _e202); + let _e205 = (*linearColor); + param_3 = _e205; + let _e206 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e207 = weight_1; + let _e208 = (_e206 * _e207); + let _e209 = (*alpha_2); + frag_color = vec4(_e208.x, _e208.y, _e208.z, _e209); + let _e214 = (*roughness_1); + param_4 = _e214; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; @@ -12030,32 +12077,32 @@ fn FonAlbedo_u0028_f1_u003b_f1_u003b(mu: ptr, r: ptr>, average: ptr) -> vec3 { - let _e197 = (*rho); let _e198 = (*rho); - let _e200 = (*average); - let _e202 = (*rho); - let _e203 = (*average); - return (((_e197 * _e198) * _e200) / (vec3(1f, 1f, 1f) - (_e202 * (1f - _e203)))); + let _e199 = (*rho); + let _e201 = (*average); + let _e203 = (*rho); + let _e204 = (*average); + return (((_e198 * _e199) * _e201) / (vec3(1f, 1f, 1f) - (_e203 * (1f - _e204)))); } fn FonAverage_u0028_f1_u003b(r_1: ptr) -> f32 { - let _e196 = (*r_1); - let _e199 = (*r_1); - return ((1f + (0.07248821f * _e196)) / (1f + (0.2877934f * _e199))); + let _e197 = (*r_1); + let _e200 = (*r_1); + return ((1f + (0.07248821f * _e197)) / (1f + (0.2877934f * _e200))); } fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, r_2: ptr, mu_i: ptr, mu_o: ptr, l_dot_v: ptr) -> vec3 { @@ -12074,70 +12121,70 @@ fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr 0f) { - let _e221 = s; - let _e222 = (*mu_i); - let _e223 = (*mu_o); - local_1 = (_e221 / max(_e222, _e223)); + let _e215 = (*l_dot_v); + let _e216 = (*mu_i); + let _e217 = (*mu_o); + s = (_e215 - (_e216 * _e217)); + let _e220 = s; + if (_e220 > 0f) { + let _e222 = s; + let _e223 = (*mu_i); + let _e224 = (*mu_o); + local_1 = (_e222 / max(_e223, _e224)); } else { - let _e226 = s; - local_1 = _e226; - } - let _e227 = local_1; - s_over_t = _e227; - let _e228 = (*r_2); - af = (1f / (1f + (0.2877934f * _e228))); - let _e232 = (*rho_1); - let _e233 = af; - let _e234 = (*r_2); - let _e235 = s_over_t; - single = (_e232 * (_e233 * (1f + (_e234 * _e235)))); - let _e240 = (*r_2); - param_5 = _e240; - let _e241 = FonAverage_u0028_f1_u003b((¶m_5)); - average_1 = _e241; - let _e242 = (*rho_1); - param_6 = _e242; - let _e243 = average_1; - param_7 = _e243; - let _e244 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_6), (¶m_7)); - let _e245 = (*mu_o); - param_8 = _e245; - let _e246 = (*r_2); - param_9 = _e246; - let _e247 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_8), (¶m_9)); - let _e250 = (*mu_i); - param_10 = _e250; - let _e251 = (*r_2); - param_11 = _e251; - let _e252 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_10), (¶m_11)); - let _e256 = average_1; - multi = (_e244 * ((max((1f - _e247), 0.0000001f) * max((1f - _e252), 0.0000001f)) / max((1f - _e256), 0.0000001f))); - let _e261 = single; - let _e262 = multi; - return ((_e261 + _e262) / vec3(3.1415927f)); + let _e227 = s; + local_1 = _e227; + } + let _e228 = local_1; + s_over_t = _e228; + let _e229 = (*r_2); + af = (1f / (1f + (0.2877934f * _e229))); + let _e233 = (*rho_1); + let _e234 = af; + let _e235 = (*r_2); + let _e236 = s_over_t; + single = (_e233 * (_e234 * (1f + (_e235 * _e236)))); + let _e241 = (*r_2); + param_5 = _e241; + let _e242 = FonAverage_u0028_f1_u003b((¶m_5)); + average_1 = _e242; + let _e243 = (*rho_1); + param_6 = _e243; + let _e244 = average_1; + param_7 = _e244; + let _e245 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_6), (¶m_7)); + let _e246 = (*mu_o); + param_8 = _e246; + let _e247 = (*r_2); + param_9 = _e247; + let _e248 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_8), (¶m_9)); + let _e251 = (*mu_i); + param_10 = _e251; + let _e252 = (*r_2); + param_11 = _e252; + let _e253 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_10), (¶m_11)); + let _e257 = average_1; + multi = (_e245 * ((max((1f - _e248), 0.0000001f) * max((1f - _e253), 0.0000001f)) / max((1f - _e257), 0.0000001f))); + let _e262 = single; + let _e263 = multi; + return ((_e262 + _e263) / vec3(3.1415927f)); } fn EonDiffuse_u0028_() -> bool { - let _e197 = frag_info.ambient_sky[3u]; - return (_e197 > 0.5f); + let _e198 = frag_info.ambient_sky[3u]; + return (_e198 > 0.5f); } fn LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b(x: ptr, y: ptr, table: ptr) -> vec2 { var inTable: vec2; - let _e199 = (*x); - let _e200 = (*y); - inTable = ((vec2(_e199, _e200) * 0.984375f) + vec2(0.0078125f)); - let _e206 = inTable[0u]; - let _e208 = inTable[1u]; - let _e209 = (*table); - return vec2(_e206, ((_e208 + _e209) * 0.5f)); + let _e200 = (*x); + let _e201 = (*y); + inTable = ((vec2(_e200, _e201) * 0.984375f) + vec2(0.0078125f)); + let _e207 = inTable[0u]; + let _e209 = inTable[1u]; + let _e210 = (*table); + return vec2(_e207, ((_e209 + _e210) * 0.5f)); } fn EnvBrdf_u0028_f1_u003b_f1_u003b(roughness_2: ptr, n_dot_v: ptr) -> vec2 { @@ -12146,18 +12193,18 @@ fn EnvBrdf_u0028_f1_u003b_f1_u003b(roughness_2: ptr, n_dot_v: ptr var param_13: f32; var param_14: f32; - let _e201 = (*roughness_2); - let _e203 = (*n_dot_v); - param_12 = clamp(_e201, 0f, 1f); - param_13 = sqrt(clamp((1f - _e203), 0f, 1f)); + let _e202 = (*roughness_2); + let _e204 = (*n_dot_v); + param_12 = clamp(_e202, 0f, 1f); + param_13 = sqrt(clamp((1f - _e204), 0f, 1f)); param_14 = 1f; - let _e207 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_12), (¶m_13), (¶m_14)); - let _e208 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e207, 0f); - dfg = _e208.xy; - let _e211 = dfg[0u]; - let _e213 = dfg[1u]; - let _e216 = dfg[1u]; - return vec2((_e211 - _e213), _e216); + let _e208 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_12), (¶m_13), (¶m_14)); + let _e209 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e208, 0f); + dfg = _e209.xy; + let _e212 = dfg[0u]; + let _e214 = dfg[1u]; + let _e217 = dfg[1u]; + return vec2((_e212 - _e214), _e217); } fn MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(f0_: ptr>, s_1: ptr) -> vec3 { @@ -12166,34 +12213,34 @@ fn MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u00 var param_16: f32; var ess: f32; - let _e202 = (*s_1).roughness; - param_15 = max(_e202, 0.045f); - let _e205 = (*s_1).n_dot_v; - param_16 = _e205; - let _e206 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_15), (¶m_16)); - ab = _e206; - let _e208 = ab[0u]; - let _e210 = ab[1u]; - ess = max((_e208 + _e210), 0.0001f); - let _e213 = (*f0_); - let _e214 = ess; - return (vec3(1f, 1f, 1f) + (_e213 * ((1f / _e214) - 1f))); + let _e203 = (*s_1).roughness; + param_15 = max(_e203, 0.045f); + let _e206 = (*s_1).n_dot_v; + param_16 = _e206; + let _e207 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_15), (¶m_16)); + ab = _e207; + let _e209 = ab[0u]; + let _e211 = ab[1u]; + ess = max((_e209 + _e211), 0.0001f); + let _e214 = (*f0_); + let _e215 = ess; + return (vec3(1f, 1f, 1f) + (_e214 * ((1f / _e215) - 1f))); } fn EnergyCompensation_u0028_() -> bool { - let _e197 = frag_info.target_origin[2u]; - return (_e197 > 0.5f); + let _e198 = frag_info.target_origin[2u]; + return (_e198 > 0.5f); } fn F_Schlick_u0028_vf3_u003b_f1_u003b(f0_1: ptr>, v_dot_h: ptr) -> vec3 { var f: f32; - let _e198 = (*v_dot_h); - f = pow((1f - _e198), 5f); - let _e201 = (*f0_1); + let _e199 = (*v_dot_h); + f = pow((1f - _e199), 5f); let _e202 = (*f0_1); - let _e204 = f; - return (_e201 + ((vec3(1f, 1f, 1f) - _e202) * _e204)); + let _e203 = (*f0_1); + let _e205 = f; + return (_e202 + ((vec3(1f, 1f, 1f) - _e203) * _e205)); } fn V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b(n_dot_v_1: ptr, n_dot_l: ptr, alpha_3: ptr) -> f32 { @@ -12201,42 +12248,42 @@ fn V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b(n_dot_v_1: ptr, alpha_4: ptr) -> f32 { var a: f32; var k: f32; - let _e199 = (*n_dot_h); - let _e200 = (*alpha_4); - a = (_e199 * _e200); - let _e202 = (*alpha_4); - let _e203 = (*n_dot_h); + let _e200 = (*n_dot_h); + let _e201 = (*alpha_4); + a = (_e200 * _e201); + let _e203 = (*alpha_4); let _e204 = (*n_dot_h); - let _e207 = a; + let _e205 = (*n_dot_h); let _e208 = a; - k = (_e202 / max(((1f - (_e203 * _e204)) + (_e207 * _e208)), 0.0000001f)); - let _e213 = k; + let _e209 = a; + k = (_e203 / max(((1f - (_e204 * _e205)) + (_e208 * _e209)), 0.0000001f)); let _e214 = k; - return ((_e213 * _e214) * 0.31830987f); + let _e215 = k; + return ((_e214 * _e215) * 0.31830987f); } fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_2: ptr, light: ptr) -> vec3 { @@ -12264,116 +12311,116 @@ fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ var param_29: f32; var param_30: f32; - let _e221 = (*s_2).roughness; - lobe = max(_e221, 0.045f); - let _e223 = lobe; + let _e222 = (*s_2).roughness; + lobe = max(_e222, 0.045f); let _e224 = lobe; - alpha_5 = (_e223 * _e224); - let _e227 = (*s_2).albedo; - let _e229 = (*s_2).metallic; - f0_2 = mix(vec3(0.04f, 0.04f, 0.04f), _e227, vec3(_e229)); - let _e233 = (*s_2).albedo; - let _e235 = (*s_2).metallic; - diffuseColor = (_e233 * (1f - _e235)); - let _e239 = (*light).n_dot_h; - param_17 = _e239; - let _e240 = alpha_5; - param_18 = _e240; - let _e241 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_17), (¶m_18)); - d_1 = _e241; - let _e243 = (*s_2).n_dot_v; - param_19 = _e243; - let _e245 = (*light).n_dot_l; - param_20 = _e245; - let _e246 = alpha_5; - param_21 = _e246; - let _e247 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_19), (¶m_20), (¶m_21)); - vis = _e247; - let _e248 = f0_2; - param_22 = _e248; - let _e250 = (*light).v_dot_h; - param_23 = _e250; - let _e251 = F_Schlick_u0028_vf3_u003b_f1_u003b((¶m_22), (¶m_23)); - f_1 = _e251; - let _e252 = d_1; - let _e253 = vis; - let _e255 = f_1; - let _e259 = frag_info.material[3u]; - specular = ((_e255 * (_e252 * _e253)) * _e259); - let _e262 = (*light).integrated; - if (_e262 > 0.5f) { - let _e266 = (*light).ltc[0u]; - let _e267 = f0_2; - let _e270 = (*light).ltc[1u]; - let _e272 = f0_2; - let _e277 = (*light).ltc[2u]; - let _e283 = frag_info.material[3u]; - let _e286 = (*light).n_dot_l; - specular = (((((_e267 * _e270) + ((vec3(1f) - _e272) * _e277)) * _e266) * _e283) / vec3(max(_e286, 0.000001f))); - } - let _e290 = EnergyCompensation_u0028_(); - if _e290 { - let _e291 = f0_2; - param_24 = _e291; - let _e292 = (*s_2); - param_25 = _e292; - let _e293 = MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_24), (¶m_25)); - let _e294 = specular; - specular = (_e294 * _e293); - } - let _e296 = diffuseColor; - let _e297 = f_1; - diffuse = ((_e296 * (vec3(1f, 1f, 1f) - _e297)) / vec3(3.1415927f)); - let _e302 = EonDiffuse_u0028_(); - if _e302 { - let _e304 = (*s_2).n; - let _e306 = (*light).l; - let _e310 = (*light).l; - let _e312 = (*s_2).v; - let _e314 = diffuseColor; - param_26 = _e314; - let _e316 = (*s_2).roughness; - param_27 = _e316; - param_28 = clamp(dot(_e304, _e306), 0.0001f, 1f); - let _e318 = (*s_2).n_dot_v; - param_29 = _e318; - param_30 = dot(_e310, _e312); - let _e319 = EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e320 = f_1; - diffuse = (_e319 * (vec3(1f, 1f, 1f) - _e320)); - } - let _e323 = diffuse; - let _e324 = specular; - return ((_e323 + _e324) * 3.1415927f); + let _e225 = lobe; + alpha_5 = (_e224 * _e225); + let _e228 = (*s_2).albedo; + let _e230 = (*s_2).metallic; + f0_2 = mix(vec3(0.04f, 0.04f, 0.04f), _e228, vec3(_e230)); + let _e234 = (*s_2).albedo; + let _e236 = (*s_2).metallic; + diffuseColor = (_e234 * (1f - _e236)); + let _e240 = (*light).n_dot_h; + param_17 = _e240; + let _e241 = alpha_5; + param_18 = _e241; + let _e242 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_17), (¶m_18)); + d_1 = _e242; + let _e244 = (*s_2).n_dot_v; + param_19 = _e244; + let _e246 = (*light).n_dot_l; + param_20 = _e246; + let _e247 = alpha_5; + param_21 = _e247; + let _e248 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_19), (¶m_20), (¶m_21)); + vis = _e248; + let _e249 = f0_2; + param_22 = _e249; + let _e251 = (*light).v_dot_h; + param_23 = _e251; + let _e252 = F_Schlick_u0028_vf3_u003b_f1_u003b((¶m_22), (¶m_23)); + f_1 = _e252; + let _e253 = d_1; + let _e254 = vis; + let _e256 = f_1; + let _e260 = frag_info.material[3u]; + specular = ((_e256 * (_e253 * _e254)) * _e260); + let _e263 = (*light).integrated; + if (_e263 > 0.5f) { + let _e267 = (*light).ltc[0u]; + let _e268 = f0_2; + let _e271 = (*light).ltc[1u]; + let _e273 = f0_2; + let _e278 = (*light).ltc[2u]; + let _e284 = frag_info.material[3u]; + let _e287 = (*light).n_dot_l; + specular = (((((_e268 * _e271) + ((vec3(1f) - _e273) * _e278)) * _e267) * _e284) / vec3(max(_e287, 0.000001f))); + } + let _e291 = EnergyCompensation_u0028_(); + if _e291 { + let _e292 = f0_2; + param_24 = _e292; + let _e293 = (*s_2); + param_25 = _e293; + let _e294 = MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_24), (¶m_25)); + let _e295 = specular; + specular = (_e295 * _e294); + } + let _e297 = diffuseColor; + let _e298 = f_1; + diffuse = ((_e297 * (vec3(1f, 1f, 1f) - _e298)) / vec3(3.1415927f)); + let _e303 = EonDiffuse_u0028_(); + if _e303 { + let _e305 = (*s_2).n; + let _e307 = (*light).l; + let _e311 = (*light).l; + let _e313 = (*s_2).v; + let _e315 = diffuseColor; + param_26 = _e315; + let _e317 = (*s_2).roughness; + param_27 = _e317; + param_28 = clamp(dot(_e305, _e307), 0.0001f, 1f); + let _e319 = (*s_2).n_dot_v; + param_29 = _e319; + param_30 = dot(_e311, _e313); + let _e320 = EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); + let _e321 = f_1; + diffuse = (_e320 * (vec3(1f, 1f, 1f) - _e321)); + } + let _e324 = diffuse; + let _e325 = specular; + return ((_e324 + _e325) * 3.1415927f); } fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e196 = (*i); - if (_e196 == 0i) { + let _e197 = (*i); + if (_e197 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e198 = (*i); - if (_e198 == 1i) { + let _e199 = (*i); + if (_e199 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e200 = (*i); - if (_e200 == 2i) { + let _e201 = (*i); + if (_e201 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e202 = (*i); - if (_e202 == 3i) { + let _e203 = (*i); + if (_e203 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e204 = (*i); - if (_e204 == 4i) { + let _e205 = (*i); + if (_e205 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e206 = (*i); - if (_e206 == 5i) { + let _e207 = (*i); + if (_e207 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e208 = (*i); - if (_e208 == 6i) { + let _e209 = (*i); + if (_e209 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -12383,20 +12430,20 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr; var param_31: i32; - let _e201 = (*i_1); - param_31 = _e201; - let _e202 = PointShadowDiskTap_u0028_i1_u003b((¶m_31)); - p = _e202; - let _e204 = p[0u]; - let _e205 = (*ca); - let _e208 = p[1u]; - let _e209 = (*sa); - let _e213 = p[0u]; - let _e214 = (*sa); - let _e217 = p[1u]; - let _e218 = (*ca); - let _e222 = (*radius); - return (vec2(((_e204 * _e205) - (_e208 * _e209)), ((_e213 * _e214) + (_e217 * _e218))) * _e222); + let _e202 = (*i_1); + param_31 = _e202; + let _e203 = PointShadowDiskTap_u0028_i1_u003b((¶m_31)); + p = _e203; + let _e205 = p[0u]; + let _e206 = (*ca); + let _e209 = p[1u]; + let _e210 = (*sa); + let _e214 = p[0u]; + let _e215 = (*sa); + let _e218 = p[1u]; + let _e219 = (*ca); + let _e223 = (*radius); + return (vec2(((_e205 * _e206) - (_e209 * _e210)), ((_e214 * _e215) + (_e218 * _e219))) * _e223); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { @@ -12404,27 +12451,27 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr; var atlas: vec2; - let _e204 = point_shadow.params[0u]; - inset = _e204; - let _e205 = (*uv); - let _e206 = (*offset); - let _e208 = inset; + let _e205 = point_shadow.params[0u]; + inset = _e205; + let _e206 = (*uv); + let _e207 = (*offset); let _e209 = inset; - local_2 = clamp((_e205 + _e206), vec2(_e208), vec2((1f - _e209))); - let _e214 = local_2; - let _e215 = (*tile); - atlas = ((_e214 + _e215) * vec2(0.16666667f, 0.16666667f)); - let _e220 = point_shadow.params3_[0u]; - if (_e220 > 0.5f) { - let _e223 = atlas[1u]; - atlas[1u] = (1f - _e223); - } - let _e226 = atlas; - let _e227 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e226, 0f); - let _e229 = atlas; - let _e230 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e229, 0f); - let _e233 = (*range); - return (min(_e227.x, _e230.x) * _e233); + let _e210 = inset; + local_2 = clamp((_e206 + _e207), vec2(_e209), vec2((1f - _e210))); + let _e215 = local_2; + let _e216 = (*tile); + atlas = ((_e215 + _e216) * vec2(0.16666667f, 0.16666667f)); + let _e221 = point_shadow.params3_[0u]; + if (_e221 > 0.5f) { + let _e224 = atlas[1u]; + atlas[1u] = (1f - _e224); + } + let _e227 = atlas; + let _e228 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e227, 0f); + let _e230 = atlas; + let _e231 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e230, 0f); + let _e234 = (*range); + return (min(_e228.x, _e231.x) * _e234); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { @@ -12434,24 +12481,24 @@ fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: pt var param_34: vec2; var param_35: f32; - let _e205 = (*uv_1); - param_32 = _e205; - let _e206 = (*offset_1); - param_33 = _e206; - let _e207 = (*tile_1); - param_34 = _e207; - let _e208 = (*range_1); - param_35 = _e208; - let _e209 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_32), (¶m_33), (¶m_34), (¶m_35)); - stored = _e209; - let _e210 = stored; - let _e211 = (*range_1); - if (_e210 >= (_e211 * 0.999f)) { + let _e206 = (*uv_1); + param_32 = _e206; + let _e207 = (*offset_1); + param_33 = _e207; + let _e208 = (*tile_1); + param_34 = _e208; + let _e209 = (*range_1); + param_35 = _e209; + let _e210 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_32), (¶m_33), (¶m_34), (¶m_35)); + stored = _e210; + let _e211 = stored; + let _e212 = (*range_1); + if (_e211 >= (_e212 * 0.999f)) { return 1f; } - let _e214 = (*receiver); - let _e215 = stored; - return select(1f, 0f, (_e214 > _e215)); + let _e215 = (*receiver); + let _e216 = stored; + return select(1f, 0f, (_e215 > _e216)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { @@ -12478,111 +12525,111 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u var world: f32; (*blockerOut) = -1f; - let _e226 = point_shadow.params2_[1u]; - lightRadius = _e226; - let _e229 = point_shadow.params2_[0u]; - minRadius = _e229; - let _e232 = point_shadow.params2_[2u]; - maxRadius = _e232; - let _e233 = lightRadius; - if (_e233 <= 0f) { - let _e235 = (*uv_2); - param_36 = _e235; + let _e227 = point_shadow.params2_[1u]; + lightRadius = _e227; + let _e230 = point_shadow.params2_[0u]; + minRadius = _e230; + let _e233 = point_shadow.params2_[2u]; + maxRadius = _e233; + let _e234 = lightRadius; + if (_e234 <= 0f) { + let _e236 = (*uv_2); + param_36 = _e236; param_37 = vec2(0f, 0f); - let _e236 = (*tile_2); - param_38 = _e236; - let _e237 = (*range_2); - param_39 = _e237; - let _e238 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - (*blockerOut) = _e238; - let _e239 = minRadius; - return _e239; + let _e237 = (*tile_2); + param_38 = _e237; + let _e238 = (*range_2); + param_39 = _e238; + let _e239 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); + (*blockerOut) = _e239; + let _e240 = minRadius; + return _e240; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e240 = i_2; - if (_e240 < 8i) { - let _e242 = i_2; - param_40 = _e242; - let _e243 = (*ca_1); - param_41 = _e243; - let _e244 = (*sa_1); - param_42 = _e244; - let _e245 = maxRadius; - param_43 = _e245; - let _e246 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43)); - let _e247 = (*uv_2); - param_44 = _e247; - param_45 = _e246; - let _e248 = (*tile_2); - param_46 = _e248; - let _e249 = (*range_2); - param_47 = _e249; - let _e250 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_44), (¶m_45), (¶m_46), (¶m_47)); - stored_1 = _e250; - let _e251 = stored_1; - let _e252 = (*range_2); - if (_e251 >= (_e252 * 0.999f)) { + let _e241 = i_2; + if (_e241 < 8i) { + let _e243 = i_2; + param_40 = _e243; + let _e244 = (*ca_1); + param_41 = _e244; + let _e245 = (*sa_1); + param_42 = _e245; + let _e246 = maxRadius; + param_43 = _e246; + let _e247 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43)); + let _e248 = (*uv_2); + param_44 = _e248; + param_45 = _e247; + let _e249 = (*tile_2); + param_46 = _e249; + let _e250 = (*range_2); + param_47 = _e250; + let _e251 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_44), (¶m_45), (¶m_46), (¶m_47)); + stored_1 = _e251; + let _e252 = stored_1; + let _e253 = (*range_2); + if (_e252 >= (_e253 * 0.999f)) { continue; } - let _e255 = stored_1; - let _e256 = (*receiver_1); - if (_e255 >= _e256) { + let _e256 = stored_1; + let _e257 = (*receiver_1); + if (_e256 >= _e257) { continue; } - let _e258 = stored_1; - let _e259 = sum; - sum = (_e259 + _e258); - let _e261 = count; - count = (_e261 + 1f); + let _e259 = stored_1; + let _e260 = sum; + sum = (_e260 + _e259); + let _e262 = count; + count = (_e262 + 1f); continue; } else { break; } continuing { - let _e263 = i_2; - i_2 = (_e263 + 1i); + let _e264 = i_2; + i_2 = (_e264 + 1i); } } - let _e265 = count; - if (_e265 < 0.5f) { + let _e266 = count; + if (_e266 < 0.5f) { return -1f; } - let _e267 = sum; - let _e268 = count; - blocker = max((_e267 / _e268), 0.0001f); - let _e271 = blocker; - (*blockerOut) = _e271; - let _e272 = lightRadius; - let _e273 = (*receiver_1); - let _e274 = blocker; - let _e278 = blocker; - world = ((_e272 * max((_e273 - _e274), 0f)) / _e278); - let _e280 = world; - let _e281 = (*receiver_1); - let _e283 = (*tanHalf); - let _e286 = minRadius; - let _e287 = maxRadius; - return clamp((_e280 / ((2f * _e281) * _e283)), _e286, _e287); + let _e268 = sum; + let _e269 = count; + blocker = max((_e268 / _e269), 0.0001f); + let _e272 = blocker; + (*blockerOut) = _e272; + let _e273 = lightRadius; + let _e274 = (*receiver_1); + let _e275 = blocker; + let _e279 = blocker; + world = ((_e273 * max((_e274 - _e275), 0f)) / _e279); + let _e281 = world; + let _e282 = (*receiver_1); + let _e284 = (*tanHalf); + let _e287 = minRadius; + let _e288 = maxRadius; + return clamp((_e281 / ((2f * _e282) * _e284)), _e287, _e288); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { var local_3: vec2; - let _e197 = (*rows); - if (_e197 > 0f) { - let _e200 = gl_FragCoord_1[0u]; - let _e201 = (*rows); - let _e203 = gl_FragCoord_1[1u]; - local_3 = vec2(_e200, (_e201 - _e203)); + let _e198 = (*rows); + if (_e198 > 0f) { + let _e201 = gl_FragCoord_1[0u]; + let _e202 = (*rows); + let _e204 = gl_FragCoord_1[1u]; + local_3 = vec2(_e201, (_e202 - _e204)); } else { - let _e206 = gl_FragCoord_1; - local_3 = _e206.xy; + let _e207 = gl_FragCoord_1; + local_3 = _e207.xy; } - let _e208 = local_3; - return _e208; + let _e209 = local_3; + return _e209; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -12637,260 +12684,260 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_69: f32; var param_70: f32; - var phi_2490_: bool; - var phi_2551_: bool; - - let _e249 = (*lightIndex); - let _e253 = point_shadow.slots[_e249][0u]; - slot = i32((_e253 + 0.5f)); - let _e256 = (*lightIndex); - let _e260 = point_shadow.slots[_e256][0u]; - if (_e260 < 0f) { + var phi_2491_: bool; + var phi_2552_: bool; + + let _e250 = (*lightIndex); + let _e254 = point_shadow.slots[_e250][0u]; + slot = i32((_e254 + 0.5f)); + let _e257 = (*lightIndex); + let _e261 = point_shadow.slots[_e257][0u]; + if (_e261 < 0f) { return 1f; } - let _e264 = point_shadow.params[2u]; - strength = _e264; - let _e265 = strength; - if (_e265 <= 0f) { + let _e265 = point_shadow.params[2u]; + strength = _e265; + let _e266 = strength; + if (_e266 <= 0f) { return 1f; } - let _e267 = slot; - let _e270 = point_shadow.lights[_e267]; - let _e272 = (*world_1); - toLight = (_e270.xyz - _e272); - let _e274 = toLight; - toLightLength = max(length(_e274), 0.000001f); - let _e277 = (*normal); - let _e278 = toLight; - let _e279 = toLightLength; - nDotL = max(dot(_e277, (_e278 / vec3(_e279))), 0.15f); - let _e284 = nDotL; + let _e268 = slot; + let _e271 = point_shadow.lights[_e268]; + let _e273 = (*world_1); + toLight = (_e271.xyz - _e273); + let _e275 = toLight; + toLightLength = max(length(_e275), 0.000001f); + let _e278 = (*normal); + let _e279 = toLight; + let _e280 = toLightLength; + nDotL = max(dot(_e278, (_e279 / vec3(_e280))), 0.15f); let _e285 = nDotL; - let _e290 = nDotL; + let _e286 = nDotL; let _e291 = nDotL; - slope = min((sqrt(max((1f - (_e284 * _e285)), 0f)) / (_e290 * _e291)), 8f); - let _e295 = toLightLength; - let _e297 = (*lightIndex); - let _e301 = point_shadow.slots[_e297][2u]; - let _e306 = point_shadow.params3_[1u]; - texel = (((2f * _e295) * max(_e301, 0.0001f)) * _e306); - let _e308 = (*world_1); - let _e309 = (*normal); - let _e310 = texel; - let _e314 = point_shadow.params[3u]; - let _e316 = slope; - origin = (_e308 + (((_e309 * _e310) * _e314) * (1f + _e316))); - let _e320 = origin; - let _e321 = slot; - let _e324 = point_shadow.lights[_e321]; - toFragment = (_e320 - _e324.xyz); - let _e327 = toFragment; - distance_ = length(_e327); - let _e329 = slot; - let _e333 = point_shadow.lights[_e329][3u]; - range_3 = max(_e333, 0.0001f); - let _e335 = distance_; - let _e336 = range_3; - if (_e335 >= _e336) { + let _e292 = nDotL; + slope = min((sqrt(max((1f - (_e285 * _e286)), 0f)) / (_e291 * _e292)), 8f); + let _e296 = toLightLength; + let _e298 = (*lightIndex); + let _e302 = point_shadow.slots[_e298][2u]; + let _e307 = point_shadow.params3_[1u]; + texel = (((2f * _e296) * max(_e302, 0.0001f)) * _e307); + let _e309 = (*world_1); + let _e310 = (*normal); + let _e311 = texel; + let _e315 = point_shadow.params[3u]; + let _e317 = slope; + origin = (_e309 + (((_e310 * _e311) * _e315) * (1f + _e317))); + let _e321 = origin; + let _e322 = slot; + let _e325 = point_shadow.lights[_e322]; + toFragment = (_e321 - _e325.xyz); + let _e328 = toFragment; + distance_ = length(_e328); + let _e330 = slot; + let _e334 = point_shadow.lights[_e330][3u]; + range_3 = max(_e334, 0.0001f); + let _e336 = distance_; + let _e337 = range_3; + if (_e336 >= _e337) { return 1f; } face = 0i; - let _e338 = (*lightIndex); - let _e342 = point_shadow.slots[_e338][1u]; - if (_e342 < 0.5f) { - let _e344 = toFragment; - a_1 = abs(_e344); - let _e347 = a_1[0u]; - let _e349 = a_1[1u]; - let _e350 = (_e347 >= _e349); - phi_2490_ = _e350; - if _e350 { - let _e352 = a_1[0u]; - let _e354 = a_1[2u]; - phi_2490_ = (_e352 >= _e354); - } - let _e357 = phi_2490_; - if _e357 { - let _e359 = toFragment[0u]; - face = select(1i, 0i, (_e359 > 0f)); + let _e339 = (*lightIndex); + let _e343 = point_shadow.slots[_e339][1u]; + if (_e343 < 0.5f) { + let _e345 = toFragment; + a_1 = abs(_e345); + let _e348 = a_1[0u]; + let _e350 = a_1[1u]; + let _e351 = (_e348 >= _e350); + phi_2491_ = _e351; + if _e351 { + let _e353 = a_1[0u]; + let _e355 = a_1[2u]; + phi_2491_ = (_e353 >= _e355); + } + let _e358 = phi_2491_; + if _e358 { + let _e360 = toFragment[0u]; + face = select(1i, 0i, (_e360 > 0f)); } else { - let _e363 = a_1[1u]; - let _e365 = a_1[2u]; - if (_e363 >= _e365) { - let _e368 = toFragment[1u]; - face = select(3i, 2i, (_e368 > 0f)); + let _e364 = a_1[1u]; + let _e366 = a_1[2u]; + if (_e364 >= _e366) { + let _e369 = toFragment[1u]; + face = select(3i, 2i, (_e369 > 0f)); } else { - let _e372 = toFragment[2u]; - face = select(5i, 4i, (_e372 > 0f)); + let _e373 = toFragment[2u]; + face = select(5i, 4i, (_e373 > 0f)); } } } - let _e375 = slot; - let _e377 = face; - let _e381 = point_shadow.faces[((_e375 * 6i) + _e377)]; - let _e382 = origin; - clip = (_e381 * vec4(_e382.x, _e382.y, _e382.z, 1f)); - let _e389 = clip[3u]; - if (_e389 <= 0f) { + let _e376 = slot; + let _e378 = face; + let _e382 = point_shadow.faces[((_e376 * 6i) + _e378)]; + let _e383 = origin; + clip = (_e382 * vec4(_e383.x, _e383.y, _e383.z, 1f)); + let _e390 = clip[3u]; + if (_e390 <= 0f) { return 1f; } - let _e391 = clip; - let _e394 = clip[3u]; - ndc = (_e391.xy / vec2(_e394)); - let _e398 = ndc[0u]; - let _e400 = (abs(_e398) > 1f); - phi_2551_ = _e400; - if !(_e400) { - let _e403 = ndc[1u]; - phi_2551_ = (abs(_e403) > 1f); - } - let _e407 = phi_2551_; - if _e407 { + let _e392 = clip; + let _e395 = clip[3u]; + ndc = (_e392.xy / vec2(_e395)); + let _e399 = ndc[0u]; + let _e401 = (abs(_e399) > 1f); + phi_2552_ = _e401; + if !(_e401) { + let _e404 = ndc[1u]; + phi_2552_ = (abs(_e404) > 1f); + } + let _e408 = phi_2552_; + if _e408 { return 1f; } - let _e409 = ndc[0u]; - let _e413 = ndc[1u]; - uv_3 = vec2(((_e409 * 0.5f) + 0.5f), (0.5f - (_e413 * 0.5f))); - let _e417 = face; - let _e419 = slot; - tile_3 = vec2(f32(_e417), f32(_e419)); - let _e422 = distance_; - let _e425 = point_shadow.params[1u]; - receiver_2 = (_e422 - _e425); - let _e429 = frag_info.target_origin[0u]; - param_48 = _e429; - let _e430 = FragCoordFromTop_u0028_f1_u003b((¶m_48)); - noise = fract((52.982918f * fract(dot(_e430, vec2(0.06711056f, 0.00583715f))))); - let _e435 = noise; - angle = (_e435 * 6.2831855f); - let _e437 = angle; - ca_2 = cos(_e437); - let _e439 = angle; - sa_2 = sin(_e439); - let _e441 = (*lightIndex); - let _e445 = point_shadow.slots[_e441][2u]; - tanHalf_1 = max(_e445, 0.0001f); + let _e410 = ndc[0u]; + let _e414 = ndc[1u]; + uv_3 = vec2(((_e410 * 0.5f) + 0.5f), (0.5f - (_e414 * 0.5f))); + let _e418 = face; + let _e420 = slot; + tile_3 = vec2(f32(_e418), f32(_e420)); + let _e423 = distance_; + let _e426 = point_shadow.params[1u]; + receiver_2 = (_e423 - _e426); + let _e430 = frag_info.target_origin[0u]; + param_48 = _e430; + let _e431 = FragCoordFromTop_u0028_f1_u003b((¶m_48)); + noise = fract((52.982918f * fract(dot(_e431, vec2(0.06711056f, 0.00583715f))))); + let _e436 = noise; + angle = (_e436 * 6.2831855f); + let _e438 = angle; + ca_2 = cos(_e438); + let _e440 = angle; + sa_2 = sin(_e440); + let _e442 = (*lightIndex); + let _e446 = point_shadow.slots[_e442][2u]; + tanHalf_1 = max(_e446, 0.0001f); blocker_1 = -1f; - let _e447 = uv_3; - param_49 = _e447; - let _e448 = tile_3; - param_50 = _e448; - let _e449 = range_3; - param_51 = _e449; - let _e450 = receiver_2; - param_52 = _e450; - let _e451 = ca_2; - param_53 = _e451; - let _e452 = sa_2; - param_54 = _e452; - let _e453 = tanHalf_1; - param_55 = _e453; - let _e454 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_49), (¶m_50), (¶m_51), (¶m_52), (¶m_53), (¶m_54), (¶m_55), (¶m_56)); - let _e455 = param_56; - blocker_1 = _e455; - radius_1 = _e454; - let _e458 = point_shadow.params2_[3u]; - if (_e458 > 0.5f) { + let _e448 = uv_3; + param_49 = _e448; + let _e449 = tile_3; + param_50 = _e449; + let _e450 = range_3; + param_51 = _e450; + let _e451 = receiver_2; + param_52 = _e451; + let _e452 = ca_2; + param_53 = _e452; + let _e453 = sa_2; + param_54 = _e453; + let _e454 = tanHalf_1; + param_55 = _e454; + let _e455 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_49), (¶m_50), (¶m_51), (¶m_52), (¶m_53), (¶m_54), (¶m_55), (¶m_56)); + let _e456 = param_56; + blocker_1 = _e456; + radius_1 = _e455; + let _e459 = point_shadow.params2_[3u]; + if (_e459 > 0.5f) { g_debug_surface_on = true; - let _e460 = radius_1; - if (_e460 < 0f) { + let _e461 = radius_1; + if (_e461 < 0f) { local_4 = vec3(0f, 0f, 1f); } else { - let _e462 = radius_1; - let _e465 = point_shadow.params2_[2u]; - let _e469 = blocker_1; - let _e470 = range_3; - local_4 = vec3(clamp((_e462 / max(_e465, 0.000001f)), 0f, 1f), clamp((_e469 / _e470), 0f, 1f), 0f); + let _e463 = radius_1; + let _e466 = point_shadow.params2_[2u]; + let _e470 = blocker_1; + let _e471 = range_3; + local_4 = vec3(clamp((_e463 / max(_e466, 0.000001f)), 0f, 1f), clamp((_e470 / _e471), 0f, 1f), 0f); } - let _e474 = local_4; - g_debug_surface = _e474; + let _e475 = local_4; + g_debug_surface = _e475; } - let _e475 = radius_1; - if (_e475 < 0f) { + let _e476 = radius_1; + if (_e476 < 0f) { return 1f; } - let _e477 = uv_3; - param_57 = _e477; + let _e478 = uv_3; + param_57 = _e478; param_58 = vec2(0f, 0f); - let _e478 = tile_3; - param_59 = _e478; - let _e479 = range_3; - param_60 = _e479; - let _e480 = receiver_2; - param_61 = _e480; - let _e481 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_57), (¶m_58), (¶m_59), (¶m_60), (¶m_61)); - lit = _e481; - let _e482 = radius_1; - if (_e482 > 0f) { + let _e479 = tile_3; + param_59 = _e479; + let _e480 = range_3; + param_60 = _e480; + let _e481 = receiver_2; + param_61 = _e481; + let _e482 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_57), (¶m_58), (¶m_59), (¶m_60), (¶m_61)); + lit = _e482; + let _e483 = radius_1; + if (_e483 > 0f) { i_3 = 0i; loop { - let _e484 = i_3; - if (_e484 < 8i) { - let _e486 = i_3; - param_62 = _e486; - let _e487 = ca_2; - param_63 = _e487; - let _e488 = sa_2; - param_64 = _e488; - let _e489 = radius_1; - param_65 = _e489; - let _e490 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_62), (¶m_63), (¶m_64), (¶m_65)); - let _e491 = uv_3; - param_66 = _e491; - param_67 = _e490; - let _e492 = tile_3; - param_68 = _e492; - let _e493 = range_3; - param_69 = _e493; - let _e494 = receiver_2; - param_70 = _e494; - let _e495 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_66), (¶m_67), (¶m_68), (¶m_69), (¶m_70)); - let _e496 = lit; - lit = (_e496 + _e495); + let _e485 = i_3; + if (_e485 < 8i) { + let _e487 = i_3; + param_62 = _e487; + let _e488 = ca_2; + param_63 = _e488; + let _e489 = sa_2; + param_64 = _e489; + let _e490 = radius_1; + param_65 = _e490; + let _e491 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_62), (¶m_63), (¶m_64), (¶m_65)); + let _e492 = uv_3; + param_66 = _e492; + param_67 = _e491; + let _e493 = tile_3; + param_68 = _e493; + let _e494 = range_3; + param_69 = _e494; + let _e495 = receiver_2; + param_70 = _e495; + let _e496 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_66), (¶m_67), (¶m_68), (¶m_69), (¶m_70)); + let _e497 = lit; + lit = (_e497 + _e496); continue; } else { break; } continuing { - let _e498 = i_3; - i_3 = (_e498 + 1i); + let _e499 = i_3; + i_3 = (_e499 + 1i); } } - let _e500 = lit; - lit = (_e500 * 0.11111111f); + let _e501 = lit; + lit = (_e501 * 0.11111111f); } - let _e502 = lit; - let _e503 = strength; - return mix(1f, _e502, clamp(_e503, 0f, 1f)); + let _e503 = lit; + let _e504 = strength; + return mix(1f, _e503, clamp(_e504, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r_3: f32; var theta: f32; - let _e200 = (*i_4); - let _e203 = (*n_1); - r_3 = sqrt(((f32(_e200) + 0.5f) / f32(_e203))); - let _e207 = (*i_4); - let _e210 = (*turn); - theta = ((f32(_e207) * 2.3999631f) + _e210); - let _e212 = r_3; - let _e213 = theta; - let _e215 = theta; - return (vec2(cos(_e213), sin(_e215)) * _e212); + let _e201 = (*i_4); + let _e204 = (*n_1); + r_3 = sqrt(((f32(_e201) + 0.5f) / f32(_e204))); + let _e208 = (*i_4); + let _e211 = (*turn); + theta = ((f32(_e208) * 2.3999631f) + _e211); + let _e213 = r_3; + let _e214 = theta; + let _e216 = theta; + return (vec2(cos(_e214), sin(_e216)) * _e213); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; var param_71: f32; - let _e199 = frag_info.target_origin[0u]; - param_71 = _e199; - let _e200 = FragCoordFromTop_u0028_f1_u003b((¶m_71)); - let _e203 = frag_info.target_origin[3u]; - at = (_e200 + vec2((5.588238f * max(_e203, 0f)))); - let _e208 = at; - return fract((52.982918f * fract(dot(_e208, vec2(0.06711056f, 0.00583715f))))); + let _e200 = frag_info.target_origin[0u]; + param_71 = _e200; + let _e201 = FragCoordFromTop_u0028_f1_u003b((¶m_71)); + let _e204 = frag_info.target_origin[3u]; + at = (_e201 + vec2((5.588238f * max(_e204, 0f)))); + let _e209 = at; + return fract((52.982918f * fract(dot(_e209, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -12899,37 +12946,37 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_3: f32; - let _e197 = (*depth); - d_3 = ((2f * clamp(_e197, 0f, 1f)) - 1f); - let _e201 = d_3; - let _e204 = d_3; - return vec2(exp((40f * _e201)), -(exp((-5f * _e204)))); + let _e198 = (*depth); + d_3 = ((2f * clamp(_e198, 0f, 1f)) - 1f); + let _e202 = d_3; + let _e205 = d_3; + return vec2(exp((40f * _e202)), -(exp((-5f * _e205)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { @@ -12947,37 +12994,37 @@ fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr(0.004f, 0.0005f) * _e213); - let _e216 = scale[0u]; - let _e218 = scale[0u]; - let _e220 = (*moments_1); - param_73 = _e220.xy; - let _e223 = warped[0u]; - param_74 = _e223; - param_75 = (_e216 * _e218); - let _e224 = (*bleed_1); - param_76 = _e224; - let _e225 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_73), (¶m_74), (¶m_75), (¶m_76)); - positive = _e225; - let _e227 = scale[1u]; - let _e229 = scale[1u]; - let _e231 = (*moments_1); - param_77 = _e231.zw; - let _e234 = warped[1u]; - param_78 = _e234; - param_79 = (_e227 * _e229); - let _e235 = (*bleed_1); - param_80 = _e235; - let _e236 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_77), (¶m_78), (¶m_79), (¶m_80)); - negative = _e236; - let _e237 = positive; - let _e238 = negative; - return min(_e237, _e238); + let _e212 = (*depth_1); + param_72 = _e212; + let _e213 = EvsmWarp_u0028_f1_u003b((¶m_72)); + warped = _e213; + let _e214 = warped; + scale = (vec2(0.004f, 0.0005f) * _e214); + let _e217 = scale[0u]; + let _e219 = scale[0u]; + let _e221 = (*moments_1); + param_73 = _e221.xy; + let _e224 = warped[0u]; + param_74 = _e224; + param_75 = (_e217 * _e219); + let _e225 = (*bleed_1); + param_76 = _e225; + let _e226 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_73), (¶m_74), (¶m_75), (¶m_76)); + positive = _e226; + let _e228 = scale[1u]; + let _e230 = scale[1u]; + let _e232 = (*moments_1); + param_77 = _e232.zw; + let _e235 = warped[1u]; + param_78 = _e235; + param_79 = (_e228 * _e230); + let _e236 = (*bleed_1); + param_80 = _e236; + let _e237 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_77), (¶m_78), (¶m_79), (¶m_80)); + negative = _e237; + let _e238 = positive; + let _e239 = negative; + return min(_e238, _e239); } fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_3: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { @@ -13014,6 +13061,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; + var stored_2: vec4; + var through: vec3; var softness: f32; var lit_1: f32; var moments_2: vec4; @@ -13044,51 +13093,51 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_89: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3608_: bool; - var phi_3619_: bool; - var phi_3780_: bool; - var phi_3787_: bool; - var phi_3794_: bool; - - let _e263 = frag_info.shadow_params[3u]; - strength_1 = _e263; - let _e264 = strength_1; - if (_e264 <= 0f) { + var phi_3611_: bool; + var phi_3622_: bool; + var phi_3783_: bool; + var phi_3790_: bool; + var phi_3797_: bool; + + let _e266 = frag_info.shadow_params[3u]; + strength_1 = _e266; + let _e267 = strength_1; + if (_e267 <= 0f) { return 1f; } - let _e266 = (*lightIndex_1); - let _e269 = frag_info.frame_params[2u]; - if (_e266 != i32((_e269 + 0.5f))) { + let _e269 = (*lightIndex_1); + let _e272 = frag_info.frame_params[2u]; + if (_e269 != i32((_e272 + 0.5f))) { return 1f; } - let _e275 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e275 + 0.5f)); - let _e278 = v_world_position_1; - let _e280 = frag_info.camera_position; - viewDistance = length((_e278 - _e280.xyz)); + let _e278 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e278 + 0.5f)); + let _e281 = v_world_position_1; + let _e283 = frag_info.camera_position; + viewDistance = length((_e281 - _e283.xyz)); cascade = 0i; - let _e284 = cascadeCount; - let _e285 = (_e284 > 1i); - phi_3608_ = _e285; - if _e285 { - let _e286 = viewDistance; - let _e289 = frag_info.shadow_cascades[0u]; - phi_3608_ = (_e286 > _e289); + let _e287 = cascadeCount; + let _e288 = (_e287 > 1i); + phi_3611_ = _e288; + if _e288 { + let _e289 = viewDistance; + let _e292 = frag_info.shadow_cascades[0u]; + phi_3611_ = (_e289 > _e292); } - let _e292 = phi_3608_; - if _e292 { + let _e295 = phi_3611_; + if _e295 { cascade = 1i; } - let _e293 = cascadeCount; - let _e294 = (_e293 > 2i); - phi_3619_ = _e294; - if _e294 { - let _e295 = viewDistance; - let _e298 = frag_info.shadow_cascades[1u]; - phi_3619_ = (_e295 > _e298); + let _e296 = cascadeCount; + let _e297 = (_e296 > 2i); + phi_3622_ = _e297; + if _e297 { + let _e298 = viewDistance; + let _e301 = frag_info.shadow_cascades[1u]; + phi_3622_ = (_e298 > _e301); } - let _e301 = phi_3619_; - if _e301 { + let _e304 = phi_3622_; + if _e304 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -13099,232 +13148,247 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e302 = attempt; - if (_e302 < 3i) { - let _e304 = cascade; - let _e305 = attempt; - which = (_e304 + _e305); - let _e307 = which; - let _e308 = cascadeCount; - if (_e307 >= _e308) { + let _e305 = attempt; + if (_e305 < 3i) { + let _e307 = cascade; + let _e308 = attempt; + which = (_e307 + _e308); + let _e310 = which; + let _e311 = cascadeCount; + if (_e310 >= _e311) { break; } - let _e310 = which; - if (_e310 == 0i) { - let _e313 = frag_info.shadow_matrix; - local_5 = _e313; + let _e313 = which; + if (_e313 == 0i) { + let _e316 = frag_info.shadow_matrix; + local_5 = _e316; } else { - let _e314 = which; - if (_e314 == 1i) { - let _e317 = frag_info.shadow_matrix_far; - local_6 = _e317; + let _e317 = which; + if (_e317 == 1i) { + let _e320 = frag_info.shadow_matrix_far; + local_6 = _e320; } else { - let _e319 = frag_info.shadow_matrix_farthest; - local_6 = _e319; + let _e322 = frag_info.shadow_matrix_farthest; + local_6 = _e322; } - let _e320 = local_6; - local_5 = _e320; - } - let _e321 = local_5; - matrix = _e321; - let _e324 = matrix[0][0u]; - let _e327 = matrix[1][0u]; - let _e330 = matrix[2][0u]; - axisX = vec3(_e324, _e327, _e330); - let _e334 = matrix[0][1u]; - let _e337 = matrix[1][1u]; - let _e340 = matrix[2][1u]; - axisY = vec3(_e334, _e337, _e340); - let _e342 = axisX; - rowX = max(length(_e342), 0.000001f); - let _e345 = axisY; - rowY = max(length(_e345), 0.000001f); - let _e350 = frag_info.shadow_cascades[3u]; - let _e352 = rowX; - texelMetres = ((2f * _e350) / _e352); - let _e356 = frag_info.shadow_cascades[3u]; - let _e359 = (*s_3).n; - let _e360 = axisX; - let _e363 = rowX; - let _e364 = rowX; - let _e368 = (*s_3).n; - let _e369 = axisY; - let _e372 = rowY; - let _e373 = rowY; - reach = ((3f * _e356) * ((abs(dot(_e359, _e360)) / (_e363 * _e364)) + (abs(dot(_e368, _e369)) / (_e372 * _e373)))); - let _e380 = frag_info.shadow_params[2u]; - let _e381 = texelMetres; - flatOffset = min(_e380, _e381); - let _e383 = v_world_position_1; - let _e385 = (*s_3).n; - let _e386 = flatOffset; - let _e387 = reach; - origin_1 = (_e383 + (_e385 * (_e386 + _e387))); - let _e391 = matrix; - let _e392 = origin_1; - lightSpace = (_e391 * vec4(_e392.x, _e392.y, _e392.z, 1f)); - let _e399 = lightSpace[3u]; - if (_e399 <= 0f) { + let _e323 = local_6; + local_5 = _e323; + } + let _e324 = local_5; + matrix = _e324; + let _e327 = matrix[0][0u]; + let _e330 = matrix[1][0u]; + let _e333 = matrix[2][0u]; + axisX = vec3(_e327, _e330, _e333); + let _e337 = matrix[0][1u]; + let _e340 = matrix[1][1u]; + let _e343 = matrix[2][1u]; + axisY = vec3(_e337, _e340, _e343); + let _e345 = axisX; + rowX = max(length(_e345), 0.000001f); + let _e348 = axisY; + rowY = max(length(_e348), 0.000001f); + let _e353 = frag_info.shadow_cascades[3u]; + let _e355 = rowX; + texelMetres = ((2f * _e353) / _e355); + let _e359 = frag_info.shadow_cascades[3u]; + let _e362 = (*s_3).n; + let _e363 = axisX; + let _e366 = rowX; + let _e367 = rowX; + let _e371 = (*s_3).n; + let _e372 = axisY; + let _e375 = rowY; + let _e376 = rowY; + reach = ((3f * _e359) * ((abs(dot(_e362, _e363)) / (_e366 * _e367)) + (abs(dot(_e371, _e372)) / (_e375 * _e376)))); + let _e383 = frag_info.shadow_params[2u]; + let _e384 = texelMetres; + flatOffset = min(_e383, _e384); + let _e386 = v_world_position_1; + let _e388 = (*s_3).n; + let _e389 = flatOffset; + let _e390 = reach; + origin_1 = (_e386 + (_e388 * (_e389 + _e390))); + let _e394 = matrix; + let _e395 = origin_1; + lightSpace = (_e394 * vec4(_e395.x, _e395.y, _e395.z, 1f)); + let _e402 = lightSpace[3u]; + if (_e402 <= 0f) { continue; } - let _e401 = lightSpace; - let _e404 = lightSpace[3u]; - candidate = (_e401.xyz / vec3(_e404)); - let _e408 = candidate[0u]; - let _e412 = candidate[1u]; - inTile = vec2(((_e408 * 0.5f) + 0.5f), (0.5f - (_e412 * 0.5f))); - let _e417 = inTile[0u]; - let _e418 = (_e417 < 0f); - phi_3780_ = _e418; - if !(_e418) { - let _e421 = inTile[0u]; - phi_3780_ = (_e421 > 1f); - } - let _e424 = phi_3780_; - phi_3787_ = _e424; - if !(_e424) { - let _e427 = inTile[1u]; - phi_3787_ = (_e427 < 0f); - } - let _e430 = phi_3787_; - phi_3794_ = _e430; - if !(_e430) { - let _e433 = inTile[1u]; - phi_3794_ = (_e433 > 1f); - } - let _e436 = phi_3794_; - if _e436 { + let _e404 = lightSpace; + let _e407 = lightSpace[3u]; + candidate = (_e404.xyz / vec3(_e407)); + let _e411 = candidate[0u]; + let _e415 = candidate[1u]; + inTile = vec2(((_e411 * 0.5f) + 0.5f), (0.5f - (_e415 * 0.5f))); + let _e420 = inTile[0u]; + let _e421 = (_e420 < 0f); + phi_3783_ = _e421; + if !(_e421) { + let _e424 = inTile[0u]; + phi_3783_ = (_e424 > 1f); + } + let _e427 = phi_3783_; + phi_3790_ = _e427; + if !(_e427) { + let _e430 = inTile[1u]; + phi_3790_ = (_e430 < 0f); + } + let _e433 = phi_3790_; + phi_3797_ = _e433; + if !(_e433) { + let _e436 = inTile[1u]; + phi_3797_ = (_e436 > 1f); + } + let _e439 = phi_3797_; + if _e439 { continue; } - let _e438 = candidate[2u]; - if (_e438 > 1f) { - let _e440 = which; - let _e441 = cascadeCount; - if (_e440 < (_e441 - 1i)) { + let _e441 = candidate[2u]; + if (_e441 > 1f) { + let _e443 = which; + let _e444 = cascadeCount; + if (_e443 < (_e444 - 1i)) { continue; } candidate[2u] = 1f; } - let _e446 = inTile[0u]; - let _e447 = which; - let _e450 = cascadeCount; - let _e454 = inTile[1u]; - uv_4 = vec2(((_e446 + f32(_e447)) / f32(_e450)), _e454); - let _e456 = candidate; - projected = _e456; - let _e457 = which; - cascade = _e457; - let _e458 = texelMetres; - cascadeTexel = _e458; - let _e461 = matrix[0][2u]; - let _e464 = matrix[1][2u]; - let _e467 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e461, _e464, _e467)), 0.000001f)); - let _e473 = (*s_3).n; - let _e476 = matrix[0][2u]; - let _e479 = matrix[1][2u]; - let _e482 = matrix[2][2u]; - let _e486 = cascadeDepth; - cosSlope = clamp((abs(dot(_e473, vec3(_e476, _e479, _e482))) * _e486), 0.1f, 1f); - let _e489 = texelMetres; - let _e490 = cosSlope; - let _e491 = cosSlope; - let _e497 = cosSlope; - let _e499 = cascadeDepth; - receiverSlope = (((_e489 * sqrt(max((1f - (_e490 * _e491)), 0f))) / _e497) / _e499); + let _e449 = inTile[0u]; + let _e450 = which; + let _e453 = cascadeCount; + let _e457 = inTile[1u]; + uv_4 = vec2(((_e449 + f32(_e450)) / f32(_e453)), _e457); + let _e459 = candidate; + projected = _e459; + let _e460 = which; + cascade = _e460; + let _e461 = texelMetres; + cascadeTexel = _e461; + let _e464 = matrix[0][2u]; + let _e467 = matrix[1][2u]; + let _e470 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e464, _e467, _e470)), 0.000001f)); + let _e476 = (*s_3).n; + let _e479 = matrix[0][2u]; + let _e482 = matrix[1][2u]; + let _e485 = matrix[2][2u]; + let _e489 = cascadeDepth; + cosSlope = clamp((abs(dot(_e476, vec3(_e479, _e482, _e485))) * _e489), 0.1f, 1f); + let _e492 = texelMetres; + let _e493 = cosSlope; + let _e494 = cosSlope; + let _e500 = cosSlope; + let _e502 = cascadeDepth; + receiverSlope = (((_e492 * sqrt(max((1f - (_e493 * _e494)), 0f))) / _e500) / _e502); found = true; break; } else { break; } continuing { - let _e501 = attempt; - attempt = (_e501 + 1i); + let _e504 = attempt; + attempt = (_e504 + 1i); } } - let _e503 = found; - if !(_e503) { + let _e506 = found; + if !(_e506) { return 1f; } - let _e505 = cascade; - if (_e505 == 0i) { - let _e509 = frag_info.shadow_bias[0u]; - local_7 = _e509; + let _e508 = cascade; + if (_e508 == 0i) { + let _e512 = frag_info.shadow_bias[0u]; + local_7 = _e512; } else { - let _e510 = cascade; - if (_e510 == 1i) { - let _e514 = frag_info.shadow_bias[1u]; - local_8 = _e514; - } else { - let _e517 = frag_info.shadow_bias[2u]; + let _e513 = cascade; + if (_e513 == 1i) { + let _e517 = frag_info.shadow_bias[1u]; local_8 = _e517; - } - let _e518 = local_8; - local_7 = _e518; - } - let _e519 = local_7; - bias = _e519; - let _e522 = frag_info.shadow_params[0u]; - let _e525 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e522, _e525); - let _e527 = cascade; - let _e529 = cascadeCount; - let _e533 = texel_1; - tileLo = (vec2((f32(_e527) / f32(_e529)), 0f) + (_e533 * 0.5f)); - let _e536 = cascade; - let _e539 = cascadeCount; - let _e543 = texel_1; - tileHi = (vec2((f32((_e536 + 1i)) / f32(_e539)), 1f) - (_e543 * 0.5f)); - let _e548 = frag_info.ambient_ground[3u]; - softness = _e548; - lit_1 = 0f; - let _e549 = softness; - if (_e549 < 0f) { - let _e551 = uv_4; - let _e552 = tileLo; - let _e553 = tileHi; - let _e555 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e551, _e552, _e553), 0f); - moments_2 = _e555; - let _e557 = projected[2u]; - let _e558 = bias; - let _e560 = softness; - let _e564 = moments_2; - param_81 = _e564; - param_82 = (_e557 - _e558); - param_83 = clamp((-(_e560) - 1f), 0f, 0.95f); - let _e565 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_81), (¶m_82), (¶m_83)); - lit_1 = _e565; + } else { + let _e520 = frag_info.shadow_bias[2u]; + local_8 = _e520; + } + let _e521 = local_8; + local_7 = _e521; + } + let _e522 = local_7; + bias = _e522; + let _e525 = frag_info.shadow_params[0u]; + let _e528 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e525, _e528); + let _e530 = cascade; + let _e532 = cascadeCount; + let _e536 = texel_1; + tileLo = (vec2((f32(_e530) / f32(_e532)), 0f) + (_e536 * 0.5f)); + let _e539 = cascade; + let _e542 = cascadeCount; + let _e546 = texel_1; + tileHi = (vec2((f32((_e539 + 1i)) / f32(_e542)), 1f) - (_e546 * 0.5f)); + let _e551 = frag_info.shadow_bias[3u]; + if (_e551 > 0.5f) { + let _e553 = uv_4; + let _e554 = tileLo; + let _e555 = tileHi; + let _e557 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e553, _e554, _e555), 0f); + stored_2 = _e557; + let _e559 = stored_2[1u]; + let _e562 = stored_2[2u]; + let _e565 = stored_2[3u]; + through = clamp(vec3((1f - _e559), (1f - _e562), _e565), vec3(0f), vec3(1f)); + let _e570 = through; + let _e571 = strength_1; + light_transmittance = mix(vec3(1f, 1f, 1f), _e570, vec3(clamp(_e571, 0f, 1f))); + } + let _e577 = frag_info.ambient_ground[3u]; + softness = _e577; + lit_1 = 0f; + let _e578 = softness; + if (_e578 < 0f) { + let _e580 = uv_4; + let _e581 = tileLo; + let _e582 = tileHi; + let _e584 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e580, _e581, _e582), 0f); + moments_2 = _e584; + let _e586 = projected[2u]; + let _e587 = bias; + let _e589 = softness; + let _e593 = moments_2; + param_81 = _e593; + param_82 = (_e586 - _e587); + param_83 = clamp((-(_e589) - 1f), 0f, 0.95f); + let _e594 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_81), (¶m_82), (¶m_83)); + lit_1 = _e594; } else { - let _e566 = softness; - if (_e566 <= 0f) { + let _e595 = softness; + if (_e595 <= 0f) { y_1 = -1i; loop { - let _e568 = y_1; - if (_e568 <= 1i) { + let _e597 = y_1; + if (_e597 <= 1i) { x_1 = -1i; loop { - let _e570 = x_1; - if (_e570 <= 1i) { - let _e572 = uv_4; - let _e573 = x_1; - let _e575 = y_1; - let _e578 = texel_1; - let _e581 = tileLo; - let _e582 = tileHi; - let _e584 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e572 + (vec2(f32(_e573), f32(_e575)) * _e578)), _e581, _e582), 0f); - occluder = _e584.x; - let _e587 = projected[2u]; - let _e588 = bias; - let _e590 = occluder; - let _e593 = lit_1; - lit_1 = (_e593 + select(1f, 0f, ((_e587 - _e588) > _e590))); + let _e599 = x_1; + if (_e599 <= 1i) { + let _e601 = uv_4; + let _e602 = x_1; + let _e604 = y_1; + let _e607 = texel_1; + let _e610 = tileLo; + let _e611 = tileHi; + let _e613 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e601 + (vec2(f32(_e602), f32(_e604)) * _e607)), _e610, _e611), 0f); + occluder = _e613.x; + let _e616 = projected[2u]; + let _e617 = bias; + let _e619 = occluder; + let _e622 = lit_1; + lit_1 = (_e622 + select(1f, 0f, ((_e616 - _e617) > _e619))); continue; } else { break; } continuing { - let _e595 = x_1; - x_1 = (_e595 + 1i); + let _e624 = x_1; + x_1 = (_e624 + 1i); } } continue; @@ -13332,125 +13396,125 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e597 = y_1; - y_1 = (_e597 + 1i); + let _e626 = y_1; + y_1 = (_e626 + 1i); } } - let _e599 = lit_1; - lit_1 = (_e599 * 0.11111111f); + let _e628 = lit_1; + lit_1 = (_e628 * 0.11111111f); } else { - let _e601 = softness; - spread = tan(min(_e601, 0.5f)); - let _e604 = ShadowNoise_u0028_(); - turn_1 = (_e604 * 6.2831855f); - let _e606 = spread; - let _e608 = projected[2u]; - let _e610 = cascadeDepth; - let _e612 = cascadeTexel; - searchRadius = clamp((((_e606 * _e608) * _e610) / _e612), 1f, 16f); + let _e630 = softness; + spread = tan(min(_e630, 0.5f)); + let _e633 = ShadowNoise_u0028_(); + turn_1 = (_e633 * 6.2831855f); + let _e635 = spread; + let _e637 = projected[2u]; + let _e639 = cascadeDepth; + let _e641 = cascadeTexel; + searchRadius = clamp((((_e635 * _e637) * _e639) / _e641), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e615 = i_5; - if (_e615 < 16i) { - let _e617 = i_5; - param_84 = _e617; + let _e644 = i_5; + if (_e644 < 16i) { + let _e646 = i_5; + param_84 = _e646; param_85 = 16i; - let _e618 = turn_1; - param_86 = _e618; - let _e619 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_84), (¶m_85), (¶m_86)); - disc = _e619; - let _e620 = bias; - let _e621 = disc; - let _e623 = searchRadius; - let _e627 = receiverSlope; - tapBias = (_e620 + (max(((length(_e621) * _e623) - 1.5f), 0f) * _e627)); - let _e630 = uv_4; - let _e631 = disc; - let _e632 = texel_1; - let _e634 = searchRadius; - let _e637 = tileLo; - let _e638 = tileHi; - let _e640 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e630 + ((_e631 * _e632) * _e634)), _e637, _e638), 0f); - occluder_1 = _e640.x; - let _e643 = projected[2u]; - let _e644 = tapBias; - let _e646 = occluder_1; - if ((_e643 - _e644) > _e646) { - let _e648 = occluder_1; - let _e649 = blockerSum; - blockerSum = (_e649 + _e648); - let _e651 = blockerCount; - blockerCount = (_e651 + 1f); + let _e647 = turn_1; + param_86 = _e647; + let _e648 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_84), (¶m_85), (¶m_86)); + disc = _e648; + let _e649 = bias; + let _e650 = disc; + let _e652 = searchRadius; + let _e656 = receiverSlope; + tapBias = (_e649 + (max(((length(_e650) * _e652) - 1.5f), 0f) * _e656)); + let _e659 = uv_4; + let _e660 = disc; + let _e661 = texel_1; + let _e663 = searchRadius; + let _e666 = tileLo; + let _e667 = tileHi; + let _e669 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e659 + ((_e660 * _e661) * _e663)), _e666, _e667), 0f); + occluder_1 = _e669.x; + let _e672 = projected[2u]; + let _e673 = tapBias; + let _e675 = occluder_1; + if ((_e672 - _e673) > _e675) { + let _e677 = occluder_1; + let _e678 = blockerSum; + blockerSum = (_e678 + _e677); + let _e680 = blockerCount; + blockerCount = (_e680 + 1f); } continue; } else { break; } continuing { - let _e653 = i_5; - i_5 = (_e653 + 1i); + let _e682 = i_5; + i_5 = (_e682 + 1i); } } - let _e655 = blockerCount; - if (_e655 <= 0f) { + let _e684 = blockerCount; + if (_e684 <= 0f) { return 1f; } - let _e658 = projected[2u]; - let _e659 = blockerSum; - let _e660 = blockerCount; - let _e664 = cascadeDepth; - gap = (max((_e658 - (_e659 / _e660)), 0f) * _e664); - let _e666 = spread; - let _e667 = gap; - let _e669 = cascadeTexel; - radius_2 = clamp(((_e666 * _e667) / _e669), 1f, 16f); + let _e687 = projected[2u]; + let _e688 = blockerSum; + let _e689 = blockerCount; + let _e693 = cascadeDepth; + gap = (max((_e687 - (_e688 / _e689)), 0f) * _e693); + let _e695 = spread; + let _e696 = gap; + let _e698 = cascadeTexel; + radius_2 = clamp(((_e695 * _e696) / _e698), 1f, 16f); i_6 = 0i; loop { - let _e672 = i_6; - if (_e672 < 16i) { - let _e674 = turn_1; - let _e676 = i_6; - param_87 = _e676; + let _e701 = i_6; + if (_e701 < 16i) { + let _e703 = turn_1; + let _e705 = i_6; + param_87 = _e705; param_88 = 16i; - param_89 = (_e674 + 1f); - let _e677 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_87), (¶m_88), (¶m_89)); - disc_1 = _e677; - let _e678 = bias; - let _e679 = disc_1; - let _e681 = radius_2; - let _e685 = receiverSlope; - tapBias_1 = (_e678 + (max(((length(_e679) * _e681) - 1.5f), 0f) * _e685)); - let _e688 = uv_4; - let _e689 = disc_1; - let _e690 = texel_1; - let _e692 = radius_2; - let _e695 = tileLo; - let _e696 = tileHi; - let _e698 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e688 + ((_e689 * _e690) * _e692)), _e695, _e696), 0f); - occluder_2 = _e698.x; - let _e701 = projected[2u]; - let _e702 = tapBias_1; - let _e704 = occluder_2; - let _e707 = lit_1; - lit_1 = (_e707 + select(1f, 0f, ((_e701 - _e702) > _e704))); + param_89 = (_e703 + 1f); + let _e706 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_87), (¶m_88), (¶m_89)); + disc_1 = _e706; + let _e707 = bias; + let _e708 = disc_1; + let _e710 = radius_2; + let _e714 = receiverSlope; + tapBias_1 = (_e707 + (max(((length(_e708) * _e710) - 1.5f), 0f) * _e714)); + let _e717 = uv_4; + let _e718 = disc_1; + let _e719 = texel_1; + let _e721 = radius_2; + let _e724 = tileLo; + let _e725 = tileHi; + let _e727 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e717 + ((_e718 * _e719) * _e721)), _e724, _e725), 0f); + occluder_2 = _e727.x; + let _e730 = projected[2u]; + let _e731 = tapBias_1; + let _e733 = occluder_2; + let _e736 = lit_1; + lit_1 = (_e736 + select(1f, 0f, ((_e730 - _e731) > _e733))); continue; } else { break; } continuing { - let _e709 = i_6; - i_6 = (_e709 + 1i); + let _e738 = i_6; + i_6 = (_e738 + 1i); } } - let _e711 = lit_1; - lit_1 = (_e711 * 0.0625f); + let _e740 = lit_1; + lit_1 = (_e740 * 0.0625f); } } - let _e713 = lit_1; - let _e714 = strength_1; - return mix(1f, _e713, clamp(_e714, 0f, 1f)); + let _e742 = lit_1; + let _e743 = strength_1; + return mix(1f, _e742, clamp(_e743, 0f, 1f)); } fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_4: ptr, light_2: ptr, index: ptr) -> f32 { @@ -13458,19 +13522,19 @@ fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_91: LightSample; var param_92: i32; - let _e201 = (*s_4); - param_90 = _e201; - let _e202 = (*light_2); - param_91 = _e202; - let _e203 = (*index); - param_92 = _e203; - let _e204 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_90), (¶m_91), (¶m_92)); - return _e204; + let _e202 = (*s_4); + param_90 = _e202; + let _e203 = (*light_2); + param_91 = _e203; + let _e204 = (*index); + param_92 = _e204; + let _e205 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_90), (¶m_91), (¶m_92)); + return _e205; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e196 = (*index_1); - return (_e196 < 8i); + let _e197 = (*index_1); + return (_e197 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -13478,26 +13542,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e200 = (*distance_1); let _e201 = (*distance_1); - attenuation = (1f / max((_e200 * _e201), 0.0001f)); - let _e205 = (*range_4); - if (_e205 > 0f) { - let _e207 = (*distance_1); - let _e208 = (*range_4); - ratio = (_e207 / _e208); - let _e210 = ratio; + let _e202 = (*distance_1); + attenuation = (1f / max((_e201 * _e202), 0.0001f)); + let _e206 = (*range_4); + if (_e206 > 0f) { + let _e208 = (*distance_1); + let _e209 = (*range_4); + ratio = (_e208 / _e209); let _e211 = ratio; - let _e213 = ratio; - let _e215 = ratio; - window = clamp((1f - (((_e210 * _e211) * _e213) * _e215)), 0f, 1f); - let _e219 = window; + let _e212 = ratio; + let _e214 = ratio; + let _e216 = ratio; + window = clamp((1f - (((_e211 * _e212) * _e214) * _e216)), 0f, 1f); let _e220 = window; - let _e222 = attenuation; - attenuation = (_e222 * (_e219 * _e220)); + let _e221 = window; + let _e223 = attenuation; + attenuation = (_e223 * (_e220 * _e221)); } - let _e224 = attenuation; - return _e224; + let _e225 = attenuation; + return _e225; } fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_2: ptr>, b: ptr>) -> vec3 { @@ -13506,25 +13570,25 @@ fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_2: ptr>, b: ptr; - let _e201 = (*a_2); - let _e202 = (*b); - axis = cross(_e201, _e202); - let _e204 = axis; - len = length(_e204); - let _e206 = (*a_2); - let _e207 = (*b); - angle_1 = acos(clamp(dot(_e206, _e207), -1f, 1f)); - let _e211 = len; - if (_e211 > 0.000001f) { - let _e213 = axis; - let _e214 = angle_1; - let _e215 = len; - local_9 = (_e213 * (_e214 / (_e215 * 6.2831855f))); + let _e202 = (*a_2); + let _e203 = (*b); + axis = cross(_e202, _e203); + let _e205 = axis; + len = length(_e205); + let _e207 = (*a_2); + let _e208 = (*b); + angle_1 = acos(clamp(dot(_e207, _e208), -1f, 1f)); + let _e212 = len; + if (_e212 > 0.000001f) { + let _e214 = axis; + let _e215 = angle_1; + let _e216 = len; + local_9 = (_e214 * (_e215 / (_e216 * 6.2831855f))); } else { local_9 = vec3(0f, 0f, 0f); } - let _e219 = local_9; - return _e219; + let _e220 = local_9; + return _e220; } fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: ptr>, v: ptr>, roughness_3: ptr, corners: ptr, 4>>) -> vec3 { @@ -13563,126 +13627,126 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_2: var param_108: f32; var param_109: f32; - let _e233 = (*roughness_3); - let _e235 = (*n_2); - let _e236 = (*v); - uv_5 = vec2(clamp(_e233, 0f, 1f), sqrt(clamp((1f - dot(_e235, _e236)), 0f, 1f))); - let _e243 = uv_5[0u]; - param_93 = _e243; - let _e245 = uv_5[1u]; - param_94 = _e245; + let _e234 = (*roughness_3); + let _e236 = (*n_2); + let _e237 = (*v); + uv_5 = vec2(clamp(_e234, 0f, 1f), sqrt(clamp((1f - dot(_e236, _e237)), 0f, 1f))); + let _e244 = uv_5[0u]; + param_93 = _e244; + let _e246 = uv_5[1u]; + param_94 = _e246; param_95 = 0f; - let _e246 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_93), (¶m_94), (¶m_95)); - let _e247 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e246, 0f); - inverse = _e247; - let _e249 = uv_5[0u]; - param_96 = _e249; - let _e251 = uv_5[1u]; - param_97 = _e251; + let _e247 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_93), (¶m_94), (¶m_95)); + let _e248 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e247, 0f); + inverse = _e248; + let _e250 = uv_5[0u]; + param_96 = _e250; + let _e252 = uv_5[1u]; + param_97 = _e252; param_98 = 1f; - let _e252 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_96), (¶m_97), (¶m_98)); - let _e253 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e252, 0f); - fit = _e253; - let _e254 = (*v); - let _e255 = (*n_2); - let _e256 = (*v); - let _e257 = (*n_2); - along = (_e254 - (_e255 * dot(_e256, _e257))); - let _e261 = along; - alongLength = length(_e261); - let _e263 = alongLength; - if (_e263 > 0.00001f) { - let _e265 = along; - let _e266 = alongLength; - local_10 = (_e265 / vec3(_e266)); + let _e253 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_96), (¶m_97), (¶m_98)); + let _e254 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e253, 0f); + fit = _e254; + let _e255 = (*v); + let _e256 = (*n_2); + let _e257 = (*v); + let _e258 = (*n_2); + along = (_e255 - (_e256 * dot(_e257, _e258))); + let _e262 = along; + alongLength = length(_e262); + let _e264 = alongLength; + if (_e264 > 0.00001f) { + let _e266 = along; + let _e267 = alongLength; + local_10 = (_e266 / vec3(_e267)); } else { - let _e269 = (*n_2); - let _e271 = (*n_2)[2u]; - local_10 = normalize(cross(_e269, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e271) < 0.999f))))); - } - let _e278 = local_10; - t1_ = _e278; - let _e279 = (*n_2); - let _e280 = t1_; - t2_ = cross(_e279, _e280); - let _e283 = inverse[0u]; - let _e285 = inverse[1u]; - let _e286 = vec3(_e283, 0f, _e285); - let _e288 = inverse[2u]; - let _e290 = inverse[3u]; - let _e291 = vec3(_e288, 0f, _e290); - minv = mat3x3(vec3(_e286.x, _e286.y, _e286.z), vec3(vec3(0f, 1f, 0f).x, vec3(0f, 1f, 0f).y, vec3(0f, 1f, 0f).z), vec3(_e291.x, _e291.y, _e291.z)); + let _e270 = (*n_2); + let _e272 = (*n_2)[2u]; + local_10 = normalize(cross(_e270, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e272) < 0.999f))))); + } + let _e279 = local_10; + t1_ = _e279; + let _e280 = (*n_2); + let _e281 = t1_; + t2_ = cross(_e280, _e281); + let _e284 = inverse[0u]; + let _e286 = inverse[1u]; + let _e287 = vec3(_e284, 0f, _e286); + let _e289 = inverse[2u]; + let _e291 = inverse[3u]; + let _e292 = vec3(_e289, 0f, _e291); + minv = mat3x3(vec3(_e287.x, _e287.y, _e287.z), vec3(vec3(0f, 1f, 0f).x, vec3(0f, 1f, 0f).y, vec3(0f, 1f, 0f).z), vec3(_e292.x, _e292.y, _e292.z)); i_7 = 0i; loop { - let _e305 = i_7; - if (_e305 < 4i) { - let _e307 = i_7; - let _e309 = (*corners)[_e307]; - p_1 = _e309; - let _e310 = i_7; - let _e311 = minv; - let _e312 = p_1; - let _e313 = t1_; - let _e315 = p_1; - let _e316 = t2_; - let _e318 = p_1; - let _e319 = (*n_2); - l[_e310] = normalize((_e311 * vec3(dot(_e312, _e313), dot(_e315, _e316), dot(_e318, _e319)))); + let _e306 = i_7; + if (_e306 < 4i) { + let _e308 = i_7; + let _e310 = (*corners)[_e308]; + p_1 = _e310; + let _e311 = i_7; + let _e312 = minv; + let _e313 = p_1; + let _e314 = t1_; + let _e316 = p_1; + let _e317 = t2_; + let _e319 = p_1; + let _e320 = (*n_2); + l[_e311] = normalize((_e312 * vec3(dot(_e313, _e314), dot(_e316, _e317), dot(_e319, _e320)))); continue; } else { break; } continuing { - let _e325 = i_7; - i_7 = (_e325 + 1i); - } - } - let _e328 = l[0i]; - param_99 = _e328; - let _e330 = l[1i]; - param_100 = _e330; - let _e331 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_99), (¶m_100)); - let _e333 = l[1i]; - param_101 = _e333; - let _e335 = l[2i]; - param_102 = _e335; - let _e336 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_101), (¶m_102)); - let _e339 = l[2i]; - param_103 = _e339; - let _e341 = l[3i]; - param_104 = _e341; - let _e342 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_103), (¶m_104)); - let _e345 = l[3i]; - param_105 = _e345; - let _e347 = l[0i]; - param_106 = _e347; - let _e348 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_105), (¶m_106)); - f_2 = -((((_e331 + _e336) + _e342) + _e348)); - let _e351 = f_2; - len_1 = length(_e351); - let _e353 = len_1; - if (_e353 > 0.000000001f) { - let _e356 = f_2[2u]; - let _e357 = len_1; - local_11 = (_e356 / _e357); + let _e326 = i_7; + i_7 = (_e326 + 1i); + } + } + let _e329 = l[0i]; + param_99 = _e329; + let _e331 = l[1i]; + param_100 = _e331; + let _e332 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_99), (¶m_100)); + let _e334 = l[1i]; + param_101 = _e334; + let _e336 = l[2i]; + param_102 = _e336; + let _e337 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_101), (¶m_102)); + let _e340 = l[2i]; + param_103 = _e340; + let _e342 = l[3i]; + param_104 = _e342; + let _e343 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_103), (¶m_104)); + let _e346 = l[3i]; + param_105 = _e346; + let _e348 = l[0i]; + param_106 = _e348; + let _e349 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_105), (¶m_106)); + f_2 = -((((_e332 + _e337) + _e343) + _e349)); + let _e352 = f_2; + len_1 = length(_e352); + let _e354 = len_1; + if (_e354 > 0.000000001f) { + let _e357 = f_2[2u]; + let _e358 = len_1; + local_11 = (_e357 / _e358); } else { local_11 = 0f; } - let _e359 = local_11; - z_1 = _e359; - let _e360 = z_1; - let _e363 = len_1; - param_107 = ((_e360 * 0.5f) + 0.5f); - param_108 = clamp(_e363, 0f, 1f); + let _e360 = local_11; + z_1 = _e360; + let _e361 = z_1; + let _e364 = len_1; + param_107 = ((_e361 * 0.5f) + 0.5f); + param_108 = clamp(_e364, 0f, 1f); param_109 = 1f; - let _e365 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_107), (¶m_108), (¶m_109)); - let _e366 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e365, 0f); - sphere = _e366.w; - let _e368 = len_1; - let _e369 = sphere; - let _e373 = fit[0u]; - let _e375 = fit[1u]; - return vec3(max((_e368 * _e369), 0f), _e373, _e375); + let _e366 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_107), (¶m_108), (¶m_109)); + let _e367 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e366, 0f); + sphere = _e367.w; + let _e369 = len_1; + let _e370 = sphere; + let _e374 = fit[0u]; + let _e376 = fit[1u]; + return vec3(max((_e369 * _e370), 0f), _e374, _e376); } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -13699,75 +13763,75 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var u: f32; var v_1: f32; - let _e212 = (*halfWidth); - let _e213 = (*halfHeight); - n_3 = cross(_e212, _e213); - let _e215 = n_3; - nLen = length(_e215); - let _e217 = nLen; - if (_e217 < 0.000000000001f) { - let _e219 = (*centre); - return _e219; - } - let _e220 = nLen; - let _e221 = n_3; - n_3 = (_e221 / vec3(_e220)); - let _e224 = (*centre); - let _e225 = (*world_2); - toPlane = (_e224 - _e225); - let _e227 = (*mirror); - let _e228 = n_3; - denom = dot(_e227, _e228); - let _e230 = denom; - if (abs(_e230) < 0.00001f) { - let _e233 = toPlane; - let _e234 = n_3; - let _e235 = toPlane; - let _e236 = n_3; - onPlane = (_e233 - (_e234 * dot(_e235, _e236))); + let _e213 = (*halfWidth); + let _e214 = (*halfHeight); + n_3 = cross(_e213, _e214); + let _e216 = n_3; + nLen = length(_e216); + let _e218 = nLen; + if (_e218 < 0.000000000001f) { + let _e220 = (*centre); + return _e220; + } + let _e221 = nLen; + let _e222 = n_3; + n_3 = (_e222 / vec3(_e221)); + let _e225 = (*centre); + let _e226 = (*world_2); + toPlane = (_e225 - _e226); + let _e228 = (*mirror); + let _e229 = n_3; + denom = dot(_e228, _e229); + let _e231 = denom; + if (abs(_e231) < 0.00001f) { + let _e234 = toPlane; + let _e235 = n_3; + let _e236 = toPlane; + let _e237 = n_3; + onPlane = (_e234 - (_e235 * dot(_e236, _e237))); } else { - let _e240 = toPlane; - let _e241 = n_3; - let _e243 = denom; - t_1 = (dot(_e240, _e241) / _e243); - let _e245 = t_1; - if (_e245 > 0f) { - let _e247 = (*mirror); - let _e248 = t_1; - local_12 = (_e247 * _e248); + let _e241 = toPlane; + let _e242 = n_3; + let _e244 = denom; + t_1 = (dot(_e241, _e242) / _e244); + let _e246 = t_1; + if (_e246 > 0f) { + let _e248 = (*mirror); + let _e249 = t_1; + local_12 = (_e248 * _e249); } else { - let _e250 = toPlane; - let _e251 = n_3; - let _e252 = toPlane; - let _e253 = n_3; - local_12 = (_e250 - (_e251 * dot(_e252, _e253))); - } - let _e257 = local_12; - onPlane = _e257; - } - let _e258 = onPlane; - let _e259 = toPlane; - offset_2 = (_e258 - _e259); - let _e261 = (*halfWidth); + let _e251 = toPlane; + let _e252 = n_3; + let _e253 = toPlane; + let _e254 = n_3; + local_12 = (_e251 - (_e252 * dot(_e253, _e254))); + } + let _e258 = local_12; + onPlane = _e258; + } + let _e259 = onPlane; + let _e260 = toPlane; + offset_2 = (_e259 - _e260); let _e262 = (*halfWidth); - wLen2_ = max(dot(_e261, _e262), 0.000000000001f); - let _e265 = (*halfHeight); + let _e263 = (*halfWidth); + wLen2_ = max(dot(_e262, _e263), 0.000000000001f); let _e266 = (*halfHeight); - hLen2_ = max(dot(_e265, _e266), 0.000000000001f); - let _e269 = offset_2; - let _e270 = (*halfWidth); - let _e272 = wLen2_; - u = clamp((dot(_e269, _e270) / _e272), -1f, 1f); - let _e275 = offset_2; - let _e276 = (*halfHeight); - let _e278 = hLen2_; - v_1 = clamp((dot(_e275, _e276) / _e278), -1f, 1f); - let _e281 = (*centre); - let _e282 = (*halfWidth); - let _e283 = u; - let _e286 = (*halfHeight); - let _e287 = v_1; - return ((_e281 + (_e282 * _e283)) + (_e286 * _e287)); + let _e267 = (*halfHeight); + hLen2_ = max(dot(_e266, _e267), 0.000000000001f); + let _e270 = offset_2; + let _e271 = (*halfWidth); + let _e273 = wLen2_; + u = clamp((dot(_e270, _e271) / _e273), -1f, 1f); + let _e276 = offset_2; + let _e277 = (*halfHeight); + let _e279 = hLen2_; + v_1 = clamp((dot(_e276, _e277) / _e279), -1f, 1f); + let _e282 = (*centre); + let _e283 = (*halfWidth); + let _e284 = u; + let _e287 = (*halfHeight); + let _e288 = v_1; + return ((_e282 + (_e283 * _e284)) + (_e287 * _e288)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr>, b_1: ptr>, n_4: ptr>) -> f32 { @@ -13778,32 +13842,32 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> var len_2: f32; var local_13: f32; - let _e204 = (*a_3); let _e205 = (*a_3); - ua = (_e204 / vec3(max(length(_e205), 0.000000000001f))); - let _e210 = (*b_1); + let _e206 = (*a_3); + ua = (_e205 / vec3(max(length(_e206), 0.000000000001f))); let _e211 = (*b_1); - ub = (_e210 / vec3(max(length(_e211), 0.000000000001f))); - let _e216 = ua; - let _e217 = ub; - angle_2 = acos(clamp(dot(_e216, _e217), -1f, 1f)); - let _e221 = ua; - let _e222 = ub; - axis_1 = cross(_e221, _e222); - let _e224 = axis_1; - len_2 = length(_e224); - let _e226 = len_2; - if (_e226 > 0.000001f) { - let _e228 = angle_2; - let _e229 = axis_1; - let _e230 = (*n_4); - let _e233 = len_2; - local_13 = ((_e228 * dot(_e229, _e230)) / _e233); + let _e212 = (*b_1); + ub = (_e211 / vec3(max(length(_e212), 0.000000000001f))); + let _e217 = ua; + let _e218 = ub; + angle_2 = acos(clamp(dot(_e217, _e218), -1f, 1f)); + let _e222 = ua; + let _e223 = ub; + axis_1 = cross(_e222, _e223); + let _e225 = axis_1; + len_2 = length(_e225); + let _e227 = len_2; + if (_e227 > 0.000001f) { + let _e229 = angle_2; + let _e230 = axis_1; + let _e231 = (*n_4); + let _e234 = len_2; + local_13 = ((_e229 * dot(_e230, _e231)) / _e234); } else { local_13 = 0f; } - let _e235 = local_13; - return _e235; + let _e236 = local_13; + return _e236; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr, 4>>, n_5: ptr>) -> f32 { @@ -13834,98 +13898,98 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr(0f, 0f, 0f); i_8 = 0i; loop { - let _e218 = i_8; - if (_e218 < 4i) { - let _e220 = i_8; - let _e222 = (*corners_1)[_e220]; - a_4 = _e222; - let _e223 = i_8; - if (_e223 == 3i) { + let _e219 = i_8; + if (_e219 < 4i) { + let _e221 = i_8; + let _e223 = (*corners_1)[_e221]; + a_4 = _e223; + let _e224 = i_8; + if (_e224 == 3i) { local_14 = 0i; } else { - let _e225 = i_8; - local_14 = (_e225 + 1i); - } - let _e227 = local_14; - let _e229 = (*corners_1)[_e227]; - b_2 = _e229; - let _e230 = a_4; - let _e231 = (*n_5); - ha = dot(_e230, _e231); - let _e233 = b_2; - let _e234 = (*n_5); - hb = dot(_e233, _e234); - let _e236 = ha; - let _e237 = hb; - d_4 = (_e236 - _e237); - let _e239 = a_4; - let _e240 = b_2; - let _e241 = a_4; - let _e243 = d_4; - if (abs(_e243) > 0.000000000001f) { - let _e246 = ha; - let _e247 = d_4; - local_15 = (_e246 / _e247); + let _e226 = i_8; + local_14 = (_e226 + 1i); + } + let _e228 = local_14; + let _e230 = (*corners_1)[_e228]; + b_2 = _e230; + let _e231 = a_4; + let _e232 = (*n_5); + ha = dot(_e231, _e232); + let _e234 = b_2; + let _e235 = (*n_5); + hb = dot(_e234, _e235); + let _e237 = ha; + let _e238 = hb; + d_4 = (_e237 - _e238); + let _e240 = a_4; + let _e241 = b_2; + let _e242 = a_4; + let _e244 = d_4; + if (abs(_e244) > 0.000000000001f) { + let _e247 = ha; + let _e248 = d_4; + local_15 = (_e247 / _e248); } else { local_15 = 0f; } - let _e249 = local_15; - q = (_e239 + ((_e240 - _e241) * _e249)); - let _e252 = ha; - aAbove = (_e252 > 0f); - let _e254 = hb; - bAbove = (_e254 > 0f); - let _e256 = aAbove; - let _e257 = bAbove; - if (_e256 || _e257) { - let _e259 = aAbove; - let _e260 = a_4; - let _e261 = q; - let _e264 = bAbove; - let _e265 = b_2; - let _e266 = q; - param_110 = select(_e261, _e260, vec3(_e259)); - param_111 = select(_e266, _e265, vec3(_e264)); - let _e269 = (*n_5); - param_112 = _e269; - let _e270 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_110), (¶m_111), (¶m_112)); - local_16 = _e270; + let _e250 = local_15; + q = (_e240 + ((_e241 - _e242) * _e250)); + let _e253 = ha; + aAbove = (_e253 > 0f); + let _e255 = hb; + bAbove = (_e255 > 0f); + let _e257 = aAbove; + let _e258 = bAbove; + if (_e257 || _e258) { + let _e260 = aAbove; + let _e261 = a_4; + let _e262 = q; + let _e265 = bAbove; + let _e266 = b_2; + let _e267 = q; + param_110 = select(_e262, _e261, vec3(_e260)); + param_111 = select(_e267, _e266, vec3(_e265)); + let _e270 = (*n_5); + param_112 = _e270; + let _e271 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_110), (¶m_111), (¶m_112)); + local_16 = _e271; } else { local_16 = 0f; } - let _e271 = local_16; - let _e272 = total; - total = (_e272 + _e271); - let _e274 = aAbove; - let _e275 = bAbove; - let _e278 = q; - let _e279 = exit; - exit = select(_e279, _e278, vec3((_e274 && !(_e275)))); - let _e282 = aAbove; - let _e284 = bAbove; - let _e286 = q; - let _e287 = entry; - entry = select(_e287, _e286, vec3((!(_e282) && _e284))); + let _e272 = local_16; + let _e273 = total; + total = (_e273 + _e272); + let _e275 = aAbove; + let _e276 = bAbove; + let _e279 = q; + let _e280 = exit; + exit = select(_e280, _e279, vec3((_e275 && !(_e276)))); + let _e283 = aAbove; + let _e285 = bAbove; + let _e287 = q; + let _e288 = entry; + entry = select(_e288, _e287, vec3((!(_e283) && _e285))); continue; } else { break; } continuing { - let _e290 = i_8; - i_8 = (_e290 + 1i); + let _e291 = i_8; + i_8 = (_e291 + 1i); } } - let _e292 = exit; - param_113 = _e292; - let _e293 = entry; - param_114 = _e293; - let _e294 = (*n_5); - param_115 = _e294; - let _e295 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_113), (¶m_114), (¶m_115)); - let _e296 = total; - total = (_e296 + _e295); - let _e298 = total; - return max((-(_e298) * 0.5f), 0f); + let _e293 = exit; + param_113 = _e293; + let _e294 = entry; + param_114 = _e294; + let _e295 = (*n_5); + param_115 = _e295; + let _e296 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_113), (¶m_114), (¶m_115)); + let _e297 = total; + total = (_e297 + _e296); + let _e299 = total; + return max((-(_e299) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { @@ -13934,91 +13998,91 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 var local_18: f32; var local_19: f32; - let _e201 = (*slot_1); let _e202 = (*slot_1); - lane = (_e201 - ((_e202 / 4i) * 4i)); - let _e206 = lane; - if (_e206 == 0i) { - let _e209 = (*four)[0u]; - local_17 = _e209; + let _e203 = (*slot_1); + lane = (_e202 - ((_e203 / 4i) * 4i)); + let _e207 = lane; + if (_e207 == 0i) { + let _e210 = (*four)[0u]; + local_17 = _e210; } else { - let _e210 = lane; - if (_e210 == 1i) { - let _e213 = (*four)[1u]; - local_18 = _e213; + let _e211 = lane; + if (_e211 == 1i) { + let _e214 = (*four)[1u]; + local_18 = _e214; } else { - let _e214 = lane; - if (_e214 == 2i) { - let _e217 = (*four)[2u]; - local_19 = _e217; + let _e215 = lane; + if (_e215 == 2i) { + let _e218 = (*four)[2u]; + local_19 = _e218; } else { - let _e219 = (*four)[3u]; - local_19 = _e219; + let _e220 = (*four)[3u]; + local_19 = _e220; } - let _e220 = local_19; - local_18 = _e220; + let _e221 = local_19; + local_18 = _e221; } - let _e221 = local_18; - local_17 = _e221; + let _e222 = local_18; + local_17 = _e222; } - let _e222 = local_17; - return _e222; + let _e223 = local_17; + return _e223; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { var param_116: vec4; var param_117: i32; - let _e198 = (*slot_2); - let _e202 = light_list_info.scales[(_e198 / 4i)]; - param_116 = _e202; - let _e203 = (*slot_2); - param_117 = _e203; - let _e204 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_116), (¶m_117)); - return _e204; + let _e199 = (*slot_2); + let _e203 = light_list_info.scales[(_e199 / 4i)]; + param_116 = _e203; + let _e204 = (*slot_2); + param_117 = _e204; + let _e205 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_116), (¶m_117)); + return _e205; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_5: vec4; var b_3: vec4; - let _e200 = light_list_info.slot_rows[0i]; - let _e201 = (*row); - a_5 = abs((_e200 - vec4(_e201))); - let _e207 = light_list_info.slot_rows[1i]; - let _e208 = (*row); - b_3 = abs((_e207 - vec4(_e208))); - let _e213 = a_5[0u]; - let _e215 = a_5[1u]; - let _e218 = a_5[2u]; - let _e220 = a_5[3u]; - let _e224 = b_3[0u]; - let _e226 = b_3[1u]; - let _e229 = b_3[2u]; - let _e231 = b_3[3u]; - return (min(min(min(_e213, _e215), min(_e218, _e220)), min(min(_e224, _e226), min(_e229, _e231))) < 0.5f); + let _e201 = light_list_info.slot_rows[0i]; + let _e202 = (*row); + a_5 = abs((_e201 - vec4(_e202))); + let _e208 = light_list_info.slot_rows[1i]; + let _e209 = (*row); + b_3 = abs((_e208 - vec4(_e209))); + let _e214 = a_5[0u]; + let _e216 = a_5[1u]; + let _e219 = a_5[2u]; + let _e221 = a_5[3u]; + let _e225 = b_3[0u]; + let _e227 = b_3[1u]; + let _e230 = b_3[2u]; + let _e232 = b_3[3u]; + return (min(min(min(_e214, _e216), min(_e219, _e221)), min(min(_e225, _e227), min(_e230, _e232))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { var param_118: vec4; var param_119: i32; - let _e198 = (*slot_3); - let _e202 = light_list_info.indices[(_e198 / 4i)]; - param_118 = _e202; - let _e203 = (*slot_3); - param_119 = _e203; - let _e204 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_118), (¶m_119)); - return _e204; + let _e199 = (*slot_3); + let _e203 = light_list_info.indices[(_e199 / 4i)]; + param_118 = _e203; + let _e204 = (*slot_3); + param_119 = _e204; + let _e205 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_118), (¶m_119)); + return _e205; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e197 = (*texel_2); - let _e201 = light_list_info.list[1u]; - let _e203 = (*row_1); - let _e207 = light_list_info.list[2u]; - let _e210 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e197 + 0.5f) * _e201), ((_e203 + 0.5f) * _e207)), 0f); - return _e210; + let _e198 = (*texel_2); + let _e202 = light_list_info.list[1u]; + let _e204 = (*row_1); + let _e208 = light_list_info.list[2u]; + let _e211 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e198 + 0.5f) * _e202), ((_e204 + 0.5f) * _e208)), 0f); + return _e211; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -14032,35 +14096,35 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var param_122: vec4; var param_123: i32; - let _e205 = g_cluster_offset; - let _e206 = (*slot_4); - entry_1 = (_e205 + f32(_e206)); - let _e209 = entry_1; - row_2 = floor((_e209 / 16f)); - let _e212 = entry_1; - let _e213 = row_2; - within = (_e212 - (_e213 * 16f)); - let _e216 = within; - texel_3 = floor((_e216 / 4f)); - let _e221 = light_list_info.cluster_depth[3u]; - let _e222 = row_2; - let _e224 = texel_3; - param_120 = _e224; - param_121 = (_e221 + _e222); - let _e225 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_120), (¶m_121)); - four_1 = _e225; - let _e226 = within; - let _e227 = texel_3; - let _e232 = four_1; - param_122 = _e232; - param_123 = i32(((_e226 - (_e227 * 4f)) + 0.5f)); - let _e233 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_122), (¶m_123)); - return _e233; + let _e206 = g_cluster_offset; + let _e207 = (*slot_4); + entry_1 = (_e206 + f32(_e207)); + let _e210 = entry_1; + row_2 = floor((_e210 / 16f)); + let _e213 = entry_1; + let _e214 = row_2; + within = (_e213 - (_e214 * 16f)); + let _e217 = within; + texel_3 = floor((_e217 / 4f)); + let _e222 = light_list_info.cluster_depth[3u]; + let _e223 = row_2; + let _e225 = texel_3; + param_120 = _e225; + param_121 = (_e222 + _e223); + let _e226 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_120), (¶m_121)); + four_1 = _e226; + let _e227 = within; + let _e228 = texel_3; + let _e233 = four_1; + param_122 = _e233; + param_123 = i32(((_e227 - (_e228 * 4f)) + 0.5f)); + let _e234 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_122), (¶m_123)); + return _e234; } fn Clustered_u0028_() -> bool { - let _e197 = light_list_info.cluster_grid[3u]; - return (_e197 > 0.5f); + let _e198 = light_list_info.cluster_grid[3u]; + return (_e198 > 0.5f); } fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_5: ptr) -> LightSample { @@ -14120,275 +14184,275 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e252 = (*index_2); - if (_e252 < 8i) { - let _e254 = (*index_2); - let _e257 = frag_info.light_position[_e254]; - position = _e257; - let _e258 = (*index_2); - let _e261 = frag_info.light_color[_e258]; - color_1 = _e261; - let _e262 = (*index_2); - let _e265 = frag_info.light_direction[_e262]; - direction = _e265; - let _e266 = (*index_2); - let _e269 = frag_info.light_cone[_e266]; - cone = _e269; + let _e253 = (*index_2); + if (_e253 < 8i) { + let _e255 = (*index_2); + let _e258 = frag_info.light_position[_e255]; + position = _e258; + let _e259 = (*index_2); + let _e262 = frag_info.light_color[_e259]; + color_1 = _e262; + let _e263 = (*index_2); + let _e266 = frag_info.light_direction[_e263]; + direction = _e266; + let _e267 = (*index_2); + let _e270 = frag_info.light_cone[_e267]; + cone = _e270; } else { - let _e270 = (*index_2); - slot_5 = (_e270 - 8i); - let _e272 = Clustered_u0028_(); - clustered = _e272; - let _e273 = clustered; - if _e273 { - let _e274 = slot_5; - param_124 = _e274; - let _e275 = ClusterRow_u0028_i1_u003b((¶m_124)); - local_20 = _e275; + let _e271 = (*index_2); + slot_5 = (_e271 - 8i); + let _e273 = Clustered_u0028_(); + clustered = _e273; + let _e274 = clustered; + if _e274 { + let _e275 = slot_5; + param_124 = _e275; + let _e276 = ClusterRow_u0028_i1_u003b((¶m_124)); + local_20 = _e276; } else { - let _e276 = slot_5; - param_125 = _e276; - let _e277 = LightListRow_u0028_i1_u003b((¶m_125)); - local_20 = _e277; - } - let _e278 = local_20; - listRow = _e278; - let _e279 = listRow; - let _e283 = light_list_info.list[2u]; - v_2 = ((_e279 + 0.5f) * _e283); - let _e287 = light_list_info.list[1u]; - u_1 = _e287; - let _e288 = u_1; - let _e290 = v_2; - let _e292 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e288), _e290), 0f); - position = _e292; - let _e293 = u_1; - let _e295 = v_2; - let _e297 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e293), _e295), 0f); - color_1 = _e297; - let _e298 = u_1; - let _e300 = v_2; - let _e302 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e298), _e300), 0f); - direction = _e302; - let _e303 = u_1; - let _e305 = v_2; - let _e307 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e303), _e305), 0f); - cone = _e307; - let _e308 = clustered; - if _e308 { - let _e309 = listRow; - param_126 = _e309; - let _e310 = InSlots_u0028_f1_u003b((¶m_126)); - local_21 = select(1f, 0f, _e310); + let _e277 = slot_5; + param_125 = _e277; + let _e278 = LightListRow_u0028_i1_u003b((¶m_125)); + local_20 = _e278; + } + let _e279 = local_20; + listRow = _e279; + let _e280 = listRow; + let _e284 = light_list_info.list[2u]; + v_2 = ((_e280 + 0.5f) * _e284); + let _e288 = light_list_info.list[1u]; + u_1 = _e288; + let _e289 = u_1; + let _e291 = v_2; + let _e293 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e289), _e291), 0f); + position = _e293; + let _e294 = u_1; + let _e296 = v_2; + let _e298 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e294), _e296), 0f); + color_1 = _e298; + let _e299 = u_1; + let _e301 = v_2; + let _e303 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e299), _e301), 0f); + direction = _e303; + let _e304 = u_1; + let _e306 = v_2; + let _e308 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e304), _e306), 0f); + cone = _e308; + let _e309 = clustered; + if _e309 { + let _e310 = listRow; + param_126 = _e310; + let _e311 = InSlots_u0028_f1_u003b((¶m_126)); + local_21 = select(1f, 0f, _e311); } else { - let _e312 = slot_5; - param_127 = _e312; - let _e313 = LightListScale_u0028_i1_u003b((¶m_127)); - local_21 = _e313; - } - let _e314 = local_21; - let _e316 = color_1[3u]; - color_1[3u] = (_e316 * _e314); - } - let _e320 = position[3u]; - type_42 = _e320; - let _e321 = type_42; - if (_e321 > 2.5f) { - let _e323 = direction; - halfWidth_1 = _e323.xyz; - let _e325 = cone; - halfHeight_1 = _e325.xyz; - let _e327 = position; - let _e329 = v_world_position_1; - toCentre = (_e327.xyz - _e329); - let _e331 = toCentre; - let _e332 = halfWidth_1; - let _e334 = halfHeight_1; - corners_2[0i] = ((_e331 - _e332) - _e334); - let _e337 = toCentre; - let _e338 = halfWidth_1; - let _e340 = halfHeight_1; - corners_2[1i] = ((_e337 + _e338) - _e340); - let _e343 = toCentre; - let _e344 = halfWidth_1; - let _e346 = halfHeight_1; - corners_2[2i] = ((_e343 + _e344) + _e346); - let _e349 = toCentre; - let _e350 = halfWidth_1; - let _e352 = halfHeight_1; - corners_2[3i] = ((_e349 - _e350) + _e352); - let _e355 = toCentre; - let _e356 = halfWidth_1; - let _e357 = halfHeight_1; - behind = (dot(_e355, cross(_e356, _e357)) >= 0f); - let _e361 = behind; - if _e361 { + let _e313 = slot_5; + param_127 = _e313; + let _e314 = LightListScale_u0028_i1_u003b((¶m_127)); + local_21 = _e314; + } + let _e315 = local_21; + let _e317 = color_1[3u]; + color_1[3u] = (_e317 * _e315); + } + let _e321 = position[3u]; + type_42 = _e321; + let _e322 = type_42; + if (_e322 > 2.5f) { + let _e324 = direction; + halfWidth_1 = _e324.xyz; + let _e326 = cone; + halfHeight_1 = _e326.xyz; + let _e328 = position; + let _e330 = v_world_position_1; + toCentre = (_e328.xyz - _e330); + let _e332 = toCentre; + let _e333 = halfWidth_1; + let _e335 = halfHeight_1; + corners_2[0i] = ((_e332 - _e333) - _e335); + let _e338 = toCentre; + let _e339 = halfWidth_1; + let _e341 = halfHeight_1; + corners_2[1i] = ((_e338 + _e339) - _e341); + let _e344 = toCentre; + let _e345 = halfWidth_1; + let _e347 = halfHeight_1; + corners_2[2i] = ((_e344 + _e345) + _e347); + let _e350 = toCentre; + let _e351 = halfWidth_1; + let _e353 = halfHeight_1; + corners_2[3i] = ((_e350 - _e351) + _e353); + let _e356 = toCentre; + let _e357 = halfWidth_1; + let _e358 = halfHeight_1; + behind = (dot(_e356, cross(_e357, _e358)) >= 0f); + let _e362 = behind; + if _e362 { local_22 = 0f; } else { - let _e362 = corners_2; - param_128 = _e362; - let _e364 = (*s_5).n; - param_129 = _e364; - let _e365 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_128), (¶m_129)); - local_22 = _e365; - } - let _e366 = local_22; - formFactor = _e366; - let _e367 = halfWidth_1; - let _e368 = halfHeight_1; - area = (length(cross(_e367, _e368)) * 4f); - let _e372 = area; - if (_e372 > 0.000000001f) { - let _e374 = area; - local_23 = (1f / _e374); + let _e363 = corners_2; + param_128 = _e363; + let _e365 = (*s_5).n; + param_129 = _e365; + let _e366 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_128), (¶m_129)); + local_22 = _e366; + } + let _e367 = local_22; + formFactor = _e367; + let _e368 = halfWidth_1; + let _e369 = halfHeight_1; + area = (length(cross(_e368, _e369)) * 4f); + let _e373 = area; + if (_e373 > 0.000000001f) { + let _e375 = area; + local_23 = (1f / _e375); } else { local_23 = 0f; } - let _e376 = local_23; - radiance = _e376; - let _e377 = toCentre; - distance_2 = length(_e377); - let _e380 = direction[3u]; - if (_e380 > 0f) { - let _e382 = distance_2; - let _e384 = direction[3u]; - ratio_1 = (_e382 / _e384); - let _e386 = ratio_1; + let _e377 = local_23; + radiance = _e377; + let _e378 = toCentre; + distance_2 = length(_e378); + let _e381 = direction[3u]; + if (_e381 > 0f) { + let _e383 = distance_2; + let _e385 = direction[3u]; + ratio_1 = (_e383 / _e385); let _e387 = ratio_1; - let _e389 = ratio_1; - let _e391 = ratio_1; - window_1 = clamp((1f - (((_e386 * _e387) * _e389) * _e391)), 0f, 1f); - let _e395 = window_1; + let _e388 = ratio_1; + let _e390 = ratio_1; + let _e392 = ratio_1; + window_1 = clamp((1f - (((_e387 * _e388) * _e390) * _e392)), 0f, 1f); let _e396 = window_1; - let _e398 = radiance; - radiance = (_e398 * (_e395 * _e396)); - } - let _e401 = (*s_5).v; - let _e404 = (*s_5).n; - mirror_1 = reflect(-(_e401), _e404); - let _e406 = position; - param_130 = _e406.xyz; - let _e408 = halfWidth_1; - param_131 = _e408; - let _e409 = halfHeight_1; - param_132 = _e409; - let _e410 = v_world_position_1; - param_133 = _e410; - let _e411 = mirror_1; - param_134 = _e411; - let _e412 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_130), (¶m_131), (¶m_132), (¶m_133), (¶m_134)); - representative = _e412; - let _e413 = representative; - let _e414 = v_world_position_1; - toPoint = (_e413 - _e414); - let _e416 = toPoint; - pointDistance = length(_e416); - let _e418 = pointDistance; - if (_e418 > 0.000001f) { - let _e420 = toPoint; - let _e421 = pointDistance; - local_24 = (_e420 / vec3(_e421)); + let _e397 = window_1; + let _e399 = radiance; + radiance = (_e399 * (_e396 * _e397)); + } + let _e402 = (*s_5).v; + let _e405 = (*s_5).n; + mirror_1 = reflect(-(_e402), _e405); + let _e407 = position; + param_130 = _e407.xyz; + let _e409 = halfWidth_1; + param_131 = _e409; + let _e410 = halfHeight_1; + param_132 = _e410; + let _e411 = v_world_position_1; + param_133 = _e411; + let _e412 = mirror_1; + param_134 = _e412; + let _e413 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_130), (¶m_131), (¶m_132), (¶m_133), (¶m_134)); + representative = _e413; + let _e414 = representative; + let _e415 = v_world_position_1; + toPoint = (_e414 - _e415); + let _e417 = toPoint; + pointDistance = length(_e417); + let _e419 = pointDistance; + if (_e419 > 0.000001f) { + let _e421 = toPoint; + let _e422 = pointDistance; + local_24 = (_e421 / vec3(_e422)); } else { - let _e425 = (*s_5).n; - local_24 = _e425; - } - let _e426 = local_24; - light_3.l = _e426; - let _e429 = light_3.l; - let _e431 = (*s_5).v; - light_3.h = normalize((_e429 + _e431)); - let _e435 = formFactor; - light_3.n_dot_l = _e435; - let _e438 = (*s_5).n; - let _e440 = light_3.h; - light_3.n_dot_h = max(dot(_e438, _e440), 0f); - let _e445 = (*s_5).v; - let _e447 = light_3.h; - light_3.v_dot_h = max(dot(_e445, _e447), 0f); - let _e451 = color_1; - let _e454 = color_1[3u]; - let _e456 = radiance; - light_3.radiance = ((_e451.xyz * _e454) * _e456); + let _e426 = (*s_5).n; + local_24 = _e426; + } + let _e427 = local_24; + light_3.l = _e427; + let _e430 = light_3.l; + let _e432 = (*s_5).v; + light_3.h = normalize((_e430 + _e432)); + let _e436 = formFactor; + light_3.n_dot_l = _e436; + let _e439 = (*s_5).n; + let _e441 = light_3.h; + light_3.n_dot_h = max(dot(_e439, _e441), 0f); + let _e446 = (*s_5).v; + let _e448 = light_3.h; + light_3.v_dot_h = max(dot(_e446, _e448), 0f); + let _e452 = color_1; + let _e455 = color_1[3u]; + let _e457 = radiance; + light_3.radiance = ((_e452.xyz * _e455) * _e457); light_3.integrated = 1f; - let _e461 = (*s_5).n; - param_135 = _e461; - let _e463 = (*s_5).v; - param_136 = _e463; - let _e465 = (*s_5).roughness; - param_137 = _e465; - let _e466 = corners_2; - param_138 = _e466; - let _e467 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_135), (¶m_136), (¶m_137), (¶m_138)); - light_3.ltc = _e467; - let _e469 = light_3; - return _e469; - } - let _e470 = direction; - aim = normalize(_e470.xyz); + let _e462 = (*s_5).n; + param_135 = _e462; + let _e464 = (*s_5).v; + param_136 = _e464; + let _e466 = (*s_5).roughness; + param_137 = _e466; + let _e467 = corners_2; + param_138 = _e467; + let _e468 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_135), (¶m_136), (¶m_137), (¶m_138)); + light_3.ltc = _e468; + let _e470 = light_3; + return _e470; + } + let _e471 = direction; + aim = normalize(_e471.xyz); attenuation_1 = 1f; - let _e473 = type_42; - if (_e473 < 0.5f) { - let _e475 = aim; - light_3.l = -(_e475); + let _e474 = type_42; + if (_e474 < 0.5f) { + let _e476 = aim; + light_3.l = -(_e476); } else { - let _e478 = position; - let _e480 = v_world_position_1; - toLight_1 = (_e478.xyz - _e480); - let _e482 = toLight_1; - distance_3 = length(_e482); - let _e484 = distance_3; - if (_e484 < 0.000001f) { - let _e487 = (*s_5).n; - light_3.l = _e487; - let _e490 = (*s_5).n; - light_3.h = _e490; + let _e479 = position; + let _e481 = v_world_position_1; + toLight_1 = (_e479.xyz - _e481); + let _e483 = toLight_1; + distance_3 = length(_e483); + let _e485 = distance_3; + if (_e485 < 0.000001f) { + let _e488 = (*s_5).n; + light_3.l = _e488; + let _e491 = (*s_5).n; + light_3.h = _e491; light_3.radiance = vec3(0f, 0f, 0f); light_3.n_dot_l = 0f; light_3.n_dot_h = 0f; light_3.v_dot_h = 0f; - let _e496 = light_3; - return _e496; - } - let _e497 = toLight_1; - let _e498 = distance_3; - light_3.l = (_e497 / vec3(_e498)); - let _e502 = distance_3; - param_139 = _e502; - let _e504 = direction[3u]; - param_140 = _e504; - let _e505 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_139), (¶m_140)); - attenuation_1 = _e505; - let _e506 = type_42; - if (_e506 > 1.5f) { - let _e508 = aim; - let _e510 = light_3.l; - cosAngle = dot(_e508, -(_e510)); - let _e513 = cosAngle; - let _e515 = cone[1u]; - let _e518 = cone[0u]; - let _e520 = cone[1u]; - let _e524 = attenuation_1; - attenuation_1 = (_e524 * clamp(((_e513 - _e515) / (_e518 - _e520)), 0f, 1f)); - } - } - let _e527 = light_3.l; - let _e529 = (*s_5).v; - light_3.h = normalize((_e527 + _e529)); - let _e534 = (*s_5).n; - let _e536 = light_3.l; - light_3.n_dot_l = max(dot(_e534, _e536), 0f); - let _e541 = (*s_5).n; - let _e543 = light_3.h; - light_3.n_dot_h = max(dot(_e541, _e543), 0f); - let _e548 = (*s_5).v; - let _e550 = light_3.h; - light_3.v_dot_h = max(dot(_e548, _e550), 0f); - let _e554 = color_1; - let _e557 = color_1[3u]; - let _e559 = attenuation_1; - light_3.radiance = ((_e554.xyz * _e557) * _e559); - let _e562 = light_3; - return _e562; + let _e497 = light_3; + return _e497; + } + let _e498 = toLight_1; + let _e499 = distance_3; + light_3.l = (_e498 / vec3(_e499)); + let _e503 = distance_3; + param_139 = _e503; + let _e505 = direction[3u]; + param_140 = _e505; + let _e506 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_139), (¶m_140)); + attenuation_1 = _e506; + let _e507 = type_42; + if (_e507 > 1.5f) { + let _e509 = aim; + let _e511 = light_3.l; + cosAngle = dot(_e509, -(_e511)); + let _e514 = cosAngle; + let _e516 = cone[1u]; + let _e519 = cone[0u]; + let _e521 = cone[1u]; + let _e525 = attenuation_1; + attenuation_1 = (_e525 * clamp(((_e514 - _e516) / (_e519 - _e521)), 0f, 1f)); + } + } + let _e528 = light_3.l; + let _e530 = (*s_5).v; + light_3.h = normalize((_e528 + _e530)); + let _e535 = (*s_5).n; + let _e537 = light_3.l; + light_3.n_dot_l = max(dot(_e535, _e537), 0f); + let _e542 = (*s_5).n; + let _e544 = light_3.h; + light_3.n_dot_h = max(dot(_e542, _e544), 0f); + let _e549 = (*s_5).v; + let _e551 = light_3.h; + light_3.v_dot_h = max(dot(_e549, _e551), 0f); + let _e555 = color_1; + let _e558 = color_1[3u]; + let _e560 = attenuation_1; + light_3.radiance = ((_e555.xyz * _e558) * _e560); + let _e563 = light_3; + return _e563; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -14406,58 +14470,58 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var param_141: f32; var param_142: f32; - let _e210 = light_list_info.cluster_view_projection; - let _e211 = (*world_3); - clip_1 = (_e210 * vec4(_e211.x, _e211.y, _e211.z, 1f)); - let _e217 = clip_1; - let _e220 = clip_1[3u]; - ndc_1 = (_e217.xy / vec2(max(_e220, 0.000001f))); - let _e225 = light_list_info.cluster_grid; - grid = _e225.xyz; - let _e229 = light_list_info.cluster_depth[0u]; - near_1 = _e229; - let _e231 = ndc_1[0u]; - let _e235 = grid[0u]; - let _e239 = grid[0u]; - tx = clamp(floor((((_e231 * 0.5f) + 0.5f) * _e235)), 0f, (_e239 - 1f)); - let _e243 = ndc_1[1u]; - let _e247 = grid[1u]; - let _e251 = grid[1u]; - ty = clamp(floor((((_e243 * 0.5f) + 0.5f) * _e247)), 0f, (_e251 - 1f)); - let _e255 = clip_1[3u]; - let _e256 = near_1; - if (_e255 <= _e256) { + let _e211 = light_list_info.cluster_view_projection; + let _e212 = (*world_3); + clip_1 = (_e211 * vec4(_e212.x, _e212.y, _e212.z, 1f)); + let _e218 = clip_1; + let _e221 = clip_1[3u]; + ndc_1 = (_e218.xy / vec2(max(_e221, 0.000001f))); + let _e226 = light_list_info.cluster_grid; + grid = _e226.xyz; + let _e230 = light_list_info.cluster_depth[0u]; + near_1 = _e230; + let _e232 = ndc_1[0u]; + let _e236 = grid[0u]; + let _e240 = grid[0u]; + tx = clamp(floor((((_e232 * 0.5f) + 0.5f) * _e236)), 0f, (_e240 - 1f)); + let _e244 = ndc_1[1u]; + let _e248 = grid[1u]; + let _e252 = grid[1u]; + ty = clamp(floor((((_e244 * 0.5f) + 0.5f) * _e248)), 0f, (_e252 - 1f)); + let _e256 = clip_1[3u]; + let _e257 = near_1; + if (_e256 <= _e257) { local_25 = 0f; } else { - let _e259 = clip_1[3u]; - let _e260 = near_1; - let _e265 = light_list_info.cluster_depth[1u]; - let _e269 = grid[2u]; - local_25 = clamp(floor((log((_e259 / _e260)) * _e265)), 0f, (_e269 - 1f)); - } - let _e272 = local_25; - tz = _e272; - let _e273 = tx; - let _e274 = ty; - let _e276 = grid[0u]; - let _e279 = tz; - let _e281 = grid[0u]; - let _e284 = grid[1u]; - cell = ((_e273 + (_e274 * _e276)) + ((_e279 * _e281) * _e284)); - let _e287 = cell; - row_3 = floor((_e287 / 4f)); - let _e290 = cell; - let _e291 = row_3; - let _e296 = light_list_info.cluster_depth[2u]; - let _e297 = row_3; - param_141 = (_e290 - (_e291 * 4f)); - param_142 = (_e296 + _e297); - let _e299 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_141), (¶m_142)); - header = _e299; - let _e301 = header[0u]; - g_cluster_offset = _e301; - let _e303 = header[1u]; - g_cluster_count = _e303; + let _e260 = clip_1[3u]; + let _e261 = near_1; + let _e266 = light_list_info.cluster_depth[1u]; + let _e270 = grid[2u]; + local_25 = clamp(floor((log((_e260 / _e261)) * _e266)), 0f, (_e270 - 1f)); + } + let _e273 = local_25; + tz = _e273; + let _e274 = tx; + let _e275 = ty; + let _e277 = grid[0u]; + let _e280 = tz; + let _e282 = grid[0u]; + let _e285 = grid[1u]; + cell = ((_e274 + (_e275 * _e277)) + ((_e280 * _e282) * _e285)); + let _e288 = cell; + row_3 = floor((_e288 / 4f)); + let _e291 = cell; + let _e292 = row_3; + let _e297 = light_list_info.cluster_depth[2u]; + let _e298 = row_3; + param_141 = (_e291 - (_e292 * 4f)); + param_142 = (_e297 + _e298); + let _e300 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_141), (¶m_142)); + header = _e300; + let _e302 = header[0u]; + g_cluster_offset = _e302; + let _e304 = header[1u]; + g_cluster_count = _e304; return; } @@ -14465,19 +14529,19 @@ fn LightCount_u0028_() -> i32 { var tail: f32; var param_143: vec3; - let _e199 = light_list_info.list[0u]; - tail = _e199; - let _e200 = Clustered_u0028_(); - if _e200 { - let _e201 = v_world_position_1; - param_143 = _e201; + let _e200 = light_list_info.list[0u]; + tail = _e200; + let _e201 = Clustered_u0028_(); + if _e201 { + let _e202 = v_world_position_1; + param_143 = _e202; FindCluster_u0028_vf3_u003b((¶m_143)); - let _e202 = g_cluster_count; - tail = _e202; + let _e203 = g_cluster_count; + tail = _e203; } - let _e205 = frag_info.frame_params[1u]; - let _e209 = tail; - return (clamp(i32((_e205 + 0.5f)), 0i, 8i) + clamp(i32((_e209 + 0.5f)), 0i, 24i)); + let _e206 = frag_info.frame_params[1u]; + let _e210 = tail; + return (clamp(i32((_e206 + 0.5f)), 0i, 8i) + clamp(i32((_e210 + 0.5f)), 0i, 24i)); } fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) -> vec3 { @@ -14500,87 +14564,89 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_154: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e213 = LightCount_u0028_(); - count_1 = _e213; + let _e214 = LightCount_u0028_(); + count_1 = _e214; i_9 = 0i; loop { - let _e214 = i_9; - if (_e214 < 32i) { - let _e216 = i_9; - let _e217 = count_1; - if (_e216 >= _e217) { + let _e215 = i_9; + if (_e215 < 32i) { + let _e217 = i_9; + let _e218 = count_1; + if (_e217 >= _e218) { break; } - let _e219 = i_9; - param_144 = _e219; - let _e220 = (*s_6); - param_145 = _e220; - let _e221 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_144), (¶m_145)); - light_4 = _e221; - let _e223 = light_4.n_dot_l; - if (_e223 <= 0f) { + let _e220 = i_9; + param_144 = _e220; + let _e221 = (*s_6); + param_145 = _e221; + let _e222 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_144), (¶m_145)); + light_4 = _e222; + let _e224 = light_4.n_dot_l; + if (_e224 <= 0f) { continue; } - let _e225 = i_9; - param_146 = _e225; - let _e226 = LightHasShadow_u0028_i1_u003b((¶m_146)); - if _e226 { - let _e227 = (*s_6); - param_147 = _e227; - let _e228 = light_4; - param_148 = _e228; - let _e229 = i_9; - param_149 = _e229; - let _e230 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_147), (¶m_148), (¶m_149)); - let _e231 = v_world_position_1; - param_150 = _e231; - let _e233 = (*s_6).n; - param_151 = _e233; - let _e234 = i_9; - param_152 = _e234; - let _e235 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_150), (¶m_151), (¶m_152)); - local_26 = (_e230 * _e235); + light_transmittance = vec3(1f, 1f, 1f); + let _e226 = i_9; + param_146 = _e226; + let _e227 = LightHasShadow_u0028_i1_u003b((¶m_146)); + if _e227 { + let _e228 = (*s_6); + param_147 = _e228; + let _e229 = light_4; + param_148 = _e229; + let _e230 = i_9; + param_149 = _e230; + let _e231 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_147), (¶m_148), (¶m_149)); + let _e232 = v_world_position_1; + param_150 = _e232; + let _e234 = (*s_6).n; + param_151 = _e234; + let _e235 = i_9; + param_152 = _e235; + let _e236 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_150), (¶m_151), (¶m_152)); + local_26 = (_e231 * _e236); } else { local_26 = 1f; } - let _e237 = local_26; - visibility = _e237; - let _e238 = visibility; - if (_e238 <= 0f) { + let _e238 = local_26; + visibility = _e238; + let _e239 = visibility; + if (_e239 <= 0f) { continue; } - let _e240 = (*s_6); - param_153 = _e240; - let _e241 = light_4; - param_154 = _e241; - let _e242 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_153), (¶m_154)); - let _e244 = light_4.radiance; - let _e247 = light_4.n_dot_l; - let _e249 = visibility; - let _e251 = total_1; - total_1 = (_e251 + (((_e242 * _e244) * _e247) * _e249)); + let _e241 = (*s_6); + param_153 = _e241; + let _e242 = light_4; + param_154 = _e242; + let _e243 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_153), (¶m_154)); + let _e245 = light_4.radiance; + let _e248 = light_4.n_dot_l; + let _e250 = visibility; + let _e252 = light_transmittance; + let _e254 = total_1; + total_1 = (_e254 + ((((_e243 * _e245) * _e248) * _e250) * _e252)); continue; } else { break; } continuing { - let _e253 = i_9; - i_9 = (_e253 + 1i); + let _e256 = i_9; + i_9 = (_e256 + 1i); } } - let _e255 = total_1; - return _e255; + let _e258 = total_1; + return _e258; } fn SampleLightmap_u0028_() -> vec3 { var texel_4: vec4; - let _e196 = v_lightmap_uv_1; - let _e197 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e196); - texel_4 = _e197; - let _e198 = texel_4; - let _e201 = texel_4[3u]; - return ((_e198.xyz * _e201) * 8f); + let _e197 = v_lightmap_uv_1; + let _e198 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e197); + texel_4 = _e198; + let _e199 = texel_4; + let _e202 = texel_4[3u]; + return ((_e199.xyz * _e202) * 8f); } fn EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b(rho_2: ptr>, r_4: ptr, mu_1: ptr) -> vec3 { @@ -14591,80 +14657,80 @@ fn EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b(rho_2: ptr>, var param_158: vec3; var param_159: f32; - let _e204 = (*mu_1); - param_155 = _e204; - let _e205 = (*r_4); - param_156 = _e205; - let _e206 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_155), (¶m_156)); - e_1 = _e206; - let _e207 = (*rho_2); - let _e208 = e_1; - let _e210 = (*r_4); - param_157 = _e210; - let _e211 = FonAverage_u0028_f1_u003b((¶m_157)); - let _e212 = (*rho_2); - param_158 = _e212; - param_159 = _e211; - let _e213 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_158), (¶m_159)); - let _e214 = e_1; - return ((_e207 * _e208) + (_e213 * (1f - _e214))); + let _e205 = (*mu_1); + param_155 = _e205; + let _e206 = (*r_4); + param_156 = _e206; + let _e207 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_155), (¶m_156)); + e_1 = _e207; + let _e208 = (*rho_2); + let _e209 = e_1; + let _e211 = (*r_4); + param_157 = _e211; + let _e212 = FonAverage_u0028_f1_u003b((¶m_157)); + let _e213 = (*rho_2); + param_158 = _e213; + param_159 = _e212; + let _e214 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_158), (¶m_159)); + let _e215 = e_1; + return ((_e208 * _e209) + (_e214 * (1f - _e215))); } fn MaterialLodBias_u0028_() -> f32 { - let _e197 = frag_info.target_origin[1u]; - return _e197; + let _e198 = frag_info.target_origin[1u]; + return _e198; } fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) { var orm: vec3; - let _e197 = v_texcoord_1; - let _e198 = MaterialLodBias_u0028_(); - let _e199 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e197, _e198); - orm = _e199.xyz; - let _e202 = (*s_7).metallic; - let _e204 = orm[2u]; - (*s_7).metallic = clamp((_e202 * _e204), 0f, 1f); - let _e209 = (*s_7).roughness; - let _e211 = orm[1u]; - (*s_7).roughness = clamp((_e209 * _e211), 0.02f, 1f); + let _e198 = v_texcoord_1; + let _e199 = MaterialLodBias_u0028_(); + let _e200 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e198, _e199); + orm = _e200.xyz; + let _e203 = (*s_7).metallic; + let _e205 = orm[2u]; + (*s_7).metallic = clamp((_e203 * _e205), 0f, 1f); + let _e210 = (*s_7).roughness; + let _e212 = orm[1u]; + (*s_7).roughness = clamp((_e210 * _e212), 0.02f, 1f); return; } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e196 = (*srgb); - let _e199 = (*srgb); - let _e204 = (*srgb); - return mix((_e196 / vec3(12.92f)), pow(((_e199 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e204)); + let _e197 = (*srgb); + let _e200 = (*srgb); + let _e205 = (*srgb); + return mix((_e197 / vec3(12.92f)), pow(((_e200 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e205)); } fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) { var emissive: vec3; var param_160: vec3; - let _e198 = v_texcoord_1; - let _e199 = MaterialLodBias_u0028_(); - let _e200 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e198, _e199); - param_160 = _e200.xyz; - let _e202 = SrgbToLinear_u0028_vf3_u003b((¶m_160)); - emissive = _e202; - let _e203 = emissive; - let _e205 = frag_info.emissive; - let _e210 = frag_info.material2_[3u]; - (*s_8).emissive = ((_e203 * _e205.xyz) * _e210); + let _e199 = v_texcoord_1; + let _e200 = MaterialLodBias_u0028_(); + let _e201 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e199, _e200); + param_160 = _e201.xyz; + let _e203 = SrgbToLinear_u0028_vf3_u003b((¶m_160)); + emissive = _e203; + let _e204 = emissive; + let _e206 = frag_info.emissive; + let _e211 = frag_info.material2_[3u]; + (*s_8).emissive = ((_e204 * _e206.xyz) * _e211); return; } fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) { var occlusion: f32; - let _e197 = v_texcoord_1; - let _e198 = MaterialLodBias_u0028_(); - let _e199 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e197, _e198); - occlusion = _e199.x; - let _e201 = occlusion; - let _e204 = frag_info.material2_[2u]; - (*s_9).occlusion = mix(1f, _e201, clamp(_e204, 0f, 1f)); + let _e198 = v_texcoord_1; + let _e199 = MaterialLodBias_u0028_(); + let _e200 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e198, _e199); + occlusion = _e200.x; + let _e202 = occlusion; + let _e205 = frag_info.material2_[2u]; + (*s_9).occlusion = mix(1f, _e202, clamp(_e205, 0f, 1f)); return; } @@ -14674,64 +14740,64 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var b_4: vec3; var sampled: vec3; - let _e200 = v_texcoord_1; - let _e201 = MaterialLodBias_u0028_(); - let _e202 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e200, _e201); - sampledTexel = _e202; - let _e203 = v_tangent_1; - t_2 = _e203.xyz; - let _e205 = t_2; - let _e207 = (*s_10).n; - let _e209 = (*s_10).n; - let _e210 = t_2; - t_2 = (_e205 - (_e207 * dot(_e209, _e210))); - let _e214 = t_2; + let _e201 = v_texcoord_1; + let _e202 = MaterialLodBias_u0028_(); + let _e203 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e201, _e202); + sampledTexel = _e203; + let _e204 = v_tangent_1; + t_2 = _e204.xyz; + let _e206 = t_2; + let _e208 = (*s_10).n; + let _e210 = (*s_10).n; + let _e211 = t_2; + t_2 = (_e206 - (_e208 * dot(_e210, _e211))); let _e215 = t_2; - if (dot(_e214, _e215) < 0.000000000001f) { + let _e216 = t_2; + if (dot(_e215, _e216) < 0.000000000001f) { return; } - let _e218 = t_2; - t_2 = normalize(_e218); - let _e221 = (*s_10).n; - let _e222 = t_2; - let _e225 = v_tangent_1[3u]; - b_4 = (cross(_e221, _e222) * _e225); - let _e227 = gl_FrontFacing_1; - if !(_e227) { - let _e229 = t_2; - t_2 = -(_e229); - } - let _e231 = sampledTexel; - sampled = ((_e231.xyz * 2f) - vec3(1f)); - let _e238 = frag_info.emissive[3u]; - if (_e238 > 0.5f) { - let _e240 = sampled; - let _e242 = sampled; - sampled[2u] = sqrt(max((1f - dot(_e240.xy, _e242.xy)), 0f)); - } - let _e251 = frag_info.material2_[1u]; - let _e252 = sampled; - let _e254 = (_e252.xy * _e251); - sampled[0u] = _e254.x; - sampled[1u] = _e254.y; - let _e259 = t_2; - let _e261 = sampled[0u]; - let _e263 = b_4; - let _e265 = sampled[1u]; - let _e269 = (*s_10).n; - let _e271 = sampled[2u]; - (*s_10).n = normalize((((_e259 * _e261) + (_e263 * _e265)) + (_e269 * _e271))); - let _e277 = (*s_10).n; - let _e279 = (*s_10).v; - (*s_10).n_dot_v = max(dot(_e277, _e279), 0.0001f); + let _e219 = t_2; + t_2 = normalize(_e219); + let _e222 = (*s_10).n; + let _e223 = t_2; + let _e226 = v_tangent_1[3u]; + b_4 = (cross(_e222, _e223) * _e226); + let _e228 = gl_FrontFacing_1; + if !(_e228) { + let _e230 = t_2; + t_2 = -(_e230); + } + let _e232 = sampledTexel; + sampled = ((_e232.xyz * 2f) - vec3(1f)); + let _e239 = frag_info.emissive[3u]; + if (_e239 > 0.5f) { + let _e241 = sampled; + let _e243 = sampled; + sampled[2u] = sqrt(max((1f - dot(_e241.xy, _e243.xy)), 0f)); + } + let _e252 = frag_info.material2_[1u]; + let _e253 = sampled; + let _e255 = (_e253.xy * _e252); + sampled[0u] = _e255.x; + sampled[1u] = _e255.y; + let _e260 = t_2; + let _e262 = sampled[0u]; + let _e264 = b_4; + let _e266 = sampled[1u]; + let _e270 = (*s_10).n; + let _e272 = sampled[2u]; + (*s_10).n = normalize((((_e260 * _e262) + (_e264 * _e266)) + (_e270 * _e272))); + let _e278 = (*s_10).n; + let _e280 = (*s_10).v; + (*s_10).n_dot_v = max(dot(_e278, _e280), 0.0001f); return; } fn AtlasTexel_u0028_vf2_u003b(texel_5: ptr>) -> vec4 { - let _e196 = (*texel_5); - let _e200 = irradiance_info.atlas; - let _e203 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e196 + vec2(0.5f)) * _e200.xy), 0f); - return _e203; + let _e197 = (*texel_5); + let _e201 = irradiance_info.atlas; + let _e204 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e197 + vec2(0.5f)) * _e201.xy), 0f); + return _e204; } fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr>, interior: ptr, uv_6: ptr>) -> vec4 { @@ -14747,42 +14813,42 @@ fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr; var param_164: vec2; - let _e209 = (*uv_6); - let _e210 = (*interior); - at_1 = ((vec2(1f) + (_e209 * _e210)) - vec2(0.5f)); - let _e216 = at_1; - low = floor(_e216); - let _e218 = at_1; - let _e219 = low; - f_3 = (_e218 - _e219); - let _e221 = (*corner); - let _e222 = low; - param_161 = (_e221 + _e222); - let _e224 = AtlasTexel_u0028_vf2_u003b((¶m_161)); - a_6 = _e224; - let _e225 = (*corner); - let _e226 = low; - param_162 = ((_e225 + _e226) + vec2(1f, 0f)); - let _e229 = AtlasTexel_u0028_vf2_u003b((¶m_162)); - b_5 = _e229; - let _e230 = (*corner); - let _e231 = low; - param_163 = ((_e230 + _e231) + vec2(0f, 1f)); - let _e234 = AtlasTexel_u0028_vf2_u003b((¶m_163)); - c = _e234; - let _e235 = (*corner); - let _e236 = low; - param_164 = ((_e235 + _e236) + vec2(1f, 1f)); - let _e239 = AtlasTexel_u0028_vf2_u003b((¶m_164)); - d_5 = _e239; - let _e240 = a_6; - let _e241 = b_5; - let _e243 = f_3[0u]; - let _e246 = c; - let _e247 = d_5; - let _e249 = f_3[0u]; - let _e253 = f_3[1u]; - return mix(mix(_e240, _e241, vec4(_e243)), mix(_e246, _e247, vec4(_e249)), vec4(_e253)); + let _e210 = (*uv_6); + let _e211 = (*interior); + at_1 = ((vec2(1f) + (_e210 * _e211)) - vec2(0.5f)); + let _e217 = at_1; + low = floor(_e217); + let _e219 = at_1; + let _e220 = low; + f_3 = (_e219 - _e220); + let _e222 = (*corner); + let _e223 = low; + param_161 = (_e222 + _e223); + let _e225 = AtlasTexel_u0028_vf2_u003b((¶m_161)); + a_6 = _e225; + let _e226 = (*corner); + let _e227 = low; + param_162 = ((_e226 + _e227) + vec2(1f, 0f)); + let _e230 = AtlasTexel_u0028_vf2_u003b((¶m_162)); + b_5 = _e230; + let _e231 = (*corner); + let _e232 = low; + param_163 = ((_e231 + _e232) + vec2(0f, 1f)); + let _e235 = AtlasTexel_u0028_vf2_u003b((¶m_163)); + c = _e235; + let _e236 = (*corner); + let _e237 = low; + param_164 = ((_e236 + _e237) + vec2(1f, 1f)); + let _e240 = AtlasTexel_u0028_vf2_u003b((¶m_164)); + d_5 = _e240; + let _e241 = a_6; + let _e242 = b_5; + let _e244 = f_3[0u]; + let _e247 = c; + let _e248 = d_5; + let _e250 = f_3[0u]; + let _e254 = f_3[1u]; + return mix(mix(_e241, _e242, vec4(_e244)), mix(_e247, _e248, vec4(_e250)), vec4(_e254)); } fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec2 { @@ -14790,29 +14856,29 @@ fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec var n_6: vec3; var xy: vec2; - let _e200 = (*direction_1)[0u]; - let _e203 = (*direction_1)[1u]; - let _e207 = (*direction_1)[2u]; - sum_1 = ((abs(_e200) + abs(_e203)) + abs(_e207)); - let _e210 = sum_1; - if (_e210 <= 0f) { + let _e201 = (*direction_1)[0u]; + let _e204 = (*direction_1)[1u]; + let _e208 = (*direction_1)[2u]; + sum_1 = ((abs(_e201) + abs(_e204)) + abs(_e208)); + let _e211 = sum_1; + if (_e211 <= 0f) { return vec2(0.5f, 0.5f); } - let _e212 = (*direction_1); - let _e213 = sum_1; - n_6 = (_e212 / vec3(_e213)); - let _e216 = n_6; - xy = _e216.xy; - let _e219 = n_6[2u]; - if (_e219 < 0f) { - let _e222 = n_6[1u]; - let _e226 = n_6[0u]; - let _e231 = n_6[0u]; - let _e235 = n_6[1u]; - xy = vec2(((1f - abs(_e222)) * select(-1f, 1f, (_e226 >= 0f))), ((1f - abs(_e231)) * select(-1f, 1f, (_e235 >= 0f)))); + let _e213 = (*direction_1); + let _e214 = sum_1; + n_6 = (_e213 / vec3(_e214)); + let _e217 = n_6; + xy = _e217.xy; + let _e220 = n_6[2u]; + if (_e220 < 0f) { + let _e223 = n_6[1u]; + let _e227 = n_6[0u]; + let _e232 = n_6[0u]; + let _e236 = n_6[1u]; + xy = vec2(((1f - abs(_e223)) * select(-1f, 1f, (_e227 >= 0f))), ((1f - abs(_e232)) * select(-1f, 1f, (_e236 >= 0f)))); } - let _e240 = xy; - return ((_e240 * 0.5f) + vec2(0.5f)); + let _e241 = xy; + return ((_e241 * 0.5f) + vec2(0.5f)); } fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view: ptr>) -> vec3 { @@ -14860,190 +14926,190 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var local_27: vec3; - let _e242 = irradiance_info.origin; - origin_2 = _e242.xyz; - let _e245 = irradiance_info.spacing; - spacing = _e245.xyz; - let _e248 = irradiance_info.counts; - counts = _e248.xyz; - let _e252 = irradiance_info.tiles[0u]; - irradianceTile = _e252; - let _e255 = irradiance_info.tiles[1u]; - depthTile = _e255; - let _e258 = irradiance_info.tiles[2u]; - columns = _e258; - let _e261 = irradiance_info.tiles[3u]; - momentsTop = _e261; - let _e262 = (*normal_1); - unit = normalize(_e262); - let _e264 = (*world_4); - let _e265 = unit; - let _e268 = irradiance_info.spacing[3u]; - let _e271 = (*view); - let _e274 = irradiance_info.counts[3u]; - biased = ((_e264 + (_e265 * _e268)) + (_e271 * _e274)); - let _e277 = biased; - let _e278 = origin_2; - let _e280 = spacing; - grid_1 = ((_e277 - _e278) / _e280); - let _e282 = grid_1; - let _e284 = counts; - base = clamp(floor(_e282), vec3(0f, 0f, 0f), (_e284 - vec3(2f))); - let _e288 = grid_1; - let _e289 = base; - f_4 = clamp((_e288 - _e289), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e243 = irradiance_info.origin; + origin_2 = _e243.xyz; + let _e246 = irradiance_info.spacing; + spacing = _e246.xyz; + let _e249 = irradiance_info.counts; + counts = _e249.xyz; + let _e253 = irradiance_info.tiles[0u]; + irradianceTile = _e253; + let _e256 = irradiance_info.tiles[1u]; + depthTile = _e256; + let _e259 = irradiance_info.tiles[2u]; + columns = _e259; + let _e262 = irradiance_info.tiles[3u]; + momentsTop = _e262; + let _e263 = (*normal_1); + unit = normalize(_e263); + let _e265 = (*world_4); + let _e266 = unit; + let _e269 = irradiance_info.spacing[3u]; + let _e272 = (*view); + let _e275 = irradiance_info.counts[3u]; + biased = ((_e265 + (_e266 * _e269)) + (_e272 * _e275)); + let _e278 = biased; + let _e279 = origin_2; + let _e281 = spacing; + grid_1 = ((_e278 - _e279) / _e281); + let _e283 = grid_1; + let _e285 = counts; + base = clamp(floor(_e283), vec3(0f, 0f, 0f), (_e285 - vec3(2f))); + let _e289 = grid_1; + let _e290 = base; + f_4 = clamp((_e289 - _e290), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); total_2 = vec3(0f, 0f, 0f); weights = 0f; corner_1 = 0i; loop { - let _e292 = corner_1; - if (_e292 < 8i) { - let _e294 = corner_1; - let _e297 = corner_1; - let _e302 = corner_1; - offset_3 = vec3(f32((_e294 & 1i)), f32(((_e297 >> bitcast(1i)) & 1i)), f32(((_e302 >> bitcast(2i)) & 1i))); - let _e308 = base; - let _e309 = offset_3; - cell_1 = (_e308 + _e309); - let _e312 = cell_1[2u]; - let _e314 = counts[1u]; - let _e317 = cell_1[1u]; - let _e320 = counts[0u]; - let _e323 = cell_1[0u]; - probe = ((((_e312 * _e314) + _e317) * _e320) + _e323); - let _e325 = probe; - let _e326 = columns; - let _e331 = probe; - let _e332 = columns; - tile_4 = vec2((_e325 - (floor((_e325 / _e326)) * _e326)), floor((_e331 / _e332))); - let _e336 = tile_4; - let _e337 = irradianceTile; - irradianceCorner = (_e336 * (_e337 + 2f)); - let _e341 = tile_4[0u]; - let _e342 = depthTile; - let _e345 = momentsTop; - let _e347 = tile_4[1u]; - let _e348 = depthTile; - momentCorner = vec2((_e341 * (_e342 + 2f)), (_e345 + (_e347 * (_e348 + 2f)))); - let _e353 = irradianceCorner; - param_165 = (_e353 + vec2(1f)); - let _e356 = AtlasTexel_u0028_vf2_u003b((¶m_165)); - if (_e356.w < 0.5f) { + let _e293 = corner_1; + if (_e293 < 8i) { + let _e295 = corner_1; + let _e298 = corner_1; + let _e303 = corner_1; + offset_3 = vec3(f32((_e295 & 1i)), f32(((_e298 >> bitcast(1i)) & 1i)), f32(((_e303 >> bitcast(2i)) & 1i))); + let _e309 = base; + let _e310 = offset_3; + cell_1 = (_e309 + _e310); + let _e313 = cell_1[2u]; + let _e315 = counts[1u]; + let _e318 = cell_1[1u]; + let _e321 = counts[0u]; + let _e324 = cell_1[0u]; + probe = ((((_e313 * _e315) + _e318) * _e321) + _e324); + let _e326 = probe; + let _e327 = columns; + let _e332 = probe; + let _e333 = columns; + tile_4 = vec2((_e326 - (floor((_e326 / _e327)) * _e327)), floor((_e332 / _e333))); + let _e337 = tile_4; + let _e338 = irradianceTile; + irradianceCorner = (_e337 * (_e338 + 2f)); + let _e342 = tile_4[0u]; + let _e343 = depthTile; + let _e346 = momentsTop; + let _e348 = tile_4[1u]; + let _e349 = depthTile; + momentCorner = vec2((_e342 * (_e343 + 2f)), (_e346 + (_e348 * (_e349 + 2f)))); + let _e354 = irradianceCorner; + param_165 = (_e354 + vec2(1f)); + let _e357 = AtlasTexel_u0028_vf2_u003b((¶m_165)); + if (_e357.w < 0.5f) { continue; } - let _e359 = f_4; - let _e361 = f_4; - let _e362 = offset_3; - trilinear = mix((vec3(1f, 1f, 1f) - _e359), _e361, _e362); - let _e365 = trilinear[0u]; - let _e367 = trilinear[1u]; - let _e370 = trilinear[2u]; - weight_2 = max(((_e365 * _e367) * _e370), 0.001f); - let _e373 = origin_2; - let _e374 = spacing; - let _e375 = cell_1; - probePosition = (_e373 + (_e374 * _e375)); - let _e378 = probePosition; - let _e379 = biased; - toProbe = (_e378 - _e379); - let _e381 = toProbe; - distance_4 = length(_e381); - let _e383 = distance_4; - if (_e383 > 0.000001f) { - let _e385 = toProbe; - let _e386 = distance_4; - direction_2 = (_e385 / vec3(_e386)); - let _e389 = unit; - let _e390 = probePosition; - let _e391 = (*world_4); - facing = ((dot(_e389, normalize((_e390 - _e391))) * 0.5f) + 0.5f); - let _e397 = facing; + let _e360 = f_4; + let _e362 = f_4; + let _e363 = offset_3; + trilinear = mix((vec3(1f, 1f, 1f) - _e360), _e362, _e363); + let _e366 = trilinear[0u]; + let _e368 = trilinear[1u]; + let _e371 = trilinear[2u]; + weight_2 = max(((_e366 * _e368) * _e371), 0.001f); + let _e374 = origin_2; + let _e375 = spacing; + let _e376 = cell_1; + probePosition = (_e374 + (_e375 * _e376)); + let _e379 = probePosition; + let _e380 = biased; + toProbe = (_e379 - _e380); + let _e382 = toProbe; + distance_4 = length(_e382); + let _e384 = distance_4; + if (_e384 > 0.000001f) { + let _e386 = toProbe; + let _e387 = distance_4; + direction_2 = (_e386 / vec3(_e387)); + let _e390 = unit; + let _e391 = probePosition; + let _e392 = (*world_4); + facing = ((dot(_e390, normalize((_e391 - _e392))) * 0.5f) + 0.5f); let _e398 = facing; - probeWeight = ((_e397 * _e398) + 0.2f); - let _e401 = direction_2; - param_166 = -(_e401); - let _e403 = ProbeOctahedral_u0028_vf3_u003b((¶m_166)); - let _e404 = momentCorner; - param_167 = _e404; - let _e405 = depthTile; - param_168 = _e405; - param_169 = _e403; - let _e406 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_167), (¶m_168), (¶m_169)); - moments_3 = _e406.xy; + let _e399 = facing; + probeWeight = ((_e398 * _e399) + 0.2f); + let _e402 = direction_2; + param_166 = -(_e402); + let _e404 = ProbeOctahedral_u0028_vf3_u003b((¶m_166)); + let _e405 = momentCorner; + param_167 = _e405; + let _e406 = depthTile; + param_168 = _e406; + param_169 = _e404; + let _e407 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_167), (¶m_168), (¶m_169)); + moments_3 = _e407.xy; chebyshev = 1f; - let _e408 = distance_4; - let _e410 = moments_3[0u]; - if (_e408 > _e410) { - let _e413 = moments_3[1u]; - let _e415 = moments_3[0u]; - let _e417 = moments_3[0u]; - variance_1 = max((_e413 - (_e415 * _e417)), 0.000001f); - let _e421 = distance_4; - let _e423 = moments_3[0u]; - difference = (_e421 - _e423); - let _e425 = variance_1; + let _e409 = distance_4; + let _e411 = moments_3[0u]; + if (_e409 > _e411) { + let _e414 = moments_3[1u]; + let _e416 = moments_3[0u]; + let _e418 = moments_3[0u]; + variance_1 = max((_e414 - (_e416 * _e418)), 0.000001f); + let _e422 = distance_4; + let _e424 = moments_3[0u]; + difference = (_e422 - _e424); let _e426 = variance_1; - let _e427 = difference; + let _e427 = variance_1; let _e428 = difference; - chebyshev = (_e425 / (_e426 + (_e427 * _e428))); - let _e432 = chebyshev; + let _e429 = difference; + chebyshev = (_e426 / (_e427 + (_e428 * _e429))); let _e433 = chebyshev; - let _e435 = chebyshev; - chebyshev = ((_e432 * _e433) * _e435); + let _e434 = chebyshev; + let _e436 = chebyshev; + chebyshev = ((_e433 * _e434) * _e436); } - let _e437 = probeWeight; - let _e438 = chebyshev; - probeWeight = max((_e437 * max(_e438, 0.05f)), 0.000001f); - let _e442 = probeWeight; - if (_e442 < 0.2f) { - let _e444 = probeWeight; + let _e438 = probeWeight; + let _e439 = chebyshev; + probeWeight = max((_e438 * max(_e439, 0.05f)), 0.000001f); + let _e443 = probeWeight; + if (_e443 < 0.2f) { let _e445 = probeWeight; - let _e448 = probeWeight; - probeWeight = (_e448 * ((_e444 * _e445) * 25f)); + let _e446 = probeWeight; + let _e449 = probeWeight; + probeWeight = (_e449 * ((_e445 * _e446) * 25f)); } - let _e450 = probeWeight; - let _e451 = weight_2; - weight_2 = (_e451 * _e450); - } - let _e453 = unit; - param_170 = _e453; - let _e454 = ProbeOctahedral_u0028_vf3_u003b((¶m_170)); - let _e455 = irradianceCorner; - param_171 = _e455; - let _e456 = irradianceTile; - param_172 = _e456; - param_173 = _e454; - let _e457 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_171), (¶m_172), (¶m_173)); - let _e459 = weight_2; - let _e461 = total_2; - total_2 = (_e461 + (_e457.xyz * _e459)); - let _e463 = weight_2; - let _e464 = weights; - weights = (_e464 + _e463); + let _e451 = probeWeight; + let _e452 = weight_2; + weight_2 = (_e452 * _e451); + } + let _e454 = unit; + param_170 = _e454; + let _e455 = ProbeOctahedral_u0028_vf3_u003b((¶m_170)); + let _e456 = irradianceCorner; + param_171 = _e456; + let _e457 = irradianceTile; + param_172 = _e457; + param_173 = _e455; + let _e458 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_171), (¶m_172), (¶m_173)); + let _e460 = weight_2; + let _e462 = total_2; + total_2 = (_e462 + (_e458.xyz * _e460)); + let _e464 = weight_2; + let _e465 = weights; + weights = (_e465 + _e464); continue; } else { break; } continuing { - let _e466 = corner_1; - corner_1 = (_e466 + 1i); + let _e467 = corner_1; + corner_1 = (_e467 + 1i); } } - let _e468 = weights; - if (_e468 > 0f) { - let _e470 = total_2; - let _e471 = weights; - local_27 = (_e470 / vec3(_e471)); + let _e469 = weights; + if (_e469 > 0f) { + let _e471 = total_2; + let _e472 = weights; + local_27 = (_e471 / vec3(_e472)); } else { local_27 = vec3(0f, 0f, 0f); } - let _e474 = local_27; - return _e474; + let _e475 = local_27; + return _e475; } fn IrradianceEnabled_u0028_() -> bool { - let _e197 = irradiance_info.origin[3u]; - return (_e197 > 0.5f); + let _e198 = irradiance_info.origin[3u]; + return (_e198 > 0.5f); } fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_11: ptr) { @@ -15054,33 +15120,33 @@ fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_178: Surface; var param_179: Surface; - let _e202 = IrradianceEnabled_u0028_(); - if _e202 { - let _e203 = v_world_position_1; - param_174 = _e203; - let _e205 = (*s_11).n; - param_175 = _e205; - let _e207 = (*s_11).v; - param_176 = _e207; - let _e208 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_174), (¶m_175), (¶m_176)); - let _e211 = frag_info.material[2u]; - (*s_11).ambient = (_e208 * _e211); - } - let _e214 = (*s_11); - param_177 = _e214; + let _e203 = IrradianceEnabled_u0028_(); + if _e203 { + let _e204 = v_world_position_1; + param_174 = _e204; + let _e206 = (*s_11).n; + param_175 = _e206; + let _e208 = (*s_11).v; + param_176 = _e208; + let _e209 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_174), (¶m_175), (¶m_176)); + let _e212 = frag_info.material[2u]; + (*s_11).ambient = (_e209 * _e212); + } + let _e215 = (*s_11); + param_177 = _e215; ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_177)); - let _e215 = param_177; - (*s_11) = _e215; - let _e216 = (*s_11); - param_178 = _e216; + let _e216 = param_177; + (*s_11) = _e216; + let _e217 = (*s_11); + param_178 = _e217; ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_178)); - let _e217 = param_178; - (*s_11) = _e217; - let _e218 = (*s_11); - param_179 = _e218; + let _e218 = param_178; + (*s_11) = _e218; + let _e219 = (*s_11); + param_179 = _e219; ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_179)); - let _e219 = param_179; - (*s_11) = _e219; + let _e220 = param_179; + (*s_11) = _e220; return; } @@ -15093,78 +15159,78 @@ fn ReadSurface_u0028_() -> Surface { var anchored: vec3; var noise_1: f32; - let _e202 = v_texcoord_1; - let _e203 = MaterialLodBias_u0028_(); - let _e204 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e202, _e203); - texel_6 = _e204; - let _e205 = texel_6; - param_180 = _e205.xyz; - let _e207 = SrgbToLinear_u0028_vf3_u003b((¶m_180)); - let _e209 = frag_info.base_color; - param_181 = _e209.xyz; - let _e211 = SrgbToLinear_u0028_vf3_u003b((¶m_181)); - let _e213 = v_color_1; - s_12.albedo = ((_e207 * _e211) * _e213.xyz); - let _e218 = texel_6[3u]; - let _e221 = frag_info.base_color[3u]; - let _e224 = v_color_1[3u]; - s_12.alpha = ((_e218 * _e221) * _e224); - let _e228 = s_12.albedo; - g_albedo = _e228; - let _e231 = frag_info.material2_[0u]; - cutoff = _e231; - let _e232 = cutoff; - if (_e232 >= 0f) { - let _e235 = s_12.alpha; - let _e236 = cutoff; - if (_e235 < _e236) { + let _e203 = v_texcoord_1; + let _e204 = MaterialLodBias_u0028_(); + let _e205 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e203, _e204); + texel_6 = _e205; + let _e206 = texel_6; + param_180 = _e206.xyz; + let _e208 = SrgbToLinear_u0028_vf3_u003b((¶m_180)); + let _e210 = frag_info.base_color; + param_181 = _e210.xyz; + let _e212 = SrgbToLinear_u0028_vf3_u003b((¶m_181)); + let _e214 = v_color_1; + s_12.albedo = ((_e208 * _e212) * _e214.xyz); + let _e219 = texel_6[3u]; + let _e222 = frag_info.base_color[3u]; + let _e225 = v_color_1[3u]; + s_12.alpha = ((_e219 * _e222) * _e225); + let _e229 = s_12.albedo; + g_albedo = _e229; + let _e232 = frag_info.material2_[0u]; + cutoff = _e232; + let _e233 = cutoff; + if (_e233 >= 0f) { + let _e236 = s_12.alpha; + let _e237 = cutoff; + if (_e236 < _e237) { discard; } } else { - let _e238 = cutoff; - if (_e238 < -1.5f) { - let _e240 = v_world_position_1; - anchored = floor((_e240 * 16f)); - let _e243 = anchored; - noise_1 = fract((sin(dot(_e243, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e249 = s_12.alpha; - let _e250 = noise_1; - if (_e249 < _e250) { + let _e239 = cutoff; + if (_e239 < -1.5f) { + let _e241 = v_world_position_1; + anchored = floor((_e241 * 16f)); + let _e244 = anchored; + noise_1 = fract((sin(dot(_e244, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e250 = s_12.alpha; + let _e251 = noise_1; + if (_e250 < _e251) { discard; } } } - let _e252 = cutoff; - let _e254 = cutoff; - g_premultiply = ((_e252 < 0f) && (_e254 > -0.75f)); - let _e257 = v_normal_1; - s_12.n = normalize(_e257); - let _e260 = gl_FrontFacing_1; - if !(_e260) { - let _e263 = s_12.n; - s_12.n = -(_e263); - } - let _e267 = frag_info.camera_position; - let _e269 = v_world_position_1; - s_12.v = normalize((_e267.xyz - _e269)); - let _e274 = s_12.n; - let _e276 = s_12.v; - s_12.n_dot_v = max(dot(_e274, _e276), 0.0001f); - let _e282 = frag_info.material[0u]; - s_12.metallic = clamp(_e282, 0f, 1f); - let _e287 = frag_info.material[1u]; - s_12.roughness = clamp(_e287, 0.02f, 1f); - let _e291 = frag_info.ambient_ground; - let _e294 = frag_info.ambient_sky; - let _e298 = s_12.n[1u]; - let _e305 = frag_info.material[2u]; - s_12.ambient = (mix(_e291.xyz, _e294.xyz, vec3(((_e298 * 0.5f) + 0.5f))) * _e305); - let _e310 = frag_info.frame_params[0u]; - s_12.exposure = max(_e310, 0f); + let _e253 = cutoff; + let _e255 = cutoff; + g_premultiply = ((_e253 < 0f) && (_e255 > -0.75f)); + let _e258 = v_normal_1; + s_12.n = normalize(_e258); + let _e261 = gl_FrontFacing_1; + if !(_e261) { + let _e264 = s_12.n; + s_12.n = -(_e264); + } + let _e268 = frag_info.camera_position; + let _e270 = v_world_position_1; + s_12.v = normalize((_e268.xyz - _e270)); + let _e275 = s_12.n; + let _e277 = s_12.v; + s_12.n_dot_v = max(dot(_e275, _e277), 0.0001f); + let _e283 = frag_info.material[0u]; + s_12.metallic = clamp(_e283, 0f, 1f); + let _e288 = frag_info.material[1u]; + s_12.roughness = clamp(_e288, 0.02f, 1f); + let _e292 = frag_info.ambient_ground; + let _e295 = frag_info.ambient_sky; + let _e299 = s_12.n[1u]; + let _e306 = frag_info.material[2u]; + s_12.ambient = (mix(_e292.xyz, _e295.xyz, vec3(((_e299 * 0.5f) + 0.5f))) * _e306); + let _e311 = frag_info.frame_params[0u]; + s_12.exposure = max(_e311, 0f); s_12.occlusion = 1f; s_12.emissive = vec3(0f, 0f, 0f); - let _e315 = s_12; - return _e315; + let _e316 = s_12; + return _e316; } fn main_1() { @@ -15201,112 +15267,113 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e221 = ReadSurface_u0028_(); - s_13 = _e221; - let _e222 = s_13; - param_182 = _e222; + light_transmittance = vec3(1f, 1f, 1f); + let _e222 = ReadSurface_u0028_(); + s_13 = _e222; + let _e223 = s_13; + param_182 = _e223; ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_182)); - let _e223 = param_182; - s_13 = _e223; - let _e224 = s_13; - param_183 = _e224; + let _e224 = param_182; + s_13 = _e224; + let _e225 = s_13; + param_183 = _e225; ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_183)); - let _e225 = param_183; - s_13 = _e225; - let _e227 = s_13.metallic; - metallic = clamp(_e227, 0f, 1f); - let _e230 = s_13.albedo; - let _e231 = metallic; - diffuseColor_1 = (_e230 * (1f - _e231)); - let _e234 = EonDiffuse_u0028_(); - if _e234 { - let _e235 = diffuseColor_1; - param_184 = _e235; - let _e237 = s_13.roughness; - param_185 = _e237; - let _e239 = s_13.n_dot_v; - param_186 = _e239; - let _e240 = EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_184), (¶m_185), (¶m_186)); - diffuseColor_1 = _e240; - } - let _e241 = diffuseColor_1; - let _e243 = s_13.ambient; - let _e246 = s_13.occlusion; - ambient = ((_e241 * _e243) * _e246); - let _e250 = frag_info.frame_params[3u]; - levels = _e250; - let _e251 = levels; - if (_e251 > 0f) { - let _e254 = s_13.albedo; - let _e255 = metallic; - f0_3 = mix(vec3(0.04f, 0.04f, 0.04f), _e254, vec3(_e255)); - let _e259 = s_13.v; - let _e262 = s_13.n; - reflected = reflect(-(_e259), _e262); - let _e265 = s_13.n; - let _e266 = levels; - let _e267 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e265, _e266); - irradiance = _e267.xyz; - let _e269 = reflected; - let _e271 = s_13.roughness; - let _e272 = levels; - let _e274 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e269, (_e271 * _e272)); - prefiltered = _e274.xyz; - let _e277 = s_13.roughness; - param_187 = _e277; - let _e279 = s_13.n_dot_v; - param_188 = _e279; - let _e280 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_187), (¶m_188)); - ab_1 = _e280; - let _e281 = f0_3; - let _e283 = ab_1[0u]; - let _e286 = ab_1[1u]; - single_1 = ((_e281 * _e283) + vec3(_e286)); - let _e289 = prefiltered; - let _e290 = single_1; - specular_1 = (_e289 * _e290); - let _e292 = EnergyCompensation_u0028_(); - if _e292 { - let _e294 = ab_1[0u]; - let _e296 = ab_1[1u]; - missed = (1f - (_e294 + _e296)); - let _e299 = f0_3; + let _e226 = param_183; + s_13 = _e226; + let _e228 = s_13.metallic; + metallic = clamp(_e228, 0f, 1f); + let _e231 = s_13.albedo; + let _e232 = metallic; + diffuseColor_1 = (_e231 * (1f - _e232)); + let _e235 = EonDiffuse_u0028_(); + if _e235 { + let _e236 = diffuseColor_1; + param_184 = _e236; + let _e238 = s_13.roughness; + param_185 = _e238; + let _e240 = s_13.n_dot_v; + param_186 = _e240; + let _e241 = EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_184), (¶m_185), (¶m_186)); + diffuseColor_1 = _e241; + } + let _e242 = diffuseColor_1; + let _e244 = s_13.ambient; + let _e247 = s_13.occlusion; + ambient = ((_e242 * _e244) * _e247); + let _e251 = frag_info.frame_params[3u]; + levels = _e251; + let _e252 = levels; + if (_e252 > 0f) { + let _e255 = s_13.albedo; + let _e256 = metallic; + f0_3 = mix(vec3(0.04f, 0.04f, 0.04f), _e255, vec3(_e256)); + let _e260 = s_13.v; + let _e263 = s_13.n; + reflected = reflect(-(_e260), _e263); + let _e266 = s_13.n; + let _e267 = levels; + let _e268 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e266, _e267); + irradiance = _e268.xyz; + let _e270 = reflected; + let _e272 = s_13.roughness; + let _e273 = levels; + let _e275 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e270, (_e272 * _e273)); + prefiltered = _e275.xyz; + let _e278 = s_13.roughness; + param_187 = _e278; + let _e280 = s_13.n_dot_v; + param_188 = _e280; + let _e281 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_187), (¶m_188)); + ab_1 = _e281; + let _e282 = f0_3; + let _e284 = ab_1[0u]; + let _e287 = ab_1[1u]; + single_1 = ((_e282 * _e284) + vec3(_e287)); + let _e290 = prefiltered; + let _e291 = single_1; + specular_1 = (_e290 * _e291); + let _e293 = EnergyCompensation_u0028_(); + if _e293 { + let _e295 = ab_1[0u]; + let _e297 = ab_1[1u]; + missed = (1f - (_e295 + _e297)); let _e300 = f0_3; - average_2 = (_e299 + ((vec3(1f, 1f, 1f) - _e300) / vec3(21f))); - let _e305 = single_1; - let _e306 = average_2; - let _e308 = missed; - let _e309 = average_2; - multiple = ((_e305 * _e306) / (vec3(1f, 1f, 1f) - (_e309 * _e308))); - let _e313 = multiple; - let _e314 = missed; - let _e316 = irradiance; - let _e318 = specular_1; - specular_1 = (_e318 + ((_e313 * _e314) * _e316)); - } - let _e320 = diffuseColor_1; - let _e321 = irradiance; - let _e323 = specular_1; - let _e327 = frag_info.material[2u]; - let _e330 = s_13.occlusion; - ambient = ((((_e320 * _e321) + _e323) * _e327) * _e330); - } - let _e332 = diffuseColor_1; - let _e333 = SampleLightmap_u0028_(); - let _e336 = s_13.occlusion; - let _e338 = ambient; - ambient = (_e338 + ((_e332 * _e333) * _e336)); - let _e340 = s_13; - param_189 = _e340; - let _e341 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_189)); - let _e343 = s_13.occlusion; - let _e345 = ambient; - let _e348 = s_13.emissive; - param_190 = (((_e341 * _e343) + _e345) + _e348); - let _e351 = s_13.alpha; - param_191 = _e351; - let _e353 = s_13.roughness; - param_192 = _e353; + let _e301 = f0_3; + average_2 = (_e300 + ((vec3(1f, 1f, 1f) - _e301) / vec3(21f))); + let _e306 = single_1; + let _e307 = average_2; + let _e309 = missed; + let _e310 = average_2; + multiple = ((_e306 * _e307) / (vec3(1f, 1f, 1f) - (_e310 * _e309))); + let _e314 = multiple; + let _e315 = missed; + let _e317 = irradiance; + let _e319 = specular_1; + specular_1 = (_e319 + ((_e314 * _e315) * _e317)); + } + let _e321 = diffuseColor_1; + let _e322 = irradiance; + let _e324 = specular_1; + let _e328 = frag_info.material[2u]; + let _e331 = s_13.occlusion; + ambient = ((((_e321 * _e322) + _e324) * _e328) * _e331); + } + let _e333 = diffuseColor_1; + let _e334 = SampleLightmap_u0028_(); + let _e337 = s_13.occlusion; + let _e339 = ambient; + ambient = (_e339 + ((_e333 * _e334) * _e337)); + let _e341 = s_13; + param_189 = _e341; + let _e342 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_189)); + let _e344 = s_13.occlusion; + let _e346 = ambient; + let _e349 = s_13.emissive; + param_190 = (((_e342 * _e344) + _e346) + _e349); + let _e352 = s_13.alpha; + param_191 = _e352; + let _e354 = s_13.roughness; + param_192 = _e354; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_190), (¶m_191), (¶m_192)); return; } @@ -15762,6 +15829,7 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; +var light_transmittance: vec3; var g_f0_dielectric: vec3; var g_f90_: f32; var g_coat: f32; @@ -15871,10 +15939,10 @@ var scene_colour_texture_tex: texture_2d; var scene_colour_texture_smp: sampler; fn ViewDepth_u0028_() -> f32 { - let _e261 = v_world_position_1; - let _e263 = fog_info.eye; - let _e267 = fog_info.forward; - return dot((_e261 - _e263.xyz), _e267.xyz); + let _e262 = v_world_position_1; + let _e264 = fog_info.eye; + let _e268 = fog_info.forward; + return dot((_e262 - _e264.xyz), _e268.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -15883,22 +15951,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e266 = ViewDepth_u0028_(); - z = abs(_e266); - let _e268 = z; - near = (_e268 / 5f); - let _e270 = z; - far = (_e270 / 200f); - let _e272 = far; + let _e267 = ViewDepth_u0028_(); + z = abs(_e267); + let _e269 = z; + near = (_e269 / 5f); + let _e271 = z; + far = (_e271 / 200f); let _e273 = far; - let _e275 = far; - far3_ = ((_e272 * _e273) * _e275); - let _e277 = (*alpha); - let _e278 = near; + let _e274 = far; + let _e276 = far; + far3_ = ((_e273 * _e274) * _e276); + let _e278 = (*alpha); let _e279 = near; - let _e282 = far3_; + let _e280 = near; let _e283 = far3_; - return (_e277 * clamp((10f / ((0.00001f + (_e278 * _e279)) + (_e282 * _e283))), 0.01f, 3000f)); + let _e284 = far3_; + return (_e278 * clamp((10f / ((0.00001f + (_e279 * _e280)) + (_e283 * _e284))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -15910,39 +15978,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e270 = fog_info.forward[3u]; - mode = _e270; - let _e271 = mode; - if (_e271 > 0.5f) { - let _e274 = frag_color[3u]; - alpha_1 = _e274; - let _e275 = alpha_1; - param = _e275; - let _e276 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e276; - let _e277 = frag_color; - let _e279 = weight; - let _e280 = (_e277.xyz * _e279); - let _e281 = alpha_1; - let _e282 = weight; - accumulate = vec4(_e280.x, _e280.y, _e280.z, (_e281 * _e282)); - let _e288 = mode; - let _e290 = mode; - revealage = ((_e288 > 1.5f) && (_e290 < 2.5f)); - let _e293 = revealage; - if _e293 { - let _e294 = alpha_1; - local = vec4(_e294); + let _e271 = fog_info.forward[3u]; + mode = _e271; + let _e272 = mode; + if (_e272 > 0.5f) { + let _e275 = frag_color[3u]; + alpha_1 = _e275; + let _e276 = alpha_1; + param = _e276; + let _e277 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e277; + let _e278 = frag_color; + let _e280 = weight; + let _e281 = (_e278.xyz * _e280); + let _e282 = alpha_1; + let _e283 = weight; + accumulate = vec4(_e281.x, _e281.y, _e281.z, (_e282 * _e283)); + let _e289 = mode; + let _e291 = mode; + revealage = ((_e289 > 1.5f) && (_e291 < 2.5f)); + let _e294 = revealage; + if _e294 { + let _e295 = alpha_1; + local = vec4(_e295); } else { - let _e296 = accumulate; - local = _e296; + let _e297 = accumulate; + local = _e297; } - let _e297 = local; - frag_color = _e297; - let _e298 = mode; - if (_e298 > 2.5f) { - let _e300 = alpha_1; - frag_surface = vec4(_e300); + let _e298 = local; + frag_color = _e298; + let _e299 = mode; + if (_e299 > 2.5f) { + let _e301 = alpha_1; + frag_surface = vec4(_e301); } } return; @@ -15951,29 +16019,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e264 = (*n)[0u]; - let _e267 = (*n)[1u]; - let _e271 = (*n)[2u]; - let _e274 = (*n); - (*n) = (_e274 / vec3(((abs(_e264) + abs(_e267)) + abs(_e271)))); - let _e277 = (*n); - e = _e277.xy; - let _e280 = (*n)[2u]; - if (_e280 < 0f) { - let _e282 = (*n); - let _e288 = (*n)[0u]; - let _e292 = (*n)[1u]; - e = ((vec2(1f) - abs(_e282.yx)) * vec2(select(-1f, 1f, (_e288 >= 0f)), select(-1f, 1f, (_e292 >= 0f)))); + let _e265 = (*n)[0u]; + let _e268 = (*n)[1u]; + let _e272 = (*n)[2u]; + let _e275 = (*n); + (*n) = (_e275 / vec3(((abs(_e265) + abs(_e268)) + abs(_e272)))); + let _e278 = (*n); + e = _e278.xy; + let _e281 = (*n)[2u]; + if (_e281 < 0f) { + let _e283 = (*n); + let _e289 = (*n)[0u]; + let _e293 = (*n)[1u]; + e = ((vec2(1f) - abs(_e283.yx)) * vec2(select(-1f, 1f, (_e289 >= 0f)), select(-1f, 1f, (_e293 >= 0f)))); } - let _e297 = e; - return ((_e297 * 0.5f) + vec2(0.5f)); + let _e298 = e; + return ((_e298 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e262 = (*linear); - let _e264 = (*linear); - let _e268 = (*linear); - return mix((_e262 * 12.92f), ((pow(_e264, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e268)); + let _e263 = (*linear); + let _e265 = (*linear); + let _e269 = (*linear); + return mix((_e263 * 12.92f), ((pow(_e265, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e269)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -15981,57 +16049,57 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e265 = g_albedo; - param_1 = clamp(_e265, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e267 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e267.x, _e267.y, _e267.z, 1f); - let _e272 = g_debug_surface_on; - if _e272 { - let _e273 = g_debug_surface; - let _e274 = ViewDepth_u0028_(); - frag_surface = vec4(_e273.x, _e273.y, _e273.z, _e274); + let _e266 = g_albedo; + param_1 = clamp(_e266, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e268 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e268.x, _e268.y, _e268.z, 1f); + let _e273 = g_debug_surface_on; + if _e273 { + let _e274 = g_debug_surface; + let _e275 = ViewDepth_u0028_(); + frag_surface = vec4(_e274.x, _e274.y, _e274.z, _e275); return; } - let _e279 = v_normal_1; - geometric = normalize(_e279); - let _e281 = gl_FrontFacing_1; - if !(_e281) { - let _e283 = geometric; - geometric = -(_e283); - } - let _e285 = geometric; - param_2 = _e285; - let _e286 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e287 = (*roughness); - let _e289 = ViewDepth_u0028_(); - frag_surface = vec4(_e286.x, _e286.y, clamp(_e287, 0f, 1f), _e289); + let _e280 = v_normal_1; + geometric = normalize(_e280); + let _e282 = gl_FrontFacing_1; + if !(_e282) { + let _e284 = geometric; + geometric = -(_e284); + } + let _e286 = geometric; + param_2 = _e286; + let _e287 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e288 = (*roughness); + let _e290 = ViewDepth_u0028_(); + frag_surface = vec4(_e287.x, _e287.y, clamp(_e288, 0f, 1f), _e290); return; } fn EyeDistance_u0028_() -> f32 { - let _e261 = v_world_position_1; - let _e263 = fog_info.eye; - return distance(_e261, _e263.xyz); + let _e262 = v_world_position_1; + let _e264 = fog_info.eye; + return distance(_e262, _e264.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; - let _e266 = fog_info.fog[3u]; - density = _e266; - let _e267 = density; - if (_e267 <= 0f) { - let _e269 = (*color); - return _e269; + let _e267 = fog_info.fog[3u]; + density = _e267; + let _e268 = density; + if (_e268 <= 0f) { + let _e270 = (*color); + return _e270; } - let _e270 = EyeDistance_u0028_(); - d = _e270; - let _e272 = fog_info.fog; - let _e274 = (*color); - let _e275 = density; - let _e277 = d; - return mix(_e272.xyz, _e274, vec3(clamp(exp((-(_e275) * _e277)), 0f, 1f))); + let _e271 = EyeDistance_u0028_(); + d = _e271; + let _e273 = fog_info.fog; + let _e275 = (*color); + let _e276 = density; + let _e278 = d; + return mix(_e273.xyz, _e275, vec3(clamp(exp((-(_e276) * _e278)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -16039,18 +16107,18 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e267 = g_premultiply; - let _e268 = (*alpha_2); - weight_1 = select(1f, _e268, _e267); - let _e270 = (*linearColor); - param_3 = _e270; - let _e271 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e272 = weight_1; - let _e273 = (_e271 * _e272); - let _e274 = (*alpha_2); - frag_color = vec4(_e273.x, _e273.y, _e273.z, _e274); - let _e279 = (*roughness_1); - param_4 = _e279; + let _e268 = g_premultiply; + let _e269 = (*alpha_2); + weight_1 = select(1f, _e269, _e268); + let _e271 = (*linearColor); + param_3 = _e271; + let _e272 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e273 = weight_1; + let _e274 = (_e272 * _e273); + let _e275 = (*alpha_2); + frag_color = vec4(_e274.x, _e274.y, _e274.z, _e275); + let _e280 = (*roughness_1); + param_4 = _e280; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; @@ -16061,42 +16129,42 @@ fn V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b(n_dot_v: ptr, alpha_4: ptr) -> f32 { var a: f32; var k: f32; - let _e265 = (*n_dot_h); - let _e266 = (*alpha_4); - a = (_e265 * _e266); - let _e268 = (*alpha_4); - let _e269 = (*n_dot_h); + let _e266 = (*n_dot_h); + let _e267 = (*alpha_4); + a = (_e266 * _e267); + let _e269 = (*alpha_4); let _e270 = (*n_dot_h); - let _e273 = a; + let _e271 = (*n_dot_h); let _e274 = a; - k = (_e268 / max(((1f - (_e269 * _e270)) + (_e273 * _e274)), 0.0000001f)); - let _e279 = k; + let _e275 = a; + k = (_e269 / max(((1f - (_e270 * _e271)) + (_e274 * _e275)), 0.0000001f)); let _e280 = k; - return ((_e279 * _e280) * 0.31830987f); + let _e281 = k; + return ((_e280 * _e281) * 0.31830987f); } fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>) -> vec3 { @@ -16105,37 +16173,37 @@ fn LtcEdge_u0028_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr; - let _e267 = (*a_1); - let _e268 = (*b); - axis = cross(_e267, _e268); - let _e270 = axis; - len = length(_e270); - let _e272 = (*a_1); - let _e273 = (*b); - angle = acos(clamp(dot(_e272, _e273), -1f, 1f)); - let _e277 = len; - if (_e277 > 0.000001f) { - let _e279 = axis; - let _e280 = angle; - let _e281 = len; - local_1 = (_e279 * (_e280 / (_e281 * 6.2831855f))); + let _e268 = (*a_1); + let _e269 = (*b); + axis = cross(_e268, _e269); + let _e271 = axis; + len = length(_e271); + let _e273 = (*a_1); + let _e274 = (*b); + angle = acos(clamp(dot(_e273, _e274), -1f, 1f)); + let _e278 = len; + if (_e278 > 0.000001f) { + let _e280 = axis; + let _e281 = angle; + let _e282 = len; + local_1 = (_e280 * (_e281 / (_e282 * 6.2831855f))); } else { local_1 = vec3(0f, 0f, 0f); } - let _e285 = local_1; - return _e285; + let _e286 = local_1; + return _e286; } fn LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b(x: ptr, y: ptr, table: ptr) -> vec2 { var inTable: vec2; - let _e265 = (*x); - let _e266 = (*y); - inTable = ((vec2(_e265, _e266) * 0.984375f) + vec2(0.0078125f)); - let _e272 = inTable[0u]; - let _e274 = inTable[1u]; - let _e275 = (*table); - return vec2(_e272, ((_e274 + _e275) * 0.5f)); + let _e266 = (*x); + let _e267 = (*y); + inTable = ((vec2(_e266, _e267) * 0.984375f) + vec2(0.0078125f)); + let _e273 = inTable[0u]; + let _e275 = inTable[1u]; + let _e276 = (*table); + return vec2(_e273, ((_e275 + _e276) * 0.5f)); } fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: ptr>, v: ptr>, roughness_2: ptr, corners: ptr, 4>>) -> vec3 { @@ -16174,126 +16242,126 @@ fn LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b(n_1: var param_20: f32; var param_21: f32; - let _e299 = (*roughness_2); - let _e301 = (*n_1); - let _e302 = (*v); - uv = vec2(clamp(_e299, 0f, 1f), sqrt(clamp((1f - dot(_e301, _e302)), 0f, 1f))); - let _e309 = uv[0u]; - param_5 = _e309; - let _e311 = uv[1u]; - param_6 = _e311; + let _e300 = (*roughness_2); + let _e302 = (*n_1); + let _e303 = (*v); + uv = vec2(clamp(_e300, 0f, 1f), sqrt(clamp((1f - dot(_e302, _e303)), 0f, 1f))); + let _e310 = uv[0u]; + param_5 = _e310; + let _e312 = uv[1u]; + param_6 = _e312; param_7 = 0f; - let _e312 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_5), (¶m_6), (¶m_7)); - let _e313 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e312, 0f); - inverse = _e313; - let _e315 = uv[0u]; - param_8 = _e315; - let _e317 = uv[1u]; - param_9 = _e317; + let _e313 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_5), (¶m_6), (¶m_7)); + let _e314 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e313, 0f); + inverse = _e314; + let _e316 = uv[0u]; + param_8 = _e316; + let _e318 = uv[1u]; + param_9 = _e318; param_10 = 1f; - let _e318 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_8), (¶m_9), (¶m_10)); - let _e319 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e318, 0f); - fit = _e319; - let _e320 = (*v); - let _e321 = (*n_1); - let _e322 = (*v); - let _e323 = (*n_1); - along = (_e320 - (_e321 * dot(_e322, _e323))); - let _e327 = along; - alongLength = length(_e327); - let _e329 = alongLength; - if (_e329 > 0.00001f) { - let _e331 = along; - let _e332 = alongLength; - local_2 = (_e331 / vec3(_e332)); + let _e319 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_8), (¶m_9), (¶m_10)); + let _e320 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e319, 0f); + fit = _e320; + let _e321 = (*v); + let _e322 = (*n_1); + let _e323 = (*v); + let _e324 = (*n_1); + along = (_e321 - (_e322 * dot(_e323, _e324))); + let _e328 = along; + alongLength = length(_e328); + let _e330 = alongLength; + if (_e330 > 0.00001f) { + let _e332 = along; + let _e333 = alongLength; + local_2 = (_e332 / vec3(_e333)); } else { - let _e335 = (*n_1); - let _e337 = (*n_1)[2u]; - local_2 = normalize(cross(_e335, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e337) < 0.999f))))); - } - let _e344 = local_2; - t1_ = _e344; - let _e345 = (*n_1); - let _e346 = t1_; - t2_ = cross(_e345, _e346); - let _e349 = inverse[0u]; - let _e351 = inverse[1u]; - let _e352 = vec3(_e349, 0f, _e351); - let _e354 = inverse[2u]; - let _e356 = inverse[3u]; - let _e357 = vec3(_e354, 0f, _e356); - minv = mat3x3(vec3(_e352.x, _e352.y, _e352.z), vec3(vec3(0f, 1f, 0f).x, vec3(0f, 1f, 0f).y, vec3(0f, 1f, 0f).z), vec3(_e357.x, _e357.y, _e357.z)); + let _e336 = (*n_1); + let _e338 = (*n_1)[2u]; + local_2 = normalize(cross(_e336, select(vec3(1f, 0f, 0f), vec3(0f, 0f, 1f), vec3((abs(_e338) < 0.999f))))); + } + let _e345 = local_2; + t1_ = _e345; + let _e346 = (*n_1); + let _e347 = t1_; + t2_ = cross(_e346, _e347); + let _e350 = inverse[0u]; + let _e352 = inverse[1u]; + let _e353 = vec3(_e350, 0f, _e352); + let _e355 = inverse[2u]; + let _e357 = inverse[3u]; + let _e358 = vec3(_e355, 0f, _e357); + minv = mat3x3(vec3(_e353.x, _e353.y, _e353.z), vec3(vec3(0f, 1f, 0f).x, vec3(0f, 1f, 0f).y, vec3(0f, 1f, 0f).z), vec3(_e358.x, _e358.y, _e358.z)); i = 0i; loop { - let _e371 = i; - if (_e371 < 4i) { - let _e373 = i; - let _e375 = (*corners)[_e373]; - p = _e375; - let _e376 = i; - let _e377 = minv; - let _e378 = p; - let _e379 = t1_; - let _e381 = p; - let _e382 = t2_; - let _e384 = p; - let _e385 = (*n_1); - l[_e376] = normalize((_e377 * vec3(dot(_e378, _e379), dot(_e381, _e382), dot(_e384, _e385)))); + let _e372 = i; + if (_e372 < 4i) { + let _e374 = i; + let _e376 = (*corners)[_e374]; + p = _e376; + let _e377 = i; + let _e378 = minv; + let _e379 = p; + let _e380 = t1_; + let _e382 = p; + let _e383 = t2_; + let _e385 = p; + let _e386 = (*n_1); + l[_e377] = normalize((_e378 * vec3(dot(_e379, _e380), dot(_e382, _e383), dot(_e385, _e386)))); continue; } else { break; } continuing { - let _e391 = i; - i = (_e391 + 1i); - } - } - let _e394 = l[0i]; - param_11 = _e394; - let _e396 = l[1i]; - param_12 = _e396; - let _e397 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_11), (¶m_12)); - let _e399 = l[1i]; - param_13 = _e399; - let _e401 = l[2i]; - param_14 = _e401; - let _e402 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_13), (¶m_14)); - let _e405 = l[2i]; - param_15 = _e405; - let _e407 = l[3i]; - param_16 = _e407; - let _e408 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_15), (¶m_16)); - let _e411 = l[3i]; - param_17 = _e411; - let _e413 = l[0i]; - param_18 = _e413; - let _e414 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_17), (¶m_18)); - f = -((((_e397 + _e402) + _e408) + _e414)); - let _e417 = f; - len_1 = length(_e417); - let _e419 = len_1; - if (_e419 > 0.000000001f) { - let _e422 = f[2u]; - let _e423 = len_1; - local_3 = (_e422 / _e423); + let _e392 = i; + i = (_e392 + 1i); + } + } + let _e395 = l[0i]; + param_11 = _e395; + let _e397 = l[1i]; + param_12 = _e397; + let _e398 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_11), (¶m_12)); + let _e400 = l[1i]; + param_13 = _e400; + let _e402 = l[2i]; + param_14 = _e402; + let _e403 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_13), (¶m_14)); + let _e406 = l[2i]; + param_15 = _e406; + let _e408 = l[3i]; + param_16 = _e408; + let _e409 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_15), (¶m_16)); + let _e412 = l[3i]; + param_17 = _e412; + let _e414 = l[0i]; + param_18 = _e414; + let _e415 = LtcEdge_u0028_vf3_u003b_vf3_u003b((¶m_17), (¶m_18)); + f = -((((_e398 + _e403) + _e409) + _e415)); + let _e418 = f; + len_1 = length(_e418); + let _e420 = len_1; + if (_e420 > 0.000000001f) { + let _e423 = f[2u]; + let _e424 = len_1; + local_3 = (_e423 / _e424); } else { local_3 = 0f; } - let _e425 = local_3; - z_1 = _e425; - let _e426 = z_1; - let _e429 = len_1; - param_19 = ((_e426 * 0.5f) + 0.5f); - param_20 = clamp(_e429, 0f, 1f); + let _e426 = local_3; + z_1 = _e426; + let _e427 = z_1; + let _e430 = len_1; + param_19 = ((_e427 * 0.5f) + 0.5f); + param_20 = clamp(_e430, 0f, 1f); param_21 = 1f; - let _e431 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_19), (¶m_20), (¶m_21)); - let _e432 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e431, 0f); - sphere = _e432.w; - let _e434 = len_1; - let _e435 = sphere; - let _e439 = fit[0u]; - let _e441 = fit[1u]; - return vec3(max((_e434 * _e435), 0f), _e439, _e441); + let _e432 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_19), (¶m_20), (¶m_21)); + let _e433 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e432, 0f); + sphere = _e433.w; + let _e435 = len_1; + let _e436 = sphere; + let _e440 = fit[0u]; + let _e442 = fit[1u]; + return vec3(max((_e435 * _e436), 0f), _e440, _e442); } fn CoatLobe_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s: ptr, light: ptr) -> f32 { @@ -16315,115 +16383,115 @@ fn CoatLobe_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u0 var param_30: f32; var f_1: f32; - let _e281 = (*light).integrated; - if (_e281 > 0.5f) { - let _e283 = g_coat_n; - param_22 = _e283; - let _e285 = (*s).v; - param_23 = _e285; - let _e286 = g_coat_roughness; - param_24 = _e286; - let _e287 = g_rect_corners; - param_25 = _e287; - let _e288 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_22), (¶m_23), (¶m_24), (¶m_25)); - ltc = _e288; - let _e290 = ltc[0u]; - let _e292 = ltc[1u]; - let _e295 = ltc[2u]; - let _e301 = frag_info.material[3u]; - let _e304 = (*light).n_dot_l; - return (((_e290 * ((0.04f * _e292) + (0.96f * _e295))) * _e301) / max(_e304, 0.000001f)); - } - let _e307 = g_coat_roughness; - lobe = max(_e307, 0.045f); - let _e309 = lobe; + let _e282 = (*light).integrated; + if (_e282 > 0.5f) { + let _e284 = g_coat_n; + param_22 = _e284; + let _e286 = (*s).v; + param_23 = _e286; + let _e287 = g_coat_roughness; + param_24 = _e287; + let _e288 = g_rect_corners; + param_25 = _e288; + let _e289 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_22), (¶m_23), (¶m_24), (¶m_25)); + ltc = _e289; + let _e291 = ltc[0u]; + let _e293 = ltc[1u]; + let _e296 = ltc[2u]; + let _e302 = frag_info.material[3u]; + let _e305 = (*light).n_dot_l; + return (((_e291 * ((0.04f * _e293) + (0.96f * _e296))) * _e302) / max(_e305, 0.000001f)); + } + let _e308 = g_coat_roughness; + lobe = max(_e308, 0.045f); let _e310 = lobe; - alpha_5 = (_e309 * _e310); - let _e312 = g_coat_n; - let _e314 = (*light).l; - n_dot_l_1 = max(dot(_e312, _e314), 0f); - let _e317 = g_coat_n; - let _e319 = (*light).h; - n_dot_h_1 = max(dot(_e317, _e319), 0f); - let _e322 = n_dot_h_1; - param_26 = _e322; - let _e323 = alpha_5; - param_27 = _e323; - let _e324 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_26), (¶m_27)); - d_1 = _e324; - let _e325 = g_coat_n_dot_v; - param_28 = _e325; - let _e326 = n_dot_l_1; - param_29 = _e326; - let _e327 = alpha_5; - param_30 = _e327; - let _e328 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_28), (¶m_29), (¶m_30)); - vis = _e328; - let _e330 = (*light).v_dot_h; - f_1 = (0.04f + (0.96f * pow((1f - _e330), 5f))); - let _e335 = d_1; - let _e336 = vis; - let _e338 = f_1; - let _e342 = frag_info.material[3u]; - let _e344 = n_dot_l_1; - let _e347 = (*light).n_dot_l; - return (((((_e335 * _e336) * _e338) * _e342) * _e344) / max(_e347, 0.000001f)); + let _e311 = lobe; + alpha_5 = (_e310 * _e311); + let _e313 = g_coat_n; + let _e315 = (*light).l; + n_dot_l_1 = max(dot(_e313, _e315), 0f); + let _e318 = g_coat_n; + let _e320 = (*light).h; + n_dot_h_1 = max(dot(_e318, _e320), 0f); + let _e323 = n_dot_h_1; + param_26 = _e323; + let _e324 = alpha_5; + param_27 = _e324; + let _e325 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_26), (¶m_27)); + d_1 = _e325; + let _e326 = g_coat_n_dot_v; + param_28 = _e326; + let _e327 = n_dot_l_1; + param_29 = _e327; + let _e328 = alpha_5; + param_30 = _e328; + let _e329 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_28), (¶m_29), (¶m_30)); + vis = _e329; + let _e331 = (*light).v_dot_h; + f_1 = (0.04f + (0.96f * pow((1f - _e331), 5f))); + let _e336 = d_1; + let _e337 = vis; + let _e339 = f_1; + let _e343 = frag_info.material[3u]; + let _e345 = n_dot_l_1; + let _e348 = (*light).n_dot_l; + return (((((_e336 * _e337) * _e339) * _e343) * _e345) / max(_e348, 0.000001f)); } fn V_Neubelt_u0028_f1_u003b_f1_u003b(n_dot_v_1: ptr, n_dot_l_2: ptr) -> f32 { - let _e263 = (*n_dot_l_2); - let _e264 = (*n_dot_v_1); - let _e266 = (*n_dot_l_2); - let _e267 = (*n_dot_v_1); - return (1f / (4f * ((_e263 + _e264) - (_e266 * _e267)))); + let _e264 = (*n_dot_l_2); + let _e265 = (*n_dot_v_1); + let _e267 = (*n_dot_l_2); + let _e268 = (*n_dot_v_1); + return (1f / (4f * ((_e264 + _e265) - (_e267 * _e268)))); } fn D_Charlie_u0028_f1_u003b_f1_u003b(roughness_3: ptr, n_dot_h_2: ptr) -> f32 { var inv_alpha: f32; var sin2h: f32; - let _e265 = (*roughness_3); let _e266 = (*roughness_3); - inv_alpha = (1f / (_e265 * _e266)); - let _e269 = (*n_dot_h_2); + let _e267 = (*roughness_3); + inv_alpha = (1f / (_e266 * _e267)); let _e270 = (*n_dot_h_2); - sin2h = max((1f - (_e269 * _e270)), 0.0078125f); - let _e274 = inv_alpha; - let _e276 = sin2h; - let _e277 = inv_alpha; - return (((2f + _e274) * pow(_e276, (_e277 * 0.5f))) / 6.2831855f); + let _e271 = (*n_dot_h_2); + sin2h = max((1f - (_e270 * _e271)), 0.0078125f); + let _e275 = inv_alpha; + let _e277 = sin2h; + let _e278 = inv_alpha; + return (((2f + _e275) * pow(_e277, (_e278 * 0.5f))) / 6.2831855f); } fn FonAlbedo_u0028_f1_u003b_f1_u003b(mu: ptr, r: ptr) -> f32 { var m: f32; var g: f32; - let _e265 = (*mu); - m = (1f - _e265); - let _e267 = m; + let _e266 = (*mu); + m = (1f - _e266); let _e268 = m; let _e269 = m; let _e270 = m; - g = (_e267 * (0.05710853f + (_e268 * (0.49188188f + (_e269 * (-0.33218145f + (_e270 * 0.071443f))))))); - let _e278 = (*r); - let _e279 = g; - let _e282 = (*r); - return ((1f + (_e278 * _e279)) / (1f + (0.2877934f * _e282))); + let _e271 = m; + g = (_e268 * (0.05710853f + (_e269 * (0.49188188f + (_e270 * (-0.33218145f + (_e271 * 0.071443f))))))); + let _e279 = (*r); + let _e280 = g; + let _e283 = (*r); + return ((1f + (_e279 * _e280)) / (1f + (0.2877934f * _e283))); } fn EonMultiAlbedo_u0028_vf3_u003b_f1_u003b(rho: ptr>, average: ptr) -> vec3 { - let _e263 = (*rho); let _e264 = (*rho); - let _e266 = (*average); - let _e268 = (*rho); - let _e269 = (*average); - return (((_e263 * _e264) * _e266) / (vec3(1f, 1f, 1f) - (_e268 * (1f - _e269)))); + let _e265 = (*rho); + let _e267 = (*average); + let _e269 = (*rho); + let _e270 = (*average); + return (((_e264 * _e265) * _e267) / (vec3(1f, 1f, 1f) - (_e269 * (1f - _e270)))); } fn FonAverage_u0028_f1_u003b(r_1: ptr) -> f32 { - let _e262 = (*r_1); - let _e265 = (*r_1); - return ((1f + (0.07248821f * _e262)) / (1f + (0.2877934f * _e265))); + let _e263 = (*r_1); + let _e266 = (*r_1); + return ((1f + (0.07248821f * _e263)) / (1f + (0.2877934f * _e266))); } fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr>, r_2: ptr, mu_i: ptr, mu_o: ptr, l_dot_v: ptr) -> vec3 { @@ -16442,58 +16510,58 @@ fn EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(rho_1: ptr 0f) { - let _e287 = s_1; - let _e288 = (*mu_i); - let _e289 = (*mu_o); - local_4 = (_e287 / max(_e288, _e289)); + let _e281 = (*l_dot_v); + let _e282 = (*mu_i); + let _e283 = (*mu_o); + s_1 = (_e281 - (_e282 * _e283)); + let _e286 = s_1; + if (_e286 > 0f) { + let _e288 = s_1; + let _e289 = (*mu_i); + let _e290 = (*mu_o); + local_4 = (_e288 / max(_e289, _e290)); } else { - let _e292 = s_1; - local_4 = _e292; - } - let _e293 = local_4; - s_over_t = _e293; - let _e294 = (*r_2); - af = (1f / (1f + (0.2877934f * _e294))); - let _e298 = (*rho_1); - let _e299 = af; - let _e300 = (*r_2); - let _e301 = s_over_t; - single = (_e298 * (_e299 * (1f + (_e300 * _e301)))); - let _e306 = (*r_2); - param_31 = _e306; - let _e307 = FonAverage_u0028_f1_u003b((¶m_31)); - average_1 = _e307; - let _e308 = (*rho_1); - param_32 = _e308; - let _e309 = average_1; - param_33 = _e309; - let _e310 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_32), (¶m_33)); - let _e311 = (*mu_o); - param_34 = _e311; - let _e312 = (*r_2); - param_35 = _e312; - let _e313 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_34), (¶m_35)); - let _e316 = (*mu_i); - param_36 = _e316; - let _e317 = (*r_2); - param_37 = _e317; - let _e318 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_36), (¶m_37)); - let _e322 = average_1; - multi = (_e310 * ((max((1f - _e313), 0.0000001f) * max((1f - _e318), 0.0000001f)) / max((1f - _e322), 0.0000001f))); - let _e327 = single; - let _e328 = multi; - return ((_e327 + _e328) / vec3(3.1415927f)); + let _e293 = s_1; + local_4 = _e293; + } + let _e294 = local_4; + s_over_t = _e294; + let _e295 = (*r_2); + af = (1f / (1f + (0.2877934f * _e295))); + let _e299 = (*rho_1); + let _e300 = af; + let _e301 = (*r_2); + let _e302 = s_over_t; + single = (_e299 * (_e300 * (1f + (_e301 * _e302)))); + let _e307 = (*r_2); + param_31 = _e307; + let _e308 = FonAverage_u0028_f1_u003b((¶m_31)); + average_1 = _e308; + let _e309 = (*rho_1); + param_32 = _e309; + let _e310 = average_1; + param_33 = _e310; + let _e311 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_32), (¶m_33)); + let _e312 = (*mu_o); + param_34 = _e312; + let _e313 = (*r_2); + param_35 = _e313; + let _e314 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_34), (¶m_35)); + let _e317 = (*mu_i); + param_36 = _e317; + let _e318 = (*r_2); + param_37 = _e318; + let _e319 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_36), (¶m_37)); + let _e323 = average_1; + multi = (_e311 * ((max((1f - _e314), 0.0000001f) * max((1f - _e319), 0.0000001f)) / max((1f - _e323), 0.0000001f))); + let _e328 = single; + let _e329 = multi; + return ((_e328 + _e329) / vec3(3.1415927f)); } fn EonDiffuse_u0028_() -> bool { - let _e263 = frag_info.ambient_sky[3u]; - return (_e263 > 0.5f); + let _e264 = frag_info.ambient_sky[3u]; + return (_e264 > 0.5f); } fn EnvBrdf_u0028_f1_u003b_f1_u003b(roughness_4: ptr, n_dot_v_2: ptr) -> vec2 { @@ -16502,18 +16570,18 @@ fn EnvBrdf_u0028_f1_u003b_f1_u003b(roughness_4: ptr, n_dot_v_2: p var param_39: f32; var param_40: f32; - let _e267 = (*roughness_4); - let _e269 = (*n_dot_v_2); - param_38 = clamp(_e267, 0f, 1f); - param_39 = sqrt(clamp((1f - _e269), 0f, 1f)); + let _e268 = (*roughness_4); + let _e270 = (*n_dot_v_2); + param_38 = clamp(_e268, 0f, 1f); + param_39 = sqrt(clamp((1f - _e270), 0f, 1f)); param_40 = 1f; - let _e273 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_38), (¶m_39), (¶m_40)); - let _e274 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e273, 0f); - dfg = _e274.xy; - let _e277 = dfg[0u]; - let _e279 = dfg[1u]; - let _e282 = dfg[1u]; - return vec2((_e277 - _e279), _e282); + let _e274 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_38), (¶m_39), (¶m_40)); + let _e275 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e274, 0f); + dfg = _e275.xy; + let _e278 = dfg[0u]; + let _e280 = dfg[1u]; + let _e283 = dfg[1u]; + return vec2((_e278 - _e280), _e283); } fn MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(f0_: ptr>, s_2: ptr) -> vec3 { @@ -16522,58 +16590,58 @@ fn MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u00 var param_42: f32; var ess: f32; - let _e268 = (*s_2).roughness; - param_41 = max(_e268, 0.045f); - let _e271 = (*s_2).n_dot_v; - param_42 = _e271; - let _e272 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_41), (¶m_42)); - ab = _e272; - let _e274 = ab[0u]; - let _e276 = ab[1u]; - ess = max((_e274 + _e276), 0.0001f); - let _e279 = (*f0_); - let _e280 = ess; - return (vec3(1f, 1f, 1f) + (_e279 * ((1f / _e280) - 1f))); + let _e269 = (*s_2).roughness; + param_41 = max(_e269, 0.045f); + let _e272 = (*s_2).n_dot_v; + param_42 = _e272; + let _e273 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_41), (¶m_42)); + ab = _e273; + let _e275 = ab[0u]; + let _e277 = ab[1u]; + ess = max((_e275 + _e277), 0.0001f); + let _e280 = (*f0_); + let _e281 = ess; + return (vec3(1f, 1f, 1f) + (_e280 * ((1f / _e281) - 1f))); } fn EnergyCompensation_u0028_() -> bool { - let _e263 = frag_info.target_origin[2u]; - return (_e263 > 0.5f); + let _e264 = frag_info.target_origin[2u]; + return (_e264 > 0.5f); } fn F_SchlickF90_u0028_vf3_u003b_vf3_u003b_f1_u003b(f0_1: ptr>, f90_: ptr>, v_dot_h: ptr) -> vec3 { var f_2: f32; - let _e265 = (*v_dot_h); - f_2 = pow((1f - _e265), 5f); - let _e268 = (*f0_1); - let _e269 = (*f90_); - let _e270 = (*f0_1); - let _e272 = f_2; - return (_e268 + ((_e269 - _e270) * _e272)); + let _e266 = (*v_dot_h); + f_2 = pow((1f - _e266), 5f); + let _e269 = (*f0_1); + let _e270 = (*f90_); + let _e271 = (*f0_1); + let _e273 = f_2; + return (_e269 + ((_e270 - _e271) * _e273)); } fn V_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(n_dot_l_3: ptr, n_dot_v_3: ptr, b_dot_v: ptr, t_dot_v: ptr, t_dot_l: ptr, b_dot_l: ptr, at: ptr, ab_1: ptr) -> f32 { var ggx_v: f32; var ggx_l: f32; - let _e271 = (*n_dot_l_3); - let _e272 = (*at); - let _e273 = (*t_dot_v); - let _e275 = (*ab_1); - let _e276 = (*b_dot_v); - let _e278 = (*n_dot_v_3); - ggx_v = (_e271 * length(vec3((_e272 * _e273), (_e275 * _e276), _e278))); - let _e282 = (*n_dot_v_3); - let _e283 = (*at); - let _e284 = (*t_dot_l); - let _e286 = (*ab_1); - let _e287 = (*b_dot_l); - let _e289 = (*n_dot_l_3); - ggx_l = (_e282 * length(vec3((_e283 * _e284), (_e286 * _e287), _e289))); - let _e293 = ggx_v; - let _e294 = ggx_l; - return clamp((0.5f / max((_e293 + _e294), 0.00001f)), 0f, 1f); + let _e272 = (*n_dot_l_3); + let _e273 = (*at); + let _e274 = (*t_dot_v); + let _e276 = (*ab_1); + let _e277 = (*b_dot_v); + let _e279 = (*n_dot_v_3); + ggx_v = (_e272 * length(vec3((_e273 * _e274), (_e276 * _e277), _e279))); + let _e283 = (*n_dot_v_3); + let _e284 = (*at); + let _e285 = (*t_dot_l); + let _e287 = (*ab_1); + let _e288 = (*b_dot_l); + let _e290 = (*n_dot_l_3); + ggx_l = (_e283 * length(vec3((_e284 * _e285), (_e287 * _e288), _e290))); + let _e294 = ggx_v; + let _e295 = ggx_l; + return clamp((0.5f / max((_e294 + _e295), 0.00001f)), 0f, 1f); } fn D_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(n_dot_h_3: ptr, t_dot_h: ptr, b_dot_h: ptr, at_1: ptr, ab_2: ptr) -> f32 { @@ -16581,24 +16649,24 @@ fn D_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(n_dot_h_3 var f_3: vec3; var w2_: f32; - let _e269 = (*at_1); - let _e270 = (*ab_2); - a2_1 = (_e269 * _e270); - let _e272 = (*ab_2); - let _e273 = (*t_dot_h); - let _e275 = (*at_1); - let _e276 = (*b_dot_h); - let _e278 = a2_1; - let _e279 = (*n_dot_h_3); - f_3 = vec3((_e272 * _e273), (_e275 * _e276), (_e278 * _e279)); - let _e282 = a2_1; - let _e283 = f_3; + let _e270 = (*at_1); + let _e271 = (*ab_2); + a2_1 = (_e270 * _e271); + let _e273 = (*ab_2); + let _e274 = (*t_dot_h); + let _e276 = (*at_1); + let _e277 = (*b_dot_h); + let _e279 = a2_1; + let _e280 = (*n_dot_h_3); + f_3 = vec3((_e273 * _e274), (_e276 * _e277), (_e279 * _e280)); + let _e283 = a2_1; let _e284 = f_3; - w2_ = (_e282 / max(dot(_e283, _e284), 0.000000000001f)); - let _e288 = a2_1; - let _e289 = w2_; - let _e291 = w2_; - return (((_e288 * _e289) * _e291) / 3.1415927f); + let _e285 = f_3; + w2_ = (_e283 / max(dot(_e284, _e285), 0.000000000001f)); + let _e289 = a2_1; + let _e290 = w2_; + let _e292 = w2_; + return (((_e289 * _e290) * _e292) / 3.1415927f); } fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s_3: ptr, light_1: ptr) -> vec3 { @@ -16650,197 +16718,197 @@ fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ var param_75: Surface; var param_76: LightSample; - let _e311 = (*s_3).roughness; - lobe_1 = max(_e311, 0.045f); - let _e313 = lobe_1; + let _e312 = (*s_3).roughness; + lobe_1 = max(_e312, 0.045f); let _e314 = lobe_1; - alpha_6 = (_e313 * _e314); - let _e316 = g_f0_dielectric; - let _e318 = (*s_3).albedo; - let _e320 = (*s_3).metallic; - f0_2 = mix(_e316, _e318, vec3(_e320)); - let _e323 = g_f90_; - let _e325 = (*s_3).metallic; - f90_1 = vec3(mix(_e323, 1f, _e325)); - let _e329 = (*s_3).albedo; - let _e331 = (*s_3).metallic; - diffuseColor = (_e329 * (1f - _e331)); - let _e335 = (*light_1).n_dot_h; - param_43 = _e335; - let _e336 = alpha_6; - param_44 = _e336; - let _e337 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_43), (¶m_44)); - d_2 = _e337; - let _e339 = (*s_3).n_dot_v; - param_45 = _e339; - let _e341 = (*light_1).n_dot_l; - param_46 = _e341; - let _e342 = alpha_6; - param_47 = _e342; - let _e343 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_45), (¶m_46), (¶m_47)); - vis_1 = _e343; - let _e344 = g_aniso; - if (_e344 > 0f) { - let _e346 = alpha_6; - let _e347 = g_aniso; + let _e315 = lobe_1; + alpha_6 = (_e314 * _e315); + let _e317 = g_f0_dielectric; + let _e319 = (*s_3).albedo; + let _e321 = (*s_3).metallic; + f0_2 = mix(_e317, _e319, vec3(_e321)); + let _e324 = g_f90_; + let _e326 = (*s_3).metallic; + f90_1 = vec3(mix(_e324, 1f, _e326)); + let _e330 = (*s_3).albedo; + let _e332 = (*s_3).metallic; + diffuseColor = (_e330 * (1f - _e332)); + let _e336 = (*light_1).n_dot_h; + param_43 = _e336; + let _e337 = alpha_6; + param_44 = _e337; + let _e338 = D_GGX_u0028_f1_u003b_f1_u003b((¶m_43), (¶m_44)); + d_2 = _e338; + let _e340 = (*s_3).n_dot_v; + param_45 = _e340; + let _e342 = (*light_1).n_dot_l; + param_46 = _e342; + let _e343 = alpha_6; + param_47 = _e343; + let _e344 = V_SmithGGXCorrelated_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_45), (¶m_46), (¶m_47)); + vis_1 = _e344; + let _e345 = g_aniso; + if (_e345 > 0f) { + let _e347 = alpha_6; let _e348 = g_aniso; - at_2 = mix(_e346, 1f, (_e347 * _e348)); - let _e351 = alpha_6; - ab_3 = max(_e351, 0.001f); - let _e353 = g_aniso_t; - let _e355 = (*light_1).h; - let _e357 = g_aniso_b; - let _e359 = (*light_1).h; - let _e362 = (*light_1).n_dot_h; - param_48 = _e362; - param_49 = dot(_e353, _e355); - param_50 = dot(_e357, _e359); - let _e363 = at_2; - param_51 = _e363; - let _e364 = ab_3; - param_52 = _e364; - let _e365 = D_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_48), (¶m_49), (¶m_50), (¶m_51), (¶m_52)); - d_2 = _e365; - let _e366 = g_aniso_b; - let _e368 = (*s_3).v; - let _e370 = g_aniso_t; - let _e372 = (*s_3).v; - let _e374 = g_aniso_t; - let _e376 = (*light_1).l; - let _e378 = g_aniso_b; - let _e380 = (*light_1).l; - let _e383 = (*light_1).n_dot_l; - param_53 = _e383; - let _e385 = (*s_3).n_dot_v; - param_54 = _e385; - param_55 = dot(_e366, _e368); - param_56 = dot(_e370, _e372); - param_57 = dot(_e374, _e376); - param_58 = dot(_e378, _e380); - let _e386 = at_2; - param_59 = _e386; - let _e387 = ab_3; - param_60 = _e387; - let _e388 = V_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_53), (¶m_54), (¶m_55), (¶m_56), (¶m_57), (¶m_58), (¶m_59), (¶m_60)); - vis_1 = _e388; - } - let _e389 = f0_2; - param_61 = _e389; - let _e390 = f90_1; - param_62 = _e390; - let _e392 = (*light_1).v_dot_h; - param_63 = _e392; - let _e393 = F_SchlickF90_u0028_vf3_u003b_vf3_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - f_4 = _e393; - let _e394 = f_4; - let _e395 = g_irid_fresnel; - let _e396 = g_iridescence; - f_4 = mix(_e394, _e395, vec3(_e396)); - let _e399 = d_2; - let _e400 = vis_1; - let _e402 = f_4; - let _e406 = frag_info.material[3u]; - specular = ((_e402 * (_e399 * _e400)) * _e406); - let _e409 = (*light_1).integrated; - if (_e409 > 0.5f) { - let _e413 = (*light_1).ltc[0u]; - let _e414 = f0_2; - let _e417 = (*light_1).ltc[1u]; - let _e419 = f90_1; - let _e420 = f0_2; - let _e424 = (*light_1).ltc[2u]; - let _e430 = frag_info.material[3u]; - let _e433 = (*light_1).n_dot_l; - specular = (((((_e414 * _e417) + ((_e419 - _e420) * _e424)) * _e413) * _e430) / vec3(max(_e433, 0.000001f))); - } - let _e437 = EnergyCompensation_u0028_(); - if _e437 { - let _e438 = f0_2; - param_64 = _e438; - let _e439 = (*s_3); - param_65 = _e439; - let _e440 = MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_64), (¶m_65)); - let _e441 = specular; - specular = (_e441 * _e440); - } - let _e443 = diffuseColor; - let _e444 = f_4; - diffuse = ((_e443 * (vec3(1f, 1f, 1f) - _e444)) / vec3(3.1415927f)); - let _e449 = EonDiffuse_u0028_(); - if _e449 { - let _e451 = (*s_3).n; - let _e453 = (*light_1).l; - let _e457 = (*light_1).l; - let _e459 = (*s_3).v; - let _e461 = diffuseColor; - param_66 = _e461; - let _e463 = (*s_3).roughness; - param_67 = _e463; - param_68 = clamp(dot(_e451, _e453), 0.0001f, 1f); - let _e465 = (*s_3).n_dot_v; - param_69 = _e465; - param_70 = dot(_e457, _e459); - let _e466 = EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_66), (¶m_67), (¶m_68), (¶m_69), (¶m_70)); - let _e467 = f_4; - diffuse = (_e466 * (vec3(1f, 1f, 1f) - _e467)); - } - let _e470 = g_transmission; - let _e472 = diffuse; - diffuse = (_e472 * (1f - _e470)); - let _e474 = g_sheen; - let _e475 = g_sheen_roughness; - param_71 = _e475; - let _e477 = (*light_1).n_dot_h; - param_72 = _e477; - let _e478 = D_Charlie_u0028_f1_u003b_f1_u003b((¶m_71), (¶m_72)); - let _e481 = (*s_3).n; - let _e483 = (*light_1).l; - let _e487 = (*s_3).n_dot_v; - param_73 = _e487; - param_74 = clamp(dot(_e481, _e483), 0f, 1f); - let _e488 = V_Neubelt_u0028_f1_u003b_f1_u003b((¶m_73), (¶m_74)); - sheen = ((_e474 * _e478) * _e488); - let _e490 = diffuse; - let _e491 = specular; - let _e493 = g_sheen_scale; - let _e495 = sheen; - let _e497 = g_coat_through; - let _e499 = g_coat; - let _e500 = (*s_3); - param_75 = _e500; - let _e501 = (*light_1); - param_76 = _e501; - let _e502 = CoatLobe_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_75), (¶m_76)); - return ((((((_e490 + _e491) * _e493) + _e495) * _e497) + vec3((_e499 * _e502))) * 3.1415927f); + let _e349 = g_aniso; + at_2 = mix(_e347, 1f, (_e348 * _e349)); + let _e352 = alpha_6; + ab_3 = max(_e352, 0.001f); + let _e354 = g_aniso_t; + let _e356 = (*light_1).h; + let _e358 = g_aniso_b; + let _e360 = (*light_1).h; + let _e363 = (*light_1).n_dot_h; + param_48 = _e363; + param_49 = dot(_e354, _e356); + param_50 = dot(_e358, _e360); + let _e364 = at_2; + param_51 = _e364; + let _e365 = ab_3; + param_52 = _e365; + let _e366 = D_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_48), (¶m_49), (¶m_50), (¶m_51), (¶m_52)); + d_2 = _e366; + let _e367 = g_aniso_b; + let _e369 = (*s_3).v; + let _e371 = g_aniso_t; + let _e373 = (*s_3).v; + let _e375 = g_aniso_t; + let _e377 = (*light_1).l; + let _e379 = g_aniso_b; + let _e381 = (*light_1).l; + let _e384 = (*light_1).n_dot_l; + param_53 = _e384; + let _e386 = (*s_3).n_dot_v; + param_54 = _e386; + param_55 = dot(_e367, _e369); + param_56 = dot(_e371, _e373); + param_57 = dot(_e375, _e377); + param_58 = dot(_e379, _e381); + let _e387 = at_2; + param_59 = _e387; + let _e388 = ab_3; + param_60 = _e388; + let _e389 = V_GGXAnisotropic_u0028_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_53), (¶m_54), (¶m_55), (¶m_56), (¶m_57), (¶m_58), (¶m_59), (¶m_60)); + vis_1 = _e389; + } + let _e390 = f0_2; + param_61 = _e390; + let _e391 = f90_1; + param_62 = _e391; + let _e393 = (*light_1).v_dot_h; + param_63 = _e393; + let _e394 = F_SchlickF90_u0028_vf3_u003b_vf3_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); + f_4 = _e394; + let _e395 = f_4; + let _e396 = g_irid_fresnel; + let _e397 = g_iridescence; + f_4 = mix(_e395, _e396, vec3(_e397)); + let _e400 = d_2; + let _e401 = vis_1; + let _e403 = f_4; + let _e407 = frag_info.material[3u]; + specular = ((_e403 * (_e400 * _e401)) * _e407); + let _e410 = (*light_1).integrated; + if (_e410 > 0.5f) { + let _e414 = (*light_1).ltc[0u]; + let _e415 = f0_2; + let _e418 = (*light_1).ltc[1u]; + let _e420 = f90_1; + let _e421 = f0_2; + let _e425 = (*light_1).ltc[2u]; + let _e431 = frag_info.material[3u]; + let _e434 = (*light_1).n_dot_l; + specular = (((((_e415 * _e418) + ((_e420 - _e421) * _e425)) * _e414) * _e431) / vec3(max(_e434, 0.000001f))); + } + let _e438 = EnergyCompensation_u0028_(); + if _e438 { + let _e439 = f0_2; + param_64 = _e439; + let _e440 = (*s_3); + param_65 = _e440; + let _e441 = MultiscatterScale_u0028_vf3_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_64), (¶m_65)); + let _e442 = specular; + specular = (_e442 * _e441); + } + let _e444 = diffuseColor; + let _e445 = f_4; + diffuse = ((_e444 * (vec3(1f, 1f, 1f) - _e445)) / vec3(3.1415927f)); + let _e450 = EonDiffuse_u0028_(); + if _e450 { + let _e452 = (*s_3).n; + let _e454 = (*light_1).l; + let _e458 = (*light_1).l; + let _e460 = (*s_3).v; + let _e462 = diffuseColor; + param_66 = _e462; + let _e464 = (*s_3).roughness; + param_67 = _e464; + param_68 = clamp(dot(_e452, _e454), 0.0001f, 1f); + let _e466 = (*s_3).n_dot_v; + param_69 = _e466; + param_70 = dot(_e458, _e460); + let _e467 = EonLobe_u0028_vf3_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_66), (¶m_67), (¶m_68), (¶m_69), (¶m_70)); + let _e468 = f_4; + diffuse = (_e467 * (vec3(1f, 1f, 1f) - _e468)); + } + let _e471 = g_transmission; + let _e473 = diffuse; + diffuse = (_e473 * (1f - _e471)); + let _e475 = g_sheen; + let _e476 = g_sheen_roughness; + param_71 = _e476; + let _e478 = (*light_1).n_dot_h; + param_72 = _e478; + let _e479 = D_Charlie_u0028_f1_u003b_f1_u003b((¶m_71), (¶m_72)); + let _e482 = (*s_3).n; + let _e484 = (*light_1).l; + let _e488 = (*s_3).n_dot_v; + param_73 = _e488; + param_74 = clamp(dot(_e482, _e484), 0f, 1f); + let _e489 = V_Neubelt_u0028_f1_u003b_f1_u003b((¶m_73), (¶m_74)); + sheen = ((_e475 * _e479) * _e489); + let _e491 = diffuse; + let _e492 = specular; + let _e494 = g_sheen_scale; + let _e496 = sheen; + let _e498 = g_coat_through; + let _e500 = g_coat; + let _e501 = (*s_3); + param_75 = _e501; + let _e502 = (*light_1); + param_76 = _e502; + let _e503 = CoatLobe_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_75), (¶m_76)); + return ((((((_e491 + _e492) * _e494) + _e496) * _e498) + vec3((_e500 * _e503))) * 3.1415927f); } fn PointShadowDiskTap_u0028_i1_u003b(i_1: ptr) -> vec2 { - let _e262 = (*i_1); - if (_e262 == 0i) { + let _e263 = (*i_1); + if (_e263 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e264 = (*i_1); - if (_e264 == 1i) { + let _e265 = (*i_1); + if (_e265 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e266 = (*i_1); - if (_e266 == 2i) { + let _e267 = (*i_1); + if (_e267 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e268 = (*i_1); - if (_e268 == 3i) { + let _e269 = (*i_1); + if (_e269 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e270 = (*i_1); - if (_e270 == 4i) { + let _e271 = (*i_1); + if (_e271 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e272 = (*i_1); - if (_e272 == 5i) { + let _e273 = (*i_1); + if (_e273 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e274 = (*i_1); - if (_e274 == 6i) { + let _e275 = (*i_1); + if (_e275 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -16850,20 +16918,20 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_2: ptr; var param_77: i32; - let _e267 = (*i_2); - param_77 = _e267; - let _e268 = PointShadowDiskTap_u0028_i1_u003b((¶m_77)); - p_1 = _e268; - let _e270 = p_1[0u]; - let _e271 = (*ca); - let _e274 = p_1[1u]; - let _e275 = (*sa); - let _e279 = p_1[0u]; - let _e280 = (*sa); - let _e283 = p_1[1u]; - let _e284 = (*ca); - let _e288 = (*radius); - return (vec2(((_e270 * _e271) - (_e274 * _e275)), ((_e279 * _e280) + (_e283 * _e284))) * _e288); + let _e268 = (*i_2); + param_77 = _e268; + let _e269 = PointShadowDiskTap_u0028_i1_u003b((¶m_77)); + p_1 = _e269; + let _e271 = p_1[0u]; + let _e272 = (*ca); + let _e275 = p_1[1u]; + let _e276 = (*sa); + let _e280 = p_1[0u]; + let _e281 = (*sa); + let _e284 = p_1[1u]; + let _e285 = (*ca); + let _e289 = (*radius); + return (vec2(((_e271 * _e272) - (_e275 * _e276)), ((_e280 * _e281) + (_e284 * _e285))) * _e289); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv_1: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { @@ -16871,27 +16939,27 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv_1: ptr; var atlas: vec2; - let _e270 = point_shadow.params[0u]; - inset = _e270; - let _e271 = (*uv_1); - let _e272 = (*offset); - let _e274 = inset; + let _e271 = point_shadow.params[0u]; + inset = _e271; + let _e272 = (*uv_1); + let _e273 = (*offset); let _e275 = inset; - local_5 = clamp((_e271 + _e272), vec2(_e274), vec2((1f - _e275))); - let _e280 = local_5; - let _e281 = (*tile); - atlas = ((_e280 + _e281) * vec2(0.16666667f, 0.16666667f)); - let _e286 = point_shadow.params3_[0u]; - if (_e286 > 0.5f) { - let _e289 = atlas[1u]; - atlas[1u] = (1f - _e289); - } - let _e292 = atlas; - let _e293 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e292, 0f); - let _e295 = atlas; - let _e296 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e295, 0f); - let _e299 = (*range); - return (min(_e293.x, _e296.x) * _e299); + let _e276 = inset; + local_5 = clamp((_e272 + _e273), vec2(_e275), vec2((1f - _e276))); + let _e281 = local_5; + let _e282 = (*tile); + atlas = ((_e281 + _e282) * vec2(0.16666667f, 0.16666667f)); + let _e287 = point_shadow.params3_[0u]; + if (_e287 > 0.5f) { + let _e290 = atlas[1u]; + atlas[1u] = (1f - _e290); + } + let _e293 = atlas; + let _e294 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e293, 0f); + let _e296 = atlas; + let _e297 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e296, 0f); + let _e300 = (*range); + return (min(_e294.x, _e297.x) * _e300); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_2: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { @@ -16901,24 +16969,24 @@ fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_2: pt var param_80: vec2; var param_81: f32; - let _e271 = (*uv_2); - param_78 = _e271; - let _e272 = (*offset_1); - param_79 = _e272; - let _e273 = (*tile_1); - param_80 = _e273; - let _e274 = (*range_1); - param_81 = _e274; - let _e275 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_78), (¶m_79), (¶m_80), (¶m_81)); - stored = _e275; - let _e276 = stored; - let _e277 = (*range_1); - if (_e276 >= (_e277 * 0.999f)) { + let _e272 = (*uv_2); + param_78 = _e272; + let _e273 = (*offset_1); + param_79 = _e273; + let _e274 = (*tile_1); + param_80 = _e274; + let _e275 = (*range_1); + param_81 = _e275; + let _e276 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_78), (¶m_79), (¶m_80), (¶m_81)); + stored = _e276; + let _e277 = stored; + let _e278 = (*range_1); + if (_e277 >= (_e278 * 0.999f)) { return 1f; } - let _e280 = (*receiver); - let _e281 = stored; - return select(1f, 0f, (_e280 > _e281)); + let _e281 = (*receiver); + let _e282 = stored; + return select(1f, 0f, (_e281 > _e282)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_3: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { @@ -16945,111 +17013,111 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u var world: f32; (*blockerOut) = -1f; - let _e292 = point_shadow.params2_[1u]; - lightRadius = _e292; - let _e295 = point_shadow.params2_[0u]; - minRadius = _e295; - let _e298 = point_shadow.params2_[2u]; - maxRadius = _e298; - let _e299 = lightRadius; - if (_e299 <= 0f) { - let _e301 = (*uv_3); - param_82 = _e301; + let _e293 = point_shadow.params2_[1u]; + lightRadius = _e293; + let _e296 = point_shadow.params2_[0u]; + minRadius = _e296; + let _e299 = point_shadow.params2_[2u]; + maxRadius = _e299; + let _e300 = lightRadius; + if (_e300 <= 0f) { + let _e302 = (*uv_3); + param_82 = _e302; param_83 = vec2(0f, 0f); - let _e302 = (*tile_2); - param_84 = _e302; - let _e303 = (*range_2); - param_85 = _e303; - let _e304 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_82), (¶m_83), (¶m_84), (¶m_85)); - (*blockerOut) = _e304; - let _e305 = minRadius; - return _e305; + let _e303 = (*tile_2); + param_84 = _e303; + let _e304 = (*range_2); + param_85 = _e304; + let _e305 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_82), (¶m_83), (¶m_84), (¶m_85)); + (*blockerOut) = _e305; + let _e306 = minRadius; + return _e306; } sum = 0f; count = 0f; i_3 = 0i; loop { - let _e306 = i_3; - if (_e306 < 8i) { - let _e308 = i_3; - param_86 = _e308; - let _e309 = (*ca_1); - param_87 = _e309; - let _e310 = (*sa_1); - param_88 = _e310; - let _e311 = maxRadius; - param_89 = _e311; - let _e312 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_86), (¶m_87), (¶m_88), (¶m_89)); - let _e313 = (*uv_3); - param_90 = _e313; - param_91 = _e312; - let _e314 = (*tile_2); - param_92 = _e314; - let _e315 = (*range_2); - param_93 = _e315; - let _e316 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_90), (¶m_91), (¶m_92), (¶m_93)); - stored_1 = _e316; - let _e317 = stored_1; - let _e318 = (*range_2); - if (_e317 >= (_e318 * 0.999f)) { + let _e307 = i_3; + if (_e307 < 8i) { + let _e309 = i_3; + param_86 = _e309; + let _e310 = (*ca_1); + param_87 = _e310; + let _e311 = (*sa_1); + param_88 = _e311; + let _e312 = maxRadius; + param_89 = _e312; + let _e313 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_86), (¶m_87), (¶m_88), (¶m_89)); + let _e314 = (*uv_3); + param_90 = _e314; + param_91 = _e313; + let _e315 = (*tile_2); + param_92 = _e315; + let _e316 = (*range_2); + param_93 = _e316; + let _e317 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_90), (¶m_91), (¶m_92), (¶m_93)); + stored_1 = _e317; + let _e318 = stored_1; + let _e319 = (*range_2); + if (_e318 >= (_e319 * 0.999f)) { continue; } - let _e321 = stored_1; - let _e322 = (*receiver_1); - if (_e321 >= _e322) { + let _e322 = stored_1; + let _e323 = (*receiver_1); + if (_e322 >= _e323) { continue; } - let _e324 = stored_1; - let _e325 = sum; - sum = (_e325 + _e324); - let _e327 = count; - count = (_e327 + 1f); + let _e325 = stored_1; + let _e326 = sum; + sum = (_e326 + _e325); + let _e328 = count; + count = (_e328 + 1f); continue; } else { break; } continuing { - let _e329 = i_3; - i_3 = (_e329 + 1i); + let _e330 = i_3; + i_3 = (_e330 + 1i); } } - let _e331 = count; - if (_e331 < 0.5f) { + let _e332 = count; + if (_e332 < 0.5f) { return -1f; } - let _e333 = sum; - let _e334 = count; - blocker = max((_e333 / _e334), 0.0001f); - let _e337 = blocker; - (*blockerOut) = _e337; - let _e338 = lightRadius; - let _e339 = (*receiver_1); - let _e340 = blocker; - let _e344 = blocker; - world = ((_e338 * max((_e339 - _e340), 0f)) / _e344); - let _e346 = world; - let _e347 = (*receiver_1); - let _e349 = (*tanHalf); - let _e352 = minRadius; - let _e353 = maxRadius; - return clamp((_e346 / ((2f * _e347) * _e349)), _e352, _e353); + let _e334 = sum; + let _e335 = count; + blocker = max((_e334 / _e335), 0.0001f); + let _e338 = blocker; + (*blockerOut) = _e338; + let _e339 = lightRadius; + let _e340 = (*receiver_1); + let _e341 = blocker; + let _e345 = blocker; + world = ((_e339 * max((_e340 - _e341), 0f)) / _e345); + let _e347 = world; + let _e348 = (*receiver_1); + let _e350 = (*tanHalf); + let _e353 = minRadius; + let _e354 = maxRadius; + return clamp((_e347 / ((2f * _e348) * _e350)), _e353, _e354); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { var local_6: vec2; - let _e263 = (*rows); - if (_e263 > 0f) { - let _e266 = gl_FragCoord_1[0u]; - let _e267 = (*rows); - let _e269 = gl_FragCoord_1[1u]; - local_6 = vec2(_e266, (_e267 - _e269)); + let _e264 = (*rows); + if (_e264 > 0f) { + let _e267 = gl_FragCoord_1[0u]; + let _e268 = (*rows); + let _e270 = gl_FragCoord_1[1u]; + local_6 = vec2(_e267, (_e268 - _e270)); } else { - let _e272 = gl_FragCoord_1; - local_6 = _e272.xy; + let _e273 = gl_FragCoord_1; + local_6 = _e273.xy; } - let _e274 = local_6; - return _e274; + let _e275 = local_6; + return _e275; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -17104,260 +17172,260 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_115: f32; var param_116: f32; - var phi_2602_: bool; - var phi_2663_: bool; - - let _e315 = (*lightIndex); - let _e319 = point_shadow.slots[_e315][0u]; - slot = i32((_e319 + 0.5f)); - let _e322 = (*lightIndex); - let _e326 = point_shadow.slots[_e322][0u]; - if (_e326 < 0f) { + var phi_2603_: bool; + var phi_2664_: bool; + + let _e316 = (*lightIndex); + let _e320 = point_shadow.slots[_e316][0u]; + slot = i32((_e320 + 0.5f)); + let _e323 = (*lightIndex); + let _e327 = point_shadow.slots[_e323][0u]; + if (_e327 < 0f) { return 1f; } - let _e330 = point_shadow.params[2u]; - strength = _e330; - let _e331 = strength; - if (_e331 <= 0f) { + let _e331 = point_shadow.params[2u]; + strength = _e331; + let _e332 = strength; + if (_e332 <= 0f) { return 1f; } - let _e333 = slot; - let _e336 = point_shadow.lights[_e333]; - let _e338 = (*world_1); - toLight = (_e336.xyz - _e338); - let _e340 = toLight; - toLightLength = max(length(_e340), 0.000001f); - let _e343 = (*normal); - let _e344 = toLight; - let _e345 = toLightLength; - nDotL = max(dot(_e343, (_e344 / vec3(_e345))), 0.15f); - let _e350 = nDotL; + let _e334 = slot; + let _e337 = point_shadow.lights[_e334]; + let _e339 = (*world_1); + toLight = (_e337.xyz - _e339); + let _e341 = toLight; + toLightLength = max(length(_e341), 0.000001f); + let _e344 = (*normal); + let _e345 = toLight; + let _e346 = toLightLength; + nDotL = max(dot(_e344, (_e345 / vec3(_e346))), 0.15f); let _e351 = nDotL; - let _e356 = nDotL; + let _e352 = nDotL; let _e357 = nDotL; - slope = min((sqrt(max((1f - (_e350 * _e351)), 0f)) / (_e356 * _e357)), 8f); - let _e361 = toLightLength; - let _e363 = (*lightIndex); - let _e367 = point_shadow.slots[_e363][2u]; - let _e372 = point_shadow.params3_[1u]; - texel = (((2f * _e361) * max(_e367, 0.0001f)) * _e372); - let _e374 = (*world_1); - let _e375 = (*normal); - let _e376 = texel; - let _e380 = point_shadow.params[3u]; - let _e382 = slope; - origin = (_e374 + (((_e375 * _e376) * _e380) * (1f + _e382))); - let _e386 = origin; - let _e387 = slot; - let _e390 = point_shadow.lights[_e387]; - toFragment = (_e386 - _e390.xyz); - let _e393 = toFragment; - distance_ = length(_e393); - let _e395 = slot; - let _e399 = point_shadow.lights[_e395][3u]; - range_3 = max(_e399, 0.0001f); - let _e401 = distance_; - let _e402 = range_3; - if (_e401 >= _e402) { + let _e358 = nDotL; + slope = min((sqrt(max((1f - (_e351 * _e352)), 0f)) / (_e357 * _e358)), 8f); + let _e362 = toLightLength; + let _e364 = (*lightIndex); + let _e368 = point_shadow.slots[_e364][2u]; + let _e373 = point_shadow.params3_[1u]; + texel = (((2f * _e362) * max(_e368, 0.0001f)) * _e373); + let _e375 = (*world_1); + let _e376 = (*normal); + let _e377 = texel; + let _e381 = point_shadow.params[3u]; + let _e383 = slope; + origin = (_e375 + (((_e376 * _e377) * _e381) * (1f + _e383))); + let _e387 = origin; + let _e388 = slot; + let _e391 = point_shadow.lights[_e388]; + toFragment = (_e387 - _e391.xyz); + let _e394 = toFragment; + distance_ = length(_e394); + let _e396 = slot; + let _e400 = point_shadow.lights[_e396][3u]; + range_3 = max(_e400, 0.0001f); + let _e402 = distance_; + let _e403 = range_3; + if (_e402 >= _e403) { return 1f; } face = 0i; - let _e404 = (*lightIndex); - let _e408 = point_shadow.slots[_e404][1u]; - if (_e408 < 0.5f) { - let _e410 = toFragment; - a_2 = abs(_e410); - let _e413 = a_2[0u]; - let _e415 = a_2[1u]; - let _e416 = (_e413 >= _e415); - phi_2602_ = _e416; - if _e416 { - let _e418 = a_2[0u]; - let _e420 = a_2[2u]; - phi_2602_ = (_e418 >= _e420); - } - let _e423 = phi_2602_; - if _e423 { - let _e425 = toFragment[0u]; - face = select(1i, 0i, (_e425 > 0f)); + let _e405 = (*lightIndex); + let _e409 = point_shadow.slots[_e405][1u]; + if (_e409 < 0.5f) { + let _e411 = toFragment; + a_2 = abs(_e411); + let _e414 = a_2[0u]; + let _e416 = a_2[1u]; + let _e417 = (_e414 >= _e416); + phi_2603_ = _e417; + if _e417 { + let _e419 = a_2[0u]; + let _e421 = a_2[2u]; + phi_2603_ = (_e419 >= _e421); + } + let _e424 = phi_2603_; + if _e424 { + let _e426 = toFragment[0u]; + face = select(1i, 0i, (_e426 > 0f)); } else { - let _e429 = a_2[1u]; - let _e431 = a_2[2u]; - if (_e429 >= _e431) { - let _e434 = toFragment[1u]; - face = select(3i, 2i, (_e434 > 0f)); + let _e430 = a_2[1u]; + let _e432 = a_2[2u]; + if (_e430 >= _e432) { + let _e435 = toFragment[1u]; + face = select(3i, 2i, (_e435 > 0f)); } else { - let _e438 = toFragment[2u]; - face = select(5i, 4i, (_e438 > 0f)); + let _e439 = toFragment[2u]; + face = select(5i, 4i, (_e439 > 0f)); } } } - let _e441 = slot; - let _e443 = face; - let _e447 = point_shadow.faces[((_e441 * 6i) + _e443)]; - let _e448 = origin; - clip = (_e447 * vec4(_e448.x, _e448.y, _e448.z, 1f)); - let _e455 = clip[3u]; - if (_e455 <= 0f) { + let _e442 = slot; + let _e444 = face; + let _e448 = point_shadow.faces[((_e442 * 6i) + _e444)]; + let _e449 = origin; + clip = (_e448 * vec4(_e449.x, _e449.y, _e449.z, 1f)); + let _e456 = clip[3u]; + if (_e456 <= 0f) { return 1f; } - let _e457 = clip; - let _e460 = clip[3u]; - ndc = (_e457.xy / vec2(_e460)); - let _e464 = ndc[0u]; - let _e466 = (abs(_e464) > 1f); - phi_2663_ = _e466; - if !(_e466) { - let _e469 = ndc[1u]; - phi_2663_ = (abs(_e469) > 1f); - } - let _e473 = phi_2663_; - if _e473 { + let _e458 = clip; + let _e461 = clip[3u]; + ndc = (_e458.xy / vec2(_e461)); + let _e465 = ndc[0u]; + let _e467 = (abs(_e465) > 1f); + phi_2664_ = _e467; + if !(_e467) { + let _e470 = ndc[1u]; + phi_2664_ = (abs(_e470) > 1f); + } + let _e474 = phi_2664_; + if _e474 { return 1f; } - let _e475 = ndc[0u]; - let _e479 = ndc[1u]; - uv_4 = vec2(((_e475 * 0.5f) + 0.5f), (0.5f - (_e479 * 0.5f))); - let _e483 = face; - let _e485 = slot; - tile_3 = vec2(f32(_e483), f32(_e485)); - let _e488 = distance_; - let _e491 = point_shadow.params[1u]; - receiver_2 = (_e488 - _e491); - let _e495 = frag_info.target_origin[0u]; - param_94 = _e495; - let _e496 = FragCoordFromTop_u0028_f1_u003b((¶m_94)); - noise = fract((52.982918f * fract(dot(_e496, vec2(0.06711056f, 0.00583715f))))); - let _e501 = noise; - angle_1 = (_e501 * 6.2831855f); - let _e503 = angle_1; - ca_2 = cos(_e503); - let _e505 = angle_1; - sa_2 = sin(_e505); - let _e507 = (*lightIndex); - let _e511 = point_shadow.slots[_e507][2u]; - tanHalf_1 = max(_e511, 0.0001f); + let _e476 = ndc[0u]; + let _e480 = ndc[1u]; + uv_4 = vec2(((_e476 * 0.5f) + 0.5f), (0.5f - (_e480 * 0.5f))); + let _e484 = face; + let _e486 = slot; + tile_3 = vec2(f32(_e484), f32(_e486)); + let _e489 = distance_; + let _e492 = point_shadow.params[1u]; + receiver_2 = (_e489 - _e492); + let _e496 = frag_info.target_origin[0u]; + param_94 = _e496; + let _e497 = FragCoordFromTop_u0028_f1_u003b((¶m_94)); + noise = fract((52.982918f * fract(dot(_e497, vec2(0.06711056f, 0.00583715f))))); + let _e502 = noise; + angle_1 = (_e502 * 6.2831855f); + let _e504 = angle_1; + ca_2 = cos(_e504); + let _e506 = angle_1; + sa_2 = sin(_e506); + let _e508 = (*lightIndex); + let _e512 = point_shadow.slots[_e508][2u]; + tanHalf_1 = max(_e512, 0.0001f); blocker_1 = -1f; - let _e513 = uv_4; - param_95 = _e513; - let _e514 = tile_3; - param_96 = _e514; - let _e515 = range_3; - param_97 = _e515; - let _e516 = receiver_2; - param_98 = _e516; - let _e517 = ca_2; - param_99 = _e517; - let _e518 = sa_2; - param_100 = _e518; - let _e519 = tanHalf_1; - param_101 = _e519; - let _e520 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_95), (¶m_96), (¶m_97), (¶m_98), (¶m_99), (¶m_100), (¶m_101), (¶m_102)); - let _e521 = param_102; - blocker_1 = _e521; - radius_1 = _e520; - let _e524 = point_shadow.params2_[3u]; - if (_e524 > 0.5f) { + let _e514 = uv_4; + param_95 = _e514; + let _e515 = tile_3; + param_96 = _e515; + let _e516 = range_3; + param_97 = _e516; + let _e517 = receiver_2; + param_98 = _e517; + let _e518 = ca_2; + param_99 = _e518; + let _e519 = sa_2; + param_100 = _e519; + let _e520 = tanHalf_1; + param_101 = _e520; + let _e521 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_95), (¶m_96), (¶m_97), (¶m_98), (¶m_99), (¶m_100), (¶m_101), (¶m_102)); + let _e522 = param_102; + blocker_1 = _e522; + radius_1 = _e521; + let _e525 = point_shadow.params2_[3u]; + if (_e525 > 0.5f) { g_debug_surface_on = true; - let _e526 = radius_1; - if (_e526 < 0f) { + let _e527 = radius_1; + if (_e527 < 0f) { local_7 = vec3(0f, 0f, 1f); } else { - let _e528 = radius_1; - let _e531 = point_shadow.params2_[2u]; - let _e535 = blocker_1; - let _e536 = range_3; - local_7 = vec3(clamp((_e528 / max(_e531, 0.000001f)), 0f, 1f), clamp((_e535 / _e536), 0f, 1f), 0f); + let _e529 = radius_1; + let _e532 = point_shadow.params2_[2u]; + let _e536 = blocker_1; + let _e537 = range_3; + local_7 = vec3(clamp((_e529 / max(_e532, 0.000001f)), 0f, 1f), clamp((_e536 / _e537), 0f, 1f), 0f); } - let _e540 = local_7; - g_debug_surface = _e540; + let _e541 = local_7; + g_debug_surface = _e541; } - let _e541 = radius_1; - if (_e541 < 0f) { + let _e542 = radius_1; + if (_e542 < 0f) { return 1f; } - let _e543 = uv_4; - param_103 = _e543; + let _e544 = uv_4; + param_103 = _e544; param_104 = vec2(0f, 0f); - let _e544 = tile_3; - param_105 = _e544; - let _e545 = range_3; - param_106 = _e545; - let _e546 = receiver_2; - param_107 = _e546; - let _e547 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_103), (¶m_104), (¶m_105), (¶m_106), (¶m_107)); - lit = _e547; - let _e548 = radius_1; - if (_e548 > 0f) { + let _e545 = tile_3; + param_105 = _e545; + let _e546 = range_3; + param_106 = _e546; + let _e547 = receiver_2; + param_107 = _e547; + let _e548 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_103), (¶m_104), (¶m_105), (¶m_106), (¶m_107)); + lit = _e548; + let _e549 = radius_1; + if (_e549 > 0f) { i_4 = 0i; loop { - let _e550 = i_4; - if (_e550 < 8i) { - let _e552 = i_4; - param_108 = _e552; - let _e553 = ca_2; - param_109 = _e553; - let _e554 = sa_2; - param_110 = _e554; - let _e555 = radius_1; - param_111 = _e555; - let _e556 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_108), (¶m_109), (¶m_110), (¶m_111)); - let _e557 = uv_4; - param_112 = _e557; - param_113 = _e556; - let _e558 = tile_3; - param_114 = _e558; - let _e559 = range_3; - param_115 = _e559; - let _e560 = receiver_2; - param_116 = _e560; - let _e561 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_112), (¶m_113), (¶m_114), (¶m_115), (¶m_116)); - let _e562 = lit; - lit = (_e562 + _e561); + let _e551 = i_4; + if (_e551 < 8i) { + let _e553 = i_4; + param_108 = _e553; + let _e554 = ca_2; + param_109 = _e554; + let _e555 = sa_2; + param_110 = _e555; + let _e556 = radius_1; + param_111 = _e556; + let _e557 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_108), (¶m_109), (¶m_110), (¶m_111)); + let _e558 = uv_4; + param_112 = _e558; + param_113 = _e557; + let _e559 = tile_3; + param_114 = _e559; + let _e560 = range_3; + param_115 = _e560; + let _e561 = receiver_2; + param_116 = _e561; + let _e562 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_112), (¶m_113), (¶m_114), (¶m_115), (¶m_116)); + let _e563 = lit; + lit = (_e563 + _e562); continue; } else { break; } continuing { - let _e564 = i_4; - i_4 = (_e564 + 1i); + let _e565 = i_4; + i_4 = (_e565 + 1i); } } - let _e566 = lit; - lit = (_e566 * 0.11111111f); + let _e567 = lit; + lit = (_e567 * 0.11111111f); } - let _e568 = lit; - let _e569 = strength; - return mix(1f, _e568, clamp(_e569, 0f, 1f)); + let _e569 = lit; + let _e570 = strength; + return mix(1f, _e569, clamp(_e570, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_5: ptr, n_2: ptr, turn: ptr) -> vec2 { var r_3: f32; var theta: f32; - let _e266 = (*i_5); - let _e269 = (*n_2); - r_3 = sqrt(((f32(_e266) + 0.5f) / f32(_e269))); - let _e273 = (*i_5); - let _e276 = (*turn); - theta = ((f32(_e273) * 2.3999631f) + _e276); - let _e278 = r_3; - let _e279 = theta; - let _e281 = theta; - return (vec2(cos(_e279), sin(_e281)) * _e278); + let _e267 = (*i_5); + let _e270 = (*n_2); + r_3 = sqrt(((f32(_e267) + 0.5f) / f32(_e270))); + let _e274 = (*i_5); + let _e277 = (*turn); + theta = ((f32(_e274) * 2.3999631f) + _e277); + let _e279 = r_3; + let _e280 = theta; + let _e282 = theta; + return (vec2(cos(_e280), sin(_e282)) * _e279); } fn ShadowNoise_u0028_() -> f32 { var at_3: vec2; var param_117: f32; - let _e265 = frag_info.target_origin[0u]; - param_117 = _e265; - let _e266 = FragCoordFromTop_u0028_f1_u003b((¶m_117)); - let _e269 = frag_info.target_origin[3u]; - at_3 = (_e266 + vec2((5.588238f * max(_e269, 0f)))); - let _e274 = at_3; - return fract((52.982918f * fract(dot(_e274, vec2(0.06711056f, 0.00583715f))))); + let _e266 = frag_info.target_origin[0u]; + param_117 = _e266; + let _e267 = FragCoordFromTop_u0028_f1_u003b((¶m_117)); + let _e270 = frag_info.target_origin[3u]; + at_3 = (_e267 + vec2((5.588238f * max(_e270, 0f)))); + let _e275 = at_3; + return fract((52.982918f * fract(dot(_e275, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -17366,37 +17434,37 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_4: f32; - let _e263 = (*depth); - d_4 = ((2f * clamp(_e263, 0f, 1f)) - 1f); - let _e267 = d_4; - let _e270 = d_4; - return vec2(exp((40f * _e267)), -(exp((-5f * _e270)))); + let _e264 = (*depth); + d_4 = ((2f * clamp(_e264, 0f, 1f)) - 1f); + let _e268 = d_4; + let _e271 = d_4; + return vec2(exp((40f * _e268)), -(exp((-5f * _e271)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { @@ -17414,37 +17482,37 @@ fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr(0.004f, 0.0005f) * _e279); - let _e282 = scale[0u]; - let _e284 = scale[0u]; - let _e286 = (*moments_1); - param_119 = _e286.xy; - let _e289 = warped[0u]; - param_120 = _e289; - param_121 = (_e282 * _e284); - let _e290 = (*bleed_1); - param_122 = _e290; - let _e291 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_119), (¶m_120), (¶m_121), (¶m_122)); - positive = _e291; - let _e293 = scale[1u]; - let _e295 = scale[1u]; - let _e297 = (*moments_1); - param_123 = _e297.zw; - let _e300 = warped[1u]; - param_124 = _e300; - param_125 = (_e293 * _e295); - let _e301 = (*bleed_1); - param_126 = _e301; - let _e302 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_123), (¶m_124), (¶m_125), (¶m_126)); - negative = _e302; - let _e303 = positive; - let _e304 = negative; - return min(_e303, _e304); + let _e278 = (*depth_1); + param_118 = _e278; + let _e279 = EvsmWarp_u0028_f1_u003b((¶m_118)); + warped = _e279; + let _e280 = warped; + scale = (vec2(0.004f, 0.0005f) * _e280); + let _e283 = scale[0u]; + let _e285 = scale[0u]; + let _e287 = (*moments_1); + param_119 = _e287.xy; + let _e290 = warped[0u]; + param_120 = _e290; + param_121 = (_e283 * _e285); + let _e291 = (*bleed_1); + param_122 = _e291; + let _e292 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_119), (¶m_120), (¶m_121), (¶m_122)); + positive = _e292; + let _e294 = scale[1u]; + let _e296 = scale[1u]; + let _e298 = (*moments_1); + param_123 = _e298.zw; + let _e301 = warped[1u]; + param_124 = _e301; + param_125 = (_e294 * _e296); + let _e302 = (*bleed_1); + param_126 = _e302; + let _e303 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_123), (¶m_124), (¶m_125), (¶m_126)); + negative = _e303; + let _e304 = positive; + let _e305 = negative; + return min(_e304, _e305); } fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_4: ptr, light_2: ptr, lightIndex_1: ptr) -> f32 { @@ -17481,6 +17549,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; + var stored_2: vec4; + var through: vec3; var softness: f32; var lit_1: f32; var moments_2: vec4; @@ -17511,51 +17581,51 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_135: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3823_: bool; - var phi_3834_: bool; - var phi_3995_: bool; - var phi_4002_: bool; - var phi_4009_: bool; - - let _e329 = frag_info.shadow_params[3u]; - strength_1 = _e329; - let _e330 = strength_1; - if (_e330 <= 0f) { + var phi_3826_: bool; + var phi_3837_: bool; + var phi_3998_: bool; + var phi_4005_: bool; + var phi_4012_: bool; + + let _e332 = frag_info.shadow_params[3u]; + strength_1 = _e332; + let _e333 = strength_1; + if (_e333 <= 0f) { return 1f; } - let _e332 = (*lightIndex_1); - let _e335 = frag_info.frame_params[2u]; - if (_e332 != i32((_e335 + 0.5f))) { + let _e335 = (*lightIndex_1); + let _e338 = frag_info.frame_params[2u]; + if (_e335 != i32((_e338 + 0.5f))) { return 1f; } - let _e341 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e341 + 0.5f)); - let _e344 = v_world_position_1; - let _e346 = frag_info.camera_position; - viewDistance = length((_e344 - _e346.xyz)); + let _e344 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e344 + 0.5f)); + let _e347 = v_world_position_1; + let _e349 = frag_info.camera_position; + viewDistance = length((_e347 - _e349.xyz)); cascade = 0i; - let _e350 = cascadeCount; - let _e351 = (_e350 > 1i); - phi_3823_ = _e351; - if _e351 { - let _e352 = viewDistance; - let _e355 = frag_info.shadow_cascades[0u]; - phi_3823_ = (_e352 > _e355); - } - let _e358 = phi_3823_; - if _e358 { + let _e353 = cascadeCount; + let _e354 = (_e353 > 1i); + phi_3826_ = _e354; + if _e354 { + let _e355 = viewDistance; + let _e358 = frag_info.shadow_cascades[0u]; + phi_3826_ = (_e355 > _e358); + } + let _e361 = phi_3826_; + if _e361 { cascade = 1i; } - let _e359 = cascadeCount; - let _e360 = (_e359 > 2i); - phi_3834_ = _e360; - if _e360 { - let _e361 = viewDistance; - let _e364 = frag_info.shadow_cascades[1u]; - phi_3834_ = (_e361 > _e364); + let _e362 = cascadeCount; + let _e363 = (_e362 > 2i); + phi_3837_ = _e363; + if _e363 { + let _e364 = viewDistance; + let _e367 = frag_info.shadow_cascades[1u]; + phi_3837_ = (_e364 > _e367); } - let _e367 = phi_3834_; - if _e367 { + let _e370 = phi_3837_; + if _e370 { cascade = 2i; } uv_5 = vec2(0f, 0f); @@ -17566,232 +17636,247 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e368 = attempt; - if (_e368 < 3i) { - let _e370 = cascade; - let _e371 = attempt; - which = (_e370 + _e371); - let _e373 = which; - let _e374 = cascadeCount; - if (_e373 >= _e374) { + let _e371 = attempt; + if (_e371 < 3i) { + let _e373 = cascade; + let _e374 = attempt; + which = (_e373 + _e374); + let _e376 = which; + let _e377 = cascadeCount; + if (_e376 >= _e377) { break; } - let _e376 = which; - if (_e376 == 0i) { - let _e379 = frag_info.shadow_matrix; - local_8 = _e379; + let _e379 = which; + if (_e379 == 0i) { + let _e382 = frag_info.shadow_matrix; + local_8 = _e382; } else { - let _e380 = which; - if (_e380 == 1i) { - let _e383 = frag_info.shadow_matrix_far; - local_9 = _e383; + let _e383 = which; + if (_e383 == 1i) { + let _e386 = frag_info.shadow_matrix_far; + local_9 = _e386; } else { - let _e385 = frag_info.shadow_matrix_farthest; - local_9 = _e385; + let _e388 = frag_info.shadow_matrix_farthest; + local_9 = _e388; } - let _e386 = local_9; - local_8 = _e386; - } - let _e387 = local_8; - matrix = _e387; - let _e390 = matrix[0][0u]; - let _e393 = matrix[1][0u]; - let _e396 = matrix[2][0u]; - axisX = vec3(_e390, _e393, _e396); - let _e400 = matrix[0][1u]; - let _e403 = matrix[1][1u]; - let _e406 = matrix[2][1u]; - axisY = vec3(_e400, _e403, _e406); - let _e408 = axisX; - rowX = max(length(_e408), 0.000001f); - let _e411 = axisY; - rowY = max(length(_e411), 0.000001f); - let _e416 = frag_info.shadow_cascades[3u]; - let _e418 = rowX; - texelMetres = ((2f * _e416) / _e418); - let _e422 = frag_info.shadow_cascades[3u]; - let _e425 = (*s_4).n; - let _e426 = axisX; - let _e429 = rowX; - let _e430 = rowX; - let _e434 = (*s_4).n; - let _e435 = axisY; - let _e438 = rowY; - let _e439 = rowY; - reach = ((3f * _e422) * ((abs(dot(_e425, _e426)) / (_e429 * _e430)) + (abs(dot(_e434, _e435)) / (_e438 * _e439)))); - let _e446 = frag_info.shadow_params[2u]; - let _e447 = texelMetres; - flatOffset = min(_e446, _e447); - let _e449 = v_world_position_1; - let _e451 = (*s_4).n; - let _e452 = flatOffset; - let _e453 = reach; - origin_1 = (_e449 + (_e451 * (_e452 + _e453))); - let _e457 = matrix; - let _e458 = origin_1; - lightSpace = (_e457 * vec4(_e458.x, _e458.y, _e458.z, 1f)); - let _e465 = lightSpace[3u]; - if (_e465 <= 0f) { + let _e389 = local_9; + local_8 = _e389; + } + let _e390 = local_8; + matrix = _e390; + let _e393 = matrix[0][0u]; + let _e396 = matrix[1][0u]; + let _e399 = matrix[2][0u]; + axisX = vec3(_e393, _e396, _e399); + let _e403 = matrix[0][1u]; + let _e406 = matrix[1][1u]; + let _e409 = matrix[2][1u]; + axisY = vec3(_e403, _e406, _e409); + let _e411 = axisX; + rowX = max(length(_e411), 0.000001f); + let _e414 = axisY; + rowY = max(length(_e414), 0.000001f); + let _e419 = frag_info.shadow_cascades[3u]; + let _e421 = rowX; + texelMetres = ((2f * _e419) / _e421); + let _e425 = frag_info.shadow_cascades[3u]; + let _e428 = (*s_4).n; + let _e429 = axisX; + let _e432 = rowX; + let _e433 = rowX; + let _e437 = (*s_4).n; + let _e438 = axisY; + let _e441 = rowY; + let _e442 = rowY; + reach = ((3f * _e425) * ((abs(dot(_e428, _e429)) / (_e432 * _e433)) + (abs(dot(_e437, _e438)) / (_e441 * _e442)))); + let _e449 = frag_info.shadow_params[2u]; + let _e450 = texelMetres; + flatOffset = min(_e449, _e450); + let _e452 = v_world_position_1; + let _e454 = (*s_4).n; + let _e455 = flatOffset; + let _e456 = reach; + origin_1 = (_e452 + (_e454 * (_e455 + _e456))); + let _e460 = matrix; + let _e461 = origin_1; + lightSpace = (_e460 * vec4(_e461.x, _e461.y, _e461.z, 1f)); + let _e468 = lightSpace[3u]; + if (_e468 <= 0f) { continue; } - let _e467 = lightSpace; - let _e470 = lightSpace[3u]; - candidate = (_e467.xyz / vec3(_e470)); - let _e474 = candidate[0u]; - let _e478 = candidate[1u]; - inTile = vec2(((_e474 * 0.5f) + 0.5f), (0.5f - (_e478 * 0.5f))); - let _e483 = inTile[0u]; - let _e484 = (_e483 < 0f); - phi_3995_ = _e484; - if !(_e484) { - let _e487 = inTile[0u]; - phi_3995_ = (_e487 > 1f); - } - let _e490 = phi_3995_; - phi_4002_ = _e490; - if !(_e490) { - let _e493 = inTile[1u]; - phi_4002_ = (_e493 < 0f); - } - let _e496 = phi_4002_; - phi_4009_ = _e496; - if !(_e496) { - let _e499 = inTile[1u]; - phi_4009_ = (_e499 > 1f); - } - let _e502 = phi_4009_; - if _e502 { + let _e470 = lightSpace; + let _e473 = lightSpace[3u]; + candidate = (_e470.xyz / vec3(_e473)); + let _e477 = candidate[0u]; + let _e481 = candidate[1u]; + inTile = vec2(((_e477 * 0.5f) + 0.5f), (0.5f - (_e481 * 0.5f))); + let _e486 = inTile[0u]; + let _e487 = (_e486 < 0f); + phi_3998_ = _e487; + if !(_e487) { + let _e490 = inTile[0u]; + phi_3998_ = (_e490 > 1f); + } + let _e493 = phi_3998_; + phi_4005_ = _e493; + if !(_e493) { + let _e496 = inTile[1u]; + phi_4005_ = (_e496 < 0f); + } + let _e499 = phi_4005_; + phi_4012_ = _e499; + if !(_e499) { + let _e502 = inTile[1u]; + phi_4012_ = (_e502 > 1f); + } + let _e505 = phi_4012_; + if _e505 { continue; } - let _e504 = candidate[2u]; - if (_e504 > 1f) { - let _e506 = which; - let _e507 = cascadeCount; - if (_e506 < (_e507 - 1i)) { + let _e507 = candidate[2u]; + if (_e507 > 1f) { + let _e509 = which; + let _e510 = cascadeCount; + if (_e509 < (_e510 - 1i)) { continue; } candidate[2u] = 1f; } - let _e512 = inTile[0u]; - let _e513 = which; - let _e516 = cascadeCount; - let _e520 = inTile[1u]; - uv_5 = vec2(((_e512 + f32(_e513)) / f32(_e516)), _e520); - let _e522 = candidate; - projected = _e522; - let _e523 = which; - cascade = _e523; - let _e524 = texelMetres; - cascadeTexel = _e524; - let _e527 = matrix[0][2u]; - let _e530 = matrix[1][2u]; - let _e533 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e527, _e530, _e533)), 0.000001f)); - let _e539 = (*s_4).n; - let _e542 = matrix[0][2u]; - let _e545 = matrix[1][2u]; - let _e548 = matrix[2][2u]; - let _e552 = cascadeDepth; - cosSlope = clamp((abs(dot(_e539, vec3(_e542, _e545, _e548))) * _e552), 0.1f, 1f); - let _e555 = texelMetres; - let _e556 = cosSlope; - let _e557 = cosSlope; - let _e563 = cosSlope; - let _e565 = cascadeDepth; - receiverSlope = (((_e555 * sqrt(max((1f - (_e556 * _e557)), 0f))) / _e563) / _e565); + let _e515 = inTile[0u]; + let _e516 = which; + let _e519 = cascadeCount; + let _e523 = inTile[1u]; + uv_5 = vec2(((_e515 + f32(_e516)) / f32(_e519)), _e523); + let _e525 = candidate; + projected = _e525; + let _e526 = which; + cascade = _e526; + let _e527 = texelMetres; + cascadeTexel = _e527; + let _e530 = matrix[0][2u]; + let _e533 = matrix[1][2u]; + let _e536 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e530, _e533, _e536)), 0.000001f)); + let _e542 = (*s_4).n; + let _e545 = matrix[0][2u]; + let _e548 = matrix[1][2u]; + let _e551 = matrix[2][2u]; + let _e555 = cascadeDepth; + cosSlope = clamp((abs(dot(_e542, vec3(_e545, _e548, _e551))) * _e555), 0.1f, 1f); + let _e558 = texelMetres; + let _e559 = cosSlope; + let _e560 = cosSlope; + let _e566 = cosSlope; + let _e568 = cascadeDepth; + receiverSlope = (((_e558 * sqrt(max((1f - (_e559 * _e560)), 0f))) / _e566) / _e568); found = true; break; } else { break; } continuing { - let _e567 = attempt; - attempt = (_e567 + 1i); + let _e570 = attempt; + attempt = (_e570 + 1i); } } - let _e569 = found; - if !(_e569) { + let _e572 = found; + if !(_e572) { return 1f; } - let _e571 = cascade; - if (_e571 == 0i) { - let _e575 = frag_info.shadow_bias[0u]; - local_10 = _e575; + let _e574 = cascade; + if (_e574 == 0i) { + let _e578 = frag_info.shadow_bias[0u]; + local_10 = _e578; } else { - let _e576 = cascade; - if (_e576 == 1i) { - let _e580 = frag_info.shadow_bias[1u]; - local_11 = _e580; - } else { - let _e583 = frag_info.shadow_bias[2u]; + let _e579 = cascade; + if (_e579 == 1i) { + let _e583 = frag_info.shadow_bias[1u]; local_11 = _e583; - } - let _e584 = local_11; - local_10 = _e584; - } - let _e585 = local_10; - bias = _e585; - let _e588 = frag_info.shadow_params[0u]; - let _e591 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e588, _e591); - let _e593 = cascade; - let _e595 = cascadeCount; - let _e599 = texel_1; - tileLo = (vec2((f32(_e593) / f32(_e595)), 0f) + (_e599 * 0.5f)); - let _e602 = cascade; - let _e605 = cascadeCount; - let _e609 = texel_1; - tileHi = (vec2((f32((_e602 + 1i)) / f32(_e605)), 1f) - (_e609 * 0.5f)); - let _e614 = frag_info.ambient_ground[3u]; - softness = _e614; + } else { + let _e586 = frag_info.shadow_bias[2u]; + local_11 = _e586; + } + let _e587 = local_11; + local_10 = _e587; + } + let _e588 = local_10; + bias = _e588; + let _e591 = frag_info.shadow_params[0u]; + let _e594 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e591, _e594); + let _e596 = cascade; + let _e598 = cascadeCount; + let _e602 = texel_1; + tileLo = (vec2((f32(_e596) / f32(_e598)), 0f) + (_e602 * 0.5f)); + let _e605 = cascade; + let _e608 = cascadeCount; + let _e612 = texel_1; + tileHi = (vec2((f32((_e605 + 1i)) / f32(_e608)), 1f) - (_e612 * 0.5f)); + let _e617 = frag_info.shadow_bias[3u]; + if (_e617 > 0.5f) { + let _e619 = uv_5; + let _e620 = tileLo; + let _e621 = tileHi; + let _e623 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e619, _e620, _e621), 0f); + stored_2 = _e623; + let _e625 = stored_2[1u]; + let _e628 = stored_2[2u]; + let _e631 = stored_2[3u]; + through = clamp(vec3((1f - _e625), (1f - _e628), _e631), vec3(0f), vec3(1f)); + let _e636 = through; + let _e637 = strength_1; + light_transmittance = mix(vec3(1f, 1f, 1f), _e636, vec3(clamp(_e637, 0f, 1f))); + } + let _e643 = frag_info.ambient_ground[3u]; + softness = _e643; lit_1 = 0f; - let _e615 = softness; - if (_e615 < 0f) { - let _e617 = uv_5; - let _e618 = tileLo; - let _e619 = tileHi; - let _e621 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e617, _e618, _e619), 0f); - moments_2 = _e621; - let _e623 = projected[2u]; - let _e624 = bias; - let _e626 = softness; - let _e630 = moments_2; - param_127 = _e630; - param_128 = (_e623 - _e624); - param_129 = clamp((-(_e626) - 1f), 0f, 0.95f); - let _e631 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_127), (¶m_128), (¶m_129)); - lit_1 = _e631; + let _e644 = softness; + if (_e644 < 0f) { + let _e646 = uv_5; + let _e647 = tileLo; + let _e648 = tileHi; + let _e650 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e646, _e647, _e648), 0f); + moments_2 = _e650; + let _e652 = projected[2u]; + let _e653 = bias; + let _e655 = softness; + let _e659 = moments_2; + param_127 = _e659; + param_128 = (_e652 - _e653); + param_129 = clamp((-(_e655) - 1f), 0f, 0.95f); + let _e660 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_127), (¶m_128), (¶m_129)); + lit_1 = _e660; } else { - let _e632 = softness; - if (_e632 <= 0f) { + let _e661 = softness; + if (_e661 <= 0f) { y_1 = -1i; loop { - let _e634 = y_1; - if (_e634 <= 1i) { + let _e663 = y_1; + if (_e663 <= 1i) { x_1 = -1i; loop { - let _e636 = x_1; - if (_e636 <= 1i) { - let _e638 = uv_5; - let _e639 = x_1; - let _e641 = y_1; - let _e644 = texel_1; - let _e647 = tileLo; - let _e648 = tileHi; - let _e650 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e638 + (vec2(f32(_e639), f32(_e641)) * _e644)), _e647, _e648), 0f); - occluder = _e650.x; - let _e653 = projected[2u]; - let _e654 = bias; - let _e656 = occluder; - let _e659 = lit_1; - lit_1 = (_e659 + select(1f, 0f, ((_e653 - _e654) > _e656))); + let _e665 = x_1; + if (_e665 <= 1i) { + let _e667 = uv_5; + let _e668 = x_1; + let _e670 = y_1; + let _e673 = texel_1; + let _e676 = tileLo; + let _e677 = tileHi; + let _e679 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e667 + (vec2(f32(_e668), f32(_e670)) * _e673)), _e676, _e677), 0f); + occluder = _e679.x; + let _e682 = projected[2u]; + let _e683 = bias; + let _e685 = occluder; + let _e688 = lit_1; + lit_1 = (_e688 + select(1f, 0f, ((_e682 - _e683) > _e685))); continue; } else { break; } continuing { - let _e661 = x_1; - x_1 = (_e661 + 1i); + let _e690 = x_1; + x_1 = (_e690 + 1i); } } continue; @@ -17799,125 +17884,125 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e663 = y_1; - y_1 = (_e663 + 1i); + let _e692 = y_1; + y_1 = (_e692 + 1i); } } - let _e665 = lit_1; - lit_1 = (_e665 * 0.11111111f); + let _e694 = lit_1; + lit_1 = (_e694 * 0.11111111f); } else { - let _e667 = softness; - spread = tan(min(_e667, 0.5f)); - let _e670 = ShadowNoise_u0028_(); - turn_1 = (_e670 * 6.2831855f); - let _e672 = spread; - let _e674 = projected[2u]; - let _e676 = cascadeDepth; - let _e678 = cascadeTexel; - searchRadius = clamp((((_e672 * _e674) * _e676) / _e678), 1f, 16f); + let _e696 = softness; + spread = tan(min(_e696, 0.5f)); + let _e699 = ShadowNoise_u0028_(); + turn_1 = (_e699 * 6.2831855f); + let _e701 = spread; + let _e703 = projected[2u]; + let _e705 = cascadeDepth; + let _e707 = cascadeTexel; + searchRadius = clamp((((_e701 * _e703) * _e705) / _e707), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_6 = 0i; loop { - let _e681 = i_6; - if (_e681 < 16i) { - let _e683 = i_6; - param_130 = _e683; + let _e710 = i_6; + if (_e710 < 16i) { + let _e712 = i_6; + param_130 = _e712; param_131 = 16i; - let _e684 = turn_1; - param_132 = _e684; - let _e685 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_130), (¶m_131), (¶m_132)); - disc = _e685; - let _e686 = bias; - let _e687 = disc; - let _e689 = searchRadius; - let _e693 = receiverSlope; - tapBias = (_e686 + (max(((length(_e687) * _e689) - 1.5f), 0f) * _e693)); - let _e696 = uv_5; - let _e697 = disc; - let _e698 = texel_1; - let _e700 = searchRadius; - let _e703 = tileLo; - let _e704 = tileHi; - let _e706 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e696 + ((_e697 * _e698) * _e700)), _e703, _e704), 0f); - occluder_1 = _e706.x; - let _e709 = projected[2u]; - let _e710 = tapBias; - let _e712 = occluder_1; - if ((_e709 - _e710) > _e712) { - let _e714 = occluder_1; - let _e715 = blockerSum; - blockerSum = (_e715 + _e714); - let _e717 = blockerCount; - blockerCount = (_e717 + 1f); + let _e713 = turn_1; + param_132 = _e713; + let _e714 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_130), (¶m_131), (¶m_132)); + disc = _e714; + let _e715 = bias; + let _e716 = disc; + let _e718 = searchRadius; + let _e722 = receiverSlope; + tapBias = (_e715 + (max(((length(_e716) * _e718) - 1.5f), 0f) * _e722)); + let _e725 = uv_5; + let _e726 = disc; + let _e727 = texel_1; + let _e729 = searchRadius; + let _e732 = tileLo; + let _e733 = tileHi; + let _e735 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e725 + ((_e726 * _e727) * _e729)), _e732, _e733), 0f); + occluder_1 = _e735.x; + let _e738 = projected[2u]; + let _e739 = tapBias; + let _e741 = occluder_1; + if ((_e738 - _e739) > _e741) { + let _e743 = occluder_1; + let _e744 = blockerSum; + blockerSum = (_e744 + _e743); + let _e746 = blockerCount; + blockerCount = (_e746 + 1f); } continue; } else { break; } continuing { - let _e719 = i_6; - i_6 = (_e719 + 1i); + let _e748 = i_6; + i_6 = (_e748 + 1i); } } - let _e721 = blockerCount; - if (_e721 <= 0f) { + let _e750 = blockerCount; + if (_e750 <= 0f) { return 1f; } - let _e724 = projected[2u]; - let _e725 = blockerSum; - let _e726 = blockerCount; - let _e730 = cascadeDepth; - gap = (max((_e724 - (_e725 / _e726)), 0f) * _e730); - let _e732 = spread; - let _e733 = gap; - let _e735 = cascadeTexel; - radius_2 = clamp(((_e732 * _e733) / _e735), 1f, 16f); + let _e753 = projected[2u]; + let _e754 = blockerSum; + let _e755 = blockerCount; + let _e759 = cascadeDepth; + gap = (max((_e753 - (_e754 / _e755)), 0f) * _e759); + let _e761 = spread; + let _e762 = gap; + let _e764 = cascadeTexel; + radius_2 = clamp(((_e761 * _e762) / _e764), 1f, 16f); i_7 = 0i; loop { - let _e738 = i_7; - if (_e738 < 16i) { - let _e740 = turn_1; - let _e742 = i_7; - param_133 = _e742; + let _e767 = i_7; + if (_e767 < 16i) { + let _e769 = turn_1; + let _e771 = i_7; + param_133 = _e771; param_134 = 16i; - param_135 = (_e740 + 1f); - let _e743 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_133), (¶m_134), (¶m_135)); - disc_1 = _e743; - let _e744 = bias; - let _e745 = disc_1; - let _e747 = radius_2; - let _e751 = receiverSlope; - tapBias_1 = (_e744 + (max(((length(_e745) * _e747) - 1.5f), 0f) * _e751)); - let _e754 = uv_5; - let _e755 = disc_1; - let _e756 = texel_1; - let _e758 = radius_2; - let _e761 = tileLo; - let _e762 = tileHi; - let _e764 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e754 + ((_e755 * _e756) * _e758)), _e761, _e762), 0f); - occluder_2 = _e764.x; - let _e767 = projected[2u]; - let _e768 = tapBias_1; - let _e770 = occluder_2; - let _e773 = lit_1; - lit_1 = (_e773 + select(1f, 0f, ((_e767 - _e768) > _e770))); + param_135 = (_e769 + 1f); + let _e772 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_133), (¶m_134), (¶m_135)); + disc_1 = _e772; + let _e773 = bias; + let _e774 = disc_1; + let _e776 = radius_2; + let _e780 = receiverSlope; + tapBias_1 = (_e773 + (max(((length(_e774) * _e776) - 1.5f), 0f) * _e780)); + let _e783 = uv_5; + let _e784 = disc_1; + let _e785 = texel_1; + let _e787 = radius_2; + let _e790 = tileLo; + let _e791 = tileHi; + let _e793 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e783 + ((_e784 * _e785) * _e787)), _e790, _e791), 0f); + occluder_2 = _e793.x; + let _e796 = projected[2u]; + let _e797 = tapBias_1; + let _e799 = occluder_2; + let _e802 = lit_1; + lit_1 = (_e802 + select(1f, 0f, ((_e796 - _e797) > _e799))); continue; } else { break; } continuing { - let _e775 = i_7; - i_7 = (_e775 + 1i); + let _e804 = i_7; + i_7 = (_e804 + 1i); } } - let _e777 = lit_1; - lit_1 = (_e777 * 0.0625f); + let _e806 = lit_1; + lit_1 = (_e806 * 0.0625f); } } - let _e779 = lit_1; - let _e780 = strength_1; - return mix(1f, _e779, clamp(_e780, 0f, 1f)); + let _e808 = lit_1; + let _e809 = strength_1; + return mix(1f, _e808, clamp(_e809, 0f, 1f)); } fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_5: ptr, light_3: ptr, index: ptr) -> f32 { @@ -17925,19 +18010,19 @@ fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_137: LightSample; var param_138: i32; - let _e267 = (*s_5); - param_136 = _e267; - let _e268 = (*light_3); - param_137 = _e268; - let _e269 = (*index); - param_138 = _e269; - let _e270 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_136), (¶m_137), (¶m_138)); - return _e270; + let _e268 = (*s_5); + param_136 = _e268; + let _e269 = (*light_3); + param_137 = _e269; + let _e270 = (*index); + param_138 = _e270; + let _e271 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_136), (¶m_137), (¶m_138)); + return _e271; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e262 = (*index_1); - return (_e262 < 8i); + let _e263 = (*index_1); + return (_e263 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -17945,26 +18030,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e266 = (*distance_1); let _e267 = (*distance_1); - attenuation = (1f / max((_e266 * _e267), 0.0001f)); - let _e271 = (*range_4); - if (_e271 > 0f) { - let _e273 = (*distance_1); - let _e274 = (*range_4); - ratio = (_e273 / _e274); - let _e276 = ratio; + let _e268 = (*distance_1); + attenuation = (1f / max((_e267 * _e268), 0.0001f)); + let _e272 = (*range_4); + if (_e272 > 0f) { + let _e274 = (*distance_1); + let _e275 = (*range_4); + ratio = (_e274 / _e275); let _e277 = ratio; - let _e279 = ratio; - let _e281 = ratio; - window = clamp((1f - (((_e276 * _e277) * _e279) * _e281)), 0f, 1f); - let _e285 = window; + let _e278 = ratio; + let _e280 = ratio; + let _e282 = ratio; + window = clamp((1f - (((_e277 * _e278) * _e280) * _e282)), 0f, 1f); let _e286 = window; - let _e288 = attenuation; - attenuation = (_e288 * (_e285 * _e286)); + let _e287 = window; + let _e289 = attenuation; + attenuation = (_e289 * (_e286 * _e287)); } - let _e290 = attenuation; - return _e290; + let _e291 = attenuation; + return _e291; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -17981,75 +18066,75 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var u: f32; var v_1: f32; - let _e278 = (*halfWidth); - let _e279 = (*halfHeight); - n_3 = cross(_e278, _e279); - let _e281 = n_3; - nLen = length(_e281); - let _e283 = nLen; - if (_e283 < 0.000000000001f) { - let _e285 = (*centre); - return _e285; - } - let _e286 = nLen; - let _e287 = n_3; - n_3 = (_e287 / vec3(_e286)); - let _e290 = (*centre); - let _e291 = (*world_2); - toPlane = (_e290 - _e291); - let _e293 = (*mirror); - let _e294 = n_3; - denom = dot(_e293, _e294); - let _e296 = denom; - if (abs(_e296) < 0.00001f) { - let _e299 = toPlane; - let _e300 = n_3; - let _e301 = toPlane; - let _e302 = n_3; - onPlane = (_e299 - (_e300 * dot(_e301, _e302))); + let _e279 = (*halfWidth); + let _e280 = (*halfHeight); + n_3 = cross(_e279, _e280); + let _e282 = n_3; + nLen = length(_e282); + let _e284 = nLen; + if (_e284 < 0.000000000001f) { + let _e286 = (*centre); + return _e286; + } + let _e287 = nLen; + let _e288 = n_3; + n_3 = (_e288 / vec3(_e287)); + let _e291 = (*centre); + let _e292 = (*world_2); + toPlane = (_e291 - _e292); + let _e294 = (*mirror); + let _e295 = n_3; + denom = dot(_e294, _e295); + let _e297 = denom; + if (abs(_e297) < 0.00001f) { + let _e300 = toPlane; + let _e301 = n_3; + let _e302 = toPlane; + let _e303 = n_3; + onPlane = (_e300 - (_e301 * dot(_e302, _e303))); } else { - let _e306 = toPlane; - let _e307 = n_3; - let _e309 = denom; - t_1 = (dot(_e306, _e307) / _e309); - let _e311 = t_1; - if (_e311 > 0f) { - let _e313 = (*mirror); - let _e314 = t_1; - local_12 = (_e313 * _e314); + let _e307 = toPlane; + let _e308 = n_3; + let _e310 = denom; + t_1 = (dot(_e307, _e308) / _e310); + let _e312 = t_1; + if (_e312 > 0f) { + let _e314 = (*mirror); + let _e315 = t_1; + local_12 = (_e314 * _e315); } else { - let _e316 = toPlane; - let _e317 = n_3; - let _e318 = toPlane; - let _e319 = n_3; - local_12 = (_e316 - (_e317 * dot(_e318, _e319))); - } - let _e323 = local_12; - onPlane = _e323; - } - let _e324 = onPlane; - let _e325 = toPlane; - offset_2 = (_e324 - _e325); - let _e327 = (*halfWidth); + let _e317 = toPlane; + let _e318 = n_3; + let _e319 = toPlane; + let _e320 = n_3; + local_12 = (_e317 - (_e318 * dot(_e319, _e320))); + } + let _e324 = local_12; + onPlane = _e324; + } + let _e325 = onPlane; + let _e326 = toPlane; + offset_2 = (_e325 - _e326); let _e328 = (*halfWidth); - wLen2_ = max(dot(_e327, _e328), 0.000000000001f); - let _e331 = (*halfHeight); + let _e329 = (*halfWidth); + wLen2_ = max(dot(_e328, _e329), 0.000000000001f); let _e332 = (*halfHeight); - hLen2_ = max(dot(_e331, _e332), 0.000000000001f); - let _e335 = offset_2; - let _e336 = (*halfWidth); - let _e338 = wLen2_; - u = clamp((dot(_e335, _e336) / _e338), -1f, 1f); - let _e341 = offset_2; - let _e342 = (*halfHeight); - let _e344 = hLen2_; - v_1 = clamp((dot(_e341, _e342) / _e344), -1f, 1f); - let _e347 = (*centre); - let _e348 = (*halfWidth); - let _e349 = u; - let _e352 = (*halfHeight); - let _e353 = v_1; - return ((_e347 + (_e348 * _e349)) + (_e352 * _e353)); + let _e333 = (*halfHeight); + hLen2_ = max(dot(_e332, _e333), 0.000000000001f); + let _e336 = offset_2; + let _e337 = (*halfWidth); + let _e339 = wLen2_; + u = clamp((dot(_e336, _e337) / _e339), -1f, 1f); + let _e342 = offset_2; + let _e343 = (*halfHeight); + let _e345 = hLen2_; + v_1 = clamp((dot(_e342, _e343) / _e345), -1f, 1f); + let _e348 = (*centre); + let _e349 = (*halfWidth); + let _e350 = u; + let _e353 = (*halfHeight); + let _e354 = v_1; + return ((_e348 + (_e349 * _e350)) + (_e353 * _e354)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr>, b_1: ptr>, n_4: ptr>) -> f32 { @@ -18060,32 +18145,32 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_3: ptr> var len_2: f32; var local_13: f32; - let _e270 = (*a_3); let _e271 = (*a_3); - ua = (_e270 / vec3(max(length(_e271), 0.000000000001f))); - let _e276 = (*b_1); + let _e272 = (*a_3); + ua = (_e271 / vec3(max(length(_e272), 0.000000000001f))); let _e277 = (*b_1); - ub = (_e276 / vec3(max(length(_e277), 0.000000000001f))); - let _e282 = ua; - let _e283 = ub; - angle_2 = acos(clamp(dot(_e282, _e283), -1f, 1f)); - let _e287 = ua; - let _e288 = ub; - axis_1 = cross(_e287, _e288); - let _e290 = axis_1; - len_2 = length(_e290); - let _e292 = len_2; - if (_e292 > 0.000001f) { - let _e294 = angle_2; - let _e295 = axis_1; - let _e296 = (*n_4); - let _e299 = len_2; - local_13 = ((_e294 * dot(_e295, _e296)) / _e299); + let _e278 = (*b_1); + ub = (_e277 / vec3(max(length(_e278), 0.000000000001f))); + let _e283 = ua; + let _e284 = ub; + angle_2 = acos(clamp(dot(_e283, _e284), -1f, 1f)); + let _e288 = ua; + let _e289 = ub; + axis_1 = cross(_e288, _e289); + let _e291 = axis_1; + len_2 = length(_e291); + let _e293 = len_2; + if (_e293 > 0.000001f) { + let _e295 = angle_2; + let _e296 = axis_1; + let _e297 = (*n_4); + let _e300 = len_2; + local_13 = ((_e295 * dot(_e296, _e297)) / _e300); } else { local_13 = 0f; } - let _e301 = local_13; - return _e301; + let _e302 = local_13; + return _e302; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr, 4>>, n_5: ptr>) -> f32 { @@ -18116,98 +18201,98 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners_1: ptr(0f, 0f, 0f); i_8 = 0i; loop { - let _e284 = i_8; - if (_e284 < 4i) { - let _e286 = i_8; - let _e288 = (*corners_1)[_e286]; - a_4 = _e288; - let _e289 = i_8; - if (_e289 == 3i) { + let _e285 = i_8; + if (_e285 < 4i) { + let _e287 = i_8; + let _e289 = (*corners_1)[_e287]; + a_4 = _e289; + let _e290 = i_8; + if (_e290 == 3i) { local_14 = 0i; } else { - let _e291 = i_8; - local_14 = (_e291 + 1i); - } - let _e293 = local_14; - let _e295 = (*corners_1)[_e293]; - b_2 = _e295; - let _e296 = a_4; - let _e297 = (*n_5); - ha = dot(_e296, _e297); - let _e299 = b_2; - let _e300 = (*n_5); - hb = dot(_e299, _e300); - let _e302 = ha; - let _e303 = hb; - d_5 = (_e302 - _e303); - let _e305 = a_4; - let _e306 = b_2; - let _e307 = a_4; - let _e309 = d_5; - if (abs(_e309) > 0.000000000001f) { - let _e312 = ha; - let _e313 = d_5; - local_15 = (_e312 / _e313); + let _e292 = i_8; + local_14 = (_e292 + 1i); + } + let _e294 = local_14; + let _e296 = (*corners_1)[_e294]; + b_2 = _e296; + let _e297 = a_4; + let _e298 = (*n_5); + ha = dot(_e297, _e298); + let _e300 = b_2; + let _e301 = (*n_5); + hb = dot(_e300, _e301); + let _e303 = ha; + let _e304 = hb; + d_5 = (_e303 - _e304); + let _e306 = a_4; + let _e307 = b_2; + let _e308 = a_4; + let _e310 = d_5; + if (abs(_e310) > 0.000000000001f) { + let _e313 = ha; + let _e314 = d_5; + local_15 = (_e313 / _e314); } else { local_15 = 0f; } - let _e315 = local_15; - q = (_e305 + ((_e306 - _e307) * _e315)); - let _e318 = ha; - aAbove = (_e318 > 0f); - let _e320 = hb; - bAbove = (_e320 > 0f); - let _e322 = aAbove; - let _e323 = bAbove; - if (_e322 || _e323) { - let _e325 = aAbove; - let _e326 = a_4; - let _e327 = q; - let _e330 = bAbove; - let _e331 = b_2; - let _e332 = q; - param_139 = select(_e327, _e326, vec3(_e325)); - param_140 = select(_e332, _e331, vec3(_e330)); - let _e335 = (*n_5); - param_141 = _e335; - let _e336 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_139), (¶m_140), (¶m_141)); - local_16 = _e336; + let _e316 = local_15; + q = (_e306 + ((_e307 - _e308) * _e316)); + let _e319 = ha; + aAbove = (_e319 > 0f); + let _e321 = hb; + bAbove = (_e321 > 0f); + let _e323 = aAbove; + let _e324 = bAbove; + if (_e323 || _e324) { + let _e326 = aAbove; + let _e327 = a_4; + let _e328 = q; + let _e331 = bAbove; + let _e332 = b_2; + let _e333 = q; + param_139 = select(_e328, _e327, vec3(_e326)); + param_140 = select(_e333, _e332, vec3(_e331)); + let _e336 = (*n_5); + param_141 = _e336; + let _e337 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_139), (¶m_140), (¶m_141)); + local_16 = _e337; } else { local_16 = 0f; } - let _e337 = local_16; - let _e338 = total; - total = (_e338 + _e337); - let _e340 = aAbove; - let _e341 = bAbove; - let _e344 = q; - let _e345 = exit; - exit = select(_e345, _e344, vec3((_e340 && !(_e341)))); - let _e348 = aAbove; - let _e350 = bAbove; - let _e352 = q; - let _e353 = entry; - entry = select(_e353, _e352, vec3((!(_e348) && _e350))); + let _e338 = local_16; + let _e339 = total; + total = (_e339 + _e338); + let _e341 = aAbove; + let _e342 = bAbove; + let _e345 = q; + let _e346 = exit; + exit = select(_e346, _e345, vec3((_e341 && !(_e342)))); + let _e349 = aAbove; + let _e351 = bAbove; + let _e353 = q; + let _e354 = entry; + entry = select(_e354, _e353, vec3((!(_e349) && _e351))); continue; } else { break; } continuing { - let _e356 = i_8; - i_8 = (_e356 + 1i); + let _e357 = i_8; + i_8 = (_e357 + 1i); } } - let _e358 = exit; - param_142 = _e358; - let _e359 = entry; - param_143 = _e359; - let _e360 = (*n_5); - param_144 = _e360; - let _e361 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_142), (¶m_143), (¶m_144)); - let _e362 = total; - total = (_e362 + _e361); - let _e364 = total; - return max((-(_e364) * 0.5f), 0f); + let _e359 = exit; + param_142 = _e359; + let _e360 = entry; + param_143 = _e360; + let _e361 = (*n_5); + param_144 = _e361; + let _e362 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_142), (¶m_143), (¶m_144)); + let _e363 = total; + total = (_e363 + _e362); + let _e365 = total; + return max((-(_e365) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { @@ -18216,91 +18301,91 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 var local_18: f32; var local_19: f32; - let _e267 = (*slot_1); let _e268 = (*slot_1); - lane = (_e267 - ((_e268 / 4i) * 4i)); - let _e272 = lane; - if (_e272 == 0i) { - let _e275 = (*four)[0u]; - local_17 = _e275; + let _e269 = (*slot_1); + lane = (_e268 - ((_e269 / 4i) * 4i)); + let _e273 = lane; + if (_e273 == 0i) { + let _e276 = (*four)[0u]; + local_17 = _e276; } else { - let _e276 = lane; - if (_e276 == 1i) { - let _e279 = (*four)[1u]; - local_18 = _e279; + let _e277 = lane; + if (_e277 == 1i) { + let _e280 = (*four)[1u]; + local_18 = _e280; } else { - let _e280 = lane; - if (_e280 == 2i) { - let _e283 = (*four)[2u]; - local_19 = _e283; + let _e281 = lane; + if (_e281 == 2i) { + let _e284 = (*four)[2u]; + local_19 = _e284; } else { - let _e285 = (*four)[3u]; - local_19 = _e285; + let _e286 = (*four)[3u]; + local_19 = _e286; } - let _e286 = local_19; - local_18 = _e286; + let _e287 = local_19; + local_18 = _e287; } - let _e287 = local_18; - local_17 = _e287; + let _e288 = local_18; + local_17 = _e288; } - let _e288 = local_17; - return _e288; + let _e289 = local_17; + return _e289; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { var param_145: vec4; var param_146: i32; - let _e264 = (*slot_2); - let _e268 = light_list_info.scales[(_e264 / 4i)]; - param_145 = _e268; - let _e269 = (*slot_2); - param_146 = _e269; - let _e270 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_145), (¶m_146)); - return _e270; + let _e265 = (*slot_2); + let _e269 = light_list_info.scales[(_e265 / 4i)]; + param_145 = _e269; + let _e270 = (*slot_2); + param_146 = _e270; + let _e271 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_145), (¶m_146)); + return _e271; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_5: vec4; var b_3: vec4; - let _e266 = light_list_info.slot_rows[0i]; - let _e267 = (*row); - a_5 = abs((_e266 - vec4(_e267))); - let _e273 = light_list_info.slot_rows[1i]; - let _e274 = (*row); - b_3 = abs((_e273 - vec4(_e274))); - let _e279 = a_5[0u]; - let _e281 = a_5[1u]; - let _e284 = a_5[2u]; - let _e286 = a_5[3u]; - let _e290 = b_3[0u]; - let _e292 = b_3[1u]; - let _e295 = b_3[2u]; - let _e297 = b_3[3u]; - return (min(min(min(_e279, _e281), min(_e284, _e286)), min(min(_e290, _e292), min(_e295, _e297))) < 0.5f); + let _e267 = light_list_info.slot_rows[0i]; + let _e268 = (*row); + a_5 = abs((_e267 - vec4(_e268))); + let _e274 = light_list_info.slot_rows[1i]; + let _e275 = (*row); + b_3 = abs((_e274 - vec4(_e275))); + let _e280 = a_5[0u]; + let _e282 = a_5[1u]; + let _e285 = a_5[2u]; + let _e287 = a_5[3u]; + let _e291 = b_3[0u]; + let _e293 = b_3[1u]; + let _e296 = b_3[2u]; + let _e298 = b_3[3u]; + return (min(min(min(_e280, _e282), min(_e285, _e287)), min(min(_e291, _e293), min(_e296, _e298))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { var param_147: vec4; var param_148: i32; - let _e264 = (*slot_3); - let _e268 = light_list_info.indices[(_e264 / 4i)]; - param_147 = _e268; - let _e269 = (*slot_3); - param_148 = _e269; - let _e270 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_147), (¶m_148)); - return _e270; + let _e265 = (*slot_3); + let _e269 = light_list_info.indices[(_e265 / 4i)]; + param_147 = _e269; + let _e270 = (*slot_3); + param_148 = _e270; + let _e271 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_147), (¶m_148)); + return _e271; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e263 = (*texel_2); - let _e267 = light_list_info.list[1u]; - let _e269 = (*row_1); - let _e273 = light_list_info.list[2u]; - let _e276 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e263 + 0.5f) * _e267), ((_e269 + 0.5f) * _e273)), 0f); - return _e276; + let _e264 = (*texel_2); + let _e268 = light_list_info.list[1u]; + let _e270 = (*row_1); + let _e274 = light_list_info.list[2u]; + let _e277 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e264 + 0.5f) * _e268), ((_e270 + 0.5f) * _e274)), 0f); + return _e277; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -18314,35 +18399,35 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var param_151: vec4; var param_152: i32; - let _e271 = g_cluster_offset; - let _e272 = (*slot_4); - entry_1 = (_e271 + f32(_e272)); - let _e275 = entry_1; - row_2 = floor((_e275 / 16f)); - let _e278 = entry_1; - let _e279 = row_2; - within = (_e278 - (_e279 * 16f)); - let _e282 = within; - texel_3 = floor((_e282 / 4f)); - let _e287 = light_list_info.cluster_depth[3u]; - let _e288 = row_2; - let _e290 = texel_3; - param_149 = _e290; - param_150 = (_e287 + _e288); - let _e291 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_149), (¶m_150)); - four_1 = _e291; - let _e292 = within; - let _e293 = texel_3; - let _e298 = four_1; - param_151 = _e298; - param_152 = i32(((_e292 - (_e293 * 4f)) + 0.5f)); - let _e299 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_151), (¶m_152)); - return _e299; + let _e272 = g_cluster_offset; + let _e273 = (*slot_4); + entry_1 = (_e272 + f32(_e273)); + let _e276 = entry_1; + row_2 = floor((_e276 / 16f)); + let _e279 = entry_1; + let _e280 = row_2; + within = (_e279 - (_e280 * 16f)); + let _e283 = within; + texel_3 = floor((_e283 / 4f)); + let _e288 = light_list_info.cluster_depth[3u]; + let _e289 = row_2; + let _e291 = texel_3; + param_149 = _e291; + param_150 = (_e288 + _e289); + let _e292 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_149), (¶m_150)); + four_1 = _e292; + let _e293 = within; + let _e294 = texel_3; + let _e299 = four_1; + param_151 = _e299; + param_152 = i32(((_e293 - (_e294 * 4f)) + 0.5f)); + let _e300 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_151), (¶m_152)); + return _e300; } fn Clustered_u0028_() -> bool { - let _e263 = light_list_info.cluster_grid[3u]; - return (_e263 > 0.5f); + let _e264 = light_list_info.cluster_grid[3u]; + return (_e264 > 0.5f); } fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_6: ptr) -> LightSample { @@ -18402,277 +18487,277 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ light_4.integrated = 0f; light_4.ltc = vec3(0f, 0f, 0f); - let _e318 = (*index_2); - if (_e318 < 8i) { - let _e320 = (*index_2); - let _e323 = frag_info.light_position[_e320]; - position = _e323; - let _e324 = (*index_2); - let _e327 = frag_info.light_color[_e324]; - color_1 = _e327; - let _e328 = (*index_2); - let _e331 = frag_info.light_direction[_e328]; - direction = _e331; - let _e332 = (*index_2); - let _e335 = frag_info.light_cone[_e332]; - cone = _e335; + let _e319 = (*index_2); + if (_e319 < 8i) { + let _e321 = (*index_2); + let _e324 = frag_info.light_position[_e321]; + position = _e324; + let _e325 = (*index_2); + let _e328 = frag_info.light_color[_e325]; + color_1 = _e328; + let _e329 = (*index_2); + let _e332 = frag_info.light_direction[_e329]; + direction = _e332; + let _e333 = (*index_2); + let _e336 = frag_info.light_cone[_e333]; + cone = _e336; } else { - let _e336 = (*index_2); - slot_5 = (_e336 - 8i); - let _e338 = Clustered_u0028_(); - clustered = _e338; - let _e339 = clustered; - if _e339 { - let _e340 = slot_5; - param_153 = _e340; - let _e341 = ClusterRow_u0028_i1_u003b((¶m_153)); - local_20 = _e341; + let _e337 = (*index_2); + slot_5 = (_e337 - 8i); + let _e339 = Clustered_u0028_(); + clustered = _e339; + let _e340 = clustered; + if _e340 { + let _e341 = slot_5; + param_153 = _e341; + let _e342 = ClusterRow_u0028_i1_u003b((¶m_153)); + local_20 = _e342; } else { - let _e342 = slot_5; - param_154 = _e342; - let _e343 = LightListRow_u0028_i1_u003b((¶m_154)); - local_20 = _e343; - } - let _e344 = local_20; - listRow = _e344; - let _e345 = listRow; - let _e349 = light_list_info.list[2u]; - v_2 = ((_e345 + 0.5f) * _e349); - let _e353 = light_list_info.list[1u]; - u_1 = _e353; - let _e354 = u_1; - let _e356 = v_2; - let _e358 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e354), _e356), 0f); - position = _e358; - let _e359 = u_1; - let _e361 = v_2; - let _e363 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e359), _e361), 0f); - color_1 = _e363; - let _e364 = u_1; - let _e366 = v_2; - let _e368 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e364), _e366), 0f); - direction = _e368; - let _e369 = u_1; - let _e371 = v_2; - let _e373 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e369), _e371), 0f); - cone = _e373; - let _e374 = clustered; - if _e374 { - let _e375 = listRow; - param_155 = _e375; - let _e376 = InSlots_u0028_f1_u003b((¶m_155)); - local_21 = select(1f, 0f, _e376); + let _e343 = slot_5; + param_154 = _e343; + let _e344 = LightListRow_u0028_i1_u003b((¶m_154)); + local_20 = _e344; + } + let _e345 = local_20; + listRow = _e345; + let _e346 = listRow; + let _e350 = light_list_info.list[2u]; + v_2 = ((_e346 + 0.5f) * _e350); + let _e354 = light_list_info.list[1u]; + u_1 = _e354; + let _e355 = u_1; + let _e357 = v_2; + let _e359 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e355), _e357), 0f); + position = _e359; + let _e360 = u_1; + let _e362 = v_2; + let _e364 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e360), _e362), 0f); + color_1 = _e364; + let _e365 = u_1; + let _e367 = v_2; + let _e369 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e365), _e367), 0f); + direction = _e369; + let _e370 = u_1; + let _e372 = v_2; + let _e374 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e370), _e372), 0f); + cone = _e374; + let _e375 = clustered; + if _e375 { + let _e376 = listRow; + param_155 = _e376; + let _e377 = InSlots_u0028_f1_u003b((¶m_155)); + local_21 = select(1f, 0f, _e377); } else { - let _e378 = slot_5; - param_156 = _e378; - let _e379 = LightListScale_u0028_i1_u003b((¶m_156)); - local_21 = _e379; - } - let _e380 = local_21; - let _e382 = color_1[3u]; - color_1[3u] = (_e382 * _e380); - } - let _e386 = position[3u]; - type_45 = _e386; - let _e387 = type_45; - if (_e387 > 2.5f) { - let _e389 = direction; - halfWidth_1 = _e389.xyz; - let _e391 = cone; - halfHeight_1 = _e391.xyz; - let _e393 = position; - let _e395 = v_world_position_1; - toCentre = (_e393.xyz - _e395); - let _e397 = toCentre; - let _e398 = halfWidth_1; - let _e400 = halfHeight_1; - corners_2[0i] = ((_e397 - _e398) - _e400); - let _e403 = toCentre; - let _e404 = halfWidth_1; - let _e406 = halfHeight_1; - corners_2[1i] = ((_e403 + _e404) - _e406); - let _e409 = toCentre; - let _e410 = halfWidth_1; - let _e412 = halfHeight_1; - corners_2[2i] = ((_e409 + _e410) + _e412); - let _e415 = toCentre; - let _e416 = halfWidth_1; - let _e418 = halfHeight_1; - corners_2[3i] = ((_e415 - _e416) + _e418); - let _e421 = toCentre; - let _e422 = halfWidth_1; - let _e423 = halfHeight_1; - behind = (dot(_e421, cross(_e422, _e423)) >= 0f); - let _e427 = behind; - if _e427 { + let _e379 = slot_5; + param_156 = _e379; + let _e380 = LightListScale_u0028_i1_u003b((¶m_156)); + local_21 = _e380; + } + let _e381 = local_21; + let _e383 = color_1[3u]; + color_1[3u] = (_e383 * _e381); + } + let _e387 = position[3u]; + type_45 = _e387; + let _e388 = type_45; + if (_e388 > 2.5f) { + let _e390 = direction; + halfWidth_1 = _e390.xyz; + let _e392 = cone; + halfHeight_1 = _e392.xyz; + let _e394 = position; + let _e396 = v_world_position_1; + toCentre = (_e394.xyz - _e396); + let _e398 = toCentre; + let _e399 = halfWidth_1; + let _e401 = halfHeight_1; + corners_2[0i] = ((_e398 - _e399) - _e401); + let _e404 = toCentre; + let _e405 = halfWidth_1; + let _e407 = halfHeight_1; + corners_2[1i] = ((_e404 + _e405) - _e407); + let _e410 = toCentre; + let _e411 = halfWidth_1; + let _e413 = halfHeight_1; + corners_2[2i] = ((_e410 + _e411) + _e413); + let _e416 = toCentre; + let _e417 = halfWidth_1; + let _e419 = halfHeight_1; + corners_2[3i] = ((_e416 - _e417) + _e419); + let _e422 = toCentre; + let _e423 = halfWidth_1; + let _e424 = halfHeight_1; + behind = (dot(_e422, cross(_e423, _e424)) >= 0f); + let _e428 = behind; + if _e428 { local_22 = 0f; } else { - let _e428 = corners_2; - param_157 = _e428; - let _e430 = (*s_6).n; - param_158 = _e430; - let _e431 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_157), (¶m_158)); - local_22 = _e431; - } - let _e432 = local_22; - formFactor = _e432; - let _e433 = halfWidth_1; - let _e434 = halfHeight_1; - area = (length(cross(_e433, _e434)) * 4f); - let _e438 = area; - if (_e438 > 0.000000001f) { - let _e440 = area; - local_23 = (1f / _e440); + let _e429 = corners_2; + param_157 = _e429; + let _e431 = (*s_6).n; + param_158 = _e431; + let _e432 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_157), (¶m_158)); + local_22 = _e432; + } + let _e433 = local_22; + formFactor = _e433; + let _e434 = halfWidth_1; + let _e435 = halfHeight_1; + area = (length(cross(_e434, _e435)) * 4f); + let _e439 = area; + if (_e439 > 0.000000001f) { + let _e441 = area; + local_23 = (1f / _e441); } else { local_23 = 0f; } - let _e442 = local_23; - radiance = _e442; - let _e443 = toCentre; - distance_2 = length(_e443); - let _e446 = direction[3u]; - if (_e446 > 0f) { - let _e448 = distance_2; - let _e450 = direction[3u]; - ratio_1 = (_e448 / _e450); - let _e452 = ratio_1; + let _e443 = local_23; + radiance = _e443; + let _e444 = toCentre; + distance_2 = length(_e444); + let _e447 = direction[3u]; + if (_e447 > 0f) { + let _e449 = distance_2; + let _e451 = direction[3u]; + ratio_1 = (_e449 / _e451); let _e453 = ratio_1; - let _e455 = ratio_1; - let _e457 = ratio_1; - window_1 = clamp((1f - (((_e452 * _e453) * _e455) * _e457)), 0f, 1f); - let _e461 = window_1; + let _e454 = ratio_1; + let _e456 = ratio_1; + let _e458 = ratio_1; + window_1 = clamp((1f - (((_e453 * _e454) * _e456) * _e458)), 0f, 1f); let _e462 = window_1; - let _e464 = radiance; - radiance = (_e464 * (_e461 * _e462)); - } - let _e467 = (*s_6).v; - let _e470 = (*s_6).n; - mirror_1 = reflect(-(_e467), _e470); - let _e472 = position; - param_159 = _e472.xyz; - let _e474 = halfWidth_1; - param_160 = _e474; - let _e475 = halfHeight_1; - param_161 = _e475; - let _e476 = v_world_position_1; - param_162 = _e476; - let _e477 = mirror_1; - param_163 = _e477; - let _e478 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_159), (¶m_160), (¶m_161), (¶m_162), (¶m_163)); - representative = _e478; - let _e479 = representative; - let _e480 = v_world_position_1; - toPoint = (_e479 - _e480); - let _e482 = toPoint; - pointDistance = length(_e482); - let _e484 = pointDistance; - if (_e484 > 0.000001f) { - let _e486 = toPoint; - let _e487 = pointDistance; - local_24 = (_e486 / vec3(_e487)); + let _e463 = window_1; + let _e465 = radiance; + radiance = (_e465 * (_e462 * _e463)); + } + let _e468 = (*s_6).v; + let _e471 = (*s_6).n; + mirror_1 = reflect(-(_e468), _e471); + let _e473 = position; + param_159 = _e473.xyz; + let _e475 = halfWidth_1; + param_160 = _e475; + let _e476 = halfHeight_1; + param_161 = _e476; + let _e477 = v_world_position_1; + param_162 = _e477; + let _e478 = mirror_1; + param_163 = _e478; + let _e479 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_159), (¶m_160), (¶m_161), (¶m_162), (¶m_163)); + representative = _e479; + let _e480 = representative; + let _e481 = v_world_position_1; + toPoint = (_e480 - _e481); + let _e483 = toPoint; + pointDistance = length(_e483); + let _e485 = pointDistance; + if (_e485 > 0.000001f) { + let _e487 = toPoint; + let _e488 = pointDistance; + local_24 = (_e487 / vec3(_e488)); } else { - let _e491 = (*s_6).n; - local_24 = _e491; - } - let _e492 = local_24; - light_4.l = _e492; - let _e495 = light_4.l; - let _e497 = (*s_6).v; - light_4.h = normalize((_e495 + _e497)); - let _e501 = formFactor; - light_4.n_dot_l = _e501; - let _e504 = (*s_6).n; - let _e506 = light_4.h; - light_4.n_dot_h = max(dot(_e504, _e506), 0f); - let _e511 = (*s_6).v; - let _e513 = light_4.h; - light_4.v_dot_h = max(dot(_e511, _e513), 0f); - let _e517 = color_1; - let _e520 = color_1[3u]; - let _e522 = radiance; - light_4.radiance = ((_e517.xyz * _e520) * _e522); + let _e492 = (*s_6).n; + local_24 = _e492; + } + let _e493 = local_24; + light_4.l = _e493; + let _e496 = light_4.l; + let _e498 = (*s_6).v; + light_4.h = normalize((_e496 + _e498)); + let _e502 = formFactor; + light_4.n_dot_l = _e502; + let _e505 = (*s_6).n; + let _e507 = light_4.h; + light_4.n_dot_h = max(dot(_e505, _e507), 0f); + let _e512 = (*s_6).v; + let _e514 = light_4.h; + light_4.v_dot_h = max(dot(_e512, _e514), 0f); + let _e518 = color_1; + let _e521 = color_1[3u]; + let _e523 = radiance; + light_4.radiance = ((_e518.xyz * _e521) * _e523); light_4.integrated = 1f; - let _e527 = (*s_6).n; - param_164 = _e527; - let _e529 = (*s_6).v; - param_165 = _e529; - let _e531 = (*s_6).roughness; - param_166 = _e531; - let _e532 = corners_2; - param_167 = _e532; - let _e533 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_164), (¶m_165), (¶m_166), (¶m_167)); - light_4.ltc = _e533; - let _e535 = corners_2; - g_rect_corners = _e535; - let _e536 = light_4; - return _e536; - } - let _e537 = direction; - aim = normalize(_e537.xyz); + let _e528 = (*s_6).n; + param_164 = _e528; + let _e530 = (*s_6).v; + param_165 = _e530; + let _e532 = (*s_6).roughness; + param_166 = _e532; + let _e533 = corners_2; + param_167 = _e533; + let _e534 = LtcRectangle_u0028_vf3_u003b_vf3_u003b_f1_u003b_vf3_u005b_4_u005d_u003b((¶m_164), (¶m_165), (¶m_166), (¶m_167)); + light_4.ltc = _e534; + let _e536 = corners_2; + g_rect_corners = _e536; + let _e537 = light_4; + return _e537; + } + let _e538 = direction; + aim = normalize(_e538.xyz); attenuation_1 = 1f; - let _e540 = type_45; - if (_e540 < 0.5f) { - let _e542 = aim; - light_4.l = -(_e542); + let _e541 = type_45; + if (_e541 < 0.5f) { + let _e543 = aim; + light_4.l = -(_e543); } else { - let _e545 = position; - let _e547 = v_world_position_1; - toLight_1 = (_e545.xyz - _e547); - let _e549 = toLight_1; - distance_3 = length(_e549); - let _e551 = distance_3; - if (_e551 < 0.000001f) { - let _e554 = (*s_6).n; - light_4.l = _e554; - let _e557 = (*s_6).n; - light_4.h = _e557; + let _e546 = position; + let _e548 = v_world_position_1; + toLight_1 = (_e546.xyz - _e548); + let _e550 = toLight_1; + distance_3 = length(_e550); + let _e552 = distance_3; + if (_e552 < 0.000001f) { + let _e555 = (*s_6).n; + light_4.l = _e555; + let _e558 = (*s_6).n; + light_4.h = _e558; light_4.radiance = vec3(0f, 0f, 0f); light_4.n_dot_l = 0f; light_4.n_dot_h = 0f; light_4.v_dot_h = 0f; - let _e563 = light_4; - return _e563; - } - let _e564 = toLight_1; - let _e565 = distance_3; - light_4.l = (_e564 / vec3(_e565)); - let _e569 = distance_3; - param_168 = _e569; - let _e571 = direction[3u]; - param_169 = _e571; - let _e572 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_168), (¶m_169)); - attenuation_1 = _e572; - let _e573 = type_45; - if (_e573 > 1.5f) { - let _e575 = aim; - let _e577 = light_4.l; - cosAngle = dot(_e575, -(_e577)); - let _e580 = cosAngle; - let _e582 = cone[1u]; - let _e585 = cone[0u]; - let _e587 = cone[1u]; - let _e591 = attenuation_1; - attenuation_1 = (_e591 * clamp(((_e580 - _e582) / (_e585 - _e587)), 0f, 1f)); - } - } - let _e594 = light_4.l; - let _e596 = (*s_6).v; - light_4.h = normalize((_e594 + _e596)); - let _e601 = (*s_6).n; - let _e603 = light_4.l; - light_4.n_dot_l = max(dot(_e601, _e603), 0f); - let _e608 = (*s_6).n; - let _e610 = light_4.h; - light_4.n_dot_h = max(dot(_e608, _e610), 0f); - let _e615 = (*s_6).v; - let _e617 = light_4.h; - light_4.v_dot_h = max(dot(_e615, _e617), 0f); - let _e621 = color_1; - let _e624 = color_1[3u]; - let _e626 = attenuation_1; - light_4.radiance = ((_e621.xyz * _e624) * _e626); - let _e629 = light_4; - return _e629; + let _e564 = light_4; + return _e564; + } + let _e565 = toLight_1; + let _e566 = distance_3; + light_4.l = (_e565 / vec3(_e566)); + let _e570 = distance_3; + param_168 = _e570; + let _e572 = direction[3u]; + param_169 = _e572; + let _e573 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_168), (¶m_169)); + attenuation_1 = _e573; + let _e574 = type_45; + if (_e574 > 1.5f) { + let _e576 = aim; + let _e578 = light_4.l; + cosAngle = dot(_e576, -(_e578)); + let _e581 = cosAngle; + let _e583 = cone[1u]; + let _e586 = cone[0u]; + let _e588 = cone[1u]; + let _e592 = attenuation_1; + attenuation_1 = (_e592 * clamp(((_e581 - _e583) / (_e586 - _e588)), 0f, 1f)); + } + } + let _e595 = light_4.l; + let _e597 = (*s_6).v; + light_4.h = normalize((_e595 + _e597)); + let _e602 = (*s_6).n; + let _e604 = light_4.l; + light_4.n_dot_l = max(dot(_e602, _e604), 0f); + let _e609 = (*s_6).n; + let _e611 = light_4.h; + light_4.n_dot_h = max(dot(_e609, _e611), 0f); + let _e616 = (*s_6).v; + let _e618 = light_4.h; + light_4.v_dot_h = max(dot(_e616, _e618), 0f); + let _e622 = color_1; + let _e625 = color_1[3u]; + let _e627 = attenuation_1; + light_4.radiance = ((_e622.xyz * _e625) * _e627); + let _e630 = light_4; + return _e630; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -18690,58 +18775,58 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var param_170: f32; var param_171: f32; - let _e276 = light_list_info.cluster_view_projection; - let _e277 = (*world_3); - clip_1 = (_e276 * vec4(_e277.x, _e277.y, _e277.z, 1f)); - let _e283 = clip_1; - let _e286 = clip_1[3u]; - ndc_1 = (_e283.xy / vec2(max(_e286, 0.000001f))); - let _e291 = light_list_info.cluster_grid; - grid = _e291.xyz; - let _e295 = light_list_info.cluster_depth[0u]; - near_1 = _e295; - let _e297 = ndc_1[0u]; - let _e301 = grid[0u]; - let _e305 = grid[0u]; - tx = clamp(floor((((_e297 * 0.5f) + 0.5f) * _e301)), 0f, (_e305 - 1f)); - let _e309 = ndc_1[1u]; - let _e313 = grid[1u]; - let _e317 = grid[1u]; - ty = clamp(floor((((_e309 * 0.5f) + 0.5f) * _e313)), 0f, (_e317 - 1f)); - let _e321 = clip_1[3u]; - let _e322 = near_1; - if (_e321 <= _e322) { + let _e277 = light_list_info.cluster_view_projection; + let _e278 = (*world_3); + clip_1 = (_e277 * vec4(_e278.x, _e278.y, _e278.z, 1f)); + let _e284 = clip_1; + let _e287 = clip_1[3u]; + ndc_1 = (_e284.xy / vec2(max(_e287, 0.000001f))); + let _e292 = light_list_info.cluster_grid; + grid = _e292.xyz; + let _e296 = light_list_info.cluster_depth[0u]; + near_1 = _e296; + let _e298 = ndc_1[0u]; + let _e302 = grid[0u]; + let _e306 = grid[0u]; + tx = clamp(floor((((_e298 * 0.5f) + 0.5f) * _e302)), 0f, (_e306 - 1f)); + let _e310 = ndc_1[1u]; + let _e314 = grid[1u]; + let _e318 = grid[1u]; + ty = clamp(floor((((_e310 * 0.5f) + 0.5f) * _e314)), 0f, (_e318 - 1f)); + let _e322 = clip_1[3u]; + let _e323 = near_1; + if (_e322 <= _e323) { local_25 = 0f; } else { - let _e325 = clip_1[3u]; - let _e326 = near_1; - let _e331 = light_list_info.cluster_depth[1u]; - let _e335 = grid[2u]; - local_25 = clamp(floor((log((_e325 / _e326)) * _e331)), 0f, (_e335 - 1f)); - } - let _e338 = local_25; - tz = _e338; - let _e339 = tx; - let _e340 = ty; - let _e342 = grid[0u]; - let _e345 = tz; - let _e347 = grid[0u]; - let _e350 = grid[1u]; - cell = ((_e339 + (_e340 * _e342)) + ((_e345 * _e347) * _e350)); - let _e353 = cell; - row_3 = floor((_e353 / 4f)); - let _e356 = cell; - let _e357 = row_3; - let _e362 = light_list_info.cluster_depth[2u]; - let _e363 = row_3; - param_170 = (_e356 - (_e357 * 4f)); - param_171 = (_e362 + _e363); - let _e365 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_170), (¶m_171)); - header = _e365; - let _e367 = header[0u]; - g_cluster_offset = _e367; - let _e369 = header[1u]; - g_cluster_count = _e369; + let _e326 = clip_1[3u]; + let _e327 = near_1; + let _e332 = light_list_info.cluster_depth[1u]; + let _e336 = grid[2u]; + local_25 = clamp(floor((log((_e326 / _e327)) * _e332)), 0f, (_e336 - 1f)); + } + let _e339 = local_25; + tz = _e339; + let _e340 = tx; + let _e341 = ty; + let _e343 = grid[0u]; + let _e346 = tz; + let _e348 = grid[0u]; + let _e351 = grid[1u]; + cell = ((_e340 + (_e341 * _e343)) + ((_e346 * _e348) * _e351)); + let _e354 = cell; + row_3 = floor((_e354 / 4f)); + let _e357 = cell; + let _e358 = row_3; + let _e363 = light_list_info.cluster_depth[2u]; + let _e364 = row_3; + param_170 = (_e357 - (_e358 * 4f)); + param_171 = (_e363 + _e364); + let _e366 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_170), (¶m_171)); + header = _e366; + let _e368 = header[0u]; + g_cluster_offset = _e368; + let _e370 = header[1u]; + g_cluster_count = _e370; return; } @@ -18749,19 +18834,19 @@ fn LightCount_u0028_() -> i32 { var tail: f32; var param_172: vec3; - let _e265 = light_list_info.list[0u]; - tail = _e265; - let _e266 = Clustered_u0028_(); - if _e266 { - let _e267 = v_world_position_1; - param_172 = _e267; + let _e266 = light_list_info.list[0u]; + tail = _e266; + let _e267 = Clustered_u0028_(); + if _e267 { + let _e268 = v_world_position_1; + param_172 = _e268; FindCluster_u0028_vf3_u003b((¶m_172)); - let _e268 = g_cluster_count; - tail = _e268; + let _e269 = g_cluster_count; + tail = _e269; } - let _e271 = frag_info.frame_params[1u]; - let _e275 = tail; - return (clamp(i32((_e271 + 0.5f)), 0i, 8i) + clamp(i32((_e275 + 0.5f)), 0i, 24i)); + let _e272 = frag_info.frame_params[1u]; + let _e276 = tail; + return (clamp(i32((_e272 + 0.5f)), 0i, 8i) + clamp(i32((_e276 + 0.5f)), 0i, 24i)); } fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) -> vec3 { @@ -18784,87 +18869,89 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_183: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e279 = LightCount_u0028_(); - count_1 = _e279; + let _e280 = LightCount_u0028_(); + count_1 = _e280; i_9 = 0i; loop { - let _e280 = i_9; - if (_e280 < 32i) { - let _e282 = i_9; - let _e283 = count_1; - if (_e282 >= _e283) { + let _e281 = i_9; + if (_e281 < 32i) { + let _e283 = i_9; + let _e284 = count_1; + if (_e283 >= _e284) { break; } - let _e285 = i_9; - param_173 = _e285; - let _e286 = (*s_7); - param_174 = _e286; - let _e287 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_173), (¶m_174)); - light_5 = _e287; - let _e289 = light_5.n_dot_l; - if (_e289 <= 0f) { + let _e286 = i_9; + param_173 = _e286; + let _e287 = (*s_7); + param_174 = _e287; + let _e288 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_173), (¶m_174)); + light_5 = _e288; + let _e290 = light_5.n_dot_l; + if (_e290 <= 0f) { continue; } - let _e291 = i_9; - param_175 = _e291; - let _e292 = LightHasShadow_u0028_i1_u003b((¶m_175)); - if _e292 { - let _e293 = (*s_7); - param_176 = _e293; - let _e294 = light_5; - param_177 = _e294; - let _e295 = i_9; - param_178 = _e295; - let _e296 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_176), (¶m_177), (¶m_178)); - let _e297 = v_world_position_1; - param_179 = _e297; - let _e299 = (*s_7).n; - param_180 = _e299; - let _e300 = i_9; - param_181 = _e300; - let _e301 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_179), (¶m_180), (¶m_181)); - local_26 = (_e296 * _e301); + light_transmittance = vec3(1f, 1f, 1f); + let _e292 = i_9; + param_175 = _e292; + let _e293 = LightHasShadow_u0028_i1_u003b((¶m_175)); + if _e293 { + let _e294 = (*s_7); + param_176 = _e294; + let _e295 = light_5; + param_177 = _e295; + let _e296 = i_9; + param_178 = _e296; + let _e297 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_176), (¶m_177), (¶m_178)); + let _e298 = v_world_position_1; + param_179 = _e298; + let _e300 = (*s_7).n; + param_180 = _e300; + let _e301 = i_9; + param_181 = _e301; + let _e302 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_179), (¶m_180), (¶m_181)); + local_26 = (_e297 * _e302); } else { local_26 = 1f; } - let _e303 = local_26; - visibility = _e303; - let _e304 = visibility; - if (_e304 <= 0f) { + let _e304 = local_26; + visibility = _e304; + let _e305 = visibility; + if (_e305 <= 0f) { continue; } - let _e306 = (*s_7); - param_182 = _e306; - let _e307 = light_5; - param_183 = _e307; - let _e308 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_182), (¶m_183)); - let _e310 = light_5.radiance; - let _e313 = light_5.n_dot_l; - let _e315 = visibility; - let _e317 = total_1; - total_1 = (_e317 + (((_e308 * _e310) * _e313) * _e315)); + let _e307 = (*s_7); + param_182 = _e307; + let _e308 = light_5; + param_183 = _e308; + let _e309 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_182), (¶m_183)); + let _e311 = light_5.radiance; + let _e314 = light_5.n_dot_l; + let _e316 = visibility; + let _e318 = light_transmittance; + let _e320 = total_1; + total_1 = (_e320 + ((((_e309 * _e311) * _e314) * _e316) * _e318)); continue; } else { break; } continuing { - let _e319 = i_9; - i_9 = (_e319 + 1i); + let _e322 = i_9; + i_9 = (_e322 + 1i); } } - let _e321 = total_1; - return _e321; + let _e324 = total_1; + return _e324; } fn SampleLightmap_u0028_() -> vec3 { var texel_4: vec4; - let _e262 = v_lightmap_uv_1; - let _e263 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e262); - texel_4 = _e263; - let _e264 = texel_4; - let _e267 = texel_4[3u]; - return ((_e264.xyz * _e267) * 8f); + let _e263 = v_lightmap_uv_1; + let _e264 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e263); + texel_4 = _e264; + let _e265 = texel_4; + let _e268 = texel_4[3u]; + return ((_e265.xyz * _e268) * 8f); } fn SceneColourLevel_u0028_vf2_u003b_i1_u003b(uv_6: ptr>, level: ptr) -> vec3 { @@ -18872,22 +18959,22 @@ fn SceneColourLevel_u0028_vf2_u003b_i1_u003b(uv_6: ptr>, lev var inset_1: vec2; var at_4: vec2; - let _e266 = (*level); - let _e269 = layer_info.scene_levels[_e266]; - rect = _e269; - let _e271 = layer_info.scene_colour; - inset_1 = (_e271.yz * 0.5f); - let _e274 = rect; - let _e276 = (*uv_6); - let _e277 = rect; - let _e280 = inset_1; - let _e281 = rect; - let _e283 = inset_1; - let _e285 = inset_1; - at_4 = (_e274.xy + clamp((_e276 * _e277.zw), _e280, max((_e281.zw - _e283), _e285))); - let _e289 = at_4; - let _e290 = textureSampleLevel(scene_colour_texture_tex, scene_colour_texture_smp, _e289, 0f); - return _e290.xyz; + let _e267 = (*level); + let _e270 = layer_info.scene_levels[_e267]; + rect = _e270; + let _e272 = layer_info.scene_colour; + inset_1 = (_e272.yz * 0.5f); + let _e275 = rect; + let _e277 = (*uv_6); + let _e278 = rect; + let _e281 = inset_1; + let _e282 = rect; + let _e284 = inset_1; + let _e286 = inset_1; + at_4 = (_e275.xy + clamp((_e277 * _e278.zw), _e281, max((_e282.zw - _e284), _e286))); + let _e290 = at_4; + let _e291 = textureSampleLevel(scene_colour_texture_tex, scene_colour_texture_smp, _e290, 0f); + return _e291.xyz; } fn SceneColourAt_u0028_vf3_u003b_f1_u003b(world_4: ptr>, lod: ptr) -> vec3 { @@ -18905,78 +18992,78 @@ fn SceneColourAt_u0028_vf3_u003b_f1_u003b(world_4: ptr>, lod var param_186: vec2; var param_187: i32; - let _e277 = layer_info.scene_view_projection; - let _e278 = (*world_4); - clip_2 = (_e277 * vec4(_e278.x, _e278.y, _e278.z, 1f)); - let _e284 = clip_2; - let _e287 = clip_2[3u]; - ndc_2 = (_e284.xy / vec2(max(_e287, 0.000001f))); - let _e292 = ndc_2[0u]; - let _e296 = ndc_2[1u]; - view = clamp(vec2(((_e292 * 0.5f) + 0.5f), (0.5f - (_e296 * 0.5f))), vec2(0f, 0f), vec2(1f, 1f)); - let _e302 = layer_info.scene_viewport; - let _e304 = view; - let _e306 = layer_info.scene_viewport; - uv_7 = (_e302.xy + (_e304 * _e306.zw)); - let _e312 = layer_info.scene_colour[0u]; - top = (_e312 - 1f); - let _e314 = (*lod); - let _e315 = top; - level_1 = clamp(_e314, 0f, _e315); - let _e317 = level_1; - lower = floor(_e317); - let _e319 = lower; - let _e321 = uv_7; - param_184 = _e321; - param_185 = i32(_e319); - let _e322 = SceneColourLevel_u0028_vf2_u003b_i1_u003b((¶m_184), (¶m_185)); - near_2 = _e322; - let _e323 = lower; - let _e325 = top; - let _e328 = uv_7; - param_186 = _e328; - param_187 = i32(min((_e323 + 1f), _e325)); - let _e329 = SceneColourLevel_u0028_vf2_u003b_i1_u003b((¶m_186), (¶m_187)); - far_1 = _e329; - let _e330 = near_2; - let _e331 = far_1; - let _e332 = level_1; - let _e333 = lower; - return mix(_e330, _e331, vec3((_e332 - _e333))); + let _e278 = layer_info.scene_view_projection; + let _e279 = (*world_4); + clip_2 = (_e278 * vec4(_e279.x, _e279.y, _e279.z, 1f)); + let _e285 = clip_2; + let _e288 = clip_2[3u]; + ndc_2 = (_e285.xy / vec2(max(_e288, 0.000001f))); + let _e293 = ndc_2[0u]; + let _e297 = ndc_2[1u]; + view = clamp(vec2(((_e293 * 0.5f) + 0.5f), (0.5f - (_e297 * 0.5f))), vec2(0f, 0f), vec2(1f, 1f)); + let _e303 = layer_info.scene_viewport; + let _e305 = view; + let _e307 = layer_info.scene_viewport; + uv_7 = (_e303.xy + (_e305 * _e307.zw)); + let _e313 = layer_info.scene_colour[0u]; + top = (_e313 - 1f); + let _e315 = (*lod); + let _e316 = top; + level_1 = clamp(_e315, 0f, _e316); + let _e318 = level_1; + lower = floor(_e318); + let _e320 = lower; + let _e322 = uv_7; + param_184 = _e322; + param_185 = i32(_e320); + let _e323 = SceneColourLevel_u0028_vf2_u003b_i1_u003b((¶m_184), (¶m_185)); + near_2 = _e323; + let _e324 = lower; + let _e326 = top; + let _e329 = uv_7; + param_186 = _e329; + param_187 = i32(min((_e324 + 1f), _e326)); + let _e330 = SceneColourLevel_u0028_vf2_u003b_i1_u003b((¶m_186), (¶m_187)); + far_1 = _e330; + let _e331 = near_2; + let _e332 = far_1; + let _e333 = level_1; + let _e334 = lower; + return mix(_e331, _e332, vec3((_e333 - _e334))); } fn IorInfinite_u0028_() -> bool { - let _e263 = layer_info.coat[2u]; - return (_e263 == 0f); + let _e264 = layer_info.coat[2u]; + return (_e264 == 0f); } fn DispersionSpread_u0028_f1_u003b(ior: ptr) -> f32 { var local_27: f32; - let _e263 = IorInfinite_u0028_(); - if _e263 { + let _e264 = IorInfinite_u0028_(); + if _e264 { local_27 = 0f; } else { - let _e264 = (*ior); - let _e269 = layer_info.transmission[3u]; - local_27 = (((_e264 - 1f) * 0.025f) * _e269); + let _e265 = (*ior); + let _e270 = layer_info.transmission[3u]; + local_27 = (((_e265 - 1f) * 0.025f) * _e270); } - let _e271 = local_27; - return _e271; + let _e272 = local_27; + return _e272; } fn RefractionIor_u0028_() -> f32 { var local_28: f32; - let _e262 = IorInfinite_u0028_(); - if _e262 { + let _e263 = IorInfinite_u0028_(); + if _e263 { local_28 = 10000f; } else { - let _e265 = layer_info.coat[2u]; - local_28 = max(_e265, 1f); + let _e266 = layer_info.coat[2u]; + local_28 = max(_e266, 1f); } - let _e267 = local_28; - return _e267; + let _e268 = local_28; + return _e268; } fn SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_8: ptr) -> vec3 { @@ -18999,81 +19086,81 @@ fn SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3 var param_193: vec3; var param_194: f32; - let _e280 = RefractionIor_u0028_(); - ior_1 = _e280; - let _e281 = ior_1; - param_188 = _e281; - let _e282 = DispersionSpread_u0028_f1_u003b((¶m_188)); - spread_1 = _e282; - let _e286 = layer_info.scene_levels[0i][2u]; - let _e289 = layer_info.scene_colour[1u]; - width = (_e286 / max(_e289, 0.000000001f)); - let _e292 = width; - let _e296 = (*s_8).roughness; - let _e298 = ior_1; - lod_1 = ((log2(max(_e292, 1f)) * _e296) * clamp(((_e298 * 2f) - 2f), 0f, 1f)); - let _e303 = g_thickness; - thin = (_e303 <= 0f); - let _e305 = thin; - if _e305 { - let _e307 = (*s_8).v; - local_29 = -(_e307); + let _e281 = RefractionIor_u0028_(); + ior_1 = _e281; + let _e282 = ior_1; + param_188 = _e282; + let _e283 = DispersionSpread_u0028_f1_u003b((¶m_188)); + spread_1 = _e283; + let _e287 = layer_info.scene_levels[0i][2u]; + let _e290 = layer_info.scene_colour[1u]; + width = (_e287 / max(_e290, 0.000000001f)); + let _e293 = width; + let _e297 = (*s_8).roughness; + let _e299 = ior_1; + lod_1 = ((log2(max(_e293, 1f)) * _e297) * clamp(((_e299 * 2f) - 2f), 0f, 1f)); + let _e304 = g_thickness; + thin = (_e304 <= 0f); + let _e306 = thin; + if _e306 { + let _e308 = (*s_8).v; + local_29 = -(_e308); } else { - let _e310 = (*s_8).v; - let _e313 = (*s_8).n; - let _e314 = ior_1; - let _e315 = spread_1; - local_29 = refract(-(_e310), _e313, (1f / max((_e314 - _e315), 1f))); - } - let _e320 = local_29; - red = _e320; - let _e321 = thin; - if _e321 { - let _e323 = (*s_8).v; - local_30 = -(_e323); + let _e311 = (*s_8).v; + let _e314 = (*s_8).n; + let _e315 = ior_1; + let _e316 = spread_1; + local_29 = refract(-(_e311), _e314, (1f / max((_e315 - _e316), 1f))); + } + let _e321 = local_29; + red = _e321; + let _e322 = thin; + if _e322 { + let _e324 = (*s_8).v; + local_30 = -(_e324); } else { - let _e326 = (*s_8).v; - let _e329 = (*s_8).n; - let _e330 = ior_1; - local_30 = refract(-(_e326), _e329, (1f / _e330)); - } - let _e333 = local_30; - green = _e333; - let _e334 = thin; - if _e334 { - let _e336 = (*s_8).v; - local_31 = -(_e336); + let _e327 = (*s_8).v; + let _e330 = (*s_8).n; + let _e331 = ior_1; + local_30 = refract(-(_e327), _e330, (1f / _e331)); + } + let _e334 = local_30; + green = _e334; + let _e335 = thin; + if _e335 { + let _e337 = (*s_8).v; + local_31 = -(_e337); } else { - let _e339 = (*s_8).v; - let _e342 = (*s_8).n; - let _e343 = ior_1; - let _e344 = spread_1; - local_31 = refract(-(_e339), _e342, (1f / (_e343 + _e344))); - } - let _e348 = local_31; - blue = _e348; - let _e349 = v_world_position_1; - let _e350 = red; - let _e351 = g_thickness; - param_189 = (_e349 + (_e350 * _e351)); - let _e354 = lod_1; - param_190 = _e354; - let _e355 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_189), (¶m_190)); - let _e357 = v_world_position_1; - let _e358 = green; - let _e359 = g_thickness; - param_191 = (_e357 + (_e358 * _e359)); - let _e362 = lod_1; - param_192 = _e362; - let _e363 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_191), (¶m_192)); - let _e365 = v_world_position_1; - let _e366 = blue; - let _e367 = g_thickness; - param_193 = (_e365 + (_e366 * _e367)); - let _e370 = lod_1; - param_194 = _e370; - let _e371 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_193), (¶m_194)); - return vec3(_e355.x, _e363.y, _e371.z); + let _e340 = (*s_8).v; + let _e343 = (*s_8).n; + let _e344 = ior_1; + let _e345 = spread_1; + local_31 = refract(-(_e340), _e343, (1f / (_e344 + _e345))); + } + let _e349 = local_31; + blue = _e349; + let _e350 = v_world_position_1; + let _e351 = red; + let _e352 = g_thickness; + param_189 = (_e350 + (_e351 * _e352)); + let _e355 = lod_1; + param_190 = _e355; + let _e356 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_189), (¶m_190)); + let _e358 = v_world_position_1; + let _e359 = green; + let _e360 = g_thickness; + param_191 = (_e358 + (_e359 * _e360)); + let _e363 = lod_1; + param_192 = _e363; + let _e364 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_191), (¶m_192)); + let _e366 = v_world_position_1; + let _e367 = blue; + let _e368 = g_thickness; + param_193 = (_e366 + (_e367 * _e368)); + let _e371 = lod_1; + param_194 = _e371; + let _e372 = SceneColourAt_u0028_vf3_u003b_f1_u003b((¶m_193), (¶m_194)); + return vec3(_e356.x, _e364.y, _e372.z); } fn TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_f1_u003b(s_9: ptr, levels: ptr) -> vec3 { @@ -19089,71 +19176,71 @@ fn TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u var blue_1: vec3; var local_34: vec3; - let _e274 = RefractionIor_u0028_(); - ior_2 = _e274; - let _e275 = ior_2; - param_195 = _e275; - let _e276 = DispersionSpread_u0028_f1_u003b((¶m_195)); - spread_2 = _e276; - let _e278 = (*s_9).roughness; - let _e279 = ior_2; - let _e284 = (*levels); - lod_2 = ((_e278 * clamp(((_e279 * 2f) - 2f), 0f, 1f)) * _e284); - let _e286 = g_thickness; - thin_1 = (_e286 <= 0f); - let _e288 = thin_1; - if _e288 { - let _e290 = (*s_9).v; - local_32 = -(_e290); + let _e275 = RefractionIor_u0028_(); + ior_2 = _e275; + let _e276 = ior_2; + param_195 = _e276; + let _e277 = DispersionSpread_u0028_f1_u003b((¶m_195)); + spread_2 = _e277; + let _e279 = (*s_9).roughness; + let _e280 = ior_2; + let _e285 = (*levels); + lod_2 = ((_e279 * clamp(((_e280 * 2f) - 2f), 0f, 1f)) * _e285); + let _e287 = g_thickness; + thin_1 = (_e287 <= 0f); + let _e289 = thin_1; + if _e289 { + let _e291 = (*s_9).v; + local_32 = -(_e291); } else { - let _e293 = (*s_9).v; - let _e296 = (*s_9).n; - let _e297 = ior_2; - let _e298 = spread_2; - local_32 = refract(-(_e293), _e296, (1f / max((_e297 - _e298), 1f))); - } - let _e303 = local_32; - red_1 = _e303; - let _e304 = thin_1; - if _e304 { - let _e306 = (*s_9).v; - local_33 = -(_e306); + let _e294 = (*s_9).v; + let _e297 = (*s_9).n; + let _e298 = ior_2; + let _e299 = spread_2; + local_32 = refract(-(_e294), _e297, (1f / max((_e298 - _e299), 1f))); + } + let _e304 = local_32; + red_1 = _e304; + let _e305 = thin_1; + if _e305 { + let _e307 = (*s_9).v; + local_33 = -(_e307); } else { - let _e309 = (*s_9).v; - let _e312 = (*s_9).n; - let _e313 = ior_2; - local_33 = refract(-(_e309), _e312, (1f / _e313)); - } - let _e316 = local_33; - green_1 = _e316; - let _e317 = thin_1; - if _e317 { - let _e319 = (*s_9).v; - local_34 = -(_e319); + let _e310 = (*s_9).v; + let _e313 = (*s_9).n; + let _e314 = ior_2; + local_33 = refract(-(_e310), _e313, (1f / _e314)); + } + let _e317 = local_33; + green_1 = _e317; + let _e318 = thin_1; + if _e318 { + let _e320 = (*s_9).v; + local_34 = -(_e320); } else { - let _e322 = (*s_9).v; - let _e325 = (*s_9).n; - let _e326 = ior_2; - let _e327 = spread_2; - local_34 = refract(-(_e322), _e325, (1f / (_e326 + _e327))); - } - let _e331 = local_34; - blue_1 = _e331; - let _e332 = red_1; - let _e333 = lod_2; - let _e334 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e332, _e333); - let _e336 = green_1; - let _e337 = lod_2; - let _e338 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e336, _e337); - let _e340 = blue_1; - let _e341 = lod_2; - let _e342 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e340, _e341); - return vec3(_e334.x, _e338.y, _e342.z); + let _e323 = (*s_9).v; + let _e326 = (*s_9).n; + let _e327 = ior_2; + let _e328 = spread_2; + local_34 = refract(-(_e323), _e326, (1f / (_e327 + _e328))); + } + let _e332 = local_34; + blue_1 = _e332; + let _e333 = red_1; + let _e334 = lod_2; + let _e335 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e333, _e334); + let _e337 = green_1; + let _e338 = lod_2; + let _e339 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e337, _e338); + let _e341 = blue_1; + let _e342 = lod_2; + let _e343 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e341, _e342); + return vec3(_e335.x, _e339.y, _e343.z); } fn SceneColourBound_u0028_() -> bool { - let _e263 = layer_info.scene_colour[0u]; - return (_e263 > 0f); + let _e264 = layer_info.scene_colour[0u]; + return (_e264 > 0f); } fn EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b(rho_2: ptr>, r_4: ptr, mu_1: ptr) -> vec3 { @@ -19164,23 +19251,23 @@ fn EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b(rho_2: ptr>, var param_199: vec3; var param_200: f32; - let _e270 = (*mu_1); - param_196 = _e270; - let _e271 = (*r_4); - param_197 = _e271; - let _e272 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_196), (¶m_197)); - e_1 = _e272; - let _e273 = (*rho_2); - let _e274 = e_1; - let _e276 = (*r_4); - param_198 = _e276; - let _e277 = FonAverage_u0028_f1_u003b((¶m_198)); - let _e278 = (*rho_2); - param_199 = _e278; - param_200 = _e277; - let _e279 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_199), (¶m_200)); - let _e280 = e_1; - return ((_e273 * _e274) + (_e279 * (1f - _e280))); + let _e271 = (*mu_1); + param_196 = _e271; + let _e272 = (*r_4); + param_197 = _e272; + let _e273 = FonAlbedo_u0028_f1_u003b_f1_u003b((¶m_196), (¶m_197)); + e_1 = _e273; + let _e274 = (*rho_2); + let _e275 = e_1; + let _e277 = (*r_4); + param_198 = _e277; + let _e278 = FonAverage_u0028_f1_u003b((¶m_198)); + let _e279 = (*rho_2); + param_199 = _e279; + param_200 = _e278; + let _e280 = EonMultiAlbedo_u0028_vf3_u003b_f1_u003b((¶m_199), (¶m_200)); + let _e281 = e_1; + return ((_e274 * _e275) + (_e280 * (1f - _e281))); } fn IridescenceSensitivity_u0028_f1_u003b_vf3_u003b(opd: ptr, shift: ptr>) -> vec3 { @@ -19190,30 +19277,30 @@ fn IridescenceSensitivity_u0028_f1_u003b_vf3_u003b(opd: ptr, shif var variance_1: vec3; var xyz: vec3; - let _e268 = (*opd); - phase = ((6.2831855f * _e268) * 0.000000001f); + let _e269 = (*opd); + phase = ((6.2831855f * _e269) * 0.000000001f); val = vec3(0.00000000000054856f, 0.00000000000044201f, 0.00000000000052481f); pos = vec3(1681000f, 1795300f, 2208400f); variance_1 = vec3(4327800000f, 9304600000f, 6612100000f); - let _e271 = val; - let _e272 = variance_1; - let _e276 = pos; - let _e277 = phase; - let _e279 = (*shift); - let _e283 = phase; + let _e272 = val; + let _e273 = variance_1; + let _e277 = pos; + let _e278 = phase; + let _e280 = (*shift); let _e284 = phase; - let _e287 = variance_1; - xyz = (((_e271 * sqrt((_e272 * 6.2831855f))) * cos(((_e276 * _e277) + _e279))) * exp((_e287 * -((_e283 * _e284))))); - let _e291 = phase; - let _e294 = (*shift)[0u]; - let _e298 = phase; - let _e300 = phase; - let _e305 = xyz[0u]; - xyz[0u] = (_e305 + ((0.00000001644083f * cos(((2239900f * _e291) + _e294))) * exp(((-4528200000f * _e298) * _e300)))); - let _e308 = xyz; - xyz = (_e308 / vec3(0.00000010685f)); - let _e311 = xyz; - return (mat3x3(vec3(3.2404542f, -0.969266f, 0.0556434f), vec3(-1.5371385f, 1.8760108f, -0.2040259f), vec3(-0.4985314f, 0.041556f, 1.0572252f)) * _e311); + let _e285 = phase; + let _e288 = variance_1; + xyz = (((_e272 * sqrt((_e273 * 6.2831855f))) * cos(((_e277 * _e278) + _e280))) * exp((_e288 * -((_e284 * _e285))))); + let _e292 = phase; + let _e295 = (*shift)[0u]; + let _e299 = phase; + let _e301 = phase; + let _e306 = xyz[0u]; + xyz[0u] = (_e306 + ((0.00000001644083f * cos(((2239900f * _e292) + _e295))) * exp(((-4528200000f * _e299) * _e301)))); + let _e309 = xyz; + xyz = (_e309 / vec3(0.00000010685f)); + let _e312 = xyz; + return (mat3x3(vec3(3.2404542f, -0.969266f, 0.0556434f), vec3(-1.5371385f, 1.8760108f, -0.2040259f), vec3(-0.4985314f, 0.041556f, 1.0572252f)) * _e312); } fn FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b(film: ptr, cos1_: ptr, thickness: ptr, base: ptr>) -> vec3 { @@ -19243,115 +19330,115 @@ fn FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b(film: ptr; var local_35: vec3; - let _e290 = (*film); - let _e291 = (*thickness); - eta2_ = mix(1f, _e290, smoothstep(0f, 0.03f, _e291)); - let _e294 = (*cos1_); + let _e291 = (*film); + let _e292 = (*thickness); + eta2_ = mix(1f, _e291, smoothstep(0f, 0.03f, _e292)); let _e295 = (*cos1_); - let _e298 = eta2_; + let _e296 = (*cos1_); let _e299 = eta2_; - sin2Sq = ((1f - (_e294 * _e295)) / (_e298 * _e299)); - let _e302 = sin2Sq; - cos2Sq = (1f - _e302); - let _e304 = cos2Sq; - cos2_ = sqrt(max(_e304, 0f)); - let _e307 = eta2_; - let _e309 = eta2_; - r0_ = ((_e307 - 1f) / (_e309 + 1f)); - let _e312 = r0_; + let _e300 = eta2_; + sin2Sq = ((1f - (_e295 * _e296)) / (_e299 * _e300)); + let _e303 = sin2Sq; + cos2Sq = (1f - _e303); + let _e305 = cos2Sq; + cos2_ = sqrt(max(_e305, 0f)); + let _e308 = eta2_; + let _e310 = eta2_; + r0_ = ((_e308 - 1f) / (_e310 + 1f)); let _e313 = r0_; - let _e315 = r0_; + let _e314 = r0_; let _e316 = r0_; - let _e319 = (*cos1_); - r12_ = ((_e312 * _e313) + ((1f - (_e315 * _e316)) * pow((1f - _e319), 5f))); - let _e324 = r12_; - t121_ = (1f - _e324); - let _e326 = eta2_; - phi12_ = select(0f, 3.1415927f, (_e326 < 1f)); - let _e329 = phi12_; - phi21_ = (3.1415927f - _e329); - let _e331 = (*base); - sqrtBase = sqrt(clamp(_e331, vec3(0f, 0f, 0f), vec3(0.9999f, 0.9999f, 0.9999f))); - let _e334 = sqrtBase; - let _e336 = sqrtBase; - baseIor = ((vec3(1f, 1f, 1f) + _e334) / (vec3(1f, 1f, 1f) - _e336)); - let _e339 = baseIor; - let _e340 = eta2_; - let _e343 = baseIor; - let _e344 = eta2_; - r1_ = ((_e339 - vec3(_e340)) / (_e343 + vec3(_e344))); - let _e348 = r1_; + let _e317 = r0_; + let _e320 = (*cos1_); + r12_ = ((_e313 * _e314) + ((1f - (_e316 * _e317)) * pow((1f - _e320), 5f))); + let _e325 = r12_; + t121_ = (1f - _e325); + let _e327 = eta2_; + phi12_ = select(0f, 3.1415927f, (_e327 < 1f)); + let _e330 = phi12_; + phi21_ = (3.1415927f - _e330); + let _e332 = (*base); + sqrtBase = sqrt(clamp(_e332, vec3(0f, 0f, 0f), vec3(0.9999f, 0.9999f, 0.9999f))); + let _e335 = sqrtBase; + let _e337 = sqrtBase; + baseIor = ((vec3(1f, 1f, 1f) + _e335) / (vec3(1f, 1f, 1f) - _e337)); + let _e340 = baseIor; + let _e341 = eta2_; + let _e344 = baseIor; + let _e345 = eta2_; + r1_ = ((_e340 - vec3(_e341)) / (_e344 + vec3(_e345))); let _e349 = r1_; - r1_ = (_e349 * _e348); - let _e351 = r1_; + let _e350 = r1_; + r1_ = (_e350 * _e349); let _e352 = r1_; - let _e354 = cos2_; - r23_ = (_e351 + ((vec3(1f, 1f, 1f) - _e352) * pow((1f - _e354), 5f))); - let _e360 = baseIor[0u]; - let _e361 = eta2_; - let _e365 = baseIor[1u]; - let _e366 = eta2_; - let _e370 = baseIor[2u]; - let _e371 = eta2_; - phi23_ = vec3(select(0f, 3.1415927f, (_e360 < _e361)), select(0f, 3.1415927f, (_e365 < _e366)), select(0f, 3.1415927f, (_e370 < _e371))); - let _e375 = eta2_; - let _e377 = (*thickness); - let _e379 = cos2_; - opd_1 = (((2f * _e375) * _e377) * _e379); - let _e381 = phi21_; - let _e383 = phi23_; - phi = (vec3(_e381) + _e383); - let _e385 = r12_; - let _e386 = r23_; - r123_ = clamp((_e386 * _e385), vec3(0.00001f, 0.00001f, 0.00001f), vec3(0.9999f, 0.9999f, 0.9999f)); - let _e389 = r123_; - rootR123_ = sqrt(_e389); - let _e391 = t121_; + let _e353 = r1_; + let _e355 = cos2_; + r23_ = (_e352 + ((vec3(1f, 1f, 1f) - _e353) * pow((1f - _e355), 5f))); + let _e361 = baseIor[0u]; + let _e362 = eta2_; + let _e366 = baseIor[1u]; + let _e367 = eta2_; + let _e371 = baseIor[2u]; + let _e372 = eta2_; + phi23_ = vec3(select(0f, 3.1415927f, (_e361 < _e362)), select(0f, 3.1415927f, (_e366 < _e367)), select(0f, 3.1415927f, (_e371 < _e372))); + let _e376 = eta2_; + let _e378 = (*thickness); + let _e380 = cos2_; + opd_1 = (((2f * _e376) * _e378) * _e380); + let _e382 = phi21_; + let _e384 = phi23_; + phi = (vec3(_e382) + _e384); + let _e386 = r12_; + let _e387 = r23_; + r123_ = clamp((_e387 * _e386), vec3(0.00001f, 0.00001f, 0.00001f), vec3(0.9999f, 0.9999f, 0.9999f)); + let _e390 = r123_; + rootR123_ = sqrt(_e390); let _e392 = t121_; - let _e394 = r23_; - let _e396 = r123_; - rs = ((_e394 * (_e391 * _e392)) / (vec3(1f, 1f, 1f) - _e396)); - let _e399 = r12_; - let _e401 = rs; - total_2 = (vec3(_e399) + _e401); - let _e403 = rs; - let _e404 = t121_; - cm = (_e403 - vec3(_e404)); + let _e393 = t121_; + let _e395 = r23_; + let _e397 = r123_; + rs = ((_e395 * (_e392 * _e393)) / (vec3(1f, 1f, 1f) - _e397)); + let _e400 = r12_; + let _e402 = rs; + total_2 = (vec3(_e400) + _e402); + let _e404 = rs; + let _e405 = t121_; + cm = (_e404 - vec3(_e405)); m_1 = 1i; loop { - let _e407 = m_1; - if (_e407 <= 2i) { - let _e409 = rootR123_; - let _e410 = cm; - cm = (_e410 * _e409); - let _e412 = cm; - let _e414 = m_1; - let _e416 = opd_1; - let _e418 = m_1; - let _e420 = phi; - param_201 = (f32(_e414) * _e416); - param_202 = (_e420 * f32(_e418)); - let _e422 = IridescenceSensitivity_u0028_f1_u003b_vf3_u003b((¶m_201), (¶m_202)); - let _e424 = total_2; - total_2 = (_e424 + ((_e412 * 2f) * _e422)); + let _e408 = m_1; + if (_e408 <= 2i) { + let _e410 = rootR123_; + let _e411 = cm; + cm = (_e411 * _e410); + let _e413 = cm; + let _e415 = m_1; + let _e417 = opd_1; + let _e419 = m_1; + let _e421 = phi; + param_201 = (f32(_e415) * _e417); + param_202 = (_e421 * f32(_e419)); + let _e423 = IridescenceSensitivity_u0028_f1_u003b_vf3_u003b((¶m_201), (¶m_202)); + let _e425 = total_2; + total_2 = (_e425 + ((_e413 * 2f) * _e423)); continue; } else { break; } continuing { - let _e426 = m_1; - m_1 = (_e426 + 1i); + let _e427 = m_1; + m_1 = (_e427 + 1i); } } - let _e428 = cos2Sq; - if (_e428 < 0f) { + let _e429 = cos2Sq; + if (_e429 < 0f) { local_35 = vec3(1f, 1f, 1f); } else { - let _e430 = total_2; - local_35 = max(_e430, vec3(0f, 0f, 0f)); + let _e431 = total_2; + local_35 = max(_e431, vec3(0f, 0f, 0f)); } - let _e432 = local_35; - return _e432; + let _e433 = local_35; + return _e433; } fn ReadIridescence_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_10: ptr) { @@ -19361,20 +19448,20 @@ fn ReadIridescence_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_205: f32; var param_206: vec3; - let _e267 = g_f0_dielectric; - let _e269 = (*s_10).albedo; - let _e271 = (*s_10).metallic; - f0_3 = mix(_e267, _e269, vec3(clamp(_e271, 0f, 1f))); - let _e277 = layer_info.iridescence[1u]; - param_203 = _e277; - let _e279 = (*s_10).n_dot_v; - param_204 = _e279; - let _e282 = layer_info.iridescence[2u]; - param_205 = _e282; - let _e283 = f0_3; - param_206 = _e283; - let _e284 = FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b((¶m_203), (¶m_204), (¶m_205), (¶m_206)); - g_irid_fresnel = _e284; + let _e268 = g_f0_dielectric; + let _e270 = (*s_10).albedo; + let _e272 = (*s_10).metallic; + f0_3 = mix(_e268, _e270, vec3(clamp(_e272, 0f, 1f))); + let _e278 = layer_info.iridescence[1u]; + param_203 = _e278; + let _e280 = (*s_10).n_dot_v; + param_204 = _e280; + let _e283 = layer_info.iridescence[2u]; + param_205 = _e283; + let _e284 = f0_3; + param_206 = _e284; + let _e285 = FresnelIridescence_u0028_f1_u003b_f1_u003b_f1_u003b_vf3_u003b((¶m_203), (¶m_204), (¶m_205), (¶m_206)); + g_irid_fresnel = _e285; return; } @@ -19390,103 +19477,103 @@ fn ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var along_1: vec3; var local_37: f32; var local_38: vec3; - var phi_4683_: bool; + var phi_4718_: bool; - let _e274 = (*s_11).n_dot_v; - let _e278 = g_sheen_roughness; - param_207 = _e278; - param_208 = sqrt(clamp((1f - _e274), 0f, 1f)); + let _e275 = (*s_11).n_dot_v; + let _e279 = g_sheen_roughness; + param_207 = _e279; + param_208 = sqrt(clamp((1f - _e275), 0f, 1f)); param_209 = 1f; - let _e279 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_207), (¶m_208), (¶m_209)); - let _e280 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e279, 0f); - g_sheen_albedo = _e280.z; - let _e283 = g_sheen[0u]; - let _e285 = g_sheen[1u]; - let _e288 = g_sheen[2u]; - let _e290 = g_sheen_albedo; - g_sheen_scale = (1f - (max(max(_e283, _e285), _e288) * _e290)); - let _e293 = v_tangent_1; - let _e296 = (*s_11).n; - let _e298 = (*s_11).n; - let _e299 = v_tangent_1; - t_2 = (_e293.xyz - (_e296 * dot(_e298, _e299.xyz))); - let _e304 = t_2; + let _e280 = LtcUv_u0028_f1_u003b_f1_u003b_f1_u003b((¶m_207), (¶m_208), (¶m_209)); + let _e281 = textureSampleLevel(ltc_texture_tex, ltc_texture_smp, _e280, 0f); + g_sheen_albedo = _e281.z; + let _e284 = g_sheen[0u]; + let _e286 = g_sheen[1u]; + let _e289 = g_sheen[2u]; + let _e291 = g_sheen_albedo; + g_sheen_scale = (1f - (max(max(_e284, _e286), _e289) * _e291)); + let _e294 = v_tangent_1; + let _e297 = (*s_11).n; + let _e299 = (*s_11).n; + let _e300 = v_tangent_1; + t_2 = (_e294.xyz - (_e297 * dot(_e299, _e300.xyz))); let _e305 = t_2; - usable = (dot(_e304, _e305) > 0.000000000001f); - let _e308 = usable; - if _e308 { - let _e309 = t_2; - local_36 = normalize(_e309); + let _e306 = t_2; + usable = (dot(_e305, _e306) > 0.000000000001f); + let _e309 = usable; + if _e309 { + let _e310 = t_2; + local_36 = normalize(_e310); } else { local_36 = vec3(1f, 0f, 0f); } - let _e311 = local_36; - t_2 = _e311; - let _e313 = (*s_11).n; - let _e314 = t_2; - let _e317 = v_tangent_1[3u]; - b_4 = (cross(_e313, _e314) * _e317); - let _e319 = gl_FrontFacing_1; - if !(_e319) { - let _e321 = t_2; - t_2 = -(_e321); - } - let _e324 = layer_info.anisotropy; - turn_2 = _e324.yz; - let _e326 = t_2; - let _e328 = turn_2[0u]; - let _e330 = b_4; - let _e332 = turn_2[1u]; - along_1 = ((_e326 * _e328) + (_e330 * _e332)); - let _e335 = usable; - phi_4683_ = _e335; - if _e335 { - let _e336 = along_1; + let _e312 = local_36; + t_2 = _e312; + let _e314 = (*s_11).n; + let _e315 = t_2; + let _e318 = v_tangent_1[3u]; + b_4 = (cross(_e314, _e315) * _e318); + let _e320 = gl_FrontFacing_1; + if !(_e320) { + let _e322 = t_2; + t_2 = -(_e322); + } + let _e325 = layer_info.anisotropy; + turn_2 = _e325.yz; + let _e327 = t_2; + let _e329 = turn_2[0u]; + let _e331 = b_4; + let _e333 = turn_2[1u]; + along_1 = ((_e327 * _e329) + (_e331 * _e333)); + let _e336 = usable; + phi_4718_ = _e336; + if _e336 { let _e337 = along_1; - phi_4683_ = (dot(_e336, _e337) > 0.000000000001f); + let _e338 = along_1; + phi_4718_ = (dot(_e337, _e338) > 0.000000000001f); } - let _e341 = phi_4683_; - if _e341 { - let _e344 = layer_info.anisotropy[0u]; - local_37 = clamp(_e344, 0f, 1f); + let _e342 = phi_4718_; + if _e342 { + let _e345 = layer_info.anisotropy[0u]; + local_37 = clamp(_e345, 0f, 1f); } else { local_37 = 0f; } - let _e346 = local_37; - g_aniso = _e346; - let _e347 = g_aniso; - if (_e347 > 0f) { - let _e349 = along_1; - local_38 = normalize(_e349); + let _e347 = local_37; + g_aniso = _e347; + let _e348 = g_aniso; + if (_e348 > 0f) { + let _e350 = along_1; + local_38 = normalize(_e350); } else { - let _e351 = t_2; - local_38 = _e351; - } - let _e352 = local_38; - g_aniso_t = _e352; - let _e354 = (*s_11).n; - let _e355 = g_aniso_t; - g_aniso_b = cross(_e354, _e355); + let _e352 = t_2; + local_38 = _e352; + } + let _e353 = local_38; + g_aniso_t = _e353; + let _e355 = (*s_11).n; + let _e356 = g_aniso_t; + g_aniso_b = cross(_e355, _e356); return; } fn MaterialLodBias_u0028_() -> f32 { - let _e263 = frag_info.target_origin[1u]; - return _e263; + let _e264 = frag_info.target_origin[1u]; + return _e264; } fn MapUv_u0028_i1_u003b(slot_6: ptr) -> vec2 { var uvw: vec3; - let _e263 = v_texcoord_1; - uvw = vec3(_e263.x, _e263.y, 1f); - let _e267 = (*slot_6); - let _e271 = layer_info.uv_transform[(_e267 * 2i)]; - let _e273 = uvw; - let _e275 = (*slot_6); - let _e280 = layer_info.uv_transform[((_e275 * 2i) + 1i)]; - let _e282 = uvw; - return vec2(dot(_e271.xyz, _e273), dot(_e280.xyz, _e282)); + let _e264 = v_texcoord_1; + uvw = vec3(_e264.x, _e264.y, 1f); + let _e268 = (*slot_6); + let _e272 = layer_info.uv_transform[(_e268 * 2i)]; + let _e274 = uvw; + let _e276 = (*slot_6); + let _e281 = layer_info.uv_transform[((_e276 * 2i) + 1i)]; + let _e283 = uvw; + return vec2(dot(_e272.xyz, _e274), dot(_e281.xyz, _e283)); } fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_12: ptr) { @@ -19494,24 +19581,24 @@ fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d var param_210: i32; param_210 = 1i; - let _e264 = MapUv_u0028_i1_u003b((¶m_210)); - let _e265 = MaterialLodBias_u0028_(); - let _e266 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e264, _e265); - orm = _e266.xyz; - let _e269 = (*s_12).metallic; - let _e271 = orm[2u]; - (*s_12).metallic = clamp((_e269 * _e271), 0f, 1f); - let _e276 = (*s_12).roughness; - let _e278 = orm[1u]; - (*s_12).roughness = clamp((_e276 * _e278), 0.02f, 1f); + let _e265 = MapUv_u0028_i1_u003b((¶m_210)); + let _e266 = MaterialLodBias_u0028_(); + let _e267 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e265, _e266); + orm = _e267.xyz; + let _e270 = (*s_12).metallic; + let _e272 = orm[2u]; + (*s_12).metallic = clamp((_e270 * _e272), 0f, 1f); + let _e277 = (*s_12).roughness; + let _e279 = orm[1u]; + (*s_12).roughness = clamp((_e277 * _e279), 0.02f, 1f); return; } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e262 = (*srgb); - let _e265 = (*srgb); - let _e270 = (*srgb); - return mix((_e262 / vec3(12.92f)), pow(((_e265 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e270)); + let _e263 = (*srgb); + let _e266 = (*srgb); + let _e271 = (*srgb); + return mix((_e263 / vec3(12.92f)), pow(((_e266 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e271)); } fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_13: ptr) { @@ -19520,16 +19607,16 @@ fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_212: vec3; param_211 = 4i; - let _e265 = MapUv_u0028_i1_u003b((¶m_211)); - let _e266 = MaterialLodBias_u0028_(); - let _e267 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e265, _e266); - param_212 = _e267.xyz; - let _e269 = SrgbToLinear_u0028_vf3_u003b((¶m_212)); - emissive = _e269; - let _e270 = emissive; - let _e272 = frag_info.emissive; - let _e277 = frag_info.material2_[3u]; - (*s_13).emissive = ((_e270 * _e272.xyz) * _e277); + let _e266 = MapUv_u0028_i1_u003b((¶m_211)); + let _e267 = MaterialLodBias_u0028_(); + let _e268 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e266, _e267); + param_212 = _e268.xyz; + let _e270 = SrgbToLinear_u0028_vf3_u003b((¶m_212)); + emissive = _e270; + let _e271 = emissive; + let _e273 = frag_info.emissive; + let _e278 = frag_info.material2_[3u]; + (*s_13).emissive = ((_e271 * _e273.xyz) * _e278); return; } @@ -19538,13 +19625,13 @@ fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u00 var param_213: i32; param_213 = 3i; - let _e264 = MapUv_u0028_i1_u003b((¶m_213)); - let _e265 = MaterialLodBias_u0028_(); - let _e266 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e264, _e265); - occlusion = _e266.x; - let _e268 = occlusion; - let _e271 = frag_info.material2_[2u]; - (*s_14).occlusion = mix(1f, _e268, clamp(_e271, 0f, 1f)); + let _e265 = MapUv_u0028_i1_u003b((¶m_213)); + let _e266 = MaterialLodBias_u0028_(); + let _e267 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e265, _e266); + occlusion = _e267.x; + let _e269 = occlusion; + let _e272 = frag_info.material2_[2u]; + (*s_14).occlusion = mix(1f, _e269, clamp(_e272, 0f, 1f)); return; } @@ -19552,17 +19639,17 @@ fn MapMatrix_u0028_i1_u003b(slot_7: ptr) -> vec4 { var u_2: vec4; var v_3: vec4; - let _e264 = (*slot_7); - let _e268 = layer_info.uv_transform[(_e264 * 2i)]; - u_2 = _e268; - let _e269 = (*slot_7); - let _e274 = layer_info.uv_transform[((_e269 * 2i) + 1i)]; - v_3 = _e274; - let _e276 = u_2[0u]; - let _e278 = u_2[1u]; - let _e280 = v_3[0u]; - let _e282 = v_3[1u]; - return vec4(_e276, _e278, _e280, _e282); + let _e265 = (*slot_7); + let _e269 = layer_info.uv_transform[(_e265 * 2i)]; + u_2 = _e269; + let _e270 = (*slot_7); + let _e275 = layer_info.uv_transform[((_e270 * 2i) + 1i)]; + v_3 = _e275; + let _e277 = u_2[0u]; + let _e279 = u_2[1u]; + let _e281 = v_3[0u]; + let _e283 = v_3[1u]; + return vec4(_e277, _e279, _e281, _e283); } fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_15: ptr) { @@ -19581,147 +19668,147 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var local_40: vec3; var local_41: vec3; var sampled: vec3; - var phi_3502_: bool; - var phi_3509_: bool; - var phi_3516_: bool; - var phi_3523_: bool; + var phi_3505_: bool; + var phi_3512_: bool; + var phi_3519_: bool; + var phi_3526_: bool; param_214 = 2i; - let _e277 = MapUv_u0028_i1_u003b((¶m_214)); - let _e278 = MaterialLodBias_u0028_(); - let _e279 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e277, _e278); - sampledTexel = _e279; - let _e280 = v_tangent_1; - t_3 = _e280.xyz; - let _e282 = t_3; - let _e284 = (*s_15).n; - let _e286 = (*s_15).n; - let _e287 = t_3; - t_3 = (_e282 - (_e284 * dot(_e286, _e287))); - let _e291 = t_3; + let _e278 = MapUv_u0028_i1_u003b((¶m_214)); + let _e279 = MaterialLodBias_u0028_(); + let _e280 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e278, _e279); + sampledTexel = _e280; + let _e281 = v_tangent_1; + t_3 = _e281.xyz; + let _e283 = t_3; + let _e285 = (*s_15).n; + let _e287 = (*s_15).n; + let _e288 = t_3; + t_3 = (_e283 - (_e285 * dot(_e287, _e288))); let _e292 = t_3; - if (dot(_e291, _e292) < 0.000000000001f) { + let _e293 = t_3; + if (dot(_e292, _e293) < 0.000000000001f) { return; } - let _e295 = t_3; - t_3 = normalize(_e295); - let _e298 = (*s_15).n; - let _e299 = t_3; - let _e302 = v_tangent_1[3u]; - b_5 = (cross(_e298, _e299) * _e302); + let _e296 = t_3; + t_3 = normalize(_e296); + let _e299 = (*s_15).n; + let _e300 = t_3; + let _e303 = v_tangent_1[3u]; + b_5 = (cross(_e299, _e300) * _e303); param_215 = 2i; - let _e304 = MapMatrix_u0028_i1_u003b((¶m_215)); - m_2 = _e304; - let _e306 = m_2[0u]; - let _e308 = m_2[3u]; - let _e311 = m_2[1u]; - let _e313 = m_2[2u]; - det = ((_e306 * _e308) - (_e311 * _e313)); - let _e316 = det; - flip = select(1f, -1f, (_e316 < 0f)); - let _e319 = gl_FrontFacing_1; - if _e319 { - let _e320 = b_5; - local_39 = _e320; - } else { + let _e305 = MapMatrix_u0028_i1_u003b((¶m_215)); + m_2 = _e305; + let _e307 = m_2[0u]; + let _e309 = m_2[3u]; + let _e312 = m_2[1u]; + let _e314 = m_2[2u]; + det = ((_e307 * _e309) - (_e312 * _e314)); + let _e317 = det; + flip = select(1f, -1f, (_e317 < 0f)); + let _e320 = gl_FrontFacing_1; + if _e320 { let _e321 = b_5; - local_39 = -(_e321); - } - let _e323 = local_39; - front = _e323; - let _e324 = t_3; - let _e326 = m_2[3u]; - let _e328 = front; - let _e330 = m_2[2u]; - let _e333 = flip; - turned = (((_e324 * _e326) + (_e328 * _e330)) * _e333); - let _e336 = m_2[1u]; - let _e337 = (_e336 != 0f); - phi_3502_ = _e337; - if !(_e337) { - let _e340 = m_2[2u]; - phi_3502_ = (_e340 != 0f); - } - let _e343 = phi_3502_; - phi_3509_ = _e343; - if !(_e343) { - let _e346 = m_2[0u]; - phi_3509_ = (_e346 < 0f); - } - let _e349 = phi_3509_; - phi_3516_ = _e349; - if !(_e349) { - let _e352 = m_2[3u]; - phi_3516_ = (_e352 < 0f); - } - let _e355 = phi_3516_; - phi_3523_ = _e355; - if _e355 { - let _e356 = turned; + local_39 = _e321; + } else { + let _e322 = b_5; + local_39 = -(_e322); + } + let _e324 = local_39; + front = _e324; + let _e325 = t_3; + let _e327 = m_2[3u]; + let _e329 = front; + let _e331 = m_2[2u]; + let _e334 = flip; + turned = (((_e325 * _e327) + (_e329 * _e331)) * _e334); + let _e337 = m_2[1u]; + let _e338 = (_e337 != 0f); + phi_3505_ = _e338; + if !(_e338) { + let _e341 = m_2[2u]; + phi_3505_ = (_e341 != 0f); + } + let _e344 = phi_3505_; + phi_3512_ = _e344; + if !(_e344) { + let _e347 = m_2[0u]; + phi_3512_ = (_e347 < 0f); + } + let _e350 = phi_3512_; + phi_3519_ = _e350; + if !(_e350) { + let _e353 = m_2[3u]; + phi_3519_ = (_e353 < 0f); + } + let _e356 = phi_3519_; + phi_3526_ = _e356; + if _e356 { let _e357 = turned; - phi_3523_ = (dot(_e356, _e357) > 0.000000000001f); - } - let _e361 = phi_3523_; - turns = _e361; - let _e362 = turns; - if _e362 { - let _e363 = turned; - local_40 = normalize(_e363); + let _e358 = turned; + phi_3526_ = (dot(_e357, _e358) > 0.000000000001f); + } + let _e362 = phi_3526_; + turns = _e362; + let _e363 = turns; + if _e363 { + let _e364 = turned; + local_40 = normalize(_e364); } else { - let _e365 = t_3; - local_40 = _e365; - } - let _e366 = local_40; - t_3 = _e366; - let _e367 = turns; - if _e367 { - let _e369 = (*s_15).n; - let _e370 = t_3; - let _e373 = v_tangent_1[3u]; - let _e375 = flip; - local_41 = ((cross(_e369, _e370) * _e373) * _e375); + let _e366 = t_3; + local_40 = _e366; + } + let _e367 = local_40; + t_3 = _e367; + let _e368 = turns; + if _e368 { + let _e370 = (*s_15).n; + let _e371 = t_3; + let _e374 = v_tangent_1[3u]; + let _e376 = flip; + local_41 = ((cross(_e370, _e371) * _e374) * _e376); } else { - let _e377 = b_5; - local_41 = _e377; - } - let _e378 = local_41; - b_5 = _e378; - let _e379 = gl_FrontFacing_1; - if !(_e379) { - let _e381 = t_3; - t_3 = -(_e381); - } - let _e383 = sampledTexel; - sampled = ((_e383.xyz * 2f) - vec3(1f)); - let _e390 = frag_info.emissive[3u]; - if (_e390 > 0.5f) { - let _e392 = sampled; - let _e394 = sampled; - sampled[2u] = sqrt(max((1f - dot(_e392.xy, _e394.xy)), 0f)); - } - let _e403 = frag_info.material2_[1u]; - let _e404 = sampled; - let _e406 = (_e404.xy * _e403); - sampled[0u] = _e406.x; - sampled[1u] = _e406.y; - let _e411 = t_3; - let _e413 = sampled[0u]; - let _e415 = b_5; - let _e417 = sampled[1u]; - let _e421 = (*s_15).n; - let _e423 = sampled[2u]; - (*s_15).n = normalize((((_e411 * _e413) + (_e415 * _e417)) + (_e421 * _e423))); - let _e429 = (*s_15).n; - let _e431 = (*s_15).v; - (*s_15).n_dot_v = max(dot(_e429, _e431), 0.0001f); + let _e378 = b_5; + local_41 = _e378; + } + let _e379 = local_41; + b_5 = _e379; + let _e380 = gl_FrontFacing_1; + if !(_e380) { + let _e382 = t_3; + t_3 = -(_e382); + } + let _e384 = sampledTexel; + sampled = ((_e384.xyz * 2f) - vec3(1f)); + let _e391 = frag_info.emissive[3u]; + if (_e391 > 0.5f) { + let _e393 = sampled; + let _e395 = sampled; + sampled[2u] = sqrt(max((1f - dot(_e393.xy, _e395.xy)), 0f)); + } + let _e404 = frag_info.material2_[1u]; + let _e405 = sampled; + let _e407 = (_e405.xy * _e404); + sampled[0u] = _e407.x; + sampled[1u] = _e407.y; + let _e412 = t_3; + let _e414 = sampled[0u]; + let _e416 = b_5; + let _e418 = sampled[1u]; + let _e422 = (*s_15).n; + let _e424 = sampled[2u]; + (*s_15).n = normalize((((_e412 * _e414) + (_e416 * _e418)) + (_e422 * _e424))); + let _e430 = (*s_15).n; + let _e432 = (*s_15).v; + (*s_15).n_dot_v = max(dot(_e430, _e432), 0.0001f); return; } fn AtlasTexel_u0028_vf2_u003b(texel_5: ptr>) -> vec4 { - let _e262 = (*texel_5); - let _e266 = irradiance_info.atlas; - let _e269 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e262 + vec2(0.5f)) * _e266.xy), 0f); - return _e269; + let _e263 = (*texel_5); + let _e267 = irradiance_info.atlas; + let _e270 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e263 + vec2(0.5f)) * _e267.xy), 0f); + return _e270; } fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr>, interior: ptr, uv_8: ptr>) -> vec4 { @@ -19737,42 +19824,42 @@ fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr; var param_219: vec2; - let _e275 = (*uv_8); - let _e276 = (*interior); - at_5 = ((vec2(1f) + (_e275 * _e276)) - vec2(0.5f)); - let _e282 = at_5; - low = floor(_e282); - let _e284 = at_5; - let _e285 = low; - f_5 = (_e284 - _e285); - let _e287 = (*corner); - let _e288 = low; - param_216 = (_e287 + _e288); - let _e290 = AtlasTexel_u0028_vf2_u003b((¶m_216)); - a_6 = _e290; - let _e291 = (*corner); - let _e292 = low; - param_217 = ((_e291 + _e292) + vec2(1f, 0f)); - let _e295 = AtlasTexel_u0028_vf2_u003b((¶m_217)); - b_6 = _e295; - let _e296 = (*corner); - let _e297 = low; - param_218 = ((_e296 + _e297) + vec2(0f, 1f)); - let _e300 = AtlasTexel_u0028_vf2_u003b((¶m_218)); - c = _e300; - let _e301 = (*corner); - let _e302 = low; - param_219 = ((_e301 + _e302) + vec2(1f, 1f)); - let _e305 = AtlasTexel_u0028_vf2_u003b((¶m_219)); - d_6 = _e305; - let _e306 = a_6; - let _e307 = b_6; - let _e309 = f_5[0u]; - let _e312 = c; - let _e313 = d_6; - let _e315 = f_5[0u]; - let _e319 = f_5[1u]; - return mix(mix(_e306, _e307, vec4(_e309)), mix(_e312, _e313, vec4(_e315)), vec4(_e319)); + let _e276 = (*uv_8); + let _e277 = (*interior); + at_5 = ((vec2(1f) + (_e276 * _e277)) - vec2(0.5f)); + let _e283 = at_5; + low = floor(_e283); + let _e285 = at_5; + let _e286 = low; + f_5 = (_e285 - _e286); + let _e288 = (*corner); + let _e289 = low; + param_216 = (_e288 + _e289); + let _e291 = AtlasTexel_u0028_vf2_u003b((¶m_216)); + a_6 = _e291; + let _e292 = (*corner); + let _e293 = low; + param_217 = ((_e292 + _e293) + vec2(1f, 0f)); + let _e296 = AtlasTexel_u0028_vf2_u003b((¶m_217)); + b_6 = _e296; + let _e297 = (*corner); + let _e298 = low; + param_218 = ((_e297 + _e298) + vec2(0f, 1f)); + let _e301 = AtlasTexel_u0028_vf2_u003b((¶m_218)); + c = _e301; + let _e302 = (*corner); + let _e303 = low; + param_219 = ((_e302 + _e303) + vec2(1f, 1f)); + let _e306 = AtlasTexel_u0028_vf2_u003b((¶m_219)); + d_6 = _e306; + let _e307 = a_6; + let _e308 = b_6; + let _e310 = f_5[0u]; + let _e313 = c; + let _e314 = d_6; + let _e316 = f_5[0u]; + let _e320 = f_5[1u]; + return mix(mix(_e307, _e308, vec4(_e310)), mix(_e313, _e314, vec4(_e316)), vec4(_e320)); } fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec2 { @@ -19780,29 +19867,29 @@ fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec var n_6: vec3; var xy: vec2; - let _e266 = (*direction_1)[0u]; - let _e269 = (*direction_1)[1u]; - let _e273 = (*direction_1)[2u]; - sum_1 = ((abs(_e266) + abs(_e269)) + abs(_e273)); - let _e276 = sum_1; - if (_e276 <= 0f) { + let _e267 = (*direction_1)[0u]; + let _e270 = (*direction_1)[1u]; + let _e274 = (*direction_1)[2u]; + sum_1 = ((abs(_e267) + abs(_e270)) + abs(_e274)); + let _e277 = sum_1; + if (_e277 <= 0f) { return vec2(0.5f, 0.5f); } - let _e278 = (*direction_1); - let _e279 = sum_1; - n_6 = (_e278 / vec3(_e279)); - let _e282 = n_6; - xy = _e282.xy; - let _e285 = n_6[2u]; - if (_e285 < 0f) { - let _e288 = n_6[1u]; - let _e292 = n_6[0u]; - let _e297 = n_6[0u]; - let _e301 = n_6[1u]; - xy = vec2(((1f - abs(_e288)) * select(-1f, 1f, (_e292 >= 0f))), ((1f - abs(_e297)) * select(-1f, 1f, (_e301 >= 0f)))); + let _e279 = (*direction_1); + let _e280 = sum_1; + n_6 = (_e279 / vec3(_e280)); + let _e283 = n_6; + xy = _e283.xy; + let _e286 = n_6[2u]; + if (_e286 < 0f) { + let _e289 = n_6[1u]; + let _e293 = n_6[0u]; + let _e298 = n_6[0u]; + let _e302 = n_6[1u]; + xy = vec2(((1f - abs(_e289)) * select(-1f, 1f, (_e293 >= 0f))), ((1f - abs(_e298)) * select(-1f, 1f, (_e302 >= 0f)))); } - let _e306 = xy; - return ((_e306 * 0.5f) + vec2(0.5f)); + let _e307 = xy; + return ((_e307 * 0.5f) + vec2(0.5f)); } fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_5: ptr>, normal_1: ptr>, view_1: ptr>) -> vec3 { @@ -19850,190 +19937,190 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_5: ptr; var local_42: vec3; - let _e308 = irradiance_info.origin; - origin_2 = _e308.xyz; - let _e311 = irradiance_info.spacing; - spacing = _e311.xyz; - let _e314 = irradiance_info.counts; - counts = _e314.xyz; - let _e318 = irradiance_info.tiles[0u]; - irradianceTile = _e318; - let _e321 = irradiance_info.tiles[1u]; - depthTile = _e321; - let _e324 = irradiance_info.tiles[2u]; - columns = _e324; - let _e327 = irradiance_info.tiles[3u]; - momentsTop = _e327; - let _e328 = (*normal_1); - unit = normalize(_e328); - let _e330 = (*world_5); - let _e331 = unit; - let _e334 = irradiance_info.spacing[3u]; - let _e337 = (*view_1); - let _e340 = irradiance_info.counts[3u]; - biased = ((_e330 + (_e331 * _e334)) + (_e337 * _e340)); - let _e343 = biased; - let _e344 = origin_2; - let _e346 = spacing; - grid_1 = ((_e343 - _e344) / _e346); - let _e348 = grid_1; - let _e350 = counts; - base_1 = clamp(floor(_e348), vec3(0f, 0f, 0f), (_e350 - vec3(2f))); - let _e354 = grid_1; - let _e355 = base_1; - f_6 = clamp((_e354 - _e355), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e309 = irradiance_info.origin; + origin_2 = _e309.xyz; + let _e312 = irradiance_info.spacing; + spacing = _e312.xyz; + let _e315 = irradiance_info.counts; + counts = _e315.xyz; + let _e319 = irradiance_info.tiles[0u]; + irradianceTile = _e319; + let _e322 = irradiance_info.tiles[1u]; + depthTile = _e322; + let _e325 = irradiance_info.tiles[2u]; + columns = _e325; + let _e328 = irradiance_info.tiles[3u]; + momentsTop = _e328; + let _e329 = (*normal_1); + unit = normalize(_e329); + let _e331 = (*world_5); + let _e332 = unit; + let _e335 = irradiance_info.spacing[3u]; + let _e338 = (*view_1); + let _e341 = irradiance_info.counts[3u]; + biased = ((_e331 + (_e332 * _e335)) + (_e338 * _e341)); + let _e344 = biased; + let _e345 = origin_2; + let _e347 = spacing; + grid_1 = ((_e344 - _e345) / _e347); + let _e349 = grid_1; + let _e351 = counts; + base_1 = clamp(floor(_e349), vec3(0f, 0f, 0f), (_e351 - vec3(2f))); + let _e355 = grid_1; + let _e356 = base_1; + f_6 = clamp((_e355 - _e356), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); total_3 = vec3(0f, 0f, 0f); weights = 0f; corner_1 = 0i; loop { - let _e358 = corner_1; - if (_e358 < 8i) { - let _e360 = corner_1; - let _e363 = corner_1; - let _e368 = corner_1; - offset_3 = vec3(f32((_e360 & 1i)), f32(((_e363 >> bitcast(1i)) & 1i)), f32(((_e368 >> bitcast(2i)) & 1i))); - let _e374 = base_1; - let _e375 = offset_3; - cell_1 = (_e374 + _e375); - let _e378 = cell_1[2u]; - let _e380 = counts[1u]; - let _e383 = cell_1[1u]; - let _e386 = counts[0u]; - let _e389 = cell_1[0u]; - probe = ((((_e378 * _e380) + _e383) * _e386) + _e389); - let _e391 = probe; - let _e392 = columns; - let _e397 = probe; - let _e398 = columns; - tile_4 = vec2((_e391 - (floor((_e391 / _e392)) * _e392)), floor((_e397 / _e398))); - let _e402 = tile_4; - let _e403 = irradianceTile; - irradianceCorner = (_e402 * (_e403 + 2f)); - let _e407 = tile_4[0u]; - let _e408 = depthTile; - let _e411 = momentsTop; - let _e413 = tile_4[1u]; - let _e414 = depthTile; - momentCorner = vec2((_e407 * (_e408 + 2f)), (_e411 + (_e413 * (_e414 + 2f)))); - let _e419 = irradianceCorner; - param_220 = (_e419 + vec2(1f)); - let _e422 = AtlasTexel_u0028_vf2_u003b((¶m_220)); - if (_e422.w < 0.5f) { + let _e359 = corner_1; + if (_e359 < 8i) { + let _e361 = corner_1; + let _e364 = corner_1; + let _e369 = corner_1; + offset_3 = vec3(f32((_e361 & 1i)), f32(((_e364 >> bitcast(1i)) & 1i)), f32(((_e369 >> bitcast(2i)) & 1i))); + let _e375 = base_1; + let _e376 = offset_3; + cell_1 = (_e375 + _e376); + let _e379 = cell_1[2u]; + let _e381 = counts[1u]; + let _e384 = cell_1[1u]; + let _e387 = counts[0u]; + let _e390 = cell_1[0u]; + probe = ((((_e379 * _e381) + _e384) * _e387) + _e390); + let _e392 = probe; + let _e393 = columns; + let _e398 = probe; + let _e399 = columns; + tile_4 = vec2((_e392 - (floor((_e392 / _e393)) * _e393)), floor((_e398 / _e399))); + let _e403 = tile_4; + let _e404 = irradianceTile; + irradianceCorner = (_e403 * (_e404 + 2f)); + let _e408 = tile_4[0u]; + let _e409 = depthTile; + let _e412 = momentsTop; + let _e414 = tile_4[1u]; + let _e415 = depthTile; + momentCorner = vec2((_e408 * (_e409 + 2f)), (_e412 + (_e414 * (_e415 + 2f)))); + let _e420 = irradianceCorner; + param_220 = (_e420 + vec2(1f)); + let _e423 = AtlasTexel_u0028_vf2_u003b((¶m_220)); + if (_e423.w < 0.5f) { continue; } - let _e425 = f_6; - let _e427 = f_6; - let _e428 = offset_3; - trilinear = mix((vec3(1f, 1f, 1f) - _e425), _e427, _e428); - let _e431 = trilinear[0u]; - let _e433 = trilinear[1u]; - let _e436 = trilinear[2u]; - weight_2 = max(((_e431 * _e433) * _e436), 0.001f); - let _e439 = origin_2; - let _e440 = spacing; - let _e441 = cell_1; - probePosition = (_e439 + (_e440 * _e441)); - let _e444 = probePosition; - let _e445 = biased; - toProbe = (_e444 - _e445); - let _e447 = toProbe; - distance_4 = length(_e447); - let _e449 = distance_4; - if (_e449 > 0.000001f) { - let _e451 = toProbe; - let _e452 = distance_4; - direction_2 = (_e451 / vec3(_e452)); - let _e455 = unit; - let _e456 = probePosition; - let _e457 = (*world_5); - facing = ((dot(_e455, normalize((_e456 - _e457))) * 0.5f) + 0.5f); - let _e463 = facing; + let _e426 = f_6; + let _e428 = f_6; + let _e429 = offset_3; + trilinear = mix((vec3(1f, 1f, 1f) - _e426), _e428, _e429); + let _e432 = trilinear[0u]; + let _e434 = trilinear[1u]; + let _e437 = trilinear[2u]; + weight_2 = max(((_e432 * _e434) * _e437), 0.001f); + let _e440 = origin_2; + let _e441 = spacing; + let _e442 = cell_1; + probePosition = (_e440 + (_e441 * _e442)); + let _e445 = probePosition; + let _e446 = biased; + toProbe = (_e445 - _e446); + let _e448 = toProbe; + distance_4 = length(_e448); + let _e450 = distance_4; + if (_e450 > 0.000001f) { + let _e452 = toProbe; + let _e453 = distance_4; + direction_2 = (_e452 / vec3(_e453)); + let _e456 = unit; + let _e457 = probePosition; + let _e458 = (*world_5); + facing = ((dot(_e456, normalize((_e457 - _e458))) * 0.5f) + 0.5f); let _e464 = facing; - probeWeight = ((_e463 * _e464) + 0.2f); - let _e467 = direction_2; - param_221 = -(_e467); - let _e469 = ProbeOctahedral_u0028_vf3_u003b((¶m_221)); - let _e470 = momentCorner; - param_222 = _e470; - let _e471 = depthTile; - param_223 = _e471; - param_224 = _e469; - let _e472 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_222), (¶m_223), (¶m_224)); - moments_3 = _e472.xy; + let _e465 = facing; + probeWeight = ((_e464 * _e465) + 0.2f); + let _e468 = direction_2; + param_221 = -(_e468); + let _e470 = ProbeOctahedral_u0028_vf3_u003b((¶m_221)); + let _e471 = momentCorner; + param_222 = _e471; + let _e472 = depthTile; + param_223 = _e472; + param_224 = _e470; + let _e473 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_222), (¶m_223), (¶m_224)); + moments_3 = _e473.xy; chebyshev = 1f; - let _e474 = distance_4; - let _e476 = moments_3[0u]; - if (_e474 > _e476) { - let _e479 = moments_3[1u]; - let _e481 = moments_3[0u]; - let _e483 = moments_3[0u]; - variance_2 = max((_e479 - (_e481 * _e483)), 0.000001f); - let _e487 = distance_4; - let _e489 = moments_3[0u]; - difference = (_e487 - _e489); - let _e491 = variance_2; + let _e475 = distance_4; + let _e477 = moments_3[0u]; + if (_e475 > _e477) { + let _e480 = moments_3[1u]; + let _e482 = moments_3[0u]; + let _e484 = moments_3[0u]; + variance_2 = max((_e480 - (_e482 * _e484)), 0.000001f); + let _e488 = distance_4; + let _e490 = moments_3[0u]; + difference = (_e488 - _e490); let _e492 = variance_2; - let _e493 = difference; + let _e493 = variance_2; let _e494 = difference; - chebyshev = (_e491 / (_e492 + (_e493 * _e494))); - let _e498 = chebyshev; + let _e495 = difference; + chebyshev = (_e492 / (_e493 + (_e494 * _e495))); let _e499 = chebyshev; - let _e501 = chebyshev; - chebyshev = ((_e498 * _e499) * _e501); + let _e500 = chebyshev; + let _e502 = chebyshev; + chebyshev = ((_e499 * _e500) * _e502); } - let _e503 = probeWeight; - let _e504 = chebyshev; - probeWeight = max((_e503 * max(_e504, 0.05f)), 0.000001f); - let _e508 = probeWeight; - if (_e508 < 0.2f) { - let _e510 = probeWeight; + let _e504 = probeWeight; + let _e505 = chebyshev; + probeWeight = max((_e504 * max(_e505, 0.05f)), 0.000001f); + let _e509 = probeWeight; + if (_e509 < 0.2f) { let _e511 = probeWeight; - let _e514 = probeWeight; - probeWeight = (_e514 * ((_e510 * _e511) * 25f)); + let _e512 = probeWeight; + let _e515 = probeWeight; + probeWeight = (_e515 * ((_e511 * _e512) * 25f)); } - let _e516 = probeWeight; - let _e517 = weight_2; - weight_2 = (_e517 * _e516); - } - let _e519 = unit; - param_225 = _e519; - let _e520 = ProbeOctahedral_u0028_vf3_u003b((¶m_225)); - let _e521 = irradianceCorner; - param_226 = _e521; - let _e522 = irradianceTile; - param_227 = _e522; - param_228 = _e520; - let _e523 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_226), (¶m_227), (¶m_228)); - let _e525 = weight_2; - let _e527 = total_3; - total_3 = (_e527 + (_e523.xyz * _e525)); - let _e529 = weight_2; - let _e530 = weights; - weights = (_e530 + _e529); + let _e517 = probeWeight; + let _e518 = weight_2; + weight_2 = (_e518 * _e517); + } + let _e520 = unit; + param_225 = _e520; + let _e521 = ProbeOctahedral_u0028_vf3_u003b((¶m_225)); + let _e522 = irradianceCorner; + param_226 = _e522; + let _e523 = irradianceTile; + param_227 = _e523; + param_228 = _e521; + let _e524 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_226), (¶m_227), (¶m_228)); + let _e526 = weight_2; + let _e528 = total_3; + total_3 = (_e528 + (_e524.xyz * _e526)); + let _e530 = weight_2; + let _e531 = weights; + weights = (_e531 + _e530); continue; } else { break; } continuing { - let _e532 = corner_1; - corner_1 = (_e532 + 1i); + let _e533 = corner_1; + corner_1 = (_e533 + 1i); } } - let _e534 = weights; - if (_e534 > 0f) { - let _e536 = total_3; - let _e537 = weights; - local_42 = (_e536 / vec3(_e537)); + let _e535 = weights; + if (_e535 > 0f) { + let _e537 = total_3; + let _e538 = weights; + local_42 = (_e537 / vec3(_e538)); } else { local_42 = vec3(0f, 0f, 0f); } - let _e540 = local_42; - return _e540; + let _e541 = local_42; + return _e541; } fn IrradianceEnabled_u0028_() -> bool { - let _e263 = irradiance_info.origin[3u]; - return (_e263 > 0.5f); + let _e264 = irradiance_info.origin[3u]; + return (_e264 > 0.5f); } fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_16: ptr) { @@ -20044,33 +20131,33 @@ fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_233: Surface; var param_234: Surface; - let _e268 = IrradianceEnabled_u0028_(); - if _e268 { - let _e269 = v_world_position_1; - param_229 = _e269; - let _e271 = (*s_16).n; - param_230 = _e271; - let _e273 = (*s_16).v; - param_231 = _e273; - let _e274 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_229), (¶m_230), (¶m_231)); - let _e277 = frag_info.material[2u]; - (*s_16).ambient = (_e274 * _e277); - } - let _e280 = (*s_16); - param_232 = _e280; + let _e269 = IrradianceEnabled_u0028_(); + if _e269 { + let _e270 = v_world_position_1; + param_229 = _e270; + let _e272 = (*s_16).n; + param_230 = _e272; + let _e274 = (*s_16).v; + param_231 = _e274; + let _e275 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_229), (¶m_230), (¶m_231)); + let _e278 = frag_info.material[2u]; + (*s_16).ambient = (_e275 * _e278); + } + let _e281 = (*s_16); + param_232 = _e281; ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_232)); - let _e281 = param_232; - (*s_16) = _e281; - let _e282 = (*s_16); - param_233 = _e282; + let _e282 = param_232; + (*s_16) = _e282; + let _e283 = (*s_16); + param_233 = _e283; ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_233)); - let _e283 = param_233; - (*s_16) = _e283; - let _e284 = (*s_16); - param_234 = _e284; + let _e284 = param_233; + (*s_16) = _e284; + let _e285 = (*s_16); + param_234 = _e285; ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_234)); - let _e285 = param_234; - (*s_16) = _e285; + let _e286 = param_234; + (*s_16) = _e286; return; } @@ -20085,78 +20172,78 @@ fn ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ var local_44: vec3; var fc: f32; - let _e271 = v_texcoord_1; - let _e272 = MaterialLodBias_u0028_(); - let _e273 = textureSampleBias(coat_texture_tex, coat_texture_smp, _e271, _e272); - coatTexel = _e273; - let _e274 = v_texcoord_1; - let _e275 = MaterialLodBias_u0028_(); - let _e276 = textureSampleBias(sheen_texture_tex, sheen_texture_smp, _e274, _e275); - sheenTexel = _e276; - let _e278 = layer_info.sheen; - let _e280 = sheenTexel; - param_235 = _e280.xyz; - let _e282 = SrgbToLinear_u0028_vf3_u003b((¶m_235)); - g_sheen = (_e278.xyz * _e282); - let _e286 = layer_info.sheen[3u]; - let _e288 = sheenTexel[3u]; - g_sheen_roughness = clamp((_e286 * _e288), 0.07f, 1f); - let _e293 = layer_info.coat[2u]; - ior_3 = max(_e293, 1f); - let _e295 = IorInfinite_u0028_(); - if _e295 { + let _e272 = v_texcoord_1; + let _e273 = MaterialLodBias_u0028_(); + let _e274 = textureSampleBias(coat_texture_tex, coat_texture_smp, _e272, _e273); + coatTexel = _e274; + let _e275 = v_texcoord_1; + let _e276 = MaterialLodBias_u0028_(); + let _e277 = textureSampleBias(sheen_texture_tex, sheen_texture_smp, _e275, _e276); + sheenTexel = _e277; + let _e279 = layer_info.sheen; + let _e281 = sheenTexel; + param_235 = _e281.xyz; + let _e283 = SrgbToLinear_u0028_vf3_u003b((¶m_235)); + g_sheen = (_e279.xyz * _e283); + let _e287 = layer_info.sheen[3u]; + let _e289 = sheenTexel[3u]; + g_sheen_roughness = clamp((_e287 * _e289), 0.07f, 1f); + let _e294 = layer_info.coat[2u]; + ior_3 = max(_e294, 1f); + let _e296 = IorInfinite_u0028_(); + if _e296 { local_43 = 1f; } else { - let _e296 = ior_3; - let _e298 = ior_3; - local_43 = ((_e296 - 1f) / (_e298 + 1f)); + let _e297 = ior_3; + let _e299 = ior_3; + local_43 = ((_e297 - 1f) / (_e299 + 1f)); } - let _e301 = local_43; - r_5 = _e301; - let _e302 = r_5; + let _e302 = local_43; + r_5 = _e302; let _e303 = r_5; - let _e307 = layer_info.specular; - let _e313 = layer_info.specular[3u]; - g_f0_dielectric = (min((vec3((_e302 * _e303)) * _e307.xyz), vec3(1f, 1f, 1f)) * _e313); - let _e317 = layer_info.specular[3u]; - g_f90_ = _e317; - let _e320 = layer_info.coat[0u]; - let _e322 = coatTexel[0u]; - g_coat = clamp((_e320 * _e322), 0f, 1f); - let _e327 = layer_info.coat[1u]; - let _e329 = coatTexel[1u]; - g_coat_roughness = clamp((_e327 * _e329), 0.02f, 1f); - let _e334 = layer_info.transmission[0u]; - let _e336 = coatTexel[2u]; - g_transmission = clamp((_e334 * _e336), 0f, 1f); - let _e341 = layer_info.transmission[1u]; - let _e343 = coatTexel[3u]; - g_thickness = max((_e341 * _e343), 0f); - let _e348 = layer_info.transmission[2u]; - distance_5 = _e348; - let _e349 = distance_5; - if (_e349 > 0f) { - let _e352 = layer_info.attenuation; - let _e355 = g_thickness; - let _e356 = distance_5; - local_44 = pow(max(_e352.xyz, vec3(0.0001f, 0.0001f, 0.0001f)), vec3((_e355 / _e356))); + let _e304 = r_5; + let _e308 = layer_info.specular; + let _e314 = layer_info.specular[3u]; + g_f0_dielectric = (min((vec3((_e303 * _e304)) * _e308.xyz), vec3(1f, 1f, 1f)) * _e314); + let _e318 = layer_info.specular[3u]; + g_f90_ = _e318; + let _e321 = layer_info.coat[0u]; + let _e323 = coatTexel[0u]; + g_coat = clamp((_e321 * _e323), 0f, 1f); + let _e328 = layer_info.coat[1u]; + let _e330 = coatTexel[1u]; + g_coat_roughness = clamp((_e328 * _e330), 0.02f, 1f); + let _e335 = layer_info.transmission[0u]; + let _e337 = coatTexel[2u]; + g_transmission = clamp((_e335 * _e337), 0f, 1f); + let _e342 = layer_info.transmission[1u]; + let _e344 = coatTexel[3u]; + g_thickness = max((_e342 * _e344), 0f); + let _e349 = layer_info.transmission[2u]; + distance_5 = _e349; + let _e350 = distance_5; + if (_e350 > 0f) { + let _e353 = layer_info.attenuation; + let _e356 = g_thickness; + let _e357 = distance_5; + local_44 = pow(max(_e353.xyz, vec3(0.0001f, 0.0001f, 0.0001f)), vec3((_e356 / _e357))); } else { local_44 = vec3(1f, 1f, 1f); } - let _e360 = local_44; - g_transmittance = _e360; - let _e363 = layer_info.iridescence[0u]; - g_iridescence = clamp(_e363, 0f, 1f); - let _e366 = (*s_17).n; - g_coat_n = _e366; - let _e368 = (*s_17).n; - let _e370 = (*s_17).v; - g_coat_n_dot_v = max(dot(_e368, _e370), 0.0001f); - let _e373 = g_coat_n_dot_v; - fc = (0.04f + (0.96f * pow((1f - _e373), 5f))); - let _e378 = g_coat; - let _e379 = fc; - g_coat_through = (1f - (_e378 * _e379)); + let _e361 = local_44; + g_transmittance = _e361; + let _e364 = layer_info.iridescence[0u]; + g_iridescence = clamp(_e364, 0f, 1f); + let _e367 = (*s_17).n; + g_coat_n = _e367; + let _e369 = (*s_17).n; + let _e371 = (*s_17).v; + g_coat_n_dot_v = max(dot(_e369, _e371), 0.0001f); + let _e374 = g_coat_n_dot_v; + fc = (0.04f + (0.96f * pow((1f - _e374), 5f))); + let _e379 = g_coat; + let _e380 = fc; + g_coat_through = (1f - (_e379 * _e380)); return; } @@ -20171,78 +20258,78 @@ fn ReadSurface_u0028_() -> Surface { var noise_1: f32; param_236 = 0i; - let _e269 = MapUv_u0028_i1_u003b((¶m_236)); - let _e270 = MaterialLodBias_u0028_(); - let _e271 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e269, _e270); - texel_6 = _e271; - let _e272 = texel_6; - param_237 = _e272.xyz; - let _e274 = SrgbToLinear_u0028_vf3_u003b((¶m_237)); - let _e276 = frag_info.base_color; - param_238 = _e276.xyz; - let _e278 = SrgbToLinear_u0028_vf3_u003b((¶m_238)); - let _e280 = v_color_1; - s_18.albedo = ((_e274 * _e278) * _e280.xyz); - let _e285 = texel_6[3u]; - let _e288 = frag_info.base_color[3u]; - let _e291 = v_color_1[3u]; - s_18.alpha = ((_e285 * _e288) * _e291); - let _e295 = s_18.albedo; - g_albedo = _e295; - let _e298 = frag_info.material2_[0u]; - cutoff = _e298; - let _e299 = cutoff; - if (_e299 >= 0f) { - let _e302 = s_18.alpha; - let _e303 = cutoff; - if (_e302 < _e303) { + let _e270 = MapUv_u0028_i1_u003b((¶m_236)); + let _e271 = MaterialLodBias_u0028_(); + let _e272 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e270, _e271); + texel_6 = _e272; + let _e273 = texel_6; + param_237 = _e273.xyz; + let _e275 = SrgbToLinear_u0028_vf3_u003b((¶m_237)); + let _e277 = frag_info.base_color; + param_238 = _e277.xyz; + let _e279 = SrgbToLinear_u0028_vf3_u003b((¶m_238)); + let _e281 = v_color_1; + s_18.albedo = ((_e275 * _e279) * _e281.xyz); + let _e286 = texel_6[3u]; + let _e289 = frag_info.base_color[3u]; + let _e292 = v_color_1[3u]; + s_18.alpha = ((_e286 * _e289) * _e292); + let _e296 = s_18.albedo; + g_albedo = _e296; + let _e299 = frag_info.material2_[0u]; + cutoff = _e299; + let _e300 = cutoff; + if (_e300 >= 0f) { + let _e303 = s_18.alpha; + let _e304 = cutoff; + if (_e303 < _e304) { discard; } } else { - let _e305 = cutoff; - if (_e305 < -1.5f) { - let _e307 = v_world_position_1; - anchored = floor((_e307 * 16f)); - let _e310 = anchored; - noise_1 = fract((sin(dot(_e310, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e316 = s_18.alpha; - let _e317 = noise_1; - if (_e316 < _e317) { + let _e306 = cutoff; + if (_e306 < -1.5f) { + let _e308 = v_world_position_1; + anchored = floor((_e308 * 16f)); + let _e311 = anchored; + noise_1 = fract((sin(dot(_e311, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e317 = s_18.alpha; + let _e318 = noise_1; + if (_e317 < _e318) { discard; } } } - let _e319 = cutoff; - let _e321 = cutoff; - g_premultiply = ((_e319 < 0f) && (_e321 > -0.75f)); - let _e324 = v_normal_1; - s_18.n = normalize(_e324); - let _e327 = gl_FrontFacing_1; - if !(_e327) { - let _e330 = s_18.n; - s_18.n = -(_e330); - } - let _e334 = frag_info.camera_position; - let _e336 = v_world_position_1; - s_18.v = normalize((_e334.xyz - _e336)); - let _e341 = s_18.n; - let _e343 = s_18.v; - s_18.n_dot_v = max(dot(_e341, _e343), 0.0001f); - let _e349 = frag_info.material[0u]; - s_18.metallic = clamp(_e349, 0f, 1f); - let _e354 = frag_info.material[1u]; - s_18.roughness = clamp(_e354, 0.02f, 1f); - let _e358 = frag_info.ambient_ground; - let _e361 = frag_info.ambient_sky; - let _e365 = s_18.n[1u]; - let _e372 = frag_info.material[2u]; - s_18.ambient = (mix(_e358.xyz, _e361.xyz, vec3(((_e365 * 0.5f) + 0.5f))) * _e372); - let _e377 = frag_info.frame_params[0u]; - s_18.exposure = max(_e377, 0f); + let _e320 = cutoff; + let _e322 = cutoff; + g_premultiply = ((_e320 < 0f) && (_e322 > -0.75f)); + let _e325 = v_normal_1; + s_18.n = normalize(_e325); + let _e328 = gl_FrontFacing_1; + if !(_e328) { + let _e331 = s_18.n; + s_18.n = -(_e331); + } + let _e335 = frag_info.camera_position; + let _e337 = v_world_position_1; + s_18.v = normalize((_e335.xyz - _e337)); + let _e342 = s_18.n; + let _e344 = s_18.v; + s_18.n_dot_v = max(dot(_e342, _e344), 0.0001f); + let _e350 = frag_info.material[0u]; + s_18.metallic = clamp(_e350, 0f, 1f); + let _e355 = frag_info.material[1u]; + s_18.roughness = clamp(_e355, 0.02f, 1f); + let _e359 = frag_info.ambient_ground; + let _e362 = frag_info.ambient_sky; + let _e366 = s_18.n[1u]; + let _e373 = frag_info.material[2u]; + s_18.ambient = (mix(_e359.xyz, _e362.xyz, vec3(((_e366 * 0.5f) + 0.5f))) * _e373); + let _e378 = frag_info.frame_params[0u]; + s_18.exposure = max(_e378, 0f); s_18.occlusion = 1f; s_18.emissive = vec3(0f, 0f, 0f); - let _e382 = s_18; - return _e382; + let _e383 = s_18; + return _e383; } fn main_1() { @@ -20280,7 +20367,7 @@ fn main_1() { var average_2: vec3; var multiple: vec3; var reflects: vec3; - var through: vec3; + var through_1: vec3; var local_45: vec3; var param_249: Surface; var param_250: f32; @@ -20305,6 +20392,7 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; + light_transmittance = vec3(1f, 1f, 1f); g_f0_dielectric = vec3(0.04f, 0.04f, 0.04f); g_f90_ = 1f; g_coat = 0f; @@ -20324,248 +20412,248 @@ fn main_1() { g_transmittance = vec3(1f, 1f, 1f); g_iridescence = 0f; g_irid_fresnel = vec3(0.04f, 0.04f, 0.04f); - let _e313 = ReadSurface_u0028_(); - s_19 = _e313; - let _e314 = s_19; - param_239 = _e314; - ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_239)); + let _e314 = ReadSurface_u0028_(); + s_19 = _e314; let _e315 = s_19; - param_240 = _e315; + param_239 = _e315; + ReadLayers_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_239)); + let _e316 = s_19; + param_240 = _e316; ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_240)); - let _e316 = param_240; - s_19 = _e316; - let _e317 = s_19; - param_241 = _e317; + let _e317 = param_240; + s_19 = _e317; + let _e318 = s_19; + param_241 = _e318; ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_241)); - let _e318 = param_241; - s_19 = _e318; - let _e319 = s_19; - param_242 = _e319; - ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_242)); + let _e319 = param_241; + s_19 = _e319; let _e320 = s_19; - param_243 = _e320; + param_242 = _e320; + ReadLayersOnMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_242)); + let _e321 = s_19; + param_243 = _e321; ReadIridescence_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_243)); - let _e322 = s_19.metallic; - metallic = clamp(_e322, 0f, 1f); - let _e325 = s_19.albedo; - let _e326 = metallic; - diffuseColor_1 = (_e325 * (1f - _e326)); - let _e329 = EonDiffuse_u0028_(); - if _e329 { - let _e330 = diffuseColor_1; - param_244 = _e330; - let _e332 = s_19.roughness; - param_245 = _e332; - let _e334 = s_19.n_dot_v; - param_246 = _e334; - let _e335 = EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_244), (¶m_245), (¶m_246)); - diffuseColor_1 = _e335; - } - let _e336 = diffuseColor_1; - let _e338 = s_19.ambient; - let _e341 = s_19.occlusion; - ambient = ((_e336 * _e338) * _e341); - let _e343 = SceneColourBound_u0028_(); - let _e344 = g_transmittance; - let _e347 = g_transmission; - let _e350 = ambient; - ambient = (_e350 * mix(vec3(1f, 1f, 1f), select(_e344, vec3(0f, 0f, 0f), vec3(_e343)), vec3(_e347))); - let _e354 = frag_info.frame_params[3u]; - levels_1 = _e354; + let _e323 = s_19.metallic; + metallic = clamp(_e323, 0f, 1f); + let _e326 = s_19.albedo; + let _e327 = metallic; + diffuseColor_1 = (_e326 * (1f - _e327)); + let _e330 = EonDiffuse_u0028_(); + if _e330 { + let _e331 = diffuseColor_1; + param_244 = _e331; + let _e333 = s_19.roughness; + param_245 = _e333; + let _e335 = s_19.n_dot_v; + param_246 = _e335; + let _e336 = EonAlbedo_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_244), (¶m_245), (¶m_246)); + diffuseColor_1 = _e336; + } + let _e337 = diffuseColor_1; + let _e339 = s_19.ambient; + let _e342 = s_19.occlusion; + ambient = ((_e337 * _e339) * _e342); + let _e344 = SceneColourBound_u0028_(); + let _e345 = g_transmittance; + let _e348 = g_transmission; + let _e351 = ambient; + ambient = (_e351 * mix(vec3(1f, 1f, 1f), select(_e345, vec3(0f, 0f, 0f), vec3(_e344)), vec3(_e348))); + let _e355 = frag_info.frame_params[3u]; + levels_1 = _e355; coatAmbient = vec3(0f, 0f, 0f); - let _e356 = s_19.ambient; - sheenIncoming = _e356; - let _e357 = levels_1; - if (_e357 > 0f) { - let _e359 = g_f0_dielectric; - let _e361 = s_19.albedo; - let _e362 = metallic; - f0_4 = mix(_e359, _e361, vec3(_e362)); - let _e365 = g_f90_; - let _e366 = metallic; - f90_2 = mix(_e365, 1f, _e366); - let _e369 = s_19.n; - bent = _e369; - let _e370 = g_aniso; - if (_e370 > 0f) { - let _e372 = g_aniso_t; - let _e374 = s_19.v; - across = cross(_e372, _e374); - let _e376 = across; - let _e377 = g_aniso_t; - anisoN = cross(_e376, _e377); - let _e379 = g_aniso; - let _e381 = s_19.roughness; - bend = (1f - (_e379 * (1f - _e381))); - let _e385 = bend; + let _e357 = s_19.ambient; + sheenIncoming = _e357; + let _e358 = levels_1; + if (_e358 > 0f) { + let _e360 = g_f0_dielectric; + let _e362 = s_19.albedo; + let _e363 = metallic; + f0_4 = mix(_e360, _e362, vec3(_e363)); + let _e366 = g_f90_; + let _e367 = metallic; + f90_2 = mix(_e366, 1f, _e367); + let _e370 = s_19.n; + bent = _e370; + let _e371 = g_aniso; + if (_e371 > 0f) { + let _e373 = g_aniso_t; + let _e375 = s_19.v; + across = cross(_e373, _e375); + let _e377 = across; + let _e378 = g_aniso_t; + anisoN = cross(_e377, _e378); + let _e380 = g_aniso; + let _e382 = s_19.roughness; + bend = (1f - (_e380 * (1f - _e382))); let _e386 = bend; - let _e388 = bend; - let _e390 = bend; - bend4_ = (((_e385 * _e386) * _e388) * _e390); - let _e392 = anisoN; - let _e394 = s_19.n; - let _e395 = bend4_; - bent = normalize(mix(_e392, _e394, vec3(_e395))); - } - let _e400 = s_19.v; - let _e402 = bent; - reflected = reflect(-(_e400), _e402); - let _e405 = s_19.n; - let _e406 = levels_1; - let _e407 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e405, _e406); - irradiance = _e407.xyz; - let _e409 = reflected; - let _e411 = s_19.roughness; - let _e412 = levels_1; - let _e414 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e409, (_e411 * _e412)); - prefiltered = _e414.xyz; - let _e417 = s_19.roughness; - param_247 = _e417; - let _e419 = s_19.n_dot_v; - param_248 = _e419; - let _e420 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_247), (¶m_248)); - ab_4 = _e420; - let _e421 = f0_4; - let _e422 = g_irid_fresnel; - let _e423 = g_iridescence; - let _e427 = ab_4[0u]; - let _e429 = f90_2; - let _e431 = ab_4[1u]; - single_1 = ((mix(_e421, _e422, vec3(_e423)) * _e427) + vec3((_e429 * _e431))); - let _e435 = prefiltered; - let _e436 = single_1; - specular_1 = (_e435 * _e436); - let _e438 = EnergyCompensation_u0028_(); - if _e438 { - let _e440 = ab_4[0u]; - let _e442 = ab_4[1u]; - missed = (1f - (_e440 + _e442)); - let _e445 = f0_4; + let _e387 = bend; + let _e389 = bend; + let _e391 = bend; + bend4_ = (((_e386 * _e387) * _e389) * _e391); + let _e393 = anisoN; + let _e395 = s_19.n; + let _e396 = bend4_; + bent = normalize(mix(_e393, _e395, vec3(_e396))); + } + let _e401 = s_19.v; + let _e403 = bent; + reflected = reflect(-(_e401), _e403); + let _e406 = s_19.n; + let _e407 = levels_1; + let _e408 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e406, _e407); + irradiance = _e408.xyz; + let _e410 = reflected; + let _e412 = s_19.roughness; + let _e413 = levels_1; + let _e415 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e410, (_e412 * _e413)); + prefiltered = _e415.xyz; + let _e418 = s_19.roughness; + param_247 = _e418; + let _e420 = s_19.n_dot_v; + param_248 = _e420; + let _e421 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_247), (¶m_248)); + ab_4 = _e421; + let _e422 = f0_4; + let _e423 = g_irid_fresnel; + let _e424 = g_iridescence; + let _e428 = ab_4[0u]; + let _e430 = f90_2; + let _e432 = ab_4[1u]; + single_1 = ((mix(_e422, _e423, vec3(_e424)) * _e428) + vec3((_e430 * _e432))); + let _e436 = prefiltered; + let _e437 = single_1; + specular_1 = (_e436 * _e437); + let _e439 = EnergyCompensation_u0028_(); + if _e439 { + let _e441 = ab_4[0u]; + let _e443 = ab_4[1u]; + missed = (1f - (_e441 + _e443)); let _e446 = f0_4; - average_2 = (_e445 + ((vec3(1f, 1f, 1f) - _e446) / vec3(21f))); - let _e451 = single_1; - let _e452 = average_2; - let _e454 = missed; - let _e455 = average_2; - multiple = ((_e451 * _e452) / (vec3(1f, 1f, 1f) - (_e455 * _e454))); - let _e459 = multiple; - let _e460 = missed; - let _e462 = irradiance; - let _e464 = specular_1; - specular_1 = (_e464 + ((_e459 * _e460) * _e462)); - } - let _e466 = diffuseColor_1; - let _e467 = irradiance; - let _e469 = specular_1; - let _e473 = frag_info.material[2u]; - let _e476 = s_19.occlusion; - ambient = ((((_e466 * _e467) + _e469) * _e473) * _e476); - let _e478 = g_f0_dielectric; - let _e479 = g_irid_fresnel; - let _e480 = g_iridescence; - let _e484 = ab_4[0u]; - let _e486 = g_f90_; - let _e488 = ab_4[1u]; - reflects = ((mix(_e478, _e479, vec3(_e480)) * _e484) + vec3((_e486 * _e488))); - let _e492 = SceneColourBound_u0028_(); - if _e492 { + let _e447 = f0_4; + average_2 = (_e446 + ((vec3(1f, 1f, 1f) - _e447) / vec3(21f))); + let _e452 = single_1; + let _e453 = average_2; + let _e455 = missed; + let _e456 = average_2; + multiple = ((_e452 * _e453) / (vec3(1f, 1f, 1f) - (_e456 * _e455))); + let _e460 = multiple; + let _e461 = missed; + let _e463 = irradiance; + let _e465 = specular_1; + specular_1 = (_e465 + ((_e460 * _e461) * _e463)); + } + let _e467 = diffuseColor_1; + let _e468 = irradiance; + let _e470 = specular_1; + let _e474 = frag_info.material[2u]; + let _e477 = s_19.occlusion; + ambient = ((((_e467 * _e468) + _e470) * _e474) * _e477); + let _e479 = g_f0_dielectric; + let _e480 = g_irid_fresnel; + let _e481 = g_iridescence; + let _e485 = ab_4[0u]; + let _e487 = g_f90_; + let _e489 = ab_4[1u]; + reflects = ((mix(_e479, _e480, vec3(_e481)) * _e485) + vec3((_e487 * _e489))); + let _e493 = SceneColourBound_u0028_(); + if _e493 { local_45 = vec3(0f, 0f, 0f); } else { - let _e493 = s_19; - param_249 = _e493; - let _e494 = levels_1; - param_250 = _e494; - let _e495 = TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_f1_u003b((¶m_249), (¶m_250)); - let _e496 = g_transmittance; - let _e498 = reflects; - local_45 = ((_e495 * _e496) * (vec3(1f, 1f, 1f) - min(_e498, vec3(1f, 1f, 1f)))); - } - let _e502 = local_45; - through = _e502; - let _e503 = diffuseColor_1; - let _e504 = through; - let _e505 = irradiance; - let _e508 = g_transmission; - let _e512 = frag_info.material[2u]; - let _e515 = s_19.occlusion; - let _e517 = ambient; - ambient = (_e517 + ((((_e503 * (_e504 - _e505)) * _e508) * _e512) * _e515)); - let _e520 = s_19.v; - let _e522 = g_coat_n; - let _e524 = g_coat_roughness; - let _e525 = levels_1; - let _e527 = textureSampleLevel(environment_texture_tex, environment_texture_smp, reflect(-(_e520), _e522), (_e524 * _e525)); - coatPrefiltered = _e527.xyz; - let _e529 = g_coat_roughness; - param_251 = _e529; - let _e530 = g_coat_n_dot_v; - param_252 = _e530; - let _e531 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_251), (¶m_252)); - coatAb = _e531; - let _e532 = coatPrefiltered; - let _e534 = coatAb[0u]; - let _e537 = coatAb[1u]; - let _e540 = g_coat; - let _e544 = frag_info.material[2u]; - let _e547 = s_19.occlusion; - coatAmbient = ((((_e532 * ((0.04f * _e534) + _e537)) * _e540) * _e544) * _e547); - let _e550 = s_19.n; - let _e551 = g_sheen_roughness; - let _e552 = levels_1; - let _e554 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e550, (_e551 * _e552)); - let _e558 = frag_info.material[2u]; - sheenIncoming = (_e554.xyz * _e558); - } - let _e560 = SceneColourBound_u0028_(); - if _e560 { - let _e562 = s_19.roughness; - param_253 = _e562; - let _e564 = s_19.n_dot_v; - param_254 = _e564; - let _e565 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_253), (¶m_254)); - sceneAb = _e565; - let _e566 = g_f0_dielectric; - let _e567 = g_irid_fresnel; - let _e568 = g_iridescence; - let _e572 = sceneAb[0u]; - let _e574 = g_f90_; - let _e576 = sceneAb[1u]; - sceneReflects = ((mix(_e566, _e567, vec3(_e568)) * _e572) + vec3((_e574 * _e576))); - let _e580 = diffuseColor_1; - let _e581 = s_19; - param_255 = _e581; - let _e582 = SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_255)); - let _e584 = g_transmittance; - let _e586 = sceneReflects; - let _e590 = g_transmission; - let _e592 = ambient; - ambient = (_e592 + ((((_e580 * _e582) * _e584) * (vec3(1f, 1f, 1f) - min(_e586, vec3(1f, 1f, 1f)))) * _e590)); - } - let _e594 = diffuseColor_1; - let _e595 = SampleLightmap_u0028_(); - let _e598 = s_19.occlusion; - let _e600 = ambient; - ambient = (_e600 + ((_e594 * _e595) * _e598)); - let _e602 = g_sheen; - let _e603 = g_sheen_albedo; - let _e605 = sheenIncoming; - let _e608 = s_19.occlusion; - sheenAmbient = (((_e602 * _e603) * _e605) * _e608); - let _e610 = s_19; - param_256 = _e610; - let _e611 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_256)); - let _e613 = s_19.occlusion; - let _e615 = ambient; - let _e616 = g_sheen_scale; - let _e618 = sheenAmbient; - let _e621 = s_19.emissive; - let _e623 = g_coat_through; - let _e626 = coatAmbient; - param_257 = (((_e611 * _e613) + ((((_e615 * _e616) + _e618) + _e621) * _e623)) + _e626); - let _e629 = s_19.alpha; - param_258 = _e629; - let _e631 = s_19.roughness; - param_259 = _e631; + let _e494 = s_19; + param_249 = _e494; + let _e495 = levels_1; + param_250 = _e495; + let _e496 = TransmittedRadiance_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_f1_u003b((¶m_249), (¶m_250)); + let _e497 = g_transmittance; + let _e499 = reflects; + local_45 = ((_e496 * _e497) * (vec3(1f, 1f, 1f) - min(_e499, vec3(1f, 1f, 1f)))); + } + let _e503 = local_45; + through_1 = _e503; + let _e504 = diffuseColor_1; + let _e505 = through_1; + let _e506 = irradiance; + let _e509 = g_transmission; + let _e513 = frag_info.material[2u]; + let _e516 = s_19.occlusion; + let _e518 = ambient; + ambient = (_e518 + ((((_e504 * (_e505 - _e506)) * _e509) * _e513) * _e516)); + let _e521 = s_19.v; + let _e523 = g_coat_n; + let _e525 = g_coat_roughness; + let _e526 = levels_1; + let _e528 = textureSampleLevel(environment_texture_tex, environment_texture_smp, reflect(-(_e521), _e523), (_e525 * _e526)); + coatPrefiltered = _e528.xyz; + let _e530 = g_coat_roughness; + param_251 = _e530; + let _e531 = g_coat_n_dot_v; + param_252 = _e531; + let _e532 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_251), (¶m_252)); + coatAb = _e532; + let _e533 = coatPrefiltered; + let _e535 = coatAb[0u]; + let _e538 = coatAb[1u]; + let _e541 = g_coat; + let _e545 = frag_info.material[2u]; + let _e548 = s_19.occlusion; + coatAmbient = ((((_e533 * ((0.04f * _e535) + _e538)) * _e541) * _e545) * _e548); + let _e551 = s_19.n; + let _e552 = g_sheen_roughness; + let _e553 = levels_1; + let _e555 = textureSampleLevel(environment_texture_tex, environment_texture_smp, _e551, (_e552 * _e553)); + let _e559 = frag_info.material[2u]; + sheenIncoming = (_e555.xyz * _e559); + } + let _e561 = SceneColourBound_u0028_(); + if _e561 { + let _e563 = s_19.roughness; + param_253 = _e563; + let _e565 = s_19.n_dot_v; + param_254 = _e565; + let _e566 = EnvBrdf_u0028_f1_u003b_f1_u003b((¶m_253), (¶m_254)); + sceneAb = _e566; + let _e567 = g_f0_dielectric; + let _e568 = g_irid_fresnel; + let _e569 = g_iridescence; + let _e573 = sceneAb[0u]; + let _e575 = g_f90_; + let _e577 = sceneAb[1u]; + sceneReflects = ((mix(_e567, _e568, vec3(_e569)) * _e573) + vec3((_e575 * _e577))); + let _e581 = diffuseColor_1; + let _e582 = s_19; + param_255 = _e582; + let _e583 = SceneBehind_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_255)); + let _e585 = g_transmittance; + let _e587 = sceneReflects; + let _e591 = g_transmission; + let _e593 = ambient; + ambient = (_e593 + ((((_e581 * _e583) * _e585) * (vec3(1f, 1f, 1f) - min(_e587, vec3(1f, 1f, 1f)))) * _e591)); + } + let _e595 = diffuseColor_1; + let _e596 = SampleLightmap_u0028_(); + let _e599 = s_19.occlusion; + let _e601 = ambient; + ambient = (_e601 + ((_e595 * _e596) * _e599)); + let _e603 = g_sheen; + let _e604 = g_sheen_albedo; + let _e606 = sheenIncoming; + let _e609 = s_19.occlusion; + sheenAmbient = (((_e603 * _e604) * _e606) * _e609); + let _e611 = s_19; + param_256 = _e611; + let _e612 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_256)); + let _e614 = s_19.occlusion; + let _e616 = ambient; + let _e617 = g_sheen_scale; + let _e619 = sheenAmbient; + let _e622 = s_19.emissive; + let _e624 = g_coat_through; + let _e627 = coatAmbient; + param_257 = (((_e612 * _e614) + ((((_e616 * _e617) + _e619) + _e622) * _e624)) + _e627); + let _e630 = s_19.alpha; + param_258 = _e630; + let _e632 = s_19.roughness; + param_259 = _e632; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_257), (¶m_258), (¶m_259)); return; } @@ -21091,6 +21179,7 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; +var light_transmittance: vec3; var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; @@ -21158,10 +21247,10 @@ var shadow_texture_tex: texture_2d; var shadow_texture_smp: sampler; fn ViewDepth_u0028_() -> f32 { - let _e177 = v_world_position_1; - let _e179 = fog_info.eye; - let _e183 = fog_info.forward; - return dot((_e177 - _e179.xyz), _e183.xyz); + let _e178 = v_world_position_1; + let _e180 = fog_info.eye; + let _e184 = fog_info.forward; + return dot((_e178 - _e180.xyz), _e184.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -21170,22 +21259,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e182 = ViewDepth_u0028_(); - z = abs(_e182); - let _e184 = z; - near = (_e184 / 5f); - let _e186 = z; - far = (_e186 / 200f); - let _e188 = far; + let _e183 = ViewDepth_u0028_(); + z = abs(_e183); + let _e185 = z; + near = (_e185 / 5f); + let _e187 = z; + far = (_e187 / 200f); let _e189 = far; - let _e191 = far; - far3_ = ((_e188 * _e189) * _e191); - let _e193 = (*alpha); - let _e194 = near; + let _e190 = far; + let _e192 = far; + far3_ = ((_e189 * _e190) * _e192); + let _e194 = (*alpha); let _e195 = near; - let _e198 = far3_; + let _e196 = near; let _e199 = far3_; - return (_e193 * clamp((10f / ((0.00001f + (_e194 * _e195)) + (_e198 * _e199))), 0.01f, 3000f)); + let _e200 = far3_; + return (_e194 * clamp((10f / ((0.00001f + (_e195 * _e196)) + (_e199 * _e200))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -21197,39 +21286,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e186 = fog_info.forward[3u]; - mode = _e186; - let _e187 = mode; - if (_e187 > 0.5f) { - let _e190 = frag_color[3u]; - alpha_1 = _e190; - let _e191 = alpha_1; - param = _e191; - let _e192 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e192; - let _e193 = frag_color; - let _e195 = weight; - let _e196 = (_e193.xyz * _e195); - let _e197 = alpha_1; - let _e198 = weight; - accumulate = vec4(_e196.x, _e196.y, _e196.z, (_e197 * _e198)); - let _e204 = mode; - let _e206 = mode; - revealage = ((_e204 > 1.5f) && (_e206 < 2.5f)); - let _e209 = revealage; - if _e209 { - let _e210 = alpha_1; - local = vec4(_e210); + let _e187 = fog_info.forward[3u]; + mode = _e187; + let _e188 = mode; + if (_e188 > 0.5f) { + let _e191 = frag_color[3u]; + alpha_1 = _e191; + let _e192 = alpha_1; + param = _e192; + let _e193 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e193; + let _e194 = frag_color; + let _e196 = weight; + let _e197 = (_e194.xyz * _e196); + let _e198 = alpha_1; + let _e199 = weight; + accumulate = vec4(_e197.x, _e197.y, _e197.z, (_e198 * _e199)); + let _e205 = mode; + let _e207 = mode; + revealage = ((_e205 > 1.5f) && (_e207 < 2.5f)); + let _e210 = revealage; + if _e210 { + let _e211 = alpha_1; + local = vec4(_e211); } else { - let _e212 = accumulate; - local = _e212; + let _e213 = accumulate; + local = _e213; } - let _e213 = local; - frag_color = _e213; - let _e214 = mode; - if (_e214 > 2.5f) { - let _e216 = alpha_1; - frag_surface = vec4(_e216); + let _e214 = local; + frag_color = _e214; + let _e215 = mode; + if (_e215 > 2.5f) { + let _e217 = alpha_1; + frag_surface = vec4(_e217); } } return; @@ -21238,29 +21327,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e180 = (*n)[0u]; - let _e183 = (*n)[1u]; - let _e187 = (*n)[2u]; - let _e190 = (*n); - (*n) = (_e190 / vec3(((abs(_e180) + abs(_e183)) + abs(_e187)))); - let _e193 = (*n); - e = _e193.xy; - let _e196 = (*n)[2u]; - if (_e196 < 0f) { - let _e198 = (*n); - let _e204 = (*n)[0u]; - let _e208 = (*n)[1u]; - e = ((vec2(1f) - abs(_e198.yx)) * vec2(select(-1f, 1f, (_e204 >= 0f)), select(-1f, 1f, (_e208 >= 0f)))); + let _e181 = (*n)[0u]; + let _e184 = (*n)[1u]; + let _e188 = (*n)[2u]; + let _e191 = (*n); + (*n) = (_e191 / vec3(((abs(_e181) + abs(_e184)) + abs(_e188)))); + let _e194 = (*n); + e = _e194.xy; + let _e197 = (*n)[2u]; + if (_e197 < 0f) { + let _e199 = (*n); + let _e205 = (*n)[0u]; + let _e209 = (*n)[1u]; + e = ((vec2(1f) - abs(_e199.yx)) * vec2(select(-1f, 1f, (_e205 >= 0f)), select(-1f, 1f, (_e209 >= 0f)))); } - let _e213 = e; - return ((_e213 * 0.5f) + vec2(0.5f)); + let _e214 = e; + return ((_e214 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e178 = (*linear); - let _e180 = (*linear); - let _e184 = (*linear); - return mix((_e178 * 12.92f), ((pow(_e180, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e184)); + let _e179 = (*linear); + let _e181 = (*linear); + let _e185 = (*linear); + return mix((_e179 * 12.92f), ((pow(_e181, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e185)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -21268,57 +21357,57 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e181 = g_albedo; - param_1 = clamp(_e181, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e183 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e183.x, _e183.y, _e183.z, 1f); - let _e188 = g_debug_surface_on; - if _e188 { - let _e189 = g_debug_surface; - let _e190 = ViewDepth_u0028_(); - frag_surface = vec4(_e189.x, _e189.y, _e189.z, _e190); + let _e182 = g_albedo; + param_1 = clamp(_e182, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e184 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e184.x, _e184.y, _e184.z, 1f); + let _e189 = g_debug_surface_on; + if _e189 { + let _e190 = g_debug_surface; + let _e191 = ViewDepth_u0028_(); + frag_surface = vec4(_e190.x, _e190.y, _e190.z, _e191); return; } - let _e195 = v_normal_1; - geometric = normalize(_e195); - let _e197 = gl_FrontFacing_1; - if !(_e197) { - let _e199 = geometric; - geometric = -(_e199); - } - let _e201 = geometric; - param_2 = _e201; - let _e202 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e203 = (*roughness); - let _e205 = ViewDepth_u0028_(); - frag_surface = vec4(_e202.x, _e202.y, clamp(_e203, 0f, 1f), _e205); + let _e196 = v_normal_1; + geometric = normalize(_e196); + let _e198 = gl_FrontFacing_1; + if !(_e198) { + let _e200 = geometric; + geometric = -(_e200); + } + let _e202 = geometric; + param_2 = _e202; + let _e203 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e204 = (*roughness); + let _e206 = ViewDepth_u0028_(); + frag_surface = vec4(_e203.x, _e203.y, clamp(_e204, 0f, 1f), _e206); return; } fn EyeDistance_u0028_() -> f32 { - let _e177 = v_world_position_1; - let _e179 = fog_info.eye; - return distance(_e177, _e179.xyz); + let _e178 = v_world_position_1; + let _e180 = fog_info.eye; + return distance(_e178, _e180.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; - let _e182 = fog_info.fog[3u]; - density = _e182; - let _e183 = density; - if (_e183 <= 0f) { - let _e185 = (*color); - return _e185; + let _e183 = fog_info.fog[3u]; + density = _e183; + let _e184 = density; + if (_e184 <= 0f) { + let _e186 = (*color); + return _e186; } - let _e186 = EyeDistance_u0028_(); - d = _e186; - let _e188 = fog_info.fog; - let _e190 = (*color); - let _e191 = density; - let _e193 = d; - return mix(_e188.xyz, _e190, vec3(clamp(exp((-(_e191) * _e193)), 0f, 1f))); + let _e187 = EyeDistance_u0028_(); + d = _e187; + let _e189 = fog_info.fog; + let _e191 = (*color); + let _e192 = density; + let _e194 = d; + return mix(_e189.xyz, _e191, vec3(clamp(exp((-(_e192) * _e194)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -21326,18 +21415,18 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e183 = g_premultiply; - let _e184 = (*alpha_2); - weight_1 = select(1f, _e184, _e183); - let _e186 = (*linearColor); - param_3 = _e186; - let _e187 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e188 = weight_1; - let _e189 = (_e187 * _e188); - let _e190 = (*alpha_2); - frag_color = vec4(_e189.x, _e189.y, _e189.z, _e190); - let _e195 = (*roughness_1); - param_4 = _e195; + let _e184 = g_premultiply; + let _e185 = (*alpha_2); + weight_1 = select(1f, _e185, _e184); + let _e187 = (*linearColor); + param_3 = _e187; + let _e188 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e189 = weight_1; + let _e190 = (_e188 * _e189); + let _e191 = (*alpha_2); + frag_color = vec4(_e190.x, _e190.y, _e190.z, _e191); + let _e196 = (*roughness_1); + param_4 = _e196; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; @@ -21349,54 +21438,54 @@ fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_ var fraction: f32; var ramp: f32; - let _e184 = (*s).roughness; - bands = mix(5f, 2f, _e184); - let _e187 = (*light).n_dot_l; - let _e188 = bands; - let _e191 = bands; - quantized = (floor((_e187 * _e188)) / _e191); - let _e194 = (*light).n_dot_l; - let _e195 = bands; - fraction = fract((_e194 * _e195)); - let _e198 = fraction; - let _e200 = bands; - let _e202 = quantized; - quantized = (_e202 + (smoothstep(0.85f, 1f, _e198) / _e200)); - let _e204 = quantized; - let _e206 = (*light).n_dot_l; - ramp = (_e204 / max(_e206, 0.001f)); - let _e210 = (*s).albedo; - let _e211 = ramp; - return (_e210 * _e211); + let _e185 = (*s).roughness; + bands = mix(5f, 2f, _e185); + let _e188 = (*light).n_dot_l; + let _e189 = bands; + let _e192 = bands; + quantized = (floor((_e188 * _e189)) / _e192); + let _e195 = (*light).n_dot_l; + let _e196 = bands; + fraction = fract((_e195 * _e196)); + let _e199 = fraction; + let _e201 = bands; + let _e203 = quantized; + quantized = (_e203 + (smoothstep(0.85f, 1f, _e199) / _e201)); + let _e205 = quantized; + let _e207 = (*light).n_dot_l; + ramp = (_e205 / max(_e207, 0.001f)); + let _e211 = (*s).albedo; + let _e212 = ramp; + return (_e211 * _e212); } fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e178 = (*i); - if (_e178 == 0i) { + let _e179 = (*i); + if (_e179 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e180 = (*i); - if (_e180 == 1i) { + let _e181 = (*i); + if (_e181 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e182 = (*i); - if (_e182 == 2i) { + let _e183 = (*i); + if (_e183 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e184 = (*i); - if (_e184 == 3i) { + let _e185 = (*i); + if (_e185 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e186 = (*i); - if (_e186 == 4i) { + let _e187 = (*i); + if (_e187 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e188 = (*i); - if (_e188 == 5i) { + let _e189 = (*i); + if (_e189 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e190 = (*i); - if (_e190 == 6i) { + let _e191 = (*i); + if (_e191 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -21406,20 +21495,20 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr; var param_5: i32; - let _e183 = (*i_1); - param_5 = _e183; - let _e184 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); - p = _e184; - let _e186 = p[0u]; - let _e187 = (*ca); - let _e190 = p[1u]; - let _e191 = (*sa); - let _e195 = p[0u]; - let _e196 = (*sa); - let _e199 = p[1u]; - let _e200 = (*ca); - let _e204 = (*radius); - return (vec2(((_e186 * _e187) - (_e190 * _e191)), ((_e195 * _e196) + (_e199 * _e200))) * _e204); + let _e184 = (*i_1); + param_5 = _e184; + let _e185 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); + p = _e185; + let _e187 = p[0u]; + let _e188 = (*ca); + let _e191 = p[1u]; + let _e192 = (*sa); + let _e196 = p[0u]; + let _e197 = (*sa); + let _e200 = p[1u]; + let _e201 = (*ca); + let _e205 = (*radius); + return (vec2(((_e187 * _e188) - (_e191 * _e192)), ((_e196 * _e197) + (_e200 * _e201))) * _e205); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { @@ -21427,27 +21516,27 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr; var atlas: vec2; - let _e186 = point_shadow.params[0u]; - inset = _e186; - let _e187 = (*uv); - let _e188 = (*offset); - let _e190 = inset; + let _e187 = point_shadow.params[0u]; + inset = _e187; + let _e188 = (*uv); + let _e189 = (*offset); let _e191 = inset; - local_1 = clamp((_e187 + _e188), vec2(_e190), vec2((1f - _e191))); - let _e196 = local_1; - let _e197 = (*tile); - atlas = ((_e196 + _e197) * vec2(0.16666667f, 0.16666667f)); - let _e202 = point_shadow.params3_[0u]; - if (_e202 > 0.5f) { - let _e205 = atlas[1u]; - atlas[1u] = (1f - _e205); + let _e192 = inset; + local_1 = clamp((_e188 + _e189), vec2(_e191), vec2((1f - _e192))); + let _e197 = local_1; + let _e198 = (*tile); + atlas = ((_e197 + _e198) * vec2(0.16666667f, 0.16666667f)); + let _e203 = point_shadow.params3_[0u]; + if (_e203 > 0.5f) { + let _e206 = atlas[1u]; + atlas[1u] = (1f - _e206); } - let _e208 = atlas; - let _e209 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e208, 0f); - let _e211 = atlas; - let _e212 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e211, 0f); - let _e215 = (*range); - return (min(_e209.x, _e212.x) * _e215); + let _e209 = atlas; + let _e210 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e209, 0f); + let _e212 = atlas; + let _e213 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e212, 0f); + let _e216 = (*range); + return (min(_e210.x, _e213.x) * _e216); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { @@ -21457,24 +21546,24 @@ fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: pt var param_8: vec2; var param_9: f32; - let _e187 = (*uv_1); - param_6 = _e187; - let _e188 = (*offset_1); - param_7 = _e188; - let _e189 = (*tile_1); - param_8 = _e189; - let _e190 = (*range_1); - param_9 = _e190; - let _e191 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); - stored = _e191; - let _e192 = stored; - let _e193 = (*range_1); - if (_e192 >= (_e193 * 0.999f)) { + let _e188 = (*uv_1); + param_6 = _e188; + let _e189 = (*offset_1); + param_7 = _e189; + let _e190 = (*tile_1); + param_8 = _e190; + let _e191 = (*range_1); + param_9 = _e191; + let _e192 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); + stored = _e192; + let _e193 = stored; + let _e194 = (*range_1); + if (_e193 >= (_e194 * 0.999f)) { return 1f; } - let _e196 = (*receiver); - let _e197 = stored; - return select(1f, 0f, (_e196 > _e197)); + let _e197 = (*receiver); + let _e198 = stored; + return select(1f, 0f, (_e197 > _e198)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { @@ -21501,111 +21590,111 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u var world: f32; (*blockerOut) = -1f; - let _e208 = point_shadow.params2_[1u]; - lightRadius = _e208; - let _e211 = point_shadow.params2_[0u]; - minRadius = _e211; - let _e214 = point_shadow.params2_[2u]; - maxRadius = _e214; - let _e215 = lightRadius; - if (_e215 <= 0f) { - let _e217 = (*uv_2); - param_10 = _e217; + let _e209 = point_shadow.params2_[1u]; + lightRadius = _e209; + let _e212 = point_shadow.params2_[0u]; + minRadius = _e212; + let _e215 = point_shadow.params2_[2u]; + maxRadius = _e215; + let _e216 = lightRadius; + if (_e216 <= 0f) { + let _e218 = (*uv_2); + param_10 = _e218; param_11 = vec2(0f, 0f); - let _e218 = (*tile_2); - param_12 = _e218; - let _e219 = (*range_2); - param_13 = _e219; - let _e220 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); - (*blockerOut) = _e220; - let _e221 = minRadius; - return _e221; + let _e219 = (*tile_2); + param_12 = _e219; + let _e220 = (*range_2); + param_13 = _e220; + let _e221 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); + (*blockerOut) = _e221; + let _e222 = minRadius; + return _e222; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e222 = i_2; - if (_e222 < 8i) { - let _e224 = i_2; - param_14 = _e224; - let _e225 = (*ca_1); - param_15 = _e225; - let _e226 = (*sa_1); - param_16 = _e226; - let _e227 = maxRadius; - param_17 = _e227; - let _e228 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); - let _e229 = (*uv_2); - param_18 = _e229; - param_19 = _e228; - let _e230 = (*tile_2); - param_20 = _e230; - let _e231 = (*range_2); - param_21 = _e231; - let _e232 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); - stored_1 = _e232; - let _e233 = stored_1; - let _e234 = (*range_2); - if (_e233 >= (_e234 * 0.999f)) { + let _e223 = i_2; + if (_e223 < 8i) { + let _e225 = i_2; + param_14 = _e225; + let _e226 = (*ca_1); + param_15 = _e226; + let _e227 = (*sa_1); + param_16 = _e227; + let _e228 = maxRadius; + param_17 = _e228; + let _e229 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); + let _e230 = (*uv_2); + param_18 = _e230; + param_19 = _e229; + let _e231 = (*tile_2); + param_20 = _e231; + let _e232 = (*range_2); + param_21 = _e232; + let _e233 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); + stored_1 = _e233; + let _e234 = stored_1; + let _e235 = (*range_2); + if (_e234 >= (_e235 * 0.999f)) { continue; } - let _e237 = stored_1; - let _e238 = (*receiver_1); - if (_e237 >= _e238) { + let _e238 = stored_1; + let _e239 = (*receiver_1); + if (_e238 >= _e239) { continue; } - let _e240 = stored_1; - let _e241 = sum; - sum = (_e241 + _e240); - let _e243 = count; - count = (_e243 + 1f); + let _e241 = stored_1; + let _e242 = sum; + sum = (_e242 + _e241); + let _e244 = count; + count = (_e244 + 1f); continue; } else { break; } continuing { - let _e245 = i_2; - i_2 = (_e245 + 1i); + let _e246 = i_2; + i_2 = (_e246 + 1i); } } - let _e247 = count; - if (_e247 < 0.5f) { + let _e248 = count; + if (_e248 < 0.5f) { return -1f; } - let _e249 = sum; - let _e250 = count; - blocker = max((_e249 / _e250), 0.0001f); - let _e253 = blocker; - (*blockerOut) = _e253; - let _e254 = lightRadius; - let _e255 = (*receiver_1); - let _e256 = blocker; - let _e260 = blocker; - world = ((_e254 * max((_e255 - _e256), 0f)) / _e260); - let _e262 = world; - let _e263 = (*receiver_1); - let _e265 = (*tanHalf); - let _e268 = minRadius; - let _e269 = maxRadius; - return clamp((_e262 / ((2f * _e263) * _e265)), _e268, _e269); + let _e250 = sum; + let _e251 = count; + blocker = max((_e250 / _e251), 0.0001f); + let _e254 = blocker; + (*blockerOut) = _e254; + let _e255 = lightRadius; + let _e256 = (*receiver_1); + let _e257 = blocker; + let _e261 = blocker; + world = ((_e255 * max((_e256 - _e257), 0f)) / _e261); + let _e263 = world; + let _e264 = (*receiver_1); + let _e266 = (*tanHalf); + let _e269 = minRadius; + let _e270 = maxRadius; + return clamp((_e263 / ((2f * _e264) * _e266)), _e269, _e270); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { var local_2: vec2; - let _e179 = (*rows); - if (_e179 > 0f) { - let _e182 = gl_FragCoord_1[0u]; - let _e183 = (*rows); - let _e185 = gl_FragCoord_1[1u]; - local_2 = vec2(_e182, (_e183 - _e185)); + let _e180 = (*rows); + if (_e180 > 0f) { + let _e183 = gl_FragCoord_1[0u]; + let _e184 = (*rows); + let _e186 = gl_FragCoord_1[1u]; + local_2 = vec2(_e183, (_e184 - _e186)); } else { - let _e188 = gl_FragCoord_1; - local_2 = _e188.xy; + let _e189 = gl_FragCoord_1; + local_2 = _e189.xy; } - let _e190 = local_2; - return _e190; + let _e191 = local_2; + return _e191; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -21660,260 +21749,260 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_43: f32; var param_44: f32; - var phi_2149_: bool; - var phi_2210_: bool; - - let _e231 = (*lightIndex); - let _e235 = point_shadow.slots[_e231][0u]; - slot = i32((_e235 + 0.5f)); - let _e238 = (*lightIndex); - let _e242 = point_shadow.slots[_e238][0u]; - if (_e242 < 0f) { + var phi_2150_: bool; + var phi_2211_: bool; + + let _e232 = (*lightIndex); + let _e236 = point_shadow.slots[_e232][0u]; + slot = i32((_e236 + 0.5f)); + let _e239 = (*lightIndex); + let _e243 = point_shadow.slots[_e239][0u]; + if (_e243 < 0f) { return 1f; } - let _e246 = point_shadow.params[2u]; - strength = _e246; - let _e247 = strength; - if (_e247 <= 0f) { + let _e247 = point_shadow.params[2u]; + strength = _e247; + let _e248 = strength; + if (_e248 <= 0f) { return 1f; } - let _e249 = slot; - let _e252 = point_shadow.lights[_e249]; - let _e254 = (*world_1); - toLight = (_e252.xyz - _e254); - let _e256 = toLight; - toLightLength = max(length(_e256), 0.000001f); - let _e259 = (*normal); - let _e260 = toLight; - let _e261 = toLightLength; - nDotL = max(dot(_e259, (_e260 / vec3(_e261))), 0.15f); - let _e266 = nDotL; + let _e250 = slot; + let _e253 = point_shadow.lights[_e250]; + let _e255 = (*world_1); + toLight = (_e253.xyz - _e255); + let _e257 = toLight; + toLightLength = max(length(_e257), 0.000001f); + let _e260 = (*normal); + let _e261 = toLight; + let _e262 = toLightLength; + nDotL = max(dot(_e260, (_e261 / vec3(_e262))), 0.15f); let _e267 = nDotL; - let _e272 = nDotL; + let _e268 = nDotL; let _e273 = nDotL; - slope = min((sqrt(max((1f - (_e266 * _e267)), 0f)) / (_e272 * _e273)), 8f); - let _e277 = toLightLength; - let _e279 = (*lightIndex); - let _e283 = point_shadow.slots[_e279][2u]; - let _e288 = point_shadow.params3_[1u]; - texel = (((2f * _e277) * max(_e283, 0.0001f)) * _e288); - let _e290 = (*world_1); - let _e291 = (*normal); - let _e292 = texel; - let _e296 = point_shadow.params[3u]; - let _e298 = slope; - origin = (_e290 + (((_e291 * _e292) * _e296) * (1f + _e298))); - let _e302 = origin; - let _e303 = slot; - let _e306 = point_shadow.lights[_e303]; - toFragment = (_e302 - _e306.xyz); - let _e309 = toFragment; - distance_ = length(_e309); - let _e311 = slot; - let _e315 = point_shadow.lights[_e311][3u]; - range_3 = max(_e315, 0.0001f); - let _e317 = distance_; - let _e318 = range_3; - if (_e317 >= _e318) { + let _e274 = nDotL; + slope = min((sqrt(max((1f - (_e267 * _e268)), 0f)) / (_e273 * _e274)), 8f); + let _e278 = toLightLength; + let _e280 = (*lightIndex); + let _e284 = point_shadow.slots[_e280][2u]; + let _e289 = point_shadow.params3_[1u]; + texel = (((2f * _e278) * max(_e284, 0.0001f)) * _e289); + let _e291 = (*world_1); + let _e292 = (*normal); + let _e293 = texel; + let _e297 = point_shadow.params[3u]; + let _e299 = slope; + origin = (_e291 + (((_e292 * _e293) * _e297) * (1f + _e299))); + let _e303 = origin; + let _e304 = slot; + let _e307 = point_shadow.lights[_e304]; + toFragment = (_e303 - _e307.xyz); + let _e310 = toFragment; + distance_ = length(_e310); + let _e312 = slot; + let _e316 = point_shadow.lights[_e312][3u]; + range_3 = max(_e316, 0.0001f); + let _e318 = distance_; + let _e319 = range_3; + if (_e318 >= _e319) { return 1f; } face = 0i; - let _e320 = (*lightIndex); - let _e324 = point_shadow.slots[_e320][1u]; - if (_e324 < 0.5f) { - let _e326 = toFragment; - a = abs(_e326); - let _e329 = a[0u]; - let _e331 = a[1u]; - let _e332 = (_e329 >= _e331); - phi_2149_ = _e332; - if _e332 { - let _e334 = a[0u]; - let _e336 = a[2u]; - phi_2149_ = (_e334 >= _e336); - } - let _e339 = phi_2149_; - if _e339 { - let _e341 = toFragment[0u]; - face = select(1i, 0i, (_e341 > 0f)); + let _e321 = (*lightIndex); + let _e325 = point_shadow.slots[_e321][1u]; + if (_e325 < 0.5f) { + let _e327 = toFragment; + a = abs(_e327); + let _e330 = a[0u]; + let _e332 = a[1u]; + let _e333 = (_e330 >= _e332); + phi_2150_ = _e333; + if _e333 { + let _e335 = a[0u]; + let _e337 = a[2u]; + phi_2150_ = (_e335 >= _e337); + } + let _e340 = phi_2150_; + if _e340 { + let _e342 = toFragment[0u]; + face = select(1i, 0i, (_e342 > 0f)); } else { - let _e345 = a[1u]; - let _e347 = a[2u]; - if (_e345 >= _e347) { - let _e350 = toFragment[1u]; - face = select(3i, 2i, (_e350 > 0f)); + let _e346 = a[1u]; + let _e348 = a[2u]; + if (_e346 >= _e348) { + let _e351 = toFragment[1u]; + face = select(3i, 2i, (_e351 > 0f)); } else { - let _e354 = toFragment[2u]; - face = select(5i, 4i, (_e354 > 0f)); + let _e355 = toFragment[2u]; + face = select(5i, 4i, (_e355 > 0f)); } } } - let _e357 = slot; - let _e359 = face; - let _e363 = point_shadow.faces[((_e357 * 6i) + _e359)]; - let _e364 = origin; - clip = (_e363 * vec4(_e364.x, _e364.y, _e364.z, 1f)); - let _e371 = clip[3u]; - if (_e371 <= 0f) { + let _e358 = slot; + let _e360 = face; + let _e364 = point_shadow.faces[((_e358 * 6i) + _e360)]; + let _e365 = origin; + clip = (_e364 * vec4(_e365.x, _e365.y, _e365.z, 1f)); + let _e372 = clip[3u]; + if (_e372 <= 0f) { return 1f; } - let _e373 = clip; - let _e376 = clip[3u]; - ndc = (_e373.xy / vec2(_e376)); - let _e380 = ndc[0u]; - let _e382 = (abs(_e380) > 1f); - phi_2210_ = _e382; - if !(_e382) { - let _e385 = ndc[1u]; - phi_2210_ = (abs(_e385) > 1f); - } - let _e389 = phi_2210_; - if _e389 { + let _e374 = clip; + let _e377 = clip[3u]; + ndc = (_e374.xy / vec2(_e377)); + let _e381 = ndc[0u]; + let _e383 = (abs(_e381) > 1f); + phi_2211_ = _e383; + if !(_e383) { + let _e386 = ndc[1u]; + phi_2211_ = (abs(_e386) > 1f); + } + let _e390 = phi_2211_; + if _e390 { return 1f; } - let _e391 = ndc[0u]; - let _e395 = ndc[1u]; - uv_3 = vec2(((_e391 * 0.5f) + 0.5f), (0.5f - (_e395 * 0.5f))); - let _e399 = face; - let _e401 = slot; - tile_3 = vec2(f32(_e399), f32(_e401)); - let _e404 = distance_; - let _e407 = point_shadow.params[1u]; - receiver_2 = (_e404 - _e407); - let _e411 = frag_info.target_origin[0u]; - param_22 = _e411; - let _e412 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); - noise = fract((52.982918f * fract(dot(_e412, vec2(0.06711056f, 0.00583715f))))); - let _e417 = noise; - angle = (_e417 * 6.2831855f); - let _e419 = angle; - ca_2 = cos(_e419); - let _e421 = angle; - sa_2 = sin(_e421); - let _e423 = (*lightIndex); - let _e427 = point_shadow.slots[_e423][2u]; - tanHalf_1 = max(_e427, 0.0001f); + let _e392 = ndc[0u]; + let _e396 = ndc[1u]; + uv_3 = vec2(((_e392 * 0.5f) + 0.5f), (0.5f - (_e396 * 0.5f))); + let _e400 = face; + let _e402 = slot; + tile_3 = vec2(f32(_e400), f32(_e402)); + let _e405 = distance_; + let _e408 = point_shadow.params[1u]; + receiver_2 = (_e405 - _e408); + let _e412 = frag_info.target_origin[0u]; + param_22 = _e412; + let _e413 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); + noise = fract((52.982918f * fract(dot(_e413, vec2(0.06711056f, 0.00583715f))))); + let _e418 = noise; + angle = (_e418 * 6.2831855f); + let _e420 = angle; + ca_2 = cos(_e420); + let _e422 = angle; + sa_2 = sin(_e422); + let _e424 = (*lightIndex); + let _e428 = point_shadow.slots[_e424][2u]; + tanHalf_1 = max(_e428, 0.0001f); blocker_1 = -1f; - let _e429 = uv_3; - param_23 = _e429; - let _e430 = tile_3; - param_24 = _e430; - let _e431 = range_3; - param_25 = _e431; - let _e432 = receiver_2; - param_26 = _e432; - let _e433 = ca_2; - param_27 = _e433; - let _e434 = sa_2; - param_28 = _e434; - let _e435 = tanHalf_1; - param_29 = _e435; - let _e436 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e437 = param_30; - blocker_1 = _e437; - radius_1 = _e436; - let _e440 = point_shadow.params2_[3u]; - if (_e440 > 0.5f) { + let _e430 = uv_3; + param_23 = _e430; + let _e431 = tile_3; + param_24 = _e431; + let _e432 = range_3; + param_25 = _e432; + let _e433 = receiver_2; + param_26 = _e433; + let _e434 = ca_2; + param_27 = _e434; + let _e435 = sa_2; + param_28 = _e435; + let _e436 = tanHalf_1; + param_29 = _e436; + let _e437 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); + let _e438 = param_30; + blocker_1 = _e438; + radius_1 = _e437; + let _e441 = point_shadow.params2_[3u]; + if (_e441 > 0.5f) { g_debug_surface_on = true; - let _e442 = radius_1; - if (_e442 < 0f) { + let _e443 = radius_1; + if (_e443 < 0f) { local_3 = vec3(0f, 0f, 1f); } else { - let _e444 = radius_1; - let _e447 = point_shadow.params2_[2u]; - let _e451 = blocker_1; - let _e452 = range_3; - local_3 = vec3(clamp((_e444 / max(_e447, 0.000001f)), 0f, 1f), clamp((_e451 / _e452), 0f, 1f), 0f); + let _e445 = radius_1; + let _e448 = point_shadow.params2_[2u]; + let _e452 = blocker_1; + let _e453 = range_3; + local_3 = vec3(clamp((_e445 / max(_e448, 0.000001f)), 0f, 1f), clamp((_e452 / _e453), 0f, 1f), 0f); } - let _e456 = local_3; - g_debug_surface = _e456; + let _e457 = local_3; + g_debug_surface = _e457; } - let _e457 = radius_1; - if (_e457 < 0f) { + let _e458 = radius_1; + if (_e458 < 0f) { return 1f; } - let _e459 = uv_3; - param_31 = _e459; + let _e460 = uv_3; + param_31 = _e460; param_32 = vec2(0f, 0f); - let _e460 = tile_3; - param_33 = _e460; - let _e461 = range_3; - param_34 = _e461; - let _e462 = receiver_2; - param_35 = _e462; - let _e463 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); - lit = _e463; - let _e464 = radius_1; - if (_e464 > 0f) { + let _e461 = tile_3; + param_33 = _e461; + let _e462 = range_3; + param_34 = _e462; + let _e463 = receiver_2; + param_35 = _e463; + let _e464 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); + lit = _e464; + let _e465 = radius_1; + if (_e465 > 0f) { i_3 = 0i; loop { - let _e466 = i_3; - if (_e466 < 8i) { - let _e468 = i_3; - param_36 = _e468; - let _e469 = ca_2; - param_37 = _e469; - let _e470 = sa_2; - param_38 = _e470; - let _e471 = radius_1; - param_39 = _e471; - let _e472 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - let _e473 = uv_3; - param_40 = _e473; - param_41 = _e472; - let _e474 = tile_3; - param_42 = _e474; - let _e475 = range_3; - param_43 = _e475; - let _e476 = receiver_2; - param_44 = _e476; - let _e477 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); - let _e478 = lit; - lit = (_e478 + _e477); + let _e467 = i_3; + if (_e467 < 8i) { + let _e469 = i_3; + param_36 = _e469; + let _e470 = ca_2; + param_37 = _e470; + let _e471 = sa_2; + param_38 = _e471; + let _e472 = radius_1; + param_39 = _e472; + let _e473 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); + let _e474 = uv_3; + param_40 = _e474; + param_41 = _e473; + let _e475 = tile_3; + param_42 = _e475; + let _e476 = range_3; + param_43 = _e476; + let _e477 = receiver_2; + param_44 = _e477; + let _e478 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); + let _e479 = lit; + lit = (_e479 + _e478); continue; } else { break; } continuing { - let _e480 = i_3; - i_3 = (_e480 + 1i); + let _e481 = i_3; + i_3 = (_e481 + 1i); } } - let _e482 = lit; - lit = (_e482 * 0.11111111f); + let _e483 = lit; + lit = (_e483 * 0.11111111f); } - let _e484 = lit; - let _e485 = strength; - return mix(1f, _e484, clamp(_e485, 0f, 1f)); + let _e485 = lit; + let _e486 = strength; + return mix(1f, _e485, clamp(_e486, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r: f32; var theta: f32; - let _e182 = (*i_4); - let _e185 = (*n_1); - r = sqrt(((f32(_e182) + 0.5f) / f32(_e185))); - let _e189 = (*i_4); - let _e192 = (*turn); - theta = ((f32(_e189) * 2.3999631f) + _e192); - let _e194 = r; - let _e195 = theta; - let _e197 = theta; - return (vec2(cos(_e195), sin(_e197)) * _e194); + let _e183 = (*i_4); + let _e186 = (*n_1); + r = sqrt(((f32(_e183) + 0.5f) / f32(_e186))); + let _e190 = (*i_4); + let _e193 = (*turn); + theta = ((f32(_e190) * 2.3999631f) + _e193); + let _e195 = r; + let _e196 = theta; + let _e198 = theta; + return (vec2(cos(_e196), sin(_e198)) * _e195); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; var param_45: f32; - let _e181 = frag_info.target_origin[0u]; - param_45 = _e181; - let _e182 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); - let _e185 = frag_info.target_origin[3u]; - at = (_e182 + vec2((5.588238f * max(_e185, 0f)))); - let _e190 = at; - return fract((52.982918f * fract(dot(_e190, vec2(0.06711056f, 0.00583715f))))); + let _e182 = frag_info.target_origin[0u]; + param_45 = _e182; + let _e183 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); + let _e186 = frag_info.target_origin[3u]; + at = (_e183 + vec2((5.588238f * max(_e186, 0f)))); + let _e191 = at; + return fract((52.982918f * fract(dot(_e191, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -21922,85 +22011,85 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { - var d_2: f32; - - let _e179 = (*depth); - d_2 = ((2f * clamp(_e179, 0f, 1f)) - 1f); - let _e183 = d_2; - let _e186 = d_2; - return vec2(exp((40f * _e183)), -(exp((-5f * _e186)))); + let _e203 = d_1; + pMax = (_e200 / (_e201 + (_e202 * _e203))); + let _e207 = pMax; + let _e208 = (*bleed); + let _e210 = (*bleed); + reduced = clamp(((_e207 - _e208) / max((1f - _e210), 0.0001f)), 0f, 1f); + let _e215 = (*t); + let _e217 = (*moments)[0u]; + let _e219 = reduced; + return select(_e219, 1f, (_e215 <= _e217)); } - -fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { - var warped: vec2; - var param_46: f32; - var scale: vec2; - var positive: f32; - var param_47: vec2; - var param_48: f32; - var param_49: f32; - var param_50: f32; - var negative: f32; - var param_51: vec2; - var param_52: f32; - var param_53: f32; - var param_54: f32; - - let _e193 = (*depth_1); - param_46 = _e193; - let _e194 = EvsmWarp_u0028_f1_u003b((¶m_46)); - warped = _e194; - let _e195 = warped; - scale = (vec2(0.004f, 0.0005f) * _e195); - let _e198 = scale[0u]; - let _e200 = scale[0u]; - let _e202 = (*moments_1); - param_47 = _e202.xy; - let _e205 = warped[0u]; - param_48 = _e205; - param_49 = (_e198 * _e200); - let _e206 = (*bleed_1); - param_50 = _e206; - let _e207 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); - positive = _e207; - let _e209 = scale[1u]; - let _e211 = scale[1u]; - let _e213 = (*moments_1); - param_51 = _e213.zw; - let _e216 = warped[1u]; - param_52 = _e216; - param_53 = (_e209 * _e211); - let _e217 = (*bleed_1); - param_54 = _e217; - let _e218 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); - negative = _e218; - let _e219 = positive; - let _e220 = negative; - return min(_e219, _e220); + +fn EvsmWarp_u0028_f1_u003b(depth: ptr) -> vec2 { + var d_2: f32; + + let _e180 = (*depth); + d_2 = ((2f * clamp(_e180, 0f, 1f)) - 1f); + let _e184 = d_2; + let _e187 = d_2; + return vec2(exp((40f * _e184)), -(exp((-5f * _e187)))); +} + +fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { + var warped: vec2; + var param_46: f32; + var scale: vec2; + var positive: f32; + var param_47: vec2; + var param_48: f32; + var param_49: f32; + var param_50: f32; + var negative: f32; + var param_51: vec2; + var param_52: f32; + var param_53: f32; + var param_54: f32; + + let _e194 = (*depth_1); + param_46 = _e194; + let _e195 = EvsmWarp_u0028_f1_u003b((¶m_46)); + warped = _e195; + let _e196 = warped; + scale = (vec2(0.004f, 0.0005f) * _e196); + let _e199 = scale[0u]; + let _e201 = scale[0u]; + let _e203 = (*moments_1); + param_47 = _e203.xy; + let _e206 = warped[0u]; + param_48 = _e206; + param_49 = (_e199 * _e201); + let _e207 = (*bleed_1); + param_50 = _e207; + let _e208 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); + positive = _e208; + let _e210 = scale[1u]; + let _e212 = scale[1u]; + let _e214 = (*moments_1); + param_51 = _e214.zw; + let _e217 = warped[1u]; + param_52 = _e217; + param_53 = (_e210 * _e212); + let _e218 = (*bleed_1); + param_54 = _e218; + let _e219 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); + negative = _e219; + let _e220 = positive; + let _e221 = negative; + return min(_e220, _e221); } fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_1: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { @@ -22037,6 +22126,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; + var stored_2: vec4; + var through: vec3; var softness: f32; var lit_1: f32; var moments_2: vec4; @@ -22067,51 +22158,51 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_63: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_3269_: bool; - var phi_3280_: bool; - var phi_3441_: bool; - var phi_3448_: bool; - var phi_3455_: bool; - - let _e245 = frag_info.shadow_params[3u]; - strength_1 = _e245; - let _e246 = strength_1; - if (_e246 <= 0f) { + var phi_3272_: bool; + var phi_3283_: bool; + var phi_3444_: bool; + var phi_3451_: bool; + var phi_3458_: bool; + + let _e248 = frag_info.shadow_params[3u]; + strength_1 = _e248; + let _e249 = strength_1; + if (_e249 <= 0f) { return 1f; } - let _e248 = (*lightIndex_1); - let _e251 = frag_info.frame_params[2u]; - if (_e248 != i32((_e251 + 0.5f))) { + let _e251 = (*lightIndex_1); + let _e254 = frag_info.frame_params[2u]; + if (_e251 != i32((_e254 + 0.5f))) { return 1f; } - let _e257 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e257 + 0.5f)); - let _e260 = v_world_position_1; - let _e262 = frag_info.camera_position; - viewDistance = length((_e260 - _e262.xyz)); + let _e260 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e260 + 0.5f)); + let _e263 = v_world_position_1; + let _e265 = frag_info.camera_position; + viewDistance = length((_e263 - _e265.xyz)); cascade = 0i; - let _e266 = cascadeCount; - let _e267 = (_e266 > 1i); - phi_3269_ = _e267; - if _e267 { - let _e268 = viewDistance; - let _e271 = frag_info.shadow_cascades[0u]; - phi_3269_ = (_e268 > _e271); - } - let _e274 = phi_3269_; - if _e274 { + let _e269 = cascadeCount; + let _e270 = (_e269 > 1i); + phi_3272_ = _e270; + if _e270 { + let _e271 = viewDistance; + let _e274 = frag_info.shadow_cascades[0u]; + phi_3272_ = (_e271 > _e274); + } + let _e277 = phi_3272_; + if _e277 { cascade = 1i; } - let _e275 = cascadeCount; - let _e276 = (_e275 > 2i); - phi_3280_ = _e276; - if _e276 { - let _e277 = viewDistance; - let _e280 = frag_info.shadow_cascades[1u]; - phi_3280_ = (_e277 > _e280); + let _e278 = cascadeCount; + let _e279 = (_e278 > 2i); + phi_3283_ = _e279; + if _e279 { + let _e280 = viewDistance; + let _e283 = frag_info.shadow_cascades[1u]; + phi_3283_ = (_e280 > _e283); } - let _e283 = phi_3280_; - if _e283 { + let _e286 = phi_3283_; + if _e286 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -22122,232 +22213,247 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e284 = attempt; - if (_e284 < 3i) { - let _e286 = cascade; - let _e287 = attempt; - which = (_e286 + _e287); - let _e289 = which; - let _e290 = cascadeCount; - if (_e289 >= _e290) { + let _e287 = attempt; + if (_e287 < 3i) { + let _e289 = cascade; + let _e290 = attempt; + which = (_e289 + _e290); + let _e292 = which; + let _e293 = cascadeCount; + if (_e292 >= _e293) { break; } - let _e292 = which; - if (_e292 == 0i) { - let _e295 = frag_info.shadow_matrix; - local_4 = _e295; + let _e295 = which; + if (_e295 == 0i) { + let _e298 = frag_info.shadow_matrix; + local_4 = _e298; } else { - let _e296 = which; - if (_e296 == 1i) { - let _e299 = frag_info.shadow_matrix_far; - local_5 = _e299; + let _e299 = which; + if (_e299 == 1i) { + let _e302 = frag_info.shadow_matrix_far; + local_5 = _e302; } else { - let _e301 = frag_info.shadow_matrix_farthest; - local_5 = _e301; + let _e304 = frag_info.shadow_matrix_farthest; + local_5 = _e304; } - let _e302 = local_5; - local_4 = _e302; - } - let _e303 = local_4; - matrix = _e303; - let _e306 = matrix[0][0u]; - let _e309 = matrix[1][0u]; - let _e312 = matrix[2][0u]; - axisX = vec3(_e306, _e309, _e312); - let _e316 = matrix[0][1u]; - let _e319 = matrix[1][1u]; - let _e322 = matrix[2][1u]; - axisY = vec3(_e316, _e319, _e322); - let _e324 = axisX; - rowX = max(length(_e324), 0.000001f); - let _e327 = axisY; - rowY = max(length(_e327), 0.000001f); - let _e332 = frag_info.shadow_cascades[3u]; - let _e334 = rowX; - texelMetres = ((2f * _e332) / _e334); - let _e338 = frag_info.shadow_cascades[3u]; - let _e341 = (*s_1).n; - let _e342 = axisX; - let _e345 = rowX; - let _e346 = rowX; - let _e350 = (*s_1).n; - let _e351 = axisY; - let _e354 = rowY; - let _e355 = rowY; - reach = ((3f * _e338) * ((abs(dot(_e341, _e342)) / (_e345 * _e346)) + (abs(dot(_e350, _e351)) / (_e354 * _e355)))); - let _e362 = frag_info.shadow_params[2u]; - let _e363 = texelMetres; - flatOffset = min(_e362, _e363); - let _e365 = v_world_position_1; - let _e367 = (*s_1).n; - let _e368 = flatOffset; - let _e369 = reach; - origin_1 = (_e365 + (_e367 * (_e368 + _e369))); - let _e373 = matrix; - let _e374 = origin_1; - lightSpace = (_e373 * vec4(_e374.x, _e374.y, _e374.z, 1f)); - let _e381 = lightSpace[3u]; - if (_e381 <= 0f) { + let _e305 = local_5; + local_4 = _e305; + } + let _e306 = local_4; + matrix = _e306; + let _e309 = matrix[0][0u]; + let _e312 = matrix[1][0u]; + let _e315 = matrix[2][0u]; + axisX = vec3(_e309, _e312, _e315); + let _e319 = matrix[0][1u]; + let _e322 = matrix[1][1u]; + let _e325 = matrix[2][1u]; + axisY = vec3(_e319, _e322, _e325); + let _e327 = axisX; + rowX = max(length(_e327), 0.000001f); + let _e330 = axisY; + rowY = max(length(_e330), 0.000001f); + let _e335 = frag_info.shadow_cascades[3u]; + let _e337 = rowX; + texelMetres = ((2f * _e335) / _e337); + let _e341 = frag_info.shadow_cascades[3u]; + let _e344 = (*s_1).n; + let _e345 = axisX; + let _e348 = rowX; + let _e349 = rowX; + let _e353 = (*s_1).n; + let _e354 = axisY; + let _e357 = rowY; + let _e358 = rowY; + reach = ((3f * _e341) * ((abs(dot(_e344, _e345)) / (_e348 * _e349)) + (abs(dot(_e353, _e354)) / (_e357 * _e358)))); + let _e365 = frag_info.shadow_params[2u]; + let _e366 = texelMetres; + flatOffset = min(_e365, _e366); + let _e368 = v_world_position_1; + let _e370 = (*s_1).n; + let _e371 = flatOffset; + let _e372 = reach; + origin_1 = (_e368 + (_e370 * (_e371 + _e372))); + let _e376 = matrix; + let _e377 = origin_1; + lightSpace = (_e376 * vec4(_e377.x, _e377.y, _e377.z, 1f)); + let _e384 = lightSpace[3u]; + if (_e384 <= 0f) { continue; } - let _e383 = lightSpace; - let _e386 = lightSpace[3u]; - candidate = (_e383.xyz / vec3(_e386)); - let _e390 = candidate[0u]; - let _e394 = candidate[1u]; - inTile = vec2(((_e390 * 0.5f) + 0.5f), (0.5f - (_e394 * 0.5f))); - let _e399 = inTile[0u]; - let _e400 = (_e399 < 0f); - phi_3441_ = _e400; - if !(_e400) { - let _e403 = inTile[0u]; - phi_3441_ = (_e403 > 1f); - } - let _e406 = phi_3441_; - phi_3448_ = _e406; - if !(_e406) { - let _e409 = inTile[1u]; - phi_3448_ = (_e409 < 0f); - } - let _e412 = phi_3448_; - phi_3455_ = _e412; - if !(_e412) { - let _e415 = inTile[1u]; - phi_3455_ = (_e415 > 1f); - } - let _e418 = phi_3455_; - if _e418 { + let _e386 = lightSpace; + let _e389 = lightSpace[3u]; + candidate = (_e386.xyz / vec3(_e389)); + let _e393 = candidate[0u]; + let _e397 = candidate[1u]; + inTile = vec2(((_e393 * 0.5f) + 0.5f), (0.5f - (_e397 * 0.5f))); + let _e402 = inTile[0u]; + let _e403 = (_e402 < 0f); + phi_3444_ = _e403; + if !(_e403) { + let _e406 = inTile[0u]; + phi_3444_ = (_e406 > 1f); + } + let _e409 = phi_3444_; + phi_3451_ = _e409; + if !(_e409) { + let _e412 = inTile[1u]; + phi_3451_ = (_e412 < 0f); + } + let _e415 = phi_3451_; + phi_3458_ = _e415; + if !(_e415) { + let _e418 = inTile[1u]; + phi_3458_ = (_e418 > 1f); + } + let _e421 = phi_3458_; + if _e421 { continue; } - let _e420 = candidate[2u]; - if (_e420 > 1f) { - let _e422 = which; - let _e423 = cascadeCount; - if (_e422 < (_e423 - 1i)) { + let _e423 = candidate[2u]; + if (_e423 > 1f) { + let _e425 = which; + let _e426 = cascadeCount; + if (_e425 < (_e426 - 1i)) { continue; } candidate[2u] = 1f; } - let _e428 = inTile[0u]; - let _e429 = which; - let _e432 = cascadeCount; - let _e436 = inTile[1u]; - uv_4 = vec2(((_e428 + f32(_e429)) / f32(_e432)), _e436); - let _e438 = candidate; - projected = _e438; - let _e439 = which; - cascade = _e439; - let _e440 = texelMetres; - cascadeTexel = _e440; - let _e443 = matrix[0][2u]; - let _e446 = matrix[1][2u]; - let _e449 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e443, _e446, _e449)), 0.000001f)); - let _e455 = (*s_1).n; - let _e458 = matrix[0][2u]; - let _e461 = matrix[1][2u]; - let _e464 = matrix[2][2u]; - let _e468 = cascadeDepth; - cosSlope = clamp((abs(dot(_e455, vec3(_e458, _e461, _e464))) * _e468), 0.1f, 1f); - let _e471 = texelMetres; - let _e472 = cosSlope; - let _e473 = cosSlope; - let _e479 = cosSlope; - let _e481 = cascadeDepth; - receiverSlope = (((_e471 * sqrt(max((1f - (_e472 * _e473)), 0f))) / _e479) / _e481); + let _e431 = inTile[0u]; + let _e432 = which; + let _e435 = cascadeCount; + let _e439 = inTile[1u]; + uv_4 = vec2(((_e431 + f32(_e432)) / f32(_e435)), _e439); + let _e441 = candidate; + projected = _e441; + let _e442 = which; + cascade = _e442; + let _e443 = texelMetres; + cascadeTexel = _e443; + let _e446 = matrix[0][2u]; + let _e449 = matrix[1][2u]; + let _e452 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e446, _e449, _e452)), 0.000001f)); + let _e458 = (*s_1).n; + let _e461 = matrix[0][2u]; + let _e464 = matrix[1][2u]; + let _e467 = matrix[2][2u]; + let _e471 = cascadeDepth; + cosSlope = clamp((abs(dot(_e458, vec3(_e461, _e464, _e467))) * _e471), 0.1f, 1f); + let _e474 = texelMetres; + let _e475 = cosSlope; + let _e476 = cosSlope; + let _e482 = cosSlope; + let _e484 = cascadeDepth; + receiverSlope = (((_e474 * sqrt(max((1f - (_e475 * _e476)), 0f))) / _e482) / _e484); found = true; break; } else { break; } continuing { - let _e483 = attempt; - attempt = (_e483 + 1i); + let _e486 = attempt; + attempt = (_e486 + 1i); } } - let _e485 = found; - if !(_e485) { + let _e488 = found; + if !(_e488) { return 1f; } - let _e487 = cascade; - if (_e487 == 0i) { - let _e491 = frag_info.shadow_bias[0u]; - local_6 = _e491; + let _e490 = cascade; + if (_e490 == 0i) { + let _e494 = frag_info.shadow_bias[0u]; + local_6 = _e494; } else { - let _e492 = cascade; - if (_e492 == 1i) { - let _e496 = frag_info.shadow_bias[1u]; - local_7 = _e496; - } else { - let _e499 = frag_info.shadow_bias[2u]; + let _e495 = cascade; + if (_e495 == 1i) { + let _e499 = frag_info.shadow_bias[1u]; local_7 = _e499; - } - let _e500 = local_7; - local_6 = _e500; - } - let _e501 = local_6; - bias = _e501; - let _e504 = frag_info.shadow_params[0u]; - let _e507 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e504, _e507); - let _e509 = cascade; - let _e511 = cascadeCount; - let _e515 = texel_1; - tileLo = (vec2((f32(_e509) / f32(_e511)), 0f) + (_e515 * 0.5f)); - let _e518 = cascade; - let _e521 = cascadeCount; - let _e525 = texel_1; - tileHi = (vec2((f32((_e518 + 1i)) / f32(_e521)), 1f) - (_e525 * 0.5f)); - let _e530 = frag_info.ambient_ground[3u]; - softness = _e530; + } else { + let _e502 = frag_info.shadow_bias[2u]; + local_7 = _e502; + } + let _e503 = local_7; + local_6 = _e503; + } + let _e504 = local_6; + bias = _e504; + let _e507 = frag_info.shadow_params[0u]; + let _e510 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e507, _e510); + let _e512 = cascade; + let _e514 = cascadeCount; + let _e518 = texel_1; + tileLo = (vec2((f32(_e512) / f32(_e514)), 0f) + (_e518 * 0.5f)); + let _e521 = cascade; + let _e524 = cascadeCount; + let _e528 = texel_1; + tileHi = (vec2((f32((_e521 + 1i)) / f32(_e524)), 1f) - (_e528 * 0.5f)); + let _e533 = frag_info.shadow_bias[3u]; + if (_e533 > 0.5f) { + let _e535 = uv_4; + let _e536 = tileLo; + let _e537 = tileHi; + let _e539 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e535, _e536, _e537), 0f); + stored_2 = _e539; + let _e541 = stored_2[1u]; + let _e544 = stored_2[2u]; + let _e547 = stored_2[3u]; + through = clamp(vec3((1f - _e541), (1f - _e544), _e547), vec3(0f), vec3(1f)); + let _e552 = through; + let _e553 = strength_1; + light_transmittance = mix(vec3(1f, 1f, 1f), _e552, vec3(clamp(_e553, 0f, 1f))); + } + let _e559 = frag_info.ambient_ground[3u]; + softness = _e559; lit_1 = 0f; - let _e531 = softness; - if (_e531 < 0f) { - let _e533 = uv_4; - let _e534 = tileLo; - let _e535 = tileHi; - let _e537 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e533, _e534, _e535), 0f); - moments_2 = _e537; - let _e539 = projected[2u]; - let _e540 = bias; - let _e542 = softness; - let _e546 = moments_2; - param_55 = _e546; - param_56 = (_e539 - _e540); - param_57 = clamp((-(_e542) - 1f), 0f, 0.95f); - let _e547 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); - lit_1 = _e547; + let _e560 = softness; + if (_e560 < 0f) { + let _e562 = uv_4; + let _e563 = tileLo; + let _e564 = tileHi; + let _e566 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e562, _e563, _e564), 0f); + moments_2 = _e566; + let _e568 = projected[2u]; + let _e569 = bias; + let _e571 = softness; + let _e575 = moments_2; + param_55 = _e575; + param_56 = (_e568 - _e569); + param_57 = clamp((-(_e571) - 1f), 0f, 0.95f); + let _e576 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); + lit_1 = _e576; } else { - let _e548 = softness; - if (_e548 <= 0f) { + let _e577 = softness; + if (_e577 <= 0f) { y = -1i; loop { - let _e550 = y; - if (_e550 <= 1i) { + let _e579 = y; + if (_e579 <= 1i) { x = -1i; loop { - let _e552 = x; - if (_e552 <= 1i) { - let _e554 = uv_4; - let _e555 = x; - let _e557 = y; - let _e560 = texel_1; - let _e563 = tileLo; - let _e564 = tileHi; - let _e566 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e554 + (vec2(f32(_e555), f32(_e557)) * _e560)), _e563, _e564), 0f); - occluder = _e566.x; - let _e569 = projected[2u]; - let _e570 = bias; - let _e572 = occluder; - let _e575 = lit_1; - lit_1 = (_e575 + select(1f, 0f, ((_e569 - _e570) > _e572))); + let _e581 = x; + if (_e581 <= 1i) { + let _e583 = uv_4; + let _e584 = x; + let _e586 = y; + let _e589 = texel_1; + let _e592 = tileLo; + let _e593 = tileHi; + let _e595 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e583 + (vec2(f32(_e584), f32(_e586)) * _e589)), _e592, _e593), 0f); + occluder = _e595.x; + let _e598 = projected[2u]; + let _e599 = bias; + let _e601 = occluder; + let _e604 = lit_1; + lit_1 = (_e604 + select(1f, 0f, ((_e598 - _e599) > _e601))); continue; } else { break; } continuing { - let _e577 = x; - x = (_e577 + 1i); + let _e606 = x; + x = (_e606 + 1i); } } continue; @@ -22355,125 +22461,125 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e579 = y; - y = (_e579 + 1i); + let _e608 = y; + y = (_e608 + 1i); } } - let _e581 = lit_1; - lit_1 = (_e581 * 0.11111111f); + let _e610 = lit_1; + lit_1 = (_e610 * 0.11111111f); } else { - let _e583 = softness; - spread = tan(min(_e583, 0.5f)); - let _e586 = ShadowNoise_u0028_(); - turn_1 = (_e586 * 6.2831855f); - let _e588 = spread; - let _e590 = projected[2u]; - let _e592 = cascadeDepth; - let _e594 = cascadeTexel; - searchRadius = clamp((((_e588 * _e590) * _e592) / _e594), 1f, 16f); + let _e612 = softness; + spread = tan(min(_e612, 0.5f)); + let _e615 = ShadowNoise_u0028_(); + turn_1 = (_e615 * 6.2831855f); + let _e617 = spread; + let _e619 = projected[2u]; + let _e621 = cascadeDepth; + let _e623 = cascadeTexel; + searchRadius = clamp((((_e617 * _e619) * _e621) / _e623), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e597 = i_5; - if (_e597 < 16i) { - let _e599 = i_5; - param_58 = _e599; + let _e626 = i_5; + if (_e626 < 16i) { + let _e628 = i_5; + param_58 = _e628; param_59 = 16i; - let _e600 = turn_1; - param_60 = _e600; - let _e601 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); - disc = _e601; - let _e602 = bias; - let _e603 = disc; - let _e605 = searchRadius; - let _e609 = receiverSlope; - tapBias = (_e602 + (max(((length(_e603) * _e605) - 1.5f), 0f) * _e609)); - let _e612 = uv_4; - let _e613 = disc; - let _e614 = texel_1; - let _e616 = searchRadius; - let _e619 = tileLo; - let _e620 = tileHi; - let _e622 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e612 + ((_e613 * _e614) * _e616)), _e619, _e620), 0f); - occluder_1 = _e622.x; - let _e625 = projected[2u]; - let _e626 = tapBias; - let _e628 = occluder_1; - if ((_e625 - _e626) > _e628) { - let _e630 = occluder_1; - let _e631 = blockerSum; - blockerSum = (_e631 + _e630); - let _e633 = blockerCount; - blockerCount = (_e633 + 1f); + let _e629 = turn_1; + param_60 = _e629; + let _e630 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); + disc = _e630; + let _e631 = bias; + let _e632 = disc; + let _e634 = searchRadius; + let _e638 = receiverSlope; + tapBias = (_e631 + (max(((length(_e632) * _e634) - 1.5f), 0f) * _e638)); + let _e641 = uv_4; + let _e642 = disc; + let _e643 = texel_1; + let _e645 = searchRadius; + let _e648 = tileLo; + let _e649 = tileHi; + let _e651 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e641 + ((_e642 * _e643) * _e645)), _e648, _e649), 0f); + occluder_1 = _e651.x; + let _e654 = projected[2u]; + let _e655 = tapBias; + let _e657 = occluder_1; + if ((_e654 - _e655) > _e657) { + let _e659 = occluder_1; + let _e660 = blockerSum; + blockerSum = (_e660 + _e659); + let _e662 = blockerCount; + blockerCount = (_e662 + 1f); } continue; } else { break; } continuing { - let _e635 = i_5; - i_5 = (_e635 + 1i); + let _e664 = i_5; + i_5 = (_e664 + 1i); } } - let _e637 = blockerCount; - if (_e637 <= 0f) { + let _e666 = blockerCount; + if (_e666 <= 0f) { return 1f; } - let _e640 = projected[2u]; - let _e641 = blockerSum; - let _e642 = blockerCount; - let _e646 = cascadeDepth; - gap = (max((_e640 - (_e641 / _e642)), 0f) * _e646); - let _e648 = spread; - let _e649 = gap; - let _e651 = cascadeTexel; - radius_2 = clamp(((_e648 * _e649) / _e651), 1f, 16f); + let _e669 = projected[2u]; + let _e670 = blockerSum; + let _e671 = blockerCount; + let _e675 = cascadeDepth; + gap = (max((_e669 - (_e670 / _e671)), 0f) * _e675); + let _e677 = spread; + let _e678 = gap; + let _e680 = cascadeTexel; + radius_2 = clamp(((_e677 * _e678) / _e680), 1f, 16f); i_6 = 0i; loop { - let _e654 = i_6; - if (_e654 < 16i) { - let _e656 = turn_1; - let _e658 = i_6; - param_61 = _e658; + let _e683 = i_6; + if (_e683 < 16i) { + let _e685 = turn_1; + let _e687 = i_6; + param_61 = _e687; param_62 = 16i; - param_63 = (_e656 + 1f); - let _e659 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - disc_1 = _e659; - let _e660 = bias; - let _e661 = disc_1; - let _e663 = radius_2; - let _e667 = receiverSlope; - tapBias_1 = (_e660 + (max(((length(_e661) * _e663) - 1.5f), 0f) * _e667)); - let _e670 = uv_4; - let _e671 = disc_1; - let _e672 = texel_1; - let _e674 = radius_2; - let _e677 = tileLo; - let _e678 = tileHi; - let _e680 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e670 + ((_e671 * _e672) * _e674)), _e677, _e678), 0f); - occluder_2 = _e680.x; - let _e683 = projected[2u]; - let _e684 = tapBias_1; - let _e686 = occluder_2; - let _e689 = lit_1; - lit_1 = (_e689 + select(1f, 0f, ((_e683 - _e684) > _e686))); + param_63 = (_e685 + 1f); + let _e688 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); + disc_1 = _e688; + let _e689 = bias; + let _e690 = disc_1; + let _e692 = radius_2; + let _e696 = receiverSlope; + tapBias_1 = (_e689 + (max(((length(_e690) * _e692) - 1.5f), 0f) * _e696)); + let _e699 = uv_4; + let _e700 = disc_1; + let _e701 = texel_1; + let _e703 = radius_2; + let _e706 = tileLo; + let _e707 = tileHi; + let _e709 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e699 + ((_e700 * _e701) * _e703)), _e706, _e707), 0f); + occluder_2 = _e709.x; + let _e712 = projected[2u]; + let _e713 = tapBias_1; + let _e715 = occluder_2; + let _e718 = lit_1; + lit_1 = (_e718 + select(1f, 0f, ((_e712 - _e713) > _e715))); continue; } else { break; } continuing { - let _e691 = i_6; - i_6 = (_e691 + 1i); + let _e720 = i_6; + i_6 = (_e720 + 1i); } } - let _e693 = lit_1; - lit_1 = (_e693 * 0.0625f); + let _e722 = lit_1; + lit_1 = (_e722 * 0.0625f); } } - let _e695 = lit_1; - let _e696 = strength_1; - return mix(1f, _e695, clamp(_e696, 0f, 1f)); + let _e724 = lit_1; + let _e725 = strength_1; + return mix(1f, _e724, clamp(_e725, 0f, 1f)); } fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_2: ptr, index: ptr) -> f32 { @@ -22481,19 +22587,19 @@ fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_65: LightSample; var param_66: i32; - let _e183 = (*s_2); - param_64 = _e183; - let _e184 = (*light_2); - param_65 = _e184; - let _e185 = (*index); - param_66 = _e185; - let _e186 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); - return _e186; + let _e184 = (*s_2); + param_64 = _e184; + let _e185 = (*light_2); + param_65 = _e185; + let _e186 = (*index); + param_66 = _e186; + let _e187 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); + return _e187; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e178 = (*index_1); - return (_e178 < 8i); + let _e179 = (*index_1); + return (_e179 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -22501,26 +22607,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e182 = (*distance_1); let _e183 = (*distance_1); - attenuation = (1f / max((_e182 * _e183), 0.0001f)); - let _e187 = (*range_4); - if (_e187 > 0f) { - let _e189 = (*distance_1); - let _e190 = (*range_4); - ratio = (_e189 / _e190); - let _e192 = ratio; + let _e184 = (*distance_1); + attenuation = (1f / max((_e183 * _e184), 0.0001f)); + let _e188 = (*range_4); + if (_e188 > 0f) { + let _e190 = (*distance_1); + let _e191 = (*range_4); + ratio = (_e190 / _e191); let _e193 = ratio; - let _e195 = ratio; - let _e197 = ratio; - window = clamp((1f - (((_e192 * _e193) * _e195) * _e197)), 0f, 1f); - let _e201 = window; + let _e194 = ratio; + let _e196 = ratio; + let _e198 = ratio; + window = clamp((1f - (((_e193 * _e194) * _e196) * _e198)), 0f, 1f); let _e202 = window; - let _e204 = attenuation; - attenuation = (_e204 * (_e201 * _e202)); + let _e203 = window; + let _e205 = attenuation; + attenuation = (_e205 * (_e202 * _e203)); } - let _e206 = attenuation; - return _e206; + let _e207 = attenuation; + return _e207; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -22537,75 +22643,75 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var u: f32; var v: f32; - let _e194 = (*halfWidth); - let _e195 = (*halfHeight); - n_2 = cross(_e194, _e195); - let _e197 = n_2; - nLen = length(_e197); - let _e199 = nLen; - if (_e199 < 0.000000000001f) { - let _e201 = (*centre); - return _e201; - } - let _e202 = nLen; - let _e203 = n_2; - n_2 = (_e203 / vec3(_e202)); - let _e206 = (*centre); - let _e207 = (*world_2); - toPlane = (_e206 - _e207); - let _e209 = (*mirror); - let _e210 = n_2; - denom = dot(_e209, _e210); - let _e212 = denom; - if (abs(_e212) < 0.00001f) { - let _e215 = toPlane; - let _e216 = n_2; - let _e217 = toPlane; - let _e218 = n_2; - onPlane = (_e215 - (_e216 * dot(_e217, _e218))); + let _e195 = (*halfWidth); + let _e196 = (*halfHeight); + n_2 = cross(_e195, _e196); + let _e198 = n_2; + nLen = length(_e198); + let _e200 = nLen; + if (_e200 < 0.000000000001f) { + let _e202 = (*centre); + return _e202; + } + let _e203 = nLen; + let _e204 = n_2; + n_2 = (_e204 / vec3(_e203)); + let _e207 = (*centre); + let _e208 = (*world_2); + toPlane = (_e207 - _e208); + let _e210 = (*mirror); + let _e211 = n_2; + denom = dot(_e210, _e211); + let _e213 = denom; + if (abs(_e213) < 0.00001f) { + let _e216 = toPlane; + let _e217 = n_2; + let _e218 = toPlane; + let _e219 = n_2; + onPlane = (_e216 - (_e217 * dot(_e218, _e219))); } else { - let _e222 = toPlane; - let _e223 = n_2; - let _e225 = denom; - t_1 = (dot(_e222, _e223) / _e225); - let _e227 = t_1; - if (_e227 > 0f) { - let _e229 = (*mirror); - let _e230 = t_1; - local_8 = (_e229 * _e230); + let _e223 = toPlane; + let _e224 = n_2; + let _e226 = denom; + t_1 = (dot(_e223, _e224) / _e226); + let _e228 = t_1; + if (_e228 > 0f) { + let _e230 = (*mirror); + let _e231 = t_1; + local_8 = (_e230 * _e231); } else { - let _e232 = toPlane; - let _e233 = n_2; - let _e234 = toPlane; - let _e235 = n_2; - local_8 = (_e232 - (_e233 * dot(_e234, _e235))); + let _e233 = toPlane; + let _e234 = n_2; + let _e235 = toPlane; + let _e236 = n_2; + local_8 = (_e233 - (_e234 * dot(_e235, _e236))); } - let _e239 = local_8; - onPlane = _e239; + let _e240 = local_8; + onPlane = _e240; } - let _e240 = onPlane; - let _e241 = toPlane; - offset_2 = (_e240 - _e241); - let _e243 = (*halfWidth); + let _e241 = onPlane; + let _e242 = toPlane; + offset_2 = (_e241 - _e242); let _e244 = (*halfWidth); - wLen2_ = max(dot(_e243, _e244), 0.000000000001f); - let _e247 = (*halfHeight); + let _e245 = (*halfWidth); + wLen2_ = max(dot(_e244, _e245), 0.000000000001f); let _e248 = (*halfHeight); - hLen2_ = max(dot(_e247, _e248), 0.000000000001f); - let _e251 = offset_2; - let _e252 = (*halfWidth); - let _e254 = wLen2_; - u = clamp((dot(_e251, _e252) / _e254), -1f, 1f); - let _e257 = offset_2; - let _e258 = (*halfHeight); - let _e260 = hLen2_; - v = clamp((dot(_e257, _e258) / _e260), -1f, 1f); - let _e263 = (*centre); - let _e264 = (*halfWidth); - let _e265 = u; - let _e268 = (*halfHeight); - let _e269 = v; - return ((_e263 + (_e264 * _e265)) + (_e268 * _e269)); + let _e249 = (*halfHeight); + hLen2_ = max(dot(_e248, _e249), 0.000000000001f); + let _e252 = offset_2; + let _e253 = (*halfWidth); + let _e255 = wLen2_; + u = clamp((dot(_e252, _e253) / _e255), -1f, 1f); + let _e258 = offset_2; + let _e259 = (*halfHeight); + let _e261 = hLen2_; + v = clamp((dot(_e258, _e259) / _e261), -1f, 1f); + let _e264 = (*centre); + let _e265 = (*halfWidth); + let _e266 = u; + let _e269 = (*halfHeight); + let _e270 = v; + return ((_e264 + (_e265 * _e266)) + (_e269 * _e270)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>, n_3: ptr>) -> f32 { @@ -22616,32 +22722,32 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var len: f32; var local_9: f32; - let _e186 = (*a_1); let _e187 = (*a_1); - ua = (_e186 / vec3(max(length(_e187), 0.000000000001f))); - let _e192 = (*b); + let _e188 = (*a_1); + ua = (_e187 / vec3(max(length(_e188), 0.000000000001f))); let _e193 = (*b); - ub = (_e192 / vec3(max(length(_e193), 0.000000000001f))); - let _e198 = ua; - let _e199 = ub; - angle_1 = acos(clamp(dot(_e198, _e199), -1f, 1f)); - let _e203 = ua; - let _e204 = ub; - axis = cross(_e203, _e204); - let _e206 = axis; - len = length(_e206); - let _e208 = len; - if (_e208 > 0.000001f) { - let _e210 = angle_1; - let _e211 = axis; - let _e212 = (*n_3); - let _e215 = len; - local_9 = ((_e210 * dot(_e211, _e212)) / _e215); + let _e194 = (*b); + ub = (_e193 / vec3(max(length(_e194), 0.000000000001f))); + let _e199 = ua; + let _e200 = ub; + angle_1 = acos(clamp(dot(_e199, _e200), -1f, 1f)); + let _e204 = ua; + let _e205 = ub; + axis = cross(_e204, _e205); + let _e207 = axis; + len = length(_e207); + let _e209 = len; + if (_e209 > 0.000001f) { + let _e211 = angle_1; + let _e212 = axis; + let _e213 = (*n_3); + let _e216 = len; + local_9 = ((_e211 * dot(_e212, _e213)) / _e216); } else { local_9 = 0f; } - let _e217 = local_9; - return _e217; + let _e218 = local_9; + return _e218; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr, 4>>, n_4: ptr>) -> f32 { @@ -22672,98 +22778,98 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr(0f, 0f, 0f); i_7 = 0i; loop { - let _e200 = i_7; - if (_e200 < 4i) { - let _e202 = i_7; - let _e204 = (*corners)[_e202]; - a_2 = _e204; - let _e205 = i_7; - if (_e205 == 3i) { + let _e201 = i_7; + if (_e201 < 4i) { + let _e203 = i_7; + let _e205 = (*corners)[_e203]; + a_2 = _e205; + let _e206 = i_7; + if (_e206 == 3i) { local_10 = 0i; } else { - let _e207 = i_7; - local_10 = (_e207 + 1i); + let _e208 = i_7; + local_10 = (_e208 + 1i); } - let _e209 = local_10; - let _e211 = (*corners)[_e209]; - b_1 = _e211; - let _e212 = a_2; - let _e213 = (*n_4); - ha = dot(_e212, _e213); - let _e215 = b_1; - let _e216 = (*n_4); - hb = dot(_e215, _e216); - let _e218 = ha; - let _e219 = hb; - d_3 = (_e218 - _e219); - let _e221 = a_2; - let _e222 = b_1; - let _e223 = a_2; - let _e225 = d_3; - if (abs(_e225) > 0.000000000001f) { - let _e228 = ha; - let _e229 = d_3; - local_11 = (_e228 / _e229); + let _e210 = local_10; + let _e212 = (*corners)[_e210]; + b_1 = _e212; + let _e213 = a_2; + let _e214 = (*n_4); + ha = dot(_e213, _e214); + let _e216 = b_1; + let _e217 = (*n_4); + hb = dot(_e216, _e217); + let _e219 = ha; + let _e220 = hb; + d_3 = (_e219 - _e220); + let _e222 = a_2; + let _e223 = b_1; + let _e224 = a_2; + let _e226 = d_3; + if (abs(_e226) > 0.000000000001f) { + let _e229 = ha; + let _e230 = d_3; + local_11 = (_e229 / _e230); } else { local_11 = 0f; } - let _e231 = local_11; - q = (_e221 + ((_e222 - _e223) * _e231)); - let _e234 = ha; - aAbove = (_e234 > 0f); - let _e236 = hb; - bAbove = (_e236 > 0f); - let _e238 = aAbove; - let _e239 = bAbove; - if (_e238 || _e239) { - let _e241 = aAbove; - let _e242 = a_2; - let _e243 = q; - let _e246 = bAbove; - let _e247 = b_1; - let _e248 = q; - param_67 = select(_e243, _e242, vec3(_e241)); - param_68 = select(_e248, _e247, vec3(_e246)); - let _e251 = (*n_4); - param_69 = _e251; - let _e252 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); - local_12 = _e252; + let _e232 = local_11; + q = (_e222 + ((_e223 - _e224) * _e232)); + let _e235 = ha; + aAbove = (_e235 > 0f); + let _e237 = hb; + bAbove = (_e237 > 0f); + let _e239 = aAbove; + let _e240 = bAbove; + if (_e239 || _e240) { + let _e242 = aAbove; + let _e243 = a_2; + let _e244 = q; + let _e247 = bAbove; + let _e248 = b_1; + let _e249 = q; + param_67 = select(_e244, _e243, vec3(_e242)); + param_68 = select(_e249, _e248, vec3(_e247)); + let _e252 = (*n_4); + param_69 = _e252; + let _e253 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); + local_12 = _e253; } else { local_12 = 0f; } - let _e253 = local_12; - let _e254 = total; - total = (_e254 + _e253); - let _e256 = aAbove; - let _e257 = bAbove; - let _e260 = q; - let _e261 = exit; - exit = select(_e261, _e260, vec3((_e256 && !(_e257)))); - let _e264 = aAbove; - let _e266 = bAbove; - let _e268 = q; - let _e269 = entry; - entry = select(_e269, _e268, vec3((!(_e264) && _e266))); + let _e254 = local_12; + let _e255 = total; + total = (_e255 + _e254); + let _e257 = aAbove; + let _e258 = bAbove; + let _e261 = q; + let _e262 = exit; + exit = select(_e262, _e261, vec3((_e257 && !(_e258)))); + let _e265 = aAbove; + let _e267 = bAbove; + let _e269 = q; + let _e270 = entry; + entry = select(_e270, _e269, vec3((!(_e265) && _e267))); continue; } else { break; } continuing { - let _e272 = i_7; - i_7 = (_e272 + 1i); + let _e273 = i_7; + i_7 = (_e273 + 1i); } } - let _e274 = exit; - param_70 = _e274; - let _e275 = entry; - param_71 = _e275; - let _e276 = (*n_4); - param_72 = _e276; - let _e277 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); - let _e278 = total; - total = (_e278 + _e277); - let _e280 = total; - return max((-(_e280) * 0.5f), 0f); + let _e275 = exit; + param_70 = _e275; + let _e276 = entry; + param_71 = _e276; + let _e277 = (*n_4); + param_72 = _e277; + let _e278 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); + let _e279 = total; + total = (_e279 + _e278); + let _e281 = total; + return max((-(_e281) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { @@ -22772,91 +22878,91 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 var local_14: f32; var local_15: f32; - let _e183 = (*slot_1); - let _e184 = (*slot_1); - lane = (_e183 - ((_e184 / 4i) * 4i)); - let _e188 = lane; - if (_e188 == 0i) { - let _e191 = (*four)[0u]; - local_13 = _e191; + let _e184 = (*slot_1); + let _e185 = (*slot_1); + lane = (_e184 - ((_e185 / 4i) * 4i)); + let _e189 = lane; + if (_e189 == 0i) { + let _e192 = (*four)[0u]; + local_13 = _e192; } else { - let _e192 = lane; - if (_e192 == 1i) { - let _e195 = (*four)[1u]; - local_14 = _e195; + let _e193 = lane; + if (_e193 == 1i) { + let _e196 = (*four)[1u]; + local_14 = _e196; } else { - let _e196 = lane; - if (_e196 == 2i) { - let _e199 = (*four)[2u]; - local_15 = _e199; + let _e197 = lane; + if (_e197 == 2i) { + let _e200 = (*four)[2u]; + local_15 = _e200; } else { - let _e201 = (*four)[3u]; - local_15 = _e201; + let _e202 = (*four)[3u]; + local_15 = _e202; } - let _e202 = local_15; - local_14 = _e202; + let _e203 = local_15; + local_14 = _e203; } - let _e203 = local_14; - local_13 = _e203; + let _e204 = local_14; + local_13 = _e204; } - let _e204 = local_13; - return _e204; + let _e205 = local_13; + return _e205; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { var param_73: vec4; var param_74: i32; - let _e180 = (*slot_2); - let _e184 = light_list_info.scales[(_e180 / 4i)]; - param_73 = _e184; - let _e185 = (*slot_2); - param_74 = _e185; - let _e186 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); - return _e186; + let _e181 = (*slot_2); + let _e185 = light_list_info.scales[(_e181 / 4i)]; + param_73 = _e185; + let _e186 = (*slot_2); + param_74 = _e186; + let _e187 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); + return _e187; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_3: vec4; var b_2: vec4; - let _e182 = light_list_info.slot_rows[0i]; - let _e183 = (*row); - a_3 = abs((_e182 - vec4(_e183))); - let _e189 = light_list_info.slot_rows[1i]; - let _e190 = (*row); - b_2 = abs((_e189 - vec4(_e190))); - let _e195 = a_3[0u]; - let _e197 = a_3[1u]; - let _e200 = a_3[2u]; - let _e202 = a_3[3u]; - let _e206 = b_2[0u]; - let _e208 = b_2[1u]; - let _e211 = b_2[2u]; - let _e213 = b_2[3u]; - return (min(min(min(_e195, _e197), min(_e200, _e202)), min(min(_e206, _e208), min(_e211, _e213))) < 0.5f); + let _e183 = light_list_info.slot_rows[0i]; + let _e184 = (*row); + a_3 = abs((_e183 - vec4(_e184))); + let _e190 = light_list_info.slot_rows[1i]; + let _e191 = (*row); + b_2 = abs((_e190 - vec4(_e191))); + let _e196 = a_3[0u]; + let _e198 = a_3[1u]; + let _e201 = a_3[2u]; + let _e203 = a_3[3u]; + let _e207 = b_2[0u]; + let _e209 = b_2[1u]; + let _e212 = b_2[2u]; + let _e214 = b_2[3u]; + return (min(min(min(_e196, _e198), min(_e201, _e203)), min(min(_e207, _e209), min(_e212, _e214))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { var param_75: vec4; var param_76: i32; - let _e180 = (*slot_3); - let _e184 = light_list_info.indices[(_e180 / 4i)]; - param_75 = _e184; - let _e185 = (*slot_3); - param_76 = _e185; - let _e186 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); - return _e186; + let _e181 = (*slot_3); + let _e185 = light_list_info.indices[(_e181 / 4i)]; + param_75 = _e185; + let _e186 = (*slot_3); + param_76 = _e186; + let _e187 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); + return _e187; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e179 = (*texel_2); - let _e183 = light_list_info.list[1u]; - let _e185 = (*row_1); - let _e189 = light_list_info.list[2u]; - let _e192 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e179 + 0.5f) * _e183), ((_e185 + 0.5f) * _e189)), 0f); - return _e192; + let _e180 = (*texel_2); + let _e184 = light_list_info.list[1u]; + let _e186 = (*row_1); + let _e190 = light_list_info.list[2u]; + let _e193 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e180 + 0.5f) * _e184), ((_e186 + 0.5f) * _e190)), 0f); + return _e193; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -22870,35 +22976,35 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var param_79: vec4; var param_80: i32; - let _e187 = g_cluster_offset; - let _e188 = (*slot_4); - entry_1 = (_e187 + f32(_e188)); - let _e191 = entry_1; - row_2 = floor((_e191 / 16f)); - let _e194 = entry_1; - let _e195 = row_2; - within = (_e194 - (_e195 * 16f)); - let _e198 = within; - texel_3 = floor((_e198 / 4f)); - let _e203 = light_list_info.cluster_depth[3u]; - let _e204 = row_2; - let _e206 = texel_3; - param_77 = _e206; - param_78 = (_e203 + _e204); - let _e207 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); - four_1 = _e207; - let _e208 = within; - let _e209 = texel_3; - let _e214 = four_1; - param_79 = _e214; - param_80 = i32(((_e208 - (_e209 * 4f)) + 0.5f)); - let _e215 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); - return _e215; + let _e188 = g_cluster_offset; + let _e189 = (*slot_4); + entry_1 = (_e188 + f32(_e189)); + let _e192 = entry_1; + row_2 = floor((_e192 / 16f)); + let _e195 = entry_1; + let _e196 = row_2; + within = (_e195 - (_e196 * 16f)); + let _e199 = within; + texel_3 = floor((_e199 / 4f)); + let _e204 = light_list_info.cluster_depth[3u]; + let _e205 = row_2; + let _e207 = texel_3; + param_77 = _e207; + param_78 = (_e204 + _e205); + let _e208 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); + four_1 = _e208; + let _e209 = within; + let _e210 = texel_3; + let _e215 = four_1; + param_79 = _e215; + param_80 = i32(((_e209 - (_e210 * 4f)) + 0.5f)); + let _e216 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); + return _e216; } fn Clustered_u0028_() -> bool { - let _e179 = light_list_info.cluster_grid[3u]; - return (_e179 > 0.5f); + let _e180 = light_list_info.cluster_grid[3u]; + return (_e180 > 0.5f); } fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_3: ptr) -> LightSample { @@ -22954,264 +23060,264 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e230 = (*index_2); - if (_e230 < 8i) { - let _e232 = (*index_2); - let _e235 = frag_info.light_position[_e232]; - position = _e235; - let _e236 = (*index_2); - let _e239 = frag_info.light_color[_e236]; - color_1 = _e239; - let _e240 = (*index_2); - let _e243 = frag_info.light_direction[_e240]; - direction = _e243; - let _e244 = (*index_2); - let _e247 = frag_info.light_cone[_e244]; - cone = _e247; + let _e231 = (*index_2); + if (_e231 < 8i) { + let _e233 = (*index_2); + let _e236 = frag_info.light_position[_e233]; + position = _e236; + let _e237 = (*index_2); + let _e240 = frag_info.light_color[_e237]; + color_1 = _e240; + let _e241 = (*index_2); + let _e244 = frag_info.light_direction[_e241]; + direction = _e244; + let _e245 = (*index_2); + let _e248 = frag_info.light_cone[_e245]; + cone = _e248; } else { - let _e248 = (*index_2); - slot_5 = (_e248 - 8i); - let _e250 = Clustered_u0028_(); - clustered = _e250; - let _e251 = clustered; - if _e251 { - let _e252 = slot_5; - param_81 = _e252; - let _e253 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_16 = _e253; - } else { - let _e254 = slot_5; - param_82 = _e254; - let _e255 = LightListRow_u0028_i1_u003b((¶m_82)); - local_16 = _e255; - } - let _e256 = local_16; - listRow = _e256; - let _e257 = listRow; - let _e261 = light_list_info.list[2u]; - v_1 = ((_e257 + 0.5f) * _e261); - let _e265 = light_list_info.list[1u]; - u_1 = _e265; - let _e266 = u_1; - let _e268 = v_1; - let _e270 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e266), _e268), 0f); - position = _e270; - let _e271 = u_1; - let _e273 = v_1; - let _e275 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e271), _e273), 0f); - color_1 = _e275; - let _e276 = u_1; - let _e278 = v_1; - let _e280 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e276), _e278), 0f); - direction = _e280; - let _e281 = u_1; - let _e283 = v_1; - let _e285 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e281), _e283), 0f); - cone = _e285; - let _e286 = clustered; - if _e286 { - let _e287 = listRow; - param_83 = _e287; - let _e288 = InSlots_u0028_f1_u003b((¶m_83)); - local_17 = select(1f, 0f, _e288); - } else { - let _e290 = slot_5; - param_84 = _e290; - let _e291 = LightListScale_u0028_i1_u003b((¶m_84)); - local_17 = _e291; - } - let _e292 = local_17; - let _e294 = color_1[3u]; - color_1[3u] = (_e294 * _e292); - } - let _e298 = position[3u]; - type_39 = _e298; - let _e299 = type_39; - if (_e299 > 2.5f) { - let _e301 = direction; - halfWidth_1 = _e301.xyz; - let _e303 = cone; - halfHeight_1 = _e303.xyz; - let _e305 = position; - let _e307 = v_world_position_1; - toCentre = (_e305.xyz - _e307); - let _e309 = toCentre; - let _e310 = halfWidth_1; - let _e312 = halfHeight_1; - corners_1[0i] = ((_e309 - _e310) - _e312); - let _e315 = toCentre; - let _e316 = halfWidth_1; - let _e318 = halfHeight_1; - corners_1[1i] = ((_e315 + _e316) - _e318); - let _e321 = toCentre; - let _e322 = halfWidth_1; - let _e324 = halfHeight_1; - corners_1[2i] = ((_e321 + _e322) + _e324); - let _e327 = toCentre; - let _e328 = halfWidth_1; - let _e330 = halfHeight_1; - corners_1[3i] = ((_e327 - _e328) + _e330); - let _e333 = toCentre; - let _e334 = halfWidth_1; - let _e335 = halfHeight_1; - behind = (dot(_e333, cross(_e334, _e335)) >= 0f); - let _e339 = behind; - if _e339 { - local_18 = 0f; - } else { - let _e340 = corners_1; - param_85 = _e340; - let _e342 = (*s_3).n; - param_86 = _e342; - let _e343 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_18 = _e343; - } - let _e344 = local_18; - formFactor = _e344; - let _e345 = halfWidth_1; - let _e346 = halfHeight_1; - area = (length(cross(_e345, _e346)) * 4f); - let _e350 = area; - if (_e350 > 0.000000001f) { - let _e352 = area; - local_19 = (1f / _e352); - } else { - local_19 = 0f; - } - let _e354 = local_19; - radiance = _e354; - let _e355 = toCentre; - distance_2 = length(_e355); - let _e358 = direction[3u]; - if (_e358 > 0f) { - let _e360 = distance_2; - let _e362 = direction[3u]; - ratio_1 = (_e360 / _e362); - let _e364 = ratio_1; - let _e365 = ratio_1; - let _e367 = ratio_1; - let _e369 = ratio_1; - window_1 = clamp((1f - (((_e364 * _e365) * _e367) * _e369)), 0f, 1f); - let _e373 = window_1; - let _e374 = window_1; - let _e376 = radiance; - radiance = (_e376 * (_e373 * _e374)); - } - let _e379 = (*s_3).v; - let _e382 = (*s_3).n; - mirror_1 = reflect(-(_e379), _e382); - let _e384 = position; - param_87 = _e384.xyz; - let _e386 = halfWidth_1; - param_88 = _e386; - let _e387 = halfHeight_1; - param_89 = _e387; - let _e388 = v_world_position_1; - param_90 = _e388; - let _e389 = mirror_1; - param_91 = _e389; - let _e390 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); - representative = _e390; - let _e391 = representative; - let _e392 = v_world_position_1; - toPoint = (_e391 - _e392); - let _e394 = toPoint; - pointDistance = length(_e394); - let _e396 = pointDistance; - if (_e396 > 0.000001f) { - let _e398 = toPoint; - let _e399 = pointDistance; - local_20 = (_e398 / vec3(_e399)); + let _e249 = (*index_2); + slot_5 = (_e249 - 8i); + let _e251 = Clustered_u0028_(); + clustered = _e251; + let _e252 = clustered; + if _e252 { + let _e253 = slot_5; + param_81 = _e253; + let _e254 = ClusterRow_u0028_i1_u003b((¶m_81)); + local_16 = _e254; } else { - let _e403 = (*s_3).n; - local_20 = _e403; + let _e255 = slot_5; + param_82 = _e255; + let _e256 = LightListRow_u0028_i1_u003b((¶m_82)); + local_16 = _e256; + } + let _e257 = local_16; + listRow = _e257; + let _e258 = listRow; + let _e262 = light_list_info.list[2u]; + v_1 = ((_e258 + 0.5f) * _e262); + let _e266 = light_list_info.list[1u]; + u_1 = _e266; + let _e267 = u_1; + let _e269 = v_1; + let _e271 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e267), _e269), 0f); + position = _e271; + let _e272 = u_1; + let _e274 = v_1; + let _e276 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e272), _e274), 0f); + color_1 = _e276; + let _e277 = u_1; + let _e279 = v_1; + let _e281 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e277), _e279), 0f); + direction = _e281; + let _e282 = u_1; + let _e284 = v_1; + let _e286 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e282), _e284), 0f); + cone = _e286; + let _e287 = clustered; + if _e287 { + let _e288 = listRow; + param_83 = _e288; + let _e289 = InSlots_u0028_f1_u003b((¶m_83)); + local_17 = select(1f, 0f, _e289); + } else { + let _e291 = slot_5; + param_84 = _e291; + let _e292 = LightListScale_u0028_i1_u003b((¶m_84)); + local_17 = _e292; + } + let _e293 = local_17; + let _e295 = color_1[3u]; + color_1[3u] = (_e295 * _e293); + } + let _e299 = position[3u]; + type_39 = _e299; + let _e300 = type_39; + if (_e300 > 2.5f) { + let _e302 = direction; + halfWidth_1 = _e302.xyz; + let _e304 = cone; + halfHeight_1 = _e304.xyz; + let _e306 = position; + let _e308 = v_world_position_1; + toCentre = (_e306.xyz - _e308); + let _e310 = toCentre; + let _e311 = halfWidth_1; + let _e313 = halfHeight_1; + corners_1[0i] = ((_e310 - _e311) - _e313); + let _e316 = toCentre; + let _e317 = halfWidth_1; + let _e319 = halfHeight_1; + corners_1[1i] = ((_e316 + _e317) - _e319); + let _e322 = toCentre; + let _e323 = halfWidth_1; + let _e325 = halfHeight_1; + corners_1[2i] = ((_e322 + _e323) + _e325); + let _e328 = toCentre; + let _e329 = halfWidth_1; + let _e331 = halfHeight_1; + corners_1[3i] = ((_e328 - _e329) + _e331); + let _e334 = toCentre; + let _e335 = halfWidth_1; + let _e336 = halfHeight_1; + behind = (dot(_e334, cross(_e335, _e336)) >= 0f); + let _e340 = behind; + if _e340 { + local_18 = 0f; + } else { + let _e341 = corners_1; + param_85 = _e341; + let _e343 = (*s_3).n; + param_86 = _e343; + let _e344 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); + local_18 = _e344; + } + let _e345 = local_18; + formFactor = _e345; + let _e346 = halfWidth_1; + let _e347 = halfHeight_1; + area = (length(cross(_e346, _e347)) * 4f); + let _e351 = area; + if (_e351 > 0.000000001f) { + let _e353 = area; + local_19 = (1f / _e353); + } else { + local_19 = 0f; } - let _e404 = local_20; - light_3.l = _e404; - let _e407 = light_3.l; - let _e409 = (*s_3).v; - light_3.h = normalize((_e407 + _e409)); - let _e413 = formFactor; - light_3.n_dot_l = _e413; - let _e416 = (*s_3).n; - let _e418 = light_3.h; - light_3.n_dot_h = max(dot(_e416, _e418), 0f); - let _e423 = (*s_3).v; - let _e425 = light_3.h; - light_3.v_dot_h = max(dot(_e423, _e425), 0f); - let _e429 = color_1; - let _e432 = color_1[3u]; - let _e434 = radiance; - light_3.radiance = ((_e429.xyz * _e432) * _e434); - let _e437 = light_3; - return _e437; - } - let _e438 = direction; - aim = normalize(_e438.xyz); + let _e355 = local_19; + radiance = _e355; + let _e356 = toCentre; + distance_2 = length(_e356); + let _e359 = direction[3u]; + if (_e359 > 0f) { + let _e361 = distance_2; + let _e363 = direction[3u]; + ratio_1 = (_e361 / _e363); + let _e365 = ratio_1; + let _e366 = ratio_1; + let _e368 = ratio_1; + let _e370 = ratio_1; + window_1 = clamp((1f - (((_e365 * _e366) * _e368) * _e370)), 0f, 1f); + let _e374 = window_1; + let _e375 = window_1; + let _e377 = radiance; + radiance = (_e377 * (_e374 * _e375)); + } + let _e380 = (*s_3).v; + let _e383 = (*s_3).n; + mirror_1 = reflect(-(_e380), _e383); + let _e385 = position; + param_87 = _e385.xyz; + let _e387 = halfWidth_1; + param_88 = _e387; + let _e388 = halfHeight_1; + param_89 = _e388; + let _e389 = v_world_position_1; + param_90 = _e389; + let _e390 = mirror_1; + param_91 = _e390; + let _e391 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); + representative = _e391; + let _e392 = representative; + let _e393 = v_world_position_1; + toPoint = (_e392 - _e393); + let _e395 = toPoint; + pointDistance = length(_e395); + let _e397 = pointDistance; + if (_e397 > 0.000001f) { + let _e399 = toPoint; + let _e400 = pointDistance; + local_20 = (_e399 / vec3(_e400)); + } else { + let _e404 = (*s_3).n; + local_20 = _e404; + } + let _e405 = local_20; + light_3.l = _e405; + let _e408 = light_3.l; + let _e410 = (*s_3).v; + light_3.h = normalize((_e408 + _e410)); + let _e414 = formFactor; + light_3.n_dot_l = _e414; + let _e417 = (*s_3).n; + let _e419 = light_3.h; + light_3.n_dot_h = max(dot(_e417, _e419), 0f); + let _e424 = (*s_3).v; + let _e426 = light_3.h; + light_3.v_dot_h = max(dot(_e424, _e426), 0f); + let _e430 = color_1; + let _e433 = color_1[3u]; + let _e435 = radiance; + light_3.radiance = ((_e430.xyz * _e433) * _e435); + let _e438 = light_3; + return _e438; + } + let _e439 = direction; + aim = normalize(_e439.xyz); attenuation_1 = 1f; - let _e441 = type_39; - if (_e441 < 0.5f) { - let _e443 = aim; - light_3.l = -(_e443); + let _e442 = type_39; + if (_e442 < 0.5f) { + let _e444 = aim; + light_3.l = -(_e444); } else { - let _e446 = position; - let _e448 = v_world_position_1; - toLight_1 = (_e446.xyz - _e448); - let _e450 = toLight_1; - distance_3 = length(_e450); - let _e452 = distance_3; - if (_e452 < 0.000001f) { - let _e455 = (*s_3).n; - light_3.l = _e455; - let _e458 = (*s_3).n; - light_3.h = _e458; + let _e447 = position; + let _e449 = v_world_position_1; + toLight_1 = (_e447.xyz - _e449); + let _e451 = toLight_1; + distance_3 = length(_e451); + let _e453 = distance_3; + if (_e453 < 0.000001f) { + let _e456 = (*s_3).n; + light_3.l = _e456; + let _e459 = (*s_3).n; + light_3.h = _e459; light_3.radiance = vec3(0f, 0f, 0f); light_3.n_dot_l = 0f; light_3.n_dot_h = 0f; light_3.v_dot_h = 0f; - let _e464 = light_3; - return _e464; - } - let _e465 = toLight_1; - let _e466 = distance_3; - light_3.l = (_e465 / vec3(_e466)); - let _e470 = distance_3; - param_92 = _e470; - let _e472 = direction[3u]; - param_93 = _e472; - let _e473 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); - attenuation_1 = _e473; - let _e474 = type_39; - if (_e474 > 1.5f) { - let _e476 = aim; - let _e478 = light_3.l; - cosAngle = dot(_e476, -(_e478)); - let _e481 = cosAngle; - let _e483 = cone[1u]; - let _e486 = cone[0u]; - let _e488 = cone[1u]; - let _e492 = attenuation_1; - attenuation_1 = (_e492 * clamp(((_e481 - _e483) / (_e486 - _e488)), 0f, 1f)); - } - } - let _e495 = light_3.l; - let _e497 = (*s_3).v; - light_3.h = normalize((_e495 + _e497)); - let _e502 = (*s_3).n; - let _e504 = light_3.l; - light_3.n_dot_l = max(dot(_e502, _e504), 0f); - let _e509 = (*s_3).n; - let _e511 = light_3.h; - light_3.n_dot_h = max(dot(_e509, _e511), 0f); - let _e516 = (*s_3).v; - let _e518 = light_3.h; - light_3.v_dot_h = max(dot(_e516, _e518), 0f); - let _e522 = color_1; - let _e525 = color_1[3u]; - let _e527 = attenuation_1; - light_3.radiance = ((_e522.xyz * _e525) * _e527); - let _e530 = light_3; - return _e530; + let _e465 = light_3; + return _e465; + } + let _e466 = toLight_1; + let _e467 = distance_3; + light_3.l = (_e466 / vec3(_e467)); + let _e471 = distance_3; + param_92 = _e471; + let _e473 = direction[3u]; + param_93 = _e473; + let _e474 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); + attenuation_1 = _e474; + let _e475 = type_39; + if (_e475 > 1.5f) { + let _e477 = aim; + let _e479 = light_3.l; + cosAngle = dot(_e477, -(_e479)); + let _e482 = cosAngle; + let _e484 = cone[1u]; + let _e487 = cone[0u]; + let _e489 = cone[1u]; + let _e493 = attenuation_1; + attenuation_1 = (_e493 * clamp(((_e482 - _e484) / (_e487 - _e489)), 0f, 1f)); + } + } + let _e496 = light_3.l; + let _e498 = (*s_3).v; + light_3.h = normalize((_e496 + _e498)); + let _e503 = (*s_3).n; + let _e505 = light_3.l; + light_3.n_dot_l = max(dot(_e503, _e505), 0f); + let _e510 = (*s_3).n; + let _e512 = light_3.h; + light_3.n_dot_h = max(dot(_e510, _e512), 0f); + let _e517 = (*s_3).v; + let _e519 = light_3.h; + light_3.v_dot_h = max(dot(_e517, _e519), 0f); + let _e523 = color_1; + let _e526 = color_1[3u]; + let _e528 = attenuation_1; + light_3.radiance = ((_e523.xyz * _e526) * _e528); + let _e531 = light_3; + return _e531; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -23229,58 +23335,58 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var param_94: f32; var param_95: f32; - let _e192 = light_list_info.cluster_view_projection; - let _e193 = (*world_3); - clip_1 = (_e192 * vec4(_e193.x, _e193.y, _e193.z, 1f)); - let _e199 = clip_1; - let _e202 = clip_1[3u]; - ndc_1 = (_e199.xy / vec2(max(_e202, 0.000001f))); - let _e207 = light_list_info.cluster_grid; - grid = _e207.xyz; - let _e211 = light_list_info.cluster_depth[0u]; - near_1 = _e211; - let _e213 = ndc_1[0u]; - let _e217 = grid[0u]; - let _e221 = grid[0u]; - tx = clamp(floor((((_e213 * 0.5f) + 0.5f) * _e217)), 0f, (_e221 - 1f)); - let _e225 = ndc_1[1u]; - let _e229 = grid[1u]; - let _e233 = grid[1u]; - ty = clamp(floor((((_e225 * 0.5f) + 0.5f) * _e229)), 0f, (_e233 - 1f)); - let _e237 = clip_1[3u]; - let _e238 = near_1; - if (_e237 <= _e238) { + let _e193 = light_list_info.cluster_view_projection; + let _e194 = (*world_3); + clip_1 = (_e193 * vec4(_e194.x, _e194.y, _e194.z, 1f)); + let _e200 = clip_1; + let _e203 = clip_1[3u]; + ndc_1 = (_e200.xy / vec2(max(_e203, 0.000001f))); + let _e208 = light_list_info.cluster_grid; + grid = _e208.xyz; + let _e212 = light_list_info.cluster_depth[0u]; + near_1 = _e212; + let _e214 = ndc_1[0u]; + let _e218 = grid[0u]; + let _e222 = grid[0u]; + tx = clamp(floor((((_e214 * 0.5f) + 0.5f) * _e218)), 0f, (_e222 - 1f)); + let _e226 = ndc_1[1u]; + let _e230 = grid[1u]; + let _e234 = grid[1u]; + ty = clamp(floor((((_e226 * 0.5f) + 0.5f) * _e230)), 0f, (_e234 - 1f)); + let _e238 = clip_1[3u]; + let _e239 = near_1; + if (_e238 <= _e239) { local_21 = 0f; } else { - let _e241 = clip_1[3u]; - let _e242 = near_1; - let _e247 = light_list_info.cluster_depth[1u]; - let _e251 = grid[2u]; - local_21 = clamp(floor((log((_e241 / _e242)) * _e247)), 0f, (_e251 - 1f)); - } - let _e254 = local_21; - tz = _e254; - let _e255 = tx; - let _e256 = ty; - let _e258 = grid[0u]; - let _e261 = tz; - let _e263 = grid[0u]; - let _e266 = grid[1u]; - cell = ((_e255 + (_e256 * _e258)) + ((_e261 * _e263) * _e266)); - let _e269 = cell; - row_3 = floor((_e269 / 4f)); - let _e272 = cell; - let _e273 = row_3; - let _e278 = light_list_info.cluster_depth[2u]; - let _e279 = row_3; - param_94 = (_e272 - (_e273 * 4f)); - param_95 = (_e278 + _e279); - let _e281 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); - header = _e281; - let _e283 = header[0u]; - g_cluster_offset = _e283; - let _e285 = header[1u]; - g_cluster_count = _e285; + let _e242 = clip_1[3u]; + let _e243 = near_1; + let _e248 = light_list_info.cluster_depth[1u]; + let _e252 = grid[2u]; + local_21 = clamp(floor((log((_e242 / _e243)) * _e248)), 0f, (_e252 - 1f)); + } + let _e255 = local_21; + tz = _e255; + let _e256 = tx; + let _e257 = ty; + let _e259 = grid[0u]; + let _e262 = tz; + let _e264 = grid[0u]; + let _e267 = grid[1u]; + cell = ((_e256 + (_e257 * _e259)) + ((_e262 * _e264) * _e267)); + let _e270 = cell; + row_3 = floor((_e270 / 4f)); + let _e273 = cell; + let _e274 = row_3; + let _e279 = light_list_info.cluster_depth[2u]; + let _e280 = row_3; + param_94 = (_e273 - (_e274 * 4f)); + param_95 = (_e279 + _e280); + let _e282 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); + header = _e282; + let _e284 = header[0u]; + g_cluster_offset = _e284; + let _e286 = header[1u]; + g_cluster_count = _e286; return; } @@ -23288,19 +23394,19 @@ fn LightCount_u0028_() -> i32 { var tail: f32; var param_96: vec3; - let _e181 = light_list_info.list[0u]; - tail = _e181; - let _e182 = Clustered_u0028_(); - if _e182 { - let _e183 = v_world_position_1; - param_96 = _e183; + let _e182 = light_list_info.list[0u]; + tail = _e182; + let _e183 = Clustered_u0028_(); + if _e183 { + let _e184 = v_world_position_1; + param_96 = _e184; FindCluster_u0028_vf3_u003b((¶m_96)); - let _e184 = g_cluster_count; - tail = _e184; + let _e185 = g_cluster_count; + tail = _e185; } - let _e187 = frag_info.frame_params[1u]; - let _e191 = tail; - return (clamp(i32((_e187 + 0.5f)), 0i, 8i) + clamp(i32((_e191 + 0.5f)), 0i, 24i)); + let _e188 = frag_info.frame_params[1u]; + let _e192 = tail; + return (clamp(i32((_e188 + 0.5f)), 0i, 8i) + clamp(i32((_e192 + 0.5f)), 0i, 24i)); } fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_4: ptr) -> vec3 { @@ -23323,144 +23429,146 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_107: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e195 = LightCount_u0028_(); - count_1 = _e195; + let _e196 = LightCount_u0028_(); + count_1 = _e196; i_8 = 0i; loop { - let _e196 = i_8; - if (_e196 < 32i) { - let _e198 = i_8; - let _e199 = count_1; - if (_e198 >= _e199) { + let _e197 = i_8; + if (_e197 < 32i) { + let _e199 = i_8; + let _e200 = count_1; + if (_e199 >= _e200) { break; } - let _e201 = i_8; - param_97 = _e201; - let _e202 = (*s_4); - param_98 = _e202; - let _e203 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); - light_4 = _e203; - let _e205 = light_4.n_dot_l; - if (_e205 <= 0f) { + let _e202 = i_8; + param_97 = _e202; + let _e203 = (*s_4); + param_98 = _e203; + let _e204 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); + light_4 = _e204; + let _e206 = light_4.n_dot_l; + if (_e206 <= 0f) { continue; } - let _e207 = i_8; - param_99 = _e207; - let _e208 = LightHasShadow_u0028_i1_u003b((¶m_99)); - if _e208 { - let _e209 = (*s_4); - param_100 = _e209; - let _e210 = light_4; - param_101 = _e210; - let _e211 = i_8; - param_102 = _e211; - let _e212 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); - let _e213 = v_world_position_1; - param_103 = _e213; - let _e215 = (*s_4).n; - param_104 = _e215; - let _e216 = i_8; - param_105 = _e216; - let _e217 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_22 = (_e212 * _e217); + light_transmittance = vec3(1f, 1f, 1f); + let _e208 = i_8; + param_99 = _e208; + let _e209 = LightHasShadow_u0028_i1_u003b((¶m_99)); + if _e209 { + let _e210 = (*s_4); + param_100 = _e210; + let _e211 = light_4; + param_101 = _e211; + let _e212 = i_8; + param_102 = _e212; + let _e213 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); + let _e214 = v_world_position_1; + param_103 = _e214; + let _e216 = (*s_4).n; + param_104 = _e216; + let _e217 = i_8; + param_105 = _e217; + let _e218 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); + local_22 = (_e213 * _e218); } else { local_22 = 1f; } - let _e219 = local_22; - visibility = _e219; - let _e220 = visibility; - if (_e220 <= 0f) { + let _e220 = local_22; + visibility = _e220; + let _e221 = visibility; + if (_e221 <= 0f) { continue; } - let _e222 = (*s_4); - param_106 = _e222; - let _e223 = light_4; - param_107 = _e223; - let _e224 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); - let _e226 = light_4.radiance; - let _e229 = light_4.n_dot_l; - let _e231 = visibility; - let _e233 = total_1; - total_1 = (_e233 + (((_e224 * _e226) * _e229) * _e231)); + let _e223 = (*s_4); + param_106 = _e223; + let _e224 = light_4; + param_107 = _e224; + let _e225 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); + let _e227 = light_4.radiance; + let _e230 = light_4.n_dot_l; + let _e232 = visibility; + let _e234 = light_transmittance; + let _e236 = total_1; + total_1 = (_e236 + ((((_e225 * _e227) * _e230) * _e232) * _e234)); continue; } else { break; } continuing { - let _e235 = i_8; - i_8 = (_e235 + 1i); + let _e238 = i_8; + i_8 = (_e238 + 1i); } } - let _e237 = total_1; - return _e237; + let _e240 = total_1; + return _e240; } fn SampleLightmap_u0028_() -> vec3 { var texel_4: vec4; - let _e178 = v_lightmap_uv_1; - let _e179 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e178); - texel_4 = _e179; - let _e180 = texel_4; - let _e183 = texel_4[3u]; - return ((_e180.xyz * _e183) * 8f); + let _e179 = v_lightmap_uv_1; + let _e180 = textureSample(lightmap_texture_tex, lightmap_texture_smp, _e179); + texel_4 = _e180; + let _e181 = texel_4; + let _e184 = texel_4[3u]; + return ((_e181.xyz * _e184) * 8f); } fn MaterialLodBias_u0028_() -> f32 { - let _e179 = frag_info.target_origin[1u]; - return _e179; + let _e180 = frag_info.target_origin[1u]; + return _e180; } fn ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_5: ptr) { var orm: vec3; - let _e179 = v_texcoord_1; - let _e180 = MaterialLodBias_u0028_(); - let _e181 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e179, _e180); - orm = _e181.xyz; - let _e184 = (*s_5).metallic; - let _e186 = orm[2u]; - (*s_5).metallic = clamp((_e184 * _e186), 0f, 1f); - let _e191 = (*s_5).roughness; - let _e193 = orm[1u]; - (*s_5).roughness = clamp((_e191 * _e193), 0.02f, 1f); - return; -} - -fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e178 = (*srgb); - let _e181 = (*srgb); - let _e186 = (*srgb); - return mix((_e178 / vec3(12.92f)), pow(((_e181 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e186)); -} - -fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) { - var emissive: vec3; - var param_108: vec3; - let _e180 = v_texcoord_1; let _e181 = MaterialLodBias_u0028_(); - let _e182 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e180, _e181); - param_108 = _e182.xyz; - let _e184 = SrgbToLinear_u0028_vf3_u003b((¶m_108)); - emissive = _e184; - let _e185 = emissive; - let _e187 = frag_info.emissive; - let _e192 = frag_info.material2_[3u]; - (*s_6).emissive = ((_e185 * _e187.xyz) * _e192); + let _e182 = textureSampleBias(metallic_roughness_texture_tex, metallic_roughness_texture_smp, _e180, _e181); + orm = _e182.xyz; + let _e185 = (*s_5).metallic; + let _e187 = orm[2u]; + (*s_5).metallic = clamp((_e185 * _e187), 0f, 1f); + let _e192 = (*s_5).roughness; + let _e194 = orm[1u]; + (*s_5).roughness = clamp((_e192 * _e194), 0.02f, 1f); + return; +} + +fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { + let _e179 = (*srgb); + let _e182 = (*srgb); + let _e187 = (*srgb); + return mix((_e179 / vec3(12.92f)), pow(((_e182 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e187)); +} + +fn ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_6: ptr) { + var emissive: vec3; + var param_108: vec3; + + let _e181 = v_texcoord_1; + let _e182 = MaterialLodBias_u0028_(); + let _e183 = textureSampleBias(emissive_texture_tex, emissive_texture_smp, _e181, _e182); + param_108 = _e183.xyz; + let _e185 = SrgbToLinear_u0028_vf3_u003b((¶m_108)); + emissive = _e185; + let _e186 = emissive; + let _e188 = frag_info.emissive; + let _e193 = frag_info.material2_[3u]; + (*s_6).emissive = ((_e186 * _e188.xyz) * _e193); return; } fn ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_7: ptr) { var occlusion: f32; - let _e179 = v_texcoord_1; - let _e180 = MaterialLodBias_u0028_(); - let _e181 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e179, _e180); - occlusion = _e181.x; - let _e183 = occlusion; - let _e186 = frag_info.material2_[2u]; - (*s_7).occlusion = mix(1f, _e183, clamp(_e186, 0f, 1f)); + let _e180 = v_texcoord_1; + let _e181 = MaterialLodBias_u0028_(); + let _e182 = textureSampleBias(occlusion_texture_tex, occlusion_texture_smp, _e180, _e181); + occlusion = _e182.x; + let _e184 = occlusion; + let _e187 = frag_info.material2_[2u]; + (*s_7).occlusion = mix(1f, _e184, clamp(_e187, 0f, 1f)); return; } @@ -23470,64 +23578,64 @@ fn ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_ var b_3: vec3; var sampled: vec3; - let _e182 = v_texcoord_1; - let _e183 = MaterialLodBias_u0028_(); - let _e184 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e182, _e183); - sampledTexel = _e184; - let _e185 = v_tangent_1; - t_2 = _e185.xyz; - let _e187 = t_2; - let _e189 = (*s_8).n; - let _e191 = (*s_8).n; - let _e192 = t_2; - t_2 = (_e187 - (_e189 * dot(_e191, _e192))); - let _e196 = t_2; + let _e183 = v_texcoord_1; + let _e184 = MaterialLodBias_u0028_(); + let _e185 = textureSampleBias(normal_texture_tex, normal_texture_smp, _e183, _e184); + sampledTexel = _e185; + let _e186 = v_tangent_1; + t_2 = _e186.xyz; + let _e188 = t_2; + let _e190 = (*s_8).n; + let _e192 = (*s_8).n; + let _e193 = t_2; + t_2 = (_e188 - (_e190 * dot(_e192, _e193))); let _e197 = t_2; - if (dot(_e196, _e197) < 0.000000000001f) { + let _e198 = t_2; + if (dot(_e197, _e198) < 0.000000000001f) { return; } - let _e200 = t_2; - t_2 = normalize(_e200); - let _e203 = (*s_8).n; - let _e204 = t_2; - let _e207 = v_tangent_1[3u]; - b_3 = (cross(_e203, _e204) * _e207); - let _e209 = gl_FrontFacing_1; - if !(_e209) { - let _e211 = t_2; - t_2 = -(_e211); - } - let _e213 = sampledTexel; - sampled = ((_e213.xyz * 2f) - vec3(1f)); - let _e220 = frag_info.emissive[3u]; - if (_e220 > 0.5f) { - let _e222 = sampled; - let _e224 = sampled; - sampled[2u] = sqrt(max((1f - dot(_e222.xy, _e224.xy)), 0f)); + let _e201 = t_2; + t_2 = normalize(_e201); + let _e204 = (*s_8).n; + let _e205 = t_2; + let _e208 = v_tangent_1[3u]; + b_3 = (cross(_e204, _e205) * _e208); + let _e210 = gl_FrontFacing_1; + if !(_e210) { + let _e212 = t_2; + t_2 = -(_e212); } - let _e233 = frag_info.material2_[1u]; - let _e234 = sampled; - let _e236 = (_e234.xy * _e233); - sampled[0u] = _e236.x; - sampled[1u] = _e236.y; - let _e241 = t_2; - let _e243 = sampled[0u]; - let _e245 = b_3; - let _e247 = sampled[1u]; - let _e251 = (*s_8).n; - let _e253 = sampled[2u]; - (*s_8).n = normalize((((_e241 * _e243) + (_e245 * _e247)) + (_e251 * _e253))); - let _e259 = (*s_8).n; - let _e261 = (*s_8).v; - (*s_8).n_dot_v = max(dot(_e259, _e261), 0.0001f); + let _e214 = sampledTexel; + sampled = ((_e214.xyz * 2f) - vec3(1f)); + let _e221 = frag_info.emissive[3u]; + if (_e221 > 0.5f) { + let _e223 = sampled; + let _e225 = sampled; + sampled[2u] = sqrt(max((1f - dot(_e223.xy, _e225.xy)), 0f)); + } + let _e234 = frag_info.material2_[1u]; + let _e235 = sampled; + let _e237 = (_e235.xy * _e234); + sampled[0u] = _e237.x; + sampled[1u] = _e237.y; + let _e242 = t_2; + let _e244 = sampled[0u]; + let _e246 = b_3; + let _e248 = sampled[1u]; + let _e252 = (*s_8).n; + let _e254 = sampled[2u]; + (*s_8).n = normalize((((_e242 * _e244) + (_e246 * _e248)) + (_e252 * _e254))); + let _e260 = (*s_8).n; + let _e262 = (*s_8).v; + (*s_8).n_dot_v = max(dot(_e260, _e262), 0.0001f); return; } fn AtlasTexel_u0028_vf2_u003b(texel_5: ptr>) -> vec4 { - let _e178 = (*texel_5); - let _e182 = irradiance_info.atlas; - let _e185 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e178 + vec2(0.5f)) * _e182.xy), 0f); - return _e185; + let _e179 = (*texel_5); + let _e183 = irradiance_info.atlas; + let _e186 = textureSampleLevel(irradiance_texture_tex, irradiance_texture_smp, ((_e179 + vec2(0.5f)) * _e183.xy), 0f); + return _e186; } fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr>, interior: ptr, uv_5: ptr>) -> vec4 { @@ -23543,42 +23651,42 @@ fn TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b(corner: ptr; var param_112: vec2; - let _e191 = (*uv_5); - let _e192 = (*interior); - at_1 = ((vec2(1f) + (_e191 * _e192)) - vec2(0.5f)); - let _e198 = at_1; - low = floor(_e198); - let _e200 = at_1; - let _e201 = low; - f = (_e200 - _e201); - let _e203 = (*corner); - let _e204 = low; - param_109 = (_e203 + _e204); - let _e206 = AtlasTexel_u0028_vf2_u003b((¶m_109)); - a_4 = _e206; - let _e207 = (*corner); - let _e208 = low; - param_110 = ((_e207 + _e208) + vec2(1f, 0f)); - let _e211 = AtlasTexel_u0028_vf2_u003b((¶m_110)); - b_4 = _e211; - let _e212 = (*corner); - let _e213 = low; - param_111 = ((_e212 + _e213) + vec2(0f, 1f)); - let _e216 = AtlasTexel_u0028_vf2_u003b((¶m_111)); - c = _e216; - let _e217 = (*corner); - let _e218 = low; - param_112 = ((_e217 + _e218) + vec2(1f, 1f)); - let _e221 = AtlasTexel_u0028_vf2_u003b((¶m_112)); - d_4 = _e221; - let _e222 = a_4; - let _e223 = b_4; - let _e225 = f[0u]; - let _e228 = c; - let _e229 = d_4; - let _e231 = f[0u]; - let _e235 = f[1u]; - return mix(mix(_e222, _e223, vec4(_e225)), mix(_e228, _e229, vec4(_e231)), vec4(_e235)); + let _e192 = (*uv_5); + let _e193 = (*interior); + at_1 = ((vec2(1f) + (_e192 * _e193)) - vec2(0.5f)); + let _e199 = at_1; + low = floor(_e199); + let _e201 = at_1; + let _e202 = low; + f = (_e201 - _e202); + let _e204 = (*corner); + let _e205 = low; + param_109 = (_e204 + _e205); + let _e207 = AtlasTexel_u0028_vf2_u003b((¶m_109)); + a_4 = _e207; + let _e208 = (*corner); + let _e209 = low; + param_110 = ((_e208 + _e209) + vec2(1f, 0f)); + let _e212 = AtlasTexel_u0028_vf2_u003b((¶m_110)); + b_4 = _e212; + let _e213 = (*corner); + let _e214 = low; + param_111 = ((_e213 + _e214) + vec2(0f, 1f)); + let _e217 = AtlasTexel_u0028_vf2_u003b((¶m_111)); + c = _e217; + let _e218 = (*corner); + let _e219 = low; + param_112 = ((_e218 + _e219) + vec2(1f, 1f)); + let _e222 = AtlasTexel_u0028_vf2_u003b((¶m_112)); + d_4 = _e222; + let _e223 = a_4; + let _e224 = b_4; + let _e226 = f[0u]; + let _e229 = c; + let _e230 = d_4; + let _e232 = f[0u]; + let _e236 = f[1u]; + return mix(mix(_e223, _e224, vec4(_e226)), mix(_e229, _e230, vec4(_e232)), vec4(_e236)); } fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec2 { @@ -23586,29 +23694,29 @@ fn ProbeOctahedral_u0028_vf3_u003b(direction_1: ptr>) -> vec var n_5: vec3; var xy: vec2; - let _e182 = (*direction_1)[0u]; - let _e185 = (*direction_1)[1u]; - let _e189 = (*direction_1)[2u]; - sum_1 = ((abs(_e182) + abs(_e185)) + abs(_e189)); - let _e192 = sum_1; - if (_e192 <= 0f) { + let _e183 = (*direction_1)[0u]; + let _e186 = (*direction_1)[1u]; + let _e190 = (*direction_1)[2u]; + sum_1 = ((abs(_e183) + abs(_e186)) + abs(_e190)); + let _e193 = sum_1; + if (_e193 <= 0f) { return vec2(0.5f, 0.5f); } - let _e194 = (*direction_1); - let _e195 = sum_1; - n_5 = (_e194 / vec3(_e195)); - let _e198 = n_5; - xy = _e198.xy; - let _e201 = n_5[2u]; - if (_e201 < 0f) { - let _e204 = n_5[1u]; - let _e208 = n_5[0u]; - let _e213 = n_5[0u]; - let _e217 = n_5[1u]; - xy = vec2(((1f - abs(_e204)) * select(-1f, 1f, (_e208 >= 0f))), ((1f - abs(_e213)) * select(-1f, 1f, (_e217 >= 0f)))); + let _e195 = (*direction_1); + let _e196 = sum_1; + n_5 = (_e195 / vec3(_e196)); + let _e199 = n_5; + xy = _e199.xy; + let _e202 = n_5[2u]; + if (_e202 < 0f) { + let _e205 = n_5[1u]; + let _e209 = n_5[0u]; + let _e214 = n_5[0u]; + let _e218 = n_5[1u]; + xy = vec2(((1f - abs(_e205)) * select(-1f, 1f, (_e209 >= 0f))), ((1f - abs(_e214)) * select(-1f, 1f, (_e218 >= 0f)))); } - let _e222 = xy; - return ((_e222 * 0.5f) + vec2(0.5f)); + let _e223 = xy; + return ((_e223 * 0.5f) + vec2(0.5f)); } fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr>, normal_1: ptr>, view: ptr>) -> vec3 { @@ -23637,209 +23745,209 @@ fn SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b(world_4: ptr; var weight_2: f32; var probePosition: vec3; - var toProbe: vec3; - var distance_4: f32; - var direction_2: vec3; - var facing: f32; - var probeWeight: f32; - var moments_3: vec2; - var param_114: vec3; - var param_115: vec2; - var param_116: f32; - var param_117: vec2; - var chebyshev: f32; - var variance_1: f32; - var difference: f32; - var param_118: vec3; - var param_119: vec2; - var param_120: f32; - var param_121: vec2; - var local_23: vec3; - - let _e224 = irradiance_info.origin; - origin_2 = _e224.xyz; - let _e227 = irradiance_info.spacing; - spacing = _e227.xyz; - let _e230 = irradiance_info.counts; - counts = _e230.xyz; - let _e234 = irradiance_info.tiles[0u]; - irradianceTile = _e234; - let _e237 = irradiance_info.tiles[1u]; - depthTile = _e237; - let _e240 = irradiance_info.tiles[2u]; - columns = _e240; - let _e243 = irradiance_info.tiles[3u]; - momentsTop = _e243; - let _e244 = (*normal_1); - unit = normalize(_e244); - let _e246 = (*world_4); - let _e247 = unit; - let _e250 = irradiance_info.spacing[3u]; - let _e253 = (*view); - let _e256 = irradiance_info.counts[3u]; - biased = ((_e246 + (_e247 * _e250)) + (_e253 * _e256)); - let _e259 = biased; - let _e260 = origin_2; - let _e262 = spacing; - grid_1 = ((_e259 - _e260) / _e262); - let _e264 = grid_1; - let _e266 = counts; - base = clamp(floor(_e264), vec3(0f, 0f, 0f), (_e266 - vec3(2f))); - let _e270 = grid_1; - let _e271 = base; - f_1 = clamp((_e270 - _e271), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - total_2 = vec3(0f, 0f, 0f); - weights = 0f; - corner_1 = 0i; - loop { - let _e274 = corner_1; - if (_e274 < 8i) { - let _e276 = corner_1; - let _e279 = corner_1; - let _e284 = corner_1; - offset_3 = vec3(f32((_e276 & 1i)), f32(((_e279 >> bitcast(1i)) & 1i)), f32(((_e284 >> bitcast(2i)) & 1i))); - let _e290 = base; - let _e291 = offset_3; - cell_1 = (_e290 + _e291); - let _e294 = cell_1[2u]; - let _e296 = counts[1u]; - let _e299 = cell_1[1u]; - let _e302 = counts[0u]; - let _e305 = cell_1[0u]; - probe = ((((_e294 * _e296) + _e299) * _e302) + _e305); - let _e307 = probe; - let _e308 = columns; - let _e313 = probe; - let _e314 = columns; - tile_4 = vec2((_e307 - (floor((_e307 / _e308)) * _e308)), floor((_e313 / _e314))); - let _e318 = tile_4; - let _e319 = irradianceTile; - irradianceCorner = (_e318 * (_e319 + 2f)); - let _e323 = tile_4[0u]; - let _e324 = depthTile; - let _e327 = momentsTop; - let _e329 = tile_4[1u]; - let _e330 = depthTile; - momentCorner = vec2((_e323 * (_e324 + 2f)), (_e327 + (_e329 * (_e330 + 2f)))); - let _e335 = irradianceCorner; - param_113 = (_e335 + vec2(1f)); - let _e338 = AtlasTexel_u0028_vf2_u003b((¶m_113)); - if (_e338.w < 0.5f) { - continue; - } - let _e341 = f_1; - let _e343 = f_1; - let _e344 = offset_3; - trilinear = mix((vec3(1f, 1f, 1f) - _e341), _e343, _e344); - let _e347 = trilinear[0u]; - let _e349 = trilinear[1u]; - let _e352 = trilinear[2u]; - weight_2 = max(((_e347 * _e349) * _e352), 0.001f); - let _e355 = origin_2; - let _e356 = spacing; - let _e357 = cell_1; - probePosition = (_e355 + (_e356 * _e357)); - let _e360 = probePosition; - let _e361 = biased; - toProbe = (_e360 - _e361); - let _e363 = toProbe; - distance_4 = length(_e363); - let _e365 = distance_4; - if (_e365 > 0.000001f) { - let _e367 = toProbe; - let _e368 = distance_4; - direction_2 = (_e367 / vec3(_e368)); - let _e371 = unit; - let _e372 = probePosition; - let _e373 = (*world_4); - facing = ((dot(_e371, normalize((_e372 - _e373))) * 0.5f) + 0.5f); - let _e379 = facing; + var toProbe: vec3; + var distance_4: f32; + var direction_2: vec3; + var facing: f32; + var probeWeight: f32; + var moments_3: vec2; + var param_114: vec3; + var param_115: vec2; + var param_116: f32; + var param_117: vec2; + var chebyshev: f32; + var variance_1: f32; + var difference: f32; + var param_118: vec3; + var param_119: vec2; + var param_120: f32; + var param_121: vec2; + var local_23: vec3; + + let _e225 = irradiance_info.origin; + origin_2 = _e225.xyz; + let _e228 = irradiance_info.spacing; + spacing = _e228.xyz; + let _e231 = irradiance_info.counts; + counts = _e231.xyz; + let _e235 = irradiance_info.tiles[0u]; + irradianceTile = _e235; + let _e238 = irradiance_info.tiles[1u]; + depthTile = _e238; + let _e241 = irradiance_info.tiles[2u]; + columns = _e241; + let _e244 = irradiance_info.tiles[3u]; + momentsTop = _e244; + let _e245 = (*normal_1); + unit = normalize(_e245); + let _e247 = (*world_4); + let _e248 = unit; + let _e251 = irradiance_info.spacing[3u]; + let _e254 = (*view); + let _e257 = irradiance_info.counts[3u]; + biased = ((_e247 + (_e248 * _e251)) + (_e254 * _e257)); + let _e260 = biased; + let _e261 = origin_2; + let _e263 = spacing; + grid_1 = ((_e260 - _e261) / _e263); + let _e265 = grid_1; + let _e267 = counts; + base = clamp(floor(_e265), vec3(0f, 0f, 0f), (_e267 - vec3(2f))); + let _e271 = grid_1; + let _e272 = base; + f_1 = clamp((_e271 - _e272), vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + total_2 = vec3(0f, 0f, 0f); + weights = 0f; + corner_1 = 0i; + loop { + let _e275 = corner_1; + if (_e275 < 8i) { + let _e277 = corner_1; + let _e280 = corner_1; + let _e285 = corner_1; + offset_3 = vec3(f32((_e277 & 1i)), f32(((_e280 >> bitcast(1i)) & 1i)), f32(((_e285 >> bitcast(2i)) & 1i))); + let _e291 = base; + let _e292 = offset_3; + cell_1 = (_e291 + _e292); + let _e295 = cell_1[2u]; + let _e297 = counts[1u]; + let _e300 = cell_1[1u]; + let _e303 = counts[0u]; + let _e306 = cell_1[0u]; + probe = ((((_e295 * _e297) + _e300) * _e303) + _e306); + let _e308 = probe; + let _e309 = columns; + let _e314 = probe; + let _e315 = columns; + tile_4 = vec2((_e308 - (floor((_e308 / _e309)) * _e309)), floor((_e314 / _e315))); + let _e319 = tile_4; + let _e320 = irradianceTile; + irradianceCorner = (_e319 * (_e320 + 2f)); + let _e324 = tile_4[0u]; + let _e325 = depthTile; + let _e328 = momentsTop; + let _e330 = tile_4[1u]; + let _e331 = depthTile; + momentCorner = vec2((_e324 * (_e325 + 2f)), (_e328 + (_e330 * (_e331 + 2f)))); + let _e336 = irradianceCorner; + param_113 = (_e336 + vec2(1f)); + let _e339 = AtlasTexel_u0028_vf2_u003b((¶m_113)); + if (_e339.w < 0.5f) { + continue; + } + let _e342 = f_1; + let _e344 = f_1; + let _e345 = offset_3; + trilinear = mix((vec3(1f, 1f, 1f) - _e342), _e344, _e345); + let _e348 = trilinear[0u]; + let _e350 = trilinear[1u]; + let _e353 = trilinear[2u]; + weight_2 = max(((_e348 * _e350) * _e353), 0.001f); + let _e356 = origin_2; + let _e357 = spacing; + let _e358 = cell_1; + probePosition = (_e356 + (_e357 * _e358)); + let _e361 = probePosition; + let _e362 = biased; + toProbe = (_e361 - _e362); + let _e364 = toProbe; + distance_4 = length(_e364); + let _e366 = distance_4; + if (_e366 > 0.000001f) { + let _e368 = toProbe; + let _e369 = distance_4; + direction_2 = (_e368 / vec3(_e369)); + let _e372 = unit; + let _e373 = probePosition; + let _e374 = (*world_4); + facing = ((dot(_e372, normalize((_e373 - _e374))) * 0.5f) + 0.5f); let _e380 = facing; - probeWeight = ((_e379 * _e380) + 0.2f); - let _e383 = direction_2; - param_114 = -(_e383); - let _e385 = ProbeOctahedral_u0028_vf3_u003b((¶m_114)); - let _e386 = momentCorner; - param_115 = _e386; - let _e387 = depthTile; - param_116 = _e387; - param_117 = _e385; - let _e388 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_115), (¶m_116), (¶m_117)); - moments_3 = _e388.xy; + let _e381 = facing; + probeWeight = ((_e380 * _e381) + 0.2f); + let _e384 = direction_2; + param_114 = -(_e384); + let _e386 = ProbeOctahedral_u0028_vf3_u003b((¶m_114)); + let _e387 = momentCorner; + param_115 = _e387; + let _e388 = depthTile; + param_116 = _e388; + param_117 = _e386; + let _e389 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_115), (¶m_116), (¶m_117)); + moments_3 = _e389.xy; chebyshev = 1f; - let _e390 = distance_4; - let _e392 = moments_3[0u]; - if (_e390 > _e392) { - let _e395 = moments_3[1u]; - let _e397 = moments_3[0u]; - let _e399 = moments_3[0u]; - variance_1 = max((_e395 - (_e397 * _e399)), 0.000001f); - let _e403 = distance_4; - let _e405 = moments_3[0u]; - difference = (_e403 - _e405); - let _e407 = variance_1; + let _e391 = distance_4; + let _e393 = moments_3[0u]; + if (_e391 > _e393) { + let _e396 = moments_3[1u]; + let _e398 = moments_3[0u]; + let _e400 = moments_3[0u]; + variance_1 = max((_e396 - (_e398 * _e400)), 0.000001f); + let _e404 = distance_4; + let _e406 = moments_3[0u]; + difference = (_e404 - _e406); let _e408 = variance_1; - let _e409 = difference; + let _e409 = variance_1; let _e410 = difference; - chebyshev = (_e407 / (_e408 + (_e409 * _e410))); - let _e414 = chebyshev; + let _e411 = difference; + chebyshev = (_e408 / (_e409 + (_e410 * _e411))); let _e415 = chebyshev; - let _e417 = chebyshev; - chebyshev = ((_e414 * _e415) * _e417); + let _e416 = chebyshev; + let _e418 = chebyshev; + chebyshev = ((_e415 * _e416) * _e418); } - let _e419 = probeWeight; - let _e420 = chebyshev; - probeWeight = max((_e419 * max(_e420, 0.05f)), 0.000001f); - let _e424 = probeWeight; - if (_e424 < 0.2f) { - let _e426 = probeWeight; + let _e420 = probeWeight; + let _e421 = chebyshev; + probeWeight = max((_e420 * max(_e421, 0.05f)), 0.000001f); + let _e425 = probeWeight; + if (_e425 < 0.2f) { let _e427 = probeWeight; - let _e430 = probeWeight; - probeWeight = (_e430 * ((_e426 * _e427) * 25f)); + let _e428 = probeWeight; + let _e431 = probeWeight; + probeWeight = (_e431 * ((_e427 * _e428) * 25f)); } - let _e432 = probeWeight; - let _e433 = weight_2; - weight_2 = (_e433 * _e432); - } - let _e435 = unit; - param_118 = _e435; - let _e436 = ProbeOctahedral_u0028_vf3_u003b((¶m_118)); - let _e437 = irradianceCorner; - param_119 = _e437; - let _e438 = irradianceTile; - param_120 = _e438; - param_121 = _e436; - let _e439 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_119), (¶m_120), (¶m_121)); - let _e441 = weight_2; - let _e443 = total_2; - total_2 = (_e443 + (_e439.xyz * _e441)); - let _e445 = weight_2; - let _e446 = weights; - weights = (_e446 + _e445); + let _e433 = probeWeight; + let _e434 = weight_2; + weight_2 = (_e434 * _e433); + } + let _e436 = unit; + param_118 = _e436; + let _e437 = ProbeOctahedral_u0028_vf3_u003b((¶m_118)); + let _e438 = irradianceCorner; + param_119 = _e438; + let _e439 = irradianceTile; + param_120 = _e439; + param_121 = _e437; + let _e440 = TileBilinear_u0028_vf2_u003b_f1_u003b_vf2_u003b((¶m_119), (¶m_120), (¶m_121)); + let _e442 = weight_2; + let _e444 = total_2; + total_2 = (_e444 + (_e440.xyz * _e442)); + let _e446 = weight_2; + let _e447 = weights; + weights = (_e447 + _e446); continue; } else { break; } continuing { - let _e448 = corner_1; - corner_1 = (_e448 + 1i); + let _e449 = corner_1; + corner_1 = (_e449 + 1i); } } - let _e450 = weights; - if (_e450 > 0f) { - let _e452 = total_2; - let _e453 = weights; - local_23 = (_e452 / vec3(_e453)); + let _e451 = weights; + if (_e451 > 0f) { + let _e453 = total_2; + let _e454 = weights; + local_23 = (_e453 / vec3(_e454)); } else { local_23 = vec3(0f, 0f, 0f); } - let _e456 = local_23; - return _e456; + let _e457 = local_23; + return _e457; } fn IrradianceEnabled_u0028_() -> bool { - let _e179 = irradiance_info.origin[3u]; - return (_e179 > 0.5f); + let _e180 = irradiance_info.origin[3u]; + return (_e180 > 0.5f); } fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_9: ptr) { @@ -23850,33 +23958,33 @@ fn ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_126: Surface; var param_127: Surface; - let _e184 = IrradianceEnabled_u0028_(); - if _e184 { - let _e185 = v_world_position_1; - param_122 = _e185; - let _e187 = (*s_9).n; - param_123 = _e187; - let _e189 = (*s_9).v; - param_124 = _e189; - let _e190 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_122), (¶m_123), (¶m_124)); - let _e193 = frag_info.material[2u]; - (*s_9).ambient = (_e190 * _e193); + let _e185 = IrradianceEnabled_u0028_(); + if _e185 { + let _e186 = v_world_position_1; + param_122 = _e186; + let _e188 = (*s_9).n; + param_123 = _e188; + let _e190 = (*s_9).v; + param_124 = _e190; + let _e191 = SampleIrradiance_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_122), (¶m_123), (¶m_124)); + let _e194 = frag_info.material[2u]; + (*s_9).ambient = (_e191 * _e194); } - let _e196 = (*s_9); - param_125 = _e196; + let _e197 = (*s_9); + param_125 = _e197; ApplyNormalMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_125)); - let _e197 = param_125; - (*s_9) = _e197; - let _e198 = (*s_9); - param_126 = _e198; + let _e198 = param_125; + (*s_9) = _e198; + let _e199 = (*s_9); + param_126 = _e199; ApplyOcclusionMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_126)); - let _e199 = param_126; - (*s_9) = _e199; - let _e200 = (*s_9); - param_127 = _e200; + let _e200 = param_126; + (*s_9) = _e200; + let _e201 = (*s_9); + param_127 = _e201; ApplyEmissiveMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_127)); - let _e201 = param_127; - (*s_9) = _e201; + let _e202 = param_127; + (*s_9) = _e202; return; } @@ -23889,78 +23997,78 @@ fn ReadSurface_u0028_() -> Surface { var anchored: vec3; var noise_1: f32; - let _e184 = v_texcoord_1; - let _e185 = MaterialLodBias_u0028_(); - let _e186 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e184, _e185); - texel_6 = _e186; - let _e187 = texel_6; - param_128 = _e187.xyz; - let _e189 = SrgbToLinear_u0028_vf3_u003b((¶m_128)); - let _e191 = frag_info.base_color; - param_129 = _e191.xyz; - let _e193 = SrgbToLinear_u0028_vf3_u003b((¶m_129)); - let _e195 = v_color_1; - s_10.albedo = ((_e189 * _e193) * _e195.xyz); - let _e200 = texel_6[3u]; - let _e203 = frag_info.base_color[3u]; - let _e206 = v_color_1[3u]; - s_10.alpha = ((_e200 * _e203) * _e206); - let _e210 = s_10.albedo; - g_albedo = _e210; - let _e213 = frag_info.material2_[0u]; - cutoff = _e213; - let _e214 = cutoff; - if (_e214 >= 0f) { - let _e217 = s_10.alpha; - let _e218 = cutoff; - if (_e217 < _e218) { + let _e185 = v_texcoord_1; + let _e186 = MaterialLodBias_u0028_(); + let _e187 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e185, _e186); + texel_6 = _e187; + let _e188 = texel_6; + param_128 = _e188.xyz; + let _e190 = SrgbToLinear_u0028_vf3_u003b((¶m_128)); + let _e192 = frag_info.base_color; + param_129 = _e192.xyz; + let _e194 = SrgbToLinear_u0028_vf3_u003b((¶m_129)); + let _e196 = v_color_1; + s_10.albedo = ((_e190 * _e194) * _e196.xyz); + let _e201 = texel_6[3u]; + let _e204 = frag_info.base_color[3u]; + let _e207 = v_color_1[3u]; + s_10.alpha = ((_e201 * _e204) * _e207); + let _e211 = s_10.albedo; + g_albedo = _e211; + let _e214 = frag_info.material2_[0u]; + cutoff = _e214; + let _e215 = cutoff; + if (_e215 >= 0f) { + let _e218 = s_10.alpha; + let _e219 = cutoff; + if (_e218 < _e219) { discard; } } else { - let _e220 = cutoff; - if (_e220 < -1.5f) { - let _e222 = v_world_position_1; - anchored = floor((_e222 * 16f)); - let _e225 = anchored; - noise_1 = fract((sin(dot(_e225, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e231 = s_10.alpha; - let _e232 = noise_1; - if (_e231 < _e232) { + let _e221 = cutoff; + if (_e221 < -1.5f) { + let _e223 = v_world_position_1; + anchored = floor((_e223 * 16f)); + let _e226 = anchored; + noise_1 = fract((sin(dot(_e226, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e232 = s_10.alpha; + let _e233 = noise_1; + if (_e232 < _e233) { discard; } } } - let _e234 = cutoff; - let _e236 = cutoff; - g_premultiply = ((_e234 < 0f) && (_e236 > -0.75f)); - let _e239 = v_normal_1; - s_10.n = normalize(_e239); - let _e242 = gl_FrontFacing_1; - if !(_e242) { - let _e245 = s_10.n; - s_10.n = -(_e245); - } - let _e249 = frag_info.camera_position; - let _e251 = v_world_position_1; - s_10.v = normalize((_e249.xyz - _e251)); - let _e256 = s_10.n; - let _e258 = s_10.v; - s_10.n_dot_v = max(dot(_e256, _e258), 0.0001f); - let _e264 = frag_info.material[0u]; - s_10.metallic = clamp(_e264, 0f, 1f); - let _e269 = frag_info.material[1u]; - s_10.roughness = clamp(_e269, 0.02f, 1f); - let _e273 = frag_info.ambient_ground; - let _e276 = frag_info.ambient_sky; - let _e280 = s_10.n[1u]; - let _e287 = frag_info.material[2u]; - s_10.ambient = (mix(_e273.xyz, _e276.xyz, vec3(((_e280 * 0.5f) + 0.5f))) * _e287); - let _e292 = frag_info.frame_params[0u]; - s_10.exposure = max(_e292, 0f); + let _e235 = cutoff; + let _e237 = cutoff; + g_premultiply = ((_e235 < 0f) && (_e237 > -0.75f)); + let _e240 = v_normal_1; + s_10.n = normalize(_e240); + let _e243 = gl_FrontFacing_1; + if !(_e243) { + let _e246 = s_10.n; + s_10.n = -(_e246); + } + let _e250 = frag_info.camera_position; + let _e252 = v_world_position_1; + s_10.v = normalize((_e250.xyz - _e252)); + let _e257 = s_10.n; + let _e259 = s_10.v; + s_10.n_dot_v = max(dot(_e257, _e259), 0.0001f); + let _e265 = frag_info.material[0u]; + s_10.metallic = clamp(_e265, 0f, 1f); + let _e270 = frag_info.material[1u]; + s_10.roughness = clamp(_e270, 0.02f, 1f); + let _e274 = frag_info.ambient_ground; + let _e277 = frag_info.ambient_sky; + let _e281 = s_10.n[1u]; + let _e288 = frag_info.material[2u]; + s_10.ambient = (mix(_e274.xyz, _e277.xyz, vec3(((_e281 * 0.5f) + 0.5f))) * _e288); + let _e293 = frag_info.frame_params[0u]; + s_10.exposure = max(_e293, 0f); s_10.occlusion = 1f; s_10.emissive = vec3(0f, 0f, 0f); - let _e297 = s_10; - return _e297; + let _e298 = s_10; + return _e298; } fn main_1() { @@ -23980,38 +24088,39 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e186 = ReadSurface_u0028_(); - s_11 = _e186; - let _e187 = s_11; - param_130 = _e187; + light_transmittance = vec3(1f, 1f, 1f); + let _e187 = ReadSurface_u0028_(); + s_11 = _e187; + let _e188 = s_11; + param_130 = _e188; ApplyCommonMaps_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_130)); - let _e188 = param_130; - s_11 = _e188; - let _e189 = s_11; - param_131 = _e189; + let _e189 = param_130; + s_11 = _e189; + let _e190 = s_11; + param_131 = _e190; ApplyMetallicRoughnessMap_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_131)); - let _e190 = param_131; - s_11 = _e190; - let _e192 = s_11.n_dot_v; - let _e197 = frag_info.material[3u]; - rim = (pow((1f - _e192), 3f) * _e197); - let _e200 = s_11.albedo; - let _e202 = s_11.ambient; - let _e203 = SampleLightmap_u0028_(); - let _e207 = s_11.occlusion; - ambient = ((_e200 * (_e202 + _e203)) * _e207); - let _e209 = s_11; - param_132 = _e209; - let _e210 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_132)); - let _e212 = s_11.occlusion; - let _e214 = ambient; - let _e216 = rim; - let _e221 = s_11.emissive; - param_133 = ((((_e210 * _e212) + _e214) + vec3((_e216 * 0.35f))) + _e221); - let _e224 = s_11.alpha; - param_134 = _e224; - let _e226 = s_11.roughness; - param_135 = _e226; + let _e191 = param_131; + s_11 = _e191; + let _e193 = s_11.n_dot_v; + let _e198 = frag_info.material[3u]; + rim = (pow((1f - _e193), 3f) * _e198); + let _e201 = s_11.albedo; + let _e203 = s_11.ambient; + let _e204 = SampleLightmap_u0028_(); + let _e208 = s_11.occlusion; + ambient = ((_e201 * (_e203 + _e204)) * _e208); + let _e210 = s_11; + param_132 = _e210; + let _e211 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_132)); + let _e213 = s_11.occlusion; + let _e215 = ambient; + let _e217 = rim; + let _e222 = s_11.emissive; + param_133 = ((((_e211 * _e213) + _e215) + vec3((_e217 * 0.35f))) + _e222); + let _e225 = s_11.alpha; + param_134 = _e225; + let _e227 = s_11.roughness; + param_135 = _e227; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_133), (¶m_134), (¶m_135)); return; } @@ -39374,15 +39483,115 @@ fn main(@location(4) v_texcoord: vec2, @location(6) v_world_position: vec3< ], ), ], - samplers: [ - WebGpuSampler( - name: 'base_color_texture', - group: 1, - textureBinding: 2, - samplerBinding: 3, - dimension: WebGpuTextureDimension.twoDimensional, - ), - ], + samplers: [ + WebGpuSampler( + name: 'base_color_texture', + group: 1, + textureBinding: 2, + samplerBinding: 3, + dimension: WebGpuTextureDimension.twoDimensional, + ), + ], + ), + 'ShadowTransmittance': WebGpuStage( + wgsl: r''' +struct TransmittanceInfo { + color: vec4, + light: vec4, + params: vec4, +} + +var g_albedo: vec3; +var g_debug_surface: vec3; +var g_debug_surface_on: bool; +var g_premultiply: bool; +@group(1) @binding(0) +var transmittance_info: TransmittanceInfo; +var v_normal_1: vec3; +var frag_color: vec4; +var v_world_position_1: vec3; +var v_texcoord_1: vec2; +var v_tangent_1: vec4; +var v_color_1: vec4; +var v_lightmap_uv_1: vec2; + +fn main_1() { + var opacity: f32; + var transmission: f32; + var body: vec3; + var facing: f32; + var f0_: f32; + var fresnel: f32; + var through: vec3; + + g_albedo = vec3(0f, 0f, 0f); + g_debug_surface = vec3(0f, 0f, 0f); + g_debug_surface_on = false; + g_premultiply = false; + let _e34 = transmittance_info.color[3u]; + opacity = clamp(_e34, 0f, 1f); + let _e38 = transmittance_info.params[0u]; + transmission = clamp(_e38, 0f, 1f); + let _e40 = opacity; + let _e43 = opacity; + let _e44 = transmission; + let _e47 = transmittance_info.color; + body = (vec3((1f - _e40)) + (clamp(_e47.xyz, vec3(0f), vec3(1f)) * (_e43 * _e44))); + let _e54 = v_normal_1; + let _e57 = transmittance_info.light; + facing = abs(dot(normalize(_e54), _e57.xyz)); + let _e63 = transmittance_info.params[1u]; + f0_ = clamp(_e63, 0f, 1f); + let _e65 = f0_; + let _e66 = f0_; + let _e68 = facing; + fresnel = (_e65 + ((1f - _e66) * pow((1f - clamp(_e68, 0f, 1f)), 5f))); + let _e74 = body; + let _e77 = fresnel; + through = (sqrt(max(_e74, vec3(0f, 0f, 0f))) * (1f - _e77)); + let _e81 = through[0u]; + let _e84 = through[1u]; + let _e87 = through[2u]; + frag_color = vec4(0f, (1f - _e81), (1f - _e84), _e87); + return; +} + +@fragment +fn main(@location(2) v_normal: vec3, @location(6) v_world_position: vec3, @location(4) v_texcoord: vec2, @location(3) v_tangent: vec4, @location(0) v_color: vec4, @location(1) v_lightmap_uv: vec2) -> @location(0) vec4 { + v_normal_1 = v_normal; + v_world_position_1 = v_world_position; + v_texcoord_1 = v_texcoord; + v_tangent_1 = v_tangent; + v_color_1 = v_color; + v_lightmap_uv_1 = v_lightmap_uv; + main_1(); + let _e13 = frag_color; + return _e13; +} +''', + attributes: [], + blocks: [ + WebGpuBlock( + name: 'TransmittanceInfo', + group: 1, + binding: 0, + sizeInBytes: 48, + members: [ + WebGpuBlockMember(name: 'color', offsetInBytes: 0, sizeInBytes: 16), + WebGpuBlockMember( + name: 'light', + offsetInBytes: 16, + sizeInBytes: 16, + ), + WebGpuBlockMember( + name: 'params', + offsetInBytes: 32, + sizeInBytes: 16, + ), + ], + ), + ], + samplers: [], ), 'ShadowCopy': WebGpuStage( wgsl: r''' @@ -41120,6 +41329,7 @@ var g_debug_surface_on: bool; var g_premultiply: bool; var g_cluster_offset: f32; var g_cluster_count: f32; +var light_transmittance: vec3; var v_world_position_1: vec3; @group(1) @binding(0) var fog_info: FogInfo; @@ -41165,10 +41375,10 @@ var v_tangent_1: vec4; var v_lightmap_uv_1: vec2; fn ViewDepth_u0028_() -> f32 { - let _e166 = v_world_position_1; - let _e168 = fog_info.eye; - let _e172 = fog_info.forward; - return dot((_e166 - _e168.xyz), _e172.xyz); + let _e167 = v_world_position_1; + let _e169 = fog_info.eye; + let _e173 = fog_info.forward; + return dot((_e167 - _e169.xyz), _e173.xyz); } fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { @@ -41177,22 +41387,22 @@ fn WeightedBlendedWeight_u0028_f1_u003b(alpha: ptr) -> f32 { var far: f32; var far3_: f32; - let _e171 = ViewDepth_u0028_(); - z = abs(_e171); - let _e173 = z; - near = (_e173 / 5f); - let _e175 = z; - far = (_e175 / 200f); - let _e177 = far; + let _e172 = ViewDepth_u0028_(); + z = abs(_e172); + let _e174 = z; + near = (_e174 / 5f); + let _e176 = z; + far = (_e176 / 200f); let _e178 = far; - let _e180 = far; - far3_ = ((_e177 * _e178) * _e180); - let _e182 = (*alpha); - let _e183 = near; + let _e179 = far; + let _e181 = far; + far3_ = ((_e178 * _e179) * _e181); + let _e183 = (*alpha); let _e184 = near; - let _e187 = far3_; + let _e185 = near; let _e188 = far3_; - return (_e182 * clamp((10f / ((0.00001f + (_e183 * _e184)) + (_e187 * _e188))), 0.01f, 3000f)); + let _e189 = far3_; + return (_e183 * clamp((10f / ((0.00001f + (_e184 * _e185)) + (_e188 * _e189))), 0.01f, 3000f)); } fn WriteWeightedBlended_u0028_() { @@ -41204,39 +41414,39 @@ fn WriteWeightedBlended_u0028_() { var revealage: bool; var local: vec4; - let _e175 = fog_info.forward[3u]; - mode = _e175; - let _e176 = mode; - if (_e176 > 0.5f) { - let _e179 = frag_color[3u]; - alpha_1 = _e179; - let _e180 = alpha_1; - param = _e180; - let _e181 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); - weight = _e181; - let _e182 = frag_color; - let _e184 = weight; - let _e185 = (_e182.xyz * _e184); - let _e186 = alpha_1; - let _e187 = weight; - accumulate = vec4(_e185.x, _e185.y, _e185.z, (_e186 * _e187)); - let _e193 = mode; - let _e195 = mode; - revealage = ((_e193 > 1.5f) && (_e195 < 2.5f)); - let _e198 = revealage; - if _e198 { - let _e199 = alpha_1; - local = vec4(_e199); + let _e176 = fog_info.forward[3u]; + mode = _e176; + let _e177 = mode; + if (_e177 > 0.5f) { + let _e180 = frag_color[3u]; + alpha_1 = _e180; + let _e181 = alpha_1; + param = _e181; + let _e182 = WeightedBlendedWeight_u0028_f1_u003b((¶m)); + weight = _e182; + let _e183 = frag_color; + let _e185 = weight; + let _e186 = (_e183.xyz * _e185); + let _e187 = alpha_1; + let _e188 = weight; + accumulate = vec4(_e186.x, _e186.y, _e186.z, (_e187 * _e188)); + let _e194 = mode; + let _e196 = mode; + revealage = ((_e194 > 1.5f) && (_e196 < 2.5f)); + let _e199 = revealage; + if _e199 { + let _e200 = alpha_1; + local = vec4(_e200); } else { - let _e201 = accumulate; - local = _e201; + let _e202 = accumulate; + local = _e202; } - let _e202 = local; - frag_color = _e202; - let _e203 = mode; - if (_e203 > 2.5f) { - let _e205 = alpha_1; - frag_surface = vec4(_e205); + let _e203 = local; + frag_color = _e203; + let _e204 = mode; + if (_e204 > 2.5f) { + let _e206 = alpha_1; + frag_surface = vec4(_e206); } } return; @@ -41245,29 +41455,29 @@ fn WriteWeightedBlended_u0028_() { fn EncodeOctahedral_u0028_vf3_u003b(n: ptr>) -> vec2 { var e: vec2; - let _e169 = (*n)[0u]; - let _e172 = (*n)[1u]; - let _e176 = (*n)[2u]; - let _e179 = (*n); - (*n) = (_e179 / vec3(((abs(_e169) + abs(_e172)) + abs(_e176)))); - let _e182 = (*n); - e = _e182.xy; - let _e185 = (*n)[2u]; - if (_e185 < 0f) { - let _e187 = (*n); - let _e193 = (*n)[0u]; - let _e197 = (*n)[1u]; - e = ((vec2(1f) - abs(_e187.yx)) * vec2(select(-1f, 1f, (_e193 >= 0f)), select(-1f, 1f, (_e197 >= 0f)))); + let _e170 = (*n)[0u]; + let _e173 = (*n)[1u]; + let _e177 = (*n)[2u]; + let _e180 = (*n); + (*n) = (_e180 / vec3(((abs(_e170) + abs(_e173)) + abs(_e177)))); + let _e183 = (*n); + e = _e183.xy; + let _e186 = (*n)[2u]; + if (_e186 < 0f) { + let _e188 = (*n); + let _e194 = (*n)[0u]; + let _e198 = (*n)[1u]; + e = ((vec2(1f) - abs(_e188.yx)) * vec2(select(-1f, 1f, (_e194 >= 0f)), select(-1f, 1f, (_e198 >= 0f)))); } - let _e202 = e; - return ((_e202 * 0.5f) + vec2(0.5f)); + let _e203 = e; + return ((_e203 * 0.5f) + vec2(0.5f)); } fn LinearToSrgb_u0028_vf3_u003b(linear: ptr>) -> vec3 { - let _e167 = (*linear); - let _e169 = (*linear); - let _e173 = (*linear); - return mix((_e167 * 12.92f), ((pow(_e169, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e173)); + let _e168 = (*linear); + let _e170 = (*linear); + let _e174 = (*linear); + return mix((_e168 * 12.92f), ((pow(_e170, vec3(0.41666666f, 0.41666666f, 0.41666666f)) * 1.055f) - vec3(0.055f, 0.055f, 0.055f)), step(vec3(0.0031308f, 0.0031308f, 0.0031308f), _e174)); } fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { @@ -41275,57 +41485,57 @@ fn WriteSurfaceGeometry_u0028_f1_u003b(roughness: ptr) { var geometric: vec3; var param_2: vec3; - let _e170 = g_albedo; - param_1 = clamp(_e170, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); - let _e172 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); - frag_albedo = vec4(_e172.x, _e172.y, _e172.z, 1f); - let _e177 = g_debug_surface_on; - if _e177 { - let _e178 = g_debug_surface; - let _e179 = ViewDepth_u0028_(); - frag_surface = vec4(_e178.x, _e178.y, _e178.z, _e179); + let _e171 = g_albedo; + param_1 = clamp(_e171, vec3(0f, 0f, 0f), vec3(1f, 1f, 1f)); + let _e173 = LinearToSrgb_u0028_vf3_u003b((¶m_1)); + frag_albedo = vec4(_e173.x, _e173.y, _e173.z, 1f); + let _e178 = g_debug_surface_on; + if _e178 { + let _e179 = g_debug_surface; + let _e180 = ViewDepth_u0028_(); + frag_surface = vec4(_e179.x, _e179.y, _e179.z, _e180); return; } - let _e184 = v_normal_1; - geometric = normalize(_e184); - let _e186 = gl_FrontFacing_1; - if !(_e186) { - let _e188 = geometric; - geometric = -(_e188); - } - let _e190 = geometric; - param_2 = _e190; - let _e191 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); - let _e192 = (*roughness); - let _e194 = ViewDepth_u0028_(); - frag_surface = vec4(_e191.x, _e191.y, clamp(_e192, 0f, 1f), _e194); + let _e185 = v_normal_1; + geometric = normalize(_e185); + let _e187 = gl_FrontFacing_1; + if !(_e187) { + let _e189 = geometric; + geometric = -(_e189); + } + let _e191 = geometric; + param_2 = _e191; + let _e192 = EncodeOctahedral_u0028_vf3_u003b((¶m_2)); + let _e193 = (*roughness); + let _e195 = ViewDepth_u0028_(); + frag_surface = vec4(_e192.x, _e192.y, clamp(_e193, 0f, 1f), _e195); return; } fn EyeDistance_u0028_() -> f32 { - let _e166 = v_world_position_1; - let _e168 = fog_info.eye; - return distance(_e166, _e168.xyz); + let _e167 = v_world_position_1; + let _e169 = fog_info.eye; + return distance(_e167, _e169.xyz); } fn ApplyFog_u0028_vf3_u003b(color: ptr>) -> vec3 { var density: f32; var d: f32; - let _e171 = fog_info.fog[3u]; - density = _e171; - let _e172 = density; - if (_e172 <= 0f) { - let _e174 = (*color); - return _e174; - } - let _e175 = EyeDistance_u0028_(); - d = _e175; - let _e177 = fog_info.fog; - let _e179 = (*color); - let _e180 = density; - let _e182 = d; - return mix(_e177.xyz, _e179, vec3(clamp(exp((-(_e180) * _e182)), 0f, 1f))); + let _e172 = fog_info.fog[3u]; + density = _e172; + let _e173 = density; + if (_e173 <= 0f) { + let _e175 = (*color); + return _e175; + } + let _e176 = EyeDistance_u0028_(); + d = _e176; + let _e178 = fog_info.fog; + let _e180 = (*color); + let _e181 = density; + let _e183 = d; + return mix(_e178.xyz, _e180, vec3(clamp(exp((-(_e181) * _e183)), 0f, 1f))); } fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr>, alpha_2: ptr, roughness_1: ptr) { @@ -41333,55 +41543,55 @@ fn WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b(linearColor: ptr; var param_4: f32; - let _e172 = g_premultiply; - let _e173 = (*alpha_2); - weight_1 = select(1f, _e173, _e172); - let _e175 = (*linearColor); - param_3 = _e175; - let _e176 = ApplyFog_u0028_vf3_u003b((¶m_3)); - let _e177 = weight_1; - let _e178 = (_e176 * _e177); - let _e179 = (*alpha_2); - frag_color = vec4(_e178.x, _e178.y, _e178.z, _e179); - let _e184 = (*roughness_1); - param_4 = _e184; + let _e173 = g_premultiply; + let _e174 = (*alpha_2); + weight_1 = select(1f, _e174, _e173); + let _e176 = (*linearColor); + param_3 = _e176; + let _e177 = ApplyFog_u0028_vf3_u003b((¶m_3)); + let _e178 = weight_1; + let _e179 = (_e177 * _e178); + let _e180 = (*alpha_2); + frag_color = vec4(_e179.x, _e179.y, _e179.z, _e180); + let _e185 = (*roughness_1); + param_4 = _e185; WriteSurfaceGeometry_u0028_f1_u003b((¶m_4)); WriteWeightedBlended_u0028_(); return; } fn ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b(s: ptr, light: ptr) -> vec3 { - let _e169 = (*s).albedo; - return _e169; + let _e170 = (*s).albedo; + return _e170; } fn PointShadowDiskTap_u0028_i1_u003b(i: ptr) -> vec2 { - let _e167 = (*i); - if (_e167 == 0i) { + let _e168 = (*i); + if (_e168 == 0i) { return vec2(-0.94201624f, -0.39906216f); } - let _e169 = (*i); - if (_e169 == 1i) { + let _e170 = (*i); + if (_e170 == 1i) { return vec2(0.9455861f, -0.76890725f); } - let _e171 = (*i); - if (_e171 == 2i) { + let _e172 = (*i); + if (_e172 == 2i) { return vec2(-0.0941841f, -0.9293887f); } - let _e173 = (*i); - if (_e173 == 3i) { + let _e174 = (*i); + if (_e174 == 3i) { return vec2(0.34495938f, 0.2938776f); } - let _e175 = (*i); - if (_e175 == 4i) { + let _e176 = (*i); + if (_e176 == 4i) { return vec2(-0.9158858f, 0.45771432f); } - let _e177 = (*i); - if (_e177 == 5i) { + let _e178 = (*i); + if (_e178 == 5i) { return vec2(-0.8154423f, -0.87912464f); } - let _e179 = (*i); - if (_e179 == 6i) { + let _e180 = (*i); + if (_e180 == 6i) { return vec2(-0.38277543f, 0.27676845f); } return vec2(0.974844f, 0.7564838f); @@ -41391,20 +41601,20 @@ fn PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b(i_1: ptr; var param_5: i32; - let _e172 = (*i_1); - param_5 = _e172; - let _e173 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); - p = _e173; - let _e175 = p[0u]; - let _e176 = (*ca); - let _e179 = p[1u]; - let _e180 = (*sa); - let _e184 = p[0u]; - let _e185 = (*sa); - let _e188 = p[1u]; - let _e189 = (*ca); - let _e193 = (*radius); - return (vec2(((_e175 * _e176) - (_e179 * _e180)), ((_e184 * _e185) + (_e188 * _e189))) * _e193); + let _e173 = (*i_1); + param_5 = _e173; + let _e174 = PointShadowDiskTap_u0028_i1_u003b((¶m_5)); + p = _e174; + let _e176 = p[0u]; + let _e177 = (*ca); + let _e180 = p[1u]; + let _e181 = (*sa); + let _e185 = p[0u]; + let _e186 = (*sa); + let _e189 = p[1u]; + let _e190 = (*ca); + let _e194 = (*radius); + return (vec2(((_e176 * _e177) - (_e180 * _e181)), ((_e185 * _e186) + (_e189 * _e190))) * _e194); } fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr>, offset: ptr>, tile: ptr>, range: ptr) -> f32 { @@ -41412,27 +41622,27 @@ fn PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b(uv: ptr; var atlas: vec2; - let _e175 = point_shadow.params[0u]; - inset = _e175; - let _e176 = (*uv); - let _e177 = (*offset); - let _e179 = inset; + let _e176 = point_shadow.params[0u]; + inset = _e176; + let _e177 = (*uv); + let _e178 = (*offset); let _e180 = inset; - local_1 = clamp((_e176 + _e177), vec2(_e179), vec2((1f - _e180))); - let _e185 = local_1; - let _e186 = (*tile); - atlas = ((_e185 + _e186) * vec2(0.16666667f, 0.16666667f)); - let _e191 = point_shadow.params3_[0u]; - if (_e191 > 0.5f) { - let _e194 = atlas[1u]; - atlas[1u] = (1f - _e194); - } - let _e197 = atlas; - let _e198 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e197, 0f); - let _e200 = atlas; - let _e201 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e200, 0f); - let _e204 = (*range); - return (min(_e198.x, _e201.x) * _e204); + let _e181 = inset; + local_1 = clamp((_e177 + _e178), vec2(_e180), vec2((1f - _e181))); + let _e186 = local_1; + let _e187 = (*tile); + atlas = ((_e186 + _e187) * vec2(0.16666667f, 0.16666667f)); + let _e192 = point_shadow.params3_[0u]; + if (_e192 > 0.5f) { + let _e195 = atlas[1u]; + atlas[1u] = (1f - _e195); + } + let _e198 = atlas; + let _e199 = textureSampleLevel(point_shadow_texture_tex, point_shadow_texture_smp, _e198, 0f); + let _e201 = atlas; + let _e202 = textureSampleLevel(point_shadow_static_texture_tex, point_shadow_static_texture_smp, _e201, 0f); + let _e205 = (*range); + return (min(_e199.x, _e202.x) * _e205); } fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: ptr>, offset_1: ptr>, tile_1: ptr>, range_1: ptr, receiver: ptr) -> f32 { @@ -41442,24 +41652,24 @@ fn PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b(uv_1: pt var param_8: vec2; var param_9: f32; - let _e176 = (*uv_1); - param_6 = _e176; - let _e177 = (*offset_1); - param_7 = _e177; - let _e178 = (*tile_1); - param_8 = _e178; - let _e179 = (*range_1); - param_9 = _e179; - let _e180 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); - stored = _e180; - let _e181 = stored; - let _e182 = (*range_1); - if (_e181 >= (_e182 * 0.999f)) { + let _e177 = (*uv_1); + param_6 = _e177; + let _e178 = (*offset_1); + param_7 = _e178; + let _e179 = (*tile_1); + param_8 = _e179; + let _e180 = (*range_1); + param_9 = _e180; + let _e181 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_6), (¶m_7), (¶m_8), (¶m_9)); + stored = _e181; + let _e182 = stored; + let _e183 = (*range_1); + if (_e182 >= (_e183 * 0.999f)) { return 1f; } - let _e185 = (*receiver); - let _e186 = stored; - return select(1f, 0f, (_e185 > _e186)); + let _e186 = (*receiver); + let _e187 = stored; + return select(1f, 0f, (_e186 > _e187)); } fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b(uv_2: ptr>, tile_2: ptr>, range_2: ptr, receiver_1: ptr, ca_1: ptr, sa_1: ptr, tanHalf: ptr, blockerOut: ptr) -> f32 { @@ -41486,111 +41696,111 @@ fn PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u var world: f32; (*blockerOut) = -1f; - let _e197 = point_shadow.params2_[1u]; - lightRadius = _e197; - let _e200 = point_shadow.params2_[0u]; - minRadius = _e200; - let _e203 = point_shadow.params2_[2u]; - maxRadius = _e203; - let _e204 = lightRadius; - if (_e204 <= 0f) { - let _e206 = (*uv_2); - param_10 = _e206; + let _e198 = point_shadow.params2_[1u]; + lightRadius = _e198; + let _e201 = point_shadow.params2_[0u]; + minRadius = _e201; + let _e204 = point_shadow.params2_[2u]; + maxRadius = _e204; + let _e205 = lightRadius; + if (_e205 <= 0f) { + let _e207 = (*uv_2); + param_10 = _e207; param_11 = vec2(0f, 0f); - let _e207 = (*tile_2); - param_12 = _e207; - let _e208 = (*range_2); - param_13 = _e208; - let _e209 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); - (*blockerOut) = _e209; - let _e210 = minRadius; - return _e210; + let _e208 = (*tile_2); + param_12 = _e208; + let _e209 = (*range_2); + param_13 = _e209; + let _e210 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_10), (¶m_11), (¶m_12), (¶m_13)); + (*blockerOut) = _e210; + let _e211 = minRadius; + return _e211; } sum = 0f; count = 0f; i_2 = 0i; loop { - let _e211 = i_2; - if (_e211 < 8i) { - let _e213 = i_2; - param_14 = _e213; - let _e214 = (*ca_1); - param_15 = _e214; - let _e215 = (*sa_1); - param_16 = _e215; - let _e216 = maxRadius; - param_17 = _e216; - let _e217 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); - let _e218 = (*uv_2); - param_18 = _e218; - param_19 = _e217; - let _e219 = (*tile_2); - param_20 = _e219; - let _e220 = (*range_2); - param_21 = _e220; - let _e221 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); - stored_1 = _e221; - let _e222 = stored_1; - let _e223 = (*range_2); - if (_e222 >= (_e223 * 0.999f)) { + let _e212 = i_2; + if (_e212 < 8i) { + let _e214 = i_2; + param_14 = _e214; + let _e215 = (*ca_1); + param_15 = _e215; + let _e216 = (*sa_1); + param_16 = _e216; + let _e217 = maxRadius; + param_17 = _e217; + let _e218 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_14), (¶m_15), (¶m_16), (¶m_17)); + let _e219 = (*uv_2); + param_18 = _e219; + param_19 = _e218; + let _e220 = (*tile_2); + param_20 = _e220; + let _e221 = (*range_2); + param_21 = _e221; + let _e222 = PointShadowDistance_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b((¶m_18), (¶m_19), (¶m_20), (¶m_21)); + stored_1 = _e222; + let _e223 = stored_1; + let _e224 = (*range_2); + if (_e223 >= (_e224 * 0.999f)) { continue; } - let _e226 = stored_1; - let _e227 = (*receiver_1); - if (_e226 >= _e227) { + let _e227 = stored_1; + let _e228 = (*receiver_1); + if (_e227 >= _e228) { continue; } - let _e229 = stored_1; - let _e230 = sum; - sum = (_e230 + _e229); - let _e232 = count; - count = (_e232 + 1f); + let _e230 = stored_1; + let _e231 = sum; + sum = (_e231 + _e230); + let _e233 = count; + count = (_e233 + 1f); continue; } else { break; } continuing { - let _e234 = i_2; - i_2 = (_e234 + 1i); + let _e235 = i_2; + i_2 = (_e235 + 1i); } } - let _e236 = count; - if (_e236 < 0.5f) { + let _e237 = count; + if (_e237 < 0.5f) { return -1f; } - let _e238 = sum; - let _e239 = count; - blocker = max((_e238 / _e239), 0.0001f); - let _e242 = blocker; - (*blockerOut) = _e242; - let _e243 = lightRadius; - let _e244 = (*receiver_1); - let _e245 = blocker; - let _e249 = blocker; - world = ((_e243 * max((_e244 - _e245), 0f)) / _e249); - let _e251 = world; - let _e252 = (*receiver_1); - let _e254 = (*tanHalf); - let _e257 = minRadius; - let _e258 = maxRadius; - return clamp((_e251 / ((2f * _e252) * _e254)), _e257, _e258); + let _e239 = sum; + let _e240 = count; + blocker = max((_e239 / _e240), 0.0001f); + let _e243 = blocker; + (*blockerOut) = _e243; + let _e244 = lightRadius; + let _e245 = (*receiver_1); + let _e246 = blocker; + let _e250 = blocker; + world = ((_e244 * max((_e245 - _e246), 0f)) / _e250); + let _e252 = world; + let _e253 = (*receiver_1); + let _e255 = (*tanHalf); + let _e258 = minRadius; + let _e259 = maxRadius; + return clamp((_e252 / ((2f * _e253) * _e255)), _e258, _e259); } fn FragCoordFromTop_u0028_f1_u003b(rows: ptr) -> vec2 { var local_2: vec2; - let _e168 = (*rows); - if (_e168 > 0f) { - let _e171 = gl_FragCoord_1[0u]; - let _e172 = (*rows); - let _e174 = gl_FragCoord_1[1u]; - local_2 = vec2(_e171, (_e172 - _e174)); + let _e169 = (*rows); + if (_e169 > 0f) { + let _e172 = gl_FragCoord_1[0u]; + let _e173 = (*rows); + let _e175 = gl_FragCoord_1[1u]; + local_2 = vec2(_e172, (_e173 - _e175)); } else { - let _e177 = gl_FragCoord_1; - local_2 = _e177.xy; + let _e178 = gl_FragCoord_1; + local_2 = _e178.xy; } - let _e179 = local_2; - return _e179; + let _e180 = local_2; + return _e180; } fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr>, normal: ptr>, lightIndex: ptr) -> f32 { @@ -41645,260 +41855,260 @@ fn PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b(world_1: ptr; var param_43: f32; var param_44: f32; - var phi_2339_: bool; - var phi_2400_: bool; - - let _e220 = (*lightIndex); - let _e224 = point_shadow.slots[_e220][0u]; - slot = i32((_e224 + 0.5f)); - let _e227 = (*lightIndex); - let _e231 = point_shadow.slots[_e227][0u]; - if (_e231 < 0f) { + var phi_2340_: bool; + var phi_2401_: bool; + + let _e221 = (*lightIndex); + let _e225 = point_shadow.slots[_e221][0u]; + slot = i32((_e225 + 0.5f)); + let _e228 = (*lightIndex); + let _e232 = point_shadow.slots[_e228][0u]; + if (_e232 < 0f) { return 1f; } - let _e235 = point_shadow.params[2u]; - strength = _e235; - let _e236 = strength; - if (_e236 <= 0f) { + let _e236 = point_shadow.params[2u]; + strength = _e236; + let _e237 = strength; + if (_e237 <= 0f) { return 1f; } - let _e238 = slot; - let _e241 = point_shadow.lights[_e238]; - let _e243 = (*world_1); - toLight = (_e241.xyz - _e243); - let _e245 = toLight; - toLightLength = max(length(_e245), 0.000001f); - let _e248 = (*normal); - let _e249 = toLight; - let _e250 = toLightLength; - nDotL = max(dot(_e248, (_e249 / vec3(_e250))), 0.15f); - let _e255 = nDotL; + let _e239 = slot; + let _e242 = point_shadow.lights[_e239]; + let _e244 = (*world_1); + toLight = (_e242.xyz - _e244); + let _e246 = toLight; + toLightLength = max(length(_e246), 0.000001f); + let _e249 = (*normal); + let _e250 = toLight; + let _e251 = toLightLength; + nDotL = max(dot(_e249, (_e250 / vec3(_e251))), 0.15f); let _e256 = nDotL; - let _e261 = nDotL; + let _e257 = nDotL; let _e262 = nDotL; - slope = min((sqrt(max((1f - (_e255 * _e256)), 0f)) / (_e261 * _e262)), 8f); - let _e266 = toLightLength; - let _e268 = (*lightIndex); - let _e272 = point_shadow.slots[_e268][2u]; - let _e277 = point_shadow.params3_[1u]; - texel = (((2f * _e266) * max(_e272, 0.0001f)) * _e277); - let _e279 = (*world_1); - let _e280 = (*normal); - let _e281 = texel; - let _e285 = point_shadow.params[3u]; - let _e287 = slope; - origin = (_e279 + (((_e280 * _e281) * _e285) * (1f + _e287))); - let _e291 = origin; - let _e292 = slot; - let _e295 = point_shadow.lights[_e292]; - toFragment = (_e291 - _e295.xyz); - let _e298 = toFragment; - distance_ = length(_e298); - let _e300 = slot; - let _e304 = point_shadow.lights[_e300][3u]; - range_3 = max(_e304, 0.0001f); - let _e306 = distance_; - let _e307 = range_3; - if (_e306 >= _e307) { + let _e263 = nDotL; + slope = min((sqrt(max((1f - (_e256 * _e257)), 0f)) / (_e262 * _e263)), 8f); + let _e267 = toLightLength; + let _e269 = (*lightIndex); + let _e273 = point_shadow.slots[_e269][2u]; + let _e278 = point_shadow.params3_[1u]; + texel = (((2f * _e267) * max(_e273, 0.0001f)) * _e278); + let _e280 = (*world_1); + let _e281 = (*normal); + let _e282 = texel; + let _e286 = point_shadow.params[3u]; + let _e288 = slope; + origin = (_e280 + (((_e281 * _e282) * _e286) * (1f + _e288))); + let _e292 = origin; + let _e293 = slot; + let _e296 = point_shadow.lights[_e293]; + toFragment = (_e292 - _e296.xyz); + let _e299 = toFragment; + distance_ = length(_e299); + let _e301 = slot; + let _e305 = point_shadow.lights[_e301][3u]; + range_3 = max(_e305, 0.0001f); + let _e307 = distance_; + let _e308 = range_3; + if (_e307 >= _e308) { return 1f; } face = 0i; - let _e309 = (*lightIndex); - let _e313 = point_shadow.slots[_e309][1u]; - if (_e313 < 0.5f) { - let _e315 = toFragment; - a = abs(_e315); - let _e318 = a[0u]; - let _e320 = a[1u]; - let _e321 = (_e318 >= _e320); - phi_2339_ = _e321; - if _e321 { - let _e323 = a[0u]; - let _e325 = a[2u]; - phi_2339_ = (_e323 >= _e325); - } - let _e328 = phi_2339_; - if _e328 { - let _e330 = toFragment[0u]; - face = select(1i, 0i, (_e330 > 0f)); + let _e310 = (*lightIndex); + let _e314 = point_shadow.slots[_e310][1u]; + if (_e314 < 0.5f) { + let _e316 = toFragment; + a = abs(_e316); + let _e319 = a[0u]; + let _e321 = a[1u]; + let _e322 = (_e319 >= _e321); + phi_2340_ = _e322; + if _e322 { + let _e324 = a[0u]; + let _e326 = a[2u]; + phi_2340_ = (_e324 >= _e326); + } + let _e329 = phi_2340_; + if _e329 { + let _e331 = toFragment[0u]; + face = select(1i, 0i, (_e331 > 0f)); } else { - let _e334 = a[1u]; - let _e336 = a[2u]; - if (_e334 >= _e336) { - let _e339 = toFragment[1u]; - face = select(3i, 2i, (_e339 > 0f)); + let _e335 = a[1u]; + let _e337 = a[2u]; + if (_e335 >= _e337) { + let _e340 = toFragment[1u]; + face = select(3i, 2i, (_e340 > 0f)); } else { - let _e343 = toFragment[2u]; - face = select(5i, 4i, (_e343 > 0f)); + let _e344 = toFragment[2u]; + face = select(5i, 4i, (_e344 > 0f)); } } } - let _e346 = slot; - let _e348 = face; - let _e352 = point_shadow.faces[((_e346 * 6i) + _e348)]; - let _e353 = origin; - clip = (_e352 * vec4(_e353.x, _e353.y, _e353.z, 1f)); - let _e360 = clip[3u]; - if (_e360 <= 0f) { + let _e347 = slot; + let _e349 = face; + let _e353 = point_shadow.faces[((_e347 * 6i) + _e349)]; + let _e354 = origin; + clip = (_e353 * vec4(_e354.x, _e354.y, _e354.z, 1f)); + let _e361 = clip[3u]; + if (_e361 <= 0f) { return 1f; } - let _e362 = clip; - let _e365 = clip[3u]; - ndc = (_e362.xy / vec2(_e365)); - let _e369 = ndc[0u]; - let _e371 = (abs(_e369) > 1f); - phi_2400_ = _e371; - if !(_e371) { - let _e374 = ndc[1u]; - phi_2400_ = (abs(_e374) > 1f); - } - let _e378 = phi_2400_; - if _e378 { + let _e363 = clip; + let _e366 = clip[3u]; + ndc = (_e363.xy / vec2(_e366)); + let _e370 = ndc[0u]; + let _e372 = (abs(_e370) > 1f); + phi_2401_ = _e372; + if !(_e372) { + let _e375 = ndc[1u]; + phi_2401_ = (abs(_e375) > 1f); + } + let _e379 = phi_2401_; + if _e379 { return 1f; } - let _e380 = ndc[0u]; - let _e384 = ndc[1u]; - uv_3 = vec2(((_e380 * 0.5f) + 0.5f), (0.5f - (_e384 * 0.5f))); - let _e388 = face; - let _e390 = slot; - tile_3 = vec2(f32(_e388), f32(_e390)); - let _e393 = distance_; - let _e396 = point_shadow.params[1u]; - receiver_2 = (_e393 - _e396); - let _e400 = frag_info.target_origin[0u]; - param_22 = _e400; - let _e401 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); - noise = fract((52.982918f * fract(dot(_e401, vec2(0.06711056f, 0.00583715f))))); - let _e406 = noise; - angle = (_e406 * 6.2831855f); - let _e408 = angle; - ca_2 = cos(_e408); - let _e410 = angle; - sa_2 = sin(_e410); - let _e412 = (*lightIndex); - let _e416 = point_shadow.slots[_e412][2u]; - tanHalf_1 = max(_e416, 0.0001f); + let _e381 = ndc[0u]; + let _e385 = ndc[1u]; + uv_3 = vec2(((_e381 * 0.5f) + 0.5f), (0.5f - (_e385 * 0.5f))); + let _e389 = face; + let _e391 = slot; + tile_3 = vec2(f32(_e389), f32(_e391)); + let _e394 = distance_; + let _e397 = point_shadow.params[1u]; + receiver_2 = (_e394 - _e397); + let _e401 = frag_info.target_origin[0u]; + param_22 = _e401; + let _e402 = FragCoordFromTop_u0028_f1_u003b((¶m_22)); + noise = fract((52.982918f * fract(dot(_e402, vec2(0.06711056f, 0.00583715f))))); + let _e407 = noise; + angle = (_e407 * 6.2831855f); + let _e409 = angle; + ca_2 = cos(_e409); + let _e411 = angle; + sa_2 = sin(_e411); + let _e413 = (*lightIndex); + let _e417 = point_shadow.slots[_e413][2u]; + tanHalf_1 = max(_e417, 0.0001f); blocker_1 = -1f; - let _e418 = uv_3; - param_23 = _e418; - let _e419 = tile_3; - param_24 = _e419; - let _e420 = range_3; - param_25 = _e420; - let _e421 = receiver_2; - param_26 = _e421; - let _e422 = ca_2; - param_27 = _e422; - let _e423 = sa_2; - param_28 = _e423; - let _e424 = tanHalf_1; - param_29 = _e424; - let _e425 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); - let _e426 = param_30; - blocker_1 = _e426; - radius_1 = _e425; - let _e429 = point_shadow.params2_[3u]; - if (_e429 > 0.5f) { + let _e419 = uv_3; + param_23 = _e419; + let _e420 = tile_3; + param_24 = _e420; + let _e421 = range_3; + param_25 = _e421; + let _e422 = receiver_2; + param_26 = _e422; + let _e423 = ca_2; + param_27 = _e423; + let _e424 = sa_2; + param_28 = _e424; + let _e425 = tanHalf_1; + param_29 = _e425; + let _e426 = PointShadowPenumbra_u0028_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_23), (¶m_24), (¶m_25), (¶m_26), (¶m_27), (¶m_28), (¶m_29), (¶m_30)); + let _e427 = param_30; + blocker_1 = _e427; + radius_1 = _e426; + let _e430 = point_shadow.params2_[3u]; + if (_e430 > 0.5f) { g_debug_surface_on = true; - let _e431 = radius_1; - if (_e431 < 0f) { + let _e432 = radius_1; + if (_e432 < 0f) { local_3 = vec3(0f, 0f, 1f); } else { - let _e433 = radius_1; - let _e436 = point_shadow.params2_[2u]; - let _e440 = blocker_1; - let _e441 = range_3; - local_3 = vec3(clamp((_e433 / max(_e436, 0.000001f)), 0f, 1f), clamp((_e440 / _e441), 0f, 1f), 0f); + let _e434 = radius_1; + let _e437 = point_shadow.params2_[2u]; + let _e441 = blocker_1; + let _e442 = range_3; + local_3 = vec3(clamp((_e434 / max(_e437, 0.000001f)), 0f, 1f), clamp((_e441 / _e442), 0f, 1f), 0f); } - let _e445 = local_3; - g_debug_surface = _e445; + let _e446 = local_3; + g_debug_surface = _e446; } - let _e446 = radius_1; - if (_e446 < 0f) { + let _e447 = radius_1; + if (_e447 < 0f) { return 1f; } - let _e448 = uv_3; - param_31 = _e448; + let _e449 = uv_3; + param_31 = _e449; param_32 = vec2(0f, 0f); - let _e449 = tile_3; - param_33 = _e449; - let _e450 = range_3; - param_34 = _e450; - let _e451 = receiver_2; - param_35 = _e451; - let _e452 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); - lit = _e452; - let _e453 = radius_1; - if (_e453 > 0f) { + let _e450 = tile_3; + param_33 = _e450; + let _e451 = range_3; + param_34 = _e451; + let _e452 = receiver_2; + param_35 = _e452; + let _e453 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_31), (¶m_32), (¶m_33), (¶m_34), (¶m_35)); + lit = _e453; + let _e454 = radius_1; + if (_e454 > 0f) { i_3 = 0i; loop { - let _e455 = i_3; - if (_e455 < 8i) { - let _e457 = i_3; - param_36 = _e457; - let _e458 = ca_2; - param_37 = _e458; - let _e459 = sa_2; - param_38 = _e459; - let _e460 = radius_1; - param_39 = _e460; - let _e461 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); - let _e462 = uv_3; - param_40 = _e462; - param_41 = _e461; - let _e463 = tile_3; - param_42 = _e463; - let _e464 = range_3; - param_43 = _e464; - let _e465 = receiver_2; - param_44 = _e465; - let _e466 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); - let _e467 = lit; - lit = (_e467 + _e466); + let _e456 = i_3; + if (_e456 < 8i) { + let _e458 = i_3; + param_36 = _e458; + let _e459 = ca_2; + param_37 = _e459; + let _e460 = sa_2; + param_38 = _e460; + let _e461 = radius_1; + param_39 = _e461; + let _e462 = PointShadowOffset_u0028_i1_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_36), (¶m_37), (¶m_38), (¶m_39)); + let _e463 = uv_3; + param_40 = _e463; + param_41 = _e462; + let _e464 = tile_3; + param_42 = _e464; + let _e465 = range_3; + param_43 = _e465; + let _e466 = receiver_2; + param_44 = _e466; + let _e467 = PointShadowTap_u0028_vf2_u003b_vf2_u003b_vf2_u003b_f1_u003b_f1_u003b((¶m_40), (¶m_41), (¶m_42), (¶m_43), (¶m_44)); + let _e468 = lit; + lit = (_e468 + _e467); continue; } else { break; } continuing { - let _e469 = i_3; - i_3 = (_e469 + 1i); + let _e470 = i_3; + i_3 = (_e470 + 1i); } } - let _e471 = lit; - lit = (_e471 * 0.11111111f); + let _e472 = lit; + lit = (_e472 * 0.11111111f); } - let _e473 = lit; - let _e474 = strength; - return mix(1f, _e473, clamp(_e474, 0f, 1f)); + let _e474 = lit; + let _e475 = strength; + return mix(1f, _e474, clamp(_e475, 0f, 1f)); } fn VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b(i_4: ptr, n_1: ptr, turn: ptr) -> vec2 { var r: f32; var theta: f32; - let _e171 = (*i_4); - let _e174 = (*n_1); - r = sqrt(((f32(_e171) + 0.5f) / f32(_e174))); - let _e178 = (*i_4); - let _e181 = (*turn); - theta = ((f32(_e178) * 2.3999631f) + _e181); - let _e183 = r; - let _e184 = theta; - let _e186 = theta; - return (vec2(cos(_e184), sin(_e186)) * _e183); + let _e172 = (*i_4); + let _e175 = (*n_1); + r = sqrt(((f32(_e172) + 0.5f) / f32(_e175))); + let _e179 = (*i_4); + let _e182 = (*turn); + theta = ((f32(_e179) * 2.3999631f) + _e182); + let _e184 = r; + let _e185 = theta; + let _e187 = theta; + return (vec2(cos(_e185), sin(_e187)) * _e184); } fn ShadowNoise_u0028_() -> f32 { var at: vec2; var param_45: f32; - let _e170 = frag_info.target_origin[0u]; - param_45 = _e170; - let _e171 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); - let _e174 = frag_info.target_origin[3u]; - at = (_e171 + vec2((5.588238f * max(_e174, 0f)))); - let _e179 = at; - return fract((52.982918f * fract(dot(_e179, vec2(0.06711056f, 0.00583715f))))); + let _e171 = frag_info.target_origin[0u]; + param_45 = _e171; + let _e172 = FragCoordFromTop_u0028_f1_u003b((¶m_45)); + let _e175 = frag_info.target_origin[3u]; + at = (_e172 + vec2((5.588238f * max(_e175, 0f)))); + let _e180 = at; + return fract((52.982918f * fract(dot(_e180, vec2(0.06711056f, 0.00583715f))))); } fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr>, t: ptr, minVariance: ptr, bleed: ptr) -> f32 { @@ -41907,37 +42117,37 @@ fn EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b(moments: ptr) -> vec2 { var d_2: f32; - let _e168 = (*depth); - d_2 = ((2f * clamp(_e168, 0f, 1f)) - 1f); - let _e172 = d_2; - let _e175 = d_2; - return vec2(exp((40f * _e172)), -(exp((-5f * _e175)))); + let _e169 = (*depth); + d_2 = ((2f * clamp(_e169, 0f, 1f)) - 1f); + let _e173 = d_2; + let _e176 = d_2; + return vec2(exp((40f * _e173)), -(exp((-5f * _e176)))); } fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr>, depth_1: ptr, bleed_1: ptr) -> f32 { @@ -41955,37 +42165,37 @@ fn EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b(moments_1: ptr(0.004f, 0.0005f) * _e184); - let _e187 = scale[0u]; - let _e189 = scale[0u]; - let _e191 = (*moments_1); - param_47 = _e191.xy; - let _e194 = warped[0u]; - param_48 = _e194; - param_49 = (_e187 * _e189); - let _e195 = (*bleed_1); - param_50 = _e195; - let _e196 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); - positive = _e196; - let _e198 = scale[1u]; - let _e200 = scale[1u]; - let _e202 = (*moments_1); - param_51 = _e202.zw; - let _e205 = warped[1u]; - param_52 = _e205; - param_53 = (_e198 * _e200); - let _e206 = (*bleed_1); - param_54 = _e206; - let _e207 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); - negative = _e207; - let _e208 = positive; - let _e209 = negative; - return min(_e208, _e209); + let _e183 = (*depth_1); + param_46 = _e183; + let _e184 = EvsmWarp_u0028_f1_u003b((¶m_46)); + warped = _e184; + let _e185 = warped; + scale = (vec2(0.004f, 0.0005f) * _e185); + let _e188 = scale[0u]; + let _e190 = scale[0u]; + let _e192 = (*moments_1); + param_47 = _e192.xy; + let _e195 = warped[0u]; + param_48 = _e195; + param_49 = (_e188 * _e190); + let _e196 = (*bleed_1); + param_50 = _e196; + let _e197 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_47), (¶m_48), (¶m_49), (¶m_50)); + positive = _e197; + let _e199 = scale[1u]; + let _e201 = scale[1u]; + let _e203 = (*moments_1); + param_51 = _e203.zw; + let _e206 = warped[1u]; + param_52 = _e206; + param_53 = (_e199 * _e201); + let _e207 = (*bleed_1); + param_54 = _e207; + let _e208 = EvsmChebyshev_u0028_vf2_u003b_f1_u003b_f1_u003b_f1_u003b((¶m_51), (¶m_52), (¶m_53), (¶m_54)); + negative = _e208; + let _e209 = positive; + let _e210 = negative; + return min(_e209, _e210); } fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_1: ptr, light_1: ptr, lightIndex_1: ptr) -> f32 { @@ -42022,6 +42232,8 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var texel_1: vec2; var tileLo: vec2; var tileHi: vec2; + var stored_2: vec4; + var through: vec3; var softness: f32; var lit_1: f32; var moments_2: vec4; @@ -42052,51 +42264,51 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf var param_63: f32; var tapBias_1: f32; var occluder_2: f32; - var phi_2719_: bool; - var phi_2730_: bool; - var phi_2891_: bool; - var phi_2898_: bool; - var phi_2905_: bool; - - let _e234 = frag_info.shadow_params[3u]; - strength_1 = _e234; - let _e235 = strength_1; - if (_e235 <= 0f) { + var phi_2722_: bool; + var phi_2733_: bool; + var phi_2894_: bool; + var phi_2901_: bool; + var phi_2908_: bool; + + let _e237 = frag_info.shadow_params[3u]; + strength_1 = _e237; + let _e238 = strength_1; + if (_e238 <= 0f) { return 1f; } - let _e237 = (*lightIndex_1); - let _e240 = frag_info.frame_params[2u]; - if (_e237 != i32((_e240 + 0.5f))) { + let _e240 = (*lightIndex_1); + let _e243 = frag_info.frame_params[2u]; + if (_e240 != i32((_e243 + 0.5f))) { return 1f; } - let _e246 = frag_info.shadow_cascades[2u]; - cascadeCount = i32((_e246 + 0.5f)); - let _e249 = v_world_position_1; - let _e251 = frag_info.camera_position; - viewDistance = length((_e249 - _e251.xyz)); + let _e249 = frag_info.shadow_cascades[2u]; + cascadeCount = i32((_e249 + 0.5f)); + let _e252 = v_world_position_1; + let _e254 = frag_info.camera_position; + viewDistance = length((_e252 - _e254.xyz)); cascade = 0i; - let _e255 = cascadeCount; - let _e256 = (_e255 > 1i); - phi_2719_ = _e256; - if _e256 { - let _e257 = viewDistance; - let _e260 = frag_info.shadow_cascades[0u]; - phi_2719_ = (_e257 > _e260); - } - let _e263 = phi_2719_; - if _e263 { + let _e258 = cascadeCount; + let _e259 = (_e258 > 1i); + phi_2722_ = _e259; + if _e259 { + let _e260 = viewDistance; + let _e263 = frag_info.shadow_cascades[0u]; + phi_2722_ = (_e260 > _e263); + } + let _e266 = phi_2722_; + if _e266 { cascade = 1i; } - let _e264 = cascadeCount; - let _e265 = (_e264 > 2i); - phi_2730_ = _e265; - if _e265 { - let _e266 = viewDistance; - let _e269 = frag_info.shadow_cascades[1u]; - phi_2730_ = (_e266 > _e269); + let _e267 = cascadeCount; + let _e268 = (_e267 > 2i); + phi_2733_ = _e268; + if _e268 { + let _e269 = viewDistance; + let _e272 = frag_info.shadow_cascades[1u]; + phi_2733_ = (_e269 > _e272); } - let _e272 = phi_2730_; - if _e272 { + let _e275 = phi_2733_; + if _e275 { cascade = 2i; } uv_4 = vec2(0f, 0f); @@ -42107,232 +42319,247 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf receiverSlope = 0f; attempt = 0i; loop { - let _e273 = attempt; - if (_e273 < 3i) { - let _e275 = cascade; - let _e276 = attempt; - which = (_e275 + _e276); - let _e278 = which; - let _e279 = cascadeCount; - if (_e278 >= _e279) { + let _e276 = attempt; + if (_e276 < 3i) { + let _e278 = cascade; + let _e279 = attempt; + which = (_e278 + _e279); + let _e281 = which; + let _e282 = cascadeCount; + if (_e281 >= _e282) { break; } - let _e281 = which; - if (_e281 == 0i) { - let _e284 = frag_info.shadow_matrix; - local_4 = _e284; + let _e284 = which; + if (_e284 == 0i) { + let _e287 = frag_info.shadow_matrix; + local_4 = _e287; } else { - let _e285 = which; - if (_e285 == 1i) { - let _e288 = frag_info.shadow_matrix_far; - local_5 = _e288; + let _e288 = which; + if (_e288 == 1i) { + let _e291 = frag_info.shadow_matrix_far; + local_5 = _e291; } else { - let _e290 = frag_info.shadow_matrix_farthest; - local_5 = _e290; + let _e293 = frag_info.shadow_matrix_farthest; + local_5 = _e293; } - let _e291 = local_5; - local_4 = _e291; - } - let _e292 = local_4; - matrix = _e292; - let _e295 = matrix[0][0u]; - let _e298 = matrix[1][0u]; - let _e301 = matrix[2][0u]; - axisX = vec3(_e295, _e298, _e301); - let _e305 = matrix[0][1u]; - let _e308 = matrix[1][1u]; - let _e311 = matrix[2][1u]; - axisY = vec3(_e305, _e308, _e311); - let _e313 = axisX; - rowX = max(length(_e313), 0.000001f); - let _e316 = axisY; - rowY = max(length(_e316), 0.000001f); - let _e321 = frag_info.shadow_cascades[3u]; - let _e323 = rowX; - texelMetres = ((2f * _e321) / _e323); - let _e327 = frag_info.shadow_cascades[3u]; - let _e330 = (*s_1).n; - let _e331 = axisX; - let _e334 = rowX; - let _e335 = rowX; - let _e339 = (*s_1).n; - let _e340 = axisY; - let _e343 = rowY; - let _e344 = rowY; - reach = ((3f * _e327) * ((abs(dot(_e330, _e331)) / (_e334 * _e335)) + (abs(dot(_e339, _e340)) / (_e343 * _e344)))); - let _e351 = frag_info.shadow_params[2u]; - let _e352 = texelMetres; - flatOffset = min(_e351, _e352); - let _e354 = v_world_position_1; - let _e356 = (*s_1).n; - let _e357 = flatOffset; - let _e358 = reach; - origin_1 = (_e354 + (_e356 * (_e357 + _e358))); - let _e362 = matrix; - let _e363 = origin_1; - lightSpace = (_e362 * vec4(_e363.x, _e363.y, _e363.z, 1f)); - let _e370 = lightSpace[3u]; - if (_e370 <= 0f) { + let _e294 = local_5; + local_4 = _e294; + } + let _e295 = local_4; + matrix = _e295; + let _e298 = matrix[0][0u]; + let _e301 = matrix[1][0u]; + let _e304 = matrix[2][0u]; + axisX = vec3(_e298, _e301, _e304); + let _e308 = matrix[0][1u]; + let _e311 = matrix[1][1u]; + let _e314 = matrix[2][1u]; + axisY = vec3(_e308, _e311, _e314); + let _e316 = axisX; + rowX = max(length(_e316), 0.000001f); + let _e319 = axisY; + rowY = max(length(_e319), 0.000001f); + let _e324 = frag_info.shadow_cascades[3u]; + let _e326 = rowX; + texelMetres = ((2f * _e324) / _e326); + let _e330 = frag_info.shadow_cascades[3u]; + let _e333 = (*s_1).n; + let _e334 = axisX; + let _e337 = rowX; + let _e338 = rowX; + let _e342 = (*s_1).n; + let _e343 = axisY; + let _e346 = rowY; + let _e347 = rowY; + reach = ((3f * _e330) * ((abs(dot(_e333, _e334)) / (_e337 * _e338)) + (abs(dot(_e342, _e343)) / (_e346 * _e347)))); + let _e354 = frag_info.shadow_params[2u]; + let _e355 = texelMetres; + flatOffset = min(_e354, _e355); + let _e357 = v_world_position_1; + let _e359 = (*s_1).n; + let _e360 = flatOffset; + let _e361 = reach; + origin_1 = (_e357 + (_e359 * (_e360 + _e361))); + let _e365 = matrix; + let _e366 = origin_1; + lightSpace = (_e365 * vec4(_e366.x, _e366.y, _e366.z, 1f)); + let _e373 = lightSpace[3u]; + if (_e373 <= 0f) { continue; } - let _e372 = lightSpace; - let _e375 = lightSpace[3u]; - candidate = (_e372.xyz / vec3(_e375)); - let _e379 = candidate[0u]; - let _e383 = candidate[1u]; - inTile = vec2(((_e379 * 0.5f) + 0.5f), (0.5f - (_e383 * 0.5f))); - let _e388 = inTile[0u]; - let _e389 = (_e388 < 0f); - phi_2891_ = _e389; - if !(_e389) { - let _e392 = inTile[0u]; - phi_2891_ = (_e392 > 1f); - } - let _e395 = phi_2891_; - phi_2898_ = _e395; - if !(_e395) { - let _e398 = inTile[1u]; - phi_2898_ = (_e398 < 0f); - } - let _e401 = phi_2898_; - phi_2905_ = _e401; - if !(_e401) { - let _e404 = inTile[1u]; - phi_2905_ = (_e404 > 1f); + let _e375 = lightSpace; + let _e378 = lightSpace[3u]; + candidate = (_e375.xyz / vec3(_e378)); + let _e382 = candidate[0u]; + let _e386 = candidate[1u]; + inTile = vec2(((_e382 * 0.5f) + 0.5f), (0.5f - (_e386 * 0.5f))); + let _e391 = inTile[0u]; + let _e392 = (_e391 < 0f); + phi_2894_ = _e392; + if !(_e392) { + let _e395 = inTile[0u]; + phi_2894_ = (_e395 > 1f); + } + let _e398 = phi_2894_; + phi_2901_ = _e398; + if !(_e398) { + let _e401 = inTile[1u]; + phi_2901_ = (_e401 < 0f); + } + let _e404 = phi_2901_; + phi_2908_ = _e404; + if !(_e404) { + let _e407 = inTile[1u]; + phi_2908_ = (_e407 > 1f); } - let _e407 = phi_2905_; - if _e407 { + let _e410 = phi_2908_; + if _e410 { continue; } - let _e409 = candidate[2u]; - if (_e409 > 1f) { - let _e411 = which; - let _e412 = cascadeCount; - if (_e411 < (_e412 - 1i)) { + let _e412 = candidate[2u]; + if (_e412 > 1f) { + let _e414 = which; + let _e415 = cascadeCount; + if (_e414 < (_e415 - 1i)) { continue; } candidate[2u] = 1f; } - let _e417 = inTile[0u]; - let _e418 = which; - let _e421 = cascadeCount; - let _e425 = inTile[1u]; - uv_4 = vec2(((_e417 + f32(_e418)) / f32(_e421)), _e425); - let _e427 = candidate; - projected = _e427; - let _e428 = which; - cascade = _e428; - let _e429 = texelMetres; - cascadeTexel = _e429; - let _e432 = matrix[0][2u]; - let _e435 = matrix[1][2u]; - let _e438 = matrix[2][2u]; - cascadeDepth = (1f / max(length(vec3(_e432, _e435, _e438)), 0.000001f)); - let _e444 = (*s_1).n; - let _e447 = matrix[0][2u]; - let _e450 = matrix[1][2u]; - let _e453 = matrix[2][2u]; - let _e457 = cascadeDepth; - cosSlope = clamp((abs(dot(_e444, vec3(_e447, _e450, _e453))) * _e457), 0.1f, 1f); - let _e460 = texelMetres; - let _e461 = cosSlope; - let _e462 = cosSlope; - let _e468 = cosSlope; - let _e470 = cascadeDepth; - receiverSlope = (((_e460 * sqrt(max((1f - (_e461 * _e462)), 0f))) / _e468) / _e470); + let _e420 = inTile[0u]; + let _e421 = which; + let _e424 = cascadeCount; + let _e428 = inTile[1u]; + uv_4 = vec2(((_e420 + f32(_e421)) / f32(_e424)), _e428); + let _e430 = candidate; + projected = _e430; + let _e431 = which; + cascade = _e431; + let _e432 = texelMetres; + cascadeTexel = _e432; + let _e435 = matrix[0][2u]; + let _e438 = matrix[1][2u]; + let _e441 = matrix[2][2u]; + cascadeDepth = (1f / max(length(vec3(_e435, _e438, _e441)), 0.000001f)); + let _e447 = (*s_1).n; + let _e450 = matrix[0][2u]; + let _e453 = matrix[1][2u]; + let _e456 = matrix[2][2u]; + let _e460 = cascadeDepth; + cosSlope = clamp((abs(dot(_e447, vec3(_e450, _e453, _e456))) * _e460), 0.1f, 1f); + let _e463 = texelMetres; + let _e464 = cosSlope; + let _e465 = cosSlope; + let _e471 = cosSlope; + let _e473 = cascadeDepth; + receiverSlope = (((_e463 * sqrt(max((1f - (_e464 * _e465)), 0f))) / _e471) / _e473); found = true; break; } else { break; } continuing { - let _e472 = attempt; - attempt = (_e472 + 1i); + let _e475 = attempt; + attempt = (_e475 + 1i); } } - let _e474 = found; - if !(_e474) { + let _e477 = found; + if !(_e477) { return 1f; } - let _e476 = cascade; - if (_e476 == 0i) { - let _e480 = frag_info.shadow_bias[0u]; - local_6 = _e480; + let _e479 = cascade; + if (_e479 == 0i) { + let _e483 = frag_info.shadow_bias[0u]; + local_6 = _e483; } else { - let _e481 = cascade; - if (_e481 == 1i) { - let _e485 = frag_info.shadow_bias[1u]; - local_7 = _e485; - } else { - let _e488 = frag_info.shadow_bias[2u]; + let _e484 = cascade; + if (_e484 == 1i) { + let _e488 = frag_info.shadow_bias[1u]; local_7 = _e488; - } - let _e489 = local_7; - local_6 = _e489; - } - let _e490 = local_6; - bias = _e490; - let _e493 = frag_info.shadow_params[0u]; - let _e496 = frag_info.shadow_cascades[3u]; - texel_1 = vec2(_e493, _e496); - let _e498 = cascade; - let _e500 = cascadeCount; - let _e504 = texel_1; - tileLo = (vec2((f32(_e498) / f32(_e500)), 0f) + (_e504 * 0.5f)); - let _e507 = cascade; - let _e510 = cascadeCount; - let _e514 = texel_1; - tileHi = (vec2((f32((_e507 + 1i)) / f32(_e510)), 1f) - (_e514 * 0.5f)); - let _e519 = frag_info.ambient_ground[3u]; - softness = _e519; + } else { + let _e491 = frag_info.shadow_bias[2u]; + local_7 = _e491; + } + let _e492 = local_7; + local_6 = _e492; + } + let _e493 = local_6; + bias = _e493; + let _e496 = frag_info.shadow_params[0u]; + let _e499 = frag_info.shadow_cascades[3u]; + texel_1 = vec2(_e496, _e499); + let _e501 = cascade; + let _e503 = cascadeCount; + let _e507 = texel_1; + tileLo = (vec2((f32(_e501) / f32(_e503)), 0f) + (_e507 * 0.5f)); + let _e510 = cascade; + let _e513 = cascadeCount; + let _e517 = texel_1; + tileHi = (vec2((f32((_e510 + 1i)) / f32(_e513)), 1f) - (_e517 * 0.5f)); + let _e522 = frag_info.shadow_bias[3u]; + if (_e522 > 0.5f) { + let _e524 = uv_4; + let _e525 = tileLo; + let _e526 = tileHi; + let _e528 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e524, _e525, _e526), 0f); + stored_2 = _e528; + let _e530 = stored_2[1u]; + let _e533 = stored_2[2u]; + let _e536 = stored_2[3u]; + through = clamp(vec3((1f - _e530), (1f - _e533), _e536), vec3(0f), vec3(1f)); + let _e541 = through; + let _e542 = strength_1; + light_transmittance = mix(vec3(1f, 1f, 1f), _e541, vec3(clamp(_e542, 0f, 1f))); + } + let _e548 = frag_info.ambient_ground[3u]; + softness = _e548; lit_1 = 0f; - let _e520 = softness; - if (_e520 < 0f) { - let _e522 = uv_4; - let _e523 = tileLo; - let _e524 = tileHi; - let _e526 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e522, _e523, _e524), 0f); - moments_2 = _e526; - let _e528 = projected[2u]; - let _e529 = bias; - let _e531 = softness; - let _e535 = moments_2; - param_55 = _e535; - param_56 = (_e528 - _e529); - param_57 = clamp((-(_e531) - 1f), 0f, 0.95f); - let _e536 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); - lit_1 = _e536; + let _e549 = softness; + if (_e549 < 0f) { + let _e551 = uv_4; + let _e552 = tileLo; + let _e553 = tileHi; + let _e555 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp(_e551, _e552, _e553), 0f); + moments_2 = _e555; + let _e557 = projected[2u]; + let _e558 = bias; + let _e560 = softness; + let _e564 = moments_2; + param_55 = _e564; + param_56 = (_e557 - _e558); + param_57 = clamp((-(_e560) - 1f), 0f, 0.95f); + let _e565 = EvsmVisibility_u0028_vf4_u003b_f1_u003b_f1_u003b((¶m_55), (¶m_56), (¶m_57)); + lit_1 = _e565; } else { - let _e537 = softness; - if (_e537 <= 0f) { + let _e566 = softness; + if (_e566 <= 0f) { y = -1i; loop { - let _e539 = y; - if (_e539 <= 1i) { + let _e568 = y; + if (_e568 <= 1i) { x = -1i; loop { - let _e541 = x; - if (_e541 <= 1i) { - let _e543 = uv_4; - let _e544 = x; - let _e546 = y; - let _e549 = texel_1; - let _e552 = tileLo; - let _e553 = tileHi; - let _e555 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e543 + (vec2(f32(_e544), f32(_e546)) * _e549)), _e552, _e553), 0f); - occluder = _e555.x; - let _e558 = projected[2u]; - let _e559 = bias; - let _e561 = occluder; - let _e564 = lit_1; - lit_1 = (_e564 + select(1f, 0f, ((_e558 - _e559) > _e561))); + let _e570 = x; + if (_e570 <= 1i) { + let _e572 = uv_4; + let _e573 = x; + let _e575 = y; + let _e578 = texel_1; + let _e581 = tileLo; + let _e582 = tileHi; + let _e584 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e572 + (vec2(f32(_e573), f32(_e575)) * _e578)), _e581, _e582), 0f); + occluder = _e584.x; + let _e587 = projected[2u]; + let _e588 = bias; + let _e590 = occluder; + let _e593 = lit_1; + lit_1 = (_e593 + select(1f, 0f, ((_e587 - _e588) > _e590))); continue; } else { break; } continuing { - let _e566 = x; - x = (_e566 + 1i); + let _e595 = x; + x = (_e595 + 1i); } } continue; @@ -42340,125 +42567,125 @@ fn ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf break; } continuing { - let _e568 = y; - y = (_e568 + 1i); + let _e597 = y; + y = (_e597 + 1i); } } - let _e570 = lit_1; - lit_1 = (_e570 * 0.11111111f); + let _e599 = lit_1; + lit_1 = (_e599 * 0.11111111f); } else { - let _e572 = softness; - spread = tan(min(_e572, 0.5f)); - let _e575 = ShadowNoise_u0028_(); - turn_1 = (_e575 * 6.2831855f); - let _e577 = spread; - let _e579 = projected[2u]; - let _e581 = cascadeDepth; - let _e583 = cascadeTexel; - searchRadius = clamp((((_e577 * _e579) * _e581) / _e583), 1f, 16f); + let _e601 = softness; + spread = tan(min(_e601, 0.5f)); + let _e604 = ShadowNoise_u0028_(); + turn_1 = (_e604 * 6.2831855f); + let _e606 = spread; + let _e608 = projected[2u]; + let _e610 = cascadeDepth; + let _e612 = cascadeTexel; + searchRadius = clamp((((_e606 * _e608) * _e610) / _e612), 1f, 16f); blockerSum = 0f; blockerCount = 0f; i_5 = 0i; loop { - let _e586 = i_5; - if (_e586 < 16i) { - let _e588 = i_5; - param_58 = _e588; + let _e615 = i_5; + if (_e615 < 16i) { + let _e617 = i_5; + param_58 = _e617; param_59 = 16i; - let _e589 = turn_1; - param_60 = _e589; - let _e590 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); - disc = _e590; - let _e591 = bias; - let _e592 = disc; - let _e594 = searchRadius; - let _e598 = receiverSlope; - tapBias = (_e591 + (max(((length(_e592) * _e594) - 1.5f), 0f) * _e598)); - let _e601 = uv_4; - let _e602 = disc; - let _e603 = texel_1; - let _e605 = searchRadius; - let _e608 = tileLo; - let _e609 = tileHi; - let _e611 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e601 + ((_e602 * _e603) * _e605)), _e608, _e609), 0f); - occluder_1 = _e611.x; - let _e614 = projected[2u]; - let _e615 = tapBias; - let _e617 = occluder_1; - if ((_e614 - _e615) > _e617) { - let _e619 = occluder_1; - let _e620 = blockerSum; - blockerSum = (_e620 + _e619); - let _e622 = blockerCount; - blockerCount = (_e622 + 1f); + let _e618 = turn_1; + param_60 = _e618; + let _e619 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_58), (¶m_59), (¶m_60)); + disc = _e619; + let _e620 = bias; + let _e621 = disc; + let _e623 = searchRadius; + let _e627 = receiverSlope; + tapBias = (_e620 + (max(((length(_e621) * _e623) - 1.5f), 0f) * _e627)); + let _e630 = uv_4; + let _e631 = disc; + let _e632 = texel_1; + let _e634 = searchRadius; + let _e637 = tileLo; + let _e638 = tileHi; + let _e640 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e630 + ((_e631 * _e632) * _e634)), _e637, _e638), 0f); + occluder_1 = _e640.x; + let _e643 = projected[2u]; + let _e644 = tapBias; + let _e646 = occluder_1; + if ((_e643 - _e644) > _e646) { + let _e648 = occluder_1; + let _e649 = blockerSum; + blockerSum = (_e649 + _e648); + let _e651 = blockerCount; + blockerCount = (_e651 + 1f); } continue; } else { break; } continuing { - let _e624 = i_5; - i_5 = (_e624 + 1i); + let _e653 = i_5; + i_5 = (_e653 + 1i); } } - let _e626 = blockerCount; - if (_e626 <= 0f) { + let _e655 = blockerCount; + if (_e655 <= 0f) { return 1f; } - let _e629 = projected[2u]; - let _e630 = blockerSum; - let _e631 = blockerCount; - let _e635 = cascadeDepth; - gap = (max((_e629 - (_e630 / _e631)), 0f) * _e635); - let _e637 = spread; - let _e638 = gap; - let _e640 = cascadeTexel; - radius_2 = clamp(((_e637 * _e638) / _e640), 1f, 16f); + let _e658 = projected[2u]; + let _e659 = blockerSum; + let _e660 = blockerCount; + let _e664 = cascadeDepth; + gap = (max((_e658 - (_e659 / _e660)), 0f) * _e664); + let _e666 = spread; + let _e667 = gap; + let _e669 = cascadeTexel; + radius_2 = clamp(((_e666 * _e667) / _e669), 1f, 16f); i_6 = 0i; loop { - let _e643 = i_6; - if (_e643 < 16i) { - let _e645 = turn_1; - let _e647 = i_6; - param_61 = _e647; + let _e672 = i_6; + if (_e672 < 16i) { + let _e674 = turn_1; + let _e676 = i_6; + param_61 = _e676; param_62 = 16i; - param_63 = (_e645 + 1f); - let _e648 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); - disc_1 = _e648; - let _e649 = bias; - let _e650 = disc_1; - let _e652 = radius_2; - let _e656 = receiverSlope; - tapBias_1 = (_e649 + (max(((length(_e650) * _e652) - 1.5f), 0f) * _e656)); - let _e659 = uv_4; - let _e660 = disc_1; - let _e661 = texel_1; - let _e663 = radius_2; - let _e666 = tileLo; - let _e667 = tileHi; - let _e669 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e659 + ((_e660 * _e661) * _e663)), _e666, _e667), 0f); - occluder_2 = _e669.x; - let _e672 = projected[2u]; - let _e673 = tapBias_1; - let _e675 = occluder_2; - let _e678 = lit_1; - lit_1 = (_e678 + select(1f, 0f, ((_e672 - _e673) > _e675))); + param_63 = (_e674 + 1f); + let _e677 = VogelDisc_u0028_i1_u003b_i1_u003b_f1_u003b((¶m_61), (¶m_62), (¶m_63)); + disc_1 = _e677; + let _e678 = bias; + let _e679 = disc_1; + let _e681 = radius_2; + let _e685 = receiverSlope; + tapBias_1 = (_e678 + (max(((length(_e679) * _e681) - 1.5f), 0f) * _e685)); + let _e688 = uv_4; + let _e689 = disc_1; + let _e690 = texel_1; + let _e692 = radius_2; + let _e695 = tileLo; + let _e696 = tileHi; + let _e698 = textureSampleLevel(shadow_texture_tex, shadow_texture_smp, clamp((_e688 + ((_e689 * _e690) * _e692)), _e695, _e696), 0f); + occluder_2 = _e698.x; + let _e701 = projected[2u]; + let _e702 = tapBias_1; + let _e704 = occluder_2; + let _e707 = lit_1; + lit_1 = (_e707 + select(1f, 0f, ((_e701 - _e702) > _e704))); continue; } else { break; } continuing { - let _e680 = i_6; - i_6 = (_e680 + 1i); + let _e709 = i_6; + i_6 = (_e709 + 1i); } } - let _e682 = lit_1; - lit_1 = (_e682 * 0.0625f); + let _e711 = lit_1; + lit_1 = (_e711 * 0.0625f); } } - let _e684 = lit_1; - let _e685 = strength_1; - return mix(1f, _e684, clamp(_e685, 0f, 1f)); + let _e713 = lit_1; + let _e714 = strength_1; + return mix(1f, _e713, clamp(_e714, 0f, 1f)); } fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b(s_2: ptr, light_2: ptr, index: ptr) -> f32 { @@ -42466,19 +42693,19 @@ fn LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d var param_65: LightSample; var param_66: i32; - let _e172 = (*s_2); - param_64 = _e172; - let _e173 = (*light_2); - param_65 = _e173; - let _e174 = (*index); - param_66 = _e174; - let _e175 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); - return _e175; + let _e173 = (*s_2); + param_64 = _e173; + let _e174 = (*light_2); + param_65 = _e174; + let _e175 = (*index); + param_66 = _e175; + let _e176 = ShadowFactor_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_64), (¶m_65), (¶m_66)); + return _e176; } fn LightHasShadow_u0028_i1_u003b(index_1: ptr) -> bool { - let _e167 = (*index_1); - return (_e167 < 8i); + let _e168 = (*index_1); + return (_e168 < 8i); } fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, range_4: ptr) -> f32 { @@ -42486,26 +42713,26 @@ fn PunctualAttenuation_u0028_f1_u003b_f1_u003b(distance_1: ptr, r var ratio: f32; var window: f32; - let _e171 = (*distance_1); let _e172 = (*distance_1); - attenuation = (1f / max((_e171 * _e172), 0.0001f)); - let _e176 = (*range_4); - if (_e176 > 0f) { - let _e178 = (*distance_1); - let _e179 = (*range_4); - ratio = (_e178 / _e179); - let _e181 = ratio; + let _e173 = (*distance_1); + attenuation = (1f / max((_e172 * _e173), 0.0001f)); + let _e177 = (*range_4); + if (_e177 > 0f) { + let _e179 = (*distance_1); + let _e180 = (*range_4); + ratio = (_e179 / _e180); let _e182 = ratio; - let _e184 = ratio; - let _e186 = ratio; - window = clamp((1f - (((_e181 * _e182) * _e184) * _e186)), 0f, 1f); - let _e190 = window; + let _e183 = ratio; + let _e185 = ratio; + let _e187 = ratio; + window = clamp((1f - (((_e182 * _e183) * _e185) * _e187)), 0f, 1f); let _e191 = window; - let _e193 = attenuation; - attenuation = (_e193 * (_e190 * _e191)); + let _e192 = window; + let _e194 = attenuation; + attenuation = (_e194 * (_e191 * _e192)); } - let _e195 = attenuation; - return _e195; + let _e196 = attenuation; + return _e196; } fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b(centre: ptr>, halfWidth: ptr>, halfHeight: ptr>, world_2: ptr>, mirror: ptr>) -> vec3 { @@ -42522,75 +42749,75 @@ fn RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b var u: f32; var v: f32; - let _e183 = (*halfWidth); - let _e184 = (*halfHeight); - n_2 = cross(_e183, _e184); - let _e186 = n_2; - nLen = length(_e186); - let _e188 = nLen; - if (_e188 < 0.000000000001f) { - let _e190 = (*centre); - return _e190; - } - let _e191 = nLen; - let _e192 = n_2; - n_2 = (_e192 / vec3(_e191)); - let _e195 = (*centre); - let _e196 = (*world_2); - toPlane = (_e195 - _e196); - let _e198 = (*mirror); - let _e199 = n_2; - denom = dot(_e198, _e199); - let _e201 = denom; - if (abs(_e201) < 0.00001f) { - let _e204 = toPlane; - let _e205 = n_2; - let _e206 = toPlane; - let _e207 = n_2; - onPlane = (_e204 - (_e205 * dot(_e206, _e207))); + let _e184 = (*halfWidth); + let _e185 = (*halfHeight); + n_2 = cross(_e184, _e185); + let _e187 = n_2; + nLen = length(_e187); + let _e189 = nLen; + if (_e189 < 0.000000000001f) { + let _e191 = (*centre); + return _e191; + } + let _e192 = nLen; + let _e193 = n_2; + n_2 = (_e193 / vec3(_e192)); + let _e196 = (*centre); + let _e197 = (*world_2); + toPlane = (_e196 - _e197); + let _e199 = (*mirror); + let _e200 = n_2; + denom = dot(_e199, _e200); + let _e202 = denom; + if (abs(_e202) < 0.00001f) { + let _e205 = toPlane; + let _e206 = n_2; + let _e207 = toPlane; + let _e208 = n_2; + onPlane = (_e205 - (_e206 * dot(_e207, _e208))); } else { - let _e211 = toPlane; - let _e212 = n_2; - let _e214 = denom; - t_1 = (dot(_e211, _e212) / _e214); - let _e216 = t_1; - if (_e216 > 0f) { - let _e218 = (*mirror); - let _e219 = t_1; - local_8 = (_e218 * _e219); + let _e212 = toPlane; + let _e213 = n_2; + let _e215 = denom; + t_1 = (dot(_e212, _e213) / _e215); + let _e217 = t_1; + if (_e217 > 0f) { + let _e219 = (*mirror); + let _e220 = t_1; + local_8 = (_e219 * _e220); } else { - let _e221 = toPlane; - let _e222 = n_2; - let _e223 = toPlane; - let _e224 = n_2; - local_8 = (_e221 - (_e222 * dot(_e223, _e224))); - } - let _e228 = local_8; - onPlane = _e228; - } - let _e229 = onPlane; - let _e230 = toPlane; - offset_2 = (_e229 - _e230); - let _e232 = (*halfWidth); + let _e222 = toPlane; + let _e223 = n_2; + let _e224 = toPlane; + let _e225 = n_2; + local_8 = (_e222 - (_e223 * dot(_e224, _e225))); + } + let _e229 = local_8; + onPlane = _e229; + } + let _e230 = onPlane; + let _e231 = toPlane; + offset_2 = (_e230 - _e231); let _e233 = (*halfWidth); - wLen2_ = max(dot(_e232, _e233), 0.000000000001f); - let _e236 = (*halfHeight); + let _e234 = (*halfWidth); + wLen2_ = max(dot(_e233, _e234), 0.000000000001f); let _e237 = (*halfHeight); - hLen2_ = max(dot(_e236, _e237), 0.000000000001f); - let _e240 = offset_2; - let _e241 = (*halfWidth); - let _e243 = wLen2_; - u = clamp((dot(_e240, _e241) / _e243), -1f, 1f); - let _e246 = offset_2; - let _e247 = (*halfHeight); - let _e249 = hLen2_; - v = clamp((dot(_e246, _e247) / _e249), -1f, 1f); - let _e252 = (*centre); - let _e253 = (*halfWidth); - let _e254 = u; - let _e257 = (*halfHeight); - let _e258 = v; - return ((_e252 + (_e253 * _e254)) + (_e257 * _e258)); + let _e238 = (*halfHeight); + hLen2_ = max(dot(_e237, _e238), 0.000000000001f); + let _e241 = offset_2; + let _e242 = (*halfWidth); + let _e244 = wLen2_; + u = clamp((dot(_e241, _e242) / _e244), -1f, 1f); + let _e247 = offset_2; + let _e248 = (*halfHeight); + let _e250 = hLen2_; + v = clamp((dot(_e247, _e248) / _e250), -1f, 1f); + let _e253 = (*centre); + let _e254 = (*halfWidth); + let _e255 = u; + let _e258 = (*halfHeight); + let _e259 = v; + return ((_e253 + (_e254 * _e255)) + (_e258 * _e259)); } fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr>, b: ptr>, n_3: ptr>) -> f32 { @@ -42601,32 +42828,32 @@ fn LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b(a_1: ptr> var len: f32; var local_9: f32; - let _e175 = (*a_1); let _e176 = (*a_1); - ua = (_e175 / vec3(max(length(_e176), 0.000000000001f))); - let _e181 = (*b); + let _e177 = (*a_1); + ua = (_e176 / vec3(max(length(_e177), 0.000000000001f))); let _e182 = (*b); - ub = (_e181 / vec3(max(length(_e182), 0.000000000001f))); - let _e187 = ua; - let _e188 = ub; - angle_1 = acos(clamp(dot(_e187, _e188), -1f, 1f)); - let _e192 = ua; - let _e193 = ub; - axis = cross(_e192, _e193); - let _e195 = axis; - len = length(_e195); - let _e197 = len; - if (_e197 > 0.000001f) { - let _e199 = angle_1; - let _e200 = axis; - let _e201 = (*n_3); - let _e204 = len; - local_9 = ((_e199 * dot(_e200, _e201)) / _e204); + let _e183 = (*b); + ub = (_e182 / vec3(max(length(_e183), 0.000000000001f))); + let _e188 = ua; + let _e189 = ub; + angle_1 = acos(clamp(dot(_e188, _e189), -1f, 1f)); + let _e193 = ua; + let _e194 = ub; + axis = cross(_e193, _e194); + let _e196 = axis; + len = length(_e196); + let _e198 = len; + if (_e198 > 0.000001f) { + let _e200 = angle_1; + let _e201 = axis; + let _e202 = (*n_3); + let _e205 = len; + local_9 = ((_e200 * dot(_e201, _e202)) / _e205); } else { local_9 = 0f; } - let _e206 = local_9; - return _e206; + let _e207 = local_9; + return _e207; } fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr, 4>>, n_4: ptr>) -> f32 { @@ -42657,98 +42884,98 @@ fn RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b(corners: ptr(0f, 0f, 0f); i_7 = 0i; loop { - let _e189 = i_7; - if (_e189 < 4i) { - let _e191 = i_7; - let _e193 = (*corners)[_e191]; - a_2 = _e193; - let _e194 = i_7; - if (_e194 == 3i) { + let _e190 = i_7; + if (_e190 < 4i) { + let _e192 = i_7; + let _e194 = (*corners)[_e192]; + a_2 = _e194; + let _e195 = i_7; + if (_e195 == 3i) { local_10 = 0i; } else { - let _e196 = i_7; - local_10 = (_e196 + 1i); - } - let _e198 = local_10; - let _e200 = (*corners)[_e198]; - b_1 = _e200; - let _e201 = a_2; - let _e202 = (*n_4); - ha = dot(_e201, _e202); - let _e204 = b_1; - let _e205 = (*n_4); - hb = dot(_e204, _e205); - let _e207 = ha; - let _e208 = hb; - d_3 = (_e207 - _e208); - let _e210 = a_2; - let _e211 = b_1; - let _e212 = a_2; - let _e214 = d_3; - if (abs(_e214) > 0.000000000001f) { - let _e217 = ha; - let _e218 = d_3; - local_11 = (_e217 / _e218); + let _e197 = i_7; + local_10 = (_e197 + 1i); + } + let _e199 = local_10; + let _e201 = (*corners)[_e199]; + b_1 = _e201; + let _e202 = a_2; + let _e203 = (*n_4); + ha = dot(_e202, _e203); + let _e205 = b_1; + let _e206 = (*n_4); + hb = dot(_e205, _e206); + let _e208 = ha; + let _e209 = hb; + d_3 = (_e208 - _e209); + let _e211 = a_2; + let _e212 = b_1; + let _e213 = a_2; + let _e215 = d_3; + if (abs(_e215) > 0.000000000001f) { + let _e218 = ha; + let _e219 = d_3; + local_11 = (_e218 / _e219); } else { local_11 = 0f; } - let _e220 = local_11; - q = (_e210 + ((_e211 - _e212) * _e220)); - let _e223 = ha; - aAbove = (_e223 > 0f); - let _e225 = hb; - bAbove = (_e225 > 0f); - let _e227 = aAbove; - let _e228 = bAbove; - if (_e227 || _e228) { - let _e230 = aAbove; - let _e231 = a_2; - let _e232 = q; - let _e235 = bAbove; - let _e236 = b_1; - let _e237 = q; - param_67 = select(_e232, _e231, vec3(_e230)); - param_68 = select(_e237, _e236, vec3(_e235)); - let _e240 = (*n_4); - param_69 = _e240; - let _e241 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); - local_12 = _e241; + let _e221 = local_11; + q = (_e211 + ((_e212 - _e213) * _e221)); + let _e224 = ha; + aAbove = (_e224 > 0f); + let _e226 = hb; + bAbove = (_e226 > 0f); + let _e228 = aAbove; + let _e229 = bAbove; + if (_e228 || _e229) { + let _e231 = aAbove; + let _e232 = a_2; + let _e233 = q; + let _e236 = bAbove; + let _e237 = b_1; + let _e238 = q; + param_67 = select(_e233, _e232, vec3(_e231)); + param_68 = select(_e238, _e237, vec3(_e236)); + let _e241 = (*n_4); + param_69 = _e241; + let _e242 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_67), (¶m_68), (¶m_69)); + local_12 = _e242; } else { local_12 = 0f; } - let _e242 = local_12; - let _e243 = total; - total = (_e243 + _e242); - let _e245 = aAbove; - let _e246 = bAbove; - let _e249 = q; - let _e250 = exit; - exit = select(_e250, _e249, vec3((_e245 && !(_e246)))); - let _e253 = aAbove; - let _e255 = bAbove; - let _e257 = q; - let _e258 = entry; - entry = select(_e258, _e257, vec3((!(_e253) && _e255))); + let _e243 = local_12; + let _e244 = total; + total = (_e244 + _e243); + let _e246 = aAbove; + let _e247 = bAbove; + let _e250 = q; + let _e251 = exit; + exit = select(_e251, _e250, vec3((_e246 && !(_e247)))); + let _e254 = aAbove; + let _e256 = bAbove; + let _e258 = q; + let _e259 = entry; + entry = select(_e259, _e258, vec3((!(_e254) && _e256))); continue; } else { break; } continuing { - let _e261 = i_7; - i_7 = (_e261 + 1i); + let _e262 = i_7; + i_7 = (_e262 + 1i); } } - let _e263 = exit; - param_70 = _e263; - let _e264 = entry; - param_71 = _e264; - let _e265 = (*n_4); - param_72 = _e265; - let _e266 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); - let _e267 = total; - total = (_e267 + _e266); - let _e269 = total; - return max((-(_e269) * 0.5f), 0f); + let _e264 = exit; + param_70 = _e264; + let _e265 = entry; + param_71 = _e265; + let _e266 = (*n_4); + param_72 = _e266; + let _e267 = LambertEdge_u0028_vf3_u003b_vf3_u003b_vf3_u003b((¶m_70), (¶m_71), (¶m_72)); + let _e268 = total; + total = (_e268 + _e267); + let _e270 = total; + return max((-(_e270) * 0.5f), 0f); } fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1: ptr) -> f32 { @@ -42757,91 +42984,91 @@ fn LightListLane_u0028_vf4_u003b_i1_u003b(four: ptr>, slot_1 var local_14: f32; var local_15: f32; - let _e172 = (*slot_1); let _e173 = (*slot_1); - lane = (_e172 - ((_e173 / 4i) * 4i)); - let _e177 = lane; - if (_e177 == 0i) { - let _e180 = (*four)[0u]; - local_13 = _e180; + let _e174 = (*slot_1); + lane = (_e173 - ((_e174 / 4i) * 4i)); + let _e178 = lane; + if (_e178 == 0i) { + let _e181 = (*four)[0u]; + local_13 = _e181; } else { - let _e181 = lane; - if (_e181 == 1i) { - let _e184 = (*four)[1u]; - local_14 = _e184; + let _e182 = lane; + if (_e182 == 1i) { + let _e185 = (*four)[1u]; + local_14 = _e185; } else { - let _e185 = lane; - if (_e185 == 2i) { - let _e188 = (*four)[2u]; - local_15 = _e188; + let _e186 = lane; + if (_e186 == 2i) { + let _e189 = (*four)[2u]; + local_15 = _e189; } else { - let _e190 = (*four)[3u]; - local_15 = _e190; + let _e191 = (*four)[3u]; + local_15 = _e191; } - let _e191 = local_15; - local_14 = _e191; + let _e192 = local_15; + local_14 = _e192; } - let _e192 = local_14; - local_13 = _e192; + let _e193 = local_14; + local_13 = _e193; } - let _e193 = local_13; - return _e193; + let _e194 = local_13; + return _e194; } fn LightListScale_u0028_i1_u003b(slot_2: ptr) -> f32 { var param_73: vec4; var param_74: i32; - let _e169 = (*slot_2); - let _e173 = light_list_info.scales[(_e169 / 4i)]; - param_73 = _e173; - let _e174 = (*slot_2); - param_74 = _e174; - let _e175 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); - return _e175; + let _e170 = (*slot_2); + let _e174 = light_list_info.scales[(_e170 / 4i)]; + param_73 = _e174; + let _e175 = (*slot_2); + param_74 = _e175; + let _e176 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_73), (¶m_74)); + return _e176; } fn InSlots_u0028_f1_u003b(row: ptr) -> bool { var a_3: vec4; var b_2: vec4; - let _e171 = light_list_info.slot_rows[0i]; - let _e172 = (*row); - a_3 = abs((_e171 - vec4(_e172))); - let _e178 = light_list_info.slot_rows[1i]; - let _e179 = (*row); - b_2 = abs((_e178 - vec4(_e179))); - let _e184 = a_3[0u]; - let _e186 = a_3[1u]; - let _e189 = a_3[2u]; - let _e191 = a_3[3u]; - let _e195 = b_2[0u]; - let _e197 = b_2[1u]; - let _e200 = b_2[2u]; - let _e202 = b_2[3u]; - return (min(min(min(_e184, _e186), min(_e189, _e191)), min(min(_e195, _e197), min(_e200, _e202))) < 0.5f); + let _e172 = light_list_info.slot_rows[0i]; + let _e173 = (*row); + a_3 = abs((_e172 - vec4(_e173))); + let _e179 = light_list_info.slot_rows[1i]; + let _e180 = (*row); + b_2 = abs((_e179 - vec4(_e180))); + let _e185 = a_3[0u]; + let _e187 = a_3[1u]; + let _e190 = a_3[2u]; + let _e192 = a_3[3u]; + let _e196 = b_2[0u]; + let _e198 = b_2[1u]; + let _e201 = b_2[2u]; + let _e203 = b_2[3u]; + return (min(min(min(_e185, _e187), min(_e190, _e192)), min(min(_e196, _e198), min(_e201, _e203))) < 0.5f); } fn LightListRow_u0028_i1_u003b(slot_3: ptr) -> f32 { var param_75: vec4; var param_76: i32; - let _e169 = (*slot_3); - let _e173 = light_list_info.indices[(_e169 / 4i)]; - param_75 = _e173; - let _e174 = (*slot_3); - param_76 = _e174; - let _e175 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); - return _e175; + let _e170 = (*slot_3); + let _e174 = light_list_info.indices[(_e170 / 4i)]; + param_75 = _e174; + let _e175 = (*slot_3); + param_76 = _e175; + let _e176 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_75), (¶m_76)); + return _e176; } fn LightListTexel_u0028_f1_u003b_f1_u003b(texel_2: ptr, row_1: ptr) -> vec4 { - let _e168 = (*texel_2); - let _e172 = light_list_info.list[1u]; - let _e174 = (*row_1); - let _e178 = light_list_info.list[2u]; - let _e181 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e168 + 0.5f) * _e172), ((_e174 + 0.5f) * _e178)), 0f); - return _e181; + let _e169 = (*texel_2); + let _e173 = light_list_info.list[1u]; + let _e175 = (*row_1); + let _e179 = light_list_info.list[2u]; + let _e182 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2(((_e169 + 0.5f) * _e173), ((_e175 + 0.5f) * _e179)), 0f); + return _e182; } fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { @@ -42855,35 +43082,35 @@ fn ClusterRow_u0028_i1_u003b(slot_4: ptr) -> f32 { var param_79: vec4; var param_80: i32; - let _e176 = g_cluster_offset; - let _e177 = (*slot_4); - entry_1 = (_e176 + f32(_e177)); - let _e180 = entry_1; - row_2 = floor((_e180 / 16f)); - let _e183 = entry_1; - let _e184 = row_2; - within = (_e183 - (_e184 * 16f)); - let _e187 = within; - texel_3 = floor((_e187 / 4f)); - let _e192 = light_list_info.cluster_depth[3u]; - let _e193 = row_2; - let _e195 = texel_3; - param_77 = _e195; - param_78 = (_e192 + _e193); - let _e196 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); - four_1 = _e196; - let _e197 = within; - let _e198 = texel_3; - let _e203 = four_1; - param_79 = _e203; - param_80 = i32(((_e197 - (_e198 * 4f)) + 0.5f)); - let _e204 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); - return _e204; + let _e177 = g_cluster_offset; + let _e178 = (*slot_4); + entry_1 = (_e177 + f32(_e178)); + let _e181 = entry_1; + row_2 = floor((_e181 / 16f)); + let _e184 = entry_1; + let _e185 = row_2; + within = (_e184 - (_e185 * 16f)); + let _e188 = within; + texel_3 = floor((_e188 / 4f)); + let _e193 = light_list_info.cluster_depth[3u]; + let _e194 = row_2; + let _e196 = texel_3; + param_77 = _e196; + param_78 = (_e193 + _e194); + let _e197 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_77), (¶m_78)); + four_1 = _e197; + let _e198 = within; + let _e199 = texel_3; + let _e204 = four_1; + param_79 = _e204; + param_80 = i32(((_e198 - (_e199 * 4f)) + 0.5f)); + let _e205 = LightListLane_u0028_vf4_u003b_i1_u003b((¶m_79), (¶m_80)); + return _e205; } fn Clustered_u0028_() -> bool { - let _e168 = light_list_info.cluster_grid[3u]; - return (_e168 > 0.5f); + let _e169 = light_list_info.cluster_grid[3u]; + return (_e169 > 0.5f); } fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(index_2: ptr, s_3: ptr) -> LightSample { @@ -42939,264 +43166,264 @@ fn SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_ light_3.integrated = 0f; light_3.ltc = vec3(0f, 0f, 0f); - let _e219 = (*index_2); - if (_e219 < 8i) { - let _e221 = (*index_2); - let _e224 = frag_info.light_position[_e221]; - position = _e224; - let _e225 = (*index_2); - let _e228 = frag_info.light_color[_e225]; - color_1 = _e228; - let _e229 = (*index_2); - let _e232 = frag_info.light_direction[_e229]; - direction = _e232; - let _e233 = (*index_2); - let _e236 = frag_info.light_cone[_e233]; - cone = _e236; + let _e220 = (*index_2); + if (_e220 < 8i) { + let _e222 = (*index_2); + let _e225 = frag_info.light_position[_e222]; + position = _e225; + let _e226 = (*index_2); + let _e229 = frag_info.light_color[_e226]; + color_1 = _e229; + let _e230 = (*index_2); + let _e233 = frag_info.light_direction[_e230]; + direction = _e233; + let _e234 = (*index_2); + let _e237 = frag_info.light_cone[_e234]; + cone = _e237; } else { - let _e237 = (*index_2); - slot_5 = (_e237 - 8i); - let _e239 = Clustered_u0028_(); - clustered = _e239; - let _e240 = clustered; - if _e240 { - let _e241 = slot_5; - param_81 = _e241; - let _e242 = ClusterRow_u0028_i1_u003b((¶m_81)); - local_16 = _e242; + let _e238 = (*index_2); + slot_5 = (_e238 - 8i); + let _e240 = Clustered_u0028_(); + clustered = _e240; + let _e241 = clustered; + if _e241 { + let _e242 = slot_5; + param_81 = _e242; + let _e243 = ClusterRow_u0028_i1_u003b((¶m_81)); + local_16 = _e243; } else { - let _e243 = slot_5; - param_82 = _e243; - let _e244 = LightListRow_u0028_i1_u003b((¶m_82)); - local_16 = _e244; - } - let _e245 = local_16; - listRow = _e245; - let _e246 = listRow; - let _e250 = light_list_info.list[2u]; - v_1 = ((_e246 + 0.5f) * _e250); - let _e254 = light_list_info.list[1u]; - u_1 = _e254; - let _e255 = u_1; - let _e257 = v_1; - let _e259 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e255), _e257), 0f); - position = _e259; - let _e260 = u_1; - let _e262 = v_1; - let _e264 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e260), _e262), 0f); - color_1 = _e264; - let _e265 = u_1; - let _e267 = v_1; - let _e269 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e265), _e267), 0f); - direction = _e269; - let _e270 = u_1; - let _e272 = v_1; - let _e274 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e270), _e272), 0f); - cone = _e274; - let _e275 = clustered; - if _e275 { - let _e276 = listRow; - param_83 = _e276; - let _e277 = InSlots_u0028_f1_u003b((¶m_83)); - local_17 = select(1f, 0f, _e277); + let _e244 = slot_5; + param_82 = _e244; + let _e245 = LightListRow_u0028_i1_u003b((¶m_82)); + local_16 = _e245; + } + let _e246 = local_16; + listRow = _e246; + let _e247 = listRow; + let _e251 = light_list_info.list[2u]; + v_1 = ((_e247 + 0.5f) * _e251); + let _e255 = light_list_info.list[1u]; + u_1 = _e255; + let _e256 = u_1; + let _e258 = v_1; + let _e260 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((0.5f * _e256), _e258), 0f); + position = _e260; + let _e261 = u_1; + let _e263 = v_1; + let _e265 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((1.5f * _e261), _e263), 0f); + color_1 = _e265; + let _e266 = u_1; + let _e268 = v_1; + let _e270 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((2.5f * _e266), _e268), 0f); + direction = _e270; + let _e271 = u_1; + let _e273 = v_1; + let _e275 = textureSampleLevel(light_list_texture_tex, light_list_texture_smp, vec2((3.5f * _e271), _e273), 0f); + cone = _e275; + let _e276 = clustered; + if _e276 { + let _e277 = listRow; + param_83 = _e277; + let _e278 = InSlots_u0028_f1_u003b((¶m_83)); + local_17 = select(1f, 0f, _e278); } else { - let _e279 = slot_5; - param_84 = _e279; - let _e280 = LightListScale_u0028_i1_u003b((¶m_84)); - local_17 = _e280; - } - let _e281 = local_17; - let _e283 = color_1[3u]; - color_1[3u] = (_e283 * _e281); - } - let _e287 = position[3u]; - type_39 = _e287; - let _e288 = type_39; - if (_e288 > 2.5f) { - let _e290 = direction; - halfWidth_1 = _e290.xyz; - let _e292 = cone; - halfHeight_1 = _e292.xyz; - let _e294 = position; - let _e296 = v_world_position_1; - toCentre = (_e294.xyz - _e296); - let _e298 = toCentre; - let _e299 = halfWidth_1; - let _e301 = halfHeight_1; - corners_1[0i] = ((_e298 - _e299) - _e301); - let _e304 = toCentre; - let _e305 = halfWidth_1; - let _e307 = halfHeight_1; - corners_1[1i] = ((_e304 + _e305) - _e307); - let _e310 = toCentre; - let _e311 = halfWidth_1; - let _e313 = halfHeight_1; - corners_1[2i] = ((_e310 + _e311) + _e313); - let _e316 = toCentre; - let _e317 = halfWidth_1; - let _e319 = halfHeight_1; - corners_1[3i] = ((_e316 - _e317) + _e319); - let _e322 = toCentre; - let _e323 = halfWidth_1; - let _e324 = halfHeight_1; - behind = (dot(_e322, cross(_e323, _e324)) >= 0f); - let _e328 = behind; - if _e328 { + let _e280 = slot_5; + param_84 = _e280; + let _e281 = LightListScale_u0028_i1_u003b((¶m_84)); + local_17 = _e281; + } + let _e282 = local_17; + let _e284 = color_1[3u]; + color_1[3u] = (_e284 * _e282); + } + let _e288 = position[3u]; + type_39 = _e288; + let _e289 = type_39; + if (_e289 > 2.5f) { + let _e291 = direction; + halfWidth_1 = _e291.xyz; + let _e293 = cone; + halfHeight_1 = _e293.xyz; + let _e295 = position; + let _e297 = v_world_position_1; + toCentre = (_e295.xyz - _e297); + let _e299 = toCentre; + let _e300 = halfWidth_1; + let _e302 = halfHeight_1; + corners_1[0i] = ((_e299 - _e300) - _e302); + let _e305 = toCentre; + let _e306 = halfWidth_1; + let _e308 = halfHeight_1; + corners_1[1i] = ((_e305 + _e306) - _e308); + let _e311 = toCentre; + let _e312 = halfWidth_1; + let _e314 = halfHeight_1; + corners_1[2i] = ((_e311 + _e312) + _e314); + let _e317 = toCentre; + let _e318 = halfWidth_1; + let _e320 = halfHeight_1; + corners_1[3i] = ((_e317 - _e318) + _e320); + let _e323 = toCentre; + let _e324 = halfWidth_1; + let _e325 = halfHeight_1; + behind = (dot(_e323, cross(_e324, _e325)) >= 0f); + let _e329 = behind; + if _e329 { local_18 = 0f; } else { - let _e329 = corners_1; - param_85 = _e329; - let _e331 = (*s_3).n; - param_86 = _e331; - let _e332 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); - local_18 = _e332; - } - let _e333 = local_18; - formFactor = _e333; - let _e334 = halfWidth_1; - let _e335 = halfHeight_1; - area = (length(cross(_e334, _e335)) * 4f); - let _e339 = area; - if (_e339 > 0.000000001f) { - let _e341 = area; - local_19 = (1f / _e341); + let _e330 = corners_1; + param_85 = _e330; + let _e332 = (*s_3).n; + param_86 = _e332; + let _e333 = RectangleFormFactor_u0028_vf3_u005b_4_u005d_u003b_vf3_u003b((¶m_85), (¶m_86)); + local_18 = _e333; + } + let _e334 = local_18; + formFactor = _e334; + let _e335 = halfWidth_1; + let _e336 = halfHeight_1; + area = (length(cross(_e335, _e336)) * 4f); + let _e340 = area; + if (_e340 > 0.000000001f) { + let _e342 = area; + local_19 = (1f / _e342); } else { local_19 = 0f; } - let _e343 = local_19; - radiance = _e343; - let _e344 = toCentre; - distance_2 = length(_e344); - let _e347 = direction[3u]; - if (_e347 > 0f) { - let _e349 = distance_2; - let _e351 = direction[3u]; - ratio_1 = (_e349 / _e351); - let _e353 = ratio_1; + let _e344 = local_19; + radiance = _e344; + let _e345 = toCentre; + distance_2 = length(_e345); + let _e348 = direction[3u]; + if (_e348 > 0f) { + let _e350 = distance_2; + let _e352 = direction[3u]; + ratio_1 = (_e350 / _e352); let _e354 = ratio_1; - let _e356 = ratio_1; - let _e358 = ratio_1; - window_1 = clamp((1f - (((_e353 * _e354) * _e356) * _e358)), 0f, 1f); - let _e362 = window_1; + let _e355 = ratio_1; + let _e357 = ratio_1; + let _e359 = ratio_1; + window_1 = clamp((1f - (((_e354 * _e355) * _e357) * _e359)), 0f, 1f); let _e363 = window_1; - let _e365 = radiance; - radiance = (_e365 * (_e362 * _e363)); - } - let _e368 = (*s_3).v; - let _e371 = (*s_3).n; - mirror_1 = reflect(-(_e368), _e371); - let _e373 = position; - param_87 = _e373.xyz; - let _e375 = halfWidth_1; - param_88 = _e375; - let _e376 = halfHeight_1; - param_89 = _e376; - let _e377 = v_world_position_1; - param_90 = _e377; - let _e378 = mirror_1; - param_91 = _e378; - let _e379 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); - representative = _e379; - let _e380 = representative; - let _e381 = v_world_position_1; - toPoint = (_e380 - _e381); - let _e383 = toPoint; - pointDistance = length(_e383); - let _e385 = pointDistance; - if (_e385 > 0.000001f) { - let _e387 = toPoint; - let _e388 = pointDistance; - local_20 = (_e387 / vec3(_e388)); + let _e364 = window_1; + let _e366 = radiance; + radiance = (_e366 * (_e363 * _e364)); + } + let _e369 = (*s_3).v; + let _e372 = (*s_3).n; + mirror_1 = reflect(-(_e369), _e372); + let _e374 = position; + param_87 = _e374.xyz; + let _e376 = halfWidth_1; + param_88 = _e376; + let _e377 = halfHeight_1; + param_89 = _e377; + let _e378 = v_world_position_1; + param_90 = _e378; + let _e379 = mirror_1; + param_91 = _e379; + let _e380 = RectangleClosestPoint_u0028_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b_vf3_u003b((¶m_87), (¶m_88), (¶m_89), (¶m_90), (¶m_91)); + representative = _e380; + let _e381 = representative; + let _e382 = v_world_position_1; + toPoint = (_e381 - _e382); + let _e384 = toPoint; + pointDistance = length(_e384); + let _e386 = pointDistance; + if (_e386 > 0.000001f) { + let _e388 = toPoint; + let _e389 = pointDistance; + local_20 = (_e388 / vec3(_e389)); } else { - let _e392 = (*s_3).n; - local_20 = _e392; - } - let _e393 = local_20; - light_3.l = _e393; - let _e396 = light_3.l; - let _e398 = (*s_3).v; - light_3.h = normalize((_e396 + _e398)); - let _e402 = formFactor; - light_3.n_dot_l = _e402; - let _e405 = (*s_3).n; - let _e407 = light_3.h; - light_3.n_dot_h = max(dot(_e405, _e407), 0f); - let _e412 = (*s_3).v; - let _e414 = light_3.h; - light_3.v_dot_h = max(dot(_e412, _e414), 0f); - let _e418 = color_1; - let _e421 = color_1[3u]; - let _e423 = radiance; - light_3.radiance = ((_e418.xyz * _e421) * _e423); - let _e426 = light_3; - return _e426; - } - let _e427 = direction; - aim = normalize(_e427.xyz); + let _e393 = (*s_3).n; + local_20 = _e393; + } + let _e394 = local_20; + light_3.l = _e394; + let _e397 = light_3.l; + let _e399 = (*s_3).v; + light_3.h = normalize((_e397 + _e399)); + let _e403 = formFactor; + light_3.n_dot_l = _e403; + let _e406 = (*s_3).n; + let _e408 = light_3.h; + light_3.n_dot_h = max(dot(_e406, _e408), 0f); + let _e413 = (*s_3).v; + let _e415 = light_3.h; + light_3.v_dot_h = max(dot(_e413, _e415), 0f); + let _e419 = color_1; + let _e422 = color_1[3u]; + let _e424 = radiance; + light_3.radiance = ((_e419.xyz * _e422) * _e424); + let _e427 = light_3; + return _e427; + } + let _e428 = direction; + aim = normalize(_e428.xyz); attenuation_1 = 1f; - let _e430 = type_39; - if (_e430 < 0.5f) { - let _e432 = aim; - light_3.l = -(_e432); + let _e431 = type_39; + if (_e431 < 0.5f) { + let _e433 = aim; + light_3.l = -(_e433); } else { - let _e435 = position; - let _e437 = v_world_position_1; - toLight_1 = (_e435.xyz - _e437); - let _e439 = toLight_1; - distance_3 = length(_e439); - let _e441 = distance_3; - if (_e441 < 0.000001f) { - let _e444 = (*s_3).n; - light_3.l = _e444; - let _e447 = (*s_3).n; - light_3.h = _e447; + let _e436 = position; + let _e438 = v_world_position_1; + toLight_1 = (_e436.xyz - _e438); + let _e440 = toLight_1; + distance_3 = length(_e440); + let _e442 = distance_3; + if (_e442 < 0.000001f) { + let _e445 = (*s_3).n; + light_3.l = _e445; + let _e448 = (*s_3).n; + light_3.h = _e448; light_3.radiance = vec3(0f, 0f, 0f); light_3.n_dot_l = 0f; light_3.n_dot_h = 0f; light_3.v_dot_h = 0f; - let _e453 = light_3; - return _e453; - } - let _e454 = toLight_1; - let _e455 = distance_3; - light_3.l = (_e454 / vec3(_e455)); - let _e459 = distance_3; - param_92 = _e459; - let _e461 = direction[3u]; - param_93 = _e461; - let _e462 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); - attenuation_1 = _e462; - let _e463 = type_39; - if (_e463 > 1.5f) { - let _e465 = aim; - let _e467 = light_3.l; - cosAngle = dot(_e465, -(_e467)); - let _e470 = cosAngle; - let _e472 = cone[1u]; - let _e475 = cone[0u]; - let _e477 = cone[1u]; - let _e481 = attenuation_1; - attenuation_1 = (_e481 * clamp(((_e470 - _e472) / (_e475 - _e477)), 0f, 1f)); - } - } - let _e484 = light_3.l; - let _e486 = (*s_3).v; - light_3.h = normalize((_e484 + _e486)); - let _e491 = (*s_3).n; - let _e493 = light_3.l; - light_3.n_dot_l = max(dot(_e491, _e493), 0f); - let _e498 = (*s_3).n; - let _e500 = light_3.h; - light_3.n_dot_h = max(dot(_e498, _e500), 0f); - let _e505 = (*s_3).v; - let _e507 = light_3.h; - light_3.v_dot_h = max(dot(_e505, _e507), 0f); - let _e511 = color_1; - let _e514 = color_1[3u]; - let _e516 = attenuation_1; - light_3.radiance = ((_e511.xyz * _e514) * _e516); - let _e519 = light_3; - return _e519; + let _e454 = light_3; + return _e454; + } + let _e455 = toLight_1; + let _e456 = distance_3; + light_3.l = (_e455 / vec3(_e456)); + let _e460 = distance_3; + param_92 = _e460; + let _e462 = direction[3u]; + param_93 = _e462; + let _e463 = PunctualAttenuation_u0028_f1_u003b_f1_u003b((¶m_92), (¶m_93)); + attenuation_1 = _e463; + let _e464 = type_39; + if (_e464 > 1.5f) { + let _e466 = aim; + let _e468 = light_3.l; + cosAngle = dot(_e466, -(_e468)); + let _e471 = cosAngle; + let _e473 = cone[1u]; + let _e476 = cone[0u]; + let _e478 = cone[1u]; + let _e482 = attenuation_1; + attenuation_1 = (_e482 * clamp(((_e471 - _e473) / (_e476 - _e478)), 0f, 1f)); + } + } + let _e485 = light_3.l; + let _e487 = (*s_3).v; + light_3.h = normalize((_e485 + _e487)); + let _e492 = (*s_3).n; + let _e494 = light_3.l; + light_3.n_dot_l = max(dot(_e492, _e494), 0f); + let _e499 = (*s_3).n; + let _e501 = light_3.h; + light_3.n_dot_h = max(dot(_e499, _e501), 0f); + let _e506 = (*s_3).v; + let _e508 = light_3.h; + light_3.v_dot_h = max(dot(_e506, _e508), 0f); + let _e512 = color_1; + let _e515 = color_1[3u]; + let _e517 = attenuation_1; + light_3.radiance = ((_e512.xyz * _e515) * _e517); + let _e520 = light_3; + return _e520; } fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { @@ -43214,58 +43441,58 @@ fn FindCluster_u0028_vf3_u003b(world_3: ptr>) { var param_94: f32; var param_95: f32; - let _e181 = light_list_info.cluster_view_projection; - let _e182 = (*world_3); - clip_1 = (_e181 * vec4(_e182.x, _e182.y, _e182.z, 1f)); - let _e188 = clip_1; - let _e191 = clip_1[3u]; - ndc_1 = (_e188.xy / vec2(max(_e191, 0.000001f))); - let _e196 = light_list_info.cluster_grid; - grid = _e196.xyz; - let _e200 = light_list_info.cluster_depth[0u]; - near_1 = _e200; - let _e202 = ndc_1[0u]; - let _e206 = grid[0u]; - let _e210 = grid[0u]; - tx = clamp(floor((((_e202 * 0.5f) + 0.5f) * _e206)), 0f, (_e210 - 1f)); - let _e214 = ndc_1[1u]; - let _e218 = grid[1u]; - let _e222 = grid[1u]; - ty = clamp(floor((((_e214 * 0.5f) + 0.5f) * _e218)), 0f, (_e222 - 1f)); - let _e226 = clip_1[3u]; - let _e227 = near_1; - if (_e226 <= _e227) { + let _e182 = light_list_info.cluster_view_projection; + let _e183 = (*world_3); + clip_1 = (_e182 * vec4(_e183.x, _e183.y, _e183.z, 1f)); + let _e189 = clip_1; + let _e192 = clip_1[3u]; + ndc_1 = (_e189.xy / vec2(max(_e192, 0.000001f))); + let _e197 = light_list_info.cluster_grid; + grid = _e197.xyz; + let _e201 = light_list_info.cluster_depth[0u]; + near_1 = _e201; + let _e203 = ndc_1[0u]; + let _e207 = grid[0u]; + let _e211 = grid[0u]; + tx = clamp(floor((((_e203 * 0.5f) + 0.5f) * _e207)), 0f, (_e211 - 1f)); + let _e215 = ndc_1[1u]; + let _e219 = grid[1u]; + let _e223 = grid[1u]; + ty = clamp(floor((((_e215 * 0.5f) + 0.5f) * _e219)), 0f, (_e223 - 1f)); + let _e227 = clip_1[3u]; + let _e228 = near_1; + if (_e227 <= _e228) { local_21 = 0f; } else { - let _e230 = clip_1[3u]; - let _e231 = near_1; - let _e236 = light_list_info.cluster_depth[1u]; - let _e240 = grid[2u]; - local_21 = clamp(floor((log((_e230 / _e231)) * _e236)), 0f, (_e240 - 1f)); - } - let _e243 = local_21; - tz = _e243; - let _e244 = tx; - let _e245 = ty; - let _e247 = grid[0u]; - let _e250 = tz; - let _e252 = grid[0u]; - let _e255 = grid[1u]; - cell = ((_e244 + (_e245 * _e247)) + ((_e250 * _e252) * _e255)); - let _e258 = cell; - row_3 = floor((_e258 / 4f)); - let _e261 = cell; - let _e262 = row_3; - let _e267 = light_list_info.cluster_depth[2u]; - let _e268 = row_3; - param_94 = (_e261 - (_e262 * 4f)); - param_95 = (_e267 + _e268); - let _e270 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); - header = _e270; - let _e272 = header[0u]; - g_cluster_offset = _e272; - let _e274 = header[1u]; - g_cluster_count = _e274; + let _e231 = clip_1[3u]; + let _e232 = near_1; + let _e237 = light_list_info.cluster_depth[1u]; + let _e241 = grid[2u]; + local_21 = clamp(floor((log((_e231 / _e232)) * _e237)), 0f, (_e241 - 1f)); + } + let _e244 = local_21; + tz = _e244; + let _e245 = tx; + let _e246 = ty; + let _e248 = grid[0u]; + let _e251 = tz; + let _e253 = grid[0u]; + let _e256 = grid[1u]; + cell = ((_e245 + (_e246 * _e248)) + ((_e251 * _e253) * _e256)); + let _e259 = cell; + row_3 = floor((_e259 / 4f)); + let _e262 = cell; + let _e263 = row_3; + let _e268 = light_list_info.cluster_depth[2u]; + let _e269 = row_3; + param_94 = (_e262 - (_e263 * 4f)); + param_95 = (_e268 + _e269); + let _e271 = LightListTexel_u0028_f1_u003b_f1_u003b((¶m_94), (¶m_95)); + header = _e271; + let _e273 = header[0u]; + g_cluster_offset = _e273; + let _e275 = header[1u]; + g_cluster_count = _e275; return; } @@ -43273,19 +43500,19 @@ fn LightCount_u0028_() -> i32 { var tail: f32; var param_96: vec3; - let _e170 = light_list_info.list[0u]; - tail = _e170; - let _e171 = Clustered_u0028_(); - if _e171 { - let _e172 = v_world_position_1; - param_96 = _e172; + let _e171 = light_list_info.list[0u]; + tail = _e171; + let _e172 = Clustered_u0028_(); + if _e172 { + let _e173 = v_world_position_1; + param_96 = _e173; FindCluster_u0028_vf3_u003b((¶m_96)); - let _e173 = g_cluster_count; - tail = _e173; + let _e174 = g_cluster_count; + tail = _e174; } - let _e176 = frag_info.frame_params[1u]; - let _e180 = tail; - return (clamp(i32((_e176 + 0.5f)), 0i, 8i) + clamp(i32((_e180 + 0.5f)), 0i, 24i)); + let _e177 = frag_info.frame_params[1u]; + let _e181 = tail; + return (clamp(i32((_e177 + 0.5f)), 0i, 8i) + clamp(i32((_e181 + 0.5f)), 0i, 24i)); } fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b(s_4: ptr) -> vec3 { @@ -43308,93 +43535,95 @@ fn AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002 var param_107: LightSample; total_1 = vec3(0f, 0f, 0f); - let _e184 = LightCount_u0028_(); - count_1 = _e184; + let _e185 = LightCount_u0028_(); + count_1 = _e185; i_8 = 0i; loop { - let _e185 = i_8; - if (_e185 < 32i) { - let _e187 = i_8; - let _e188 = count_1; - if (_e187 >= _e188) { + let _e186 = i_8; + if (_e186 < 32i) { + let _e188 = i_8; + let _e189 = count_1; + if (_e188 >= _e189) { break; } - let _e190 = i_8; - param_97 = _e190; - let _e191 = (*s_4); - param_98 = _e191; - let _e192 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); - light_4 = _e192; - let _e194 = light_4.n_dot_l; - if (_e194 <= 0f) { + let _e191 = i_8; + param_97 = _e191; + let _e192 = (*s_4); + param_98 = _e192; + let _e193 = SampleLight_u0028_i1_u003b_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_97), (¶m_98)); + light_4 = _e193; + let _e195 = light_4.n_dot_l; + if (_e195 <= 0f) { continue; } - let _e196 = i_8; - param_99 = _e196; - let _e197 = LightHasShadow_u0028_i1_u003b((¶m_99)); - if _e197 { - let _e198 = (*s_4); - param_100 = _e198; - let _e199 = light_4; - param_101 = _e199; - let _e200 = i_8; - param_102 = _e200; - let _e201 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); - let _e202 = v_world_position_1; - param_103 = _e202; - let _e204 = (*s_4).n; - param_104 = _e204; - let _e205 = i_8; - param_105 = _e205; - let _e206 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); - local_22 = (_e201 * _e206); + light_transmittance = vec3(1f, 1f, 1f); + let _e197 = i_8; + param_99 = _e197; + let _e198 = LightHasShadow_u0028_i1_u003b((¶m_99)); + if _e198 { + let _e199 = (*s_4); + param_100 = _e199; + let _e200 = light_4; + param_101 = _e200; + let _e201 = i_8; + param_102 = _e201; + let _e202 = LightVisibility_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b_i1_u003b((¶m_100), (¶m_101), (¶m_102)); + let _e203 = v_world_position_1; + param_103 = _e203; + let _e205 = (*s_4).n; + param_104 = _e205; + let _e206 = i_8; + param_105 = _e206; + let _e207 = PointShadowFactor_u0028_vf3_u003b_vf3_u003b_i1_u003b((¶m_103), (¶m_104), (¶m_105)); + local_22 = (_e202 * _e207); } else { local_22 = 1f; } - let _e208 = local_22; - visibility = _e208; - let _e209 = visibility; - if (_e209 <= 0f) { + let _e209 = local_22; + visibility = _e209; + let _e210 = visibility; + if (_e210 <= 0f) { continue; } - let _e211 = (*s_4); - param_106 = _e211; - let _e212 = light_4; - param_107 = _e212; - let _e213 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); - let _e215 = light_4.radiance; - let _e218 = light_4.n_dot_l; - let _e220 = visibility; - let _e222 = total_1; - total_1 = (_e222 + (((_e213 * _e215) * _e218) * _e220)); + let _e212 = (*s_4); + param_106 = _e212; + let _e213 = light_4; + param_107 = _e213; + let _e214 = ShadeLight_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b_struct_u002d_LightSample_u002d_vf3_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf31_u003b((¶m_106), (¶m_107)); + let _e216 = light_4.radiance; + let _e219 = light_4.n_dot_l; + let _e221 = visibility; + let _e223 = light_transmittance; + let _e225 = total_1; + total_1 = (_e225 + ((((_e214 * _e216) * _e219) * _e221) * _e223)); continue; } else { break; } continuing { - let _e224 = i_8; - i_8 = (_e224 + 1i); + let _e227 = i_8; + i_8 = (_e227 + 1i); } } - let _e226 = total_1; - return _e226; + let _e229 = total_1; + return _e229; } fn SrgbToLinear_u0028_vf3_u003b(srgb: ptr>) -> vec3 { - let _e167 = (*srgb); - let _e170 = (*srgb); - let _e175 = (*srgb); - return mix((_e167 / vec3(12.92f)), pow(((_e170 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e175)); + let _e168 = (*srgb); + let _e171 = (*srgb); + let _e176 = (*srgb); + return mix((_e168 / vec3(12.92f)), pow(((_e171 + vec3(0.055f, 0.055f, 0.055f)) / vec3(1.055f)), vec3(2.4f, 2.4f, 2.4f)), step(vec3(0.04045f, 0.04045f, 0.04045f), _e176)); } fn ImpostorRight_u0028_vf3_u003b(d_4: ptr>) -> vec3 { var up: vec3; - let _e169 = (*d_4)[1u]; - up = select(vec3(0f, 1f, 0f), vec3(0f, 0f, -1f), vec3((abs(_e169) > 0.999f))); - let _e174 = up; - let _e175 = (*d_4); - return normalize(cross(_e174, _e175)); + let _e170 = (*d_4)[1u]; + up = select(vec3(0f, 1f, 0f), vec3(0f, 0f, -1f), vec3((abs(_e170) > 0.999f))); + let _e175 = up; + let _e176 = (*d_4); + return normalize(cross(_e175, _e176)); } fn ImpostorDecode_u0028_vf2_u003b(uv_5: ptr>) -> vec3 { @@ -43404,25 +43633,25 @@ fn ImpostorDecode_u0028_vf2_u003b(uv_5: ptr>) -> vec3 { var folded: vec2; var xz: vec2; - let _e172 = (*uv_5); - p_1 = ((_e172 * 2f) - vec2(1f, 1f)); - let _e176 = p_1[0u]; - let _e180 = p_1[1u]; - y_1 = ((1f - abs(_e176)) - abs(_e180)); - let _e184 = p_1[0u]; - let _e188 = p_1[1u]; - s_5 = vec2(select(-1f, 1f, (_e184 >= 0f)), select(-1f, 1f, (_e188 >= 0f))); - let _e192 = p_1; - let _e196 = s_5; - folded = ((vec2(1f, 1f) - abs(_e192.yx)) * _e196); - let _e198 = y_1; - let _e200 = p_1; - let _e201 = folded; - xz = select(_e201, _e200, vec2((_e198 >= 0f))); - let _e205 = xz[0u]; - let _e206 = y_1; - let _e208 = xz[1u]; - return normalize(vec3(_e205, _e206, _e208)); + let _e173 = (*uv_5); + p_1 = ((_e173 * 2f) - vec2(1f, 1f)); + let _e177 = p_1[0u]; + let _e181 = p_1[1u]; + y_1 = ((1f - abs(_e177)) - abs(_e181)); + let _e185 = p_1[0u]; + let _e189 = p_1[1u]; + s_5 = vec2(select(-1f, 1f, (_e185 >= 0f)), select(-1f, 1f, (_e189 >= 0f))); + let _e193 = p_1; + let _e197 = s_5; + folded = ((vec2(1f, 1f) - abs(_e193.yx)) * _e197); + let _e199 = y_1; + let _e201 = p_1; + let _e202 = folded; + xz = select(_e202, _e201, vec2((_e199 >= 0f))); + let _e206 = xz[0u]; + let _e207 = y_1; + let _e209 = xz[1u]; + return normalize(vec3(_e206, _e207, _e209)); } fn ImpostorViewUv_u0028_vf2_u003b_vf3_u003b(cell_1: ptr>, offset_3: ptr>) -> vec2 { @@ -43433,25 +43662,25 @@ fn ImpostorViewUv_u0028_vf2_u003b_vf3_u003b(cell_1: ptr>, of var up_1: vec3; var local_23: vec2; - let _e174 = (*cell_1); - param_108 = (_e174 / vec2(7f)); - let _e177 = ImpostorDecode_u0028_vf2_u003b((¶m_108)); - d_5 = _e177; - let _e178 = d_5; - param_109 = _e178; - let _e179 = ImpostorRight_u0028_vf3_u003b((¶m_109)); - right = _e179; - let _e180 = d_5; - let _e181 = right; - up_1 = cross(_e180, _e181); - let _e183 = (*offset_3); - let _e184 = right; - let _e188 = (*offset_3); - let _e189 = up_1; - local_23 = vec2(((dot(_e183, _e184) * 0.5f) + 0.5f), (0.5f - (dot(_e188, _e189) * 0.5f))); - let _e194 = (*cell_1); - let _e195 = local_23; - return ((_e194 + clamp(_e195, vec2(0f, 0f), vec2(1f, 1f))) / vec2(8f)); + let _e175 = (*cell_1); + param_108 = (_e175 / vec2(7f)); + let _e178 = ImpostorDecode_u0028_vf2_u003b((¶m_108)); + d_5 = _e178; + let _e179 = d_5; + param_109 = _e179; + let _e180 = ImpostorRight_u0028_vf3_u003b((¶m_109)); + right = _e180; + let _e181 = d_5; + let _e182 = right; + up_1 = cross(_e181, _e182); + let _e184 = (*offset_3); + let _e185 = right; + let _e189 = (*offset_3); + let _e190 = up_1; + local_23 = vec2(((dot(_e184, _e185) * 0.5f) + 0.5f), (0.5f - (dot(_e189, _e190) * 0.5f))); + let _e195 = (*cell_1); + let _e196 = local_23; + return ((_e195 + clamp(_e196, vec2(0f, 0f), vec2(1f, 1f))) / vec2(8f)); } fn ImpostorEncode_u0028_vf3_u003b(d_6: ptr>) -> vec2 { @@ -43460,28 +43689,28 @@ fn ImpostorEncode_u0028_vf3_u003b(d_6: ptr>) -> vec2 { var s_6: vec2; var folded_1: vec2; - let _e171 = (*d_6); - a_4 = abs(_e171); - let _e173 = (*d_6); - let _e176 = a_4[0u]; - let _e178 = a_4[1u]; - let _e181 = a_4[2u]; - p_2 = (_e173.xz / vec2(max(((_e176 + _e178) + _e181), 0.00000001f))); - let _e187 = p_2[0u]; - let _e191 = p_2[1u]; - s_6 = vec2(select(-1f, 1f, (_e187 >= 0f)), select(-1f, 1f, (_e191 >= 0f))); - let _e195 = p_2; - let _e199 = s_6; - folded_1 = ((vec2(1f, 1f) - abs(_e195.yx)) * _e199); - let _e202 = (*d_6)[1u]; - let _e204 = p_2; - let _e205 = folded_1; - return ((select(_e205, _e204, vec2((_e202 >= 0f))) * 0.5f) + vec2(0.5f, 0.5f)); + let _e172 = (*d_6); + a_4 = abs(_e172); + let _e174 = (*d_6); + let _e177 = a_4[0u]; + let _e179 = a_4[1u]; + let _e182 = a_4[2u]; + p_2 = (_e174.xz / vec2(max(((_e177 + _e179) + _e182), 0.00000001f))); + let _e188 = p_2[0u]; + let _e192 = p_2[1u]; + s_6 = vec2(select(-1f, 1f, (_e188 >= 0f)), select(-1f, 1f, (_e192 >= 0f))); + let _e196 = p_2; + let _e200 = s_6; + folded_1 = ((vec2(1f, 1f) - abs(_e196.yx)) * _e200); + let _e203 = (*d_6)[1u]; + let _e205 = p_2; + let _e206 = folded_1; + return ((select(_e206, _e205, vec2((_e203 >= 0f))) * 0.5f) + vec2(0.5f, 0.5f)); } fn MaterialLodBias_u0028_() -> f32 { - let _e168 = frag_info.target_origin[1u]; - return _e168; + let _e169 = frag_info.target_origin[1u]; + return _e169; } fn ReadSurface_u0028_() -> Surface { @@ -43493,78 +43722,78 @@ fn ReadSurface_u0028_() -> Surface { var anchored: vec3; var noise_1: f32; - let _e173 = v_texcoord_1; - let _e174 = MaterialLodBias_u0028_(); - let _e175 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e173, _e174); - texel_4 = _e175; - let _e176 = texel_4; - param_110 = _e176.xyz; - let _e178 = SrgbToLinear_u0028_vf3_u003b((¶m_110)); - let _e180 = frag_info.base_color; - param_111 = _e180.xyz; - let _e182 = SrgbToLinear_u0028_vf3_u003b((¶m_111)); - let _e184 = v_color_1; - s_7.albedo = ((_e178 * _e182) * _e184.xyz); - let _e189 = texel_4[3u]; - let _e192 = frag_info.base_color[3u]; - let _e195 = v_color_1[3u]; - s_7.alpha = ((_e189 * _e192) * _e195); - let _e199 = s_7.albedo; - g_albedo = _e199; - let _e202 = frag_info.material2_[0u]; - cutoff = _e202; - let _e203 = cutoff; - if (_e203 >= 0f) { - let _e206 = s_7.alpha; - let _e207 = cutoff; - if (_e206 < _e207) { + let _e174 = v_texcoord_1; + let _e175 = MaterialLodBias_u0028_(); + let _e176 = textureSampleBias(base_color_texture_tex, base_color_texture_smp, _e174, _e175); + texel_4 = _e176; + let _e177 = texel_4; + param_110 = _e177.xyz; + let _e179 = SrgbToLinear_u0028_vf3_u003b((¶m_110)); + let _e181 = frag_info.base_color; + param_111 = _e181.xyz; + let _e183 = SrgbToLinear_u0028_vf3_u003b((¶m_111)); + let _e185 = v_color_1; + s_7.albedo = ((_e179 * _e183) * _e185.xyz); + let _e190 = texel_4[3u]; + let _e193 = frag_info.base_color[3u]; + let _e196 = v_color_1[3u]; + s_7.alpha = ((_e190 * _e193) * _e196); + let _e200 = s_7.albedo; + g_albedo = _e200; + let _e203 = frag_info.material2_[0u]; + cutoff = _e203; + let _e204 = cutoff; + if (_e204 >= 0f) { + let _e207 = s_7.alpha; + let _e208 = cutoff; + if (_e207 < _e208) { discard; } } else { - let _e209 = cutoff; - if (_e209 < -1.5f) { - let _e211 = v_world_position_1; - anchored = floor((_e211 * 16f)); - let _e214 = anchored; - noise_1 = fract((sin(dot(_e214, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); - let _e220 = s_7.alpha; - let _e221 = noise_1; - if (_e220 < _e221) { + let _e210 = cutoff; + if (_e210 < -1.5f) { + let _e212 = v_world_position_1; + anchored = floor((_e212 * 16f)); + let _e215 = anchored; + noise_1 = fract((sin(dot(_e215, vec3(12.9898f, 78.233f, 37.719f))) * 43758.547f)); + let _e221 = s_7.alpha; + let _e222 = noise_1; + if (_e221 < _e222) { discard; } } } - let _e223 = cutoff; - let _e225 = cutoff; - g_premultiply = ((_e223 < 0f) && (_e225 > -0.75f)); - let _e228 = v_normal_1; - s_7.n = normalize(_e228); - let _e231 = gl_FrontFacing_1; - if !(_e231) { - let _e234 = s_7.n; - s_7.n = -(_e234); - } - let _e238 = frag_info.camera_position; - let _e240 = v_world_position_1; - s_7.v = normalize((_e238.xyz - _e240)); - let _e245 = s_7.n; - let _e247 = s_7.v; - s_7.n_dot_v = max(dot(_e245, _e247), 0.0001f); - let _e253 = frag_info.material[0u]; - s_7.metallic = clamp(_e253, 0f, 1f); - let _e258 = frag_info.material[1u]; - s_7.roughness = clamp(_e258, 0.02f, 1f); - let _e262 = frag_info.ambient_ground; - let _e265 = frag_info.ambient_sky; - let _e269 = s_7.n[1u]; - let _e276 = frag_info.material[2u]; - s_7.ambient = (mix(_e262.xyz, _e265.xyz, vec3(((_e269 * 0.5f) + 0.5f))) * _e276); - let _e281 = frag_info.frame_params[0u]; - s_7.exposure = max(_e281, 0f); + let _e224 = cutoff; + let _e226 = cutoff; + g_premultiply = ((_e224 < 0f) && (_e226 > -0.75f)); + let _e229 = v_normal_1; + s_7.n = normalize(_e229); + let _e232 = gl_FrontFacing_1; + if !(_e232) { + let _e235 = s_7.n; + s_7.n = -(_e235); + } + let _e239 = frag_info.camera_position; + let _e241 = v_world_position_1; + s_7.v = normalize((_e239.xyz - _e241)); + let _e246 = s_7.n; + let _e248 = s_7.v; + s_7.n_dot_v = max(dot(_e246, _e248), 0.0001f); + let _e254 = frag_info.material[0u]; + s_7.metallic = clamp(_e254, 0f, 1f); + let _e259 = frag_info.material[1u]; + s_7.roughness = clamp(_e259, 0.02f, 1f); + let _e263 = frag_info.ambient_ground; + let _e266 = frag_info.ambient_sky; + let _e270 = s_7.n[1u]; + let _e277 = frag_info.material[2u]; + s_7.ambient = (mix(_e263.xyz, _e266.xyz, vec3(((_e270 * 0.5f) + 0.5f))) * _e277); + let _e282 = frag_info.frame_params[0u]; + s_7.exposure = max(_e282, 0f); s_7.occlusion = 1f; s_7.emissive = vec3(0f, 0f, 0f); - let _e286 = s_7; - return _e286; + let _e287 = s_7; + return _e287; } fn main_1() { @@ -43621,176 +43850,177 @@ fn main_1() { g_premultiply = false; g_cluster_offset = 0f; g_cluster_count = 0f; - let _e212 = ReadSurface_u0028_(); - s_8 = _e212; - let _e213 = v_color_1; - d_7 = normalize(_e213.xyz); - let _e216 = d_7; - param_112 = _e216; - let _e217 = ImpostorRight_u0028_vf3_u003b((¶m_112)); - right_1 = _e217; - let _e218 = d_7; - let _e219 = right_1; - up_2 = cross(_e218, _e219); - let _e221 = right_1; - let _e223 = v_texcoord_1[0u]; - let _e227 = up_2; - let _e229 = v_texcoord_1[1u]; - offset_4 = ((_e221 * ((_e223 * 2f) - 1f)) + (_e227 * (1f - (_e229 * 2f)))); - let _e234 = d_7; - param_113 = _e234; - let _e235 = ImpostorEncode_u0028_vf3_u003b((¶m_113)); - g = (_e235 * 7f); - let _e237 = g; - base = clamp(floor(_e237), vec2(0f, 0f), vec2(6f, 6f)); - let _e240 = g; - let _e241 = base; - f = (_e240 - _e241); - let _e244 = f[0u]; - let _e246 = f[1u]; - lower = ((_e244 + _e246) < 1f); - let _e249 = lower; - if _e249 { - let _e250 = base; - local_24 = _e250; - } else { + light_transmittance = vec3(1f, 1f, 1f); + let _e213 = ReadSurface_u0028_(); + s_8 = _e213; + let _e214 = v_color_1; + d_7 = normalize(_e214.xyz); + let _e217 = d_7; + param_112 = _e217; + let _e218 = ImpostorRight_u0028_vf3_u003b((¶m_112)); + right_1 = _e218; + let _e219 = d_7; + let _e220 = right_1; + up_2 = cross(_e219, _e220); + let _e222 = right_1; + let _e224 = v_texcoord_1[0u]; + let _e228 = up_2; + let _e230 = v_texcoord_1[1u]; + offset_4 = ((_e222 * ((_e224 * 2f) - 1f)) + (_e228 * (1f - (_e230 * 2f)))); + let _e235 = d_7; + param_113 = _e235; + let _e236 = ImpostorEncode_u0028_vf3_u003b((¶m_113)); + g = (_e236 * 7f); + let _e238 = g; + base = clamp(floor(_e238), vec2(0f, 0f), vec2(6f, 6f)); + let _e241 = g; + let _e242 = base; + f = (_e241 - _e242); + let _e245 = f[0u]; + let _e247 = f[1u]; + lower = ((_e245 + _e247) < 1f); + let _e250 = lower; + if _e250 { let _e251 = base; - local_24 = (_e251 + vec2(1f, 1f)); - } - let _e253 = local_24; - c0_ = _e253; - let _e254 = base; - c1_ = (_e254 + vec2(1f, 0f)); - let _e256 = base; - c2_ = (_e256 + vec2(0f, 1f)); - let _e258 = lower; - if _e258 { - let _e260 = f[0u]; - let _e263 = f[1u]; - let _e266 = f[0u]; - let _e268 = f[1u]; - local_25 = vec3(((1f - _e260) - _e263), _e266, _e268); + local_24 = _e251; } else { - let _e271 = f[0u]; - let _e273 = f[1u]; - let _e277 = f[1u]; - let _e280 = f[0u]; - local_25 = vec3(((_e271 + _e273) - 1f), (1f - _e277), (1f - _e280)); - } - let _e283 = local_25; - w = _e283; - let _e284 = c0_; - param_114 = _e284; - let _e285 = offset_4; - param_115 = _e285; - let _e286 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_114), (¶m_115)); - uv0_ = _e286; - let _e287 = c1_; - param_116 = _e287; - let _e288 = offset_4; - param_117 = _e288; - let _e289 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_116), (¶m_117)); - uv1_ = _e289; - let _e290 = c2_; - param_118 = _e290; - let _e291 = offset_4; - param_119 = _e291; - let _e292 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_118), (¶m_119)); - uv2_ = _e292; - let _e293 = uv0_; - let _e294 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e293, 0f); - a0_ = _e294; - let _e295 = uv1_; - let _e296 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e295, 0f); - a1_ = _e296; - let _e297 = uv2_; - let _e298 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e297, 0f); - a2_ = _e298; - let _e299 = uv0_; - let _e300 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e299, 0f); - n0_ = _e300; - let _e301 = uv1_; - let _e302 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e301, 0f); - n1_ = _e302; - let _e303 = uv2_; - let _e304 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e303, 0f); - n2_ = _e304; - let _e305 = w; - let _e307 = a0_[3u]; - let _e309 = a1_[3u]; - let _e311 = a2_[3u]; - wa = (_e305 * vec3(_e307, _e309, _e311)); - let _e315 = wa[0u]; - let _e317 = wa[1u]; - let _e320 = wa[2u]; - alpha_3 = ((_e315 + _e317) + _e320); - let _e322 = alpha_3; - if (_e322 < 0.5f) { + let _e252 = base; + local_24 = (_e252 + vec2(1f, 1f)); + } + let _e254 = local_24; + c0_ = _e254; + let _e255 = base; + c1_ = (_e255 + vec2(1f, 0f)); + let _e257 = base; + c2_ = (_e257 + vec2(0f, 1f)); + let _e259 = lower; + if _e259 { + let _e261 = f[0u]; + let _e264 = f[1u]; + let _e267 = f[0u]; + let _e269 = f[1u]; + local_25 = vec3(((1f - _e261) - _e264), _e267, _e269); + } else { + let _e272 = f[0u]; + let _e274 = f[1u]; + let _e278 = f[1u]; + let _e281 = f[0u]; + local_25 = vec3(((_e272 + _e274) - 1f), (1f - _e278), (1f - _e281)); + } + let _e284 = local_25; + w = _e284; + let _e285 = c0_; + param_114 = _e285; + let _e286 = offset_4; + param_115 = _e286; + let _e287 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_114), (¶m_115)); + uv0_ = _e287; + let _e288 = c1_; + param_116 = _e288; + let _e289 = offset_4; + param_117 = _e289; + let _e290 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_116), (¶m_117)); + uv1_ = _e290; + let _e291 = c2_; + param_118 = _e291; + let _e292 = offset_4; + param_119 = _e292; + let _e293 = ImpostorViewUv_u0028_vf2_u003b_vf3_u003b((¶m_118), (¶m_119)); + uv2_ = _e293; + let _e294 = uv0_; + let _e295 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e294, 0f); + a0_ = _e295; + let _e296 = uv1_; + let _e297 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e296, 0f); + a1_ = _e297; + let _e298 = uv2_; + let _e299 = textureSampleLevel(base_color_texture_tex, base_color_texture_smp, _e298, 0f); + a2_ = _e299; + let _e300 = uv0_; + let _e301 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e300, 0f); + n0_ = _e301; + let _e302 = uv1_; + let _e303 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e302, 0f); + n1_ = _e303; + let _e304 = uv2_; + let _e305 = textureSampleLevel(normal_texture_tex, normal_texture_smp, _e304, 0f); + n2_ = _e305; + let _e306 = w; + let _e308 = a0_[3u]; + let _e310 = a1_[3u]; + let _e312 = a2_[3u]; + wa = (_e306 * vec3(_e308, _e310, _e312)); + let _e316 = wa[0u]; + let _e318 = wa[1u]; + let _e321 = wa[2u]; + alpha_3 = ((_e316 + _e318) + _e321); + let _e323 = alpha_3; + if (_e323 < 0.5f) { discard; } - let _e324 = a0_; - let _e327 = wa[0u]; - let _e329 = a1_; - let _e332 = wa[1u]; - let _e335 = a2_; - let _e338 = wa[2u]; - let _e341 = alpha_3; - srgb_1 = ((((_e324.xyz * _e327) + (_e329.xyz * _e332)) + (_e335.xyz * _e338)) / vec3(_e341)); - let _e344 = n0_; - let _e349 = wa[0u]; - let _e351 = n1_; - let _e356 = wa[1u]; - let _e359 = n2_; - let _e364 = wa[2u]; - local_26 = (((((_e344.xyz * 2f) - vec3(1f, 1f, 1f)) * _e349) + (((_e351.xyz * 2f) - vec3(1f, 1f, 1f)) * _e356)) + (((_e359.xyz * 2f) - vec3(1f, 1f, 1f)) * _e364)); - let _e367 = local_26; + let _e325 = a0_; + let _e328 = wa[0u]; + let _e330 = a1_; + let _e333 = wa[1u]; + let _e336 = a2_; + let _e339 = wa[2u]; + let _e342 = alpha_3; + srgb_1 = ((((_e325.xyz * _e328) + (_e330.xyz * _e333)) + (_e336.xyz * _e339)) / vec3(_e342)); + let _e345 = n0_; + let _e350 = wa[0u]; + let _e352 = n1_; + let _e357 = wa[1u]; + let _e360 = n2_; + let _e365 = wa[2u]; + local_26 = (((((_e345.xyz * 2f) - vec3(1f, 1f, 1f)) * _e350) + (((_e352.xyz * 2f) - vec3(1f, 1f, 1f)) * _e357)) + (((_e360.xyz * 2f) - vec3(1f, 1f, 1f)) * _e365)); let _e368 = local_26; - let _e371 = local_26; - let _e372 = d_7; - local_26 = normalize(select(_e372, _e371, vec3((dot(_e367, _e368) > 0.000000000001f)))); - let _e376 = v_tangent_1; - worldRight = normalize(_e376.xyz); - let _e379 = v_normal_1; - worldFacing = normalize(_e379); - let _e381 = worldFacing; - let _e382 = worldRight; - worldUp = cross(_e381, _e382); - let _e384 = worldRight; - let _e385 = local_26; - let _e386 = right_1; - let _e389 = worldUp; - let _e390 = local_26; - let _e391 = up_2; - let _e395 = worldFacing; - let _e396 = local_26; - let _e397 = d_7; - s_8.n = normalize((((_e384 * dot(_e385, _e386)) + (_e389 * dot(_e390, _e391))) + (_e395 * dot(_e396, _e397)))); - let _e404 = s_8.n; - let _e406 = s_8.v; - s_8.n_dot_v = max(dot(_e404, _e406), 0.0001f); - let _e410 = srgb_1; - param_120 = _e410; - let _e411 = SrgbToLinear_u0028_vf3_u003b((¶m_120)); - let _e413 = frag_info.base_color; - param_121 = _e413.xyz; - let _e415 = SrgbToLinear_u0028_vf3_u003b((¶m_121)); - s_8.albedo = (_e411 * _e415); + let _e369 = local_26; + let _e372 = local_26; + let _e373 = d_7; + local_26 = normalize(select(_e373, _e372, vec3((dot(_e368, _e369) > 0.000000000001f)))); + let _e377 = v_tangent_1; + worldRight = normalize(_e377.xyz); + let _e380 = v_normal_1; + worldFacing = normalize(_e380); + let _e382 = worldFacing; + let _e383 = worldRight; + worldUp = cross(_e382, _e383); + let _e385 = worldRight; + let _e386 = local_26; + let _e387 = right_1; + let _e390 = worldUp; + let _e391 = local_26; + let _e392 = up_2; + let _e396 = worldFacing; + let _e397 = local_26; + let _e398 = d_7; + s_8.n = normalize((((_e385 * dot(_e386, _e387)) + (_e390 * dot(_e391, _e392))) + (_e396 * dot(_e397, _e398)))); + let _e405 = s_8.n; + let _e407 = s_8.v; + s_8.n_dot_v = max(dot(_e405, _e407), 0.0001f); + let _e411 = srgb_1; + param_120 = _e411; + let _e412 = SrgbToLinear_u0028_vf3_u003b((¶m_120)); + let _e414 = frag_info.base_color; + param_121 = _e414.xyz; + let _e416 = SrgbToLinear_u0028_vf3_u003b((¶m_121)); + s_8.albedo = (_e412 * _e416); s_8.alpha = 1f; - let _e420 = s_8.albedo; - g_albedo = _e420; - let _e422 = frag_info.ambient_ground; - let _e425 = frag_info.ambient_sky; - let _e429 = s_8.n[1u]; - let _e436 = frag_info.material[2u]; - s_8.ambient = (mix(_e422.xyz, _e425.xyz, vec3(((_e429 * 0.5f) + 0.5f))) * _e436); - let _e440 = s_8.albedo; - let _e442 = s_8.ambient; - ambient = (_e440 * _e442); - let _e444 = s_8; - param_122 = _e444; - let _e445 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_122)); - let _e446 = ambient; - param_123 = (_e445 + _e446); + let _e421 = s_8.albedo; + g_albedo = _e421; + let _e423 = frag_info.ambient_ground; + let _e426 = frag_info.ambient_sky; + let _e430 = s_8.n[1u]; + let _e437 = frag_info.material[2u]; + s_8.ambient = (mix(_e423.xyz, _e426.xyz, vec3(((_e430 * 0.5f) + 0.5f))) * _e437); + let _e441 = s_8.albedo; + let _e443 = s_8.ambient; + ambient = (_e441 * _e443); + let _e445 = s_8; + param_122 = _e445; + let _e446 = AccumulateLights_u0028_struct_u002d_Surface_u002d_vf3_u002d_f1_u002d_vf3_u002d_vf3_u002d_f1_u002d_f1_u002d_f1_u002d_f1_u002d_vf3_u002d_vf3_u002d_f11_u003b((¶m_122)); + let _e447 = ambient; + param_123 = (_e446 + _e447); param_124 = 1f; param_125 = 1f; WriteSurface_u0028_vf3_u003b_f1_u003b_f1_u003b((¶m_123), (¶m_124), (¶m_125)); diff --git a/site/content/core/backends.md b/site/content/core/backends.md index 6ca5d4729..238cca6a9 100644 --- a/site/content/core/backends.md +++ b/site/content/core/backends.md @@ -404,7 +404,7 @@ Eighty-three entry points. `kRequiredShaders` and the bundle manifest are kept i |---|---| | Vertex | `MeshVertex`, `MeshSkinnedVertex`, `MeshInstancedVertex`, `MeshLightmappedVertex`, `FullscreenVertex`, `DebugLineVertex`, `ParticleVertex`, `ParticleMeshVertex`, `PolylineVertex`, `ShadowTileResetVertex`, `SkyVertex`, `SkyCubeVertex`, `ImpostorVertex`, `VelocityVertex`, `VelocitySkinnedVertex`, `VelocityInstancedVertex` | | Lighting | `Unlit`, `Lambert`, `BlinnPhong`, `Pbr`, `Toon`, `Normals`, `PbrLayered`, `Impostor` | -| Shadows | `ShadowDepth`, `ShadowDistance`, `ShadowDepthMasked`, `ShadowDistanceMasked`, `ShadowTileReset`, `EvsmFilter`, `ShadowCopy` | +| Shadows | `ShadowDepth`, `ShadowDistance`, `ShadowDepthMasked`, `ShadowDistanceMasked`, `ShadowTileReset`, `EvsmFilter`, `ShadowCopy`, `ShadowTransmittance` | | Post | `BloomThreshold`, `BloomDownsample`, `BloomUpsample`, `Composite`, `Fxaa`, `Reflections`, `Ssao`, `SsaoBlur`, `ContactShadow`, `ContactShadowResolve`, `Splat`, `LightShafts`, `DepthOfField`, `ViewportShade`, `Luminance`, `ProbePrefilter`, `CameraVelocity`, `Velocity`, `VelocityTileMax`, `VelocityNeighborMax`, `MotionBlur`, `TemporalResolve`, `TemporalAccumulate`, `Reactive`, `Easu`, `LocalExposure`, `LocalExposureBlur`, `VolumetricFog`, `VolumetricFogUpsample`, `DepthPyramid`, `SceneColourCopy`, `WboitResolve`, `DofTileMax` | | Particles | `Particle`, `ParticleTextured`, `ParticleMesh`, `ParticleSixWay`, `ReactiveSprite`, `SplatHashed`, `ParticleSoft`, `ParticleTexturedSoft`, `ParticleSixWaySoft` | | Sky | `Sky`, `SkyCube` | diff --git a/site/content/quickstart.md b/site/content/quickstart.md index 95bc4714b..c55dc3687 100644 --- a/site/content/quickstart.md +++ b/site/content/quickstart.md @@ -91,7 +91,7 @@ tool/ci.sh # shaders, analyze, every test (cd packages/flutter3d_physics && dart test) # plain Dart, no Flutter needed ``` -There are 10883 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. +There are 10887 tests across 43 packages and twelve applications, and only about thirty need a GPU: the Impeller half of the golden set. The other half renders through the software backend, so seventy-eight scenes stay checkable in a headless run. ## Your own application diff --git a/site/content/reference/testing.md b/site/content/reference/testing.md index a336f562f..ffa4c9aa0 100644 --- a/site/content/reference/testing.md +++ b/site/content/reference/testing.md @@ -1,14 +1,14 @@ --- -description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10883 tests need a GPU. +description: Four independent golden sets, mutation-checking every new test, determinism and snapshots, and why only about thirty of 10887 tests need a GPU. --- # Testing -10883 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. +10887 tests across 43 packages and nine applications, counted the same way the `the document says how many tests there are` rule does: a scan of every `test(`/`testWidgets(` call. The rule holds `ARCHITECTURE.md` §13, the README and this page to the answer. The README went on saying 1242 across thirteen packages for as long as nothing compared it with anything. About thirty need a GPU; the [architecture](/core/architecture/) is what keeps the number that low. | Package | Tests | | Package | Tests | |---|---|---|---|---| -| `flutter3d` | 1620 | | | | +| `flutter3d` | 1624 | | | | | | | | `flutter3d_mesh` | 599 | | | | | `apps/flutter3d_modeler` | 1750 | | `flutter3d_sim` | 591 | | `pad_input` | 67 | @@ -40,7 +40,7 @@ description: Four independent golden sets, mutation-checking every new test, det | `flame_flutter3d` | 140 | | `apps/flutter3d_showcase` | 88 | | `flame_multiplayer` | 9 | | `flame_multiplayer_dashwire` | 3 | -The rows sum to 10857 rather than 10883: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. +The rows sum to 10861 rather than 10887: the remaining 26 live in `packages/*/example/test`, which the count includes and this table does not, among them the two seeds a new project starts from, `packages/flutter3d_app/example` and `packages/flutter3d_game/example`. `flutter3d_samples` is not in the table and has no `test/` at all: it is test data with two path constants over it, and other packages' decoder tests are what exercise it. `flutter3d_conformance` is missing for a different reason: it is invoked as a script harness rather than through `flutter test`, so it does not surface in a grep of `test(` calls either. See below for what that cost once. From b104ed3152f713a46edb3f8a5c4b390450d421d8 Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 14:51:43 +0300 Subject: [PATCH 056/141] The bench's glass and liquids cast shadows by what they let through The glass had its shadow switched off because the engine's shadows were all or nothing. With translucent casters on, a tube casts a faint shadow with darker edges and each liquid a shadow of its colour; the blended tabletop casts nothing, so it is shaded like a floor. The page explains how. --- packages/education/chemlab/lib/src/bench.dart | 8 ++++---- .../education/chemlab/lib/src/glassware.dart | 13 ++++++++++--- .../education/chemlab/test/goldens/bench.png | Bin 1936077 -> 1936077 bytes site/content/education/chemlab.md | 16 +++++++++++++--- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/packages/education/chemlab/lib/src/bench.dart b/packages/education/chemlab/lib/src/bench.dart index 4dfb0e37d..e899a7c23 100644 --- a/packages/education/chemlab/lib/src/bench.dart +++ b/packages/education/chemlab/lib/src/bench.dart @@ -212,10 +212,6 @@ final class Bench { glass(), name: '${vessel.name} glass', ) - // Clear glass barely darkens what is behind it, and the engine's - // shadows are all or nothing, so the glass casts none and the - // coloured liquid inside it casts the shadow. - ..castsShadow = false ..lightChannels = _vesselChannel ..setPositionFrom(vessel.at), ); @@ -265,6 +261,10 @@ final class Bench { name: 'bench', ) ..setPosition(0, -0.02, 0.6) + // A floor casts nothing. Said here because the top is blended, for + // the reflections, and a blended caster is left out of the coloured + // shadows so it does not shade itself. + ..castsShadow = false // Its own channel: the sun reaches every channel, the caustics // only this one, and the fill only the glass. ..lightChannels = _benchChannel, diff --git a/packages/education/chemlab/lib/src/glassware.dart b/packages/education/chemlab/lib/src/glassware.dart index 176165cd3..c26bc972c 100644 --- a/packages/education/chemlab/lib/src/glassware.dart +++ b/packages/education/chemlab/lib/src/glassware.dart @@ -218,9 +218,16 @@ Material liquid(Vector3 colour) => Material( Material paper([TextureHandle? label]) => Material(name: 'label', albedo: label, roughness: 0.8); -/// How the bench is drawn: under [labSky]. The tabletop's reflections are -/// mirrored geometry (see `Bench`), so no screen-space pass is needed. -final RenderSettings benchSettings = RenderSettings(sky: labSky); +/// How the bench is drawn: under [labSky], with see-through casters shading +/// the sun by what their material lets through +/// (`ShadowSettings.translucentCasters`): the glass casts a faint shadow with +/// darker edges, and each liquid a shadow of its own colour. The tabletop's +/// reflections are mirrored geometry (see `Bench`), so no screen-space pass +/// is needed. +final RenderSettings benchSettings = RenderSettings( + sky: labSky, + shadows: const ShadowSettings(translucentCasters: true), +); /// The room the bench stands in, as a sky: bright overhead, a pale horizon /// and a darker floor. Glass shows almost nothing of itself; it shows what is diff --git a/packages/education/chemlab/test/goldens/bench.png b/packages/education/chemlab/test/goldens/bench.png index aac4b083462087a938f326f874e56b52924f69b0..47ed4bb489ea8cbd0c2e398050c65a95a25c38eb 100644 GIT binary patch delta 34134 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 delta 30318 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 Material( ); ``` -## Shadows: the glass casts none +## Shadows that know what the material lets through -A shadow map knows only whether something is between a point and the light, not how much light that something lets through. Every caster darkens the bench as much as a brick would. Clear glass that does this looks wrong straight away: an empty tube threw a shadow as dark as a full one. +A shadow map holds one depth per texel: whether something stands between a point and the light, and nothing about how much light that something lets through. So every caster darkened the bench as much as a brick would, and an empty tube threw a shadow as dark as a full one. -So the glass is marked `castsShadow = false`, and the liquid inside it is what casts. The empty top of a tube leaves the bench lit, and the shadow starts where the liquid does. It is not physically right either. Real glass dims the light a little, and a coloured solution throws a tinted shadow; neither is possible until the shadow pass can record partial coverage, which is work for the engine; I would rather leave the shadow honest than fake it in a demo. +For a while the glass was simply marked `castsShadow = false`. I didn't like that, and in the end it went into the engine as `ShadowSettings.translucentCasters`. The atlas is a four-channel texture of which only red held depth, so the other three now hold what see-through casters let through, per colour. A glass or a liquid is drawn into it after the opaque casters, tested against their depth and blended so that layers combine, and the lit pass multiplies the sun by it. + +What one surface lets through comes from its material: the share that is not there at all (one minus its opacity), what its transmission passes of the rest, tinted by its colour, and what is not reflected away at its surface, from the Fresnel term of its index of refraction. That last part is why clear glass casts a faint shadow with darker edges: light meets the walls of a tube at a grazing angle near its sides and is mostly reflected there. A coloured solution casts a shadow of its own colour, and light through the walls and the liquid gets all three. + +```dart +final settings = RenderSettings( + shadows: ShadowSettings(translucentCasters: true), +); +``` + +It has limits. The atlas keeps the colour, not where along the light it was picked up, so something standing between the sun and a glass would be shaded as if it stood behind it; a bench has nothing like that. It does nothing with the moments filter, which uses those channels for itself. And a see-through surface that casts is not shaded by it, so a glass does not darken itself; the tabletop, which lets a little of the reflections under it through, casts nothing and is shaded like any floor. ## Refraction, caustics and reflections From d86fb0bcffe4a099a3015edec25e03ea07b15c4a Mon Sep 17 00:00:00 2001 From: Dmitrii Zolotov Date: Fri, 2 Oct 2026 15:08:27 +0300 Subject: [PATCH 057/141] The bench's shadows take the colour of what is in the tubes The coloured shadows were there and did not read: only the sun's share of the light is tinted, and the room was lit by a sky as bright as the sun, on a dark grey top. The top is now light and warm, the sun stronger and the sky dimmer, and the solutions let more light through (transmission 0.85), so a tube's shadow is mostly the colour of its solution. --- packages/education/chemlab/lib/src/bench.dart | 9 ++++++--- .../education/chemlab/lib/src/glassware.dart | 2 +- .../education/chemlab/test/goldens/bench.png | Bin 1936077 -> 1936077 bytes site/content/education/chemlab.md | 2 ++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/education/chemlab/lib/src/bench.dart b/packages/education/chemlab/lib/src/bench.dart index e899a7c23..c458a7972 100644 --- a/packages/education/chemlab/lib/src/bench.dart +++ b/packages/education/chemlab/lib/src/bench.dart @@ -241,7 +241,10 @@ final class Bench { scene ..environment = environment.texture ..environmentLevels = environment.levels - ..ambientIntensity = 1.0; + // The sun carries most of the light, as through a lab window: the + // coloured shadows tint only the sun's share, and under a sky as + // bright as the sun they were barely there. + ..ambientIntensity = 0.55; } scene ..add( @@ -252,7 +255,7 @@ final class Bench { ), Material( name: 'bench', - baseColor: Vector4(0.2, 0.22, 0.25, 0.92), + baseColor: Vector4(0.46, 0.45, 0.43, 0.92), alphaMode: MaterialAlphaMode.blend, // Glossy enough for the screen-space reflections, which fade // out by 0.25: the liquids show in the tabletop. @@ -270,7 +273,7 @@ final class Bench { ..lightChannels = _benchChannel, ) ..add( - LightNode(name: 'sun', intensity: 2.4) + LightNode(name: 'sun', intensity: 2.6) ..setLocalForward(Vector3(-0.5, -1.0, -0.6)), ) ..add( diff --git a/packages/education/chemlab/lib/src/glassware.dart b/packages/education/chemlab/lib/src/glassware.dart index c26bc972c..ac8b33929 100644 --- a/packages/education/chemlab/lib/src/glassware.dart +++ b/packages/education/chemlab/lib/src/glassware.dart @@ -202,7 +202,7 @@ Material liquid(Vector3 colour) => Material( depthWrite: true, extensions: MaterialExtensions( ior: 1.33, - transmission: 0.35, + transmission: 0.85, thickness: 0.06, attenuationColor: colour, attenuationDistance: 0.08, diff --git a/packages/education/chemlab/test/goldens/bench.png b/packages/education/chemlab/test/goldens/bench.png index 47ed4bb489ea8cbd0c2e398050c65a95a25c38eb..1c5c87b5c10494e0a5900fd21236e405143c145e 100644 GIT binary patch literal 1936077 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 z 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

    -

    Writing a fifth one is a documented job rather than an archaeology exercise: Writing a HAL backend covers the whole contract, the ten semantics that appear in no signature, the conformance suite you can run before compiling a single shader, and the ninety shader entry points your bundle has to answer to.

    +

    Writing a fifth one is a documented job rather than an archaeology exercise: Writing a HAL backend covers the whole contract, the ten semantics that appear in no signature, the conformance suite you can run before compiling a single shader, and the ninety-two shader entry points your bundle has to answer to.

    Writing a fifth one is a documented job rather than an archaeology exercise: Writing a HAL backend covers the whole contract, the ten semantics that appear in no signature, the conformance suite you can run before compiling a single shader, and the eighty-eight shader entry points your bundle has to answer to.

    -

    Writing a fifth one is a documented job rather than an archaeology exercise: Writing a HAL backend covers the whole contract, the ten semantics that appear in no signature, the conformance suite you can run before compiling a single shader, and the eighty-three shader entry points your bundle has to answer to.

    +

    Writing a fifth one is a documented job rather than an archaeology exercise: Writing a HAL backend covers the whole contract, the ten semantics that appear in no signature, the conformance suite you can run before compiling a single shader, and the eighty-seven shader entry points your bundle has to answer to.

    ?P^Jbumh&>W;j!t%|}Y{->jggMiyp4AHoPBBXj3 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[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

    j!JQq|$;8=S_KZo=G=#LM%W&KN=9c@ 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 z zrqpnSaWth)@MZ59QVb?at4JVB(y;e^nrLH5(D!%2x+{MDO{;BvdN*l`MO$Pu6^WIc z`C@u`pS4)O=p`M;@s3&u{?ysNE<-bPWS`wymXaK3Lk|Q8&@FAc&Pj6aX&elV9l%xA zeS%VNmOz!!!k6yC7gx`fY@I(XF@fhepoD$Q*#V_G*ky{Ulk;y<$Iqgp=pWLe;?ZxD z%%hcFmvcWkmO2PbvXB`M8QeJK%PIM_O`^$sndvEn+(`po7lplNxw84$u;Rd><NXb=pk*cU&wKqoL*qi06&3GB5*YA|~x9d`%AS#R4hq9@?65iiaw^k8RMmVpCzd`#Bd7+h44{-_<2uoQ)K z|5CHg*s1Kpmx3&#H2?0LPa&e}m-OO$Df~qlJ#$F+UjE*scfP6@bH^#Dr*O-Vi>5|X>V$vdh;R4YkQVj?(Xpd`i!5?^ARfd5a9^EXwZKhpyS1YujK!;FsPPVif0CY#K{k z6J7ZF^fH1U0Zu@6LonQ&LBf9yi3X?(uszG$AX46}U8jwk3fDD$6S~O(M#7-RNEw#C z$MoV?D8;$XZ2#G^=;j+9gbX&*^Kz!Ye9nU!D`sk|q*-Lu+?@|g%!Az#NMC3qlk=K| zg^pKP<76STB`UnQgrBnXx4&w2D-os-^3;JdngfT{p4Q($)#c`2L?D(I5m&_xs~{VQ zY(~wWD6zHx57BvUgPq`D)@0e@fFC~%xm1UMYz!n$2-o%gJ&S#Ctf6~(@N`hxxGn|B zmN?fpF>`it+EevmPt^OV{*Byp9HKC02ZD~v@JtbJv89<}2_W7APMx}hEi?b?JpJav zd~`}@xW0ANl<9O=z1gh-ad<;$@P)VVJKD)K+3uhFkuNMP)5|NMJD`&C?eYL<=s|;r zN@=_Af#tFB-Y%I=J+07$t$KZ@aBr}78=;2xlmJc zqoW3S|6%X^U63&qF0q(`7kFU11rbBJDA@1j6cu1 zYTj87GH@GK`X5Q>;gIC_zx~RUT4|aq2brd3j@%nED^qiCP0777_eLl+&5>rgM}ACm zkK7}LoaD~j6Sp}LQ2{~t@qM2E0M32jKIeL0*XxQF^u#r=7BgZ$7oNWlgG&N`cR8u# z%IC;b%z73}FhJFkl*lJB?bPJC$QWSq!5sZN_!a??jyFV#>MtTak(5w`2PcXC3m))pDpp9Fa6`_N7X# z_>)cheQrO(*CO$RWiCV78>5*I5J$d)QbjlS4ECoMFXlln!u$JH_bUe$uSo-O*qZf8 z0Wr_e%^O*_q_&H={zW%XZ}*bgve^N_^BJ5Sliuoe`NIht>eFc zT1#j0?}w&0Ssw{=K8m&wz~hJX&Gz^z5=ARug3`YfLic6$H-P;c*>giD2FV`atZ%wh z%S1zqON}@e*sv^&e4@P`km$pLxmOIqP+3|x5Zn&TkV7<7j@;SJ1V?aEL+;cHvy`KY z|5>k-VMO#1WX-3$1tm;MQ3Hys`-no-yOp|d5s<%yp%MiR7aGv_ZRz40S?X&;n;uT3 zwh)*gyvnDwp{i50>M~cs8n30TGoMW2%S~;((%v>RW+(K{2A{Cg{yE@F!EaRfGzA`e zc8*9-vm)fRnIh?TKCd9#U3>bI4QTrc= z`8K3L?xS`4R9pmb?zV1-4&R>fcyy@z^L+2efK|qxH%O_)Vq+J*aIQZ5pHexWfWuvD zD?K*~83dgsrG%9;$@) zh!y0e{=+_cY@#(EkGx22O4>d3z9I6P^Jp_ls8dz`+Rd{QqmrF%Wq(lyxaq#9JqP`9 zx{v0-z?!^njgst@>uu@ZXaFZUP9&tR?hAPzpFg)yY6D!3?dmfSU(H}r5fae6;!L-i zu~t&|=oZnD{Cc|b=a`RtqaY6qFg9U+w_$oqie+GrS_ehs1QYz_C~fff!j8EFS>p6t z;bQWup)ZaCYSkYq-vWLWTr=wDxDU*aT=J~vf4h~4&UunTkaO)31&&qsqSVpWfIJ;t z(~y9imWS#^MjFwc9zJ*FfO0*i_C1X``6{I!QA<`l1v2@dez%#o7?Y$<)H?_3$%>#YwFFsgA2Lv#uv9A0Dd9sal-nt&~n_v5Qp0@6Emnej~h@_-le5LwDBlda{ z_9FPx_b!4&_O}<^C0jXfo`8xPv~t#nAMa_oO|YRpprgR(t0;Tw3l|*agP_9{B&R@P zshD9JQvKjb0f84l%7tNkC~lON+6l0n9wb-yaf`m{ov#5`ew}5iQM@N~jBUKcCG&>$ zXo1S>|2>~Fzq(R;ea$TOpC7tWQU}LPn!<*^38O{T4sXW@jEd4F0SDo`C*^GM?Bbql zyC%1us|SHwwvxi2ZCq#bD+}qpx(o52G=Q4s>WBY1Sjq8ZzQbr1P^t z`IkOP3~uj%0GA+zoGOY?JaZ*hqM< zQ>M+7nWF0;5j(^CpT;|HxUe!|!sTtErp~SA18d`>2PQ~StCO?uN8c1}-X#r{H`IRu zy76?F2{SfEpF3wsc;%romqsWw;4M&2#*O4l7vP@T21?ptbBBtW&o2uVw~SI z2L`Md28zxz`K@6Oxu_eiioHO@KH+}t z&FO;9!Rv4b(Rr^sZ7f?vk^%7dYfCb(MUw;d#jmNI_cfAj=-&RGwi%RN#MU;ok% z#sV{!L6x%ZXOiSLI6rhM5nS8l_NINp5qhGX2(p%+1w=2#IZ?!KKSC2GW@Nn&=j!lX zHlk-JV9Se)aCAy8esrpKPhDU2H;7mY56KTsh#1uMwz#GNwfy2;B; zqki+$<7fsh3fl~r(UJISS3-H&B}8#f`W0P-(2k+uz0c)8QmeummXGHK2w7GlVHVe0 zu}R>Vm7ii*UsxU{yIwrcJC-M-m1v3+!go^Ept}nT58G8Ho|<9ozJeEPQ%!cn@%fJm z?HOk;=cswpKVJ##-B4NRq(yC%+;$YvflRk>QbCCJfpmb7&6i3v<`oCnFJm;xNYdIq zxOD8N-AJ7iEhJPtY}RBs+3*VGjJ(Y8DdX=|I1q7X-xK?a6w$L3K}n|UAxoVh`zx-J z(mojP+SE{cUj_^eeXfXyaljC4e#yA|`@ZRLAY8sH9mngH@nYNyt{SMjQ8{&Haq zJ%A69(7^R^Cfw%On|DS>dp7{65q8Kt{ju=YJg?Q4yIW4QhQEQRP0p)tL0lCo2+D>u zIYx#?!Z$c;Ev&7dR*6Tg-=r1oDi!Qmf!w-Z81wvt;&e_t!PRuGOzW9#P1HDn{REV! z(rS9+^x)DvkrAn{)?9A;@ryPOa%o8aUyvo`UG&r0U&_Jnm9poB>9KRq$r6%Wv>DB& z2Nlel)gXejeE839IGr`MoOwZsWha1=IAK%1e9q=|K!PnVVwr}ru8yEqMM8c+|CV7d zlL^q(=C3YPTo8aXYe=atX8qMrdSl!Dro4~+WkdSkIyF5(=lzND^WF~mUYUzICTgsl z0~W$!Lq@)ja>w_)aU&u}@r>Pu3w zvjP_uBF8R=%(45ZVSn>d)y=;)oM_T5h-<$==_A>L;l-Uz6|HNKrb4L%z3kc!kzWJ$ zmEMEL=MpkQ-8Ac_nhhG~)_)N^Q1e&D10Y@!akJ7PqfhzG9S`H9>>Gr~q#zf?&!+d8 zUqgbc6cZ~@Xt6=Bxwpq|+|@gJmaI0uN;#dbS}U&(WT|^BNt%rK=B=bjbX9neDX)UaiS3j`B@nn?2lBFW#TapV7s zZ%G*so}+{Yi?%HzHZzkbBUr>u6ncjI5v8(-<0#-+3dTXHtY83T?Mv@AsiO=0czoz+ zDrJBA1Eu8{PmSx*f}{ADu4!MK>GWkss*5~H+*Qmy`K7x*C+G$LN)6$h1bCxB)N~27 znt?d;wKgjGdRd_~PY?y-rE)Lc%Ag^I3O8@LFO$#xV>Hs|W5Eo|K*}iO)RNqZ)*pHV zmeof9{qvB9ZqhuwP2JIuz!J_#-mEEQyg6kFlm{kuPfb<5d||ffUI|fTRGu{*IlR5` z-((RM6lD1sse$|HDEVH{BR5v0!2r^TK1^AtlhozdAY0vo8+g^*y>{boG8A!m(-wy_ zNzyD=NzG~k;E#WA2d7*iYWIj5er`7KYO6#^AGKWgcy=q4d%NC7*5Q5B$iV@ymf7BD zBJn36)?{|V4i_TV2EtgU zmrw!n(x0zS5C8tG8S@<4RbOpf5qjVEQF%8#thX%24Ok@D-g?jMe288ql|k zP0s`IlMDZTL(aPU0v4v9zhLxUqfofa)pbvb2uZ`q(aP+P_e%01`I>`Bd7`6R(&}c= z=-3Exj~i4KWD9IZZ{qDAJf%+XUiM5Vs5O{c znR8__Ts|(U<$-l>;rPp5`On{q zYUEon4+B=)MsL?iiTV!awusKS@w)*iet&tx<5CViN<5}_Zon!=_u4i-&&k}Fe{FM7 z<~VuUIgrF%6#Ikav3m(0BJiNlGO-3L#d$~D)vf802g)Y+i}wy%uUQISTjVw2rv7f< z83hbD$}X0N?DHE{TL%Zkjt$-_bzFS*i_7A|s! zFljAH^Sjs6Fk9)BEeY`{^Nj{lj0v6VhDTFf-um617fxVZ!ib+sLZ_0 ziSzp@$Iht8P;ZO;XH$2tC!~F?1u^MYr*c1QNbH-x?n@aBa|`+k?um0${qyEXbl1`3 zt+Y?^qv=+$hX^SHuG*%A{r!nW@`Ny1c)5lT#*y}5b>!veyWM+3Qa1qfIqp)@6jxGj zspzBSk1+q~25DDPJM3Tb$-h0jpqJ!Nx z7ln3mQ(Vb*@Y+bPRq4*Eb+@fIq05GR9m0!-TSlx9xr#KDSv6E^o%v!^n0yCd_D$la zgx>t0h)@3>Q(Lax{%O9_L%oLB2WuuDbt0<&q&Eb;IPymO4Bc<=wt5^Cd|{b1Bk*xd zd^l3I>|BWIoC@csk_Z^dVNgkO4_2?n$R%WZo%slR4_|2)8bD_1yC)?R8{tanW|;(> zYoK2ge^vyBOIg}H!Cn^pZs*usl%uZl_}T8q)}$nH#H=Mk2yuKM%yykvlpnB|?pKQz zvPC88%T&K*fBA5*ct;m)uv@4l{pe5aZlXu=+oEMPy zU>Pwx)eW=f>}dy?Js|@JgW`t+73=4jV$;3AdEFc*$=_u!L4{$jMdT%vBBHmG4=UZ} z+w(>Y3WL8*Xzf`a2?r4iQ(#*}aO5DoA~j8zir+7EI-KX%nYI2YrttN-Orgr&D}(9m z63t}+*n6e1xE(oCYx(0=+4%jkY{<|$;(~-&A=;zH#wdL_=y3pyo!(8J7+5I82dUKP z9yAc;J`BG9a^tFHn@Ik2rL3q!gsix3l6jYitLumDOHmzo{2LXTde#NHq_6H=A*&<* z$-y)SMCvy_(Y#OWz1_<^q?#OU=k77D^YvxrOxkW)b_f31R%3BLvQ}~RYQDMXb;IJl+uEx9ce|LBJN|9#3UZkc z_|+tR^NJf^ass}5uX#ah4GPYwEa<>UMo3Y?IPV*OFe;ib@rfKa8?Oda>KV0nt8iGM z!$(wN?n>#ynovYV<}k~#!-Q#V;J+eQFXY+Q`w1NnyFu3){{k5l=Ux+4%kUwa?H2#J zDzG-A0?kmHYY&e05e<1ui25G$jgjPyL2qx0^$$_c6*oML9;OUY*?z{|BzkHe z5Yk6{(vj#>HSt6COcA(P3j_B*(6#@7FJ8&O^moVY_IyE{&QLN=yeDw4x5hCVDB2#E{K7N3KxnNreX15}NLDI^i$Ac=3bP*#JiUkwi$#Dk&K%@$t7 z0+G4S?(lp7AeEcweRr)qqQh|HtRiGhP9_>Y*V2R9oquTvSSc72FN)sqPp=|vExpQ} zogswv5HF~zhMBAxZv2x;ppJZ)a2+~zly#egXG#~qvJ;aZ&#f{7&4$zq|FbP6ykCMl zBRzsb|7*dUNu<91Z~fl4tv6Uj!YBOmf8J#05|?ZX2H@xAB9DfU6yx~-$c(*x>4`>8bhbf$URB6*i|irp#*gf`RoCa` zI!EX@d6xBLfYA-D18=#%nT2i!Ug4Pv6WQVFIb!Dnu9o$NA9!>p zt>H11X;IbDyvr^7^2d;cT_bF=zx4i%pQaCFbB5*3I7LiLg!19d&#a59=?rrQUpvEn zV>VP>Z;*%e7PyDwIa+zyU z$%&eY2G5-Z%V2L8ttj%q`U?)ONiybW8@m4qy!Zez=7+6lOX00sh+g{ho8OhL^Kdzj zCSV|I3$rhhd>+vu!8l@wzlr--){IS;(0jcoykfe+A$S>Z9(Da4&F=yW4;mGzvCO{& zO}mXCypU(6d2N0{Hf%U$6o$L{$cdYD_2l}9n%u}zO*A?WPzVWKfL=lkGviO6QSV8_ zt4D~`^%fd{>kI=~av}|np`t7)-bX%SNJ7Ra@I8ef>y*+|t;R`j{~{3?uiW50I-=6Kdhu!4uxD*Gp|y%4yZ zQ$!S}wGcj{na8%^Xok6CDPuAn*p>#=E$ik%vO#g({k=gXqC2MYpl zP*6yk=Cdmw`HZ5Q=A+R5SXH_aZ0Ee8oc}Cq3AMb~_i5uyY}dP6RNbqDUFSu<*hb8bM*crEl^=i4{=*vk$LWJxt@^2_K<*H!o3$;BA!z# z8N4CA4ABM)SAPw@FWlkI#qV(RaLZnk4c2~r(<;H+B=IJ?RiFIkfRRuiLAnGqgW$+x zHXO6?Z>mlwWKETKgx={7W)KE4=Wua`WmO{IdPvcq_=8HeH*22(+#l;7M|_)dn6De; z{Y&2kV_{ccp2tSxh>j4xcPzLEdR$Spnk=I!IpkoPw_~Xhih+<{=Sb4xdUR$q{*Lvb zI&@HgqN=lY@&4z25`q@sivA($n@N+p_}!L@R^fukzFuMly<=%PVOS68Du~p$k#>-} zL~LndfSuP;tSv{`cyL-%^;qZEP9C=#nNxErvzwTDcDb~Tiu7ikTi-kvu2z=z6iM=UWs2H&9c4HX?g70F-D=v$ zg7L=%glzDDHxO6dUaF(@Q_o<+VxL#^d(q#{O*e-zCc%F5B73#6CwIDI>U_zkS`v=&ThFSfEgRb#s}_D+UB^E00= zCa$3l)TM-JHi^ope_`SB&hb-z*rtcfUIXbC{Y&WSKzfYjV2#k%dSBlP^&?R--$v6< z0wTL0p0G_)&x6UfgXKUX9Ink|RE5}p_nLe8VzR6EAK@wc@B~~oK^5{dg(Cf{#XMQA zUY^v8RM4GpY{73H&!A#z@10gi{@V5w=Hd%xIjH9)eP+UHNBJOYabG#MQl_bOW`eS; zdo(M6y$Ow7$)oaX*3z~VpPvZm(0VO6W~$H;2{u&du#EK&EV*Yme=d3|yglUSqcU1M z|I?;{Nnl-Q`|xh$xc$4Tr^f!TM!}d_mC=yJT4(qhDlQkQeAV3CQcf;{0;3>7XG;Z8 zu2{{s5Hn!fW=2&dcYL_##}HT3NoaelvC&yzO9kRM4h}y_Q@zq=79Z=96AsKV6xiO^ zZ}+B)VG^4v*X4HNwX%tDNDny|?YZIF?-(7FU?ZOsWJ&YU)5*V}bLbl4EV2SW4=YdG z>BPl*7V|CM)0lH9eJ=d(Fa>j(PP%*)xpV$h#8i`Ol=Mf+J{PNv4QOJUdB<;^yp3OB zSVk-155LBNOFciB2_JbcIEOfoVQFfxJzq{l0+tQ*Usu7qP%i{59v&_!d2&7QD3lfG z6?MBRzBN+zwg>+X@NIbD+7sZB-20Pf^QW^^;OO%Aq)|C!m;B-_e#16-mLdL=cxhY! z+gP*hrcpggUWA{~&nGe#(e}@Sw;baZr*#qL#O$FKBKzoCR9A&g%HqF%;rxO)JbOo~ zn!c=E?5EUCI*+FM3WwyA&hO@n=TNEZn@91#i*%f1!%vtlq zLpowVHIRnXYAeIyMObW043e5(IK4s=hHD7G=GF{9qxyLvNS`RM64sPu3kjSgbzB2a z-PohUTgH|5otj@PO2ctIu(ew9#u#bDvNMCt{9bKMYWiu#;b)5yw-j-~eK~COOUM*Q zVhwD0z({n+0Sk_%jN=1bU94}o$T{E{NI4fl`G3t%lN(%L>3>zzd^!tUmH1&0PPR4V z&nUK_i_cm@X#84D3n~$uap-r&)D`~D(tnZhhDpKm!6cunh+Hs03SJ9*J zpKOWCCGg?%uT+?~%2l-ThMfb+o#V zi?phOw9|kp!v>|{v5|9@+BLi=+19+>>7sGe?~Q}|_VycCdiiq37$*pGlg5k6q9C7& z5?juK5%_l4-VNB9@;X)sl9hx?2GIdpAj7*VptE^GTNf_nHq}K=OO2RyTt?9%fXeXR zmN|qpvZW7&@5g(jtce~eK{*dW?~??N4rUlo0_$?nbF(Q!sMHAm$BHZL;scsi=A}HZ zMo~IkkgeJ=?9*j;mxa;8uTijGSQiST7dLr8xVJtNOqf|M*mJ`ajKSvCPfgFFPVeLI z*Bl0T6WOT>q1cJ}dZgRB` z<~rSt)}+6jMl;`1zL~D)*rVWx1mPo7%lmO3e}v`Taz59I>kJfSMO1X0UAXsd6C0O^ z9}u{OwF|k!qk|^=6ygCslp^~#r!$z8fWT^z91gdG=Ug^*A$2X~jqhhL^h#6Kci{kh zllJ1j=6BkOx&1$s(EZY5la zHeI@9xcj@nEaBQp6^u!0rVgk-y?qmqd^*9!MB=hu_}7bv#7RRsbnB1a97T8@_Dch| z)u3FYM{yR}ZLq^y%Fk!Ep)#uM0t#N!tP_VXR>vQtdI9|Z!QMrx@jGZgZdY`wPPiwf zk@cl}NJ8vow}{5|<+Gtd(!MAVi@G=u=|_ERdT#eMDRt&mm?E|0bXDOXmp(;3txSy5 zJN*zITUz;8C8J|zU{O-$VL(sI(&? zrzHvY6G9I`?h&Zy?Yx0lw_P8z14p&(zL5vI+ub#K zWjV|KMA^hW8nsu7`Tp)jU%rL!7pCXJZ+;W|IC8lX9wpIe1y1v`4AhJ2)>*4B^-Hls`@y zY}A9H;JBB6+*`CAs^iX2N_Z;O1R7omtmN{n^_UG#?El4xW+%QZ1Te6*54`k6rT3P8sa<^EE?o3av93d2dwRhezWwOA-{>z#n z;uN8dl(UQCbu9;q6j^X|U3uBv{-zQp49UV`+DREEu!)lWt@#=5Jn0LDo{{C$5nTo7 z#Gkb^lpm!{Q~CS5oBR3&dz(H|kSfaY+l<6oxb}Lj#zF3pwN;otX$Q-HE+|e*xC5_y zM)8OAx9~#h7z6?0neilBMg-3C&O~|cwx0p!k z1v-w@?Yz#bdS6P18fwZ8nWVBNig(O8sKkB1jw2)87vMZ~>>b8^ zNWxWvcGMYRdBrfUwT&&1I!M(xsy-xs-~h0l|3Xba{>p26D6Ad1tF<6WeyGZDu16Szm=8-dWTG<%$H0bn~ z8yEUa==XW4qtC$n76JZOgrQb#6@B$PY6LF6qgiJ~`NVS6fQ^EbA zPv3QK!iK&^s^(Uu=4vA%q(yGWLF*ygmLubP_RI$9wP9TIdX}f-$Sc3Qqjw&&uuSp* zi38~K(p>=lT`g=T1bXl#wWIH`+gDoL>jLVhUXD`CkG9(795#;#Byvi8)O;94$~2K~ zvF`Xu2C+gTqmT5C_H3_FgtB2X7#dVnjU|O34cSSdOiEL_VP`JB!9`~8bM8H-WosnX z8tZa=7v^6A;`5E)vtNv~8)?iFNu;ZXI#+)o`>$_iHiqJ)7#%BA>AM4r4 z@rAV6*6y9LO%X5)glu{>OVQ+M+2<#Fr!J?yAbDFzb^8hJ34cISqk6A)x^Va!VA98! zQ-DW%EoItbyP?BzhS}Qg+tb5Jk%+a*v9y)M|AJAN=+1Fy=o4?$bLAyncOmRm)C~yi z0f|@W{+gwm@wkbx_@!7vVL1(*VCS@+^DBHE{|zv09Enlw zQ6U_#yY5OC4lR5Z7>BB~S!@`$Q(|nqHlQ{(7v3KyK)FuVha5$8u2$(K4T`w!^HF3# zz1pB1mrdHfhwmem6dBtGy486pCBCBQ#C9$jp`Ftv4%JW_1ED1Q|@o6JE2*8pGJ)-yh(N_Zl80&PJ% zkgj=OgB<+xH=(lRt>0Pi++tFN_2c#=zuvD8iejT?c>i4(uLqG936O;m&+x zy==4v|6W{=t?B!)@30-y85}Sh$}L{^vtk<*J^1Is|5<}761;zvZ@fvY0e*~B?A7Q& zNBjzYX7bjQTKuEo_@*=`^k(|MwHzF@V0oRIkAswlv4A%$N}+n1E=h-}*O?wXmhn7f zc@au{UYY$n8*fqMbF{q)s-gSpaHuv+V0eoT7_)VWO!}`j!Q$33d}owTyTcgJ-d7;i z*rlZUNBMM_3z21QmCmS2AASy<%4lpD(K{q1-AK}!ve}{tl9P(*DZymRvq6@)+yIgq z#ZCD4Qt%i3HQ8-al7!?|9RH`^py4lppXEZZutdX!v$_w}wA6Wv5IGGPmfxAD^!Wz^ zxrR~*I(d-XI<))^XeD@;9U=+K!=y>65V0v+juj&Est?L_E{@0rEKG^7S%v3 zphfliCwET|o$nyXZZqT{NmN3=;4OzmNsU~7bvlx5Zl8o@UyJ`eiTKf@oM_4PXgb%N zgz>OTyDs`Yy3&;YofIN#ft%nUj`>1=)0VQzMWW!9Ki|%pGdRBS%hrt)H6-t& zp4e6`*gxsZo^3hQt?bd!_`t(|PuTqBP>e#eb8ua}Y4rS*fNjvjk>Kt7%lDo-oYDw9 z!+dHWh?rO=TH>6PZV zdLip#J+ac(fz|UNhPEpy(MDCp!a#rQ;q6h)jB;UMBEJZ)%KlM`CPZvgtWFP^E}47x zjcG9J(4zhq0Ih!5^lRCAw^fVitc0y$4a|q%M8R;%ZC&>dpb)%-k7O-J0Uh6{zLsQ=zrV z8NU!RKkX2}x@~LznoX+#!mJ0Om!Q1)w~ei~s8MWraD*LC%(+{~vK=v@B&U7Y?Fake zIMus%4$Zc8$u{_Wkgiv@(Q!sGY(nwQK1F8NuqgyfEZv=9|TotB-ef<$Ouy9$t=Lc&wnzM z*GH_3?K7);iG7=NG(Lc`);}l_>@)muvHmwk{yOttS{EUmH%FeF+LGlK-H~p`v^ODx z)KK8q@cQ~6Q6LoW@pI62wJic-fGO6h@}O@?MK68gKkW{D*gO` zSJtZs7CLnh)8eTMyK| zZua**Ks+SaY!nJzk@`i>wr}#?=e#) zO2)~ZUb)@)uf>zevI_2uO|CJaW97EE%73BZ#;wx!+b-zh7 zvjjC_)Lxbpq8ugO@xyOi;^}l{Ey1c4GI_6>zSPINro9c>(7e(dvEjem$O)F0b3)Hy z*Uw0_*W>6>eTbH}*8(P2)I%-SxCn<43ECo@1L=vCcUiIqnV+S zz<_ViTa(7y;3d0j0YC!*h*Hac^ta^P?{UP>StSc=rse*vnM!_gHnUguco~9rQ1N7Yh%y z9A0Di@Mu$jBb_7D)^)Q7!wg;B=cWqCky#l@lvAt|lC<(RuQTN|pbIBiH~JJ}F9L$b zx8iP5R#Gpc1V&)XBZxKT_(prK~!CzB4OkH-?BP|YUn2ze-K6f_sTMWDpUK-Uq1MoOHciCoT!ib{QYFj zw-@$WSf*316DfaB))-I~o>l~Lq>pEL@HOY)cyQtn{fM{s-6E_Q!e1q2o1*vohMl>>p#;Y z573o3pdWvxXT5Sh1k`aFJMjeIuHGE?<>zhb6_rtA>94^4@ObZR?j$dH=1xwS*OPv( z<8Zcd0|F;l-|-e~IYmw(0UgD1U8pqKFsmf&Ms2ZqiuM&nNi(h}_34=v;=;NY>iiIl z`YZ69Hw3#L_l!3Pei{j&`%31<|oplf`nvg49?c=$Zmh%7kw$h zf5$`DGEvUIva^D4V;3J`EjKtW!8N@{%JfVmvY=-MCVK+bsRK2In35RKx(#Qr%5>H& zNb(A6VZHi(1AtP4jyTOfDBb_$I+|=+7dGEwsRj{T95HOamkKCcGdB2j+t07YON}2a@+rUO^KHumo7=Rsn?> zebKuOw<3mSNzOBC*)e`H7yF#5khspGpm*1F97>-ld$jS>!?Ts%)y?*TJ>fte5Iii_cry zZ#+dvSB)X@X%8@}%F53LKhF^^Q|s9ZTwA@~H$}0ybXz|5crQ>|mhAoSQR?6m#j32x zvz}}(XtwVzUxoRcV0`h{+0NfplUD;UP4XgXwOuW|B<(#|ki+hWjSnnRQb{Z`6!jNN zfGUcMglgj}uaDDmJF=TqO&OIwbJw8=l{m3Bf^3(x?pz+;I?g=!QE49f$akzz0QhU> z*;re}Gp|<*%jUgI7Ygf2exK^u3oNC30p&ab-(#k%VX+0dx5Jew0nj#)=KPc7uLUws zu>dwO=am{^6A}W6{1Sjs zfMej@X@eF?Pv7jd+T>wSH@@vek{Trgyo$P+kG@mTO5$Wcs6Jh(VW=u#DlAA4E4Wqnh`IjI;yvnCLf4+FAdC`Qr zIIV?zvyuZc*~678#+_ZJjFV~>J7TCD-Zs*J zd}mM6S0yEhgqd^gDU?~rzhhVzZHO?RiZP41cwn;=l|tj70bY`zLE{3TWhcj%k1nJ@ zMF;Dul!Zc}+Q9sR@MM@AD8W!`)ckKZr=k1lqfCzUp=9~PyU*x&_LDR0dDc^DEpne{ zx^72%&1JNHt&KKBe|UK>7+~WWh-dIhhl~eF{5N)0O~H%Fy8jaBofwEX5X?bJiwqJs zxQvNq{HSs;zJC8#=(4vWdsWFmfOt>v`n&dH$^Qk|(qz?{X%WjGz@EI4)QcK|mU2#u zfYTiXLHDx?+^}dsC>f2xn$M42l)|>-D7~=LbrAMxUDOZH>y}QHYgyOk1tzO+eu`Ju zo8EPB`#7OXyp$aM>it1H#{p1oXF90qVHd#TU)6B}pNtqD+1%x}K6YKq0$U&PAtQ?c zmN&3VZ}#{RdxhApyhGe6xw?HVR^SDei;wS9^gOXG{bNQ)3!Mi;`lo+7{Y)51H)z&U zAL(t6+VYEcQhlkr-nn)2A{Od?-67EC%4;8E*Die^j^9GgZDx*)Un2Wow#mq^N$n&X zmp}6J=UcQI5FdYgt50gS_#=|9NUqVT{w?UqMD^h{vbcu3W46hc{G}hGza@YMr?0`r z77A92cGXW%G;!5+|BU_xh!(7K9R-NXB^JyKJSwa>_dWh1ZN(mq%$*%vl7?y%L4=vD$Ib(ittd`8aY*CO_1RPZNnVvE=ZT|c5+ zcxouqHVFY2wst$6j(2&k%)D;{Dc+cd-RftFHQNw^1k_U3gts*#BW#jG)#cmTld$Mz zgrZ_hw9lkNHhGY&YT1~|Q1hJ+bk$Zk?uAs33Pr)~V(&<^Q?0*XMya1+UaZ?sVT^m*p?b`=SFHz+I>TaR-YC@T*K^1BobngcEP?Q~{Ci_eh zO1v|JpOu7XuPm%ZYyHOFW4BIAn|XQ{$PU0{>#)>5`0iHWE?jad7017|lIx7M0ROae zxQW;LBG3!I@$SnUFv(^uQ=`9yYb6uJOF1C54zob61VP;yVI!4KJEC3V6$4{bPl}t6 z9S=u})Y<*+|6~ZB#VMefJH%uThucz)#q+83qgqJ)U%7e4@WX0-}sG&s- z4cnpebLT~(jvEW{j?OZtWoF25;&!Lu#FixR`^hz9=CMWw z{AO}ys7{b?5Kp16dznx)C+;dJ9)%8B+2)5bk`G8#>zvY2C(Sf^LmmCFSI0Cpgv(nd z@n9qgwZirKl7G}*GU_2TmuYovk2Jij>k&hL{lh-ZFvxTfcrNMli)&`_p*$}f4nHmk=mIAl%eNSP2O#GnXoEcb9*27eog>1 zOL86^kw|@Tc?H35^BEusX)n+&9pL)dcYV1o*p3?%i7{Jq-i-Xg2lC*~*A)s3Pi)2J z(&ilp3bF-U{XTlA}Frf*LD#N}0k zfOV-cITMoK{!`r68@L6!@;C4;`0&hlZ4Zyh*tvU?4HU%IzfvkHsg0__Bu-7elU^*H zfuLe)&R#Me{LfNgNFL_W16HJOT}kkcyx;rY!ieWMO>Q&hX{yx=d74`SO}}5gjbBJz zPAFIM`||cOO+$lI$AWmocULcG!xIv{0aBi~Yye{byNR6Eqan>wj5 zy8;a=Ck~$O>5h(hT3){|Klpc@2kw9OJS5sK^H<0=*+gh5wJ=GhspM@|+0_QE1|t&x zJ?6#a_Vb8VU+=_ru{+hu<(|AtB!pdK+^!vAmr)QL`ft$iGydx(yrkA4pt$6$gAR-FQOW*w|3n?eO^Zjkjcw7ZGNOqy&uObE?3 z@Vp4({q2Kd?04$<-^q01ytk58y8c0$Dnu6A$5pQSAhp^yA$MgaSoIq4#lRa3pnrQC zf48P*Ry}hrmG2z+<<<0hDEX2jNOIiL;q)|A` zx0RiKwTi9eBAOH#Z5Wv&aJrIs~06Uh3 z-o0OB7gs)XzgY+^EjKk$XmD?F$%^&l#8G)4wn{mTMi=1c+kT*h%>BbA!ybxX`UrOX z*pIjH=~goLv465rtIq&)IBTz8LeF$X|1i>D4|gY zZ^fJc)DX?8V??$vt(4Yc!)C1`demfi!=(LsHAe73U?NP8ayPSXRZ3tqWtqc;?1QBV z9)_hK5&!IP-woe?uojdF`-SIN+RuZTGD+v$Ns#p0&;@kMt)2E|Ruvz` z)dA_R+)e(|;t$t(An{q?z4uM;8rlwj^N6aMHR4t#3u%5kRNUh_X4U9R@dnypF#xi^ z$ru+<(B5MyLygzksx1BbT%o41RcBwaZDmvK#eP?oG2#82ja#DYZFfYE_gu|2TC>`d zG{1mcdXn7rgOZ0+X)HBWn&Vh#x=YL9;6Meqc)>+)$i+W31R93lWq_mgP#)l)5AUZI zht6|6;e^tjB}7o_h{A+A0t=7THFW^cHYb@afXYezvA^+@YMHA;qCo7296* z2JNi!@?d;54H$uLLj+&EHFE5U6@Pv#4GgDkp>y0uJ*2Qd3Fvee9a2wsxy_2NiK)K} ze~t(J+Ljb~+ zax;NA<9hKh@y+(RL?REu`DKN;Cx>@aGJs~)6*PFlda4@r)E4cd7LF^YGJB%3$` zFmdE&73X;M^i&M+5ozOG5USCo#03G9grMhfeJxz;Me)=9h8fgD2F95r3))H9Mt)7A zWWl%~#Qv2%5}x^Jh)Q+Vr;;VWQLqr`R2bBpV?6+Azv2UVCcQs3`$PtRZ@Y+*Tz*%Q zO5G+2@=Vqj(o+v1q(H!JX8Am@O=R%DnRHtwAPc{cWsO>Gdbornt-0YSvJba_$xT8y zlS;(5)nOM);w>E(o1kM8p(Y(PykQ#QNIVgY+GBIc{zBfv^|g1tJ8Q-<;AYmj)b4p@S>x~vH$@+(%YlUV z+aX{Eobyo-X)7^Bl&;d~ZsUFGw!5OR8Ve5e7PDNwLX@Pbl9m?a`#iS^r1Td4?tVwqg9= zFw1hJxd)ZGSB`RnrDbl-J;}W_H8(J= zzfS{+dF`K09pF3pnXTTguwK`C9?4P%X$oXBmd9>AP8=N}#DioN4?@Upr=wF3)}KGi zEZeGlkk#rLzRz1;jfqyp2rut|eHUkoL`+gr zY`tB&?|haU_@j_`y+QDR$!Uu4;Uv$}qwZ&}wN2jZNY&|BfQo^$D#*!s^ZnzqmM8~i z^7r6}wDvAIZ1}Hu5Nh41^{Uun(5LR=eFicMxu$_V>MhlS+(@8YMulC@aEAWTgQ$Y^ ze*{rB7;$3`li#m)@1fyiq&^gWnsfr7f>0zZobq*2#yJw9;<_PExjcLAZg$hlr0>iO zbXnC_8uE5|k7Kp1ulR0gyy9~53>&Y?Vvw5?Q_?z1c?@xh6D+GZ%);J36m5F0HZDB9 zytk(g5*_!mxzh5e>w^2wUn=mI!36oEvsgjl+vy1X@QA&sFFSpJoP)gNV4czA<6%*882fdn)1AWAG~<>6)4Ifc zo^1~+@?A>4_}Z(Ys_FqcH0#oqa)<@u^1JD3flr70sEHc&29dS%7g2YAB86jr{ZnG< z9u=@UZ7S{&`c)z8*GIx!M7f^NiZ(UIGo(gn}!_4|-pieocyq&%HR~xPQ{_45Hn07spu{p?U z)!^1dc0R_I)mKL`{FdxfJ(i(m(u(%p-o1?39dOdMtv3(S^+rgX#3L>-Gu2`xlhLod z!IgbZ4c7=}nt3GTT9kU$#OtCz$x|Y`|H6PzEAa!r-$=fcqz2+>g?|>zMW{q9gw@m6 zy0mMNbMDv#d^O=_*iYno@ve%pj7#eW*xZoH|9vu1v1~7fAkwr@#}?6sQ-ZUNTH2@k z!k;xNZ2}%!mbBQAqwT!TIstJ3-IpMN1s9k9JJ8alJeS93C~>~xHFotHHo6a1!Q*x` zVd|7|{2f2$VXD%WHZR+CUC9-hi6H2(nBn_xB6T@vUZNL$c(+k`SS|E)15WIs zv?Dj#ji9@GCqT+!ZMNR`^b2Bx)O(OS2&c3|0ut;g z*2#JO>Dy|5ZgSQL^31N=b_B_W9F#lYL>&Ev9h5+cXev%d-pQ0P<(0w*34`CBUqm{d zAl_&89IXA``_a#-`N~oY>7)KW(!V0cVIiko7cf5qx4&|r02_^`ORU)aJMPyL6szi+(jL$VFw|T43sV z@FajI68eO&e72O6LAbIbGR_3L_tU&lReau%v-E1&C;UL>y7ldbUZAkU{;%r?{`MQ4 zMDfn;w1(-(Df!=kQRYv}mp~tXA=yxqQb%1ul<_=IkDL6ESt7^{$RuR{JxK-8!&9jg zAy2ZGV}9!d35g$j<5o-Scrp@3ExH`ONZ2Y}VC@Za`Y5siQgfgK8A1YWW0Ep{lluwd zJK=0p_qh(IGi1CcuhsL1CS~=ht zq*SJ(MYN2FUHM`;YRO7W(lZE=eLWO>flnNo|h`C5QA1IV0Zu_?d+Gj^{it=!q~WE#_t zNATONrqMy)B7srESs87Zvv)ecGLH}(q^~Erao8jzOE4&w&13li>GXa!*l1G~GVrrg zW~ZAoHk;Ky(v7Db}v30MB<0#hDs-%P2 z>b2>xXy*EBVrULv1frT)M`@<%FH8)9`s^2<^Lg&uu}C|>zk5qb1G!qQc0$}m zo0I)5Gws!&N6zpw3fU}5Xr|M~K{iJ}!I%psT2N+F&At3Iw*q(XSzR5|kFd*2Yw1f5 zE1R_m%R#6zgViiL7hYEP0mVz;KZVQxMB>m_E;ZSx-uO0a#3p_=s|1gmT zBr+!j2EsPW*x32J{kNa+^77VhmFD-S-G6h~Vr#k;tl;C#y(XUA@o+&N$ISX@(MA&+9%^v1%dIlA z8U#&1Ck~Ho5NJlVR&M0F=2f&J145-9_bf-78zo}=7l032T`VS*{GK2?(Luyr$ttTG z($y)?R-G;2fRwGAUYVstX^Wtp{-|=a$x~t#Ve|7ZzDp+*Th7M;ecXKu}27y8aXpK^^#cRu3mPdQh>O;n>z zb{+13!r+X6jKK980ZV@cAI5_lXr5bG3B?i-yk?g4ZBRi|qO!Uy!<^csqi`xjk8Buv z&zCt2v44Klf!h1GMvm;4_tiVd{kk%D+QyHV>c^5l-(FfKlLBwMQz}h$zPhcpF$6;c z_2*yKEkVHGxJRbsD$&){n!)wN)cad7h^s5~Z6in_YO`7d8L)&BD2L%rwu{={{`}Du zvKCyQL07UK0g$^m>94c=(_g z3f@h(^f~L_M}f9PmnjUb zQ0TdBgh)3J0PDWtE{f z)!*AsElk(ZKFslB*#OR1%Hd)7Yvf*#nkg66egifq1&nNAEMChRMuKu-83fmr1{X3) zlGbHlAxG#zA|)U&b;J-keSgT!aKf9FplL8g=(-UHLH)*`;7F`sM5TIXC~eVbchf!Y zsRvTf%AKn$7OtV!@m}&QK>``8S*yHQlgqCvwcuJP)c-Z1S}{?hSz?Zyq+YlRxN))S$0nq?07lEMTvVpSC-}8ctRsO(*#E3F6CR{mfq2=j zG`=j6w8J#64*CqGJnPER^e=EJ(u?^>NX|L72}3`$rp`aj4AAnI2)(EmFqL*pLdauk7{% z&vGYhd=ADg`bu4Ukn!OpXr18E{yY&jOSPx;WnJ`mAzGRHc6puDt2jxbKF15JX<`0Ut&j?TRah$L6^ZUkRdwS(+8}A^58pD?QnPr4(Wf-_Q@T@Z&VU>m4t}M zpuv4n6zo{k_{K)2A67at=%uX$gN<^~z@{`$(dSedQgLk37D&(bNM5&_x78!1Si9t^ zN6D+-vK<;|E$v=tACvu|mz?@aO_xOJYJo5^HqbC|wWo{;3V zPVn(No2y$8oDq@n>1vLD4W!HM}kd3&cx?_ z;?20e91Fs!4fbsJnaD*H9Xw0Bl8DQ5RB~vnx_7U3arW4+qV;C|p|hP|%Gd8dx(pRX zAR89!Y3eg?YHfLLb~Os~5ul$RBpk?^UHYJR8b>g8&&x=Al~ppwX&HJ;szk>FOY^_k z$GQE603fW3s*Z((!%0U)!i3%3w>jYwFQ~fyovW$T5tdo685e@65O$5Vf=TY-gd8`$ zY|6mBwlG_hv{yfVHQku~3I1p+!Z`IJL0wXhFR_{7-J@i=vx%FT%6VmfJ;>1rq{&1l zqsXvawqc$4VP)$H(q-!^Xxq*_;LGz29rDhSf@)LhdB*p1ZB|&>U9xV^aW8Y_^J4Mv zY~h7ZC7U5{(n&=`fDYeO;XKAUa?ld1qXRso z>8=fq4jTWX^@U+*lp6*21cwUu3cG8h1Dpxtx9?C(y^ONHxQ)9`hy-TRwwFb9MA&YI z3^4Jy6BmVncgRFwe0Vy-JATOx-Go<1(j(o#uOomBr*{fpB~dLX+YrZwOX$S9?Eg?+ z*y(gj9-RLe>Os^ujJ>g82i{8r&jB>j(bXs8R^($MkbLl>vHSz1kP7OBl>IAj?;@ zIlvh!@<*^#5z#7~V1XETarEE=QRDPWXy3^duo@|>kOgL<1kJKDrz%Ec&@}1R&-U`X zdg*bE=;v`R@q^#ab~ddm^Pi5x3}=vLj9R7^QRGjS{Wp`Mt#vC!&B}bG2GGk5xZ3lO zItgjhMt$fPX;X02w3*k=&8D?=*~l1}l6$6_srj0&fwu4iCf$E`PV0E|egp6KNs5n{ znrkq`vxam{d+FlGg;xC!haRCcatL*(?cHusx>z2wcVNk zfp74u&uR`|3>%H50Ea-N_%ZT!D-sV5KOrHhLvU=ReNsgw|C5*p=)z2s#AHJ`I#=m1 ze$fc&8jzmPbZB|7#EJOT1}Php9 zN$c=-n)vGS^{Hw*vNH{xEkqD%YqJ51oubN5J(Z!T#XV;-_vFj|uh`ItG7gw_?H&fzmg5Qc*|(X&yny}Z(PJvL5nB&| z%LxxDue)Ro)>@(BM&zb0)aebq+QjQLOwXPlnHgR37UWHz71B3GXm~&@fb9hUZK+r5 ziW_e7z-e31H3ap0Z{vp(Kn=wm77Y17#P?uWIMjCE0HCS<(XjvfZD~t}R{d1|U+p4u zdQ=s%MZ5|bPR&n6po`Kkz9zA41l5SX;6*(v_E(JWLu2N=8Bv3qk}HG(Txgg>$5;M9x>Rhk=M`iZ4soVx2?m?@XG*OgIAki)LY~RJq2rUP-PaH z1j{~?mo|~D7~!K4E%XyJ94UR%$5L1!SCM?+XzBnt$@^FHvRwm~tCt-1*xz(Zd);&7 zd6F%;rOOBk-tN+O7<4(m?cIa_?^Hk59RK3sxna1wk|X-Oi2z+~UFGcamxH!_6ffNu z&S=JXpCT0zfOdmLw|7nq*!0|HHdqzAAGSyy>$o?#CL~UaR`uy2`1}2qJqF0T5 zI7gKhNBQG3KMEhb{;sflsRM9j@<&W+<`Ow)K6y3}PFgx{{iN;vjM#Q6(kORhPr>F! z3i3`n^V)`YGymm*nxBL9S|fzC3`L8uiQ6HA2`t}A)ftzw4tKV*Zx2OA8KBm z8i^^<$i=vA2tTCj7QAtC12roDs=|>=lq1dZ<9h$zRBUQnC;sA?^p~JLi!Dx3;Tbaz zS!!lSYWdh3+hv00aPG-QGH8#UgkN6_r!W6AEP<$;t^by*=%wbEMz~*4d$`?y;yGH8 z?>N!2ZYc$Qekil^A8)zuj++~2MDrN>9f!ybUPgh83#M;=wP=V&Y!KN$rq|O z!+RU2HLp-uE=g;S;_t?WVGAzDu%V;^p;4=}%J&KkjqYYnLeZpS=0})5j1js|iNYG) zT}AGAxJsVtZd|~qZYZvcEd9~ei|j)_1q%^E@@0f^I4&eKW({q-wrIYEa-kS(fm|s; zeJIMeu9x~EP!#Mn4ZlEJl_W(v)CvRg5Gw>VbOfD1ouJqV7Eq8@t>|r=Zht3JjSKGy z_jO0KATj+j>lk7UEt=y!H3~`;U&!WamquWS7X|6pjXLaWZL5yxP+{)bfbt-{4^D1+ zi>n)uI(3*M=6N6`w|QFtL-Y$pt77BsTUoeS&kk1_1>c%Fbu>{9y7UHry};e5FcRMB zx&0qkSG+Q;_37SB#_yNZ!O%~7V4*Q>`F)vTc6-;HAS2y$yi4~|WsQ1JLc4=R#nN6J zTJ^eP2mO}_S1zq>ami{Z6Nqg=u2FYq`+I1j=&k%56`uHB9kW}~hbEo>o^5E_FM4u8<3I)y_zh07;pnH?2BnDR zUop;6OxEe&wV%xMaCWgCdod?pcYTvXKS-cWiYO^ayQbJKXB7qU8%c1cEC*ZYr}GEQ zQVsLUq)(@$^>>pK2OEbe`0;yXiymz19(2wo!_3!~LSCB=ueCs){1zcZ#{Bx5tii-7(e*I#!kjq-W zk;jsHh5Dy8=$7)_$>+cXrwcR@Ql61r>O*$@xBVDJCh9x^v`ZwuB*+jE#%}^z5rbpY zSn&A(ocKV~Z%#{%gGo|MTy%t6cMf1$(5O*=+L9s;4^%cf&}T2<4|WGTcqSd3Zwa}d@(zId+t&3X2@!IRIa@>EHjHW{e92&kq)O5(AQ?<~ zvTxl>-^3=IeE#F9a$|8WEM&GMP1?C+JF{0&HYp8tI=$SQ?C$ux~%v7qjPHp(* zhh?9o7zc|}q?+XH1JpSa7Dvv^lnr#HZ&PlN>~tX|?_miJE4wcVGutLVi|BDb1y%Qt zig3@gpy%Dlc>gAidklvp|C>&8X!P7GavWb=b!x8MD|fPg3T~MgD6>9Hki-{!l`%Vj z1F$dGvmWvZQc$8-u`GXki0D-Bd&f#+^0Bm5%TG8a;i?hDIc_zaLV4Gj zsyV(fbLrRw(i#u%_kLyuyF~^42X(grry`?L2>vK8IQSh3YoPElk-swn-ko#Rm45Rx zBcUi(rNw4*M7;}%otu`=de`IL+>bV4H+1cgRf;0x)Cj@Q!~{k0;o(o zmn#kTR7qh(iEjhtZ@%U%>rnjhoe8FhD=13_&QFF)cLml@Izm0etUK?m_8E&W8A8?s z&PK>nBj=k21g(YEq?gQ~BXZS9DlVqtX}x`#-}CW}Ic?as_e8Ph<^cIK3_^PIL)DjADxYF6R z(9Ij5f|fDvr)mO7$Cv@5Y0D*4jDYD~sylCxW%b=^)xLj&tIIj{?)ukn>Ejx&Sr+FN zMFoG9JccYpr14_spCF!l8mi~2lC6^_PZg3*6V{fWwI^oUSzO#7Y-7rt z42Pr&k)B-qXJC3g4}N$NeapGkp=?_WP4A8btFeVICXzDjzHxbLDw*5|_9bkM5w5F~ zIG*)e#}Dm)2^k=7-A`o|*yTleZCql$(zeodMoA`wV0VdQ^F8wcg%e3MfYjYJDFmD# zj9ps~{;QaQu|*Tcc~4ZxJ3jS$`GTq-G3(j3WPj)Lmz*dM@4Gmh8U=tW+LOnOW%1x0 z;{J}QBSOT`%VYvJN?ug`{3;D4d_K!fYvXbt^^cMXnjDUYTm*Ar!tj(SV(S88nED1& z-Rwz30Jx#opi)o)VnKCkVRjqg)q73Icj29N5gJAS{@1_#GP0ka4HDS7?dP^t1$N?@ z4FA2*O9g%w*f2_g;x0PH$)dCVf2|U%S-z#EGOFz?cFlmaijTdwAJv{t>=S!tV4~e` zGn|*oxApjHLNmw(v1?;tP58Tk!tMA=P^7Mg8}?pUS5bqt;&bpu`KeD$YI4lBnqQ#g zKFSHWVHD*T64#SMcwv&NDD>1F?nfsBZo_AlFJ_;Yt&C_z0*X4-v3N+_4N0LV5oAfK zHp=LB-KPz?7q;9skfO(;C>fkTznrQn^&yEp_Iq-Xr0P!92|v$HDIf0qT0#2ledHwj|XvQoSs zETo!a6TBqt`z*{ay_I|@nIKoTCN3|tEX@C?D$&2rF(oSoQ5m5x$I%pGB1kkKK=z)f zoGDkQEbTMF#UERIf38%dB#7%WLsq~{X(cXIZCAC;ZW1B#urzEn6|7ye5b|lRfs>1M z5hwgJih4-aZ-5=if}(Ky#2m8b8r>ZV-2=HUJRuz^4N2FVW8c6xd_qgTI%A#T**#j8 z3sm;H6|@GOjM!7pOtf8~fym22H52~hwaVk(R)KkyPpF1vf33pEtF&=)Dr`OU{LNko zh&J*6fnRKQ@~!XA_b;z*$-9(X|NDeXg_>|fm88m=J9$5rJkwDrLO9{QOm)Sv`Nx9S zCi5*UX3M~Y9^?tl8thMAf>9o*iMK!Oi!eJnWo+0}7MuIwzrV$p;qT|@ETJHmR3G{A z@9Idi+XcZvhMI$ws`3~2zC$tm2_gN{qZ&R4&evoai7B3cTzNDB|jZz}PXYJK#5 zRm3g$4S1=5$mFJcST!l-kq-#0c{jv}zN#vokCVIMYV%XH&HyFYk~3!(q*Rj=|E#go z(}N2sHY4$SHSZ+-E7+@lWomqB^j3uD$em0!N0u^6&r*AMBVn<*DWO2vt7_7TzydyN z-+9xgpL5E~h`Zi4U!Hqr)onK;mHqD7W{^oK-RZUAqZ;^g9A`*3{q~1Gw-?S{*DUBa zKI;rZ45~e=hM9n$$^bh>>?o2#ElN>Is2SY#l4aLk(B9;fM)Qx**1hyS z@T|xaB5i6M-tl_Dm5Gx_sMP!}z`G9!keReT;Hd8fO{Q;KtxsOcHs{=QDcxtK$CaXC zGS5HHBr`J2rkWC&`D4FVbiSCNsh?#XUgYVY$+^_Aeu}fFQy@ zs&@@70;RUzA#`1MLQ#uCl`f9pmD9oQ`Hm zqFkWeuv_nm2wWGYwI@aiKhb&u=%G`+HHtB*b` ziR5P}ql31mCjH0oo)eh);W7#lMcu7MVm68tcK+SNVD`E@A>eTPZDE8mwn=N`_ zhOVMLH=FMMmP4K{o)5vBuCS-D5pT_dbB*c@_?VFF=kf31=eb7LBc?3Wf8(_Y2sMkB z111yZ?S7GOQ{x?$PjM8BHo)NlqrWEv?|M-)!$+>_Y_m~uEr>j-C4r5K3dxWCZdIZe ze9f*ew~k>8;D=3HTvpjwqJk4;exdpu_RoJCMC1Whak`g%{bLI2<>qF7r<8Z39v$4I zC|zMd6cF+OWCO!w_kjZl@pazM!)(Z-FLL(kEGAM7eK_0=R5Iexx-A`qht<_H?ur>9 z>zV8Ov7w|M-XQy~nMRj+BPp-1(KXAFTe^L?J@P@c{3MA}bD-KZnKjQac{&VvQOZQai5SpnO-b;WbaU?pV(F z{kaA?i0y8hh31wJ;=05pzM?Ke;Ek>(orzCTrLIkIiXbm?l=`iDyYl50v%^`GJdUP) zD+Ljo!OmGWjfdk-=}g-`V=|}qMSry3acq9c3RloW5N_O1&@MU+^Ds*f-rff6U@cqY z+2Fs7YPW((etA<%YSBL7t`-ybi-S&XX67tDkjFfF-&-NJWFeb!S|w@gjTS%sbsG-a zbFbZSTa>w+Az1JGWY$F%{B2w;^TIfe-7%*gUCz}5z;$(gZU4m@04C`+%zmEL_z(F3 z{dN7nV-~;RkvdkO9)mk*m+41B#^NfoPzVAD{&57?YwXJSmNJ9#Wq3OF(=0##$^}Hl zN?gNRJEz^bd2+c8<=;5G>Bf_vi1y3WOG#jU=BY2n-&AX}lpddceNRu= zdz8Q3bHBt}N&eUId!@4SKX9iSsA23@R9wv3)vkESYFTZ>GrsqouL}Q8iMU=jKQt>h z9aq7PILcTIF2sXbLF1sS0@JR{NiI3(|+IA4*+N1}(eL%2n|V zR{e?D18@&GJgS?%*XR>_97D0XHeu|bC#ZD~MDuC9@3}^)#YzBYjnotTd8uPWN5~%0 zk|tV%aiRoRG{4W%?@jBtlCepuIB@*d}e{%N!N? zs73ChjG5V=!9mq`Q-hO{x)tGODU!xE1}OpOoQ#1?umgM>b)~OV^2vu* zm}Ta>=(m4f8ASHn+A<~EW=_!mc9+9HAc$WV9H9X%A2!nm^ZGi7RCcb zGe0#F97E_RSfDGHSE_RVD`&M_HubWL1MLjMV|qs;o97;^%wOb&o&%dfz|dMKI9sVU z*YMN2d?`W^qqwF$o$=sb(FXH$+WYF9(gg_u;>ypDdZ+8F-Alf^Dr)O_DaaB+lYi)6 zx6mTZFNF?{$uJP;{#IEI;EZ~Wf<@R~Uhp88A|_$X z7^!3AE%y2Tl*4wZr!WG-8MA*J8tRT{}IEIeNN%K{z$62bOY>H;9Q!7ekWosX^izu#f$KSs-Jri+WiIT?>TbNpZh1|iqp8L$k zR{%gWMwKv{7ZR(^C(v6Ttp44xZ4?XYp!a`I^4@Zn?BoTxK$(7Hw_7ugP5ewUB)P5* zg8$He5?FO`JWq_5yhS$$I?7JRITQPE2mGEtARO| z5=uTAbH{GywbpqlC*LidYr8IZ%6fkeZuY67{C zd_HD2TkM`knp+|-v3{H_lIG=3yY{^>PI%mNt5)R2T-v0zik6;T_m`ib!n=OOlarge zRM~_SD3Cewv0|)@B*!G5*>a93)EFAn;8c zIgu?dOzn*Ap0*K#bs(e3W1}Rajahjbd=+DK39hVSPu9{cDFo+gLh^Y?@i~75DP^qf}G8Hk}eddi=QVQgdw1dg4Q``5zi%@+5=kADEQ> z^ajHR&0SPCbNjMirqZYYcspD)^+S>}3bHwhT%C3PzuX6M)$!~fhdXpP8(`ae_N_26 z!ABl+P5A3^;A4F6_8+_oIXKq_(epmihSs&n;^6p1gP2UXMQpm01z@>FUenZgka9`1 zeiTh|QYaGx=&7m!cc*nSAsITY$3I51lFu!O1KZWuE)_;1RNATtt` zk?2i>Weu~}i;Ov8wK>_+)$T=19r|0((PhxYGqxTn|YPq<* zwcJ{2|9#Xj->4h@E;bl(!?G~p$I@W=97=`jMZW{)rhOIbPN*1+@G9{la7u9@v^UeU zW`Sk$=gLN?t5A2%1K(hmAJ^Zpd@#?d^6q=9)ws1~+@dfud=pTR6_A5fuYdPy(Q#+_RDQ>~$NMVZpIs}Hm{0hH55|^~x8w795-(_4Up+|-ZQOO2j zcig$vtKQvKA^zE6T%*(n<26^-Ip7Q)8zK`cMN?l&qZemaEpx$NE7)t0*)c;QW-{sL zLb=#?6&lPtAKGu3M&mijttZWe?0~vU>LxlL-ZulL!;XJ0oE$ai`XV(CZlysShH4UL)ifp) z&2Ka-HoY}>eJg52&koe{v7WHJ0Xrg1HWb|R_0W;*q9jXN!yhVlPFum=0z?o%nJv8$ zLZ8Ho!iXS{WMfNTsywuz=$R{Cw`7R$#2RYkDTI69b;Dofz}B@Z!S@K~1r zs;t-C5o=1je58^z_s=_z$r+@WRY8Gw_6%5@006ryg}n){#7EOb<8^uPUM>~r-Zf^hk>ct+R?>6#UWX}0;X5=TK@Ax;btC-aX@%|Ua<*X`G`?6miPrtzo zZFBGW^G3dq@}qdidR$#@gW_rd=WXJ{U^0p84(Ic zx+f+MtEw7kac%VDpzwq}cPX#&98#4Ujp>eaY>3Y#`GoB_vxwXa;MF(#kDvLm;sSH1 zA18lT-^wdD9i?ECAMA{MUP}+VKL0k6D6g~B5G_J`$&?9S?(SSv%@Y@lK{*V+DWnGf zb8P>zq=ouVDybp$(kS!s(Y)&u2)uCRF-H>i%WCB_^SbtGJlO{gdqEyHI6Pn4eszOjrJ|KfF#G8yg(i9~}tx!WM}?OK>iJnn?E|NaP6(zXv85p-8ldM({a`42B8~l}axE%XJGW|8@ERAgzyDIZILX zYtAWxnsdyi35h+EnK+_1fE`%vT7)ycdQrx4N6GHI(yWB>V`C#%{f4Bjy9@%i`7&tC zyY-vpyhjD6+QJjMaY!#{uwXX1aM6?Qj9n@J;BTR&vYrwY%W!$GT+d&b!=0h1EU~d15LNWm_TtM2)e}I7l^w z7;(N^h@q{^DW*kw`UuLp*9DUnq@GZx9^`0ABL9M z6>U0mN%6-w0qY7oJlEp(t0$TYnu43J<=Tco+(OFi9%u(*MGzLxA88b5J`MPE4!nx1BxYu zDw5EPq`miW3C4DGWrl)21F&dbiq`A}c2>sG1k2KdI05o{?yx%x*Qh;* zqpm`q54nU!Tot6;LtZ`qEbPpM$^46lY}tRhY8$Df3jA;?>%MoC5*eo{cw4-(tF|HV zR`ke7&>v%oT|1%>;vc`IL{rdk&r;@Ofq?zn{6fL8mZ#ws^GYrl3Ii^fsF4+FbAe{d z$Xm6Yc`#VxrIxLWz-*@-5S89(*sH#2XBtBg-|XK1Jfy1vLNBx~?g|X35>IG9CYD#r z;;asM6QJv5R`@wXwN5v^_ike9xvg<>z>;{RMT25G`b6};dS@l)s_g(luWq$lxxg<> z2h;Xf7=YV=t{RC34}%U-h?HPq%IGV5kJ;`XnNCdLeHyQtTqp;UgN}Q35BY7mljpr# z(o0J2LB31%75s;TrB+VkCpb-05v-O(UErnrlmpS<EihpE!9tFx;855FZgK&Y zm!{|z6)zt3a88!cQ=l|a434@~yA;cH+o1iSc0%)ZV&B3H;E*jW9lkgE(f!m zr7xN#3UYu_F69Pp!MO?xr{c3`-liX}5=^}xeJObB?O+Ff4R281wt2jz$Oafj-j%O4 z3>rxdBol@j34l$KLoJXROG}Y+M<|VaZu)u4o^Hn=F^_&9=@VNnVsETqzsNK=M2@Jz z7mZATAer`m(!ejFChIaK3J)vj{caSzLMZ)}TpB3Z7X5IT6`q(PW0hd6>{)d0MtQV= zhhn<GROEhhqvdZ{DoXmC`pK#lY&+7u{*V zvzU)dhdLIpAZvZHZn2pawP}BG3zOixVSOi?(aNoN7?j@@NESj1ypmKqwwb+Em-1a` z$@|Z*#jKSbLpUzH9r&}b`l~ZJLmJL5*>$!L7wG0cM-cdZ3a3TO7bqc~L6_$*{T}cj zOGF44t<5z=eQ8p@ezHpW>t+7>!xW^3Aqn~2M*jJt$K^Mmx;*nrp{_@2k}j_{#erG@ z%h9KH$AGFWgsX*}8ryP{NjuQwp0M#+=J4=?Zx=gw>&J(dPPl7;F^6V9DSJDtsL$r~ z_;~gntC))l0k0Z=fABz~0=of3#3;c{4TiO_4uCxun~jc=Y{(!xdY-RengGAJ zY&AbT<_q}`YNQ(ByM*v01mr9X7@xhyY(#KRrjs8MfPMz3mk@EhK{Eq^;9-&jkvB_N z5ALImB$=)Pm+!J8H6;eQ`-tlg3AO2Zw~_)7w;JpRh=D=Sb{6UyvGv`4D7w-&bafJk zpC^v;DzC^?{u=%_Jw{nXRb279d&|r$*B#DvK5l&5jVyB?b7rF$!TM6nZBd`Hi=Tqw za`23HHNg`9|Ld=h4Z;6HH!sd=!NNCWAB7$7sNQSf!IYAa#~y=6AgnUyZv7d~1$v&*L8Ay#btj;H039EkpHnGcdC#jUEYBy z?z~+e7?sP`byn=@%mz*;25UPevNLbT<5!{~*F^NJ>Q9X1$`LsLnFYC^gB{RV0nW>6 zKy5+t<;w4Rgu`-=b$EmV;2*B8n~yRZ_Z6n=zEY3Be%Tu=RGfdz*K-TRl~h#cTy&2W zv+)f4Oh^A28mZOCP@hDh|h$^XdFXY7PB*wuZ=o`xOS z9h(adKPxZk!5_6-ieBDhb6$Z;&=BnG8}7rKrZhV;->1y{n1X>;3n``6slNDOYS8kp zJ+g`DTl-(lLteY@@dh-7)=Vw?RK8m5(Hi2K>;e~<`HD<1@kM4kGHf2_wsQy2eN{AT z2axhU`Mh97m@L?|9FXV93+{{PDe4&xk3%^yoQpfEgnL+0(|-Ii&KhQAC3{ZNBX%RG-LC7MdH1wu7;l5^YJ6%5poKW2 zY7ip2|A5|g5LQfvt0tOoT#Fc~q{gmy+Y+ARYmV|0CC-R09=Do4$*vRm+!wl&jR`)S zg2Y25Al8aZNYCD94{hrzAQu|pm4PfojJI)yhhUKDY z52M5}cQ$B}D@_)Q0_7=@sg{arrpdz_Aw_ z%)>=~a-*<4Ps$d;`zh)D=cp!GkWhsx+9whM!BclG)0~V0h0M3eX|b6hiyLjk_=Rs& zfZgRjlt6-i3q!5EB@0gx`Nkr+{?RP&-hiva`ey;VeC~*&C6WK=l7_kw74YPfAd#|$ zFlzs|<9U;XeR8-5Y zNt#EXfadNk^Bgm?19%w=LYz)naK#!+B805acSBU}e|1IY~Uph{6eY z_z5sDQrVWsbwzvm7*8EmB!*tfzaPwU=^edI#XerFhZ70-I3jYt^iA@|`SHQ`+2L!2 zD&1Jx;qd&#(@9{oipv`F@T-qXK_7|Ea^hwkh{VpACB@QuaDP`eJU@b+5ZzbTz?&%@ z(6L*3v=8Q2yra$1+=nbttN22s)SiOWWsLjYUHg)|5~J-EPG4RlW@EplxMvi!)5W~; z&-6+q#wzqYGTEv%2!p@MMo@zL$hRy@q4~QFo7JnO@et@~ZgD~6SlUpsMt^y35^})c z5Y69)=a|v{&|GjtC8H>4{Mpy*EGT=cR%EPDb*uX_Kfr-SkoOZ$wvMDJ6Yq*#H^33d z#radJ%9Pr4uTOPgdgG*hF4w?&u0i{G7SLTrIW2YU)UmHA;$3V`OlmTCok%XLk>W`v#r0$RDT%Y5ax^ zBuPw1R4w3sb!nRZ4JeL14Or73U_0P$R0og);x*k#zAL;L9o65^G2^rO&d`XUfw$B? zranpFZ+o;2M3lT|CiXE-Y$T9zX$`d!ea4P+DkpH$ildd)$saAsnvB~4{qQ5;U(Nk6 zZFw5>Dcj10byR@lMqTsRhAVJs=vgPVMP(G>Z-n3TAp9fX&CvW?jf;bp|G0!KqQ(O| zm*ap4K;l;S4gZ|wP`ci$^HB6-t1q3p4)@U=M1ECe%1ZCF@-`58H099!JqInXFkVB; zNf3xSS-*>ILlbe%20CI+uGk+vbIG13nbYw39)F0REOwN7#tBhZ2fU0M|HGd4pr1er zeg`rJwGf;^5mHK%+>=&euxH4qax-LSeZ_*LBW8 zd2%xVo(FUGdK>5!^(B!tW!ZC{sdei7Jm}G#Rhsi56d|6SsPn~zvM)98bb7iYet4{Y zp49&4z?NHAslwheIgW__?{1|j6m}(l2?yQGUg>&{Cdg0ocqVMWY^M&*E||05WaPPB z>e|!l2cw3Y7|^rYDzc5PCB7FvF)_*+q(fb5MX#wUpEGT`VD(qCSB5Hgy=d>w5q1lKdX#JW3=I9%dbhV&z@foiJ3N=0a!PuIR828|KsgM`% zCSW)n8+85C@Ta)UCp{hAaK0@S&b=h<4_4p{9b7W|&E_Y(8fKN z*f9^zN%Xs&kWdgXxBBf;BQfwIgmVI14g*k7$U^!jieR*L<&z8AjBbT&1HqLQ5N@g` z7V&c2=zg)UeK2idE?oMhXxq1beCkSkb&%r3!?8JwrpCELcHeH&m$B^}(lf$m%h#?; z3R9_ZISHRAJEd~IK2l`gHcC83N=};2xQKmvI2=ixbIr(%y8hkIRgiYeugy|<=V@}d z&T7%e==VR8H*YbH`hQh*t`t?#EKQY&1dK7r1SMM@E?msfc?B(T37TIWPgt!blScep zE5!cMk(3naV#lo>-;QMkTu$|0Qy>@ukqwL&F10R=I#QAMI&hYR7Hk`BT?*Ss_SLHw zQD)M9p0|2DeNbbW-*K}f2Lo5$!g+*Pnl#R}xBiP|^)hnnW9V=XoYJrNK5LPx)V7nx zRdQv2c@Q+Hxv^i9fy9~aC$s`LRBxP5B_)Rlsl99FJEg?)I^1Inbl^VjN#EI4zh3@3 zjL|$=&wE!pJvK5&`TW0?k$sWe*Y|7OOTLfs%mkR!4?YT2=ZO=|+;MZMEK9H%*A|LX z`S7BtTb?fY#1FQfvTO4-6esR15_Tn?WnFXFFPRJMUJ{hm2-x^aSFujcJ13wp`$io( zcz?i!RYKR*OnvJZM7)q(hdR0{x}l!{3|}~=9lcQamn$?T!?ryTJU(wCjJL)9EjK8# zd4KyLO*;r>|9Xuy8~vZY|1wml`FtoY4}IVPXH2T{ESNe^N8?eaKoPqci&VDQR3Hvf+AZvwm0q}g^uitC1&3rCgb2v;Yd zOT4TE8t1IcU`%Pt>T)i}PNhCbUF2W-`Y{`2XTZO90LYJs@8NoLqoYkGmWwCz8u+jT zNzO>-c-=}RH#mRgPq64a8z7F8D(v0{&&dOwVMR5cSXOEBgUOy*jnS3dfClB+Ird8I zBUxV08?R#}CCS!kFn3KSTf2nf0T=#qNIT6WO-aAF5wzZ{DE?M1k%Sbw+nsD;>o^)T zD6;#V9t2EWm0Tk>4EvwDH)q@(K)L}b@2q7)#Re%>M`@T7m#K*uvtOz8`-cGpwUKZ$1C6nR< z_u5rKvrllA;ooXnq*l+2&Wq@~)Z*A+K8E6(_S(-L#1?|_U$7pIPFR;O`0b7Py(nk+ z2(l*PUo-)WS}H1TFu2(&&7xE6&?1+J@a_0<~2?k|F`Jb;b-Qqhlq-zs?Jvz_#9 zYEGL|!r$k=_?EH~Q%M%}I=CCFCYMvZIVnir*vydwLFM?jB0S7L zr}Ju+PBxi(x#?D|vx!B0&VgL_L^nwRgA(GC4|m$n3SUCOiC-&ox?DM$hSHi8_9ZtW zY=U#hH8kZwhgJ zGsxqbG76E)Hs8NSf|17}S?zahOZd@$#_+6pX$4Rp2#}-C#$}&~wYul|^p1~S#%)VZ z%6b{lE2UuZ;lz-)ka@1+QA*Kbu$OZV>f<#2|}u(aVh%P00j2eKMNsQ z*n4EqIu>${EXla;LqBBCE!nw32>DtOjo9PolZK`al$BgOfH=1|y21cNj_QkzoQ10# z$SY9%cwr#UhWODXr%fjec+|Ib0tdahf&)Yf1 z`>!FuZ8}fV!1VIHByde9u0WdiK_dvzd|S&FpJxS#Ksc>|9QuOyS*`It+s{eF|I`~T z8SZ_vD6xH(A7cJ@NIBCaA=Jt#5t|0Mz@OG_<3YVT_O`LA6yj)2zB+FLICwuE(9?7@ zmW~)LVtVksUx)JN#D|rW&s+IeoSUz3MZ(}JX&j%d#9`ynI1)fwe`_bOUskCeT zF|cSF^v@3MT~Y7a(Wlpy@a ztvgTMnZ@1?hra^2^$Y3KP6$n-y*g8_e;10hb?y@zrjU~i0!!Hao00oJp3m^MidIsQ zF>k5KvDZzE1qR1GBpmg9dWrorUr*`WZ?SmqFAp=8*z)enu&;LG#V?JG-vi1)p){fd zZhYN|tf1MpfboHU6@ZwKA<`CbL%P39ny8l1A2G&{uwJK&S#rs&=OnEAZNn58ib*%W zhNv7{kW!-R^fv~U+#M8po3HP1gU;<&m|h$I6@7oFzI&>Y_GFVmV4J8>C^+!O{d|?% zNYY*})vEMqQ})r=H+ZK7e%S#66-cRtr{lzS2XjQ1EhAjvY`WW;XcxriuLNCsyGm%|69i-b_at8Wv*YU0RVCx-gKz%F{BH5#$*D>Txb}5(tZgEZDMK`` z1iWO&_zK?cojjN9 z;Bn(kp`YK)NB}ZGJA0@~zEkNsRbal0?J9XDvs#%T+mfjB#S8!O{yO`MNzkc;y{5s|7vJfNBYcy$f`{wMrQlMv2m zzt{-D+WY+9jO`MR<0$=}0;=a5SbuXuk~#AjpFvoIUy6S(dZfPdu_WiLm@9^hd&z5H z)gg%4Z;nB>a$Km)i|GuXc-j>CdCWSCKocy4Y9fK0xT%mZ!I|<6l$(0xDk#rwvs6`w zJ9X}-D<2$i>>wihP~7LhjhCc8#Kuj?itVz_$+o=Su-J;2wINITMROoC2;6~SxuBIP zGy8Eit?RUSm!`*6Qr5h0?A$l}^^o$rtvOM&a%oAw(>@oQ|u$B}2Hh@t+-douw zBcdnmw#69Eretx<(axRrsL(BdiGOB%1=j()+IhFU^l385sY!C`;Fa~%c+9KH6RkGc z5G|!)-<48iSM8bPq$E@>1*7F?Tp(%$pZ_5R|P;a`6T zhQw@F+?>NMR*55qSXI%xvSqRYcUU*Lo;gp#i-7479}P^rmh8C`s;+)JR9vSNK9o#s zSmCL-e5cc(WN1{x-TG8mc0%4aaMf)uvPrm;E|L4*d1rb3bAIW0Hl~F^5qyW%7&`IS zPjO)`J$v02=d!B5RXUEX%6Geh4j!I=p#bxom}dOgG3F)g^CWmv16F=j@eIZI`39Aj@&vXMRSPs|aHq=^k0)6>K<(E1Yf z8wZK5`*lGept+}3&~5j$A=C=ZECXlD(NU4TJgvq&>~1PGd|vs#BMU%U;BS4wr7~Tg zM3`peVD+052{g;+vJS5`(V6JSwq0zWrDzdbwJ=eP32Bt;PDz@@!k=g+T6U$1sDh@itULOZU$dhv0;0XNS?Sr+DwQ@Bz-+c}wjhr;E#bw~x3M z))ysZzQDad7L~+W=TJE--;Rhf&Y^_-!awVAJ|6m#LEcsMm@R+MTp*L;>$!*Vr)!as z(!@8Gp&PKwZ*24_LH-McxVD9R^m9_kqJ8&QjgDo11K=_gZk10sbo3joLgKD!A81wV z9*)SLqJD7bB(;1suQC}go_~|*sn&`idA_@Xp67@GZqwqaVz)-oY>8t7`hoCI=z;5M zwf>`|AV$2;^-rcx*gHKLnCO1VKrPS#LvpVq+F8#8Cfm{)dlfYDn^UT^wB-$*Vs+~!oH*^Qs`P3m&yiqIbLYy-`A(E(oUmX|keS&%yt|89k zfn{0BzyHlw?53re5-)P>rGP7;pf4?wkCzxk=OR_l@=tD;A7oMdOMj_WMsww53Oz;= zfsce6%ql>oTAE@2!<@drWvRb=d#1Czkb6y{vqK!;1+)(HHpt^*NP**LCeD(1yMiI< zFE8-MzTfz1aS*CbmyJ((G&6NOM1$W>%8a5^lT>=1a4>24U3zJ1kdTLR%EUQUm+DFZ z_0>`|V7ax{b4e}_TBr~h3A^1W)Vo8{!2Do30oy3@c$_YhZ0OA_+m)l>%bGs7s+Rsl zPdptjNA3N!-Z(zdYt+v(|M$_YMAqZ+FmT@3Q7C4Jg=UtX|5&PZPF zLsQB=gC0+~ohnZ+B*(MrxxNxwknA{rdlWRHGr!2nZAe`hD?eG}{A|GK&9|<411)03 zMjJ}yMHaM`L2xV9u1WIO@_(S0dG${HxRBoD$&agh*TMugeA5$fTfjNn2P^#kKI~Kj zcq0nH^*v0deev_IjCabMe$~IR=tccf5{S<3<1tN{@_fM8vwRgkCiSFSjp8@ko8sV5 zniZ4JqUy^p8-^5RgLn(~On@(}4G`%Od3Cn}Lvcd>fcI*MKSmbo7t|As$!I4H2are2rf zS)p4q>vvD1!H7Q9RWBo*tC0TBQ8Jza_@s~Qs9`Y7rr$p3v77B3;d?9Me9Ox~^)iK8 zg{s#PGnf7?<^(wt_oSv4e(gxWO^P z;4-DzhZdgz*0{vmFsOe$Er;lu4wT?y7*E~mn8lQu|aD2DE8i+_hmcCFa8y1>@8#> zX1tYrHCLz^mNtc9%TJsMp}6n$LS7024ADV6kcSV>P=vDgo`rs2I$guF*r#0%4VAAF zfVkKU$RQ2UulK-LGcr2-+1<33XL1Sht(oIVK zO08*5Z6==$(QPgJZH$u&HW`ct$XnEbo#_FXmYkUgpV5Ra?-7srFcT8(SH@xTTSa+3 zzL4`Rm8EpS`{*^0tQ$u6>8)|OG&a60yegVGzlo){{eJC&V(OC7xVIxrABvFziv6(f z_oZqW8X);}T4l8MUD!%BU00v%0#R5nr*|^BFM676`sQI!$GwIai->C>^~g4R)QrdL zY`=Lsdw=P~Q>yf4$+8^D4TE7e-iAPhRT<+BKkzZqZM!6a|1<|_eNIv?^A|j2^bA#Q zu$9NOki%B^b7%C%qMY~V=m_K|Ta zylhg-DivZycJ`;vtH!KK*8C?yZ~L)YaW2YKVlmG z88J*17D3j$%Rf^PT~C!xxhi|GM^dFK7xZkl)$Bt&z>I#|c%l0BdSSYyy>Ujgo^b2p z?(5wX)96b8{ra^dSGRbBc*Nz0ZQQvC|2^YddVj33RY`LV32V>rQZU(RCMvuyiO?wu*ZdA+sFzyXalbe{01B=NQ zpe#004#mLsr$N`c{jEbI1jMCi1Ma^cbO)-aj5vts%NN`8hrqQR-I7^GQMG>pd^N+l z7DV<`0+QiKzkLkHhsYq{x^G*DlI%NtrH(kSQ_mT%N#h6RW$qN&hZ-(Vd zKSkCS@7arX^7*m%iwp4&)i=a?RfgFFm~a}n`mK#JIXZvo^v&ZJ>cnIWN}NYSyV+Q< zJjJ!jQl(B*Qb1=LVU&=(nQ3590<=f(0Fj#BslER7d%-X<^6g(Z?1D{H<4``*m}|YYuauw@OSC6+n zX>{rxsNYs)_QmTRALn)W5}!dfS=h!Y}s0bY6at zD@H}p*zR3gwi!a19Nog~jf-aFl)4-peh!-5>^T)IyUens8bW4xe4jOzIo=Z-h2P9 ze{&Yp`@0#Oc_X7LmSNeCyR$FZr9VF<#D3Jiku6t zrPSF|O8rk>V?ahuvFe$(hog!swKQS5zuARP=JcrQbRj`{B;y;BAhVXp_7P!X($7X* zyGn?8(*Bxrcq=bE3p-h!NzMKa7|aRly_HayM_ZW|8uhQ)#+%T)CZ)yF2(7ajX{Y>~ zs!boT>Y2<$($K(u1(-NV`1|gi9C$s*(euF7Qw#2t>@vnKY(KZ5zO6S{7fcR|%=o9+ z!T(1<@daxin37>4RA;N8L(wd^gXvALr_Y9aQ29600x@Pm;ExCfnwaLe1X1hnhy zY@DF-3U#+(Pb}i_96Q*^M4yNC9Yx+hM1$12ez>Mac^ETsHhko(b7+<(^2A|Zfh{4{ zy{eB#ys)Wdx%xLwr`&yG*T|L2RPp?4MqGlGY-yv7>nB3?$xQ}MR{n1)dCDaW-gI(@ z6%yb#ta3Oqveuvc#@v zfR|+Pt}|^X|Jq#PK1knDm;iR=RHNCvgO1If_BfrP=IVUm@MtrtOzbx@m6?8I!5JDU zU^c(+YHB(l2{rLEDgP3fw6mXa+TH(KxandArk%=NdY>bf1EcZf(7dDW3&U-0tG9MW zb6FkoJ}gM+J*O1<~d#UkwR1TOf}Ol#WE5VJsv!F$(Ib&yW9N<e)g*%uMQWTjIZ9D~4FDh`QK=J{oMng~tV$S61lw~77x3P707G<2zP3zQOj?%*oy>CWuh8lKBMYz40ul;_%B%!ag z?y%-szhxEz*fH_ny1L=?Eh6q$)9b}G zYhdW;J2?;kwEPw;Qcn8FV|%$e-mO=Nh?j$(_l=+T?LKaBq&)qT3tm=AF6Ypey}J%F zec2ElE%XP9vTNXz()M3tKD{1WSUEJmEptp0s3ZD2n-QCWTry)E#l6Uq>_;E;NAUBR zprYrazGOjOT73vw9E0_L(*5rW|KKcW9Rd1Z&+dN@ky1ccqL_;3ML;`DH>u7DGUB2j z54n;idJTy@yyF^AEEhsjV=Ov1(W+Da`C6V~mFxpV=pdFMMpY8sSYz2w-j>B~{~E^! z!|wDaI^C;%^JR70ScKV=w1;(UN37xV-MQC8!ap+PIyGZp-fMJ#k?KF}8!t;m$*{r! zW&E+38o?&wG+P|K1pBe^V7G=mDw1zr56JKha=O_4_7l0=H&S6xwSN_;BLKcgh|s$O zF8bcxvCwyfFG8J)87t0|3xEQ#4jH9yGP207|Jhg;sZg&hJ#Q!ZL0tfaJaQaeeEUjQ zi_HbiHNwK{Kg#XfNg#(=dS%&EJIz}?eAp|@^cvv=G(fp~r~Nh{`|dew;`43~{lm~( z7VJf_b^7xK!`cux&=ouT`C5|gzyEIi_n(@Qq9LnN*H8DhghCtT?=MUq3#%Cj)oB1d z`BxG@G^@WB1$_JV#7?~Kw6%Epr+`P`XDom0b>|dxmsgxl@_<}Cic^FGMWaA|jJ&9@ zz>zmRoM!4J?>#{>T+o`ZOn3$B-sLH?RAz#UGL%O_3(8;cb0l zUouUIOO25SGIl|Bw5Ju_cf@+${1MGrxl!AH5uxE_mo{5b&kr-Uny$$*MV9kcRhwv> zlz%n33N0LZPUmysnZYK_=-Bx>RASEC&zZGn6i-qOxkW%J>V{#-sTv8GvS|XvK9~&Vqx1%8eUI*zlytJE}mlvYpdKwS$1Z(*t zr16v97}V*>4xHG>zN>&pG`AmU!gmU@{$ry9#OA{6&w2X?}CGRCiM>Nw2Qf89gN^=gt9rJr;9wveNP&wtkiOTn_2PbuN)OW_CF zhw)B9U%mpxB}ZoO58!ye>ZIQFC>S)E#-syl+VSLsF*iB|wi9la+)3NkN*nXsok6a9 zSSd(J+XFqadKt7L28ijbfuRNseSqxl zd~{t|lUevbgB)X#1BWSF_f{#Ck@~O zqyQJuodd8e_P|@6vDDuYxX+^uU6eyPV(s_NMd_V7fpZnDCxDIr`eCuxj384AFgELp z=~F*;y*MY!syR^?Gie+j;Sy?q8KEyWjCPdDyIlBBSU;~nSq_O{G8hRcqKz>@|{uH`cBRr z(AGPk_1u`5-o#U6VN#CenC{`!xnRHJ#L0a|?Tdu6rN@9O;*8u$P+N!F> zQ26KisOKki(qAs>w!nS0`c3{UDk*IkJ_fFYJ*;A{lP!$vr=gwHA87q`nfGv(wcQpe zD=8zg6AocG6vYP99{jgI_KyRLijjCJIISVZ4Co1cVl#_Q(|*Ec=^sp z+S5B)jng`y`0}%CAFVbs%jt4n`D*DbO3}7A#TAXkq&0k%?C!UmUDH2-rqL{`GDv2C zR-U&BPmK1_%atc?O_2FJyPB*HpfzfCQyHWW#RL_w6Jn}alE62f{HiepCa3V4X8Xc) z0UzKa^%on3_~(MX5Vw9Q4;*`R9iwZ5Ym0O9y|)di&G0GACIz+iNOBk){$Z$I2nHCz z-6-)#c$^9X)`uQ+ZPDv3sahPx{ZvA3U!ur!n$Mm<8y#T|NY>+%h(DhFkc*w$z@a#r z@eJm0Oa`JY_odm$-jFMt#*+THP*kBAGIZT``D*`7J{p{VLUd>dTwGht*Nih&n3&?( zv9i!Gz3Z7!`R5q0xw!h{MZ`FDcJ{fv@m)Q_$2(%e+8_)c6c z-(O?G;+PMr4+E#}0Y`ax?VWwV@P+WaZsBZvlF@fW2@VS@$zS$M*DcmH zuCaq9XzD(R&CxqBsMg-Vlk~%{oeH}KwK>a8vL8;yy+(PWxE8IGebKknl>5HI6oM;M z^d-?1Cwtl%lH$fKS?o|}FO@!#^sSd6P}Zpi4z@q%$UmsJCi+$~cZ^Q@1{mefp^2xn zSsF;+2x>`6sk~@I*x#s}p6W;@10mwZbbxi6P9<{5s9O$wz$yoK8UjBaKPGUVLt@#d zJ2W^O`r)hmpUAl$b)LCz&R#Ptk&ozTQ0YCIAG`J_^;{vM4^8dFpdf=tV(9aN$un)y z#P#m@NJFOg#%;Qi*{%I>b{2Nyi%`X10M*#U*D+5U!a}697BichT&_zL zfW86>I+zoSEybBX;A{a4OC{I0MPCLqAbBlVG?!H;oVx{A;V)sD;%&uK!z_JyPYw zrNPhrQp_%mE!kx|zyHCXlLMd6fkI{7yZVxjqVIeVAM*qg>c{YLH?ohr1sz~_DK^}8 zD!ob;8?rZJ}Py1BAcIZIe2o= zsxS+QVf`*HUH1NyYgw>RnXpwYxOeMM17$IqWI+T70#6a|bgxIzSSO zI6)KkAUF*FZ6W9J8qi>m=u+e@IicLB6UZJPp^kv40prZ(Yfa|cvw_3aM)Tz& zv^M{Sefd~r+&+1fFV%;gt3#h|BXbf z9;hAqlMl^)rS`TNC{1`+?rkO13okyUBb=(Q+B3dwd2^;}<|6Ug#dLEqsSeNFa#;=vBz^21%?0XD6I1?I%r) zkWY&5Ie`LV@NPSWQ0%q8c)W+r^|ig|*AO4+mB)FbxgJi&xIH_5|AvxTC~#A=an9P^ zP4a)aON;=6g!PMK1`qi^CaCr6@y}H0TfuL%hx(pAegAJt%T;%8C!u-T)s4;X7;$!2 zVW&jf_7@xAVm1Mqa4u?xYP((^7KVq_$dPYqm;AC2JgH)J-7=tCOb7hMzcX>ZY0r# z`WH`#<-Gwq>T zVYR6oEa=sfV5tu{6jAL)iV&)5QY|(&+R(pd3IMv-PQ-6MG9}@5GF7mLWr0RIDfG(tn!YxgSX<9RFlULB9 zwJ1u>1h8S{ef7vTUw}QLWWjFcaUAf-0@~~r{0si03Ak{k{ZAp>2{lyI#L~qnQj(G5 zxi?=sT>9aUPz|{it!|ZwSbF@N9bu{kY>ykh*~^=UGo0I3v^q%Vd4)5_2(`Jp{kNi% zy*Gz@s%tdjtuy2_wO?x-?BVvWuzOT2^djpXcxT79o=s(P%O_}Y3YdZvl~4@64z@J6 z%&A#yS{7%w;{FAeBNM>zkE|TpY}ua>PZX;jurM5ka8p9$rqFsg2;6wJwPWrwTW+*p zXn8n}9UB8N4L~sWp_#Esn}u9W>RFf!xM|OCgY%Ug?hT0V;FN(5UZDK0;)|E`HpYkY zht(jjC?n&`*%hZMjya>QNv;zkksy|>zUv>g>EqWTziCBY_k?HeJk>9GhwyG2|I#N) zf@!j3l~G8FSVz>8q5ge&!BkeTghMwM#}680_D6cPjmTJI`DtLrmc1S=y6vCn3l7T6 z!}*i)L5DLBxg{uvBot^&TsL5f5icWNq5gw?g#(utX5RtRY3G%FsCU|!F9lS8{bO~D zd(e<^th|qoQ!y&0o|eW(?r=!ksxb% z)G@jTmnxpAo_n&Auz)fr?=-PnJi$d4IOm9`^*?xV!EBLaWBAlVp`fj zIDBUp0WXs?K*Ym)y})M0Hm+qN2h-`JO!^1S2%c`!0#W%wvf^$+fJk_3}w58hAe zrp~WRTvK>%MLez#oP>;@4dlAE4{KPO>hMs^EwvWM&cqS15G(%}2&Kd?X)KU7B6CIJ z*dSlpi^6LKA{TjQTaC->xeXTF4*Yz3vI-pTugb8^l>Y z4v{1dl`MCWr>O^&rE^V2)Q&5nPg#Wf%XPmVg}R7@q(#(XK+41>&;T(g4kB<7hetVU zo{Vyt(Ac%5)N3R+8vg)J3L|A_;Hjq^SX+|Vv;xis0$Z@lLf}2(If`(`jUR{E?9%-- z_8(P=z8>#1K36(mzADmzzcw9J6M9)nIVMj-S*sS}pAgd?*=8E&M6>(-v@2`rZtlUa zaHk6;6}PDxU6Bx{ILv4A_b?e-vBq4oBK+TX48JK6r#)XsUYoA>dPumo`7ius38w*D zzJDE7YdW6jwvj~o=@YCv2Q!=5I2h(k$0l=ZHEH2x_af8$x+sloC8u8!V79w}!0D~L z0w@4Ju9ftod#Is$O=rWUQj5)>pHOjZ_Sq$~O#Kz#D3I|~oClO(9SJCpi%(OltTqP* z1~JA>MSnNmWBxsfrGqBs|Nf63_Dj!7^W7`(W3DK6QNSv|o$gEJ6Lyf=YF>P+RNwa? zqq^l>(|Iw^Hdj?&uH$>R7Efm6tt}cA4}}NE4}prayiQ}c{JNG z$Xd_>aX|@z*Va}1#v$=DaHe8s*peJoPouN&^4u=`u5S}^s8^KJK6bkgwV-F^o%hFgQlH{*zrE~unFX8BqziDJ%4xW=G z)AjjTCHW9G=o2pQb}z|y!q;VOQ);XWQZK`Bfr*Le<(-pzZ$ID+SkjkTpFnaqcRpS$ zp`#6c64u>Q(V8ViryWhg zkQzw@Q1}h6;k&P+R;CI1Yuj4??`M5TbRZDl8vge~{@;t~QqyCw_UzAerI<)oq^G0u zMx+#)cso1>^fg@xbhgQ9xe+-7+;)MH?14Xm6aOhUH1P;XppR4KcVh8fxGMUDf)3N5 zL_Xi9F~OrE$BK()AQgW03abvpKBJT*(h64AHP}8?8>Y@a#Aai1o~d&um?k2bn4Wqm zD*r(?J8CDXJyc0ACDmK>N+}e+nk06h2atslil2F`QD`E>aq^?RHvJVb#dW_(1mBiS zB@-E;BScuG87#Ldu~+KoD#<9vdKsR(Z{~2}ecfbJM75jjN*J!Gz%G4u+Mj-X$whmL z+_a{wB*NI@0A^Z2?5UySt733J85DU2+VswrHPCWRTJeKpB|R+zXpDGP3_@y*Gk7Mz z+ySH~0TxeB!}HW@CU)$_yzis@J$mnHl$n&@#L zOFx6uXUZ#7=!w5gmo}>W;u|_Cl*g%-Uj13B=3mup5x)aMPeRzASojYLd|wd_jNQOT zPl|(U9eK_}58K~r)zd#&zfrYrRxq=tJ;LU!iPr6S+WzMmjSzwk7p!4$*b&L|*@0*C z*4t0>93ozfj&egnP_m~%Ft^?~_{h`yngER-78~Gyt69^ciu`UYGuBCyj?%lUKsK-k1tD7`u_K^fMSl2{|Gmvz*>tUt9KQ9eeS zEAv(IYLtJUcqv+5kUo0D|1)i(R1A#?qMEAL{Y;%H@F_n1R@_aTWF~JVVegNrTBcr* zm5`u1U8@&cQ!pc_CHF*u&g%7JUJ@<&EeF0I5TxYnQK~xsKVLzxfI{bqlIO;JLzsbC z!>A`8-aakblSi+BN@g{296|I{^7V?K+J~0HS@~|IJ+9Q5;kB)Q<+v$pqt1L3>Y)3O z@o`gVQ8&Iz?Q~4U8C1yY7{^>~{(FVNe>0!G(De8aD5Xgi{p)Yn;p1x}!Wyn)Nr*<$ zTvml>{j^V>KZ?;>Dr&(n3>6-|SJGPG$#?IK1ZzOHeJGpfxHK!>Tgn!t%i})yZ!uIm*L>o9fLy#zmUU3}(xs!_i536z zg8hdLP3En8m%>~0bq_F$4?p}f)VwAMIPwmkdaIeW@;z!0nbS<})tAZT=F&*6e^${r=7v07NsLK-@dLQUC4;)mzG!dmm$66eH0P_d z_M?H#-2=l-*G=OFhTe{`zD)?IS%M4T{xMD5zAAR_(li47DpG2CNf{6N`cb@7Zp5M{ z;{9}!>sXB`f$Q9$y5MpERdv+T-dAHAw7M~V&56DwZnW^@enJ42MidWULo=~sn~DBj z&n}v-#D3q^n(Yr1P4VO3RR!^4Mi)1j^n2}FLpAa|9Bvpq z$b@ZuS(rxJ)J4(wNAS@oV124J+vst=I8LQuIW?7?VwX&%*Jj)=k<%}?n0c!u z&m^!Z9skZkqTwF#hgroAqTx1;24w{47PfaVdo~sAD5qQZs-hM$aLT%#;z5+i>Bhv; zt}U0*#7ArkWbDSp&UE;f)?T|}_=&u2N=@6j7m6J5&W|)O45e+hR5$V@zqKVlMF?zP*0C= z@a4)0#C*tC%I0YFy3C2*is>KSOY_dAoBT@m;kxJ_IwHuzLCi>4Z6a@FN}~JAk^9Z% zH=us}a6ihs*5X2<4o{_50#v#4$48k@hfWtn;~DTCb%OIEAzUAbFTYdt#?VqLv9 zI;U)m?*gU5re8ns-|*M9@!wl4Ic3ztW;UPxlmX%Qu79|ny!HM!?dq|h3MBMw@|knz z8cH<_AZ5yZQ?yz?VxH4e)BCdgfvaPrjoH*A(3ww^+JF`PLz;gd*PGp!x$M)nod=!* z3%8CNS{_3kLu8!EJ-GB-XF4RZ1`&lK)PGhzu39~{gz^iC^i5tIU%ec_Tog)#8ap!_8?$F~q8LFL@I@Fcjt(4pr;&+7-1Fj?!o>(@97^xf`JnIr^tsGuw z9+wRCIdLpHL^Q9$6PX;pWLAY%@EU#DjtXq~v^jLpeYXs;*))oS!>caQ^j9=cS+T!= ztT6PD-lm-7wA^>Wt6yg0&yIfQIIU`Ykli-(?kijv<#*m3lWg%b=o)=CL-x~TSKuwo zuXYXkJKeZ5F5o^4&_&H&)z!p`2QqP?t_c2r5pxI7j>!*Uhek6=z|!6|sl4-K$AG+- zc_{BV7h^!M3wIYh3dawo#y^f@aFSVkHfTK8#Ws25F5NvKH2lYTROC>Bd0y83dFXxc z6YNS=(FdLffSmPV^e}%b;+F?3-~L||(ADRAAlV3%RFc!I8KB((v_hPx$Ek?%YKL2> za_Wqc`7gB1*H6Y3kRLX-uJJoh{(`E__pPXfHtFXTNUF^fXO`|3G14I%{l&$wV*0Hx ze=W|+^@`v1ibq6^YvoMq#l7QnEHO{QG6V%7C994XLuq2~T33q=18OpgjC4`Ih@65D z*`@DLV+~2e!dJ6{>5f_gk{nAX$4!>3q%e(#-x{d?O0Dc|nkEjWofqoE0>j~3VKd~S zZWTh<6VzaZGbkX|r86w!;|Z=3*>$LH(|cQaIbXaU2WUY#4m8sOUVa!rk!}fh4T#aO zdXau|x>we@yh=$#`ZX2pGd6xGcfl2si-1zQYTsrh1dLst<;=X$tc>5BZ|Y^Lf%WlB+}lQ4X%9^g~@ zV@gB+j3Z?P18u~dDJ%ndW^ z4b(Z`5VRWmSUi0Q7vBBb+50Hw;dFIbd^h?F{(O0x(&^JhEmy4(m_f>l_d($wc@%($ z2%R#0yv~Nz=lQXGnF08pqP79qf@9(Vcc~CeF=Sg5kEd!=Rr2vt&%U~=FK^lN)!BYF z$)LN}Bc7qmuK)6twLzsPnlgzRGk7TKks-VN_H@d`m@s;zXbd6I-_++)l*T(9w*P0P zLp~&{?H*|OiB185(!-FR&k`>EKwrq>dwWuBQWjtKjk-z=9ac4d;A03@lRKENin5a+1w3ZH)v>z?ZORfykf?7 zZi5s!P#m6PcD^h|_P!nY50btH6SCDSbL(*7hRv{ve-+{xZQ8&>Xr2IAd-oeY1mnpA0rz|*rzhu720j$ZY0sLi=)4Bg+{ zX1J@EG)83J=Hac6JaaMO!74^;bIO8mFY)BCN4#Bp|AimX0Sw;y?rCByPHMo%6jr2~ zN^8wKy9cJVsV;Gv`W@KQD!LGq^K!;Kq<^+wxOdzZ$a_^OBonx6b7^xJJ)Xj4Sf$WZ z_QLN8lupTPbmb7S3RG`jL|-ozArjChm+6{4v|NXt;TKI}Fz$W;f|}Fl_+UK|({$*3 zXEcg3_|SW^f(n+ia9%7Fg01$e84n7N>%5C}5d(`201kkujS84_#-ziOfHJFW0J>xNzCu(|!f-jiDyb?4hV(?P)5{hZm6X{s2tQog85 z<#a(o&`{At`03#MjQs@(xuWM=4@SLP@!BeoT&!X2d5wpa#AicsB=~!AS_O99)AF-H zEU8~A+MTw2IETBAeZQu+iX770(Fha67uP!Yy4Ql$$e7dxiZgB2j}dEG^fDGp2}1oqw5AnvUvkxg=QT15u% zqLmKR`jh7fVwWzF@7YMsNshgsILU4!%$S7EYAn2NKV<)O6gA22u*tM_#!$)uK=iCX zF~bJjO?cOa1N@Hqs~B;n_d;_;O#bVTRDxCYLKd`g#R~mAp6Pg?Vc$=T^Z}Ys$(KaN`5L;@Xi1Zz$piYcN#Z|4Yma3)Gwp zdV@EaEBlqR9$&->gU23!$IE#sPPDiPU?VCyW8w|0(5P%ywRWTDPoxC^*ZgJF6A zS9r0n2S>uWFHL@VAm$UV?V$vb&vP$apgJMB(X)BBalP#w>h=`5ltm56P7k-wiJo^` z=JzQ#_UxMI9um)t4v%tw5Tx9~e0(6PWHjVaSd%K(oCR{_sanLETr^|ru#o-4d0^uCmbU5?ft(+O)4~_VEzRX4CJWEc zAKnrx*{h{(>%h2AY|1_fOZYo$8~r<>=X4NaNUx5+wOxD>_f^jd?>hy>4UJLM$~l(I z+e{c){!8c;7CL!!m{Se*f1-2W{F)l%E7sQVlt*Xnfa#ut{cXFsHotSwmqZ2Ok=#vM z|AJo^DxlxQCf>L;y#Cmupw%BI8LW`jd+3u$Wx6J`S#8B2Ld^|A%<|MaKZ~HGuQza| z=D&Vi2f;y*ChP?CsS)GV9p~XG?`f^vMF0E<+qQb}Up~Bw9Ewcbs7|z*9OKCUClxgP zN5oLu|3UY*zyP(jXogn(6Lno`qj9L4^M|%pSzmw6$jFh7PcHe~;879#7l2$_Hz|;- z#=QJ>HZGKSU1qdvP+L}SeMOC6HN?!yJ3iD!bliBASzM2{;+4&OD`g#4;*WR1kB4{V zftl$amZu!!6>QNns<$WRn7l6=jDV)7AGB+)3)F{0rzf(U`Nag=i?^I=}nc<;DN2G5@PHPhdn~A{Zty+NBYx^~2V=sNrxmq$%@|^ zv}`Nx4hBJGZzx6+_+J3?l(_py=7XA@BKvLLy`L2vLVll@${0AK290p$4IyrOQ4ho2 z9Fi_%<4n|JsAjWj$@z+-`L2}_w&`UC2MXDM-lpDwc{zDbRZQ*U^=n^-rFhM%hgk23 z(elqr?&kgUmZu)a(C4z0NVAaWxdmh;{1DV15^TQL1ITJh?A(Z4Sx-C#M@CzkZUtCr7?0>nL>w+bJzZEx?pHAEu#%z z{$yA&OO``i78xa*Br}@OVTXhPd<^Y9g;eFEw zW&e%eiHTOWKO4U(vA}kfNcKbjX+(D*=5CC}ZYwbu*gy8hqiF)YsFnb4>pU5sUaG=k z+X28xfW5>@G*fDl`_478lW%QE`C$zYz^T+w$&zVuw7M@&TOa^_j* z=(bN8j!Eg_USEzl2?BN&b8mdqA(D($)x22yh7UG4;syW8_T_lo5ALl*;>R|9up{G` zCqt>H1N6Y}_TUUbhi~?=$BcJ%w+Fe5JT~_~)9`J;>0O^Ng+`*(FV4?1{(g-^S6ewS z2~5AxwWlB&MHV<@5oeoc#^`v;(l@o1U#ioX+vMRz3kg{Yr~3bHd{|XPtPYXOCdCWs zOuku{Oj$@V6_hS1r8awgltFrow6}i6=*z$KY-}S{6Z}j4T9?xs0ON!F=lSooV!KyF z%NtGtfFO(a7+9X=IwTJo<&K&D7Q2>e&GHGE*(>x`?-3K<{bJ5j`-qZjPtGnII$@^^ z>>SOi{B!5y#F)V=<8M;UANEm8D0q^SK^Tk7385H}^{mIrMSfH-oG+&4=O8A`BJhp& zgxCw2wg=+Qyf1E|Jhi&L{GZ*di5xsy$udR%WM)7M-oe~xSLX)#%Gu1kGk*bJYw@@@ z$;6~w_MqzCyhxFJDp6A&kc8=f>aXA_jO>!X{0y>H+bP}7+HH;y7^d#-l{(JfHC;9D zdD})^193qXujdfOnO`5#{ZbvNR0}_6n5zD=tx?v{|L0PO(Z|%6`$}bVKaqXUp4%ho z>i8jB$w4K}mg@|5dl5<6F_>&XiMSe6@-^-@)%mml*)6tRpH>}7uqpAZ%kp7fY4dfc z$bihTVn>#(H!DuH{Hv@9C5(A+PSc{mFIl}G0HQBVq>8;oNmY)#{|uYOKs{&Z-WUh4 zhK&H4c0dSSsVKM0rOW%!caKZ2P)=kh$q%6+pPb4I({;=+bts_(?qZkbR5LHZ`bI5I z0xC9kb>8w0O16FP5m&>L!|?_00})9Y!g0PLy%>9|q!v?dwm*QkA!A{pVm5{~rjlns z6`o<1qAV0D!Miw)&El%ECey$#b$l_hQ;%`ULjjV3-xtx;an+!#L9%S^}% zRXep&>7YX7L?f)gm_f|PtE^M%+=gX3;{N`a<=a8IXr`|&%B0UxM?$=_ukad~58gh= zQUq_?@(?{GE|l-Z^$Yh^4WRs`!JQ5+`-{gJk68^?n}H{(9m~8ev@5Q&jB@&h zgL3j5{kJS)RT-rf=@k_s^ZSy|EnaX0xp)A|ZTG!!%{9BJqY?a!8BagVFY&pl&N~cr zS%+&ey%!6cQrR74@zHQ-d;nH=c|ekDp7l8}bL{Kh-K(ivq6W$NdD0W&^;8S28fMm4 zj_tD7rpNy^K~Q!$*QN$!>rU$*v=i6_WY_pHb?Z?=O|N@PQnEzdSQRRS^SFLx=UBIjFsRuywdRp@1X}HV5B;JpW7n9PM+IkSt z&@M}(n1>&T*2^Gm$@&X;lZT}11fq#xDi|)U(fEd?ynZE?N&6ExF$k!5Gd7;=?mtML z0-4Pf3MW$pab>muPGY?IPz?u6*j(@Mu+d!)?}x8DLKFqLral6)HowwZPn2?H7+F^I zgSLOWsCUf&TuW>2cp*c%^|zlY+#zjS8^XFDYbX$saI1T`-438=T_Rq7!Y#RObm7r* z$rkz4N9$SGc2LjZV>H|pBch0Mop;}TZA39jfJea0yFAD5@&o>NF$d72fAnls7u*nZ z_Bd`c0Cy30M-4zvL+YTi1}VN7b$88i&5>6PX)~!YwHzqsRNdD{PhJR&4Yh$^Hry2+ zP1f&npRFc-JPH#8B_((^C_%3`4wzP0ovXTOeEx!UH^HG!HWmhs%teACCwcj@-i6Nl zUr(9sj(fD9rFO`J8aEe=2}UPI>1P5ZjHQlJ5369n1w9gmX z+jh{Jv3P2MT)4VD7VE4yoVqSX!vuF+5`{1DvD!XI*P|ubW6Vpr$OjcMg^7hAMCD@a zfAb4P$at9h4qr3;;k=vL)<$OfGw%L)Qz=Y()if3X;zh5g#NhH)1ewk zz0}{sL#dT#VNEyryYtq^qn^|&ueUvvB*vhMwtg+{*0k=v zpN%|Ss#N>qk?C{x^>pw1=9c_6WyMd@DEH|l{vjhU^3?ab2}1YfYkigLxbstP7mp<5 zlM6SkOWy7uU)~W)VZO>t{XAlDE2oxU|4bHT3mHUuG=#gJ^i7GpIu9>;?WNQb=a|&H z`+8?1(7Ntl7xOm)#p&iZE5rOL$$(vAPfCACyxWe(3q9+KYitHe9O;C~TRP^=Z=C1a z@jdlRy+q}b(5xS5#4q*uLrBTYj~GR;PiEnpRNYu}rMRl)*ouCA{l&uR^#RoF80#)E zTi9kt-wOd6(wauD2^*r3@#=o@k-$~Q=gU&9g*Yb^V9(2QzxVqhP(TEm=mw4jZ@>>@W{52I6ykIJ(KAPLYZlL z_3m$G2;LDOVQjKTfR*QOZvzJiK2m-){jb4RU$gK?#76)yE++I2>RrXYuX24H8&x?p< zBN8~%d7kw~Zb4ob0bdw_M`SpNh&UE_68_7x_$*Dpx?kiGd~cxoXV`nG&BxYFS)c#F zUv(ESy^Xw(d3*o6<=O0E5vS&xHnr=$^CO+)FggqEia?Q6n0&p_rqs0vf;Ql&WvA4j z&VGhQe{3G9bvSQ?C8?A$QL|RV&k@5t*n|tMkBukeS=wG1Im%m4j2{2e*Vz~#rmOv) zbL{zka5>Tir=L+}B)6XMv@xwKnC`d{cG)MYJ(4JLuEw?~lW{?X2ttmIvW2Treo@v# z4Mll!$!o|P-e#0M8gM|}d~G0{L4F^jxL2)0mcii6UEyVL)Hz0V^vSU_9YxhWp8dR*fNFn1)akQSm84;tX9rsB zxA{71@;)l#Co$tEK67CxZG>Kfa|Qf^=u=70;g?^~RI?H#B@S5y+QQ=n4{Z0&-i{8hUmj@a;ZPh2(<&XzDZLAJB|(JWz5F=$GUvYZ zHhAjK4A+iC2E+c%vbR`=i$_&L9DEn2dI`>1*!ot})%#>VRMMvPOVbircNqU1S0TbR z@PD?y;nd0_^$+zH+o>wNg!4oi{fleZiSQVwAMt3$bxrMOXjhhcYgTp7Ve9+uYPt)Q z>n0RApO+mn#CQ~asZ_To&2BMs3w*z%ro|^N*PS~_0viGibn2znHO@BR%^h!9F6+a> z=h_K$_c_Fs=$!tTEpm-z6%j}N{1sXtI2#i!-Zppr@TL6qI~g!9QLCvNWX$KUx#`zC zdI$}~5VH5~XNb(u!bkZ8!1g=y&EJ!SvOKP?xL^iGhCJ>O;{w}`6`asX)4w;7kGiN} zH`RZLj3aapwC~^ zRCm3$FU}JV7^{C){bSVcyvvglN)Ct+WoxDzx~YA8e|Esh4?(Ph*~)f9^)3T9Danx2 zqDe~h7Rj$cE=%Y`4WfQ2H;Cd*p@FjsO{bicI~ofnwhX4A?w=wkn5$MkpY|Bd@|BMk zF6ycA1`8rJ8z3kQD%j4YH_ZK-5>jg29U*A<0yP092!5ZKe_s-5z%ZgH`m@zAlp_5B z@w2tT&anCGL&*B_elu4GOMWnYdWGznn?IdIcqbtRSP_R3#N4cgs z?h%@(J?9a^4IIxRg?Sa+HlhZpbEwZpis4>MUhVhVtgA^zh;^0NTvhyEy0iTN%jcHR-g3w$Ic+iOZ-;)JG0r^I5pkRP$cH)yw4Eic={Z=N8-~n zPK~;xJw2j@Oh|jbYjZ*Q+ zir?n^$7~B&K9%6;C0LlL(q3FS1P&7HeDylji6qDQn!D%DAC{@%#Q=ib2XJts>$xy= z()1@M*B$c+7rTYf&qJ@7EOmS-PDlL?3=S*;I32E-(BFqNKEyc{ojKh87EQ`VlW@;J zf|ohud3Y+7JTLDK7Jux@`WF06FVg`x17erTp>9iYwK-p^T;h1uwi6VTx7os)Lo!EI zXL7(q1?B=F8h;TZQWiPd(I2D7N#``*CiJy$4Rkk!RK-8E8xbF<3V|LUM~TvPhP_Hp zzfH!Dx=4Vrso>pW$c`TTBDqRxAgpI(F;6qHI#;9)8*Xkqp7y;bx9ghWg9l>`|H6QL z%X#RqRKGC^2UJ=d8F<>TaVD;}c{Z&8H`e!}^Z_(9#D;NqJvgKKM60zVA zXJKxFYz)ow^yi7LwcKy2z`HQS|B6#);qjfY-C1bH6HM@+iZjnJEe*1_11&1 zXj5fG@u?D?_yP``+3<<;*b{Iq7&i;wP zwlfxvON1jN*hUU=p|OOX52&)4Y<`t)?_l9*ytSg?ZHURlLF6oJgN7GLx$Og;zmsc( zeAJAL(qz{Y#jfie+pGOD5VE(1UY>h2Yn5TGaEUF_%coFM_BQf)Y${wUalx1ok(WvT zzW3L;gptX9Gj8A4@~!IXC^lW#Qgln`Hf!dab{9!^-1RlbIv{b2bA|yXJ(a z)Qy|P+$}6Oj7x9W>%!9CS-%gYG+QYK&dXAlTlsWhY{s~a6yFQo0$&b*z#0Y%asA5$ z<|`mzRaP8kpInPtB->521cE>UhR?^*zwe)rBa;E3r7(joE}Xh^)S65$UKYe(|K8%=Y*b~ndcA7%ldNS z)3VNbDt(L_PaU>yyPLXppWj9c3w-9PWhUO8uftHGU}u^wUh|^e78D_8eB3eOT=B|E z`CL*HP8DCZ#AgX_Y(wmxu+9H~6}5;-g%5W0XrhZf17GDaB%PHM-b+50Otw>=>OX-h zaQ&r`9(gJ%A*SLcI~{gx$tm3P^=FQq<{axQ0-!-;P(u}z_xYazZ4yte7e|rTT47NE{J{f9YI${r zxD4Xls2EGD)bA~L!}s4N*n{Mrmb1EH;lk;HzMY;>GBpW!fBlLd5l zH&vNeZWfE2j7+H#G+71$8tCAG>kuF@f=aUY-7ba$Y2~=6NDP;=f=>KwA=1HKu@E&*`6*s)Z|v2aGWTSJY4f#FpaoAJZC-;=6%Ufh zhp?OZ6u^isaS{9bqxJocB0F+@m9Pp4DOG+sLPQFmYV1s6L3eKcE2_eHy+S`H!vq0b z>8u|WJZf=D91b#CqOz>#X?vU_BjFAT^a6=mjcsMu#f)o4!3_!YAfw!55YPNb51u%NA!&(l|QMsBP)~W2))e zsNog+p?0=!@%y(s zsmyf$nk^n2{vz3&=bV&mW8PZ5Le?zAyj_tZrEx)wpl(ij{LB|Fh^6}?c+F8~;cPI_ zQ9Ga`cK=HVRaO#;lr=6_sJ@Tv-kLvD z@GtYV8r|>03imw<|0wU@$@5U&>)X%N+@1IVE&0$qJA-o@nJ1ckfxE*q?vJK-ICw3L zk-0M`(x6WuCC?p3Z(U93`zKxjSJ14>^j3SIqH|=fQ=N9wYXy&}f~t{={|xe7gC)W| zc2clU2^+vX-Cv8TUs)EnNsM&tuk&ftrv6AZ|5N+g@L&Cx3?7+`(>0GnTJm zQ&9z^s_g=d8;5Oqz?s^+BO?{F`IP!1Qty?bn_hbKue^0Fi`c1SJz|Uiio(T zPSRRFS6``-6f*H1Wq5f(w@_w?ET7d0q;3~MEto4BA%8S(EG(Pc$qJ_On72VD9_M*T zj$IRjL_-$$?-d`tEQVG2!8_HSCFtU=Zewr;IS5Hs7~<4v<8~O;2m_knO#7OIPY(h* zujyeGD7yo+)5h|;j~5}T<}`5Q)&mr9AA#s>3ULX>=YH70A$z@#aXeP{mrzzZc)K!n+6*UnoAzk56RJRp2}8s=%6oV&C;-o(n7pFb>d!q@;k1nt9CMdHYJ~C zEy@0M)Y$9i#*Ksi9zDjQ>Mmp6uU}{}4GF%SY(NOxs#;1CNSp-TW1Qh1<14-IgYv=Mk{w z_3>67U)1kNL3TEy=ltMM?Mx~}WUTg&v!FX(KcKhpkJ%d@sAei6_~85rY<9gTpt@5Z zgarG5%Pys02C&1}z~gzPGSQ!pC?9)y<;{znHUYG&2=5NIDsX&7tHqi9!Tg~rkfi*}7B(8I` zwjsPEX?GKLp`O( z{2X$gi8~y}?5D!dE{Bj4+AYe#V=G9aZL{q*{KTFjSyV~)lJ-!$@xGgtg%=??%^}Zt zPUp#5-)wX*oyB+_*?R$M+4y88@k^^G){euNk}tAp`^zJAWeXyay3R)Bm&p21(fbBP zuXW^8#T4_rog{|Hw+>k2Vb2`OPqNAf+pI*Fy;IEpU9iUP`FK^U*jo`EyWxkQwQ*^TTih0A@vv*$dXqmz@;i}lo7=xE z6r-QsP7lTU6z92|yxbw1uoEA7>H4Lv^ITXZY0T!{#)o>f(B(ZgqW&4a#X&aE>KMP5 zVYoy&tmF7E9se;y8&^^hNa^NL0nV^&OQI?{3YtzSm0VC=s? zijSjsEz?kO`jLPwf#=)T^y<7GGqM}^iCW+3k_|2Ke)%P|JFLn10SR8@AHSbmrkh5f z^A@r&bj`>N=%y~6!!JAh#Lu7Lf*Q!BLU*ovlH1Rg{%C`NS?Ywy88dm{1Bd(}OKg!pmd`V>XMp2yy z!c})Fzg?+rU10t8TuoR(I)8ww`EP)A$ZZ%PVN<~>#^h7_Bi0(5MhnMfMPr7gR4u;x z#4g(pdTM>0Ip4pibPtOg=08E(l)t{WBKOO#PSa@bZ3PB&NYkb=&Q>hDf!!=uxGaki znxVlMqKYup=euXLog2|&SMC&NGV+hgclAK0t-DR`tb6B^EW4JaI@9FKa@4CFuN>x8 zN(P}FjvMnHgg4dwjsQr-F84YB0&bIlJ1T;%;BKYsjo7TVqZ4Y2jtUNgw?q}^WuCC8 zrArdr;*Cu357h5snU*Qa-hzKon8qs}!b(m`Gec;KOZN@CW`b%^M(RhUKTN*abS>J& z{*DI%D&Us_1=p1P5m2+LO$)HY7>b8w z6von6`fq<=)VfEYVV(I#D&bJ46Dj#gEQ*t)=l0Sci%dF3J(F4TygTnoHRCYqF@}~O zEJ>Lb(hC7Gq{qLCDPdlC&LK4foe8{Q2QKf=NC2HQAHbg= zt8X`o_7>hUifDo|=X&xw?EU5~4WxN$rOnvBQkm)*tP4yrc%aq;4(_?NtKe2QjyJ^j z7Rel?gB;_zj`c=g*kQ@%Rn4z_%5XX5vjMRXo;7LzY6WjWo7%=cC}_encw6DwSy9rC ztSE>wX8J^+;vTHrcqW!nP>fcywp+fUvXXBqIGQ7ugN^a`reDLsguZb`MYL}4t*8E* z@j`@^pWhd!Ve+MUjdnDjE2Z_tqO%9**xe#3De5$d*E9wzJr0;o>R#WM@1aMXaF7AVOe&LM7wyXa~p(;7!p_Svpbh=Z*!Osn*@!_FT~YngQ2<_FwZdBRS)A`4h|7 zCjP_isbe*!K6X)C^`vK!+Fu6Vp$uDDn%{Dg#V$Y2(WMFsQO;-~m`VNWzjWE=ozsYH z5a_EcKv+GFwNBaB^UQ~@T~AY!?rlHiSOErr zQ=?b0YO1`cMk$Q`N`jf}umAj@&#^c)+6^bERsT5n&LycW za1VE9^a|*2tmi*vTk5o2e5_XY?yq5q2V(|lvQSC?yLPSox8-bGJLmFPhz{d<{g$_& zTYVew;S0W^NN>>7X2IEJR^(G<7nKiNlRK5hZ3Ekbh2%eO`NO&UqSD*ciY__DY;v33 zdM&mY)cJXh+&Esb*yeK$V8un~a_Q9fKxUi)W87D-7o~b{@`nr}T`H*ac^_n=thc-) zvtDcBI_A)uQEU;Y!RP2Q9@fh9VM4?edg``L7jvQ$^Y;~vb500mV8jXWeSp9x2cq-H z6K108Dh%6y;r zM}Pf<9R4u=pohDt0#nMKHYh}f3h*V982^J+P;!uF%JYvj|Ld=OoLoMCx@O4|6W?zG za^Ua(ln>|Ql=q^==#5=3-4+|RWlt~Owf)QCMZxg9&bGe^-GQ`&q7jNt36JzMqh!5meB_kkK^f(j>Mz?a~ml7XAdZW;ceF3f0? zDS7&g52mCQ50sshzzTwVk2V51FTe7_Ke`L4CKsB?R*Y!y_4ef ze#7|J?6aNrsaYIL<9NSKt{{1A)U%QX226&*dU|&{-tbQ$39I`XBx6uDcW%_W2}<8Q z=cAav3;&{8;>?AVBZD_f66!cSib~v-AGt|FoCZec$(zz&o8M$NylT_}e7Pm}N}7vq>EAZma9BTq-lhmpN~rLgnu zA!1WAZD>dkOm=qk*pCB=w$iZ8^UUOEtRAp`F!hJ3OvT(prqg^|uli0iqeov`UmPHs z1RJq3C@{%8ei8BgAC%jPgpZMCT_lE)YclTJIkQgVq<;yLxTXQSmYkU<^3uII!A3cfq>n>OeApd8m#Qml{nBE`Shfd6BvIM2=3|;|yjCSR9CIxqpk@<<*n2|kX z$&kroMTz6*&sKqoZcdJ#H{~y1I$yfRm3nk`ME(xDMT#ElqJQe^NvjO>7_t67fhLsi zdg|J3s-BwQ?pi)Z%z2Z|kE?SKRg1|=BY3wAN8GP}^Zp1C67 zQCFv)Q*b^&+c--(NtG1Xu^BU93+Iffk_}w5PKSoBnUamNd=3+MS%0#s=gktMd8dGG zO&dlOVi6abtP`HJ&=w3_`q6$8TCW^e3r2d3g1!p@c`DoF~M`?k8YF0 zUnO1zU~H%vo5UcT*ny*hb+k0b<`&YoyLhg2`4cgJmPV3owdp{8+7uc;^Z5cZHQa{p zxjE>98Y!U;eY;(8At&R=I~MHuWkiD{Y))?cq-u*hgxS;#fJWb)OW-6C&@GRHUWDA8{m9_K&$hA14;Wr$5;QZbMtn)Y`V8+(`Vvxcj=Rn7_=@>6^=Oikd$3 ze5?W+0R^oWX}Pi<#*5TobLA&E%8pyVBfpm{cdi7-5sQp-zYFr1{*dxg6wW8gR+LJ1 zvPKK}`BPJC?z78xTm3cw-MP`8FPUWeo~Z?nA7AA9j5eM#@qKOjnld_8e-4U=GPGf} zBWLc+-DSDqClEf?ba>5BWXJDlRaF~zkId`k7TZux8*eb&^2_MC5~rP7q5Qi+Y9@%v zKli2Qq75`5^wvpdxa5QXfDW;1^^>=KDbn9=B;H9D6^tH+o+K)vbIg04+%^!DkAHcK z<|`|Qdcx^3bW+7EebS~0mdhg^8*y7aQ0Q6uA5@^>WfXNOd(#{gWqp7xylkSCtHgcEI zV`($mKo!v#!U1->vBRxQ3VyXZWvNwx7KzH0hjnxD<3u7c((F_=Q>0LX?bEqr0D=#F zi8)k$FekTZWM-cI>DxVtCu+sU>J@0Y;E#R^!Vd~Hm0b0^-$$egn%prxhAm#rFo2Zl zDciF`T<{7U41yo$xR0QQjh}}R1K5vtaW}%)7ASfavH`*3ZqM}KImyZIHH@v@SIcVa z&K!)7>Nh^X-%|JF~7RmAv=-X zOf_h<@i6+@k}fNzCRoL9W;c0kynmm;D9~%)Lo+#)e8HqC)P^7Ivrcy2GH+96Oe^t- z_V1#GnrrQhgUa39?7(`~~!m;^q5 zpH#dSKYr7>LWW*6WC9*o5F-NN2t)h{_DcfS$fi}r^)d%rxV6B9yZQbsu|bePS&etx zs(1xk?2m(tHHsBntmkbSX~{T`tP4TYJCH}-?tZNjYU*u=|gjZGD9=U zynULY4@UFSn;%YZPwz9+w=0riw*xX(CoAKCi5U} zpj})rLp7Va#o?813&b);C`@~1Jo_1A_Sg;a2|)B?xLsHe8IyFl3qakjVHgo zH9XjI2z)vZj4WtQ=K$f_QLR!LjCEZ$yX#a?7iaTBKNE?ThAo>{kA8;$d$0fZ-yuXC zn5w*jxqw7iJ>(cm2;2iHAaj8(+f6!N;{fOY)CTp!gAu>z1X1KTx7XoG(Jdj_!AA#I zi2yh0JF<)5beymlvp*MX9Tk6co>@Tn#RK7gHIYEVT3wp?ZEb}HB#pq<*oVM=IcQZr zNi{+3f4FHq8-IR=7ttx)P6N>by5s_nEP=sl5Am3b%AMBUoK1Ie(EQ|7>EVc74~FSb zujDoQ4tx(3rF(24*D&t0|E$58{PZA{39q%^$BN?*u|5{qPwYL6xDWiyd#^05n(Wt` z_s&3*H{&C^}bS3fU7)-dl&o2 zHZtJdWkA1=j>1B2r4iM~Y3}h;hYkJ-i$Dq|o4dhx3>cQUmMoKaJz^l(3hzTm6_%(m zxh)Q_i{+8yD8yzm5OvYzZd%TB^6y<^g<$ufsU&AxzuV{OG)G(liXw}W^u}&p!O+bw zYn2Tpg)M<5_B&)^SRUAO{4Mys`p$Q%68YFQGnIZF$|3INI`@dAmG|Lcg4E%ZRCu z8wY{hDgn`kZYk9_<@=2i3w>#11C{};MSoQ3gMzK+h4)`Ee-m^ZLHKFd&EH*^EyR&L z{7j`+QuDxUbI9;Gxq=r;3|w(P1D3yuW$}C~GgPD~>G8I!FkzZ}XUdoOk|uja+bCno z&CoF6M(-|r>sjm!?ctHp$n8emuAT(f&ASqJi{6sM2q898AxVDYR9;ne7@<>xJ?JXC z9^xo)GU^|w$Xxu@IFTaDC}jYp$EJ0&a?@Kx@2@LX*+aZEIvs-c z=`ONTIx^<}wwmw2Ex2s$f5)y3Kcv|&e>Hq5x}W@1ea{7VuZC!dU@Pc1d-z-wm9^-X z|BOD0l?=&CKC})w1(T*j3v$cEVuKOJ-(i?z`CCF|*Yt0hBHvWZizRosf&m zpxclYbD16O8k4`b5p_JP_)4A0)Y8{Vz2*Bas)E`EsFk&JWXlx|4s>ZrL)^?*m}nGoy$+R zG@|5ObLtxfEY3(+lAuqGp4Hbock;U|T$EtwUv*9W#XoqXq2r&T*JWftwJP$st8aZ3 zGJdYn`VXUlc~fVlLuI9J%7#<}o}1D!aqQm@B29XJk8!Pb`pNMB;IOQ39xguWSMKId?_`rjoY^_QmP+*0-EI6MoIGWQkHEq!j#^bk>3#;kES)w-x zTN&$B8R2ZbYD01Uy(7TXUgG5lnIEkgiSe5c?gH)ne@w^rYv>xRKJ{ua6=TZ3JgE=@ zRIbE%W-&|AEF)6is=v9olN=Zi0YdLV`y@)I;UOpZj)Vl98}re0h)I%>iK7vMLP5Rm zJSrd+msJgtXo+$Fj62{vj!%-C>{0)C0Ei3n5NCja4sNUUHid9A{yWQN@~+naKV}v7 zmr?U@N_ep<2)LuR7_VPanlOm~{@JzAa=RR>3L{pr!-^AK*V_RNyb}-fgWM)@JajQo9Z~Tc^jo zRV=Ub2?Sn5*pvzn2~}+rCqf*}KV9F*)$!V2vv-~3G1b&lSi>F>S^Qc55SlVxK^S-V z3f4nAr+Vj>$6khqL_?w`f=(W3?vNA0X%x_ z`3h}n_~M?kLu%8PZ!G~n`3C6P1UX~-G1{}~`j>C(NQRz&Fbes`W@4Iv4l<{l|38k- z`=84H|KpWx5wb;gwn*8hLf%%gSDYw2PRQmQGb@{99((UioRg8wkuBrcvK_}bIOBUh z-`n}+`UB4Oe7&y6xIasWRQx`eNDpbstxA=7+Z%SGHD51_@i6L(`Te5&WBGAlK#W{B z&+WsG^dmdzfB!vxoI?JGm3*W3LoV{^f(}9rO40!Z%;rz+7m69D{*W3O$s60W@gB3f zyt;Yl9udr3u76Pbb${UHJQ!ZPdefnuuw%NXuF(7@=(SxiQfB@i9VZM`d1dazY-7rH z0kI+MG~$jol58pki*UUL{P%qK_7n}!N;OiBf#eh>On;^z62eD&e?PXF0t`E#rBxr? z1O5Nr+%ef|B)_z`vb(FZAxbUIKRaDh zB?V`2SJuA-@w|LPcYd>l7KQd>6*uUh$h|a7zxRnv^={8YCMEs4^ug0TR@ZA0Q|x7N zxKy@}C1%^+OJ4Rx*TQ5DbanREHe~~L_1>f6q4U>)Dqjpc6uSK$!kNKA>@XyX=+;4M zb_lf!c)Bk*r}Y1DG!@i*?)x~lbQTm)?q$7~%-tSYcoV@@N{{B-I4(>&Q;F#`|TSC`+#KL7r_R-UFTd z{dYrj@b3%p8|zb6$A`YfI22@k^EajK{$(sS{A_==JXPh9t-Zm=5C_ALCTcHsr7Ka( zqP1L>EYGXsKnu4H^A0we?*&AEYVp>)XCyOWnPVCDnDf8)g#lVSKL|doO`<0Ki^;-XpdgGXecr5MLCy+=?(!b_+)+vy6hue_dF>!2lKPh?t zBHG1(zpDou`klGbEcpW`nCj_M6L2W4vQe9vVDU@2qu@BY^S8{_6@6dQ^*>C>6m@mY z*Jd`RRkV-nKj>JC1_)SBh+<4Sg*=KZF+Wm4PfYE*v_-KlR;W{RVc1 zW%YWW=r*SNKzvQqenWhf1E{4NG+W0z#=t$uGsF(7 zJx7^yduh&HDw1?-e@JbmjNyBMj==bWQxZ7l24wx!{#+}6>@T19UJ*8rc!Wc*9*~sg zm2fdJbdzUx}L<1Md5h2@RPyTACu$78*k5B zK~4s%D!mySSrx3t46A?nw^E~;Auar*ptYGauYDP71d{2x=7H#)egj;vhwg-2!La-n zQ940J;IBmYuVmvEXbQjF4zP;H2!rS+;U6|lv-SdplKt=a-^~||f9h~$ZfSD-kE4!e z$kG$H#cS%uOrIPol)!h64g8yRKi2cK9VKvabuKX;NLr%Kx-b6%hMdF%J0@B7hA&yS<4S#O`*z{zPlTfLlB z=IjX?0a0g{c1whdQQOQV^2fVFLBoM=NHnMY z@%#xV5GlF5R0j}fk`*8N6Gy`x=<3zhBdk&vsVzTrLR8iI0<~`1&v4#KYbeHMg%}4; z7piLKx*ADsbg59(BTfCJc<=-l?Gd-LU#14`ig5D0QLM3nH(kq@$5YFhH(Y=wn9osp zmutna*n5di#ma^> z_EzNAjI4)yRP8ZwD@f2Wv^P3jf~mzsvj%tkM&I_wf=B4>CDDF8ZZ7kyww9b{1ulnh z@HXT5IL_wcsIM1mh3P#Ei&U;!c^DAhMGeqTs3V`-;iRh!yO;LVqvt`IVM*=ahBP;!e+n--^GR)Y?G{>K=0O`RC2@?32i|b~FZ2MpzMlV| zUcyL?oV&&lpNPqq4aSpv4o*=OB)U@HpQknG#Px&;>biS)AI^*)M|!NWw6`wP(RTQ? zthnfOf$q|IgWFDo@S6%iBYEyX;%|qlaQ3$7mzuyw;R)!6xF%F-=jp^oaxb#34h4TQ@CeV_(?1Rn3(!1wo8x-t1 zEJ@n~{BC)7?K&2eVY?=j)erRoz9S8)D^RW{U1Z!J4Xdc9DAx-^wcztMWpTDv-9iKX z;u3jvgPN&s?HQ-N_TDczg}3Y$uNJ3ma+YlBsa$tKYUl zqBsio-=(A#EsAcz_Cu5o{G*13~Uuo%IG80^Eh z18!UhU4Tm;7}gYA=#p)2zMY;)YGeL=`F0A#(aURPM7*~0X7%%j?&;iXz-DeD^>Smg zY24slE%rQ%_WaP<#Xb&z#`1Rs6B*IP=6Od#Rh3Arm@D_G+n-(AOAeuEJ8KVjiLqJN zYEi%Lk1;YCX3zuugx&idK|iQ1tW6PvWpBELJF?OfmoFWn!MAMrx>By>Ix;o|kWYm; zVh{z@>0Kmb_fvHMs=7ric>$;+VGk7m7|8 zykP%ZhQ_foRb>?=l=H>hXsgpl+F8Fvm)oFU3JVsoeErFJkub!^mm+QV_L;2HVan_8 zb4-i!QRKi^`>FB1^f)l1f|~BE*6?Rd-jU_k>B~!%mymj)eo=cZx?Fs&vVIv3l;ZB8 z^~bkmJsZULLC3|@;761E$cEc@p`H=qIARl_ryoGOW3A@N=>pnvP9m>?d#H-E?6p_WkQ5-nmqK*Jdx-nL8WQ~ANhWC!CO;Il`B zu5fR2E3K1U(k;E2@5R#NDX2#xv$yll$%mbO*!PNsB5E-&%+m`FizQA~Uw;gg5ysWB z{&yu!hF&TyeJ5)oOe{qT! zHoONJI~eF0h?CHEb~Ivp#(%hF-Kb*ZW@b^Ty&;|V!)6%d_u(BTM!YD**E)SJ&6w-B z;-D1Fko2^Xx9oGu(OTM9c-X>=13jav$-$08Q!cyh(n}$4_2JxQ!!Lo>zTCgQEhF}~?pE9k`9@CH6JyLvn4J$(z0u{g@HD?PYUknLT_h;RFXePCxnS)kK+ z_QXdY)u>SYYT8q8VdN=K2Lh}Xt}}}2a46A8j59`KS%A4E_6_An|LXqS85fzN>u;A@ z6*Xw~l#XpZ`?Zwlb@6L&{!L_F_n-9Ge>bWx1OU;N3Ou5wpqVE2Bl5!&!qPX~ zok5Pjw9W2h*RDX*n7cUi9zEZ>?=koSbfG7tQI=%9?F1_Hs8z7B0mldN9MlpChX>0E zgpcATg4Q!WFaazm2zN{vB8)(s-q-cnv28i|Ntwp(=+TG&T-qyI>OXxdF^zerK=b3- zN5IT3?%QBmCd*Wiea*AxlIehwL|7cQGCI9fem2>F>_ncl&~kb(diB9QLFf{+)AjAf zC^>;2bDwRZ0^KPRy0ZTmX8uyMb%t(3MA80uabej>WJ@;wiMDWpv8v?d^}j_uz3)z@ z#>E1uhTpWHD>xftU%nUiB8_rK-v@r3QrCJGEViwAV^dRQ-&FqdscNYlO>mg)RZ?!m zig-5076x$rV16=l`^GX>RU>O}D7KCTm9fr&Stg;)9pP9B#cz?8FrC>h>Ai}jF|2aC z=F+yUNY19Z)cEMzNqv*#v&!EhVS`|^|8D;AuiXWHIH3lEsUS&g6YD+f!E+DMY?OQ* zP=F)VoZ~Q48*<-35pt`}qlT(Er*bbt@G49GAZ@P7Anl<}$B-BO5dJH>`czvni{;3R zqS^xj9yU!aF-uXz+6Jr5M6*~tBqH2V0gz`fm!ZnSqU@_=6oFMhOW8z=tf+^7u`F31}7a^_*=vWuo)2bbYbs zGD31iOVcU#Dh(cyfUZ36KE|MSiB=43*1>)$&Fm?7`DCb}eE{-v?HIkq296Fk-ep#H zec`kFU?2w97?18t$jfY=PRfzy55)iAwcXGjlaMLb7;z`F40>tB z_6F;tOAhNf(06S;wJ>V#K2D?z6trzCXnDn)X85+z5t~QV+h$PXEwVh-S=KEG9M5QU z2@G?%+UeQ3QpcSww1}zv&@NuqH+kb-}g6+@|s+ouuXH12h;QOYb*d{#x+$ zRsmIw$oHnE!19mnMyKY#e_ubiR(xi$D*I3XW~P1qLc>J|SYvb&tS4YsL+Ghhgo$KR z-upOKF;QJvSzk>?&s@07`O-;?x`ppSZJAeuNbZz-o)mSSXr4Nqnmcpk0;RmD)kgR9 zAnWwhfPYbGHacNdI>~q%LkoG~Z@>x%@;}l)b)K(Mjd_!QLqSTng{UTp$Q@T%{ z`Fm8{ndN#t=;zTtT~8_sZ4B-p_k-8pI&QtS1Al9-NWNr@Vh(!t+$s^qEJ*Yjm$^Pva zIxF;OI=VjBjcV0f`ohVVby`Rx@$4(`fmUdIJqi~}0SckAI`%zzNYk8a|F$*z&H!rd z*VD9z>F=1l+QW&S<=EjJ@V@L*n0oNaNAu4J&B|e45&$z;4wZuGT4P7@@;tt6VCDO< z*%r~bQ>vhrx$xu&(m1uv)|4_VPy|)Yf#SHuHN21jG;SQf(_B8L;Y61ba_%F)o9h@) zTtUeVWGKm5ZMLJfj5&6w9$-9hqMrb{vpzXqeKbe8#{O(O;5o4Uh%)i?& z(A1-6bz+M1P;<(2sC{exvrMtEKa&yrDJc%`+vektEdRZ#U3)&q6nOh9+kFbok8C2q zyw6(^=MZMV4UVg(Xnk?lq9sLwq_MVaaQ1%XXq}L!$lQxk)oVi?)~w6OG;L}B+~Hi2 z+6JM<)8W1g8GpV9o!NbuU``DLzjuUa4;T229jD^9Rf`-CHAW0wiD+ml^A|}txir^* zYoiCUd9B5k7+^eQFoX7)lOXDZtxF%zy9;>lnNS~UGF)PoP(u5!W)|bmGvdLoZHqn> zVLEH4*0$_QH)^lyuh)F=GY~YKo*NsLO^q%D<;$rl8&zVa2$Aprujsmk2Af>2Qh#2| z?12u|YN_GW=il6eLdcD*NIP~KKmbIcc*m=89Y0V#Gl7V+#3I_kXIEZGLSLlRtxLe2 z;9ST$`W}$fU~T`V;*-pf)ZEn~f^nCxu8CoFV(5{!UmV>_kc~yhA5$Z(R~IWHb_d;n4_AP*INllYJkyi1K_n`_MYR9z0tcqlyg>4#oc2Iw z+UzW+q`4GZzw2&!`fWT1IsyP_PWj>rBsIGyKK(_QL)XIRMg$vos$w}?$hreHhW^iUuO zE0cd8$d`QBGKacE2YlLP5tvHs@;13Cd*wU~)kP5p=cm^)sNbDTcJB}%VzFBr{P|Y4 z?o4={q3LrAC(@cbGm-mkzw@ilTAX(~evAZA z$Gm#kVPo;N&~>@ z@&f*?!s_M0pLh2vSknIVBwhSE_(elIQM#|Sd~lGvW_Kp%MEf_B>a5&YRz+seF~~=# zv78zIkGFBB-0E)Nn^t!+9tE9oJPWBw$0_W>LGy77X!orB6*7ta`%|7*Ie*2J@|k(Y zWc5K$=s;^G^oh|Yr;Hcr_|b$ynXqeF|7exk;f6hGz{Sz~QFzaxErp94pWMqcx%Bo< z4&(UQq5x-?cy+2v01Q|uyMqw1c1FCKsH)}4Y%8VK%djw07$7J9ed(MQ!)QMDLn@04 z_XRa<_S7zJ_4aW`zP!~e;=x=`ppkBl`e);&U3TB{V!EZ>4p&KNC9GRv(}mW}av~$l z)vy7C)8j0c@W1<8_UC{rI008$QHj+Wsi^E+=6nGKaL4V( zog=T(G?h=T7>2nb14{$$ zz_Qv{sJVR_%(xt4aUn+Mz6IYz{DUR2CJ8|y$D^Rqp8ye=7*NIx<}tIiF*B;>bOVBd zQjXxO6Dcc#W)E|}JY;E`7rPhpubsSZoDnVk_% zX%_f3!^6{)bp_+H4^Jy`|9KV!M?ZP=w46A>@2!i3>1P#>3B~qJy(vjEx+{%dTV>9O{%rAVEL?G$t+3KpqvnXAF=wME2Qx0&Duv;(FW{tmr%}cTm)Bux z$`ezVQw?Rs#D`PEO{#W|onPo*Esh@QC)Zz2`hNQpC2Jme(UQNH`tI4n0|oCXFyCA64MI1 zKCfhsWLOdyMNBn{Q|H2`7=uOXkD z|Fwsnm)|~4=q?>KE#8oDSoa@RDu)92!gMz?ojxnLcXS^9+S|d`qn7eXSlqrp>36j8 zmfz0LTSe_PL`?Iu?aZWIay=6BsCjLTYwClFsc#%|@Aty8M%waji0%e(heUr#MSIU1E&0 zrX7FO2={T9ktF|U-Miu&;~f$$xHOo2${U4rnLi5okyCWqq2E%V;U-0=jd1!Yr9D&m z#jlR8{bYzW^qM1PGw5&lyW(iNQs+YG*$ROr=$QjVD<{eGqj1*CpZj0^U!j@UqPvBf zLD?@|09T@Z=kIQnFw76`x#&K01l^#ndlGW->kWHm`|loUWS?4Cp?*u|p2Yf>+u)~< z?>K;uYns-!_&kBb1`#V!l8q05JCA2ya+NH&eb!G4Jl4{3f1|x6A3*SpL&z{yCAmC# zH5?v?sX4Cv*ybcEqpCJM&TU@h>7JIv-C{C^IOF66dcN(l{76jVaYhsH?>0 z#ebQ*Oq3vYkl%DEUZ+c%;9dGJM* zU##j)ctEZR{JI|cI6)F`y;LnFprHJj0KswqvFU^^0#^7iuh7oePl;1Slew-$jW&0h zHt)DCHB9lPmQH%zUwxR@=}05UQTjhVjwPqnL(e-G)r?){Cmbn_9PHsTK2dFvi@jsw z3ETl4>tQ}!u{Hd{@_oSn8vT7$I#)`s+w)olB}?_E07=xpx7fVx!Lx&0TX>W_x4hsk#@tRf zA^g1d`7HdvVjt)dbZz}3dzlY5(#mRO*uP_OBP8G~bgcAdf%H<(rHTTwD}*2u*W1BA zp!+9FoFT7CzysuUs}4sl(~4}6U^ubEgGGCd2l+XJN{do<|AOEiVUrMN=-jH)&lMl^dp20EUxa~b~~Q0QqMKEv+K)3C~uZN_G#nD zYDzr2{^Y1)k!3ZYHd8#HxBB_|r-zKtjwc8db*G3=Id2`gIW^?2(DnW-F0+-)w|gD0 za{XIS@;&hjL@3O(fTS$J{=|O*g}Yr*b#CSo)&4$24#WQPu7F(ZmYnwNEv8h*R8#^D zKuY4hc5aqEDe;oG_Yldz;%c~h*(64o)!0cVT!0|EJ20{aQlZq8b@IZVUOz^Yse(e8 zioylZPB}*^Xen+pfaVa*{r1e<9=$GXdr0Nofg;C{cJh?+p8acJ9TN=am+WD z$%$6w{|LovUY?t0{8X`+mcW>8TjTvKcH%=ZeU|mGtjW)6HnLU0UX*h+b zb(R}>@ucwscE2d*qqMvljUZsXhzInDImvuY;91EMrTqO~Xv39xQCFDKW?@;a=8IN= zu-!tsgkbMRvdInhuAIS>3Y1=jli#nao*%XvWs$M0Tn^2jmS@kF;2&pAy0c{&xv$Nq z>8~Gn;h_rv@-O$w!QZ)eMyAq9nOU!VrX9TSARX>lu9#4@;t=yNlX%5t61{-X@K(Xg zpGY+MQr7_$V zi&S{z(Yi%Vl6D^>OH(T}BOs}FeXEkPbRUK_ZxFG?4y7v)w`jZnhRiOw9wGv2^#<)Z zgXLF&!^a&y8@m#-W`UM|@4nNaORFMprtA{xF5ODWU4{!q;^_t^H->2%N2-Qvs+xwY zn%YDb>3E2xgU7FbFA8sStC~oiCCU#h@6)3s4kU4+kh-O{G={`pmDhp-9JxHpP4Mp+m*1X~PjmXG6z^f{`QFVRZ3b!2 zqiM{VqL^&-d@xu3md`t_TO#*T6S*NFk={H$+7nsuV&N(9hFhYtBD|}G5J@49?~#j0 z#y?)rEQ}$j?c!m!YnSosXvsk6k-Qbyp4Vcha2X0H*jcddu8o6I!^z;z#Gai_H#rY% z=t=AiBW9issrSE<&Iz@l@9204KuJslMvm)D#yqPkg{k+&K>hnNv5`B=Wz;jo_BOP3 z?M7LJ4u1mlMSClN?-hSm23*jYS@!<7hYZDB2*SFeL1D-%*J)jK^>Gsw_Q~2cJ(GTF z1d+f40xP(W*?@9Cj4jErg$G&t!+fpKVg)YqDXtj*i8=qjR2lT{f}y1_%W-3l$C_MgV5~v z<-eo7WZ$Bp$u}u8G;TPlVBn|hjE#*KgSz=zRZ!Nnwa-I56;{5LB5JjOpE|Kj0IH;d z-RSeehfhA-m2JFvE6)haE;Rd>-@o;)qWr_f=tG04@=euOCcn0M^|NCH3Ux6Sr&~4O zR&p^KA%?fn4@v+ZbZk&`MNns>|K8gKzv9}?jN?yGjCFm96#Skv$^8u$ttbBOXh*=t zA}y+Dw3kt-{Hu0anm|DKohu&Lk>v8)&ovqM+MDZ7*mZO(RruQaZO%7GYuMj6an+OC zF5any59E1IyHs|!Ie$%EWu=C(4@YafFVNHkAab6LIX$t>0Q@uf^CAD^(>A}7=mEKV z-37^iq=$1*r0RQyTWc>kB1aiM+JHN~jbGE< zIlt#8A;)AtcXR!lu-Qu#I_G%8Y~0MP)6x^NXsign(_g0U4riL~o6OEU_hkKu>fCj< zGND8=P8EIMD9>k1XFrUfyy|~mtD~r2&O=%8(+;8bnex%&DHmnN_NOtuW9`nvqS48k zo_XI#Y?3J4Pxuq7M!%$_np{b*-fq176<7axBXroyg<)|#*!9FlXWsPwcu3Ko@X@zl zjw22%B-^7BEB|ce%{EskTABCeIWee5mcPrKc_h_DGu|V7NH_^{-HAkw`#-%CVPm7O zUIJ>cHesb9X@+Y$3cfb53%vv2|6mk*U}lYgNv6C}8+^qnZZ%0_>uuy~WbNHi_fggm zJe&Ei@3>y!I(1d2OIv|M5A!4OD7EDi(`K?gfJ{x?!a<}OVx6jY(lg_-2fjQ-8v zV(*G=PN8f`R(j(P0DhP-o_HZD)5m>CDBgBOgI$c#4gXb0c?9`|bJbSJ+1u^RLr!D> z`fhe?%F!Wt*7t!-@Wrl`AbKJH^r#pb?|6;WW6Xz)VsGgHxD)w8<1^u9%3lLK8TXJN zpHyEy_0c>f)#}etN#hDJ9MVK0zfZ-gI|TL%N7eM#I{JcYfs@5LZ9&CHFq*6KV<2Zi_ZhayLnm67J* zTkxAw!dnYc@D)$1l;}UqTWhaWYE!4B;YshYj>jb4`5$tu%u7T`JGQ=}9+Cb$TCdai zII?_4NVa}2@Q)dch6|&0mb?uyJbRJ9#F532Z@-uEuhg)_9gmHXn8CN10 zE@h@a{I@nIR$u04sj+W}BpaUdnXo-;1Ty2I)4Re-xwlmjd>{S{FDG$!va!_iw3Kc| z;6xR?v>*6xzjx-olA9;(I;+=7N*v*?KU#!3ZwE47b3E|V$@-(hTq)CbipJyU^|7qU6p(kD@(-#zVpM}sFq?{mn z68?8&xqNT9@{-XI4Rl`m(O%4b!6b|{V)nMW8Uy9PFSL=EUBW^wE}}NDddhn*K3*ei z>TVP>iZ|kN)9CFP{z>!wfX7yW@`aAhiN}nmo#7y)nz1}f1-^WU!;Bx)nf1KG1eE$?H2c}0{;3-L+X4DQEF48=!AeVr! zS0^1kr&lh5HF0CI=N#uj?KF!VLgGg^hI|65Do^}FP`J3G?7+V}?9Wd9-Kq;esgOUu zHw1FHHKLf035$6i@0B1=7V6+Kcgg4(9tgjG-y5mYAd;yn^Fk)#al}i%Va{({Mf_cm zB@DRz{>^|nNqJ2Gd$CurzOp*CK&?(H|76zlwsbtky>=szhhA^B-UNexUDMeK?O3S-n>2=r9$N0<0wlKb6M-~m(sb5sFF<8OKotbD_j-()xV*bE%HV|~@l+MpY^2X0Y&tS=u<+N0{|C zliN3_ceWs-!QSK74WHG4oG-qW!J<`h+Lxehy8+%&?yf0b7^e5f#HMy*eFAl3n%DW| zzRlW1$CuZu+xSa#WJDIX)YAlMd(@3{lVlL<+PG;SdgN^OF@tSTj1452yXm5I3=jIA zv)+3E2?9!$^A5$dT!aA`2_TFn)_3dX^gPH53*{#z2G8Nm=vEusSFl>t9}#$M!jh@g zRemm{T5rZ5Z-D?Rz#ntH^3-Epqg8*uw$p>?9#ET+ss;UzTa|JmpW#(~o`FvCMAgp_luT$l95>${7t#RL~(e@J47B??+86k`tZv?$&5Ft|+%B zG-sa|zgc7hOR!Te^5Pd=)R^@~gL`zCORTp$)$$jWTxZP~0_Gal;SM$$kP!F{H*<5Tvq&o^Pi)lqK&)%CNBP!xkEGvSD z)^ltLHc+$8h440GBJ?H_llmqXd!J<@0?_Cpcn(~F>l|Io>>pb%IdM`T6?JpTY%B=t zEbl#GHysQcn}_Gm*Apz*pc?gDmn@R=yvr!7293YC>f&MOs|+X+g=Qd(!*a^~!tKWc zlI3x-|5|@!(ZXsUbaPE{Wd4wRmYu_{g+Rp;My{B@lv1NQ0@rR#0-YAiY{75DYoGN9 zA|nC11bFl5?6qQqcoP%4Rd+BLXM8Zrpag=)(d-fM2At8m9jkt}gqzaGYka z&Z0TW{izWCBqOlMY|U_R=lKUP=43{k#ZII^x->^`7ga1#J`{iSAxukcD>O8-!Ke5{ z9xO&=WIWr;mF~d6HWokQw?u_JeMkL`wwd53qHuI0@G7prn#Vp+qk{#V-LzUz#Le`6 zqAAXM)2_O7vV+c0|8|$&ct5YO1bV~UuE9RxwupkGG@2IE(5+gu`g^|*QT8^dLypg_ z@E?zI$$bcgQ(52bSZOY~m0j3E2kap+=@M2L+5dPB&z3xCYv%(Jq`jW}vH!it2NA3X zk5JmAXDW`()Jb=-H|9Uv{b3lk`yWBXX^U+EORkfns`zp*IrEP!t>C7sk(xK}*mIYD z=kgD$Y`EI?uk(g04LnaaljBfreaYt|-RnjfLG@$>yj@1ojt*vTIB9hEMEbQB(SN6Q zoD{W$$`xGf^`|UyLZtL#%L};o?`PUR9 zbDGIl7IXxT5iE3~qFrzN^+dYgOgfwfe}Qn|Xvg6$E!Mj;L9Y!#4F$8to?XCup6}y7 z?S1o&e)QYB1ao9VjKREP96y9E1KouU5n_ooWv=(LJGv}bNsRI`ckhw?J4u$WPWAbA z_(;+qDsIoeZO%?#R{Oiy^{!yPr5W|`Qvr=xfxNHfhwq9=Dx1bJRdj`)IP`zmKrYUU zHdOzpv~19UPUi{p(1FE9Tci2a+jbsg4rmb`rl@S{&)e?j5 z53|XK?}v(ti$@_0uU2rf3+@UE5*N)siSc~w3KB^Ax4Q}Wb)GH&>A5F@F8eQ`u2VYR zVWC$_eO~F!Sn$(gjIULDQ&3Yxo|z{yh_zJ!5q8_CPo$-_F>Sv-S6w9D2QJ-BISG9`qCpR7rCaexK-6jBBqV z@z%&xWdr;3>)&{83Dw+CAtc}EleN&%Em<&abrHtoPR*`ic{WB#ZXsRy=K~1}ZY)r! zt&sMWs>CYal%qdBmzLTEtFE|unKci1ckBm`buVWhi#=zu)kcjprHX@G{o2#1FAaXI=kKOe1RlD(5)wS=B%q6-ww$ zsXaj~u+2dS+u97EJ7v_Xbk{k?V%{o$-5EJ0^{}I_-Gmln3`=t~{iPEO*A2q!bCAgq zhLUK@zrFFP#2xbzfsPv;#>)6UKp|G+iB@0BI6!^rNk0?@H@X`~cB7WaaL3F7CQL7% znc!xO#I8N{JE$nP*rk83?Kg6_NHabB@A|aDCT_5quF8aO{Nq zA}8h$Zt#pK<~1Jkt#m0D>9^ewY(TnUxe2Pu|LmQE;(jywrJRe4nyyZGV$TFXA9IzT zkNGX1?gc|vKdSevpq%#bc^lShQs3~+nvtn+*6JJEFv7LWRwN+v-is=DO0NE-s~&Ki!=(<%OiXQQKXZDUBrbhd^uH&&YoujBMfK8O zqWrsVXFoTWR12@OM++L)TTgJtfj*XBTJGT9E4>ieF+8$_Y&x6l`O|tye4|@$c|2nK z2NebKAxbK_&W}1Y7BYmb$Bi$~sF0uL6v{npv@c4?mtU>x?1lXFbJ3io;{q_l#EB;( z5m@;Zs$$}7o`D*p+W=e_gYb<2v=5xvk8}tqe6akzC~8iZ@$P-?!c)IrBjM>4gGJ&7 zK2yq7UJ|&_#vrntto(LL@dR&&JxWRt_4(1)foTdQXS1KnmaN!E$NK20vI7G>9y5W=qmO3sqJwHCOHg_-ZS%-lpYq zua;Z?Z$SD?e}rV2RqJk(GA>Fx0>3xp>-$-rl6seFRkvPd>rl5xW$>DAlVWf$uftoE zmZ|Af-j>YZ*3{jW5+%*hGjr9JYZ2b*|MKILv-}#;m@Ei*byXkLw#T-FV>d$fEhhR6 zAREIHnm-362Ir3XQSYQ3c!9)#gI11vBBAg12L;bbX+;|K!{|)tuRFrHWie9l==)CL zHBZ)#;;%mIu(N?uub*B8WjvT0>qNPxvc(>fZ;bEY2X2~``td9)lC%p^5(X&z9j5#G zKi;O1MTTFi9GTdxDE+I&JD-QTbDL zX1VG-QznnQMY&Cpq;FK}h1(Cm?k+n7NIzHTY$1Xlk4IdC;62N<7xhe6s2~4-L z*W1-I)-yNu6dkGgmeHjCOXnqz~Kd6`Dvwfy03Ptta;_A7Z-j^)R3%@>mq(4+bI-=!k5| z^|@8c|GgNVu(CPWiAK<<=>NdC#92rNIFN@`@9bp%_QYBs;Nu#Pr_az1Zvrb3-$2yx z+>ILx$R=nW+K9Jrgamd0A-M!8{MileOJKl5=#A%T?|tJtR}ROB0!jm@TUR~rC^Psr z{IyWcsLK}A?aN!?Ed-I7>+lkEx@d9MUuFr`76eRls?fv3K^GI(SVO?MBUB59d?K-M z08lt>>4}xc-T!oDOMXG|)HUZCx17xj8w{}9a$M1z5`I zsAU_Hbk(@X~Vwq$iK+r zuKvK}#mcZ6u*phWGQP=pgA@V~1NU|aR5vLNV_F9ag{>D7G}fEg3{8@e<*MVS`uX}l z4&+?`cWD$sG{q1%Y*}u`b6@G26 zd8A8()3p&}MRoF8NvY55(wTG&(%vy3YU4jj5&m53ut_~tG4qG-j|cB8#!N*4IzLfB zE^HA(6@K44BEK-ua(QLOGDW#?;AzT*y|c1pbfE749UBt4AUhnGk*bJHxpJIb+{XW? z1%%LD=)$=XfS}EdKZD{B_y_>OhcE}Yl${Le_-9(}WANSlV8f`yK@-ZL?gWof(A_pR zoojt05z0l9I+8t~LrurQEb}1q`#3u*t7?}7ao?KT$b8lr%s{`&_WN^pDt-)r!S4A>zh%&Zols;Xp}*EAtRL{BLHrd!!3Kr z9&VOmH7<+c0`B^Uj63kkwRn*41feNn^60fsxnfW?Vl}KUn)UVggEpUN=h#{3#uMA& z3?ZFZOKo=uF!$rLOr0AK{;5upYB4yv`muD+=rDFK8dUbps=v$d+(^B3tg4|R^@C}t z;yb8`Nfzdo_cVBf6WH(YUQ;rr-lZ!DXcej*fDc-@hCL#vySHA!CY-creIa*LC|^@U zZ{M1_(To=B#fw>{#F!Qtf$!~IHXl$ZnXE#?jT2nfqP2=FXrz>KyLJKzQ!h2zfbE@ zY$91EU+N`6E=PGo6>{Z zjSNs-^XsY-2kplg$_L(9gi_ELML{>v#$8#^0f)?7&2si2@ZXH_OS?-9@Dfvdyg#); z=$X5mL4xA+jzo6~yF!kULfFOYMDDn&1spDOdy0kFj-0Qx7bYxWc(5&+zW0c+S}|g= zQHjt){1n~CcC6j_<5jqW&T^AEJrB8KSuhu5u&$5mucBBYt-g)1Kp-Trc%~iJ?Y(i?T7Ul!PpEq=jD_-xmI7 zetui{Y4`oJ@FQoh4t_zPQ58Jkv-;H-y1gek>$86}IwW=#??=dw3a)`A!Avp3*YKq> z8C&?a?LvsZ?iZLUOA5;apTE2695&Kr#7C)NsuI8#i)-#*z)#M|rGm25+*4A}%%4%8 zo6#!SHfjAiLjq-wv~jC1uQ4lO{sV4q-xOZr=j-^04=?a-j7iypT>0mawN!h|nTV2( zn+e}sreKU+H~hZL=fYpZIC%bC`|~Bfxc@KK#|EGGBehV39}8bPaqE8Sxt_ljgoR%V zf1ck<5|zC^=`Yr2alr9=mBhQ2@ocKtvDW|o{KQWhu(bYvlQ#<1uT;f|QLlz#KokcL zI4&I;E$Mo0-%h)oU&E>C$%WZvlEab5P! zZ$JLp-}uH)|IDv``76JkP@|7bTI^6{rIz<`O*j}k_oh?3k71iTOm32MzXfhl>#;>m z*B4&2rP;1#f6!~Dvdumd&P~l9fyofE2iQ9U-@3(X_#Nb>%X+3}`{5!=jv4CJ;qD+W zQ4`Twjt`H#nxa8bKZghzmE+01g0@i?90HPAiMAbubHoxac~lh!0Q7zv-$@&!nRtu; zpy)?SNX}0OtZ8Z{#pr6+dXoi_ney>kbA7vX>p!;CUz&x0)OiED-r#)<8%BE|4;u#^Qxd;?U-Hf_v?86>~2thzuH}{{ongj6viDt z!Qh?m%kJM-w|m8}q%zU^e*1QDnMFX_qVW6$0h5s|gI;fNc)$=J4E&eCl;Vs1&-fEB z;?JmBH1tv+)|d!+Jq(S>mV8=u_3f%E_Y28prNxzbnLl0NVWs&~)@AX&s;sWGtA+dV zu0h(U=5LA`jF;A_eUK^ywq0BBg~u0u^teZPSOoi$W_~P+8ew61g@?PxG52Z)RvbHS z@8X2n+-hL#ZOPcAhloxhC##`DD_<*{YZ zCLi=+4no07=Zle=8r4pnS88&c*E&>y3K5M==bVv8G(@}W<4%4V84#3D%kanxx(^dS zC%*ZPpSI$so8sqfe~xUazOM`C3xq%&1X>eK5TY8`|119E{PLauP(9aH{$nFeMpv?s z2pHuG^?i-<5x=HS#J{eEUxSZ-wej!Y@2F#t76MI~fEj^HH^4nFGs>CD64hwT+4gt9 zuZ3UVD}H?j{95=u44n(VU1G0}Z-XBTKNfzUkIRt*&kx#azAb#D*ImvG6TUkg+YO($gUL}(m6v(^(LeGIiX2j|OleU? z(R8knX>f}CB&mI=n?j#cSt6ZwH_;}22)C+_eh(EVO>HB!)G7jQVl6s1`E$ps;#XX5 zu1R5x8esgWIjh^m+Q$!&_^n3bO9V7(5^&H~d(ycEzZO3E9ZOBTojTX^8}CO@p62r# zX|Xb@37hASPN+sHY6i3O+4{`=tFM~kZIx?48t#)V`B}O5k95jOsepk!@>u0>_*6{1 zEpRa9v`7xW-}nE*FVokgbIQgp(Att~@X2kL(I4`W;^ky`DK?U>^kH|#Ppc)J=v5(F zmINlKR;2eC(!fOBb@{3K;JZu8hb-!n`4tw%NBpzdXd`Q3gYV#o`m_0un2}==XQMQ-1-++Pe}hvz z?{}J=rAILG?dF@aCIAL2@2B)j?T;fn7vdI(5iW}5Ad1;0;dB4T@;HT{S-?(dJ%24} zikdP52p}bYOx}NOt%H$Vv*!Eq^|_AV|PY-%x|~BjUUmrQkbev&2k@H zo`^4z)V`TJCP;hQ9pX9iJx$DZ8HA&1oM?T?PQVolt0#cf6h3yEQ}YY`gy8p8RaTLS{U0rbg8lDn|F>!utfXk3kv?w9#pZZVH8yD^ z;0#PZkkI?Z?j(0?qHpyqu6w2Bazn@UrDfqdd(QB7NV+PY((}U)-~Ij{O0)1&*&IS!4@`Bvd;nguwk%>2l>`3%g6OP)xta&+0o5 z^iUB0u|Mtk{?s%ClbQSXOML0S1odl)Uu*vlpSDG+Ten5~pYdn*r{Cjet^Jv6|F_U? zB~><>0YuiJJop?Rk6c31&Fm@|48Ti89vtkmH65CJHL80KG%-V_x$R!;Kx0GyFdTr4-)s<`v>`?)`s&x$;{GkalO6e%#@&=eNWUI25ycMO~Z2Go6mB`1$cHf3~`; zEaGwH(;xnchny$_5rYaZ&49Qpg1Tc@g+r;T<4-%oV$9@fb(rM(l;l0V?=QxiVSzEE z{H?Q&n)P4mQq<2e*1}@klZyhA_x#z&w(W{S_kjjq`HzcRLC3MWaZIZ4{A zf9>^m;4A;^=NG>CQrw^7Pe5y!InQnVsgKk;cb37qb(X4b>tDX6l@KO^d3dcOpQ^$| zhnwR0USp=UTnp1*dH%ktH9o&z8ASn~`8i!rk!j!X+dMfIKC|_qz#Mv0nk>dRMJs%} z<7+$+`ADICgLJOZ1T5OVQKf7tF=AFZs;nYTqLK?=aE&MDx;3$0 z%>6?(#e^)lypz@}baY1E_;!H;pL)MVgI5o?X2|2;1$%;-itzEu#}Ntm?JC`mM)iDV zqE&nUm++|oU9r3lO7$tyC)`eH#$-{k`vnD0O$taB?T4vBtN%9z%%V;~x8V;JTie8& zgVg9wo1#itWZB0b=HvI5+%yv;QA~Pc|HdQhGV2NDxV#EA-LiCw^tl-C7UT~v86xc; z84y$Q4b+F;Kiv&>@RDxd*Hk5@Pg^ojD>2bo`J43tKF-c86yZC5;t@ojn#;Ft@kMV% zAIk?>d#(<4JF#N_)QZn=Lc#oy;+rgypjaI3vt60BoIx$n=W5YDx+SEUXJl}8sm489 zrUuMcxJxR{55D;^=W8u@^t+me7@mG0!iy$i@wiYQ#lyV9uBsCiW`FU|{5aJ(?0@zD z{0C({$XAu;J6Yd;pWk`P(~dS}m;3R|ghu0#q;su*$yvvia6c5V9HiEi*A4>#<1-{e z@%+ukh3#TOQqTkHw;6f5qw5c*ehQ@9el|Lbx_ZnpX)DJA4p!YMQj_jkNr#X*zt;Xi z;l$(MeBpJK0yVbwC+XD7a%pBrYP^}DS*tP)^KiBz7e486%QTH18$u>wA zz7770pDX|Q9Qg98QT%EAJ|EensL3Bnvv$lyC*Hrv4`Z2e0N<=$#qZNPsO?wkSW|o+ z`#*)h6}V|TYTW;a@thH<;Cxd0QuS=EN*VCi`R+1fS_xmeove(Y#WxQk^Z%YY>>MmqVCSz1_B!1>Q5&a1IyKJw`(>1XxolTn$RmnGkrv~s zwwUS4Cj$jdg$JgA{OZ*sGjPhIJyRvTDxKI3=8Hg?XmA|dtF0YBb|EVU6KRPKeZc#akRAWhNYkh3+ z*Wckvwvu$$$_IZ`{AgN`W2uOpf}hp#O%CMG&e0e6w+ovR=R3Zwct;KXnk(L@w$lfS zHcPeaQke-1eysZeP-zta#|9Cc#oZkEv9w(iN7 zt#WoCVe?{=};CCg4+MWyb9tO9CZ?F0jPzLcE_Xkleb{E~J~ z2a|m=glT!1sZ-D+rhhM9pTn-y{lU_#9aU&of-mYSKN{Otl9n_>l+uC8VTTXmbrpw* z-4go;&rc^BRM50N%S7=dhn#mXg-0|b-L-#UPL2{sm}Mu^9@w9$-J&9%@M)x+?3Y^h z^`&_nm3^giJAD3J)}B}T>HVXLaS{HU_&H&qs@tOFZdV41+fixG^@mqoUxJA*w(SjH zoI%*pIV79l{#-1S8}&wsroN0ega+=rFeZGDzQhC+35Xw~ z^&rDE;UkM!>^UV0DC+k21+qk%_CM_yF&UxRZ3$aa6H?z7Yebl{gRNtdx9pylhXNbj zGtbOAeq~*b*JkHPN3HYmcG+v~uB!5^f93!D8&`T}R{ym{bEvT3W#9mL*p+s~9MS&K zg?)Mq+Mm7$owc|7`?3m%m>@pP_dD82vR|{|ug&*6_?7aO()Vj9Vvw)yj6chXY4#^x zmorWx?>CI+X@6qE%QZ`%Du)w4r2k-K^G34;>@pGM+f}IbtQ^D%2)TI%bwg-qiqON! zAk&T7NXRoPz(NWjs>H|e>cUK>Cd)LRW z`i%F1>i%YZudC!C*~ck0vGoI2d`ss2MwWEe#~;0a%1oG1QCH5Z|6@_ttbgvo1#s)Q z|EJEL($%($KNsapoiAf;YqS6FgAt-J&G%bu#wE|+sX`jRFTd@7*ZMOnpvz2Oic3AP z`M&zstv9~k?+$-`-Shkl--_Sw!ncKQG2$da6I#>yzT%Ij_pYLO*g!q%G z`yLv`!bg`-Tu$J-aszC&7x=aE6R0Ro5r*rNwVuD$^CP|m*Wc3pCB85GRphaaKhIyc z=YJ7Dzfbtpj|?X7;M=SF=Z?R{m+X!G`3gRC&^{4AT>oo(0G*RO=lCe7%#_t6(*o!W3%4?9zDHQSu;ocub23(AhYqIMa8Ng z(qvXSkv|iG%8GC)RE1H7lNxw6RufC}FU0q$YbmP)r=b?U6aRL9X?9GIwVKOZ7Why| z&wcHr2O-+p}EOu~7VO%xc*YRsllN4H}et&+G z-;$^pPPu+}uU%?NbL7-2-pTR^qFb)f?rdG+^$h1BbCtoE)ETcVJAEWr!3;g8s zm+kjk+MgTWbs--WzfoY%Dbvu!vd`I$cDI^wX@5$WP0t_C*5B9qyK1=3d3<==O)Zph zX(phZ(n*Wcz}dIFnb2y-H|H=^-6XdVgQ7BxGvx8hf=eF5wBNERbL$0FVdm>zz;9=?F9P;qHB1qZfvp z*2}xi7)Ggj+4jre&vD30b8Z&ZkOx?Jykai ztW`fhe6}V|T0egiNOZ*^>6_C8y&dSXiFefhdXNI;+@FfWj2@_7f%j>PXf;&{FN?J0 zEIzIjoLH7Ogl?>q?~ZLWPZNwLxX#&Tkx&WK#J=nKTVWyO{ipr!R^?p(q9_p@uY7dY zhrL-^e>k0`l*l63NnV|*DnH<^+ihOhp?&T=KQ0@FUBXq;p(__~y-5gS)fgz|;M>A) z&9y$8hu!LXDjxV$4d`@R_zYpW_J8^GnS&F8bXm=Ls1fSJpKp7YfGf(qn~vWROkhRl zpbkA9dvne#DJIC_4&Hl~QTl$J;o9jH|e+((&W5n7Yq=4n-vfFqTt!;tZBN|$Mgkbv^XJlUq<2D_Cva) z){O!hG*(s@p~vE2B$f$fC{TD;`*!{`Ci$U*E5+4+^RNDvd-&s>1!sd_dskSxIshd{TjrbaF6t~-`_sl( zxw`C6b^zkDhO(sC%xjyv|!_Av87L-|b-c$V8Iv+Le7xR5NW{UX?`lL;oTsyyV4F-1F+t1I_-A(7C z%{`9a*X{fD*T3Jl@awbS*BkL+eZM{?>*l>a*~hIS0OCWjqt==d!q@plrdvRst(cz( z-v)o#Px1M)OkQ6XIpo0S`qWw<_;?MzEqt@*&!3<4{KgjQtWVX7pEmI`U#yQWd?d}+ zm}@P3%?G$MzYL^kVtB*%!EKxOT{mxJB9p)!AFT5!8xuU@?!8KkCVFy#K%$ng+YXXb26!g2*{Soem zx=*;+d9~$8C2^&>!B?MOH=2Ne8}Q{a)lbFrLsK@C026+vBTOEB`0%DTAbi`_eJ&5H zk{q>BVhr77vFg|J^A+QN&GY-L59%BXU->^w2@4u9Fb7!c?=_6e*%#|z)D$4^o2AG= z;sw?CX%cLcT-@!m@q5R@Z=!Tdi%|{{a*`%N0x=OGP~G@++tkjdLOAZf!HXzl4kOn7 z35v<%Pq5>t_}qLeRv5sRh0Xah{GQ^iXtl)N|Ka*HHW$HRwMxDfc}K#xd487w*|YU& z;pl}gE(`Ga4<*W1%FHN0PwoDGZAfWzv zH}Ku9@+s}-|N3wL^q>8;|L&jvJ750lZ-@a@CcO*{F&5SGQFbWI*e7TElgd^-{p8*6 zDX+S!q_d+kZK&_PR9aN^h^&n0q4~&H@q6?O_|3-)8c2-RhUV95-ymAehhnp>osncs zdx^Kwu2@%7E2>6upBe-jAY;WHGkV?(>VoDsCjR)}r^{4kjbC(KHD0Xn{EO1Mj6vuG z2d71IkcY_D#f$16keH=sj}0t_apf&}^s>B2ZkM(H5`AlYd-T$`qVxy-$3H)#@@^q>`xr7Y#Q?UjkV_9A#hV|m4~k7H|sn#tGo7R`|Q9u zr~)^DD6_`b$-x;=Fa;uQ;oJBUveN^dh$bx+IuDfF4|YK%-{|sfD9r;01tidooSo6X ztrz+pk4O3v*Vn|igJJ7o_A$scG1Rft-oe%86RqV zj|*z(_`V|Kgotr%8M#UM53X(V{f1JKx@_&wX``S9PYs$t{^|}}MzVPKhY&G&_ zWY}TL`&N3+~NuCNorA)rxEa*OQm;<~;+T@_KV9&W^Gvsh zc=v^4=It!)sp;WKV|#F04zP8bgCiB)nM;wbtjd zdL94S{(lvJ_uF6sR6QO4;`^$>SAJD8-{yD}ySN#?@a2nY3*Q+3dA@^SF!_Uqd*nZ8 z3xDO$D}Ug0ttZT$aetX68ua)e?=MeDx&s&Vu+VGaL}x-Fp?PF6?4g(Vv#K^aozxod z*{C+(a>k#E8K(yGM|lwQXY;vav8Eib^M7xcYM3!`?z(eO(1rS)TCa=_l@s^ zf7R>wGTwXs{Gd%!GA*m_>*K`lI!NOemHYdtg6mIyzI3?U+yffNo^aGH-tPuql|bMd z@H^uRx+2g$5BSCVe>x@Ex|FU~UT^ z>qC6w=!ULz27L<|gfG`_kTs5?x2%PP4>8mnS*kT>|2>XvPA zK@Cb0_WR9Le6vr$_c!pDk1F-%K1WQ%|=HL@nMhr?T^kc|Lt#l z=__B8FL>z!T7b@;Jn#L*`;nomakgt!LU|kcAG?@oCrfr$m9yd_mAP1AbX9{1pD|a%f~)>HAH}7q@C)nDw}_L!1EhtpZ9danN_!<_Pk&$%0#M!vvb`(IaD%0R9CZ=Lbe;n7xn zXfs&FA78>e?7}nZIvt>tA$2t^+zV3S{j^ENJ&?RZz9D7H;hI-I*Nt1#0#xxNxf=>A z?Oy6xd5&jQSzKk|byayh+@!T8wPAVDR+@eMVJ62~XmZ%mmFr$r^y2Y_?}P7yk9h^X zUo=WNi~3r8pr*svvC1iIn*VaMY@I|mO{VMX=J)Fyc-C(&F~dH;J|BKCzO}jZGkkvy zpJ(NBe5~6Gd{t6{@vVjlB9%SPxT@6#?ueW!&VL8&uu9wncrUOB zlREemtXlu&TR+PD$09vLnoFF~>FGzR-7EG&v)R>W9i4O3^3pA+eSw(l^#b2#y3E)2 z%S_{Ke7GYQT0CTri=0?>(@T4fZ_I7_N*khWlh?HIr;ax77u9}<%dx6 ziE`~v+?14mI@JhKmBP(RYW1WACdv6o3B0ATwCm(hQV6Q=@M=1j{zmv zztTt7*UdKGTSQCr!}Y6h)-H?q(*6ud-x@tu-oy_PIfZ{c)i3_aH^21NU*|-;3K!B* zY6g#Hzmxi9PHT$(Q&-We5;qavu2G|DB@SHRG*w%SZXsV44*xBD^S&L(8XjaL-gmMq zH5A{rJ$~29T!#91SF(0@mJ6OOHyrZmXho#HPvsIR-8ZqK`r5FX<-19IP=u01S5~|l zzXl%;19iivprEq4oDpo^gSv0s{e_T###*1ff2J%jYJmvN{8_eZbIV)I=h8E`_tdxT zbybAdrl;EYiLy&?;~+pXYgJbU6Q;?=k0}~Iq0VzzgiUO6xP*|4L>84MT&7`8{81R0 z^>^S`wI!t1fAE`%5N_iG$YKUKSa!9(c*x-~@%HDrgH(rMQ7xg}zz8+) z6{&f9Z2ZgZQ}Er|@lgAp_~`vy-ATMzl^XYdrP+b+hLN#Xto_)W2dJ;zzOUJzQ}T9` zYFOVrqKCh!o7ul)mL0@Qzuc_+Y7UXvfB)b4+6NCi;Rj^sSQ(jLM zPgPl-RTnxHH}&*n{ORv^$wy&-CPs@x!B|?gS-0B3D5Fup;uvJcYOxGtl#og))v3JW zL;K?ud}Q_q{DL4014JN~DMK;-n6CKErKG>TeIAc;~QeRFRkEi5Rf?3%GpNZ;v zD`{9^ia+za1K<)TPS097ri{n{Lx=m_>~nWE~_wmy6Q@=_aE8g z9j-Ia@XgKrnE&km=QooXqMOC<`*Fw=BJa||Q+cW`52;Tk^m>^2wr@by7TC&n&O6dLSCb-VWnMl|9JGBx$|CHMJq`=SDzF#*fGv4BZ z-I$fJ@b$A8STTdU@;4SZYt~<*hBuB6eJ9uYlc_fQ^EG@vf1fXW{!heT=Oc=-X}9<` z>toO38{&gKU!NG?WZ?T~gwdzL@ck0sMw_*A$Isq7eE!$*sfV!kC)Vfth+hl8_pI>M zWJ!9hv~9%X=bPiN{mJ^8Dg{<=eb zBLPVg#!q%t1J29nnU48x$C+p?RUcJQ`f@A=Xjtp1k?YbmvTyw;CkbuE=XFfN&J*1^ z-0$F&r-~u@eIfTMxa(w3C%wd%{tsQJP!FJnpzy(RwKnSD%oVLcvZo81WB%;gTtfUUJ88d?c{j6XQU!T8;`|mk@1Gg0kn18OI zP5h9_1-_%Hfx&&qP-UR%`Ez{cKj^pkY*U6PX#aBNW7lY3;*Z;>?k~QWY)epnB(9|- zTT+}IUrXt98Skeq6+1n@lZm(NisjxYfnjowg%9l%?i%6y!te7vOx+tAPY}O|A7c)u zJfn*K1j4sFz9E06I^z5@-Pkm1fE~M z3Nsf-d^%|%^ygFgaWDrVNiH&b_SdPXhO zVeV!RY{kIsi&9eLx7foFG@s=dV&!@P|su$8>7kvNiB^G-n`q!g+c#H zj57|tu2Xig=K-@NoAQfWmLHb>Gd+4r1yZxs8urvcp~a()W5?|X3A_da#)ZLQQruUv8dWGcnk#)!y32H|dqpp}|LkfqkAIl-+@921f2i;Z$ zy;-z(_Uxf*!kG5*`?W(C$bPD95^7y$Wp%yxh<1ib4>!9Ey2*B>rTcZYYatX}vE`#O zQ39B*vf{JU{gjcR`53x-j}o(`x<HiNEZtFSi(i>D$557nUG*8z{HYX7wypP*^SM8`K6@Mc-`CLLoWK0mY8*2A2S zjH;jXIcmQ1X)m+zx_W(_v?9yPB7z@^`OUB5z+0Y`{y1^b7d_11UvTJ(ad8^Xd4D$7 z`;V_&{1=|LVI=u*9Jw1?m6w@o@r94>04H5xXtC-9oZ~eS z?VS!;DZZpl9PDvMzTYXvnB`?B;Pi;Uj9fHAmKE6={`KSNT?O88FVTWsZ2qXA)8Kel^E+j}P>ouUPSc zrmx7~K#^&O985*g`-KuDi(peV#JEl)7_-M$r19O$>eKLN2<0_=Hi9`zbbJ0AKJz!= zPkb8zU#1u(yAGf8_l{rEnLR9sb^J*xeXRJ&5uH!OH*v=nett&$JsD~XKeDcS{@`Q4 z*H=-v7Bqg0*fHum635xQYw*jio&Wf2_?TyX9p9G(UEHPwpm=ye&!xtfcJf=6-7^dr{4vq!rLter)np5SrL)!Bt^4>L4A*$_EB;DGls3p ziPzxQk>&>{n)lr3Xn3{9$h4BoPtUzE{_rB#4DlDuw+ zQVu@ZSuhe2v`kEQZemBJ@Y#n{W2 zziav2NM?d$*^v$3ZE9G9FG)R@_g7u?KOdoA;s+&641VC|7X3(^-spHMc0A}vrF^DwkN{MU{ndzih3B<=N0^6$ylxD*PW%x zvjqeh)k1e-Hb)NEpE*5DR*YFOeU`? zn)&bYyxD?JW4wGSuZ@p?Ir{3{a%$Ai>I!>O=g})IQPJweh4&*_3lU#mJSPgHMS7Ml zaOZ1-Z|nIX>ALXgU>Ha1@bQ_!5Wz#9d8*1W zVP3|Az&DEnlDhD#ri$LpW%m6Xf3o){@ymlslMKd9-^2`EQ1G7{y|C;Yp_aRlVr9C; z+W+BX+p!cu{E(-QYya2qxN-D`I;;IBJzZK_%Ya?7JVU+?Tb2fuQtxyO5p!&HWDvId zhS3LvUHYEgPmB>mi^Hy}W0sla2U-ta=W4Sbi>EHRWLLMVEVHuws%+Q7i<;aC$|2{* z`}aI^$0i*`)-e?>9voj{>999hFwUkw&O_9-D+p-@{@T+GIHffu-#!CCPIFfKGtj}l?SGE;r*Z!m6oNaxrDlkFeXJt!eW`@2F?79OjS1F8 z!ynqoRykBB+2JQ6j6O|0*r{B#U^!Uso>5nYSAP4c{!yW?*jIEGYMO*`@r6;5T}~#B6WQf^%| z|1_C94f7XTEPV`y)s+$K6=J{)^U;_r=Ht@Dr4Zjbk2=0K_nH7)`x8csLZDKWi;jZx z#dN-)2oO_1+wsR(^X5Qg>DzN-Kln`whr=>U>{1*0=MW zK{WXM=5}BP{%n0F5!GkG_~WCa^elfcnNPw)!HK`)&pzL+i0>;uxjnzVhR^)O<5+E~ z!NM;RQ_jNg&RGzD`Jm1ED1N39KNddMN9%t?L=(P$68_519i7M*_y&CJ&sm0!|F7e7 zg`eYZYt?7ZZ+!k6zVG$%=lH!oT{wti^$LC~p*DWS;BS*`TbA##eFgcS`+s!AIdQ@c z0P%h2#*?3TSyf(=%lhL#^+75&_Oa9FRD{)i>e(W7W5oRh@l7Wbryqh2$f`Z@v`QAp z52oN*c~IAtlJuF$oo|QJdM^pFQ|I z_&9Ln@O#G(OnB++k*U()ulwVSr!eaAeDQ^E3%}O$%VL!CO*fmHwLXi%%BBML4^+`u zFXT`n&DjX@YyTU)m|X&##Y87-=5s8^5p3w4#-`E` zEjSqJ=F8l4^yin4Va^?jax8AseaC@l!@(mxd*z3n5+!3rSY7lP^rT8s|JcApcgNu&)q1VIE?C$i{q784``J%*RmNGiyW#6nK zhQQX>2niw&Rfx$iHk$CK4<9oL_zd4~K%{tH<=_7B@ymbfAN+-{eoanoDgKcvM_Ht} zpmt!MmQk&`pH%h@dES!*9MH$fuIyU&3DJ}vJK&)p$AzCk8R!tr1<-AuT?);kBqW8* zBQ3*FgiMHt_f%1NU%9y5HK5jtJS$p3eGI9UjD=8QOic2st!h=r^?tzw?)cFP1lOvoSRJLrs!N)?aS*8Z{IQsd9XbkJrvg(+Cb!~WnYfwFtEo!p?po2DNxrT= z+XA5qgC-I_QLg`3<7d@D zCKuhhYkcG=i#N264ET`19+&>LoK=>ggzzJ?JR;4GduGMK`<J%!)&>DLsnHpXewEj z@>$-a=Y18c=t<|xEZr+BG58Ti3LoM_gjqWEK@i`@;m4K6VuYF$R>uSZ#Cz)8;xh*` zS$3}BwJm8M+U-biT>j1KGh0Ah=UDIm?9dMu68V5-Q##rfi7I|bvY+=&>gDl-IurjR z5TCKwmCp6=x)O$A5_1#eT!L-hlwvB;lAYA`V)k@7*y1;rfEGFFOJR@>F*0Ld?I}O4 zfMIWBUSS_!RpsA#f1Tsz_Rig`e&#>;t%*NyKXiAy+#XdP?ONLs0ea!I4i3A@Jw4jR z{HpM<>k5xcz3$NTSw*YY2bKCE$mcA9`zY&Cy0wckaFA@qG0tDU#aQd(US`Q8BQRfX zmsgZMc|?|*Uk<0U7L6&h%1rlcsiaIzgQuP!b#gd$cIpIwc4y_WzZA~Apch5`o;o-I zORNMk*Qs+g@n43Tl$0_W-@3PY9;?au z=K(VD!OT~JOzX!&mLhzy*g?g(?az%L0Tq+cG#x3#Dml%q=f~7f$G3jJFX9`hIS2sk zf-YV8k&RrJxV)Y3$@pmtKN9iZk6ZKQp|FVj)6~>Pe*P@KBEDIYaA|IX&-~U8o7#>x2RyIR`EJCw+xc$K@Wpvx{#?xe z@i+5{`R(yE%a6l9pk}|xnXWqL$nsElOGkP=eg)E=Z?Y4nsxq5y9<5muP;xNr)$;NW zf9P_~^4ro%npTA4);jl>zIlqfH+iE5|H&dR_)gBJB+WGT-932nF#pH? z>DMy&(${BM`Z+!)d{;7?WloB|Pr(WHp;pZPd1$5&ozHNaOZNc1SSs&k3*Ybf91lb$ zJbMHDWcP(1l~;JAw;DtFJU{R6eWirfmOA$$p7l?N_52hi!TLPMmqr2V8yaD7j-T4{ z$zAH&xbt&;c-zfDeB&^JT;MR_=Uc07DnUs3z04kF)GFd@X1Ogj8VS*)>4#foZ{UJd zRmdO>M2gWgWvQR7EaBV2=QutUmx-T-Rw+-r_x_9j$?tsWD__G2T2_%|L9?1_A9QD1 zJgM4XM~WM1=6PZ2q%7>}@qly;zZ+*JaN2=SSR%LeeJ@8l07JX*F6<#NoL=Z#A1HKQ zWNeA{5!hAyInv_ee10|aLyZUYdM9l}#jQ;(jKa-u=f!rou0ki;o)_<$U*pbjq_WV>FuM0fQboUEE$D{i# zhAnQH=EDXoYXj}p$ZM6r%%0CheDii0!<<6Y+CRSbCr_Kikn+v9ZtZ`^{vTZ)#^-18 zXEhqB`(^OTVVZUOzWSR6{!F#pzb|X*f?_H5|LVvk+)?=Ycl@9DOi@QngDU<*@GWF& z>QTt3FpIz=cjBjUuAx8o%9($`{6&vbv04n5kjs)AF^8yX5!113N%#EsU>6C@r)xYtjKDb|51@@v}7ouFD zVyj-e)5*^5DiXqt0$(|v28i)JTB|s=@(0Wqt|HAn{_}j~$IbkQ@gXGwu)nWb1I$I+ zp7EbvdKgxO)*0P-Luk=+H`fkmKK=r_-QMDvvs~mS_)GHF{%ex|1`>AH3_|Q8r5_nXzA{!AiiLar8etc*99)hgB>O_gC29mb!Du~ zfju^9K+r3#CZxh|dt9pfd|&Z%{=9PlAo<-0zrQbeOW3GM)SeAQ3vWs1SuM^J3Ebog z*>pEYlg>D9P(I`R6al>5k0i2s)7c$ev%29gN48my(efEp*713e*8(-$G1UkzRjY%V zG&pSG0e{XL6Rq^dL-bLBF6Fo6RT$$Z%Bq^~WBfOTH`39@=(m!i55u*^fW%s#8X_?x zSu($}JuO3fx~wI)wUq`<`M<0#&{B|~SIxi{?v1|PcG^u^A~==x3?F0WtSq<(~_mkIy-?g)hH!a1<(YxP+Iesoo>}kW- zlG+8y6JygCCXc5Ay@}MnsSrblW~eeVH%qOJ-H;1pz>WWO)we%-{LR1p55D}BuUmRa ze6mNlLA00J-)Mn>{}7%jZB$Qd@s;-e1J{?MVkCi4GtTXle40#k@hlsGE@@q9bflYl ze10FbS5i&Jx2w)D8ASkvKzhG0aB7(~=`zGMHTWmCDLiI5Ih%=L>ae-!q(ST*1tpqhi>MULqBiED*_syx!AP|lX19yqA);!(-_c&FHAFqb#RDg?U((6y2$i2ZG4l6B*WZ-W-+y6DfysC)%QWR=U4P;llf!?Yqe%n9V|H7vl zqSugiCGpQM;=)GZ_a#*pzWA>53_f-Xdj-y3sjr!Emx_RapCNTv4f&Is-GBDDrH_~A za?&8jA4D;335F~7}ue9S!)Xqpn!&c3k zLrhh+O=EVvqJxue7nD8dHTUQ5w~7ASIuiS{>k~0kHq$TaZqkm=lqf0bbfk*cBXuZA z&lajlx{f}_uh;RZ%Uc%f`EOQ3dVVUf?D#Kw#(YmK1EFm18K5EhQT~9mBLI1K0~Sd#J66= zBqsv#lj*`Guaf`)DXo?^1(e#2Q?z4}Hx&my|CnJ|1YN+^`N)BaEIX(dkHUu(aH!YG z=XZ5#0`Z{`0R9@5 z>i==Wx4OyNj3G0H9Cm!Z;oqcax&F2AUyJ{UZ!hrq{J!w*E~TsgQA^DBmVdSrR<3z` z#D9KY%x{TL#+{T0UcraoBJTi-|F`^^isQtW7}5MuQ~tla!0W1dACDjU!+-Fd$x{Xs zB=ClE=2uiAgKjS9<}61W{dL(ywsi#(Ig)3(d$N-j>Vw@ z6#46Y$UQW=s+i`OE&Lt8!sQ;LOOI6j zXEtr+y|x5N=H}BKknTX493q9Uk~2jqTrUuTKw39%;)4ytr{VOcyhG$d#*H+*5`AAT zhqLGRBjT6I63s<*n*lm0-a9)O!nd{lY(9us#11}KwQ4`#skt-dNfA7(|Mdp`%)~5w zDq^{eW}oBx@@&Jmfh!kgxN-}T|jZixeMj)|8qJlV{i*4J8-!3e!p1w98}x* zKEK)ASF<(g9QnDAhT~AhjFwK}n2M^X8&6Fh%VA5(dhmPwYvJGO8&tG>hL5U_*07`w zWSZu^Ia;WB@#^|KIFseFF%_xMnD5m^yIbpKuvm7r_#nEDPldAQj|P8~oEH9)z+d>y zg3B!bg}?qgKl@jH17r)MFcc=b59S<6jyB4?o28}O7Mn%s+N5gM+K6juoJu5zZwq2>}6rH-fASr@P_S>pKPH%0EVn;6uZUy zfXotISK7#hhM%ja-MI<&4;aQH*@69|;i#gfRktI0Y9Kr^6IxL!Axod4r(s?AYFE%N zh4|g5#r$PU#9)6mzOPht=|^CngE`e)ZuQte*ei`6&;EywYAN$u_>?YCb;j9Q%A4OZ z=3ZGrUH0|3M}=nEgZpmb^XrO?g7aX;s3GY45*31P+94EPn6XW!Y8W*rtC5B@&bMlQ zO4h3Gqd3S%-J(ze!JoW zh1t`VwuDJJ3x`TxG*)GNfWvN8w}%VFSnMdJ%)p!Y`yxhMv=3CeL%sHVRk`4_P9APVm-fLK0wYVzk zft#ma$4-Gb0Co7FlvvZ!!VzvF+BFkNzByRj$L)RA;Rxl#(28|Z-te@lG{<`z$g|Ga4B$j>U z=dZ=*dM!Q!|MmF%z2MjVd>OyQ{+XqEeJ&$VBnxf-VXWe~%b*IqB_ZCtm%-;fzE2EKDW#eY3 z{5)dyn^_AJB%a6b3qYrcgUDdsg zNe`&V2h1s|-ZkZ+CGzUgZSbqdyO&m1N1b=>{rtzMb}t`2b~X0?qyb@DQ2Jx0;5pMk zPa$~BXR^?Jj55zb{?(K8fQC0xF*-x;g^Hghh^H1Vaq@M)TS{fEEkQ^7`Hi`@$l!xh zv^mPfgKs;3_Q9v-Ys}AG_)A z)l8L8hr05)Epl!|j?(;N*ZWf{3BEaaI9m1mp?Wp&YfmrQZT|${M5!ZJUgM_XTfZCo zy0bFm=W?yJT6I?72aR2l)R&j|zVyKj_jXMw8iz`K%yPw^^a_gyEa}WE$LG$Z*-@~= zLOB+GE`71f2-aPOCn7l*7V;E%vt*U=20ptJX-^g3{&xJzfBKET@YSzPNln8d@caxo z`;;l;)Wj$gB_B@~@wZv*2#PeKH#L}}7oCG5Gfh^4nI4Bz17In~`1Rmr z^c|keYMQrnRT={^9B@Wsj;`O85r;_u|6K}+!0HFd$rK$MoM!}1n z-c!n>X{XBjmE2rCN8FQR;vOSdXaP}uNbFe+O~niTfU6~DJ$(~F&BcpD9+Ss<>of(F zL&-CzPn=6eHO&wAC!~9zuiOegj>$oG^Du|yvG%_*Axd6c9!De);Tt!syH{QjfeT00 z!tc2W507*R$e_0!d`uw8tZp1( zRO4=ygk(J(|GeEALK+HPQvL;N#%R_{RMiVz@zHr|35!l7>uI zG;d|is_G9cJgz=9vclu4r0gd01?6g8jiQ`Fe+!{)J<|~E7xnY=`|bVHnq@TRO%EUE zpTML~t@sdQmwfu=z-W%V=!_s2=`s$CO2`t8mcRnELi7!vn%}~=h0na-BymO!Ycg(v zF4evN6DnJzX(>L}n$Mm0s2XKDbjNyDk!qd^=1!<~Mi~~zSc94dHqs(C!bM^c<>JNpCFzA;%UpYmbmDzJf*2NR@QKEDFGIbGZ*SnQ__l7x@?qrq zTI|-QTH!>Y)abyj#B&bo)bPM;%&zV>rBBF(7Tgb zL*smb?+f3EuPW4t`=h+=;eAJ+c_JHU8=ddw9B`LWBnAg(eLe?10yg6($nC^mra_G# zVEA0f!5E(*xs*$*NDB4DKd7)m#H)z!pMcNvC3?OxRav!L{A_&iq*AU3#J9o6{=PKv z2mE>iUpmL;oVqHycDR($aG435@3;J!mQr@FtwdcP(@NaT;!pgy9o*5Y4ZS;mtH65? zJip;&i3sYrl}Vqm+NrrDGYbuH{C!y_nz=VK_d7q~8o4@w`-@ct?iVf5*mZ0#u)iw` z=OavsnnWkXrEoP7WUciEzHR(`1;6hv>BO%azKwNPaYQWm63Y0s#9_m%#1kb zy0AKiVJ=*=gRCZVJM6PsR=b=X^50RuJ#814%0Xe;ju< zO2*-S8j;+q5uQoqJqSKU<`-}m}-ZT*49y4Q9sp>R-*Y&?W~ zjXMbzsT!lB~39(J*kQhi@UuS6J7vC!3j?d27!4pg4>O+bCEe2`gj{3%0)e2?WlZq zKRxAY$y;9>OvB3>Tx24N+(g*mJ9xQ8tZ);tM}lPQz3VEV5QdGC^D` zLrWLulnTf*ou!$A#)rlH?auFus|pe%-SAwBvlSWR%88tKOdrjz@?fsbBJCIrGG0T2 zhB9Dye#1R2SY8L~v{H)cz@K|BbmpXa>>-ma&92iD0Qea(`xE+Bxbu4npQ-_inb{e} z;G2_9P^?~8+u<|KYi6z(QGBDX77J1Gdq&oAqyzdC-tuJA~b&;f5tFg+yB6K z>wSqg>}z_T__gp&bzAbr<%N91x00TYb^c{9zAx?{l0L4^eQP^gg|GPLvV!-yi;iM( zr1t;RnrHv`Ro1ya*+2%J^^__{ijQI2ni#|tC~+i7s;z<5^s;Czt&p}iqRg;xsX-I7 zKP}e&X-?XU_&oC<7pE{{#h)P$bo_@(JHItqN_0<9u?6at+{4cJGx*SP^woua@qKY- z9y+GIKgTz7o}ue^WoRzUI|WNw$^^rQkr0>Mm_6sXLqxa19 z;kUEwDy#PI>yAyYzeS48XWT!j4sd89Uh&gQOaWGcq!opd7}qm#{?f1)6J0qhW~k;{ z_LHRZB)FU_W>71mZdZy<+<*T9?W5LQL&`e5*7r3$Vw~?$+CTHxTl_cH8WQ=>=;$jR zvN^2v5nmwCx~tiT_wx%1<>X4&*g2Jr8OiJCqlSja3~fDOt7SpPqvILA=2Cb*0)IU} zZtu7FHt_)>(HA|oL#i>ucetNlJ3h?!TkHGH_5JSrDz{-&jl$rI^Nab_!Z%+f#u{p} z@&~)IJ2iwbYW;eCTtYiOa7J12e|rs=Zj1}RUf^r}NoAS!-|B{ZKXiSUA)St35x zZE}P|Me`y#`sgNNB;)_!XDwSQAg}lz+bEj111d|@E&i|kpTPjxaagab?*0m>aM%) zs;jQ+c(O9H=PENhfAudT%#QCPvi{+33G!Z*-V5SxXTckn0AH$jEA6RFQd~sr=)@*e zOWg1Ki)i*e!3s8a$|Do^l7O@Vq}uvogJ+40{aE(9ZepBvE86#E-;n{PLm9~X#lcX)^+%}|@4nW5v7c^h{VueV zn0fU+z1sJyeeM|c$F)B>`yeIXTyi{`-IpYFeK z=FrYLzo^%zdet~zHYkMY_?FLI$uWiU)a&ob$#lr!q#Ek?ByOCbH1#J-ja58tG`y~= zFB;Go5wKZPXmXfoOoM5@`t_$D|M!3HkN(h4x1&{(WtIA}n2V{Ub&841j(Ny?ID4W} zvzUK~b@H=jqN^_;O^q^>E=WQ4J1R`tZLn{G2b4)>psb?pbL5N#z;wy^>Yv?*M`g6z zNWlW%r+|{J3$0ET7YQFBWB0Ka~6+{GS4t00XgLAQq*VoJ{*_{In)15_DNfUh!gIxU&APH>;Jwa zw$KZZebT_XcdaUz%IbTFs3w5aZZE3xQbQQZ*QFPJR^bTl<3*I3DJuzr-xV!tY zj`%hb@Jo7eXiSpTx9Ead)}GPTeJi_3?%eawvR{&sUfbc^gL%i!p8azi^Zxb|Eur6wXM#Pi!9s zC5kKF#edS^toUrm|7!fdsJNOwFJZ)gCp5Y4<0sQ#kUI|H-s-hLg@G6luC4HlP}|b8 z>@aX(VI*P(7om9FQ>&cVHZew>{j+@hp}l&0hG2028f)$mlnOYd&Os_66C1b%q&ntu zgQDon)jFRzP0rq`ueD;`po|$f#m}_WaC~FadLp}jcITkGlSP)^+sZ!dtJ}n9QGN$8 zzPevoMmd{F(?{aIF~QPrIQa68*ox zTR(gv)KFOr7`Yhwc2$B9XWqYH#&PoEmIUZ)u|6*Q;B}e%vFvg`IuYxzulq~5$h-Iu z;$wb%ekVSx`>S+*FZW|z?q8GrN#v!0ShnNCJi(biEc+LQ(?uh;-epPFQQ5a;-^P9|UA*FRyTsKn z_Whh5dFcLC?W{OI=v@E0e}k%9M5eI+Ns*k_aWDr1i8g!$UdYWd# z+-Fosa1Kwd~LPxWJA#GuX4J0ZRix$9k|6*J_n7(GBaXh`$r~k+fTyfF-afL( zn-;9>7!l|uHcv>M`KZToRIQ}xiTW_d$D_&Kq4(K}2qzG`y+3)D`X$(kjT;-iO=V=A zPgD)`1~oTPP5^yk*Y`z#*Q!O=bm85%u}|0*XC1PL+dAA%eP7*-%0FiCUK5(+;MyGd zWGq|uaa}*KZ@Kdu@b1;fr*3>0w28&o4P0b0p|KX{O0c z0~&qUf$#$Kaz8K(gGp>@6^nV1Cl*~|R1fx6vyHow?IwdvN^tElV1hN6ldN%W0 zx6>OdTO6eV?+}&OS)cBSZkFb9Di@9JvBW&ibh*3!dV+txG#*f_*i!$cAwB`gHbeV? zXfh`&L1#9cPv+YP^}m6l8z%pTUZblQn-{rqk;|nmkj-$ZAr;5ZTq1b4MXkilvW47Q zGmGt|U|j}+IXM~`Qs%JH8dc~s4x^a-HqAnsV#t}={z=vYlWxNDpO(_P&2fv=WqO%s zRm4HXE@boDzL+V|*OGnnlm5x+%GT_r(6@e4GFy=;re7T`FeW6OZZEyR`W~QtRR3m0 z!;?!KpWBjK#b<)Pw)xXBKKH~77^2LW~etPSWay8fH<(UNzHNF@d9(rTnU0|fCa`vFG z_(FNUc%sR;A&$0bUx~$1tR?dQmT>E@cwXZ_a6L6s*r;#z`9ii8XHP9C{*N}RG=#AJ zj-O}MF~zZx_VG~tL~PFGvphJSDrQXzjX2JonU}qGJ1ov-W(4&*As^7%^t0*+zIUw5 z{q|`nrFCM*1xF8J{H&=wtsH-xMEe9!FFM_o-oVG;Qu*h}<}FA4Rqq zekt8wn(P@0bRtH@P)26Ye(jI_URo(DHZ`y({$NsBiJoMjsM>aXljN5B%C9ujUXrsOcI8WcZaaS~=7-$FPqJ?h?fW(Vmwj9Ik+g;l zj;YCo#(wDI=YSpGX#Y}Kdu0D6f2(!B$o?h2y|*vyEv1&E^8GOo&K-7o=q>g zauE`{o7o9Y1obNLz7X>t85|CE4HlxXSbrA}hVn_&q5Az&%omNw+{L)#vaiZfa=!bM z?EA9s%l-|c&qMqEDeU+9o%YG%z`6kA_ORaL)!uy<_)oH!Z*?+0Gs`^jKH__?Q8TP$ zU+tIceUz8?-7x&0wd|L1{j?7>p46(CSJ&0PE&D^~2oX@B6*DVbrc~9g~ zF*A!Zt65(U0G$XK>VJFY#Q$NG#8Z-8;Tk24lN1}&($M|202`%d-l|>s>J!O+PBiEh zpH(xJ(s*mfc`v~$v+ij1nJ_}Nr0XQgZ;68OI&-a`1hoAIEKwHbwYSfC>TX-Pn3RvI zvoS#E*typU^-}$5+2<%KdM$VMDP_X-U+Oakd{^7|umANwhwzw0-H8#q_oYW?gkZNB-1L6FLCR*|D z$mKtl3EBk1`^cRrymU}fNPn|kqxr2|%yi5Za;7;7T-0IJ5EWbb20vtu?t$+bTu+_c zoiAW97p^gQzUrpl1q9s|Yn=IF(|86dWu{4DzUIb1t7>$xwo;1?c#%am-daWlrK_H! z6%`mC#fPak8u30k@yFKpE#KUEIu_&);)#FBKkLpvXO?`=IWGIscoDA_G%SPMg{6`1 z&tPB3rnuU_S-(yE-~K=G890s{8C~*B=ax5f(<~TH82!Jdl;qMpL z1fM2mMW}bs@4AW}GjOwN4seVoaz3s9Ac425^*ZS}8-<$Q< zj&FqmtnaJi?)Rw{3 zm_ya}D@Q-ZhY}h6iW*&xd;4Z-Tf>X_gO1td{>^93E4}`aKlmPaq{}3RTq$wKDDWp@ zT16nsTI2WBptVAQ&P+-JZf5q>Oi4>M_^aO&KmDbTd487y`{Fhh>a?8s{~(|_eZvVI z$p@}HQ&YX6_qmUE0&%o)rWbybPFL7Ar3 z)eR0QRqY!^zd`M0UjP9B07*naRAn9K{?hy6>OUggQmtRrRWSKQRGpmjxR1|%m%lkD zMPz^F^RDVAFb!|CPc$({T`3H%77AaKQjUzeaFk(&uMHEkF6{= zMf9(f;=x>nB0Pm(u&c^qYZ;^CF zw~XE!q;OoqPU;?X#~9qL{fW?>gJNqJBvbn6CFQT(Ti%Rx6C?*;c(LF2DwTbuWiPE4 z>lZXgzf1)#_mvI#Fi%M|%+l-twu=S5*?%!UJKB_Cn(rG;=qVYm%#4X^Gt{;}4yNF; zep8jq77}tsMK2Fz?vL?FV3;bUeY@BP#t{_n163>*@$4dzK{z~KhC)8CMDEXH|1r^~Svh%yoj7|DokNY-InE>ntB%LfI`?Mg?6G^G zTVHM-VTV~*#Sw?w5q`Ku3__;KE3)k4v(B>Tv(oH2Ev>35J*uj;yMPJBtjr;Fi-I!F zh4$Zl)eXs;K2w}x!%a?sPRa|Pf4L(_!~7@w5~v% z-?}+pcjx!<`SNhX`C7I4%a|7Ut!@i?D3Te{qiGP3L#LZz5+x7T;I*mjkJKxxYA*d9goglM@`w z9>s^3?#Ej9qvcJcS^A@PpMT!wKZX*8I|U%#y#EjF+pPcg`?~ChUiugNOQ+oy&!elQ zw_Z&?8Edcq;A{EDzEOeff&JZ}yx4zu|6&r5@w2Y^t!lcqOMM~8&?o25pUFO_Om6dY zcguVY4WaYHKl6uvfj1!J2rzYUp51pGSf0FIhP5Z7d{rBP@(ZuB<3RpaQ0oq}`pU1e zpZ@meAY!S+TwofvG6|QPvZnRZZlP%&MZ62yLGTD6O5beXM_gS!gtG6xC-WdUYs2Kl`Jeyb%n(IH&N`FCheE|7QP*oOrzdw+M_?u>1`6eL{~| zKxabsy8lp|PIuZLWKQ5tTa`VL$ViCOF88}V+JD;D{(olsB-9!uH)w6Gk>fl^A&vN$ zG%CEK_I|Q??$RREU_+f}z2HCcf6>GzC!t0w6-dzaeb@P|YlnC<5n-D9>YtpFTIlz>J+nQq24F&fhdf98U9ohV_=3p69eA^WoOO#h2N@s%I=p`SwC zBquLO0bejDsM>tC&JsP(JEyF(BE)<)z&m)dTlvLDrWOi}6|<7c@FVMVWM*ESvy#Tg z@9pEU1H4@A%Aiiz{JmIz-+Tb3MQpx0x0@a?7K$d+gt6N4beQTB>qxYwD4I-S;Ie)m%JuS&h%kIrA6$s+!pV7w~=QKecKWn$cJjK#l(d?8W*VKR* zRgw4G82kqRr_?A?wxs`40J%mW-Y@5OpD!VyZtMr+!=!`}j9q!Dx=K>YhjHWb9uOwm zmM*Nup&$8AE#RK7_-R4^S>HF16Xc4S8Ga<|SsdYG{8`i~($?7Dcj=Q3^~=InkN=Qw z$m%{mPg=5uN*Et@D(-gsrmbO*8v7l8+CI~(T1*2M`_(x_zux3z{|Qz4_|nzrodFmW z&EU(E8Sc{Dv8o93vYseBBHZerDkxMG3lpa4mf4b7mFZP>nl*$xvynQgc%w!G&6e!_ z0S_@|yOyrSid{jlSC76J&#xva#7v6$f*y1UJBGhfoY|wBp|m;Js6nJ0Jimq%kv|9r z&54qp{t@n^=_{pQc`O`-}%T<@=i4_NQ8yZiA{%n@7G@}K+ss^ibY z^LrnEHob%mb51?He-Yo3+?qB`f!q6i-H*9|1viXKT0c_Y7uIhxs{sBb&a3Kj2QkgM zs(Y~ko&;n3d2@ckwwBnGs9e1FM>!ee&$5s28?`8fFZLhB|CM8`_?g%5`)T6yo4@a6 z%)mcge*Sld{rmm7w|~DsKHOoPjJ?F?%X@71vD60*@BDoD_$sI?y%CjWmd{zQz5Mro zU;WnK_{D^JS57~vE12NA;!HiJ2$^@S{Jb=l8S1~m|Bt7T8i|j*vHkRKsINRdZ!X%6 zOQIU`t_!H3LIiMX#DMwFo*cg9|9-jO$Npt90PiDZf3Ux%l1p@(tfs13rP^xSzR;zt{lb2z=fS2L)Bqsx_6GZx z{=l-2jIk>^kP0sHTR}_is60EI1xIa{eN92F{Qu5AxOT}EWhx<8I_=w#QIkdCVqddl z*y%_1{n|=v*ar58&nLowIJvd=|7zbf9@ZL)v~LTI`wiH4p}S&__c#GlyJ-r>kNf>8 zb)&EQPnML~f559B?f*Ay|FZuWMkd!~T{OxSKV~*rN|t;>HTq1`Dt(ns4L+Zx2io~V zMnt>36vlDB*g&CwmVfZqe$+zU9>%?}I>Ba*=Rm(Tp?GEMtzveP)PXyvZz5rq;-64-po7ktWg`K3{q#_P?5Lk>$WehE=@NJ z%V;0O0G9;MX5hnCKGc($L`N+a*!?Qz@N7$_;wQa~obd@i=n@pT&Ki|y_LRuaC2mN7 z)u;o#Z#81C2>hC1crm<$rZS72M=&gC9$OmHS$3_R+jUD;N`EOcacagEM08CgyrKm^1M%?y&anSgl-}J2=7D9;V zt?hRZxNpcl{GT&{^H%cVI2;%f{fFe(QaK2PWt&mFlREBeP-Ftb1~&0$Xjt@O3#NZ^ z3S(-NVRafwlSF@VIlr5u*L1g?P|-GEYQX-p;t%;>ukr29KZ}heRrWhRL&aS2EpOx7 z#Lqay7?{3CN@#z*;!JXSopUoh+2HnFyt)08%EP^`_KP&;romvne6o(QFaDijx(Q|N z5Bzi2t!LtFs+5lSUqsi%p4qv^XXB;RZTDCs|3lBia3%Ne{h9dbWS+XbS}`{lA^%|! zYWAZR4htZbRyhpH06QmF8xP+~N6}jKQPDIhhH2R&+-J~7`DkT%S|(UM>N#%YP21e0!*>?|7Cpc$whBu4xXGKWpMbv}udr5%S z_myV3r8DM}Roa&Qe6asF@uzO$r@pVR{H+$>7MT7|=5L?Ee?ABMFXp!kCq@4m<^jAf zZtq{%pZCS*Y`^dKd;5dELP&6{*JW09TGb!;e)}!I_8ll%EajPI9%QY`vO%}7UyFg> zZ84qUCDvMg7UuSJL}fzVSjZo*aQms>kY9Us4$HWR=?C3lmqeQ*akXG^{qQ`R=6*r3 zd5X)Aq?Sq177oA}$E0@=w^G%lk+}XC{yi-wnq7|2*uIePKL;KMD4K5LyrS zhY>Z&C+PdN6&|eZTQ=oB@6-V0h7Bu6ooi@NO}|H$wAkN(J? z{;8$hhc<1ZrPh56Yi;b|`Vleu=(Q(mEp|fB=FyM{+8@~sW;ku%seZzPLHj1bGgtZ6Ba+;{u)qC(G{VTo zRI=}GvVT>F)OdQoY58x$w0?vtbR5v~gni1&x@x{7mKAt2h?K^?zi+|+eK<03G&O-s zR`qaEOBo455OIns-9FJHn9Ei0`{pU20{hbGb&YRqT**H@%?kXl#Pwua9LZ9#w`Qe# zok9Y-D7L#7&B-;yH&Fw5V1K$F=sNG=^v~P#RdhX28*_@DzY`42O(E$mm4o;L=FG+r zQQL9i+uDB@3{EM(>-&xft#$mYiJy+@$<;rlD>p?=F+VOenVbMq^FL$7)@z+(U3#8JV+8_jfvyAVAr3@q_iKj4^({T0Ep8@9_}dE=lw!n=cELp&Fc%Xf$@Ry z-w-k_T=5OkYrKEGzpukTm9qLC0jsjIaQeT)UEtpZaex8~q(d%zrJ)AFcXi~tG_HEhc{jRC@kQnT} z{aW^me9|seCQu={>ba=?#O3_n*e3{n)}LB`zOepW>#xiDP5X0izwY*DskbtijeYY^ zw%-@ly?wv4Z+G_T|K{w&l`Fm>3H4w#i1s^b>HALnbd_3MVJ`W7p$0?yCjSYRYa*bH z?b!d4|3IBlg{d{y7Kr~Nyyvm`XnXyg3D5d{t>4!AZLQz1Kb?}S`zve|3&uX;v%x;0 zdntg*lEQPnJv-(PYpLurR7MDx(e;xK5H-^)Ga{X?Wf%T97*scGV-TDvKPu#Sx{r8*wkMJA+?EN2gWYV6;;pTNj zgx7I|dmM+G+0o(h{h$0%a)j^xp`bWkQI!=3j}f|*E*c-(oD)?Rem>ie{lwRP;D>*z z0rlOKD~lp5+y;^J*K`}(UPZttMO5jm-Ixd9p%sRxwMS2a2lWQDs zv76ga(pv zl8>_b)OR%+#o;oqI-xz^IL5;;)FMo5cvBq+wBeY9bs~4tBX<9!Mz1GmADXXzD9gFn zZ~wfv-%M3)e>(ld(G#o!MGVW_LvN-LtZdZ}au!T0a4~Hyp+;iH?dqSw{gG4ML@JyQ^BYvMa~TO3n4LS+}QS||76)gL7Q4ap6CFm zsYZR`A^}dgZNgpgHQ;<9k%s*gXdOrdU*}8b_YL;<%!@7mvCM99QZU(R|DpfHj-Q)_ zV9zX9KM-H=AA$++eOVTxdRh_U7DBYK1(q-RXQ`ewZCR*btxItGXEA}I+n z7vi_r!@qc>(m!i4_uE7JT>neMG0Bs4rUJUk%IZz3 z|J!A~M$ggM2ZJ_b>e)KHq3xVkKIerq#+ufS^?Ho}ob-POGMc>q*a^ zd&{)Li}{HMHzoxeqCA~w*p+n-&ybPb#~XG9g=L;!HV$gZ-6u(*OG5~ZZ{wesWQ97b z@%}yW#9Q~HL8N=W_3|IVSUEgtxA}vKpH>B`QvOz34jhH~wLh!CW;f@TFS1%Zw7oxG z*Z8LUk@*Sg>6iG>_wT2U4+a7YTSEzgJwP>bs!AmmtOxdoU4!@I3-RGW{4v{;<}b!K z)7ZjVzwBSg4crK8bo+Gs|B3!n)Mx%Y>_5m47x@OpB?^tFo&Ou<%XmN5B912~0~?rr z@1N5%$ZdbY%~3DD`oC!mmiDpdZ@S;17~wa}_iwWAXx*P>r@Or?YHCr zjxJLaLgEqZ{*BoOsBBG^=#N&ZJux%b+rD_feX(X6Y0rAev4pSp6wU~c#isdXeArrN zYkm^(;q$f+9(#hAoc1x9A{fde`#1Y<9H^3<*X%z*rf;x6M2Vl1{dW4cPZ5^omNnn^ zW`9PDQp&P6vhvh+EUJgi{%`y7(*E<1kK+~RSV~Hp&iG03B)bSOUGJC8J5psAJu@g( z8)c`~sIDX`rcAa4>})jx-fW)}fl_UGv;Fz37ew%WKQ3U3oLsMP)h9>f*tbZ;=WgHE z0qkRyMgIH?eXh;_Z46?V*eTIS`>02Foc<9f8YKc zVWW1U<5ZYfL7Jc5QI1sxW66E_&+t5Zr5EeNV_!O)VNy!2YKZM|P~C z9M98H+iyP$2-2j6LAx3YK7zwe_X;3Tkdci2Dsw;hJZk%9*81ya(d>M~p>%p;{e z^uvf7u*1_WW#4svqnMi6;e+7q@9T;4MRbDsxTwp1mCJq?c+e1YmwosT%VOUCKmNn> zRjNdE*B$3;{GY@^z+4Cp#Ph|Eb2{ca@CopvNfy;DIDLNcA0rkIZknpm92~54b1(Q$ z0#X)Tvh`$Y*x-s*Cn!HG^*S1Gu??Wq-0^)GpKX4)FuiY6eOU`IE>)EumR+4J!})SA z#y4N*m;P_p{dZLN7(exW@!5~w|FYR1vs?SjvmEUbX*C1h1ExoMP2h3bCI2*UrB@=| z93UdKEDqimUMBykUdOV3jsL-6Y`~NYpT%+6`b>Q?epb*wtyjw*w>9!qKMQklYaEq? z{0Wy=n>WPDj!rYPqTJ8)GJCd4pt1ZkJIh{IomFRfnrHdxmSv}TW>2!W1?ecUTDao_ zu#I3wU?^Waskl2LtUTHk4zG%CG6XuJUd8y<-vaS1E11{t{$(JW_=fvY13%oK_8;y% z<1^wd>kvVh7bzXWFZT;PbnbWa!?O@SgPzHjF?x@@-#h+Tx)tx2fH+?*E(rJt`10ES zUC3Efi@jSq{S$?oz=F2&tHSxJmNc6AM<)WMJnY2W!m#R;g6xIZaHf`h0-XO(Q?&x#Msh)n0Vbbj?@aYCs@vEKe!sI|CdPgj}S${)&e zUc>?8y$MeTC_V%YNGJ{*WE2?^ED#4k}ABf1dbL+rF*!_x{^_ zU)`9qep~BbCAan;#W&LHSn&HIy-&?xGKHJ{fB&!jD5eP6?7Yxcb4F$e%fMb~ixkn$ z6rBZERBaoDMFkXI38fK`E-7gRMnFPB5Ts+IrB$S1MnUQB(xJOMXXtLEV`z{bVuoSr zvy`(*plFGCG2 z<1N)3L?(Hg`AprDfBr|wCvdkle~KbWWP=d7B*f{7AByPWd!MPj`GWX2i2JghaTb!j zWX`$v6+T<;&fc%j4;Hr7ZFbnylkt=5E@C^5tW$l2;G$+R-F0$%@7btYo!rcj`g4cx z*>gtej#UT0?~`J%oBeHU9YW~5AYrh5{}gP|2{zd}u&DP}VIa+^tX;vI$)^ZC6K zeSx8*X7{&N7j#j2{j6{VFO*owUIU$3ONoQ&z0#*Wk2IVk7JQ9?9w3ha91H?wlivik zf*(eBZxL=hs0fndXgXZtdThdr>Su;RoADxj((rOu4^U3De9jlXQXZZt@Z2o87ur<6 z{4+~H8XzBQ=s+&UWO(fw!0ffr`~3JiHbo&~gcJYg&--h{*Vj_y+M+VawRQy2S_)bm zOhWlhr!5B71rh^=XgBE_b=(fQej{VGVhD)#`&)q@v*4@=DA1c!RK8AzSrBa z`3y=PNau5CQkxV1DN zPnzPdpsuMPhncU8?@bfgDF1oDT^8rsok=$;!K1f?Z`cNs#@d`*m?)6Tf8=taAl`I6 zdhrLoKI&y5-Z=Ku`_TXogN9sb%1d6-?{AtE4;G_LPZJT4rF><{-;P;8iR(&qxX>cA z9-**_u)H4*{S2_Sm-Hn>OfsV8$pR1 z9FV_l`NbD{!UpbD_P_R2q}lJ&n^C0mYNPNMkDQt}A(%;k2pOy=QEc-na@pByM16$9-ll^iFu0lJrhwm9OXp~R0u(Dxs5rfILMa|X%kv%jb%Z#O z-R^yY+R8n3*c?(46@2rWv&y&_JDdmvFM`gE+F92GhW=0$J_?8&E~;Q`Uw?0ON+g1K3= z)<4wz%SPL{UETKTaKFGPXV?}B9}2z<1>dw3cbghA8m{^x4bSNwXGX8)joJ)@-#F)O zFj?DVGq_BwBz#FcNR8;}x%cZw$ky}7xUUpcv~Qrj#T+#rnbq)$CyqYVZMvJUNT-Vi zv#H;-Cm5>;)hz0KjePCD{KC*Ivd3zbs599=>lfL}Lvsajf>9xt2%6|%$up>-@9$&I z5bo-I&Wh9Vk`f7rpW!Kf58V6W-jq9p{OpU+-A`YX9>~hhbk^1T{MrL8G35Ei;f=Qz zO@eDGREBM9m-Ppku&6+mhs=W1Ry8NWyE=ABU-d~jxtUoi&O4TK9#5W4HQ-6`IFob? zkuLK6bBIal^;S=_l0xEM^fQKLGYPDUF%u;f&-|;Nr4w45r;fhI62AUw?F=zykV8_+8wp{D9ki37QyhewhhzY>xm}Auf7CxqS9{v>!rjp<*j4TaJE6E#X*^7MI z!NQhdr^Dco`*EFs%N=2$jofe`>CZ>8v2Yn`{pct24uCT`=&`8TB!&UbCZ3CTFK*Zn z5p1O?@IxY-4b?;0vR2rE8!?A1Nk-P*yRRnmeV={@s+S1dsk+}zS7ONIBp608WE7kF zo8azt5^M6*fr$F(&a9uw0e<+PB*_Ub8Vd#2`MJRBjwA%D5|M}?m!wZ`_6nmYn|bdQ z=ZMmO?|LdH>B+!=c;t~!>+)GjIWAM>OWhpAK)D&3u|2P_ z=7;mf`KGEFBfM$eJwN_S7VsEce)7S?&HDM6VAp?ES>^X*|5(jnK(Ui=$WK#g!E}=! z{?Nb9HT>k2f35U{+ZhfV3fXC|9NDZM!C7|$6V=}0-FukShwn`JrK|Bl`-b6_NLd=Q zTDI<8Goce-pNWsXX@E13?Vr5skB7Tgk11*T`?c==8E(|H0RIMh-xT#vb#XWyf+Rjn zM7A)5VN_ewdJs17CPMil2~ozs+8gK-@Qp}Rfu>`RKF5Flr!dSBU~}TTxfDJOMV>iC zEAXO%o=2>ExAB}!TYnQM0DByz;sMJ`YCf8Otj6ORE<`ZT3gonxB)g-)Sz-eKR^Q5Q zkoklmKs3rdgQ4~*QnP!+ApfiF(BAhzGlVa8#tBR59>7zg@5A$kAMLS{u+8Hb zrS1Hdv-c7qfsF`E?;-k-iLL~#C~XfWnbEvLWW2KuD@yfL;W6Er>< zNYyTyi-&~WnXGR5$o>=~?#V(i-em`AwfxOYp{>3*F-A=i#qlx{@)2fP4K(0sJbs0_ zQpNPj!Ycum#;_p;q8mW~BAl|v)~OK*d}>KV^!?*;XmYI@vd^b?dsW!9%o}NeB0ol< z9wB=Co;E;FYXOKc$$c>-Ldhkc;g~)&SjBYeR@8&wq}!EAlVVt#5{3MZ5=Cn#sW)!M z`teGbE_|;;1$e;WgVaqRwICj|Il)a@Aks*7v5Bv`ek-j0&IlRXhY{1j0IcGX(d85w zpA^5=&_>VQYIBhPtA&`dc2wvcxtTXH=gZtGq|e*eym5h3)ZKV`6hvlNW^mCYZ;yn| zy=@J)1%P55s^D{Ya~wS2uut9?pf>NPAHK-?ZJZQ<&im+aKkJz)UyM~^b8v`OlJv;z zZDk%CItQ|O*!5Q3!}QILVcIS!t5kIU(+1XXBfITicLg7Md&OiHu~_kA1MbBO&X^84 z-_lAQ#hBI_chwyx{rWos!uMvU1CrN(g2`HS;KUtF^kcFNKY>s70|Q!s5OQCMXS!U{ z%StlzKZKrKDTUYa??uHkN~H3v`lYJh!@uhsaT45&%5H8Jw{<<95Ma}6kk&ema!k5V z_6Tcz_U+E~*nZAAM#^J2#f<}VzTsQ>H~2LTTVM@!t_0nz>!o!Q!Nswa{+L(eBPfsY zgNM+>I$au&40)(T!S1?cMN6)9aI`eE=r_~2blrbJ%wImc1X=_sHmy0g=JI6=s~nmU zfSpgAVv3>9*C%UXZI#4CE$>BjfB43u4q8Mdfcr7$2bmW;hXJ1Ywcj3EUb>CVqxB{d zGjt;aW7q@9`dIN6Pjd`ydK`040czdIVjd+mySfyC(S9% zZ*J;(NZpY7)ovQcjaY}PM=O&((!7^mpahh zH_axTecyhiEhU7w(Wr*Sj?r($jbVI`1_`OqWo|qbt1P0(;+|_lK77{O_OW@EOw?|nX-r^0 z$jI`jE&BDILlexd9(fbGZZ`)&3n)7a{NBG@1ScIK_!c#m=G79R}P> z!)^&LtM)M}i1hGk9S%Z89RR)X7!9ofD8M4zH3b^6PC$K79;GuqHKKsBX{`vZf#Eb% zPrtlUE#td4l0Ttr`ysFjQ?+$EkDLG2QZXQPl=+)hdG)1RAWC5ge6ywyytzVN58Ety zJfwygyM5*|nAjpZ-UVan#s8uF#(#BSLJb3EY-Nld^T(v(s25!=BJddp#zq#d!$W?y zgf2Wd52=0O{YihGqsx~7Kzl$-_%y%vO!x3Cv?+Vuz^-oRw;ycy5+dYl;^GnF-i5?L zr`jLFuu-ZFJ@CNu@okPq2__YP^+s6M0O?>Wf<-xhe^tXj7{^VfE%DQ$j^r7)WSNBL zcYdO%1QdJ6Z8R*gG7D89$qSNqgZ5H?czvcb*zl^Wi*h$? z!R-mK)~NCklfv=7CWPe2US*`$eDT1IetZqHN2d49~4LVUewh`NKr4OdYG?sH5z%~#!if)}x#kl|a3-?V2MP@i@gtmrE@)%To0D66%Sr5Z&sB&Vli^)tb3Avh*Llh ztpJHm;%D!jCmXM=#Ae8vZk8n#)rPlw4e1<6DXhSOWiMN|GTnMw`X$9FH(9Kn2+LM4 z_5pySYzMXYu~*u^gydTEepl#x0fe0X=WyPWiH%sHcx9o3i>PG){&xrxACR{JH!iQ1 z3x?O;Y@lYdf*Q3|SeIwYk37clE89lw1TNm|2Ck27Lw0Q<4te^U(j7*EZ6olkO`7?H zkP6 zIC}eMuIKTP)ajdn3C+~{0Ysbc`wjh-@U7~;)D9knpoAuUF#e#b9)>R1d)zbo;X$9{ z$CeX%`&aXFE-&jOlR05gKw;3a^g>bHO@pS|RG1y$17`y@X6ntt?f$zh{@!QI6`n~# zo-4ZU6U5=NZeefBM&Aj(w8tFqE{CS(IG0ZwXLmtMkfo~CCBFM%lG**Z?)dD{66C+Z z&~%HTr>^!LE^IEh>7wdK4j-LOk(tLcsM%x;Q{9j9f^1k$7ayh`DN^(0ib4HSqu{R^ z5?)RCc+nOO@%XZ`>`;o6UGSbBxgNH;lb(a?bHs1rwP^BjnK+|A`8bDHm*Pv1I-9cc zpo?EP_mIQ{zw7^GVnodbl;_+Xz6UM>h0YCo2LG)Z-tJx{5=twqW%vTWOXW%hpf4N= zb1>BGZQd0`HXMDZY5PtvE<))aaV72F6Vij4S$M!@j?)(~<$o&2Q@-vJ9x6ICrP{8Z zQ^AOWPU3yW7I`th8{cQRYQ9aeKb&>h&1KsD1DXxn`tFqSvGeftp`mGn&!&#? zV@~-6DwP-#Iu}h}@z1Uo*$>Xh&oHH?5D`q#BZ{4f%ExZ+@c-T?TUpP%9m_68&?u^` zr5@{#{e7XmW1L8^+4MwM#wUg)nLMfL`LJM71+Bmhns%M7M}ZSkBfxAIyA!qqZUS#( zXMXttGE}VVj{Jh3uprCF+tRuDmIWpC0t>fDnF?-e55C0+ya#Q;=NXS>FNoLgD&uyB zixYQDaL}7)Lj!x>NAs%kVMY2?C1v1qY>N6|=)f^CVG;2J-!g03`Rg?Gzqac@Qn{A2 zmPe9q$$4&@b?%nQJAjCZ4`q7%zGM?UYPv=-eBtxC8Qm#vRS;z8)Ovg%>;k+9Ga<-n z&y8YtK#T;omUPLu_F+fzi~`^PsK2?rD^OHn!Z{3E&0jy{nuDo*MX`cLuai7jA* z$IrwoUs`WYT&khA)~O3C-xM^aZuP7YCgkx876JYY;LE_kNmYX~r9gKWj`zN!B_bL( z$^t*RD68#*-UJa=1{}`iKAkWlgbs4|JZmPf8<|Z&aXFtF@DQap-uINy3@)0O+SEED zWH$2VdwVRI+@+@o+sMJWUxKRWPrd5`-bq*O0dS=7=>3=KVJ@*UIOZts^F4zzB8Zj$ zhW+QnO0sZ(rp;%{yP_<(os(kRG2Ruujjt6iE)Vxu*c{&n*F7Ha$wo$ZY|c}{*B;V zA^~Pax}zY-HX1k0$*HknIAI7pCb{kLcDp8S&*A^EVKm@e8LV$5v!^jYs$$OSWxxhC zsJ5F`dGX%X=oPH+u5;=1i-68PHx38k`_r_CneLy9Y}ZIwm~2aAoCRd{NR2@C)EvF3 zKj4kC6e-onR=a+iTspp6viqpp;`?6|%?k0_Gx1(ixhJsVSKC26nUQq?eu|$eC|VLY=ZyVhJsq~Mn1p3ryZ2{G(u^O+yHD~o!~ z-`yW3>hH0t2km}dD19O#`LR%~QJ$)WM z&X79=#n?$DrY#d+Q(H$ZzA!z@)7_uxsPJqMG08R5;yf*76pa zJ}5JNANj)d1Zzex(T-r2;lb%w-4Hc@!)j?8!@9tY;5AIF+4eakKaiO(gU^EwdesVQ ztAram6YZhMCfScA!RjXye_ArLm>9~kalQgC@R4S7YQRq9^NpyW$;c?$EmMOVQatzx zA$`t+FmRhqP-{^yPG!iA_e`@!Oh%GHE<`e}>-wah_fGdVRhXA+y{LcZ6;z%pyVbX^ z!{W_W>6BMpDRM^|>_y3uFev(%t|Zk*rJ?$`G}5zgxS;>uG|RMDMYvFhEd5f)!>+-5 zstpZD55wA!XW6gM-10uZcxG3`^xIPPI7&DB@Z8nlefIWl00P8Ikc8AW`~;}4Ra0el zfPN!9PX>M7-A@eHap?Lshw+`fzbmpD>lWeuG`^J4;O>^*#DU3C4#&U0Ff{%*(~2aZ z#fmHAFnQO?C)Uu;bpctn4HE*Kka_siZ) z>E>8dOmN!$0>X%{m&8dwHI!t61uu3-VtHT@=N^_E0o5XyCUo-njG;pgZb^RzgIQY> zNsX6)T!Xp&TCQU;cNJ4qS@6P*;2$~d9{~dIw6G89%(!kihe7tyZ)xKvSPl2;OmOLd z%f5aQ8MlR{=SJeBkM17V2FvWXU$Vw*88?lNJOua-&e5&_jXhW`?Qq-%#rx= zUJsE&d$xQ~07Qa2M4%=ZJ=Vy2(+JCD(5gOqowWt}TyZ$Lj$g9!TOgtG-Spo711RZ& zRi~cT|7|0IjBJlJ`pZ0ULg=fLu zgQ0uEmc#)-kG7TAU8{E(W_pVVcWG4H?WF?HdgqDw*d^8+fIP(=-C_tJ|M*Ijd-PQ3 z;&sWr!o}0%?LL3qR{dT~v6pgSR6RUl9gl!0E+su>&D>zz%_a#+Tz-3O?7qkpK1)^f z%TZxX!XG@uDPI=zNi{sZ(%~Il>7z%JFXSoeWga4|W?e$yBpd`9?izR_9`M-?HTI*9f*eNHzCz9b#Lu%glP1NZ4Zg`xza}igRADIY!12`w}6m) z^DcnGv}MrGun^&W-suzIc-4_+`_GXd4|3CzmjV8`MAFy@Qr_Sc!qG-ex@a=Nr9UYZ z>+qy~@fXNFWQGJ5*NELSbs%IN&vWOMLUU}I{!}4dQDhQX0S$y80<0Mzg&JuD_87?{ zj-^0rVg4n)JD8PYTyN5r_3k4r~X-wu|;&GCas7bqx{pL z@qFWLf|hEadj&#{!_Tmr4wJuVk{^~?a4)N4i@?zdU57l@5ife#IYs44tW%eY!P{u8TL#({_FA9Tw~gXV8I% zFVr9^WLd4BM$EA@qExbYuw9bJ&0f2_@$q0`Cn6aj7vqjX5B&|7tDxv7B(tFyTTp~$ ztRj99l_wOhvaCO9qC7o}(G++;!?T4e+u*?GTZzD9YYEm^lW)e=!1C$)oA>5m7ZWjn z-(VgwZpD%hBL<{QG=n|Y%6+5FU&&)0@5i{eAP2mvREevFF3*$#+QKKqpR}Ea_K~3q zuLBxR+wjY?KepBY;dJm0b4iNcA%{lsSu;#=sWIz`+)5L&>hRV>LJ%-mL+*XedE3uz zqwX6ap6P{8*WSW}0@<09aD|5ct~-WR1YS7dpb6hoM^1!jJO6-av*LSBI_;ob8p~ypE{Z4ENNIdFYPJHsBq;~dm>rR#2Pez98 zF#n@Aiw}DT8#qwUFlT8%LMcV}qMTstMr)0x#gR35hZp+yMnMOY>2b)7iOE5mVKSv{ z=GTN}Vj2jVat-$1e4o6T1hM2&-~(=B;=*niX}Giz=_ZH}nrUwtG>=nDDtXyk$8TC1 z4rw|SIS72jaQqd#y6GK_WP~4;zJMv@RrE)9S_`H=A)5HZmTlS98O=6*VlG?brGGd~ z;VbSNi^9Y)rLFcs4uLNv7=7P6?DSk07TWsF&l8{ftr8}z!QO>_;Im5i_pD)L60`ov zb~g}06Br3YV*am(>H|CsvuF?kIYaM*=Z52{ZyOb3S<)MG=ED z3?Ylrmr1uXO)ocvK__DNSS)BhQuKA5wDBzCZlJ-!-LHu-fMxe2Q7sl|WIiRI7>cFk|#EAB?^n_b_PxM}7%9Dw0DWscnDWCDQsmT`g}A zso`H_$S352XNM$5wp{&P_7x9zNXKpkJu*30JkUzF{W8v z!dxua_*m2b$DNn#P!?($)@|4$8*I+~=U;jHo8ljM8OC2KP?lYuIaPl1Lv5NV)Bva* zcsxHF+q@ua*Nm(f<(Q5frJ*_#A5@+rdOK*?81d=fO7YeM=~ppfXPnrxmlWPj4_=M_ z;rO!x2kp4C;SZ7l^0Q?<-OsyYtG9-_5BTM}|8N9pc!jDN`=zO=8VzI{6`>-M5i@iSQ3{$-N?D)K0~5Y({0O*%X1;2( zo)h7S#s#uu%>Q=qFFy$qdMjIio0NE;sMP^m_EFUy4~Uc<5LWUt8gaPcPF$NJb$xlL}iK$T)qY6 z*}^vn8lK+>))QvmN{=11ArDcnFqSreR9tI~yZYwpk|gF^+YhZqal}g~ukSfMUsU<^dg)M?kL)Qq&)_V)? zvW2Gx>d*9Zdq17I?I9I95^J@JLD93X$jU|ihIHO;H#Hn(o_lP7Zed3{H8!;nslx%h znThzj&$#|S1JQv2M-8BZ3U_&GQQrz2VZ93Xi7n=$oDGA3pnm{x9qFMoZWIw}&fWYAl+ zFa~|to{0FoR76l21h%E-KqNSR@vAetr7v|N)dFyPO&hye0|{YXzRjHFR-JWjeXcrg zLpgH01U=WiYvM*|zoGQ0ikdNVp4&YS6uY3!G)}0~saF40ypCdDs+1rO6z|(Ai`Ozfcqx=yyFez*$_Jdb z8ZK%1!EA~bc+CLoclbJXZ+&S&@Cu4$0=Ir&gd6$Ia!_2+g87D?b;y0MwH|0vc_M@C zd#NS4!}aAb#$8D&J+F1g72Z=GGoBOs>?X7>`os|V?V1vI0YBLV`ZR3?2VPp?P6%TY zcE0vye|=O8{gSz_TIyOn{UfNVx{B}rc_$u5BBWG3!O+f92e3Wf$;M_7^CTsqhj$-a zC}Ywd){dZScOX`Ox@R1v#^|f4Oh}HR5e{PPOT81JsMB1Hh)1*m>v+mdUcL$%_?iz) zZEO)?#!sQx#kTE!_i#F|oBp9-|05NMUEm1sQ5ioT}XiIt``)44|aGx216ce;zeOIvqy^Bi7;V^Z(+baSmWGXoo)(>3lzbl}>GSZmnHFI}Y1QC&Z7Y zcYE{2vX-q^`+fCp?%6syOd|rj0qchE8!32il4J~@#Rux+qJzDQ{}q0Crp=f=Hu0wM z-V+hMJ$HJ=H_O;N^^_D%?gfEX?|c?y54CVzh5>ZH?;KTO3g{jk7d61a*foW?_~C;v=JsLVa^ETD z_5B^ZWbJN5QgRVhYk-vRj-12oMA?D*cwhIfgkJ4y!m}CJIOr4yYnni9Rp1n-0+o>{ z%zF&rmBkLeO9$T~elY;LJutD#0Pgn>Ihp3{pLL+@>eEyq#B$V90#ZD7!;c?V1w31i z6sry}c;*T#^~l(CbmnDUn$MQA?-l5bkz;@^nFpAq_RtZYUdnu-#26gf%t3ZE*O@a5 zQy+hr*xQL?_5=>M3%5v?5&xsUub%OvkqA}Er)-|lMUOn33g)eAT0>$n-?+KH;O`dtWzd^R7vXo&`|%h`5PfHgSH045nwNC0p3x z3UBoEemFkgs^BRj8;dug`i0sCt{|Pa|9KqdSzv)8d*uIRz~?vl7Mh6Tmh?pDhnWC% z3~!V6BC3DBX%dq2eeShPYt4os80ho|uu9L5Fz+j(z<0LvkZWSfc7S~gt^2BM)Vx+{{Bey)z zjR2Ipd_?h@ei@4dNhp$4qWu3t!Si7cCe1!t&Y*;LDCaTnm696XIIVpKIlTGo!=Ca0 z^)y}+UwHi)3|kn*#p8xEwsI$(Z9#hPxTBa_v|R^JXm9x@nuw$bYq7gZD0K3w)IZ%f=uS{aYrK{pURpo)yde`Pzf5q3JD9oBeJ#sUvWRMMzaLGupoyPZ_QG>^^X z9pp37vDhw=eNg!;E8Ask5zY0BaNiW~RW?a#t{A943(udKGTnECy()ald}SxB|Ce`F z?SSjVuA2F`qXx>q=Q{Dnoa)UY?JfHp$Q!q#I_FJ4tA1{Pt>uUb#2-UX9BRKKTM=Yl z4CXz4)e_4{r{+^=g3pQFrS}^(z3X91`vT?_gqZw}>mV#c(XB9rDZFU+Khu`F3GdV+ z8!0pgU3Her%Uc4)D?+TKH!w0N10DQf-ns*Lav?qSD?lGfYQSf;_X=ayWc~n^8wgx) zTO^d>81EQ0bxCuc-0I%;gc(xT66p*eij)d>E-1-?@8oRcb)?)F}pnL6F!Aa%3SG~4|nyp*eam8;EmKxoiu^l}+ zfboR9kRjy{{q&-5wuFslR*)={1@!)-{W-$`jV}9;(`F<0^05I0b>v8& zUk?90t6vXIlcynRv{JmHA3ofF#d1fK5$cc!OG8}6d-kfrc#ZBB7N${c>r5e*IwB)th(c(TXp zp=C;>`(7C1oXxE&+kP2HAM(^^p)Vfx;C_vvju}_~xygLary};PsU&?XCJ z>Wl3N9y!t4bZQI`7GA2dQmW|yEjB62jX?j6Q6zPk{C7-EuAln&hRUg=w0<$v+V+kl zFL}wfmDAg`>XM-s%RP+tv^jV&D0Qe2A1ZX(|6I*(%Q_G^Q4eKv%sDgfmgt&=#&IqUd|+O=`mLHa`6W-*4*wx%uxMyV#a(s}O%fAk3yc z10~azcs;&OVYC0Ry59@$@q}RHY<=SW)@Z>MYZ6S+H%)gp@dO@{?ul*bZAykTlZOL( z&FzfVW8l5g%Lqn~-u=r-sIbaLdrl51|9c661YG7)r{_B?f z20fm*@@OJ3-08tRZah!SSg1X}&;;p;0W3IyYc6o7xH&4Tz{J3J zR&`JTF7NT@LG?#&x2X--g+tBgwcW7)4y6aQx5CQsXA;GKe`tUD371uumx2%2K;--L zOj?%*2ZDi|u+sRDxg(jCRI&p@*mm~jw2N`y>83N<9hYE>ZV~?a!feBh!By3CJm0gg z#Db?S>nr!-QuF5A#bCY+mhP@KScsOHVQ;w7|)Tz@qC zIGr8?>zk4I=NLDmDZb;0-?*{LBXU}AS>SCLq!vsFl zS08BpR!N_Cx>cX<>Nn7ZpBZ#@lkx^AKT$(#w3T)PYgS9~t*(q)1#uf%Pa7Ey^TVV| zE44yHq65wucoT1yAd2PJ$(MB>LTF3YlM?2R$^f+h^TP&4SEFcS{6Dirw~zSz|BelR z=SL*q`L!9^q|EQgp2c`2_h|U2Y*BJMn&U3!2xc8Aln}D;H&+uLN{M(h*F+^t2(%Q)q_*Ffh0GypT*21i@$ zc7N^$kMB`-KZn3hbWU$(=nKJf%vabh045l`wN>{Aax7amI#IscBHOiTU9_*)L&h{u zGkQn_OjPCUUC`iTIAgWs&_R$m#f<-bKs8}h0cI6O5brDn+>N3%ll0l_SvfU&eGCPM zhyPhyBjL)q36AoKT@eLz$wc=?52CeG%&Uu@!I;YHWmf7ziq+T?5UdssHRm zMWyGuZi(CZk$VK?E5HhRPaEG)iF>B-na*EXglz!w8~l#_VhZQe5nw+J(kwW`Sn_Zg z9oh^F?)DB?Tu6@8Z4y!z_jQ$dpq6dn^wdmYIzfQ5Dw?-@h!HOQ zn_5D}%RoKI(!CL;PEq)u@@n#|^Me*> zgAMZOhjpTVrR{lSdEBoU3&S0%+OC70qpxJW3eI>0=;&-ZV6AMjRd=+=Vk;3UTce*K zRq)L?(j##II%S#HbXl4fAp6(lua)laL3!=3-q1$#mK$}eKW639eG-~=)sJ_J z#YF9nE1tJ7$aJdaq|*W%Ayt4e%?eb*nws>3dG|R5%~R#y9tEmK!M}a_GjUc*0FJv} zik`+54wI~!e)WVa?FFzS0eAzYL=Bv5P0ZqP_^RptB`_3r4ZY^V;QYB-B+u2e?zSv~ z)uY_jr|iJS%pw;+0uG#&rd5@(vU>FVRrxysV2p8V^k(ycAXEBBij9h(Z#4E^Dc#av z8|t$2wi$H)jVzb=$Y)p;qQlCeei?i2N#mi~j%kE}hR&PNM&G%UZtd-{ zr}4ac)cxUu>TdF5LHRs$cq?rc^eyI`(C%B6Nqa19_ImmBwCqwFp<~n2VKe6Oe2m9H z%SkYkE_v18o)SMpFQ^;Ary}g>^(m=dgGSR9wdZVH_UKc!uDPa zvTJ-d%BJ!nbMVCmgyrk!jdD`^flxfupH&E5>|zbcJ2-0S0sOf4_nK&CH0X2|yJ*S0 z$$QJZNWJz-`yK7sfq>MKB8>_jB9EOQcy!6pgx#a_<>f20w;oa?*v5eCnT7cq=Og;P z`vU~xypBb-rbLA6t;pd47rA&5GGX4_DiOJh0a-i?bijG|L zK+vmz>)XQP@%rJ*qq~MoGS#DHFEWppu{0PR^xHAac6P?g-utETw2`L(gOB_!gP7D8 z#cxSe{}zt<6IRDtuXIE_1ZvwVu!{d)8#WB*`(3qc@j9f7pkDnWa`7Bo+lCySS-g_S zh^nhyLCjo{+2iZ>%!~r;-|c}qz`P1a7t4sT3^5YDUzA(Rm9_@y)*`lRosS%7`!O9` z%D)*{+qd1HFR(2tAk%`c~KD;Zoge+pHq=L>9x5(Bn-8Zp-sL0Lb@C_z% zMPGv;Oe?PV&&-fV5o<+8ajsh7klwSG(L{I`+K*iWdt)SbQrKQxYxOgrknwCBr^iVb zGq*A$xi=jIgfnCIUWSc-o2z`f5kSl6UR`qw{aD&G*(Im!Wj!07s2H!W^~2-fd$+Qx zf9Qn>Gus%mI;GB6^JXJcmM=1wzE>Yg_O8v}jg z+Q_^y<+59L!`e}7`3~OoHw{e1<^m6t#HzK`l==IWJ=S3XF%Y{QVN{mKkx4Z^;-7%+ zezt#p;j){#;IRs)k$oU%Bg~hbpt0MmMA(&gKNJXaw>OOgb=iZyMvA1s_yg?ZIk*3#Gak@=lxzVtkEUBeASn%~zx0A7~x-fQ6Jk+AkVX%X6b{(3$~J^3|> zzgDTt)IV|J6w67-tquLfyYta5$9~n-D_8b;@}Z)>2W_o7-lO>`eTMJDsFJO}vw}=u zY5JH;DRXYaaQ!Xww&-S;Xb}cyx+KE>)7o##MZqi+Pm;f{dLEL zgSLAzQyP-cW=)A|4T8v{1unJi&5-uL$8&`D9{E`i%6hlOrW#&mmZ#F>-pD=G1?^lGDjrwjl%UY&-CwI}hhvVwJmNl*Hunc9 zem<(?zMER9wC7t?{p$p=!XR9MY7Tjd6V3g=kKo_dXjhE(a}wIx_E(;IE1FaPUC?x0 zUA(H1s@W_e|Kx82k3^Wfd{zsX*-5kdM4UCHyT$W^L(6r*AtF7p4F4FS zOp8>!bj|wmz4xxe+=D1hMN_O)zv;=t#e^2>VRnNgc0E2+)t3b7>Xlai7A7truoBxQQ z{L6M`Bg(X6Xb>)d{P_+5 z11UZ=sJW-6-N#}E)W9kx~`tN_;q&kk&Q%+em@?n10KLj`bFHMm^ z6Il1q_0>45cu9xVb;itg5k3hiX(TLW^>@H~x@C@S1kXh`Oxg6#&H@w={c~-tJ@s-R z$fy050Bl;1d3+*OReI@%EQRCcktl4g5!HSB_S^Dm`u<=E7;dSi$mgpOR2nuv|C*gJ ztm_nW;Vd5R0d)WnSW22y|5b+WmI@&q=EK5_Y3!Yvs(!7PLqte)fT;Neqn8tmU2D*o zrke1V%FXnPB9J4GwQuvcf5S8}GFlF$&5)sd)yzKtbk_4;Y=;vEk)2S6j}6>fW~r&$%)@zPO=R-s#aFSRi3;ZOws3*z(TDLHevk<%Y>)AdrXmMY(q ztH4MUX4}YX5j=ykKpu`mR~JzW`iK!|)m7el3lgx4Il+YnmL}3sJtFxcGjVWRjYL+0f>;^@r)3vTWI9B8JriU%hZHmi;(scQ-w-mOSw z$?=+Ld`(#?s}1?6h#WHePj+=>drwwkqX>OBt7glTq#>`euz-@uF68IOTbJ;!_|UQb z3LDD$Avd|^Ct~k!Y4cNb)8!9p1)p3rs*XR5eW|Tq8niVDqCElGx=pCkxT8EE=}~IyMe-N;K@) z-Tepvx?e+!d*3!)o1p2yGID zzmy#!cwZBFR~8cRI5+IG^Z$qq^xu##7g@{@wVtCrYio$a74P1WyKu*En6_96(CUgY0eOuA|Ux^*7qM-Hud3ss&cfCkm!acPAEdeP!R9_^z37=HmUwGvf z*w{%XC%L~xLh#^yUZl+H4!>#x7TIjg5rD=HNj;8CYF@8@t_~uAig(MNWK$`P^g+)*)1@aLtIw5PTm04@LFJtvxOZl!`aCL5uRjau3yn zyEI248Wz_MM`HJO@qO?c5y(CPb!uFaZ5yz3h$9k#uDI~M*c+{X`1mu(KQv^8hTbAF z&=2siVIj=&6;Dj!~6^uW#d8#8^XLW2rPt9GK>q~3G(S6|5y-vO*Srgp4?xN zf0ZmPTWTTHZ+As<;KTXlGhZx&UmR_fw7&kg^7QeX{kP0XK zPvP%Hamr?!T=uCdnva<3&Q^E;+!V(7+eV68JhJUa=;hJpIUSSumZJpF??bFhf8=j~ zImX)5%2d?#93(A{XjE-FLPsQJ<92WS3CmC3nV|!#Fl+NTup3;q38#*?tcR+DqYx?A zC2Kp->wo8wzqMYSDJK&;ETLpUlqDGdkFvh=L|{geVNpO+IX`;z$?4!VMMY)a=c6N~ z0pXV9oRF{2?~cFh`C<9ZAl1OOy0O1f_U6C&L|Jlde4_^re@8@SglJx5v=?JXTk^shyIeZsCVBcYkG1CBFk)gKQ z2m&|B#K`(N=o4TNCL+X**Y{LJ9-B5FMB?EZt0K3VRmqYtv`yq$noM#^WjG{5C}3}_$=6AnK>g^t*XlKA z`^G{+z)yVoZAqZ1sxp#4h9^;57z+>8h+`)+fc7)FJF=n;81e1e4hlQR<&s|?BEN5M zaR1?O^uG62h&r16fgE?TiHz9fjDV8L7nwi9)5S7P|Gi>NW!MUz%-d;R#9vy2qb(EH&(qgQDkKLc+e(qVlQK0&C-s35hm6W#UpWnk;ir8(HG z^!#$p0u_{T4O&S^v?IWv?s|kvmc@pm8FUTDB>A&o!n_voZk$4+Q8*#|N(>=EHb0qf zQT&4L*!LEH1$!YsP(3bW4BCK(}huu@1%GlQGBlY_`BE zD&mT#qD0(LAUIg2%eJCeRODs&E_ELFmFQ-PdW7W~Tpf1n^xP+n~`%#+kHd+ezA z-Smu4j8}aN_}NAAKsxpmLf>klSgZlKUG6m_>w4CnM>>!p_ zR{P;)L0V7Wf7#2YBDeHk8)8K%7oAYUf*mDE@-fSOKmQIG19qsO^RTr=blowGc>e6> zgya6r1D=-sKtWJzvad5%+_WUHufo5&=kO5>tHe|tMN!P}NyT<7GAEAg@Uv`a_-^}p zy_}6g?5`w7FgIzrJ#|$H0`xRS>u08pQpzScMKmw5b1$#LF|uf*nH05p)bfB@*g`1T;! zSA!?V;{X67c2OqK7bcJs8z1Sa%<_7~6D6rGMH=ZT69+-crh_DrR7+iw*mrg(2AQSL zL;oo_dcTmm$5(N$k^JuiqVx8SVP)I=9&e<(LDXvWzWBVG?YT+967 z8jEPN-*=>$UF=wtfKIR#x>2EynI$g@iIQeloI$}U(-_kMPPjg@)OVwS@Wfb^t+Ei5 zo*F8TCfnv(h3GwDwX@{e)hF|J{nNV!p?a>WKWbhrIMv z{>`*3dZ(KUqDObIhVvZcbxj1#Q+}yKy|d2*FQV4S%!;}^>kwUWy3m%phga}u2Nm|3 z!xE%)i12Ug6_nmzv{9DAy)TVXj{>5VB7+I-SOg|WlH`30q3dO8rfu>gj-IPINE@RL zHRqdPFvqoC6X?U-kG~ukYHo6a8cTSetAdN);^6oUpb7sXzo!*7Y~`tRP_w_w`}_Q- zuOYJ82g^*TG&=eE4Y33f!k|CljRac^hQJwm>^V0a$ z#BRqEglgxr#q`fTt_f(0LI1-SUx||&&jnMlfBPU}^HaW^0}y4+e3KjPXLtqCr)`SD z6fZaP^*U1P5=+uObb$r;k=wOzOE4r82Dc`NVZp>_CJdEAXKBJ6WDO>V!(Q4^iA)3@ z=^VXluHm)q#ZLzf%pk`pFpb=!2o+PuTYfrz#u3uXX3e+12)Q>L z5F@lyuQsq-pv~M75TV)Me0kDjg0A6%MN0#mj)47I_D=F>Y5$zBwgRr3=WUSmKe1I; zp6q~3#PXuyZ+paX9MrWL9a#JitR*=!2i6Q4H$yqO8;k%dQW&z4B819RNE&d{-ZQbJ zXt`JIcf~n|m%xfKSh!NfybQW^jBpKFU#Y2c;{((&o+Y>KG zYmT5|i3tk#9aKoHmk|gJpOBZZDPj%#h0I2-^y4cLL{17LrW4;$mw2q@{oh_(9IpJ+ z9iC<2V%;Ey%I%5GQ5Eq*m!%r<6?h-Jg9Wo7+feprT+=)NW9bCr!#&n5Olc)j(O*YX(tE@O1`^<#Gr*l*(R?Z~ym-I9u|tINm| zd9?Sms(?Rm*&?s^;HT=hyy9DB@6(jtDU#=IKVHjGlV0uUDZKn6Sv~Tw`9e8T>a(gP z^}+h+@Ma^mV5n)p3h9JC(_qL{_p338F~tN);CWFL`&Txqb(;@<##*n!xw61NHmxT- z>@_3vOj!5Wy`3nwn%Bn6DnX9yNxo_`y;c=x+m-X$V)ZCT_3HXvv#+=Fl|9cbp)nYd zB8_i9zOm+WOBv~s!@dV#O%5Wa6qVjKqThXY5mEOVhq9kcE3)H*lR!eptV#2`Pu&dv5Dk( zd8U6hCf^1iu-d^gpYaJbHjygNgLX5=3(8aynTpj>i3K2cl2gnVBNEj0XG>nL>gc(U zSu_Rkd~Ov=^x_iGG#pl1l6@9(d2t*c6Cixjb0h^3lECe6XWdfXcHawE-iXM;`X+~x zUWO;!qP3uazngqmkhBd;=)Ny}WPt-dE@Wdjd(V8)VmP8Ewm~dw z(#y?z*G>OQQ8zP6_v14!#=c9KvmkQ|qpU;9 zpIX1KZ?4C{AiZPzQSKw0J!=Uke<0{%fZxgL2usM{Cpu4VfkNc&>M=RkmdqV2utP2` z(Fo#@);fg1#|}Ed53+-Cg*A|q4Hfnd{8euv;AT>0tXvkK70rm63sf-*XID{LocZK& zwc*41B$8c)b)$wOuP1MnQ-%^Sx_KfbeJ7*xtH%!{qQiOf7ddyE50;G0eVsp!SScI` z!u{gSOW@Zq9bDf$?g#@WosB(}bLZQpvIg9Gw(rLN693xtnrf3u(B#cgAnvw48$^Bw zK=;Ek2(?FXOKQb)(Ed|{whhXgxY~J6A+;)#SciDeK;A_bdNUGAh&~+K-QZ5B!uue5 z!&BM+EKK6Xbzk0bd_qn_JFGS2*0L?P0d2nCh@qlC`?L+P{U;57!KnEVSJ2Acw@W#n zBj;WrMU7yBWZZhZxsm`?Tor16ruT8n=XYdj5o3DHW_I?GZ@t1_jipCwi%v_CJoZ?# zQx{~z5c^5+YY+1eDth7E<12~!<|70yaA;8RA*No7kEj~_Kj|}YI7ot7HQ~!iBM+Vr z1m#^DcSG+!`_n|bP0XlYTvs3nEQr(jxPJ`6?0J)Xk9;S7YgO(`P&$RaC?RMt+~#hv zb_~6?ryh?y2uIgs=3mPcJZ^uAVafc?#J(Ka7u@>e#7N<@%%-I&$|T zr&2+#la9FU;r?vn9L9 zZl|=@hv>Bf;&cLWQUTG3XNpf+BHS09w^F!YufhGRT#Zr1wHiZnEGTq1j-R;Zz4f&} zKn&{^h#Qdw5~2X+N|gE1gLotW#a zTtavvLG)`ge~9n<>~%wv;pE{i$CR=0Ta{tFW~m0?{A!;stJy8zpD?M9r)C@58Jma= zs_-S$N@=}d^m^#@q^$A**T_H@8}kC<^M90u_P=2UFYZswWj4=t@Xsf<*jZ7lOCbN6$_JMQ;3nd%51vIYYu&`LlML_#HG;le zf@02z8}0@`6RIR;Pt1)_Vn$IUm_~>_|L|w>6p|lx%fAShkf2(Z|1X>D6SS;+ss6Yb zb*Q)KCPl8ZM_Kf4bLE2YpMM0h1lc}zy>HQpEg5<&7V}mdZ|tLir6E(M#|~8fV%gU@Cq!ej)au=I=y`weK2Z7N z;OO476(O`&O%!r}Zf4_?cR1<4gmx}{+v}SOJLA-}BQ3Y|>x12eW60kMcFBgQbo`=>a13k#bk`!iAqi3b!|b- zB6XfHi}n-#*Gpu&f7jWYzmR_UhG*XPyvIGOc{{@V|+nIslgp*J6`*8MXDl zNzYY5LQ@93BTQ=_xfxfXobFIsov>dUc$d@)5d#+U+iri(dupDq*u>EypQjpcJ9ZnS zHGCmn6tYo1zemS_e^-;e!OYwbx` zw#5{#(gh5Nas-v)Qa9lVx{LxDcfJH@hWb09=K&`3o3j zZvmlE>Zo%uNFL@b7XW&N*iE=rH9t=_xxVhifk}-)IoHmoN07hi-%IXj7xOodJMg_( z)V8B1z4stnrudw69WIMtOhzvW(4VQFs=eBti@fqh|b;KmT*5?0%miTJ(5O}YT_Nrb?&GtsogRT9uK zm}@5MCKv=XMKRv;?T4Oq{=fb<027fbmZ#~26(qbc=%W=m^@;vML5M~ttm0LD$Eski-?s(g*>b?DjmxtOkcKU};Ld9RDR)coi! z^M-7Z0KtaqY2P$>YT>;9Y6i9IwyTJJ$HfM66%w4&ff16A94RUhWtAoavRH zX07@SDI`FMvrEw0b1S-ESUrlzL!Z*I2seGz(utO*5Zx@JaBFM0KZkPQDQ`tF+1+&| zPaz@}Ry)oj&TvPVe(~ueFC76g@!KudmQ0!mk140cE5iKONUTW}W3Ge=70i7fr>^thX2AaAmxLyr%!aAvlKcDf?`>>BY93cMp!P0CCP?<;q00 z=2oPaPgbdlWMk=IiDd45W&TjfqZfQP9B=4ssqaFnu10wTt|vZ+Mi>v2c5JGqw6VMJ z4A!~Hu<`!jLGE$H)4HVGW6F3uCIi<$Ock>^z_8L-^jNg|`>}3BMyjwyw(ja#=Tf?V z$TVdA`0oVFFzvE-+s;t`{OX{XFZWp* zFY#i(Z{j{-)DoYMF~4Apg5Im$1OuG0_OKdqHQ`n61N^32&Q&GWrszSjrs}K>t7}@9 z%3W&yQ4#}@2)l0FU&Ydk2c>TLPmes{S6o=YzP#>j<#I3mlt3{Xx_$ZG=Aiek?r?qn zh&~5uMt={pS1*1!}}C5~cL8&TiP-M)naf{|;xDlOpMh7RzB-!5y|5QWb~0-P^eL zOpO!<&erjHBNo|2C~-cM_o7mk`cMir<)BA~LAaiNehdC9*XvG^8*=v;$}J%-NtB77Zwzcqpj61HWt1&1q54#cnFOR8_B_tf`sZ!S9XwsE_x zJWFIwukEwR6@*5cz|hMD3wYJ#%KuLTApf)zi2@>*L#pnQO%8Rk#RR*-WkT_`l1%PA zaW%wYZH7YM5LcfDE5?k^o21*BD~O1GK`GfIc`3cjuudJ06nJ`FR+o|}xV&Y#6=NS#P`==H`&L?(JyT85PqL+y7jw4O(8r1*Ddz7Qo+4P3N!bM| zA5V3041YDCykxvFrA{+b-?8&x`g)7~zqbD_+0oB(=-k8>qdLK;%K4k$-x$k!T1s;s zaGWkdy|{W6DI%e%rC+<4?-@GApO>G!Bd8UYipajw6#hJrNN~^6Muiv$C);b;>!+?f z+kWryR-ejJus`816qey{V(s{V$_%*Jy?N}D5)ZoL=lBowb1CGUK87ZTaql6o9H*?B z?-@k6^~;BrAjK-L5B^}oM@b->|LCImlV`ME5T?*<1Rj@{l*%8do2E=SR6gp;6!Cjf zm%JUhOVIZWxYn9jGSF*mrxouR4SOt6j}T??BmMHRlp*+$fampYJ;4%J5K}=%A>@hglTs`!M(#t}6 zfXfo%R4+qX$d59-L7JAw_1`#6X?V`;H;<;AG+n(2EG?%~@dQLCbx%_62A<;A9eKZU zx*j+J_F(d`5g})F=EKhri0n@1Q{8zJ-i#RNqy$&3vPVu(Ye2h>i-7Xmyo2T+CZ8h1 z)TA@4KGeUPci%Vs^q~CMr?2_byus=LQ~?{z!nCdzzT-qlcVwpfQxH&H>*@49>S9S1 zD;1S6vX*%3@I!d#7pexX^N+XsI-(5?r9M=g_ts=E;~K!n5X^B^07w@8swPSzFM)Hj z6TKdIdGYJ-HOkZt9&k`qQVKv#;BNeO8JnfP7#M>yG6!{zF1@a3F1;3MP#4=;pK5v% z3j@F>bDsv2*@-n15D~fHVHA}!66?G<=SH|2mN(9@5+AG;QqeWT-nUzvnDi?r%TO8c zU&9{Y>3W?v?7epvR%M3IG`WudBkqIdDdU(G_?OP6svMfOyl%@Tab|6ReWxs)^-=FLMjO~ zQvolzy#}358<3$UuD;N+e>1#ZhnSizcM{?z{NpD+-gVIpvOo)#^tJ-x2ofdAm>7a8 zF_WEgi~&_a5XaRRbF*eiM!)$tkbHdX&%SiffITru*dX#dN(5pTw56{8|Dgt3#Ao)= zPBG5w;OINPpZbhf`s{B2uw%;rL$K+l3u^&)NwNl(9rL2NNgbd3M&7aszf?emp57{3 z#goIgqqAT+e_q8U*G zX=bu75lg$RM?nrq@vV0rkWiEd%r9HeV5SbLhp^QoceE}$e@Se(L?W}xISnzRHQUhI z#2~@cnqF}Ar3iixDim%A!yKUrwZl<_T$(oHsaqp>}Em+7&p_55{NB|9fi77 z0Vg4O(=L!B;Lxf0xx&&eN$Ghk?zkd!bW>V3eN&{qO?-)1ubrq|1;EYb5GLngFr?M} zq{*PG!Euc?r)4GJ%!%*@;>ZGfEj(ZJ^1_(d1A@T8@V=95j0Y-`U)Q#rQUK$&2w5WT z@&OSbcr|2v;WQRJhqg$$G~*2s{vxrjSR6oEDaZ7)OQwK}r0CV&QFOJmqXh$PnG6Hd z*9kqKwUOohEyZ84&883OknTU)%?#w95p{*iIzL?fw$TrUc5=C3wHS9k))wJVzKhFV1( zn@st`r=;JU6;=#^YWn)I0py-%i-+6a5bu6;J$j~3a^H^awJ-Gp2hT|H|7ytT$*N#6 zPn#9fBJDZu2FrhI=J-(fXd5uQ5u?DN3ck{fD8vmvsQ0p^(X^Z7E-CNtg!s&FdrSgG zC><@{V+U3(p>J(Au1)}>Z>WaO-#F`~?hFcH`Zx(S&S~*9{mi!0#j-w5&`MDkge{Pj z>6L(h?8kR>3WN~($$Bb+=%i62%~48SF$T zGb&GaJ`m)kzstwKBSh1^dfG6XM_)qV|2y+_b9Hb6)mbaIcjDcK6Hy!PNknet?%Xrd zopSz`_&m`7z-_nL)T3gg6!cmCa^MV7{_bmSywgF%y9;^*9pQK@8%v8)_!~GnQDH2& zZvjLWNrQQ1w}hoYo_83@df48XfA^0JG70Tw=6&UURI5rN{vLv07)$;oTjE8j<6 z1u?$H1fdY#`$)HlpD0iL$}Eq3hi`3d}DN;dw#w|j5I3j4}$}!Cc8K89qIf_<_ zjN0cB&J1@S(A~tT@X`+6h9L(fwv)44i9V7rU6QCA|<-KGV?c;Vuc3u{PYIv8?TJ^yCV*^G4&wepKrYIeO;tMs3 zWK#5L9Zx{6?f>O)8oA?0`H5Pg-XzBkEKY%+`TQ>Y9(vh}GSV1!$`7Sp9KMQ1nGH#$EX$>Uy zk7)00JIQl-!Lv_N#W8{f0;c@m#82aw)Yq1prHLQA@eZxvfCu)t+sEms;?jBDhO-^+ z2kEud>+o{u(@+u?wb}_j|01W6aQA>I@GH#Ulr;;VAUJ{<0P44QG{9#a5U7MKLVY3^nqq9 zNBh;zp976A9DQlom-vP;iJrCd2~b)2^mnUSjZV)i3@vGEUj`+{X7Yh!P7Q%_*;$q* zA0xzR|DAHJA{`l(|>F?ob4ssiy<^8LGBeFqx?aobDGR~2(a=`e2rCb2pkQiX1 zU$js~a$~<@Nb1|xDz;YtYiWuMe>~E^LQn~0LBB<=L^nseJakT;mIUWcP1Jzi8w?LL z=l0R%SxoI7pb$5<$w8%>FOY_ulmv=JD)?J?golRVgjs`q;<($Jj;@~l%CcCG%)0LQ zaas0&ys{3Fs=)PkO22I@k8p!Op&pWBFN#)MEs7BX#W z5{ysJ*8yd^-ZK;?FkV6<^WrLs=X<1LltPUe{MzzDLl&v4`Fl!&l;r76Q`?CeEs?;>kA0`r z5v_0$ytVDMPTuj#!9op!@P`V^UNn;<@y<%&u1(MDw*n0^6KV~+jT+Jo(?|{Jk+VkU z4*hvD3(^)B8u8C<)iO`N7R{LG2qRow4@YpY3Fr>S?Ku6zUleW;Gz$aX7azfvce}T= zmE9IBdNr82x6p<-lmovmU%wNG*`|8?Ra{b`YBhYg^+oGRw+3@K8QYm%+`6p7sVg|k zZ~oOO>2=ukxaz7uoaNfi%6n5Xm8+-p&rN@VuW#%e`SnI);}k)>pS_d|xD|GUSZ33U zOYwXkS=yKyuYnI*V&|F8*(Z|apu+L^u&bF1R_t!u%|rs#5LNs&F|&z(o26%`(T+bC zw)o2s3t`y(fu5k6j)BetB3c9$jrlbKm@y?rC(9brCLC=2!N)a$__y$bm2XLV zl076J=TGTk$jaB}T^PEh>*liKK6B)sYD^e)3O=O{CuT?HGhJ+G$Q!}XmNfN#)Z*hgE073MKpTO9(^o7;ke z5!YH9Zf+>Tr-Acm3}I{$%{qHN>{iE;PG@{o$!71o=DYvv2pe<=__$zOyLnxV3x0Sb z{uv>geAbq4wGf&ZcrtP3Me;S2VVOMB)C-9$f zbB9ENemoPE@A3cc4){UUf<2c#NGddQVo1ahJnIwDsDO?4Y_4O6O(~S89R(_a`b|zl zg=+o)Ah6GzlG6T~D%oNjwi#rbhM09gekUN^I(cWodUTG2Rg^vUA{n_L1z&;P>WQP; z)_MbI!B?=?lhoQh2|VJEF*;!-{D|X#31Vq^15PH)z>6ll^^j97ee$5)J7+}T&QfPK z-OJd~G|q$1s3gcR43c&gWDVK1F(H)H5tIq+U7m)otSu;KT&M?^i&L5(PLn7PJXgp& zN>|b%#l%cwvSX-}r)Aj)*MCGp9NGl3NUauFd4mv5fI5_Yba{Me_;ne?dYLB?)ma`F z@tHHBsdc57Kk=?nRqqcmrbvK48*!(ID+6Hv67w)&W{neeM<}U@-D>j4Tz{}x4d7-E z2d(c#T%&;g9hbH@E>PK3_(5Rn!SuNO2-U&$fwAkg*@SJ)`X=2A*qjRc_6MDKW|uS^ zo`5ry4Hy@}T^idj5Rs*oGcpbJLpX9aO6`8B*^bkXTJ*mQRMK_EhFjrQ|DIJ|!Oz&s zb-4-MX!E+d2bR^@=Wo#Itmq=WXqS-vR6YRnvTR+Ujo` zC$h}8Lrcs2o4~O2M%iBI((SEpA=Lb;iXPzGTjCRxx4sy#Y32$Q@3U_o*yT8VZ$2Ri zIe5~tGS$0%SJpdN9q&ND!=-yFKG6Jlt_!y_8k{(Wvq;<$F$@dHm|0y^hU*$=_j@o$ zNgS!>2*D8;(VJLRk2ozk95l{(>NwgCQ)f72|NU;f>JQx-NT3)pS5(asF*r-<6b2W?^h$?=jdKychH>g;?8 zszn1SD!wSdi6aU2O>V1%hc^?;5cvZpI^Z&Uf`6g12{MgcAi4JnMR82bQ;XJ7Hq&Be zP=Xjp&;lAOo-;$$3)!CixzUciqZ30kpAy{)`wq!uv5i@OWjhk$(_+h-ZRzQ%NOmp# z6Gqv>$E(IWXk0!$re}q5ejYmJSz2-fGm_M9hUdK3kCn3Mx(ovEJgkeV zIL0y^7dRi5{AVnFWWFqJATI|yoMoe~C&DmR!97%0XEe!tIF*2)?dV8A^q`&Qv>N>U zi~Joks?!1@aPqi{@lg8H&u`ugBZbHX?>wem^bbPdqo~iCLwDjUk5^>IxM!5A1{<~$ z>>K6qf;`KtN zL_}P*EQ+7TB|^U7x@2@^ID79PuS?d2JHFNyhdf{xJ-(x z4Onq>??QKD6&KB=p1$y&+Yb)Qbl7Cc^a|Q2vEo8cTd`Oncxl4h1R9@9WsK7(d-#08 znZJmh+$tZyTQvZm%*hCB0j9LTLBQZV^3=ixk&&p(_l`DvA*nFGRQvig!heD4X~r8y zh2?$v25}R0R#zH2Ap~wJDtD+#|EI{6iW9;X`wfb4ga)O7Oi7g0D8?3=yzP@iwL}o@CA0BuUGi4gD%Gd-8#>Yh%Yhbgdu4JCVLY| zIHl6mr&6wZH9q)LC?Lmg%sW?nx8X<-`u|5&7^tX;GVFox5eMS7An+((bd$DrIBb=4+>_m0v2n|n@M!P(;t z@*(biYA_2ymyvy!JAYnr9C-c^wl!>wJgA1(5s&&)G&Cx(F7RQxIom51x%(bh z13jECFZN;-0I#sOtQ9<67f7H_y;X7))cCb4iH6ajK|IR$d8T7QRPs)R5C>#j}3sc6RF}=Ra zwuD1R#C-2^_2hj8`_XTgX05X~q5$>>`?Ms-3 zgLV22;MFicB1f}F0eQuFpXGI7S)kNz?8Q6bVa7Wi3lTF9QP&jS>;jp*^pszyYDe)I z_d1+!|9mAe!9qOpTM6HF8wTt4KX`*{0>rlYy);Bl;jg?Q_lyG@h(lett0N!|B*wo+ zOWGHIT)=&sCv8Ba>%Nr6t8KK>2ot(r?(1IzkZ+9I-~YbG^f!JbJ9w79`KWT3Yxe0g zM*2D0cE+*Rqv?kGdMyF!$p_^rP5cru2b-J=^asM~iH~Ib6~0>QKmHG;GHk=}VNR%h zl$XTL&0v}L)cw7=TGe8)1huJwi|gIkxupH)GV+C!Z_WVR5vkJRaakX0CpsKOXNUW3 z|73l9KuzPpYax1HIPsCj{rfFn9=?uOdg98(e(3Jo3ZQ2Z>~#g=^q#Z%oc(t@Rh z!zDqyFG0a~rk+_g9Fvt(C*}$Su)rdOW?rl_>w9!s?MrGe`u`y)R3vkL^*x1{qCn*~ zBQP)41feph)XQ(hQbwk&s>$1pi8$d?GbMp13__VbgjhWebuRMoe*LgbiR;fBt&i2G zw=(L;7OCW%N@A=$RYMIkx}Q=TGlr$Ad6eoO(L7xgeATMY!$b%-OGmPNeWU4C)H;^N z7yR}f)qH&!tz607`U_X>H=epq)XuTuMVi=qPAybm8Vo>>jO?(xOEmPMu+7$gGD#0X z2ToVpP|65ncFaNQZ9+J%*yBs|#Lx8XbrE?+t9lmHsVZB|qgj1NlJS)*!FckrPt{kF zybX;2PR7lpFPo4oo9Cfk_hRQpFqcSmM&B8`4?z~Ep8fVmfc|=J+JhG6uyDVAj1rph z%y|tM1HcV?x)-)E>Lc7CIu7G2U=g488;X+(%O^pK;dqzuK#_XW22HjnT1P62aBL)O zD;9A{J_h`O);7Mb3-kjy9~soFt?pZo*pKX<4JED%){5A_g#;*J{FDs~ zt|fn+0`Xn@3dD!;g^z!YKRPh`%d)$5ZVguaIUQh3`+@^6cMLF;LOV-d4ZG%%rB7*nzM&A4~%gY%usq{OYSS zEghwQ{_`r$hl7h$M7wv{4@?zkq@ja@vf>*Nl1J<$J8+!-i^sh95f90Y5eZ1Ks)yg$hrl|_cxZ?BABlvN*e#;>s zd>L__Du4mgY=1R56}N$CIxuXhw(th|5>u<`=!Mx^1H>Hizq!NdiZ}LnEer;;tXq}_ zD%(6`J0qev#_>-*cR}4|H;(WH$W_*37F1^wnDsN71(O8W{UhA_CnDgNeg=7Y~0ljDV}Fg;m8HIiKQq;i~KXX1q-m4$inJ6S~fabYmH zE^5+QIwXCi4mp?eV6`Sz({O;P&@fqj^(KIx3`!z8NkaEh!F1;i> z@VxM{YYhjMf}Tc1>84NcYLPM~wXTqnyyO0^?Wz3GqhO)(cF|~dk)72_QJ>GlryH(Z zE$@q@7jkvDE9fs>;to4m zGcBx-2PeNPjg~O4fwW!QxPOzk?d1?&^Lc!wvMh_G)gcGexe-JQK}14|eNCyaHvg@U zN#wn&TVA}uO}Z3~JvP9LD4wTLiAOfkA0NbXE!#tSv_^X$l7v&LypH*fe@$xrUM#+z zIfscLcmqeW(8;XT-#mJc9(J2Z7t+*`0(O2PUEA=Y`g1kfZ~XJw?!ev@%;wpD)SZi` zg96Eb$!?Lm7zc)Uo8~P4H%TJ#g#55f&KY|NI8Q`^5|54do!>x)jgp14iS2KX4l6Q~ zjC-#&zfyKqv_pfV>2I`FY997MKhs+(a-h~}02>58aA>QI&StmHOvGCcfXXv22@(DM z!8B!by(3wUCG6!>6<(XmMRo%J4cGYX8v#lI!_}4;su%z*Kr))_&AS1}ISjZBeXEyi z*M04JHW*Kk7W7=KX&dT02MZ50qi+9`Y5F+VjW8*7d%U&spz9Rly6vAh_<|)?{50~) zQH33Rb=&-uwsCR|Cvcn?-%q#WfOd1SS|ty=9o|eKKiUV@L*)y z?$Ke6vHnY*mDnP|42theIzK@~YvBZvr-t>U9mfJeO^D*SaSd;_^4ntvfD#SG{k;A2 zW?J)1ijQxCY^krc4ZIc6f9q2@_{U7w!aEA?K6o@(@mi;RSxVx8Fp|vH*s_u&`HsWU zZG6^%o;eQ}KNO^kyeWj1NXf0cc2XO@FX2}Bk12bcu6M4f{BKn(b`3XVme0sY0eGJD z7{iZjVeE=;n|EqFAkQGcYqHkSYLqXQKt~MNso-Z?Y2Hsy^M3@tDE8`54>}F!1yquc zsXv}rSlqq&4*7+9jLEoXgnCOXzA`}5Tm&xnn5=!xL?P2hMoE1xxEYDeqTHdbEZCo2 z<@KWv>J%JxTsyueZTavv(aTJ@oJx<6R{p5e=ivVbb>F0t?>K-_h$APdr27$Bt&s5+ zVL&O93t3`6DJ#l2MJ1FBV#m1rJd90hw3CWb1^)6)1bx8OUf%@;FpQm<%|hPAEk6Q# z;-*3k*|^FMm{=aIzf(y{5!!@>Gv zBI_?1mxU=rv+L!tc?y+=9Jiy1M%>Dus4Co#d-LcfJCiVwrrY0oVA5tj2t#)u4niap z`axV=rG+K}6rggOG`q79+3) z?J76S$DHY3aXsy^*FS1*Zt*2)ZJSiLa#l!Nf1eyE$Z|+|6P9Ce(4JiUc2&!(;?bE~ z5s%T)Se9|kk7JX>{z}$!2HNFD-rj1|l>v2@IWj+jVr)RuA64kmw5S7kODYF^1erknmDa0QIQ z-^C0+bP9DUJl~5$z1Y{dEEPZG5+=8*)H-%}zAP?$HlPH}$Vhr5t%3jN+KLm2FlOO5 zkpC6J(^^~Cf3D-S?~aB152ip_zqEjZQ3g||wC}R7TyqnC6eW1n!4u+U7|MIon}mUL zANsyV+CWl&kq`o$>#kIVQ(gUiQFkMiZS?u7HPI=-fI@)_=gTTFXWEjU;z1j&&qdW2 zT|zm6G&0n%7@5c7vlMSIOJbWNp)J(~fT14>++NE+4KxK;F#vRLv_EM?m9hb0e76MI zf79&%MwTdT^bD9ebWZ|V-d zeQFE{&k{P=JzFIy4hy%42+yaW_Fm!I>pa6E>ioEG{OSXFemt_J=UIN5rP*mum9IZK zp57@>x5J*#IG(by+#n?w7vP`+kXtw_f?{ ztmBBxEKdLR`N}HuqbhlIj82GYa+6s>c>U&I{p}mJ#dfM^)o=Y5es668O1w}J)5`4q z55fBxVk^(I!|SxKeEmdpNDbFndHtt<^ZkG0|N0}>_+v%?Cx-Db&p6tVJ5xGvoAmv1 z7NCfGfs{O|}k6%%4k(mc7U(t1?5?`BSu&rXL9!Ffn zV*>MWsMR+0OW0AMNmnG~{flZ>EjUj8PX^l+nL)zBwax%$if$_*HZA3(tr!z zNXV~dG#!yQ$`F+bSP7Egcw6jsf$-MkX7_6r%?be+GhgTxBS>QO>OFpMEh5+oX#*S`RpY}5n1=% zV87#MSzdT=FZP3cLrG0}V^dZ3p+Cqji}vTC{e0QJFZ-N1!h4?Y7a++vHM+hpw4|4P zOU|!^GXqiChn$LHSLi5*JTmz&XA*SyRb5KrRH?)JYujf~9s5go2lf~HmbA8K=E;Z3 zVZYx)m(1h!uYpK7oLHl~)8_kp0dtT0g>8MIXvvt2gd-_GZ78UZz~ zpECz#loFiptN7;hf2CFshsORC*FRJ*pTd5=U>}f~rzX%=yGUS^JkMqft))3aj_0mH z5E+;WWJh-wfrJu;RIN!OvC3QK8Dwrn&tWOQASG-3`I#b8PKwka1#0fJb@l5lnrC`= zUX^872VvC-&Dw`RAfzb*-PZsv+YY|F`A;K$gjWNS-7GI79RBN&6OO zWk`OtuHV;&kIWI@qC{3~7mdrsWpJ_I6^EPnX;b82R_Rrl;pi7Fk%!^(*IboOD^Y?= zP+>+c{KkIA=QJseN&{&7r@1r!R2g{;hw%D}wp08c|CEw0=^0j(kpE148~;?{J-aa+ z;%oEe|Fv@E925gHvwi`e8Al%}@9)b?W5}gR#zqA>oZr?ajIyl(l$dl-e9QC*#ZM22 zC`|El%gXls9aZB$Zd8dRlFNX!BAG<=-_m`5m$EytnZe!w@$)_Ha&%6x$mEx29&R7y zSysO!@zY!1VbS69jIylsimEg6KsbC( zcgr)6C#&<>prG6#)6dRmS*RKCXS*!bAUfV2WYl(VRtvG(`!l=T4XG4z1l8or`BF_~ z@C>f|o$n*v=}E+4f3o-92~4LA^*=fmUwr4)jbttU#{8lM{Sy`2#zgZy?3R{KNBrQA z{5D5mb@8nCS^lul20~_)JYVKtdul?E_L~4F_ztH~1EZ^o= zE)X~}8H?oPgzVcR`<*}Y`-U3OL{Kj+LgxR8{69I0{9Gq;sqg;{?@_xKf9iMwxm+$C zyM-Up=50lGZ?}H;4Jq#xv)LnX&!-z`iZ}I(LMAN{;oj*;j-yOj9Qy>QUG`u5vy=+QzAyW}><9O!G>b#Rr_1*>x2&Z7Q0ZaB{ z+5*&m-5=Sv^?l_(vLA!sf5E<8>@O|Xi+%0CrTPn4KNWd^iO-;a`Y@sCZLZD=GM~OF z&TIra68~WmX@&31cX|Gn9KY63o!;!JUNO~Mew2QO_As4rZw@5ky+@K~2QTr$t zUHD>a)+95qli{I^$EY5?M2fAFoQUph>?N9f;T+A$82oBEC(BgB#_0;LE3tBQnF9EH z=7O}}#c06NJ^GRaijGL-(0mRHn!ZF)II^H5irD%5vaT7O(I6D_tpVbIJ)}aiDz2xe@FTB zh{Q1L=vo#qeW+Zp6J1jVG&yT(p?Sj zNCAcs)^yrF^kU3|x60DVPH|ufjtBcjbv`~zlk5_pU+Vc0%2v_74F>RH-?m6}^S#7y zq`6+;X0hyZuo(M;um@bAQ1qyWiu5qbvYd>t)MQT7p&E=5@E?4}1hWaop~woK=jx?o z1Bt_;+JD?e5F+IXU5O4sP;MESW2`p8mYUzLjz>BFY%MIW=6QV z#{sk>=Q4Vpl$h2fL)(8LsO~U+o+YZvvF{@nLE^EF&k_vF@;$e&p%BFXrN*ej`wQ{k zA~=bM&*RG8n9|SC46f&*CP2;lL!8MR1|>vL>dez3&^!yvjQzu*0~b)YaDo)M|HJ7x zdJA!oKYq_;q{qrxkx$^h3#wna3w`F)ZNSCDG_KqaAWOLaM*l=BZzKxHKkiVe3yhw4 zl^^rQ@A1#M|$2-0NfWx4_uK^9%G= z*$_;2#IBy%KmMD)^VfWw`7?j(YiCxKXJwZ4$Y2%(2-EEU_tRginda?L5P+84{r~eH ze~tHJSFC{ROx-Wg9^kZ8Qs-#v51NU&Y|ig`A>^n~g*rVEK}JS?wSH@b8&wquuqHfJ z4HvU7nEO-Tyw-VKR)<1+_w8=t9)q3tqmp(Vn2W1kNoHVS*LmQ<3%Fe>g;oBYodYO$GnkIR#95-M}%Ua#4~br1V5|MDk8PX(3>Z7MIidzBU)3lh;Dr zF;=S7gqqfctRBoQKEOwd559Q1nclkUcb14qrwva3N3W5?0AA8$X>L&J*{v6(?#CJ( z0U&0DrWyOS?UR3qaDV8*b=K=(nn`=XzH$9j@)`T~g8iOiYWv7Wd$_WMP;9BTWQi%C6$ zbPWBzyV_3|!|la>F8g)0Z&&-8mhBfbk`<7l161jxM2Vu4qy7SPB*Dx~uZezeN=nZQ z%ke%MMR^t@iD63=2 zgzQueyU#@5R45AL{;TJDwZG#hhpFtwzIeL%zN@F6UTjcx)yXNozWhPMcS;Z zt7u$nzb8d-RKtLiYe1pt9$4E`1*bFN`xD!qTedzYoT!M<<*EcxfQ?@QRA zgDF`!wtZXn&1t_qwTqV!+~~ia5X7|A@M|tA`9I$I99O_}Fb+gx{*B+a{HXPP;s2hx zWb!&5H~!CALjDbKs2=BoeOvbN_kyGX6Oy22;vhnnd9u6;UV;uO#*@;&wNu9?f#}va z{Ik;hu@y|N2e&p**PPtOrpUO{`|E&0lN)@p~sH-xQ z%&ZW(N6s)MdrTDaV0;t*m6y2UnJsYDimnybEyyM(5mA8RbC(fb@K4~NP>zG#iLdzY zEBRft;zgl(c|e6RS_2DaadW}t9PoZo}TP{wmbo%_jmmKdX?Gh4?Nz^^TU3uAT4_5 zt?<|Rd>*f#)6zVScfVxr@%Fcw$H#x`KaRKFKF_e@Rhq?3>E?dPAL9P}BB_OHBu6}NW* zCW1rplk30S?{;IqqK4qVfR&o7eoOaIPua1`3!d|d%5A1Va-6YLqYyEY> z4`SL@AW*#0-MIg3^$LTtzVEV6|D5&T*hl`Eme6*++8>OMFSUs!Px*puWZ#$l zFg(C#Hi=3lIciF`_U&c+_Q-zT*f$p`FiPF@pSgdjjaWqS!M?fk{PKOrYFY_c;kIZ4 z`s~QQ8o#m+;)WEXi|PD2r>P`xw%#wAQA62X)dSfli*Rc)ayt42`#QEGFt?EP=<0Ff zfKvs#+SsT5CMl7tt5K5U9<@o$bJfm2`4?;bvp>szcht6htJ$Be#%@y|DBt$`efMOI zSP!$z>U*)zqbhA|BJ)vnFiHk$qGmbzE>`nwm0VPEBH-{d!8Hi9{2Co`K4Os7xx3|g z84GoBylK{xv!IsZR_IK9#H=NMEv^j{h)?A#szB2(Sz##=o^Ya#67ABeag7#QnH|2N zsfj7}VON+5)$-A$>cf4=zTSbZY6UXaV<8u|^GK*-?wUs?cirT(A2iGRXnl><3a<`IV^gc)2dFEA|<~$jB=S z#*BBbDEej{gs^`bbNk# z>)W5+{`NdSeEs3icMkFPmw$1@FYs6IdYHu*pl9JxR`2}Mzdx<)!>^cmzW&fY{@BdV z^MkK`_58p5<&Xb||Ad`+9}UW`UoF=^!1NE`WW3y^LM`cA21gGJ8uhoN6Uq+Hk9NWC4|v!4*L%4K7biD z?hNK4+>c>dp6G?j{|SSk*szzx{KRaHIZqAP;FagZjkfp%;~NX81ZcTn%+C1s|EKM5 zW47P6{UGQ!#=Nfkd0A`i_qESHr|mf{hf1|2HDY`zF(C~V1tL^M5CnuGFQyP15=cR5 z;upba!m)xOg`jBC_!9YK)M#uFA`qkotd;=U_8jcl-Fu(C*V^lOyYK7zkKu>$`;9TL z`+lCa_o;F|th4sBo@YGso^$@^9CM7{3jwbpt5F|*c}D0BrwpaOuLd=l#TTFZ$6r++ zMdw?cOSwu`su@zl9X94~s?k9r;i3F(8TXHR0=ngWyC3BQM_~P;jvz(}v~5kK3j>B+ z#ApvWW6rlO`RJPRy-LNyOu6S)p-{G2*W?j)o8QVTpmC;)Dj5(%6P^E{x!RrDGJjBf zgs|jpqYB!}ZkiwTT-|sa}cTMa#6H%#Kp`ISLE^Y-+4hd zhP52Rsgr4|fF|Oq{W^K3e^GoCNrp)GE z(ZGv1uiQSD+}JNq#&1L^)7d~71sc5~4llfM7~A}0I}FtKbu>-?gYgZmX-N2)4_0O3 zk1`{+_|YV}x(KztyqKTF$2R^x0rvH^%|A}&3efyNjSs=s99w*dWv}?{7S$IX_fL!n zN_%K^{3pK8H}UJ8bu)gs`0{-h%+I;6LSQ4@%#WO_D_gGjZ^pP9(b+jRNc?L!kGiwln8{m8%=ued{)U-o~fzYTmBz zDIxR$k{bJkrb9@NP#EP>&_^^nd>krdyb~rIxHKOmxmBY&)B`DIS@j0H>JY*P=Pl2L z{d@-w3s*9u?j6RA6?LzN^l9YbLMT#nM~gNx+f`Vv-tbiKx&k%B{4(dkT$1~ z|Hc33&&U-ikbz@VmYF5j3{gRlH%$zh5x_xhQEc%W>78=^l4SX4loIPu-@!;CnYw4X z`i}Nys&^XWljJ|~$6jgR#>R_ii?2>YN`p42{lVfhRda-n zVSJ+p3Xhg3S|n2YKSCF%;Hzq_@n;w++oUBLVhC@^W1qdj!PtYZogC8-i51en$5}ie z-L_bS+(Zbfv4}ZH9e2hLx~j3aZrOuyLFhRCjTsd4wLs!hr!V?+fsdpRS+M}mWMD3sP+sN_%-p)_x{G9mof(oBb zCpPc!ING5J4Nuwj57!vA9Vv~So5>P+K+=A8$VXxj zdcvV4;eGnNxI0RN+^Q2uKZqO&8Y^pnT9dG(<{CM`b2qo8M0N$XDzgV-ZJ&3o+SH*? zNRVKsG5apo`OKdY2&|hf0+8wHY2l(-s%+S5W$?n-TNK>rq!0U-{=TpFg%p`~I_LGl z4H;MD)zfv zNQ8Tso!!nYip~2g!8`?`ZIP<03qkkG^>T4{1Fn&4osP1Fh;*z*L!U~IdO0%hAKoEx ze}5kcgI^ym<~Tol{^QSoWS8UaSt|sW?naop67}z_{fuuAla#H%%#4V69PeKhH&HdYc4ppej$Zy)+ z-}0aNtzx>>KkEF%_rCvH+?v{tKjQf&v2@<0{Nk(RC`sATc{Ei3vnc{eUqZe*>2$2~ zvp{v~2?Xb-ZIIc)N|)qBSa_}^_$oKG(8+&7t}M2lpLM?R9`28(y)tCndKf9|rC{yY z97}5a#XORRK$O}Yqz=|6Ud{h}09t<(Pj==fa(LNB>ywF}%HI}sX(xYc4XKn#6X(Bs zz)0qYeLuE0^nO3Ou{M(-c4ymYPy9MR8Gkbd+0b4(KQ+(f8Mu5>l)p90Nu2R5zTOut z0TMU&@6kGIPmSO$e;Xfo9G@(qyjor&@pIokC$JV5Cgh}9j(}3`;Qj=^8;!D4 zOFDVCT3oiytC4m?SW!wrR~e3_*{kz)S9wGAq)hJhbn4Rg#Zj@Wz ze)T9mHuX*2R^m@2IXk#Yv|H3kg4XXIFzMC-YXqJ6x{DPl$t5^9U!qASJu`v=@m+1Ip8Z_=u@p;`={@v^f zOP-%we!j(TeXQ+$+LGwPrL|;<;$w@?^LfjkmH(stsZDGionMAMz9f6H0gK!Gus)#p z==)2>NzKA8F|m<7HjO|u@%rife<=RcPiFqbvCzuT*(eHljNanR|GY2qCO^U6D9^?T zNn@P(*vJ*LG6E<4 zHdbM=v$uM^z@o0&UN|O8-uXn*Wp9vCV)>cOTN=Z@xfDyoD5BFIf*Gd}DJ2(*vv2aO z-Cr1F1RAhabMM?qJBf&cCofODLiq!<&hfR*kKD}Y{p zz#}kQ_wG_O7BFCwJ;U`bjw*$rwcJ#3prAsSFU3#M+zpu%zujat)1t(r!D71+wLjA~ zRc!d8OP38I7)V^beCbd&4Qw;_;K#kQ4~pYmMC@A|Svt#Xt%ACkdaAVlJvWF_ zN3)3&qi5E=m zs(C(*C=QUd|7ZRo&;|jKG%8U$cKlGAjMXN^-AC0Ew<~so90L3 z?=L{-zxH{Z|KlElZb4CAVElGSC77lSS?Q>8zuKTOr zIfaU89WWx9Lz3g^D*|g_Ty*Pvqwgv`b~pT#r>a+U)hc|&kVV^>v973EUv%-ttzSAs z3DK{EVqMg_26FMNuK4r$yTJ_0-I0EB7*3Amc>coY9(T{c@^c(-V8FxGUVi1gepB7Q zINoI9qpyEG*E9E1OFyGs?XPcPX8zoYZ@??mzX3ujfD;_i;xZ z5c5&2%JE1O2CCHA4DM?knQO7&8K|2pmaiyP#7MVw|L&fXY;JA~r_Vn7p~2qW-$#Ci z#3$=6=d$)u%&hq*ae3&tt=zf3WW#A=hr~ zdtCWp#fLfM@WA}Ry`AH7l=5|d&G@C-RDk))ySfPzxy@U)*JRsPRk?}ZfcW+HSR|n5hk2umuT(&n(&_8_ z(M7P!#y}wUY|(?Jo3Z(m<9K64978jQ0uw)ha=taU%p@i(1^oAuS{@g?y&{{@6F`g%0u2g164 zNqmb=8yP|D^7B>$*>4HL$7fHjrKp zIj*~2%HKZaUFu%FCw`-+H%B{ptV!Iv-@n-6Q|VPAt)}>;_9ItrejeCGTT{3MwAOa7 zzTs$+v`=~0+I1w-mPdqd1c{1&^K7$mx_7ff){<^^G)wHs{6C4`F5cxq*BQd75<~I( zzaF~ZPtvU2`|%|NPemLHGWY}|qDn<+d-I=$Ln^jdgX$cIDbi~iwhlxHz>b?b2vzP2 zj@KY7-Io=gzrGp#1kxJ{Q_!#I`eA#!rUV_5A{P2-+kq(CA8I%`tw|i)i`cqR7k#=} zB8??+m6!D%R_`=OaZ~L~MmoSk9D4ef6HV9AA^m0pv~*-8_~`b#dB#y+K5m>okdd~m z;;Ta&!X|B+K88GGk+=w7sdI)dNVoCF;f`CTQ8i{E)_huf)AmMb9z-7%k^?;i9y5t2 zv?6B3=kD+b347aCCpA0t;1lhy;+HRsWO9dHZrmG;Z;pNcS8K#_dzZaIT=OO>#rN{@NNs>2{yL6xY6+pC_g zmFiL-fH=x@mW&qa*QPb0XTDIfqHVC#yImz6NOh76@)Sw`WGVoQaM@?CRNFM2Is0dN z15)=V{iWF`JpZ!`ASw&@;%w&|eMO|==bK-oJkCh}O_IUPEW$^BryIC3N~}9&)6{2t zVjOy3J30BYzCPyzRl_aEC6;$nNWF4AJwx4ZvrS>mAkl3?3JwM)kkq4F9>h%@=%-pG3)lGoo?YlRx z%Q%lUfwxBVCX;^XZPBHksItrL9)ak9D=KpW*a zV(#}j33czs{nft~mm{LixDvyw8gJ%tl*e(wTO&h)!`C?S-MiN%nt8fA|LE5~2kfiY z)^zVTdC|5*qQ>$Z?DGemIrKi{pH;uHlS+XP<*lg-?|g#gEHY%GuCsfio5wY&5HTU` zq*9V;eWf6rRdyQ`>}q2U8ZIewvp!XXi}cevOf zKeXHYaFX|~QEu@iD9iZmx7&4=j-R*jyZW}rkIlg4|EKr{@_q${Vtj00z5H$M(fO}_ zv-3;T{OEl4ExzBz7w&kNjKUTjTT>T?^Kbk4bhTmS5U2Z5_`~ge;kbFTV6$B*o@?Yr zT%X2bwNOJWIv7F@!y!w}`M&h?ZViNJL?l-yQ(BiTzY{F;&=zjI9+6`{wYiLSe1jc7 zAwIa}O;6I;5rBdWF+beouk_a*pwliKv1t=G{-BhzZ{xQJVcG`CdlEklI7+7h?SK6! zes+8|z+-bdxOtxvfV8=~bAA-xJ`mrw_-2*g(lcdK$(Z=|BtGX~$7=5J@qzeyU)WDE z{zvhVs{^}98@uxnJL)vMY-%p6&`5xLzW9kDs zpJS77++>q#84jdpsZ@)=61k}YZS%j~FZ=nq#h*pNL! z&X4wZHZ>mL^dr}Mk45_`T;^@N)<<$jJkqI3H7S&6ES0SW?c7|PZp&>xZT!Us_l>oQ zWL?;VYB*kdE}$Qqqe5fdP2GgcMi;}!hzsMtUQMn8d(D-mJMHsGu~3QH`*=i3Mxt?5 zZOb76q@(O2NMa^uQ1PMoIbJ+DiXl|cl0#`Pq&SIFGLgBxO7}jcY4e7s!8hy)T44ra zoA>_I2`i8i1*G={*h=5%Q?3x9K6gRwA1PjL``;V$huioSUd1PjVIGCTtnh}wjMG~2 zaZK8Lh#K`3v~f!|>0EBj(l{zKq9oZ=60p5RB&lJkjq(G??{ZrmC*nN~C`hUq`4dR%7C{lK7Deq>*$2@j0uuIP^*&C@R z-<0(+3@4o=K6de~JTT(V(^_(Iup3D&)opgtgoYxtMEX#~6%PccH7En)Xof~OZn};) z=g&5H6epa$Lu+wwjJ$2@7z_%pHRg1|rH$h9Ouxt#HIl_gaQ5h%=s>!g(&_RR5jKIx zLH7O;Tun!^2u$42H!`L?nP+~AP_>#Lfw5EO{&8y^XfpQc3`b0yIi>;8z-4NmNMVY8 zXZ50KRdw(64QY8UvygR!j9N$5yPhgltacmB^AZztkgauWG;p`iIPcfYTr}M$7vL_5 zIMXrd@_8VPt#P{sEQ13Mc!AuAz{+*^k$`#Es@a*JGxPZD&z-*Uv#m4%!^xjVK@H@p z_U`qs{n3Bn0hy7R#}Sbc5pn&e|F;i$Tq3T&|F8SG(~Ivw=fmm6rx_0|O^Q5`5m`5s zdG(Wy*Khp!Gq}$}Ve|@+$Mv{GL;?|HmmYxIsaRBi<&iKupH2@C5BG21sw7-ai>%YV zdHn_Y(qol2H@s4#^QIAL*hH2#g;b%aoHtbo73&?ul`!Zzy<8twf5vI$&kJ_IGDXC;1+ih>*6Lq#&1+xQjI-MdC3-|Z$40!kUZACqu zPIq_D9vV6J&S)4QW^PnNoJ#&C45Zoy^wV9)jOCyp=b zL)!2+jg1n1u()pQLDG??c-IRSnZ>$gLCZ>`KSP+EXvMDqrJ(8B(!^1Jm};s*>NwKc zp}WhjkVCr*ZqepMnwev8QrX^50>F2CdXL`5ZwGQmC(4OOsy#<9OKGym4|*Seokd1; z7`2auAQWy(wq(BPKU)7-bN|M$r5t_G7LR-43pX*iCpC_|vF&K(3PA^m(bM_kWI{k* zrEbGkYNVBLVV%Eih|WjxeT`DbN zi;pcnyP;cp)$t+R`lI-{#n-kqF>eGk`O^5XR;aBSf@dc>*1~IIbtrzRj^s9eqXAOW z85%Tb@EO0(&ztzz;@cMAp2o)(zkL_I`?HL%^V5e%vc)HV99myQ8%WsbK|hZF{_*1j z>zgfZr1&-eTl|zDd`rN&Aw%lrrj=K<5z(I!Vq zNVg%T6dD=z5yqDY`{Vedsw|`r=U-mT$PLI!aV3m?QpK^M!$}R|1_1i!4sGbPnw+&Nu`EG!lx9n^fa18 z8t9v4rhoUR_Usf%h6@g)g@>i9ewwJ1}4cef4L0d)gLh72JCe^2k495zTk5cH? zWTz`;{_S8d2n9_r+%VS^KihbjT$>ETs@3e`V(A!S^MNj0OwB>&tp`Yp@T&t{H-a=8 z!*V565ecrd;df5_6hyg8yVl(`!^EExPq#v@QeJQ5vT6V8z1u~jl-^fAe8=D8c73;2 zbh@}A+$fuFGkt|jY;-%sbu58&~ zaZdI9mQ$5joKntKJ3lwaDSddDF-SRbl{iy>zbO?Azfnxk{^@M8cH_bL)92XtPg2c# zAH4w122Lrr)1~QEN>tbUk|K zucS_zEIymDU(mYrvcTp{vVcRz`__Z7&i5o_U8%i{1JlB`@t0TEU=dxoFhR0<7fUr= zhlG2d4?>smaCBW(ttrq~v(qP-tkT>l`rT5@7VfOAY}0}&|76O{q!P%jKY9>sR!^C zS0p{jW(KU$xYzq1{?Gr|!zB&`0+~l%uGh>wj^nf}GXjXnfBcXBzyHAB0Dti_Ic(7u z-8{Kx=rAZhwc?H;iBum3y7X@1xR{+Hj*4I6llA|Pe8?^$>n-BTug9fDNngKtm6`W% z-@SVKTDPirh{lpK!JusSiVQ(1)tkSd&%l9)Dt`#C^_6CmboGDVU*dn$*Is7gulS`m zA-e3=f73@s>qQ$I&hiU$ouAoM?alGfRV|W`6?0R4Ux-0)tMWB=#qaLlJ$v@z-MhE+ zT05O1uBEJhd3gBj^Un>yXFsst#7&=_8|eRGMEA_PLtNJ@Wc(QctcD;mb#2Wop>I+Z zI-~Ciy;Ob3DrE~1n`;#JKZHA zdN-(Ph;$g*{WdMP-t>65rRHuYIVs9wCWVllo; z{0g0H6)1kU_2h~*Fw=~7d?}&xUCV_y=-$m-$SQq*X?z_ID0AA5pSST%3SxQ(vkm!`c{1F^|ft%ng8p%`fz;Qu5TkVp!v5a z>$}Itw!TpO?ZQ(Zb);@`GY^=KPhVwplQ7GlBEI0IHx|PAD8AEV^-+9!l8eQy{Ox9b z9;x~C`?pn?ho+jboj-j(r@n}ZZ~Oc|Q4891A*H&=_ahVTLTmQ-D_xqK^_}@wTfNI# zH}hYqjknRN<_GN()#t>SXaR{9x@u>)fQ^Ij>3#eTP17ccev z?9$n|D&#o4B!wNaF?4g5n6j?c2<8?Wcg0Z`z0qeT<2Q%)W&BR~5hDZ&ewLer5Mk2)HTjW;@syiq-gclL}=GTt_<# zb$oURlZrHo-;`yecDaf(>N$qoUVF+L^utX60N*Q{|=Il z(Uv4&hcE;V^>J>_4R_E8L5YbI>L(H{VRqp@I^4NQUiJ6ujbiki4p{Hc>{>H*2R){% z+4Ga_qbS;aeZPYTqN-;vK*dL&n#p`HJsSJJp}5-zl_NLp&rlV@<)Dr2{N$Wz|2SIt z__olU?cgj!hv(HLT#e?DW+HFI!?EbZYzH+u!ShG^XK)`o)V_TKmfXtgCCDm+MC0h$s&^wG?@2nH z3;!Whqwokoxo6RJD5P6ls15d}y7pMgYp#caOB~H%X1UA^aCb*8_y6wS?8#c>DzxuW zd;0pu&v%;0T5Z?)UvKQZ9M=#r|9V_n%;I{v9Fd5_3`gJ}{b&EgAN)H%_VY)jv~*xO zkbW}Yh}|iv=I6jAN>7?_v074x2VXJ2@~dC`ssGoXbYJIZFOSHS9en^6ejosQ{_J^z ztgbkY`}_Or<#IiaI4-YWzj^!awb{BJ*N>l{+YZHZLs~I^7&Z!f)XYzKfXX_lDw3n% zkgZm(7DQ?6BXhm}*FX8rHxvFZ|96;;WgO%c##L_XX5ak5{{bJD4&W$9xHKZsTt(jw zD%*JFPIdO-yVXzOF|Q2|X<*ecdPKZ^^UC-twpXuzjI{fAZ{NOsTldS07cZ)B&P;4~ z0fXkJHAT_+VI9fju2$7piNYEmd+g{KZ#GY3}eOGJV+vZPvhrL6F)RR%&)rs zyTzBABjfMhI~_l__}THX#c#Jo2$kHe^8s!!e&2FRrAMuT8ou|&ozvU5bN&{HZF3yq ziwRUX8j?XM8%y0`BWu)9dGF3szbG&@1xw_+Z!o7UHUOc5NWc%~T{<5Mp9Q*tWs&@w zwgd0%R#*?@AYLE`;?BeDXr15ITdc>nP1k3O-)IQz`1h&cPJFpO6*$9&Q*s+0?~5;7 zRme+6@+UffqRpG1j9=X&Qza2@qfy%REEpvKJ`~?}w|8pZQW!P+`@M~?%4xq}Vx*^A zEoF-@ApsMrgz;nYf7*PgLe>l_jvtT44>Rd7ioZ^54sOQ}#m5$3nW7X$xA?imx1S=u zZSj{8`Ox#9==pc%|MNZQekAj^-(MBqecnd}`mOl4olD;DQNTP>`acjKPvYx*wmzTt z_@gJN`1xe~`6#}~)O?)R_?5gao7vglN4AYWM?hQZaxDZ>>U|%I-@G!Il*Oc1OOd%& zglEOs55$IgL0R47W_}7KT@R1Nx(Rh!_W6%}{?%6VqbdL?!4H{4s2IWc)#*9Wv65(k zAY_PcH39;M3?k_|2N394`XgJpAlVmMUW!X-C?}a^blNVC+ZJEP_+~Dsic(BxvK>{( zF0o9FU(AlSijR7RyXbD&)!r%CZ!ew^zbP*jqjW|$3-+8(s^ zYsT}nuzcCVOYopyAz#A$d&82t_JL6nX$C023w^yB30&l=w)6E?;k8C^u%Z%89+TIk z?MmM4!^yo$v|StUQpHCsVU@S|t(93WS~2AGTHg?s#<>5bb|ES6-R<6L|0{lG7uZQF zrC9EKUkKac=N{k0XO_ZW#jo0KG&*PlKB0w8_+(z(>@fzP)II6=5Gq9L3z0lJ4IGzW zL3FSw0hbSG+t<(j8Du||u&G;Q{dTWoa{IoZ{du##qvv(Af9!Vu*Y)&G3{OFqi zvf<2a$tQkpJ_p)A6W;=Dax%Qsa*0wOS?1cB6%}O(IZ+%B2k_Hqkx6fyC{CbUun)?woKPI7Po+@NTjmCL2ajGlq{GHDGtJ3v;+^b=evjlIPwUqSo#E`xMMm(47N$0bk-6EL_GL9*AtR$K7GrX zN2W#M&Ffc*JdRSRsUB=eW#;SGuQCGg>-Cz*x9{H8*L_@%S8rY~voBtKj>$K+^KIPL zWA#McMAo-z+F{-C@C znO{W|2-Cc^*b}3{to*8p*U{|q&L4_F!1#3+1pLjLR|XtMynO!R-Me?kammE>xLl8m z0l)TZ-~Z+}KmF+K>$GP)e`2#xM+2?q{;l&T8(!IZlmlXqkzZMvD-V#&peq* zwb5pQP{{&ti-_eOQD0f4`w_9=2M&3R+M)5gZO``{CTX9~OcH0y@_ft6Z=dAPv_vUD zd6^0-e}L(Aq5MtvyY0#V<>j~K&xVx8vHB!+es1|8Tana733_OowWh?DAJ+YQ8I$G^|<-#`8>e`pWr#_DaFB-ZzDD*vb79n+&;SazZU%%1^#=hLYHr;{-}dScAmd|lW>3_5J`XPyJ2 z(^Zkxr|r!mHnAj*-xJ?%P%?J>g!qj%+45&0XS!bw(YCaEQtY0NpQ9jV-9Z%&B4dkh zd;C$z&SQ(;-9HMMi`N8;gB^=f$`9J^o#(uK^yg)<0=HJHQ;UG^|*x#zu z9@BLPb{#gwZ?sb0tgpCKH{+M9*v@4cNLDKX9b$iH|8ryUxk@G4KPlQk;Bpr@o>2DODBeTTxn4@ki+eT3>M?qgBrOd`Q^bk3S*4 zD>u%j%t%j}iqG$jFX_zzHk2w->t_*c{5U?g_!!`F8-cZFT)$5#n#cG=7xnv;{<(9% zOHbCe`RNmM_bH}`V8`eCvEH-W{OfJf^&g#YkuW={v*cuTQn+6ynv16s_3MXqCEg%) z$tK{F7pm90$S0Y6KvR*|x67!K#HNuQ({^uMpi{YACP162C0maDQ@cI!xW#$#iMqch z>N9H01}6K(RA+p85-hN0jFGzP_}+#VNB=B6h?GEtBg5rV5foCc^)*}kOheod7IQX5 z0LGW)USmN$^AOWSVk6Y!?-r>E>(Mr*meVG8VY8Vrw+yc57GEBq)V{6wP6M_MWH&Z? zj=}ht+tglti;s?g{`z!#rZe!JX>r9vmvN`f_eL;0Uj;Sf(fKFav5$U^y>22Ny zxo*P}SoMn7UhMo%skt$8&SAj`?!;Ogf?Jxc5~6dn<^D^5%Zrabe&^@RIOFa`D`*-k zl40iY@Y?-cChnz#R!dvo@7sqf%&(V+IC7oW5!dVWa=AXlk$^qiKV%-K<&}{*@_6{- z^zsu!pASn^Sd?nJOMN_#bhGRI>(j?S)7dPEtnLv2>=(a*U;JGOI{-WGzi3Y}Tj#Dq zCK5O<@2-_U9C28tpK!fgBiH@?^?E$}m;SAX!6Vd1c$=RnO-W*C^DrB~w)R>bzoEJ$ z=?CIuHzBPWP$j97jyUY}9C1k;twjQ}JnjvCJbYnroSx799`K??c{@ewYh~HWH^&20 z-Gtf`^ZinJN*2o#zkJrp2peYh=Iz`2hkL?DGv2*>`{wnlmoGp1GJ$cp1?H*c}_tmLzd-*Y9g$?*3bCA`ES0IL%9pX!kqWrn@V?0{tVJ) zazp0d_D{Zz%xkX8kq_zDqdKgGL<{S8re4%GL|TK=5#ggC_=GQvVY8f+xBQmkJeA+t*3e^R$_e=(-Kjo}uu-PECvKJLU9WU?+{Di z$1IW!1mEYsotm2CP{PmhbjGGKTsn94e#}Sn|0KTm)9?fF`F?5XHI2XG!?yUs(UN~f z_1peGpW@UeY}vxR{%C$e{Z7q4ZsXf7G=~=V9wEJ>Q8*h{v^tKxz8#(N@FDZi=$e~E!2m>^xiq#WUO@9IRT zy4^9D4n4-Zl%?ir(yU|jT!JMoOGo4_iVBVXC7#IGTTg6#6)|Y*mSE8^(2RlR{mRor zwPzkrTpHu^Du}P1yp6p?`a}2lcYoqeh0xkP(GKq{@*h~9)qmK(!;I?NS6v(s;vj+@ zY?m$uqUJDM+7>D^1k7;caywX;-84{zxucku)amMPu~7bvg{WEUJfNBq$|;W;bF*V? zuR9R?*TMs>$`z{bcYaAF^3!AwLO9*?eEEr!_WJ$}{+VH87z2_Dpr= z`)ZNC)`~{^mzT}!_`3(ST^$}VUM;xYpMZfDKq1}=rY>yMBFWyLX-DCvL%JU6kM_^t zFtomn3%fx*^JHL;hz0Ln-;I}!eg8;2>~VZd{8otH-o1(+-Z3=i>x2p5mX>=(c1GzLT3;m=r4N4nq z5%<5?Z4L|hf}48F>s$KK z#8S#RAD9OOVLV-kFu9jfKi;HRDY%!>D#^TZ9V)n&vpYupsx)CT+|4tW59GiDYE@O3D-`%;Nfboxj>A)!nnx_2E_NXIw7VjJRGe8R3>k998zXULPWk zmh@#`j%IWZX?NuVXH@l0$I9|1&R8FPOo0kQW1@UTmg91Li|f10bYGFV);Tj5`<@i> zh`5$#=;>@0N3J-nC}X%~@6vF-yGO=f^*jCl^vsBx0PFzjM@L(&c6A$dKSoNSuUQb0 z4xXQLJ{NWpjjIYn`O@U|;mzGL91qBACVbu1OE<0$7FTMbWM(ouGTrahb*%m*vqQAY za9MTE`Oo|+CZnq?f_{a$QIQq@g6l8;nLk}R^Vj2w$j^WLV`M(Nd;ak7?zkR5{_&49 z4)b^8;44iER273{bq_fRSF$N}`PKOei+{}*4xP`AoBJiQ&Y#n>PjP3ue@8Dn6CZy< zvUT$la&)xpfBD~54*&4xul_?nNYCpLM-L`CDa5&ii-I53@0? z=94+A{k5MDP<-90iZ5iMn%jllnfUgB_%`Q5$JY&ztqY_0Hu*!^#Aolk;`9EZFK6L8 z7p2u7$G6d)32I_{BIB?8?S;)OM!8Anm+W9^>StNT~+$?vOM zRs97>LDNlNHJ?FgjX4Sl@l5;zZKikqE5Nw4-9qDIsq0~_J=s{4j^Fn*P`zoUB1to2Ld+ZG@1i*JwS z=Y8>MAY#Q2C2K-7!{J?vuWemo;qeteMSR@Ek1c*YiJwj5sn>jL=Vz-TKbn7fADbF< zX{V`Z8_5agt8x2gBVyjBf*Aa^wB^uqY|I04*Qt@vlovB%(O*7F>P5A8S7E9e7+vVH zjbGChvE7+i+ILj3HIKd)5hH5RT9=zd6*ks;cPKoEhwJE!e14YKEH7w$jkdXuC(5!EE=x|%b(dh4Y^YN^=W&1p~?p!p=_R;=9cO9)=u~yE} zBAIoQVsx7fv(K;iNnM804-Y98Xuuw2{l0gq@_ed26R=ILI##M=3O}* zSKLUR*7IbA%3@}T?R*nvvizR1O-o>58)D9-_iR!HoL2;*_YVuys>AwNInDl&fl8Rz zOkG*`!CFRNPMP7lzlaQ%$Rb7m?B;ynu~aBgc1ViVNY+U$RlBbuW3jsqBz%$OROt;> z9H~1{7%n13Os+|QEm&GC2uEUAX72V&f9uPye*2llkw`z?#r0yRyUP33@y-Uy;Qp1{ zbIVa5i{q7_KCViQtrr=;c3k7(dcAvgcYps5IAFM3E&w8q(=rxFR!=Ra9KGEm<`6$b8JzGAS)o9SP~`>WYO15S4hPV00)k1TnpNN zxn7TlOKs414{zVRep|2U>*fA8eR2qH3#J+bwWuo-vZ0YAxWFq!d@s6lSQAf-iEVvx zOR2=fhnzsWcFa;S@Aoii%oMUZ^hy<8+rn+RS>#&lIMUq?k*5iv_8a*Mp6x_r<;LQ; zz2Bu6(3_9vKks)`u`n^RNGWfJR;I-o7+7;Bu!*#5iLsAjtUg!5U=g#{+-Ilh#~9yeP8_iVEj$FFuEorz2w$9=P8$R zTvGmK(eZVE@z2ivHOhz+zkcXUZA2-V}KDw3}7>)w@qi>h*l#n&TL?f z-KjO9t0G|*lGNIqC?|f~0W|Jp?q3^Q@kjCJeP=3S_50K$H1fXq^!<=?!qfP+p-t@r z@nt-mF%G<&cPiiSk5cD}UuY(5<5wZlJDGhNUwwR`1>;c#LdkzNDpbv>7F*tuHNZ<6 z#-}EebSn(1-NrWlE+kp2D0n;6sSUo(fBEEfd@1W$Xnm_U<7Rnk&MMJnCA;b)*OQCN zNm4SPJFm;1E_dtXR#<>=93k`*^?(gWP*??KVJQ@--eOrE*zFI}g7>l}lY@y$b%M~z zk%C=HNoUd#AZ3-zN^K*zLr=(Yxs+zF>W6g?1O>4UsOq^+Us|ITkfP8j!=H%f*n-+)-0d+8(&(WZ}$JaS`RG4s4J`auYL;|e^JDSkQF=} zN6_zitN7Vw{>%D)t5@jmO)xRU7QgCW9lvBM;C;l&Se(wEQbh9hc^Q^}W#%^gTaMqv z)Rf*^k{MxbZ)sg5!Nk}8Nm>miwc@ny(Z0@HU!MC1{e4;`nt%vtvN9*K@h^0??aKzT z;~RBwxBc@dzTRnUuE@cc?-{iowauaTXH-YUG|x%tt#ZWb?{~BAMZf(5RJ?6;n{e9I zNnk%8)Ct!9@2b3WkLB>94Yutcbe|U5W6u4f%4vwd-Q!1Nu)M&w^RDCm$#G=gv>t3T zfu%7)0|HyKLd3FUe2i3*{FQc3LTSJ2`^BKo zBx2(nf3ad|6&aLyW6#p<2&_TgZ3g$^prr(sg1mHorA1h3eKhq;JAQ4!Ji=BZb7j~f z)(_h2u=XL*2jrQi&Np!Uh1~<1>%PUGH%c=}eN&sQ-BNYu+|Z8vbs5bfOs$br8=WxF zag-yZ&bEvivu4o+E25=leW`ZT_apR;@A}zdRl}!akzP3EEUyq&@k6nGX9W|BmOMeC>?$3&a(6LR{l`XRx^c!oKnCnkn^4GOL(z z|LXMhukaU+d!*TVmW>!)FaN85jrTcP_ znE1#2kJqzr;ye&cC8v6UAaNi0lFG+Wt(jBu<`oioc}PE50&8WZ37{YCbqCASrwh{<8C{ z|J+~xka?}Oj^hEiLu4M8*Kg7@-#%Oo>+^s6po$YpCz$m+Dlv3(PYb3-kW@gJ2s-bV z7zZkPWbxVyn0J1-`oiE82L{PFXpWqYq~a&<2M@SWYrpDW)#-uZkbj!ahdn>k@u2+H z+w&O8Ge^9ki6A%m!))RrR(zEi9D(Hh!u%8OjgK+3VC{b9e_l@?jF106$G0s$ocZBR{K_9<^NCZF z-OMtswP$|YZ#hvFQMDggp<70P-?U|8dUCoT;~$dPRn4VHV^m`Az0?nNRu!X;&*URs zD&)EA+)4F)_P?r=!igvMe|ZlD71Fo(kWV2W2#@2pvTVm!M)aQe`K9p-SW3Ra0Mo}o zepn3ZF+=goTOsB717UW?XATp8_VecbDFt778FhT(l^EYB_BjY2#uqC!H!|FNiMExfj&YcTcdS%eoVZYZ5Y|S=siwo!@7exu ze`0e%Yl;xV2V?0{l~UD)v}!83W!>)B=D$k;Rkb0{&JJMu>ILdxZMe|j#Z7a0%`Z~+6kv)W>at+pctk)XBO8SjA z;Yz0z$Vs{J^Pc#^i26Z*d1uoGiA|C42$>jn4Rb^C(R)L&rC4U@{&GavCPo*sYZyHRZSa zlcNZZs z@BO3xQu}Fk{Osy&8`9cno<%!Jn?R~<#NG(YQbA&TYr8;o@|Z^T)Qr?cc89#@p9{}y zP5m-&pXFwiyO<5OEHYQCiuBm0G8M*mIJ;FsBZs=sF7{me2(sg<{Xb4^L)Gl_c{tam zhMBspRcI-7PcxK{mrRdoJ=^gSBlpF2o^)_hzNof!aHmVZ&3|LUTou(NRhQOC=Kcp7 ztfGr*Z~;;z35-ez5Nn)MILU*hiN)h(#+(ktai9LV9TMb z!j@TP2`bjtvD~Yddjd{p`y+qMLS#_;^VzJmienJudd z3(O41yYH`;KU)V)X08|C{{6q#{zw1(uU?M$`q#hx?Ah}!gv0EsU;SDfm;d7LI-Y&} zEkAuUMrH91k#>GI6TdY((=78q!N_FN*7rpBy%Xtv1^{1~Vm&vDtWxOG7mtg`zRZ=5 z%Y&O;uMgAfQqs|dSm_rqZ5#hj2?y-!t7dIW(udCHV)wJBUGd*M1nd-Xwd25Anb%4S z;FGw#0G@+VB0r2o=@x3+Ro=Xt=a$UzpXKa^-; zK-rYmx6icRfq3%@Yhfd0q#WdVI;^t1Y_9i1T*j7?uqRjKOy z;SuO8Ae$5e=$>mafb5QMjcD%Anhc5hzF4K*#Aezzn$Yo%k1f9N>4c@^er%Lci{cj_ zbmDg}Aqt!x*-ZNo;Ene?mI>I;&%9aRu3}Go zYV6jmD1ZKNe7f?@LuKNt5~KJw!fV1f2D7rRmJZ_%-jDO%KW$Cg^It-UiZ3S}#+RtN zxHglfLYXEVrGfqH-{!xhnM&nHg7mSCpO_YOwRgiLKAQi$8UMU5h%(`2q5R+aebe&ToYBhgjDjAk%l#TxVcYf$W1* zX>ePfwlr3xOpqkGZ*2*O-apFm>&4+(_p~STUo{ZE?-(ENjepv2IQ4l_t~Xvj3o*%U zgJ3qDh4v9+;l7o#0-G-2R<~}K3PT?9I3O93qf}K@LOr(i=Fs^3VyiZyn{Ew4!)3Nw z1(PN<(x*=GW9KEA99NK+-@31ax~a+IqTWBfwP#o)iy z_shm!ZTqw0=acwZ;8Glx9+oElH4Bj$w(*PQd~7-&3LUBall0{*!-o2PL6FrU3tL@1 z>O%M?m8dFK3aG#nAZ=MY{*=jq)CPg0$6vfjLuc}HYtrmE<;+;>#gLDIuhs-D>8$Dm zJAV#g`KdG2`aINuj%;?jsjmMzw1JJ3osF7XMO8y zALrw`3%t3QykDlu-}jeWgL{>K)FySK%b{GEZGNJZl=j?@H}{v`XoWSh{?ajIuX?|^ zWa^~fU#eA9saVpyU?%J=t=03W)v^G0k9rdta@8-H8=`8L|IB~mm$T;Dx!MTkV?)#3v4xPRlT|&4crk{`}j2_wV)V;s5J@ zIW|85xZ(HyrG7enyqjDXM&5r9d1PMWc%a!s9PMHj8Tk1>kUttsC~~xr1ot`Q z0bwKQhBBCL{%|`7)re>RSBq!rS(%o3T-}c={4TDS!WTqd^0?MGj^hG&Tn@LK+{veH z`UwC5002ouK~y(Pfy-r>`bW+BB#t!-g`I=nY`*ZOm|k`%DV-+IjH@tOm+KouAOq{} zc(^njxo}<={?x^F*7;Bb12(yR(;p}pN_z`}j9usXF9dpYo2l8X0!3N^xLo4h8$I)= zU`4=9zuVp7I9RnrtdD;-PcP`3jPz$8o$ULI^>4f8fn1q~!K3#-EeHouK1!$~_Gn_b zkCxM}n6c&0i|0SGZ&)J&y6h;%gk@OmHP0>`^zoJU?Aqv-A4)Gt!0*oAyt^@jsTtpi zEkCr|{4j6h%U7-u>Ky+~{8m~0^N!Dn*YmgIbN=`7+n2;|^^%^>rR-cSHX@Lx$F|iM%CmckTcDexxo7+L z)moT*f>FMqo!5vm7XB`HkLdj+_gIwfqoK#&_{2V)@c#JL{7@o>ZsNC;NJy(m@33rc zW5?f6{JT!^VD;+;&*&=WI7}yB8Uj2Zr*nl?d`CM}-ndh%UDELzt&dh>QG9hoh@blM z_`@RK;v;r8fcDQe|I)Tg^!FuJpFf!&AD2MJC*4EY6!Y8n#kZd_K0X*<74V7Qu5&l* z+w$RxFH{sYuv$B2;tztd-(OuwQ~WL+f@ya19t;wSgrt17@y7?^_xsV2N24edzqFjf z_UDU@9+dWf5q{_x#*5*GYs>d9F_FaACM)ML?`!FJ#QWm&zWV9nD*>DME!69`<4KmML=_+PD7}@zY;#_hyLoL=Z)~&P7|5Jj73+lA%0@h`;AZ9R(~33C zj^RiKH)UPJFcn~#hHjlB6^77?XdzBODb~nsfml}GaW^iJ+T3U&S@4(cAIno_Ac zvVKwT;6xHk>b_MHKM^3}6F;@M36?w~tDUIHN zs?vK-N<30;O)6J8aU8NR$QKqf<+svVV6)zJaM|$SxBhLNld}V{@nW2~wJ=FuQFBJR ziCur;T1LSbG3JFZST4S5jM_u(4OIKbHj$N9w%Y1FA+;)(KXULblaBI{Yjq^@-s>#p z{;_TUjJ8JSm`+i}qs{2ZY{^|wybmkkqzSTF5B^Dtyt=&fp*;(xJ+~$Y+n)|GZ z$7k7vX99XX*|%>s`XzT)mB{5AYt8)Y8@%F=fMlbps*w$fP3mM=#F43mm}{Y&Zooc2 z#Uq@81}qT(SY~Oq3Or`DZ_JmSaQef)@7c?5d}Mbo&5+kC$Lx1`JRt9{uYT?N@Cv}o zZ~X=}uMcru9F}=~owhJ&f8P7=KL69tzXrei7Kx$A(~EDcw7>B`jkvtY>w}%1!C?M; zdyVT3jCXN)Wodc2*qq(y3MR2#X7$4KvygSzd0V!=`ip=1&0mm?i~#KTejH#OGfbUaImF@BC-n2^#q+tlWP0bpNdC`)1`f zquOh09`*UT%ucoc4SwV$=3&O?wLZ?Ec0V)+@KZTx{u%e3#cZj`@OTW zM0)QZo}aB!I>KK`1s=}$+?~)wIb}P%y@i&O(rogHU%v7K=%bl$yDBG}|Fuv!I z9#ivthTn(Qo@a-qwVnVU_3rlYDK4aZSKu30uWK2Wqp!c;kA^LN$${wiH`5lm#m96b z9%+%@3g>S33%2-n8^0rrV9t0e*;b#j$L~{pzZg|@v%-;0L383eTlf=SbKCXD8$g#~ zS7ggpvyktNjao>yuENBREq*?UuQxb%rSjW}Z?KK6Jlg*^u zPQFj0ERg5>)k~dbeb^AV7!B=uZ8N@{wNZK|9iPRe(s|h8r){22iL;mM12Xg4Qf74mr`dI(tVdzUD*}#M0}t_A%UmSQuZ| z&XCHXfL~X|$I2AFvw2cfFk-{kI%6Acde?3|vJ|TNChqDLJo7LtQzy#SckcLu z;NG-x57mUMbGYu3G1JPxR>Yp%M8A_ZZIeJpX|cd_lf6^#H|SJols3^-!f(pkHz$dr z+PYIRY?DMBYoqNsf!_aGJN3-1_rLZJR9787&TP}-bCgH#Os-AWE;m!FR@p@R25sMR zV4JPhS8+dz$n_=hkq5g8dbm`!C@g1n5Tf2rpv3@@0Zw+hvA!+A)%1x2^{9 zq(-u@;=Mz=re5m{m$OpF;pOy1>F=%`Q=7bg^@Q#aLp)71!*OJ#jq~H;{OH~W zAI^P#`ZzN-zNx#l-eaa3YxLWA;>$QEW0Rte#>vWsUC&|%TF6((M5ClT;|q%AvY#<- zy#-`YvEIq1ap<4*#&C?ruFmTY7E7$~$`u#zPyD`TAARFvyZfjGQgr?}+~V!$Z$JMP zYdYDpuMNtU@57aOef!y;dj9bj>!aTsW6+0;w9`vlFVHGHH%;nG zT)x0@R3?x|W@4E|9vF?bTDFM$tKqP_6>_6#-dQ4nOWMg)){Uz$L`HziI@wBB=Ppjm z4+H|4c|GEMUiS}2bq(Hr^|$@--LvO+b-lD(^W-Na!%1;j=V9N)ou3emwd1jvpLF*_ z`{!i#ufF|-|Kwlz2RyH@4?n{4xWw^rdH4EU^&K>FzQ9ZeCIj2&GKo9yV@>7_=>Zp(l^aL&gI(D~m0EDK8g9vV62c{@Kbd5wpZ$cf2nAZeWs`Xt|a++EyHo5vg?4%<4CN2a5MHm|T#sl~TX zuDCY3@q_qB8*Kac99jh7je=v=%se^vaTL|!|^+!jt%W{=A{I`fpeK{P#OPknhYq!hY`4XF05q!iigqQMyTnOEOn^qu!l}t%=oq zU+@m?6i+BNVJoQPUzE{@Z`!8wE4?6fcSh&D4h7Q~H{2_F|3JFPa2l81-tU$Fpgr>1 z$A(786(6R%S5Kd(@okHbpA_G=_`)M9M^b#D>Zk$$3Mlf98TEU4{1s-qJIa+XI%Vc8 zF!4v(gedin`bG~i!Z1Y&hyi(}_8pHX^+~O017TEmKLKI8I-{ybgV&BFe{gKRVeJ4IX z5Z~tMMxK@Ce~T{%E#-&({ocgay$2oZc3_;pTIkES_u+^v;TXE8fLoN!8Ha>pxpb4JXrBnZw)Q9O>uqMmIm#b1t66R^~l{z zxHucQC7Bi!`K6U^on;{Ya zv3?5jqHj%TYo{bw(r=?GTr_Pn0)7dph*1vec5eA?rAkd`?&G7ip_2=22uBh-Yv*U5hna-&eU%ZC*ZY3Wat#G(@2I3ymV~-gvDmQ;x;+sLBS?V$-cliaM2Wdu4UU zc;XQDd@!XdjX5`kWFm6+Htq6ZeGX_vcx*YACyp|=V3mm!Uu8B)REur#-EMGMBPLCy zQPoNlu$8kvO{{wX0cnGmYAa?^s->H;!gcV}Sz=6>T&ouHa89wdreP5{=oaaP%E|4% zlt`VNAC1j-tX5U$N9QZ^a2~;~3aJF>L4z4l{12T@mJKk5B=l<(jWMoLz*@roQ?I%r z6rh@F5la|7#@}3&=zU|tcB~DlSL1iY;l(HsRtc2&lWc=G1ecF!ANQqf7C|?#>Mtkl ziSxs*xW0Saq!`$cV}H9UQzLdZh5tpo zOQePS=^0>l#QmEet>f+8^WT8=+}2$w;_3W>=cXHtOI+SXJjk#eI1XOSqu*5dgE+8C zbKKExL2fH>kU#b8H{uOJho@7DunRj=>&hgQQ76plmpI*QA zyCXd>uiw0TcfR`=!13_PUjM1Yec|g32>dC5#%Ul?0J%DTA$V{ekcvWf`KM)8F=f?C z@>ZLZ={B$Ukwu@4snjD5bI*eabRN=2&{`+13ClQE9%Go{9vT1a-~IBh|HE(Saia6# zIIPW0(eRJKeT+}d z?q8r3qb7bfV`E7$|JNBm6E^p6DF(LJRed@n&#P8@V3-YqbA%!Ds8l;LfopTDQ1j1> zVEE{wUYR9wwH6PBhDuG7ZMIE?C3Wm}b3s}PcEcqlO4oQFc_*#nTS#+p+M7(5sc|{n z6n&^@1%;C5DiROltEPiMY3jA6Bm2o?XRi1r=K*hLr&hu;=&z+PKA^v z9gntDZTJP_=g3h#j^AX#q{+?ZPAEt;RZ;%~&ri61u1$fxVe{qv>q@ul%|i$C*kGe0-+wW2a} zB$dJ&6T$bG+n2@{^l+R1+)T*F%+Y!`b>?63J;fF@c*nObK0RbrU(UFBKho?{VvSo` zV45GPdFWo+;!Br`@tNOZY;xs#Ey;jw{yRU9C*#K!pO#mc^8r001K;5Hw9miYt}kx) zPecf!=i^DybOjBjRii1xDV-VJmsMY4XMMnUMU>z6j~G`oKa$T%zWRH9Dpxy^-?z1Y zM#^H7^%vZ@0B)NmB@s?kiLc_WMK*|x%f0Iaj|D}Ju0$2_Yn)9rgK*cZN)fg%5yoi% zpg)bt8hSEC1m+OV#=^jQ{7`R}iU!%rL8R+hn)&Lk+F-D&P)BI< zAx;FfKd1yB!gW*oT)=&0oCMeV@<;I-d&}2UFg`zD7rvA(9F^MehfJ2ZAn&9(*2;6GdrjAeWUbF^P;$!e;n{H08 zFB%~w3oA0)Ecm=KRev^GS4v`&?WApeM}~_4$py6kQF}YeZj1IE-g;GSi)`{6N|ngQ zuCu?dEF49%LL@L9xZVG9Ne!0r*s~)xff*j9W=#8&c=XkcKka1l3-{?Q(+3`uWwM`3&9jkqPTP1AcRjSUDT3>9QuxOTm>JdACnY=5;Zxu2; z-aoTHbL_tZU8|B;;mB>ZJpHEqJnUzlblFzp>?Zm4(TvIpQBq8vB9?chzfMFcuNTwG ziznIpB}80+*aStOJj@q+SDvi+x|NpJK}Rile#4^RKv6~i(9=^ejB*=s`G{-(*!b|U z{edIKTOf%a7NTm*y{MdODR3Z#)cdJD>pQMB)CpcQ|5USTMQZ-V*|%i<;5=xDNrDZV z2^s^Zu2gg{Ocypqf^!kgGaAs>paECk#w79Oc`ZLI!;OR~GfNoyM| zpzf4qowf` z62yPW;{t!~`@ia6|Hke4-&U6Ka{(fLdx!A)JI&u97s1?+kpYkB8a z=kqDw$QQjIiCI{Z6i{hCAab<5TbrYVSf!~9)h=z|zg{Vh%hNCp#kT-H%WwS2;~QzX z92dL8>3lZik6wR<^Y8fn_kUFy4EEc9_4<|H+L*OQtDzy$%pO-CwCy6aJAI5Jvs_Ar z5{=LghH;vI_9`D8 z0%p*ZbUv*lvPkI6-^OCM^XFE=>gG>$9g6uI62Q`XwGne|hF&M!=3%Orr`XW9`C;_z zDL+i4EfPHDei!za9GQ5Of0|SUKNufdd@RpL`NK~Uf8#|4?1S-PzwY?hGY#b@wH@2I zn;oOtha*3>spOQt70V;baQ)Bz4kdS+mQZ208(SId>Lq1z`ApqfXF1$>+264X6j1I$ z^$fv~k?YTKO@hGkM8xwm(1rhzc8z;o+ zE^qPizW5&+zrAn#t<|*&c}qD_%KTEob3C0N+{Cvn{-gEHUf;Y~->??xo%p?f9>vE` z9e>srvp)w{DExHCk0JJTfo0~u5g6@Nm~A`v-uTu&-m;I#8*$NQ0JR0%PZNJ|Pj!`U zuCt?*PF(*6QPYRLe_zP40GDflcZb8a_`b!r+xYlE{Jf1Xj{l6`ZpS}PJu;cVllTxf zj$nakNvx8=+~yzeng1yXcw5cu{_XgpELQv(f86ZPo`1V}UFlBL{N$-Qx6%B@#qK>- zB%8QbIwNVVqn9ncplg;OJT)pBt%~U;B$f&}5patxy@?SGsoIQxnYD{b|4wVK)#`5t z@wrK7H}!$h4h{9L7?nhf1zlTcw*gq`6}Qe~KM%`+3xlQ>h@nWYdd0GuFsZJ|2ECnk zs79u4Sj~SSp~^ulBF#O3RY{$k`$G2zS}KE2mU^{j$InuOtN11f=0)d}iqAh(9k5^} z!_5ONrfP-xoQkT8hh=K-YSJW+99&;>Di}!{(WEM#d!lDl7fH-Zyb!gZJUs|Q7$0NOinKk5;np{@m?wcjnq-~U(h=vUQ#n9 z;tFDQjk~mvK*u^r=Pm+R-N<1ch|((J$w($(Dd^Mcod~mP5c@O-)rmqloKGs) zGI;$zV3mW4JCIBB=3;cLieSDhf9dxTMX7hc~n;kHV_~p-*yrk;<>|P)7eN}rsqTy#| zi1Yar$9OL^=6M5(&sk@$_;f13lgdW1JDX>wzwOQcaohW&aV^#1U~` zCt+$1H+y)v`1tX$!IY;y==IX(bDqrL$ZYGp0%XiP%ezM0wsp-uNJ-w^#J}YIbH}sO z39#$K#nK+Gmv*Y<_Ztbf>zmJ?ef05`AJQX_2e2Asl1^+uNsAjZQ}6NcYOaHxjM`?y z!Od{~nLqRKUsA;TMEFPCG7(frIGs-d@Zi(uW*N_a`F|j1Qz8?f$Dfr%$+f-`-b}9b z%@MctG27F%(NI}&Mz}Ybzd`peOG?b2Bf@L8l?%zKufbfvc9 zLq^|^;$wrp&x0)ZK^NU3U6RjZ^6?~oZt;0ObQ65yGk1GB{<`1UE$95``!$c)E>X!) zMN9k&=lCT4%)fZ?;+VVtbn#i9f#BAfAIL!DX8ej@;}?aD^V9LOWztOf;ck7d_%lCs zro|P81CT?C4V>GZTSCM4{g+ED%wZx2Rt$sl@4*v)yS;2-A@! zId38*lG=-is#;lOU!WG!U#yfga*uE1);=CTZpV)di{g0}Z<_HV(jud>(rWEQCQFnZ zpNmVF2ooaUV80ZWU{|o_**trG(sPfGh|D^1^vKPC?}ArY9bdvHHGbR1FR6#9UX$V{ zvg3Ag$xW5Edr)1hZfTHZv{I`U{2Z>7K)As>26jsPS*{6WHb-oPWjV z{8Yo9IV|+N@uQ&WGvcF%+>!<1aRs1S$Cfib4 z-`YQHgmwA-ee0W=f1CZ2+CN6!q!d(UzF)1c;F_&i)E?Nx^gdqSieLN3djH^d|AgX0 z{ex`2b?=|t=I7x2&`-DHZ|6hrpFZCr*v`v7!fwtV#ZTB#gjjhas1lxrr7YS=mZcun zD)nWDTez89EH_I#FI$=J=Av*r%oF^brA=N0G!wy*KYLOO#>7hxIX38}kOy34avc1= zYW%Pia-uMlQ48xVVU(T@1asv1Q;*Sc9Gzuc6a3f4Me&vp5h;A{*ZE#Wl!4zGF&AcNn_*|c ztLoc!6TSyHb=&!>Rl|ju-dLn1RB*hzrdwm2jqX3`@yf3Xdi^oavZx#~>c8G#?e-oZ z)>^!tirh5X5~5g<1h#r=3JpRD&J4lCy3iu4SoPQ82h!&`W0c-+e2!VU+#>gEhR zJA&1BCItnljt(PH|Nfr3T+d=w9J=t_qtE$MP=~ExlE-8SAnxeHuN*C(b2GmlBj9KLt~`Kyo_t zo_>h9P}_*6wxpLf8s)mLGT-=Q1u@p9ShC#KAnV)3so72O7c5@Q7!MwxTdG|#)0gIk$HbqDbG8-$}wA8tMPip`EziSH5u-5A27mDpx4;(n@_gSP#N zlUEajyY4T8O@9xeG%s0%% z`Na;acYiBkT0qf#Qwe2rMuk%#mS9_UVCN*j1PTqVAlROokvISPs?EC-^;J_o27p6S z{*>n!144g`{c09=<{A30T}!PmU8r!YFQeF>v)E~Z5FS>hFUF1uEj_(C{Cu`E%;a5N z>Q}VP&fDIuxNv6KWqK^PVdOcKK1k2lxBKM#*7QID_%bFbkAx>8fj%w9I7wfmwti`I z3ID%14~z@kvgPe~KeB5WJ`=sq^tmop(NNAOj&!`9Pdeh8%fb^;$TZZQ7F^H+(hWFn z151iXsP!n2&%3q`Rqck4RaW8QD@l?UA*q}r*!m;b{HD}{;0ll%}@79KQ`$`Xp13Lh%>^!?OYO_ z)Fw`hrt{S>{18cdqN@9Gm7X(cXc9B-MwlN&kPgC$ijeJ)&AA+I4pcnn5@B8vFuw^X zyoNDc|0c-iUI&U3tKs_4=i|fVilxU%*Kgf!nq8hri~pe1 z$e(3;hpFoIci3I>%dF&Tpwj96NB1|&m>trrsj<%(En9Wub%BWQ>yDE1>9nc9glh(Y z*Z)y;$czoL=j%QxQ;b8&@M{@shK^`=xJiU_=ip8zvJ+&*9UgAh=_+$S`nKu)u|ATc z)r>vj#AVJGd?lY6wg0BY^t~tVJ%50C3~YEAPY$-+04s~bK@^J{SsM$t6##^UyK1S)h@N% z3d=t(l1cLGISFGXiTjn$k6@w1mC{1@TwEp`N!0N!%Iz*wgZ7>CG;dFFxw5_;2Cn{I zbY2FWdCcCMyKt#059WM|Cv1ZQL@F5h2C!)Cv$K8Xz4jp~9Qs9k z{L}1+(vbZNV^N7a%PT3z@MM8TJL&d3(r74xy~yYF**W4;i+`!8B8Osz2lSA6+MY&tv8-+V*s_tJ3m+}m2yOMa%Em+w}; zel>n@rvfC#Y7F^n-YvXZuKBPyJ2>%@rJn7cgpZU@6bquVc|E3CgPvV{$uBM1WT@7B z`1eHthHg~sS#W5aT_(%vH$NZETK>A8Y3k}NL!JOB;m;wWqjv4fnK_-+5ot@}`G(ii z&U7$9pV-wA61vNI8G%8yE~^$Nv?O>Xv?4MuVD(1O%hRGJmA7s9L5v7t%51cp2w?<;+ptrL+G* zSH&JK`sA{^BaV1<)19Lzsc_M_K%nl$p6pt<0=dIn?q%;)&a>9&t&I&T-_A$>;fWXU zxY^u&awQxsVr?NI-lO1JO>o+RjV!G#=W_Sxn2(p_V#d|?tyuXdw3*l5OmDgjS7Bie zyJubMI)?asg#PHNA;w?wjp_|YsO<7{R<-|5J5)$nv&bZu-INyC~eQb zP5+~gqqjW=YSZQxO5WAzM6uIC|k}Een8%ULmog!p%or^ z{wf{Sa&XBuXs$q{$Zr2#NNdPb(?=}bHN;NAF%gTw$HgqXXp zCt(4vcT>lj+F0(cJvHYDzS~6ZJ$l;Iv+Lhw`8iF*6hz`UFz!!S-M~ z^aSg}ia1BYq5KQ06Ubze!J=6C+vz#MY*Zf&;a$FO1|HVhr{?le-m1liwz=sRY+O4& zq8g$#fm2eqCBSO3NhKXLj@X>LD2*?h9bUL=1nwY<(_bElO}6p>=F3|+cAXD6b-js3 zvH)68$oOk}$S={0Rx}{j|A=y@eZZrpNo%Fk)kgldYKh(EB*DY)G0LQsu(r~Im4FoE z{q3_{=BN4st4;B=xr;ZeN3`-M_Q-b(ZEQpgZzs*?OD?HuF;*4P8VU`thRjY$wFHeP zIDM#+<~);Gq{A9$ zT;-%>G)%Yps;T1MbroP|Q4xSd*I6r}n8dPq`>b)x%Tyy5X8#O>4<&`}j{vjZ>obx9 zTNd;m&+d{@E?xQyH2DPl1o4KD-TEwho-C)n6jt;yMw+e<_*C4XrG)1#ZS_XDfrJ}Asv{Z*Y zN9fsMr^D5gD@#GKelcc{Cz1$_6mT%)89qv*XeP&% zH`lj~((PnilFH=vN}CMn25MYDQGUGPnyG*%8Fc=Kei-<@YfAHR{}|oqib&?duv<{a z!mtmS!03*@14O*j=i-tkGHuD!|J2;z-y6|36JZ}!;q3a6GUo<)^-=>^UsCg#Z`|MI z)niWgca{06Lv;3?svYC-asQ8wtv`Rp0!1yKuBubNjrru>^{>XgPF<%ts#o;)aa+|b zVeiLdbssW=HfG8oG?%^Qfg4eqd0U{l?+$KvYJ1Twm{a z=8ktDWA#{Od&p&j-M*8+kf~=~WV1X_2nHiSu@_xftQ>&V8q|81H_^pXF==jXV8^2J z++NvS%qfU1i!cH`T*K2q_kcK$TJtkoTK-ZFu@n*8{?0Dw=vOw8BKiYigSf4;&7i@Q zES^M7oXUFi&mSpXyx&YI&1uQyC6eM@9)|It4owt8M5T^>l$%jsWthiOYK_v@=nc;` zD?N1&^j4)8PU>Bwm1eE-!AWf7?R`3)XpW4J+0)Qz7lWoEqG` zAGZxv_mLB%hqQU;9>&4c6(+%js| zq(uaf{#o_OHQ4sf{U;GGWxUJ|Y+NiBncbUCiDyqt+7xhxbrPrv=5oMs~{-UqAwD#gqsB#L5=OH~~; zIO{gE;-VSod8eoU>Y0(Mu1y>aphEfvumc!p(vSYt&Rh-plVOtjRXRKyM{tZNi8T0F&n$EB{fl@>fV3L<*zoP;U{o;HDKiXy9spXkmc`4UK0}x z83mSj5W`98#xMKc5e~63ld&6#(L%oz`=nm#DAM)0uQIwt6&O3X6`rJSAvbG?=F?e59OzkYzDz{KVj9 zTIw7b&hQJ4_Fhs=gH(ZQ-gQe@GaoGtWy=p?^d6R%YWhckubU7J*KBs_tCHP7^*egD z*3G?=;VI=*!N4Drz4QjB3}y-d1Szivb_`PWxy=T0w^w+ViKpOX`mYHDiX|6gX;;j9=T4( z1J17gs+V9yR3uzaxg=+jlqgQk$3+YPj)+ZiRa)~ zot@FuNA&O`PA>t6Yl@-gsG+qD!+ zM5%y4q1zYcp0%5CR_!9&3*BF21=f$l)9@?96UPCg+KK1}I~>6lNk1_P%nWcBn0MM} z^3G}BaCO&@21d1tczRuQF&sQuYa#>b2{W=;Qu!u)({ROH$*g^m5>fkF*NbnOcGtYE z8;~52Bn@AT&XrJBS1WmddCQ=?v3+5C*FVGKn4R5-K5?^|C}Fp!0S`0t1TsU&Y_CF5 zbMyRjio(Ne1;g4a8fm}G{T#F8enU!s+?M{^+Rm!YM#>+Wr;j{z*_h{Pbwdd_s&O#XXbuWRuv~wTdqCzb1K$9L&SaL3oR~wac~3omyhX zvo0{Ie)S)7)08rseVz38`=23#8O+C%TMgQi;u{AG`$C|4v;|vKIr~rwU62AHwkx^M zX0Ctd)kE%BZ8N7`%s8@zw}EQWzupGS>rp&5);AGgaEJ2sVE5HCASZ0{;5ghN-~ilu zJaN!PI3|_et7BJWTCK`xe7vI(X>4d5?b``%?*ije@7Xz(r)l44JIXElWQi}IY`I`uDQ z)6=5N?+ZY>(|NY<&!Nyd;2qckPM`%lj8$I9J_QbB*#ury@*V8n;3tx%cM-_a=b$~S zU#J*X>cVUNWzYkDnxzn3Q)R7lq5vGbno~bgyeMX^)syJZZ_vx`iWmsU8 z&Tt~i;^lki@W^NFZ%iuSe6}}C-HZeWZ_POt1Ig}L93fmEFo0-ODuum;bq?K7LoIufK?5K5;(3&)^Ht+#R#uMfN34v&XF?0NigQ@m6(%AvfT$J8_buV(u zn%)d5;iLS)c{pG|4~-wZNB2MzlDXMhK;}z+jN#;vZ^a`Ow$^5u{jNv)5(6O_Y%8Di zA>L$vE#4Tm3#za>7rzULckm)h_DIVxfxh)cTPmMJi zH7yJ8FrImyhjo%fjoTAuR5!6fil#T)bN_RzACNK;9QmY1+9=wPh&+wV4yH`aN79H# zH8xhc*N*27Y6u@LAyq1qYB`gdtXRN>>Lj3|bY=^NJHhZxLw4`Tpo_69J)IUHL%L|g z&J_Tuq*m9K1XkSN*Zp)zRl7}LQGywH?@VYvg5_bGp>+euB;AnRbG#tn8>%cH6%Z>j z@O%(Sct)hi7$Y3wk^Yc2cyrPR*2KJqSdH;}m>U^&AISAs z+FsKK0LQdAXOc4=9LPWdb3Z8z3m+m0E)Ns{{aEny&IcMtcIp+fPip&%=d(Ku;kE@v zryCkKY~k`*eImd=>5m^(tf)kQGJYMUB;3q;uVt|O34ccx_~3^>v1Dfpo$&WW;d&7e zI%`DNVIEOjrLyy@HKlL5D>}aMWSPpk$N7^=TZGvkrQy8M&z_@i>RGG}zzW1gXL!Dn zLV2x|;7ZzEwvU6>QQ7k}ds0>Bc)*MqsgASXACgM<9^ZnN2#|cu@M!(C0LbhYF;(1u)G(ZBzV_&S|1-)r8Y+hryT-8k zQbxjpQ~NDmxt_fdNYdIPZsT?>eCjCXflh17D0%%R@+>#ikzYe4FX}QY{QX09^5=N@ zB}R6JXFfY2>;PE*9PYEE7R|2fBk1UVQ+I>@S87|ZI5_tFg`z~4BH;0z?XbYw6ncb= zF*`zpl!&UxfpFWZ#gd`{@mL>DKnqSij?`B>K(;g44R3l#u8oc%ng)}Z$U8L-GPYt| z8`1e4fbXGS|7T|*v8qWKs#V{Q>jk#fww8c&Bqg$B66yg19yZi)usP zv|{In5Cf{(gsXsa!iPMV%+P#&9`59SI?u$N>wo`?!^RKMCIb3nD$^}H7WIcuER}Ty z-`=H8gJ?;%3Pg%8OP`-th0R;{JxeeVL{dZlrgEi%nr|rFlyD+g#2;bXg*?4NyA5936N}(d^Ph6A+Mjy4cJfJ`FMb#6 z*!i=omb_FZWm#4z)3nTBq*epfXwo|ULQpuJDno_&5H5FLyD$q{Gj#hav$g>k5gV^B zHwPDzw=Pg43|poz4+}YC`tOt3q<<2KU8r`0HCDzi0tU=ngr&SzEc66&v;K?zMbv3} z`{Z2^Nb>74ps$LF*&_CCubGL|t<>&@!2;_{JN#Ennm8$qp?#L;L-P>~oW#A>Ztamm`BEzm)3vufNgp8M}Op%xqM`d>KVM+0SPA@s?d3@p9STu zpP%n*ySN{#+#DDVWtnidG?p0(T38@-9ZnpAEs+tOqz{yE66|z8xYSZ_XlZLD2_2ji z-?cjAHGr|OGa_|7CHtlq7N7AoN5DJ`MnPuvp6<$)^;U`iac$>RzEZ~lV6*TsG-Lh}M zHzopA7!-8BLZfBY2BXx*`3wqlpXoV0_6V?kTcgfY`gZ?6pYr00N}G>P-44Bn(jRtJ zsa6h)Z|;x9PzT6Zhv#$l`716(zI0sd`|0IJyfL!4ubq7Cu~>W(jjOzJ-DtuiF>FzX=&W^^J@j^tn% zka@WQFBK(i!`x9er1C=CZq@qp|8<5Ru6|~F>QI&R98NtmBZwVYGsgit`i~+(k&zw= z^L&p$Q=%7nXOu?aj~;bhvH8GbQs%5HBAdjtqqZ-8o^9gRModHvqO8+}SOsh;Mjp)E zG2OBhF;Hcdr^hQrhz7Bd3bF@Z?kcR_lwCRi^L5w9A1uSxkxwQKd1|NyULN!))M(V# zB4{`La69Y#w2oJRQweWj)TC~iz?P*{BiTZdt0zT>%VP6`9x)t<1tL697BclkR?&SBF+|!Mx@EOq zt>MeteY)WtuX8t7R@8Wu7J?4lPU4t4*es*Z_qVW9)wBPTw%xV@T+%F9t#%D}rtxac zwF;1)uwm?XboFv%08h_g-O?-V@!is$n-ZE*I@?SG%=%+ASC@CTg`cQH)6wR%9_-WKO9`ufAzr1M`fi6ACR zOi0OW&f#8NK)=rrGX}S>dn3w$!}eud_*+^EW%X(1v^8DS<3j0ge6b!2BZhwTjCnj> zYMsjF-}PzIP=5p7lS~2K2RUy)AI1WIhjQm-rab4E$>-6RUt%rQJt;O$c_GWGxJ=3! zn>Gx5Zg5cgGovX%(pQ1idY}apZp$_pXq75j1;R+F-Y&TB)ox+gt#C4X^%73=uzAUt%IZZ1OV!)~znv^I{ zHLW~Ol$9O}Qz9q=`-X>!KBGIwmf#x}dT6H!|>5VUV%a+rbrKD~_P$LYO|Jh(a>Ot1lf`((X_q z9Bfwla{MF3h4r?2xyCb&DJ??*v}M%9`zYGieD(_kA(>`ka*GKY$o!pX*rL9?(X*P#~YCb1Tj*` z%!R^55+cK}YPgUkdh7BS-CXGKU(JPIF6Yn;;+!)GUA=bIhtC+A+1@Z))8>k!i^0U- zMY#t3;5ka4>w*W{{XwAPJzJ-?6SYQprXh16>?tRK2f7Qr_JD_=HX~}$pli}zy&QW021Jh23*#zbjC5fdYTe{aL|iSkGwZl%VH_#v!Wub+N(V5C>u$LAr4@wlG9s1ttr{XDt{pa9S3pM3s?_Z(2 zqd-0r^0}t@Z&U{6Ra!#{*u+R*W6d@WI*%Gw6}yY`4432HJO)FqWXZrM_%)F@8+f?g z4BbGzu37thG{(z6llje)Y_srFp{z6girnc7n=u>RBJEP{oxM5BmW|yk2W2~9y&w=tGj@RZLI(!!dYKddBH9L) zZhqQ~>dOIq)7M^(S7)PIDRt?Ka%^hAh(54aMI8Y32{ocSOl_f_fAjaQ?f$J(zDEL^ z1cB|vWtNS0^~d`uzuh&h9$RirW4qRr+QTygu6UFql+NO z-SDf7GkQ%>MtV&C9qJnn|AG{dawd&_=TU>A54y|KdCzqK=pl&aa-AvG!_;{#&@BjD zOJ29JBR1?IZNRTDJvkrt%^YK&w)7x3>6|y5UryDCwR;PG>eibCMGjyx98=VNNnfgIt zI3ZWG@_v;bL`cm|WaG2LMVnI1wWBA7BpP-dpdg;jbMV2Pqm<&k*6uYuqfsWnQrf8` zDe}vGYwH#Yqqs>R-M71<>}GVj5AtveZJ&nPrbzLIkri*_t`plh3I_o#S3YvrWb?z| zqF;-ZbH&XJQ_+gACqFB89+rP+TYKx5fKm{$DcQgFWZpqtj7}qR;tK+G?@w&d=c68mo-hMbSl=D|3#|b)42S2}6_0V80u^T2_{n@RCjh{da7#t|8KM!9&{BIKcSks8M7g?b3zjlUKQ}4(U5So}vlH z)@S<1dO>f6UxgxcseTcQ{MBFRH+8<+nLBo*F@5p&J4KGKz|+Vid;`S@-4i3a!LYPH z;f}a*|1vv0j2iBEdr-E;*Q{iULAD9LcHT|(a)xhe2TZkg$azL|f-1yZo)dMr2E;*zc6gRNE5c65A*Lsa zTM4bC|Lj|OSv99~rO1QMQG3`+s}Jhazl`_K z?NX#?uEsUOrT&AoJG{+sVwamOQ;~MNv-W-V^_Rq{iZGVC^@x-wzQrMhUCE$*25C*P zMztt2(aqkvF?P^*Af)(|b^irOF~GQgCho+MBday*`Ul|ORqGGAk{#1>>BLbl+wgMs zU{zcL8KvO>J@W+Ns`o0TjsLE{Rb|&2`o)W`Y&_juaM@KYv9@+j7N$LVm4F{6=>Ds` zo5k{cED`KSE8jHuM1g6nwoB6zkK58hPsjP=jQs@nw7?l4mx7Zi(_`|EhpD4p=F{t= z61~ZHmVt#sGZN^yNyNkZT2C{v+-4#(emh>3=tir8d&*B~N|%iSb4%-X-Is9c4^7q( z%YgD)@J?`oH5qJfKUl%`W&Rp|wbmcn+}t!SGAQ+76=|;O;mor8hySYiY(2+Q3Y^7~F^!7OKRubmu^M0C0HDUcJGH(7YRJQrWzNhMdJBw2rKHB(w z5)8-qsDyTxR=wG~yWWKuYOc%%NngAro+u<7svfG#zc&%^G!Fc>oWU%SVq@L-q)q4E zhyW6?Oq&g;_^i9goper{srM3QO|QHXnL|p3$;?`;DLq9e%(9JGW>Lw@k1ar=z$+?47iBxnIHJ5K4|b;X3ey;<9eG%EG= zTcdeoRd+XI+&^delV7BlG1JX%&z{eY#Sq6ysLg_c$XMl60aH~Av*{NF1KCMVlR~is z7;y9R^1c6w@@jXJ9@%GxiNyN+nfrsv@HpwHpoevu25>MyPnQHxE=YBD?@bTMWKp9g z?^mL=NwVSsio&Wo%Ni_JgC0u+-{w~*_p&Sv3P^r1BkKo7)10jfNFn>09qAcOJx^cgoW$s?6-dHLnsFV{z{xe@4cu#YI^0V zA!z2opvN>&uj&^{!VLM5s*3d!9%_8lYW@8g`f6)|Jli0giKss{@Lw%Yz_uI(9M}j4=WUP z#W&{r$^jNaE6V^>ksU)~d5qma0myWxE7!^!7LP4J73k%~T-}O7a`_hqUHp&wzWXA7 zH*@Fu_9%7FWWECa2si9SHA{ar#LS)B=l|VbKV;?j7`-bQ@{TjlcZSm4R_p3^4b|Jj z$cdvbLxP$fL3cJQqJS%A5!BH{rfrr#SX*y6180GZA9MQr$$&(~rmB_DEBT0J_L23$ zrPpN}C%M<|=?_BbSD1KRZg_<$D7BHU=U*PUK;H28o#w{)?|fr4K76V^i5Odr$<`a_ zA$$-i_7aP5wH>h59JhY4bh-T%let|PWTuRNWdJTtq8@myL?QpL4hb81M=6&l*M~Ln z6!zD@9IchBpvq&Qnn|=zvQUq>cP;8%e=FxX{opFm>d(rvEP6d7<>(JT+3&vZEz?*n zJmg%AmMRfX+8=FaAI6oR+%Mpa3@#G)=N+_HLhaoDs3_)qeL<}MJmvuB9(cO2#atse zB}q>)s~!P%G<%n${lM!#IW>3fs&|rknZ>#qSt&mCl&U*h+sYj=npilHO%IS~VX8dU zSRA|aScceCMmvEWntWbrR)VRv-RQ=v2C4IN(zPuBGUBOwssyh4qu7R44xfk$%rz@W z-6oRLxA)3P%XpPa-^(90^IV|B3J?oD)GK;c{?T6h0HS%9%4&*##w#0J_9a!7vpJPF zeS3Tr%$Mwy19I_D4Q|c)l3oO_wdY*2GNBOL4%8c=*I28)&-5enBZgqSgdJ9DV2m!% zik+vZs(OF=JD)E^pGt8s3p`Vpeef_r9;5WfK?lTu>GbviIk;!p_NZ|b_-IzD`YGd|@D+5*Ju+J|5%m!=h0*Htyvw(KlQ#? z&~C>lXQj>4ghbi zUd5|32gLPfkV;30aPG>5UE!M=9k0($_Wzu9#P<&8qx6|Aw14mwe|*)k@i5AIVA4O+ zAngo232-^hdB}M9%*ACA$AylWqEMj77~ValCR-O?|0D2b=hRWiGf1d0;U!Lq&DM-> z?1<|UWFnCv7akq;>YmpNeRlr~@2Z3xamJLq87u314-fc9JT$8XE`TdY@H{#S#k8#= zg&Xc6q-{+K;01P^Yi6H#fmsa-ag%4RqaRg~K0{(Jst4C_e2-fNPIP z`T6wgLqTcCZq!l249exHJ#?{uj&N2hkoga!YIVTvwu#*BZbnY>KBYj;$hLHW&Osn; zSA72k3BCtyYV|&XsmVoaM8C^zM^6q?a;KVpFj>#Ltgl?l_)V3$pypNSmfJKIa$vKG z#&=)aOUkEnXhqz-D3-5T?1_ItHr=UJ_T0<1IO*}^6YIMRC#YpGi1GK{Mt2m6 z2>leEx?(|T6>v8hTi&C1A3zF~H6x7)M>^m%gAik`75S>URb2aK`7q!?EPx$U@Lwpg zX-w)8;_$Li@gML$(@gCa@e4 zum2EJ8Y<6|RxwAi4^aAOHCU zGE^xXWDZdm^F|jPQ*(`@U-Jh)6DX5=_G>c-rcaoHfgeFHWMBNl5Bw)sfE&ITgWAhX zFZX-g3Xmb_s6PBJAB?u=5LWWpDN!!$EpasT{mbb6*7OO1S4CniDr@|ChKlwvSn7Un zOFg74?=^Qw+;W@LxLz#ayV?6&bcO|FXs>i(v%4rZi}TQZuvKSa&Ci{n_t+`cwVJE?$OFhZKjZ-;j}#NtlmGpWfZGPeVSC({;gP6)`c6c!u~OcHm`-}=l$uW5-)=f#0l zs*eZT<7rQhNhDY3HQ=f*;Nni^0A0Iq(g&60Lr2TPtz@YRg1K{45SOqKRm#!7XS35m zx$RpO=x2-8{$SSmeEU+k&iKS`Qys1C=PAbDnLIwf{RXN7gD5~vzSWJb9i#WK4X0$n zj;bG-4_t&|bw)7v{zYqvK}tFAcq~i}`oI1}IhuTP(VHPH?xHx>{gTCKhT9?kc!1`` z*Qhb(cGzegTj_7eskyzwkZcaCcHpxRpN>Z8Kab1H%aPW1jLsh(i{k!KMFjg}RT0?$ zd5a|J3fuj@TMT5FN z7O)9k^N-=&L3FO`l?OXijRx`8go)4cj`5evciV~ZmesT#$p zsT0|DcbBx?yQg4xhg)jsNVyiB2N4A^tXEuxw8Bt3YIEu13Gfnr_?WZC^Umg07H2mE zpL;`z`0=lTp?^`1Ry&y?am$%g`R{_z?H^i_g?BK*>wZUWAXf@^)X8QIq^y<1IG9$bEkP2#QVZNYEvCkgWrsVuU^=jmdsd-U}B zc)s$EyM)H%#@w;d6n)++-fM5DduxN?1G!ynbACeg>ivGfQ)7V$z!CXk{Tq5g(P zMn!t0?AGY`^c~e?(4%XAf)wf{iOKO!A&KsRga>zxl>r#+Nx;2;cMA|L4;;hs+W>uc zMbX{DsDHN~uA8jPjq%25@7`+|6m|-qO094Mi#C1ew2&y;K3sT9ReDpqjP`@L-F1y7 zJXl35r(am8OH17}*~J90+Rv0%u-ug(Tgu8jc=6_oi9HwMP?JRM0F(&KqkQD!CH|Kz zPU_<^CYnEAiz#QiMa@?$tfA!S{Ojj+b^WHUUj3EX8Rpg0x|tEdh#)II=Ow8}7X#^V zk;mjSp?6)*b*a*3m+G8UWhy1yO+HL@(HBN@^AF54PqZEShvt3ix!CHTycpoj-v!3g z7OnljG+RMU=ubZdDpq@FWWDEmG?yQpu%U664_<@vI24zxuBPknzP|HXa^A9UnLjjt zO9zY{BsSAAsNc2dGUzFL4X0pY=ueLNwc=Yt{{RK+m4T`%FMaMCH`?)5RN|U$ok{|A87*LM31B zv(4DD6^W){zP*Bwi^$tlf3wKZXWQr{4o{O+doomepJb{U_uPo1G!JNrqaGHHrN}K@ zmA+TB7CJZSJlHT&Tp0C7DUkl5_2*G~UYt(TC(b^g6Px&ijR334;N+WMaU_(0x}xE zaJ-*V^J{dw4rBAs7)lPXS%fL=h$&Z|3(N)BNa%@uzgkp!VyISR=zj0V;6$=}EF5&q zdAC4ETC7$i5Hmn_ga}57`kC}tVHLypvG8Dq64&eL9YQiyf`oQ8%l*+bK(fp@K zUkTuMyK>S(xo`!Th#qE}Ys`ANaT#*Gh(rP*k;mP>xp7_BUH-1Ng>X#PRl^mx<*?1( z{DXKkAGw{UNpWDYZ;YT*e1~_<(QSK1iZP?iyStN2g$OJThR?l< zG0MYp=$EwC{h$+Y$apNGy^GiaTqaD9*7m`wgN?B2{%r*0z5|4RK9XkwMJFvOx-t1w zjU(k-Ly;j(^_h6$jx_h!VKAjIK(ursGmM?ip2QV_WZcj93M>0-r}yqO`dz4`TT^}o zxpI1kpfvuS4pM2<dG3~+1OoQN9eggEMaATCkftqs`>^X2(Wr9 z{eY>#afW7hWUc>d`R;6`%A4a!CcsA?@jaBPqiaEkHff8ZgohLrSKRNC3omd;JRnm2 za6PU~Mfr{ZjmHS+qeo+v+1jHbCHJ2XdF5x4!6%$PP6yeQEop==RVJQsZZdVhA{QLAui*7C$j}O)tTvrmv(xCPikCc!AtHeC&e_L0@T7&!m zN;T~V>Dhcgzr$#~*hUp>3ua_pig{;&xJy?mZiXv9xd&ZMLwVHlXw-psp{DOLtH<~t$o`U=P07D zcgst*Co1e}bEN50xxC+}OWKtBDlvoauLenHiysR1#%}`71YJe7holk^f$b@kq&t*x zVfVpFtTxTEDoScy>0(wuv7I-U)bQ@UGG5A%L%8UH75|qgJz>UcOr}d}@dNsII7my> z^DyVlvPCL0(q8st>g}!Jw>m%123GoEn1M0qIB=*ISH5;~e=)s9aS;%q5!3+(ZZW?k zc?G2fUj`^XMEHjcASwCv1e*~b|Fbk9d&B~@&5GiF@cD7{M(qsY3F#p*DZ-{=X9rq0 zez}6!eg@F#YBBkEmzPd>)^!+EakU?$#HXD^uI!7|+~2!|aCWafZC*q>w+tYUUGlO& z+JK+IDQ?%%_7AxcE+&!`3OEY@+%Fx8>Wn9`dy$$CXSZ75sE;@R_Yn6dV|&Sqr4``6 z7tEC9&J_*!E><7V9A^N!83@&O4JFkso9_3G1`TE`_TLG_XTln^&8Hm1$Er0I&I-{w zr<^Lp5m?1<*wJX(9q=FUP;ZetPveNb?9DZOzM#j;36yJQ-0F4lD$7sT!hIYn2ByMa z1RPXxig=DN{Cs@zy!f;<#1n3v5giAgseb+IjqD78mX12SS49FMrx?BAJ6B0B8VY;! zSfgq7kul#9|7uOb&1S>ki`NHdS9821((3WZ4XWEbj2aVrCibmrG`MLGe4}_$GA@fX z!)`O#oBfx+sIcO&p(;$0lCNZm-c3r3#m6ON_PkqA-uZZu^EX~K@@?!o8{my(xHyA# zR6f^-yBzPW6=_C9y*1lDAzCe14xJ&t~N_sv>V$u!!WuAbRBCi^{J zp(`8z1!VUyYlu1J6lAvV;F=}`5O{3P@C?PK0pG_Ho1Qmw+69x7R4YD$9Nf+puLepP zyc|k@qenL$7`zf{u;LXL2_a~=a{bXPqlz*-VyRKGNl%UcAsqTd;t$T`yLg()`y(eQ zvqG2loPzL=-i_0OUFGe_g)7^)Vu)!|YGhggFc3}vH+0$w6a%(a8su6#CA1^a!gh;3o}`|j*BPeDCmnR&@KEX`I={Mo$P zEfcp=y}&3OEypnk5sJyFZM^)nLK3+b>H5?gkO}kPiebykjTRs(!y{0M!>Dg?5`g9Px}9^U%i$C^(n_ZywF`WoE?!ny`xLG z-N_Mg>lD3GIngi)xXvH4t5tdPMcsw1UPpCGlKodNnTc75^q+E)j$TMZ{e|%jjm<4R zHMh>q4PWnzoe^>>>(lNNs8QKFcP9{d$3gSDX=%n!@rP^L^uwd<PV2;J>S#RibbRUERK;D+8a2<4_SXWvqX3Es(*2!7KWVnolwLJNh zFEZU+rly_}$PTO!wMP>L0puslK-|NME9Z9|56H_=kuA?l>UN?k$9b9Vc#r-cNoN7m zcK3JjZHx^WgW=AA;qE#Lj0Yd?R>p80?kx-(?rsk-V7R+<8}7s1ZNPw)A}uZL+voo# zO=tq4X@1GQ-+S)ooWtZb?XZ|?2-HW*%l4=tixGycx>0WK5f|OFw8Sp9smG3!!q}U; zbm(e}E-D(Ynn#x9RoOqj2miq(D|=-l0=OM<0(Vv=f8ThK*-x6s*8HRdim@Mj7-Fw_ zPkZ1`mZ_%Rm42vuj8jt-N@aVA0g>2{hp*=@A!k0+1ZBR=ZiA+%#jfXUQ>r1KMRAx$ zZqN^N0N&{6ZI_iN4{*(BYkar)4+`Aa8#%I0;_JV1uo>Gl%h?TuzVn!MLtQ&JD$iS& zDPgmqx`twcA$~a`2}J{g9Q<-QNMDp|o70^=urk@`(Z+hd5tNTr9J~uElsVBEzAH?O z)J+Cjweo*)K0a>guTAoOY;9Y%MOEBjX)OJj54&u;Pv>F&n5`V0Xp$#vx$nB44_h5a z3~xj2!nZEtt+r#eiGMJH0U{fA+0vIiu^x898aSg#0s`vmbqck0%M?t}a>X6@5l0w} zeHjs{*zz_pPa3T|_>?cGeLPMhIXBZSJ+R$>DsZ|ur>5Kv+K#p66Vn)u3#~@JZE*Rx z7c;!w=_@*}4IQvYT_lerSMK{C5?4HI-yyMx)4qiDe=uax;By{k-ZpQ{ySx2!)FrZI zX_?O3VQwfs!3L-@XH@oxTegCQEpory?sc{qK8lCz5hg=zFYymDhb4URryyweXBks} z5~Z!mNOlSR6G^X(#&Y4Xk^Pxa6h6HgGYfAbg!)5si|sS|QPvH1Id4-XRs>H!w#eB2 zKg~)aOiJO+*1V#Y9}OlZozr zM=Y@Pt^}e!d3bg`7wvuPwJ8urKi|(;^sc$e)SK%3&_V&7c9z~eb+=J=26e3j z%!H<=)$xlRe<^BhnP&(cT42qGfpWX;}1$3Q)<@} zKi(zCKfkc3KXndE>}$MLaW?luxuU(r=%=XoC|XW_=Gu%khA!UeiL8-8%V`c-Kw*(U zZ@`hj^w}kP1~Pc^&r+aa^3SuL-rh0U6nuKH--A&)P~c=apX%6?9@7E70pD?;;Y`R5 zOfhD>7zM##D-g6Fpg@EO<_H4Az_X{c{-l`}0&;PKBL=j-$5u6!*Et-%%fnw8Tb{7+ zznJ-RQ3KuvpB#%cSlN5t1l$NF+YJR%IFiC;(0Lx(d~$Y|;xFH3#=K^gCkfIYNN2T0HHt+F}u*A7Ddkj(1cL6oqZLyIt zWeQ!mJBxr2f0f?@S1Sxa-;`RQT?Jsqd?*XQ)H5Dh$Pw@bzc|ZhJ!3Hd6o6>hR$~&u z{oM4cO%&U#i0d}s_NG!3Ep^?W5caonQhSmE+`W^TNM(trHpftJ&N=xgx?USY8P+h>(%9%ucIIJlX)m@9_1=lG79R2*hoHPmOJGH_o zTY-VrZ+pxCaL=B{YR5n)jdow?z6?Uul|8*iJOeFVCIX$^C?Kd7YNofCL(39)I%RVB zRRJK^$k?r2x@S5j%{3v7vpWzHqK3j6fBdK*{n*dAL#b3|FP-kD00BbEb>AeON@Hbfn~NuF)Q22SDSiX5FbeX5zx9Jb~~h81uDW0 zvJ;r%D})}4qd#IDt6-jqNz}+B_`k0j2Fnd5V*&_a|Ah;t*m+*}6aCZIuS}bke~!>J zZW-y>$9dt!jFmS4G@yCaP9}kI9p|nt8p-aT8HJV{k3ekV^{j4`4#p1_V^0Q3hWpHY zsQ#H;{~F`2k5i=y{<3HC$I)|2^YhOScD^XV8f{_3Y*ytL?BAv!Y zb-+-Sf>K(XGRKpD+{uEQM{+|JyZu5g<`QWYIwATUEI1~V*CTF!Kc=^*(`HS)Nn0}f z`6*DPUEJu-_jf+dG+1YTJR}46`P3I9j#P6KY{mV{^u5g#snaYGV1gMDKa#|+hiC-}m$LnjZ+q$kMC^?BFcMzPfF)uveSt}0x*rI zsq4?x>bf^vi8axA*VZ1rM|l2@XCIgkYW@*i#C!DZji#8?yBz>AZ^NM)cn@=|~*^fHidh$qzQ) z__Zx)GTL3Tose(rUIj^=fm0ts-@Y_XJbxXY4r-@41%h~bl{ zI*;)Kt&J4DATUWbw`)KHw$*zd7eNu_V8j)0I4((&#B=597hsl>?p0?{8T9TsexGlZ zDn`|J(zST~*&Eot)s&7%3EpN;+3zH~_DLURdW%e&Cy!H5mS(?&33ueJ;}*Qs`}*#r$UlxP;ItJ7WsqnyuTr# zob?4qnyi-nvi}&AoTKkO_H=PFM}sD?i@ULCF;GVi)$kGp5ih zU!Z`Xy>I1doH519Z+kN{zXkr-A1)nz5#>I^!=G0S&SHh#lg=oPMbze;zPMQN8*e4) zUHk_#=10S-m|IeII_E#6Aj#^CKFv`x;sW<5XQxb&rDy!`hq31 z@)?xzWz7FrK3-k;Hpa9uM0!Ty(<$DltXA4$pI*hD=~A$c7hk;Xx;@STL5Q0c2RK997?Q#PSydilr9o{6NY zpAj7g@IRED?irL7q3C8Uz?oj_pQQ6@8>z>P^hHcKO*Js?^qRw2%5FULx_U}Ee ztq`4I_BfDnr|9l|ZE0zNJcHk1?Wf8AsEw{%VF@%ZsQY={dyTLABj7N&R0rIo zrfVTyecy%?ZHuZ1b3YPZRG=9~_3n3EWP~@IWXDP@`q52-pEwN+mXF$n^ZVu-WJN1C z9v=Z6yU)$fA7i7MI|jOgE>Tul^)zn@R84Y7@%GRq+nzG4Z7w|vvJsJP_bVioA17|E z+Ms;A@aR`eFolp{Llk-Ze0vsXw35Q380K6CN50+Fo)8~d~J&2%>`An)k z&iZfrt$>R=GW~DABqRHGqswopXU~Eez=s_xOa7_n)gN^TSpR792F$ZaMt=P{c&WZZ z38^X0P}C1etr{iT*wGH4QiE;yrom{h;%FB@bxBb0jt~D^j z=L0R}-Q{HUuE{<){U6@zk$ggL$9UoAilZ0-COW-K?y_K5U7Hy1lZl{_%X4=Vy>o zOW zuVaYXzUndv~rZOP&8j{ z=ZIz}?ZFry6HA{5+ldREP1jywdvosX& zwu^4aG*ONzFJ#M5nuSKTP~}JSi8;H-l@_<~+r@W=doPQK@4M44hubCh=nv%sr9AoJ z7w(cdozG$|&dhxkbRKNibLwpv3uh)ydflGIY5uyp=mQXG2(6J)_w}!%KF&Ymzl6*w zc`TLa6H9)Odr{Wp+LHos9Lhy{6aXn|AAnqt}psPd_x=OI2AL*CJEel5bl&HJSp3XRTJ}RQDc4s{$3!U z#Jdg%RoUsun_jWn8CRL@jCAXF`LtJm9UxgVYA?5cOjX}}{o!$G#qO1$czYKyn~L}| z*5Uy{8g<+AF|VAw6;c{!`X<)C#%bW9<^J~~tR4n93*lVcU1{qJRDDi=_~KI+{mnN( zp;l&I=+)TeqWSDUmfRI40I_d{wq7yc9E{i7##P1H^miBY+xTCO9BnkCq<>ZO$4W{-KT{$~an62X|IQ?8`k#{@gv5%Wi%KNaklN#gm$f~na$y#c znZ@Mi`UnRe>GYgmquc1_6RVUOJHmq0D$)jKvX*^VIm6?=xX&w??{Sd$SH`3Z z{`%@O7C{ZF?@Xz1du%5?X3ty_1n9JRnZNTv4!v1Y`iCSme6YuQZ1+vd4|qR5k)D47 zBCuz+k~epdPO^F9e?$*zq?gRVkF*VdhfIC%6IdyR)@s-T!& z_?JGvC9!vBLCltbLbMs88h&mOIh>t2c_p|j=OY&6Qqk(=CR6vHJ<;$tiLy4<;WdL= zvaPzY4&|cG;bSe5GKKV;0oRw71LRijhC7}kk3WP2CTt~R(+{U-)+<1;9DBddpIUuC z`Eq12Cb?lb(`BUA&EqBlqXz}!zcqUTa_~MFI22M!@P#t9ZtUhQwB+*4Dn#%d|Dot6 zkX_sR8rk=@v*nlrEBOFdtBF^9Lmzzz+Ww#G;6DWgD^P{J$G$?OR@!O3co>b_!h=}7 zP;1?xef=xbGdD^hQ>Z(lI&2oCeJ+PMkgM@`y!OH2$OeV(<6z4xb{hihy|x{%J-!)C z%seA5qh05PW$QV?ScTj#yWFYeit734C42a3VXU>ClHo7bvN@5(Nd#G_kJICPEoTYg zLg(w27xT0HifnsRrrbnN`qd&GM7m;pSRB|9n2c$j^uiq6+wraMsJ+)8%$aD0XsiT6 zU!PSqy?CCbpBA;iavIg{`NL6&k3(Vm!EtKNJ2rQ5A$UEk2JPJ1=Vv(FSVFOWzRouB z1AZY7)qcbECd)S%QHR+Wfy54$5GQlzXncqav~BRfSyRe%2~#T3SP-$5`O%=nov2Je z=wXN_CpOs*fN*Q5<1n&OVq<7!3*y>a#kI(Tp#V~ z5AATuY`DJkMYl@fu6=oDSs~5ytnsU05W1$Dxewg(^ZiqsR$2``s`0`> zd$&1aehDp@RHf4}_*l1)gi##SP4(Ccc(KCCQRr!qA^@PC$BUYnAg-(V2hD4&bkEoa7!$<5v(QrjvF|e0+MOM5_x-K(_#tj9iZo<%)ral(7XO@_-$MB! zul&0k1$`-k;OKlkS9h1uaq!TQ6xYGP19=BboRXeI!8%FX*dv=Vnt zayw%v_HisFzl%rd#xWoP+Hng$Pvq+to3-W|tXUb38LkNJA5wV+1AB0TZqX|4SrQG2 z4V&NKxF;y{FAWb)`OQ6I%Is#3`u1V2C#b-92t{>|jV}X4>uYxsdZz*4O!nIAn2czY z`9-tEX!aZS-Rxdj|7Q`5vRu8fA50y}`s_%TsVS3(U%o@?U+nTj3g7%n>Z8f_D7*r* zPI4j6JA~gVZ<0qV_ckkf3V^;G{(_e6Kd(HXfVu@_667|t+gkS#)(;aW0=KE4GBkU%?21ySn!7zZ z36iPw`S!HM=AXks94zaIS#0rxp+}(H0lTCvPe0ignnC;KXMqfWhCPRo5tseD)j8-H zC{+UQ2T$T7wjD^|^2iygm*K1V9Q=L?VIfIuiXA2}ehqmDo{&Ldy zAUgs~Vp~*EX>N>$nAM9r->dc0$rF%&#NM*`N(a1s2C_#7n-PDb&)8gU;H%ye^LBY+ zl$}u4Zx4Q8wkA>W&!`Y?qP4basY04j&-*@&-k{adb|c`dVr9FsR2^QGkxYaUoniCE zRh58csKiVTb-qXaCubUbIms2L@POIqHrUe>W@`YVP?S9vDk2WFI#i>8luM2oxz(m^ zh2`X*`?Z_KH+dDQ4 z5tQTL=44%@|@1=%I%QijigKS_UB=9V{F zrXUHLUd*;@*7nBGx0n&fYo2UagfINd=Q_k&Q8!Cv^fFd@_D!tELJEPaA*s(W%{UVI zjj-Y%eJV72DJBborZ5n7POPrDZ~USB$O_qzpT#q1l=71w0wp1&IB(9jXWs%1q_cgU z$aJp!yjCvq*^)Oi*|uZVxw+1Qdf-7Doelo}(?@DJ=?Bc)+i{8wUMp?z=EW_%N=k6` zgV%kMIpZEVRheK7_2&nf(+0y@7SQag^KX`DXg*VJ$;LxQ-Lp%o&o*b39lMg4^M7Na z5B}xT`ZRG(#w)bZYk--G9!RNdk=nW7nAw%2L0>aHoFJYvg`pdx}V1s$!=gFoBrN{qxHGA~t>XQ^Ko( z4MvEm`G&THlzYybj#OnQr3{(%PUicyNbQV~Ps}<;FTP@KASjPSq3RwOs>fr5`i*Yw zL$YPM>Y0X$6uR<*eGU=4E3Dg9Ie~*_MDlH^nUkDUA=ozX%hMtn`~Af4%C_eZEqj%y z6ZNQHtl1OQ-#H#n=ESV*HtNZIzR`_p98JhYj~<1~7fcF>u)w_NX9$Bcp%=;Et5puB z{9RFai0JuWyb?W4XN9)LrwJW#`VrOMp2`7h^IvbPj~1a#+`!`x*&17$j5ELxV{DPb zWk2SGipj778i6@j|2y-~#LBb;qDSl)w@VkN>~wmzHNy4%ogeZ-%Kwkma?AbtD+!6p zo(Z<2Lqx~S*)$v)%oK7p7PCO@_sBEk;SkYB?Bn|jkmO`koV(Sbha^CNi!gSKxo2AtCI|?q-mH={40+^4(0c6Al?W(fD9^sdTWkx&=-obHU}_(9b9~L_ zb~6n)?6(wnx0mFf7UD~OF?9UPkIM24m5x}HSGH%=ZEaLd&|ksVP+~SF%(*ju|IJZ! zwn%Afk4h~wcC)dfaaUy1Sgz{jdfcUX$brQ1Y962uH(#Mk%G#O$qk$-7oA1qep7ArL z8%I&vZR<-A{lU1&>BzKV$iFki)7!M!2GG!Wh|HhW|1!CH*+G6G$3y`hs8hqEJ;Pcg z8y5Sq)r{nr8D@*KZHb=L`gNQV-yIjn%n4Ie-#PUOw`^7NVO-bY=0staR9Lae0t&5$r*5@vB4OLk zIC)I6Ip9s;8`4(UHx(of43K(-Yz69c8tM4zvUZqk_9F^P^7`plJcvNQUG)jO@?nPJ zgbS7ex*H8^&TLUd_x0#XB_sAIPg5P=HI((8CvPzQzsPWPzW4Ps{kPrzY=rjFYH2lp z8TdW3O|BI9Y!6u8op5o*b$?Z_k?f88{D);dC@KhGyZ)!HIzQ@op-s7!E8Qj4!n_;q+(!lQ^e2BSF2L_j%{qJK5+c0&`1b6^j4b{*43@8{@SpY0^q;&$B2il0?k{F%9L<8{m6*WFn)!+R72K*2)Sn9`qvC>T;G3qE?7Ka*8Bn-nI_=u`VX6cJFd?@J zjCV=u(7dvRer~Ih0{Zu?^nV}o*wxJcCHo`mYEl$kEUP`Bbs^yZkaCZH>yN)TvILi-#gT1hnG1Q#r*mOg4GXFK8v$vtjroJKTw z>So-SN@ai8~ zt>iAi9Xv6s4r$lJ)OR6J#>#z<>-O?s^CUD)x6C6ai|%y_JV%9nc#NboG;jgoEuz(1 zHPKt$hhV`Mr^pzm{F<{Y;B#RYoKR{fdqZKpN#5&kd)&Lgn9gK5*=cbUpG{@O%g*{v zJH10Id)7of!56~mnIg{>6=v2O7p@3mTA_|mV1Kz~ zH!ur7xBl?zldvm-!U%N(-~@wwK{f}#yJQun-NtF~^}JgTq<&ujw@D1$26!0f>e*qn zc$jjxfTo&*GF?y#=k49eK`7jMlSC*ZW&j@Y<9Fb{@L&$F%}dUhI6BdWp9r0FQ?G`G z!KMzE_=DQ0bnd(DesEM5|5K%iZVDjLV;4_%>+@o}C(GoS_MW6rrC@;~ViP7d6>ig~ zE;>LzkX4Sbw4DcbVAWRc?b+b_ojlBsq{P-mtnBS8pkns1#SW|c$71&=fUo4>17uiN)e zGfbcjSVthqsd)GU>?b-KRQpf+Z9Y#%<<;E3HnS%<@R;mlS^th+2J}n3-hxm;?vhbN>Hw9E_m#M~s|Bvf2hJ3R|b`> z!RTBk90f&h|E=nE5ox&|q7sZR!g1}ZXpY%4E^^I}?;K~JuF|jrlIW{nse7{G6|0(f z;u9LTb(8l<_qaciv>bPBIsXu14kg^1hSv(%XL&^3#}|PA^iGPZ-0F@%jkVnkespZi z2ZPcKw30$@|82i9+^x4QJk6TJW#w=eQEU39G$&Z{kI>n znA!%xmhVi+;0R<1+Yf}fg_Do_z5sXj>nA6C&~a}?Si1Bt(ha~IS#9 zK%42SiI<7TN()rcR=|^0z6hd=&up~0LnJ3R=S5jj4PIJOLy^x$kM-)>P%V_SMO<@= zGbQKNdeXqbQxO&34PX9me?9>>tB$09Bb^Jph2-9NDC07$_SG2{B8**JL1*wMOWU9h&x@0^KwZ#G1p{#rnQg$w4js5wL=WZ0f%&YvbQR7MVH zxAdLf!s)Ii7!LK6_i0XebEZ4ohrN%#qVao!!_Pu^{x`;ik~Y ziH~=`RPnU0cIfSx3RfZR8rTVR8e+mRcu`)i>GN>aW)(HVZR9htFLv;a6ECfl3)!r< zF*@i3mt5^?&uh_9jWL`K6Vt;AG@7HZrg_`&PHkHK^jB>tR@^ru; za+eumYRAM|y$iolMjnL*{t6F`FRH68hh}_MgO@!jQTi7&H`|#2hkBHAIyswe5dt5> zSoqjHB>axG6yi>{z9x$9hveXRAc%j5E+Iq;AFH?{U~wKia&K80t{e_(_5hPJW9zpR z+g|aPJZKQ6o%*TSgR>w+OvcypG#k8^!qC=c<8+HGrxie5nw3w#&Y~$Y%^k6?{3muA zgM^i-H*($8)Z)r+d$nokzur);sT!D-(aiI&0-E9RT!6bo0;}}=QN2kX<{_~-W0%R& zB5GopNstG6xFPEOs-)ZTlq+`q#{TxdT-@a6`!m6?kI{Vk6h_it9hVZZ@r2WP5piG- zbPF+UTP2VND9sp>=^J7`^KW%F7pNY7x74veGGo5IvE-lZ5r-MS$>hQlTRw>CtP{9S z`;|C+7k{x$@xL8`XZ~oQZG?Cji<=PdMIg)p?%rd%Tv*~H81#t&EQ48WBB(Qa)53W6uN|@t32I%WbxlE-rX(p^eK@ z9he5jw5b`9`SnG`DLo1hZZR}VQ%9y!&QBsoqN2(i_riSN+{5_0qVSJ*an?$ozbL6b zeY*MOcS_{@JUqepPe2xs&x3c1k`7b15$!X9B&+-yz72)j@XM2LkpiTer0-o{s3sp|fRzw`N8-mA}1sr7E5wjjzLjt5$L&u-K zH9O5ExsivOuI$G_cgw>^dfP*3DEBP?32Or&b^}~Qy|R@he-P&f5;uM|T*1 zI8yxmZR?{dOi%Ymd%wHe-0+%;v@HpCEvm7dSTL%!940pRRbWUe=i52)30-Aq?O?S8! z%TX6oEn2!x@o;r&17j2D@pW?wMZW@q>_1${(h8T!D%XY+Wx0ms{tLtxu=L*1epue} z><>`}u59g8GC~?aMdo=C$N98EsePDyxTUe8j@-+E`!lZY1!VK8f|6Mce)#co+kC`r z7?}}ZH0Dsyy9}M${Z)2X^MWYq(37FGfN2TRylJWN8E1n)N_}Qx(ltoIWyhWR8B@UZ z^~rsIbL1N?FR~55k&`&1dG@f7+q){AWBw^wUeB)Jsw1<)bHA=WTx)b^-c>hKEZKhi zQ=*=XlbW_?ZVXlBl*HE>q|u&U1^+cB{cbTmb{O)nd6+Nk8@tR_7g*4a6zbm!e)iQ; z4o%>FB>Rb8sB(&x)l-vRB>4;8RgP6}Z_S&-qa)OCOJyFlNcBP#+n@982B^%9)LDp? ze+*&sByz}C%NHj8UPm|g9PT$^p6*eFiS=Idw4GEsn&(LFBl%(TYGH0UuA}}Xn`3wI z3BSUE<84QysKzV%P2h#-c2m@_*$F;xR!tf6)zQ&WVx>fXNZx%P-`Cx4ra~B(6xi=pNVa>>Ivq z`5y;}TrJhyI}~8FK@)*3VnDQW{qxW_u08B`?=O`9+kCl1kDDl=n6S^VqPg3sjKUzi z3mZ=eUZkQWwC@_~FdoWAwOvCfiV>JA27T1`8w7hM{`y{eo9!nNKshPV1pFjeqIbLG zA(yb~!|JuQ)YdBpmO-(sa%eZ0>JP}w4=AO2HTnL#-j~l4#8gUOEyp(2K6GX9ENsr9 zI~3V;7MauCg*GtV)86}fvj6doHWIe~>~ga4PHX#wFlYX{q?$Nq7$Fu9Ms?DH-M2li z5f$)4Lz%VOy)A@w@ISS7$#&fCCTZ5?MKkcz-#lk+6c9%9`-mv<4!_N3w$611>$Kdq zCB|K1=@!_mt8M`L0O^zNW`*d|J@E*I^nSm`PJxJrt4o*A#bTG_ilptdm7@ai%Elfc zBw|QDhy;>WvoQ>8bCq~SR=*#EO=$PN?qLKl{a7td)l~_ggLF{zVcA4L0%TF5{pKMM zIt#_cB^x4FO;{v~a>L3n*yKt-xrnE9L)bS4$4>IsF~(CEBc@I>pwl{++!FlVes*|$ zKq)%z!P^7Xz9`W+BylonfISU_-sNxX<)^#M!HO$T`l`AhAPSRC@N5mh1!~IS?s{!x zNI=LXUs8z6nthmwQcu`?eqF5!(EP&l*p_AS<;08Ek?Ap_aD6jjS1pY2Vr&l6{ zIIyla!3yZ1D9_s14&Q1gt`l3?ZcviJ!_D%``fipg&D6c|C=}w=oS$@{TYR--!ALmv zWM&Q)XAhw*g*O#BR}oGP-+#>ekQ7unTb8~tpox&!m+ntx*TRqx{&|qzeR_t(O_3sM zHY|BgHp5gsC;r6X3A0bjUOS#E z?=E7t4fme3#dAyPU?<7N5@jh!O8wCW-5DO_^Zxi?qBG?bLyi`s-y&bVy)a)Kf{_L z&iP5*jo9F}Y>&o-_JIRn?${Zs*eCc5S>yXAsQ}hV z?-`t%tUJhUZ%Gi6e74o6R1Xzy)P z7+Dxp1JTz!dU4{Zlr*y>hMbty49ZFaS3Z#C}6+WVCioRZ`!*z=EBtv>TML)%w1At?ZP z#Nt(WXbSKgf$t;l`slaCRr~n%mxN|HWWG^QCgiZF*aHevK%v&-iEH77#p&^D2*Vzl zry_>uGg+3q5JR*FpIvoR$pio9Ej;r@V5__RfWX;u!wtoeWr|W$XbCLQ%kDbA1vl(p z?GS3Vy)2ceE1smLUoRh;587vEjw)B^%LI9cj$VH>XFr4;TkhZ% zKT|~17i%1Xdj4ewmbhij#n68E?tPfG>;B~(Llwy#%zS+s+i~27)iPixQ|#X-^8qB* zFI9{lWcLYB&J*i82*lk`16135Klu#i77QpY+-aVG#(~8Eh&;F|mZcD&FGh?}Zi9B= z>xgI&Q>=W_#&Gg-0(K6ao~clA{e6$qcsz^sE6p(RY1UrfR`;i_BM|p+DB)m(-PZKX zpv&I!fSmZt_pTk9BNDZ64IUk&cd)}RkEz+ayo~>^ zkO(`;Q^wL-K)}F*RUI52<4{@Srl9?nI8XKF#}a~UB#m(*OXn040<{4@G|kk$$^(Xt zC<%|Qi1(69Kq_1d11TWjqL}zt-`t z|Bbv3`x5lAGew0h>Qb+_IcvnR@)3asCx|P}b$a87^iqgZj)wb#kr5I9=PTzx0bF%% zt9M$Qe^meC@nTQl0Y9oLM^E2EMGC;3ll`?6oNLeig>o%kX5)5UcZp(Nh4p-;o15aY z9+-2^HNL(VeTLPrsoZ+HbRN?j|UsHV~p~Y+>^+c8%U` zBXJDts0j7Cz3Lmpnx%}YjT)J?#F+e6dR>q33fewNOwb?MQ*UvWv;n4m_CW-y)4bn! zAxwos2P3Xg+W>H5At1%RE}!GB(%0~bEO>lk%pm;6PikfQ`843MmevJMxlS#;))|T! zcg%LySRahI&GOcA`@(INjjUX~K{Lx~{2Q;w9m`wXuqb3i)!~?;EL1vY#f5W9$+!cb z(s16&Ugx@l1Ww|nF5Epiqan*EAzzEOdi4y>>C5cDb8umQVV^ZnN8T`l??knz@}^Cmkt>qb7@Lp#bE)URxgzf}VmqmkN2Ea~IH7CB9T70&zNm2J z^X-`ld2Zu5Sr#vo%zTDQufr(l2P?K5UEB zwyTxcx$5XeCm4!Qc}brwBLcX%P~RqR#I|Jm?_X%7nwn3k-0L^Xmz6FpKA4aE{nB+o zWqvk+>;U+S1_;r9Y+iF-(9CeIQtQALK0Nhx0dECn8#tSPcGZCP{ni`xlmmo@1l=Ea z?uG(;dk?$>KAiW_Yt`U>Gpfm6p%%B$7K-nt&Qt;#DJ)d|db+95zO&ljANnI~GBx1V zrx%99O}4dVMUtgeE3p|9Ra2UtO`iH@o~^wNnIJsn-1TO!f{WF>CZ&Q*ZHM^RYGYwY z6W()5$+)XPxnfpi$UyRTFgUieF{X|(qSYxQ>{sSc?6$@6v6qVJ%ic)GoQ&8Uf?N&u zFBZ?zt!gQyaB2Ta%ljZB(}b5v&yneWNSP#jcQag_^Bg}>6=@{@9lXVYLvpPWW;?DW4)}S6wGMORfWMu%yO$&;`HqJuO3M;jCAj^Mk z4zBdI@JtN6sJN_ftsYh!jM!bX{Edo(F2f(%p!uEsS2eaET!Af~%aO_7@Im^!sd{)? z+WzYA<_|SndfUtHYsIfd%g&2ydi0UoW9sD6rdP@B*5@asFBMKs_UKkf%`HFMDu&}T zaTX}%850IJEMdtq^6~ScM1L`3n zGAeyPXX1AFlNCGav)0q@&Ul*Ic9-tg>|>jf!Ps@WpWOZIeRm=_{&HpMP60KXO!nC1 zFtUhg*362bIL~Ku_Y7${Q@M0x-c4i4WF2z(!1|TR3{Accr{eS*>WG0FKZI8b?cW77 z&|ZXs_;n|J<(82KoeUQk3m6bsldjh|3>+fkV!L~EXxRWq=Rw!SK@HdXP=wbHMPW5M zmmx?sK$Vtk^~t5Q(Pz-ASlKM?&{#)NYi(#Q)K0^dz^}e|PWsOS(Ji)RXiI>^+%6GJ zfw9(jTQE1+_mPf_yIi-~gzjyd+_kF=!nUijZ;rxEoC;@H2O;GB#y@Nx|VMRBt$H4Kv0 zRRL(yymvLst6%b_SZBR+cCDX$@Tl1WCQCWsu&HyhOvQ0XCiqx#E$B%`;L67TBzxZ2 zWZ^oN))QH!SBq-aEVDN%sUqYvx*@N61`Ve^?EH}@?fLkMCN>YOMs9yM^saZv0_I^i zq$yhH+-C+{WwEUD*f8Hdvhqg9|1*eAfHkC%Pf<Vp@_Om9V5u%e}=oO4#+kZ3U}G z^2_Vjk#F_*jNoCxq6m4Ov~c7|tHqCn4b{ZXy! zED1=wG*|2N;l(k*OYTPd&U}Y2xK3HS_LNfeQRdLO$>~ygoik^7EkYii&$u`33}S%B zgkAeXuc?q!qt8=}Y^(1w89qWjC(j!f0yc^x?*dP}@6ZX&iqLaZD#esE_|5Sl1>~um zh{=ik$19=NjlV1F>xNH?+{4*Qp zxgqgkmN;}?W^4WerD1xw0IJnc>9WsNybZeM6i)F(o^KBxo=vqhUq={FOii|$hPFG( z7LtGR?jBge>+F~EHX$hFCU(NVKSR~Rm}qilu^4I;<=U(fYE(*K`yhZLS;=-%-d-uR zq$HTqQ;A+OQQxd841cFO=zS9+Wwe0W!Db_pgUuEA?WxPc0rwwoOyzV8t@V%J%MW&E zA)Qo^xf#|9QZ%U-)F#(9Qr9#>Jt|XfbhbI6G(OJQ7v-3`IowQSzi#@w(if6#>~zz& zxu#QZybrh2u!od9}Gim!Dl<7C2t`jJ_PY%EXSluiOTLK$^ zv~JFHQ1>inpswz1U$nq)w_`wlaGT(0TVGDDk{@US#bFF`9B&})1?yr3WdSzchsrCS zyCkOZ8MZIxVSAay$Kx|Vvh4G!5N}D_qnOBH zZVnQ=Nu*7ieKf7mO3OU|GhYeuhk}`7)$Zv9h;u_>5y0}4F>G4m=T9@3fmdK(zj{Qv zx&(j4`$J4ulK=(82PD(q{|X-+$ebNNI|i@VE`(;jUz<}7w;Mf9!Vb<@mctd=aW^Cm zTL14Fue>Y>48oFj!1}1`*fK9W0@p_D*h)N9Df|xszG?heAxXYV5&33TXYvcdragh1 zw6mMdeXKI$Z)pJXZR_u<^SCVT-2zm=yACxeqe#a6`x?xoLIz$U=+9`CW3T;F7 z@z;)%r)SO|dKRD8-#NIHw7ygy8i`9j!)`ha_c--?0}^aM`Q#qV-`n;UVMv&Ao6rAW z04JVufq}>Y()}Aa7gIBhzH(;|c#F@ys z2lTz#432ub$_qPJoEr3E=Btsn9LanqvYzs+kdMwZCTf8)1E@F5BD|^cbVEp^F`3i< zOiQ>2{39r9%u7Vr>6s;<{QXW#Zc|q7QJI%I{>x`-&o)m=YkYiWN}9HZEr#!G}?b-_Ng=9m^!hHLp`~s9t3?L&q2yX>J+tYV5TDUMYfT zc~U0;IRzFjh4`u8F{!%c{UmZ5(Ug2k8n@C?Q3`GxzknWSzYvSfVzkA?c*A*OJAY_W z9l_3wFNXyBa4m5F?jLuX$P?OOyc;dvuWxhzlmDkL|I{D*CAzCf1Qxqvzyf{s7wRp? z7+-%LGbA0{9r9H9r9LFgC_04`U%#eMEL|PGZQ{`;xyj`su7uc2fxAi_Jks35gnKEt z?I@=(_knctVL9B)adNoj&B^{3|M;(*9={nh?Xmgd`8t2JA@KO=c=mQo;PRaAbU`Os z9-IUL)~IJ%*11rt<}EK3|NY?1{2Y}fD+=%6B>flGa@xk3@L`KYz{y}to^?YxJ8D!N5I`?wc8?>=uK|Q+0n#x zwFzVnR`+AouOlxxUsy(BhW@T9ZXz5kkKVkw83xSAI$M@y8WM)jKK&@I95(dEu7cj$4XdcDh!YG!Tn zk{qgXkgJapu#u=N?m$}@$l~by)a}c(aKokkc*;;|&kuD!%26d7B^AxoYFwK(LVD)H zmsLS8+Vg0M&v5^*;#Od>)%twEMh6v!6T~0!0tE4ZTmISi?-qaA+*s$g(v$5=dR5_V zR0X!nae~q$rAGABfz9= zA%N<`V*j$S1)cHN{)bJ33kH}}NKLWksRkN(FMgdrAi96W-`^CNz<^hL+P_vzFFnw! z_`=&tRIpxHRncBfkqAV;SY}rI6no5QoinOCWF#tuQ0OMH z>w+XCOv!aH-)e)bRVRY3YxQqwY-NTSRX-v)d(;x7#j<9>3>&8S>yk}@Q<_mbUaH7m0{F3|3; ziVu%mO!am-Db*e;`P14sfspvRDppYyE+PVC?iJZS=qcM*ar>#dLDeDky#v)RH5t_L z{+($9jNgW1Cg5RqjRW1Ob6U4sMwq`gKJ^{0;&%~Dz|zot>AL?C&gC+_E|OLO+MjFu z@R(_X{o97O+twPfssY*J#&@;363?~1zU@ywXPkR@_rJQfdXLa63%t8arPyOd6$ z6KpD0Zul7A7{ND0WOBe33?e1aDW zv1kEYshVwS(MPTvRA~bE!ffx@s;SMeVHB$3_-*-cvu7UlP7}J5JcvGj#7pwbrG=5L z-{EzaEQO%LJ+bDLRyCUHrG<yJYZsl5EMQr8Z7XJb%b?cQrR@0MeQ`?#Qq~MSnvM zn8Qwvli~d457SQrtP70ula`PS6Jq)C8#vvV9eG!hxY*auFiX39MdXz!4Gr`VvFGd*s(&e_b2x-S>X-*{2_?$55%RT*gd9fe3Oc zMe?B=f@)=j#kSFcG~J-!{LAu#@zqDl%Wb~@B=t%UGyD3huV5C4_uhN|v(G+cKH#2_ zI8_%d_0WNcbVg-2oj--Zb+o%vG~QFN_43S<50QIDR1#M;!Y(Db;qm!2XUrq+j?2@h z&z?X3wq3zE4OAuynkhB|wEn^tKUGL;@i&S$T<3>6Q`m56z%%74b&p6`sOG<)#kHZ- z;OiPT^OFSj{EhH<%ldxFUQ9_FMQicsd~0Dy=sxb6wa&Nf{lj6-x@3}|*p)vl9Zt+t zzf%w4o~gDxGw~@Pq<#UcLOge0C;>gy`%>$x_;$U%jps_&oyHQw zmVZ*Nknyqptw{n(@p(+$U*Gy3lV|FZ3GW=f@hfuM0#P*C$bHRy&wsA@@7i5{Z~R4x z`FZil6Vm*5d`YBJrn=)p@;ef!WDkI*3PQN4M6G(fY>KHbqkp}$HrfhC^zgE&$wnl? zLt;5*tK~-B1eetw6$JFt%iY%aUFB9sP3QBRPP%4O(p$Ftbd;G%m4il8@~bJByW%(N z8*M^T`Ja)KwNO;;3hBaIlD;3!zin6whSABqxxs8@NX@PqWU)+ib{e$emtH{lVlxm$ zp>CDx{=ADEs>kuA99^Yfekh=pwS!gz!~%#lYKF97vSSrCjDieS6)TpH@29MY-rpT6m6P{}~3X zc!FjaZm@Ls;pXOUG2p|}9m8Q}12Me(_}qcvHVj_i6(`Qw6n_}~vs%fUD&6&eL#`<5 z7|=~=w9^ewC)sb>#;D@?LwySsbhZB_pC%3OY;}cRUwF58&@HeOR+9B`o~04(V1o-q zcg)=3KCrDX8r3CNl+_wN3dXck z`r`GuL>ts1WOJhZv&@0K#WPA-F~h}gul+C#W8mcer~j)zD&VDHx>8vw2{2+!%Fk77 zJo&8fNzzr(q<(~Vj~-6+hcrvcKI6e&d%p4fZdMvt^huR@AIt4}gNC~BDnsq-JEz-` zmWCrFbO9gwzL<0Be3sp19>mUc<|99;G@r);23T6m1BRC`z70}C@%1-fWMf`SGcns- zmI6HMg{;${5s49eF(T0-M)Yu|SP_=IaT+2#6Z0tpc=qfKA?vMgNbb+=d~0z; z9)*R^!jdOiQj?J_Cn8_S?S2s^s}skhv64G`IMvks&48Eb!%x8vd~f#;j8?1pG~AJ+N5FO3mjcr?Wq2(OK% zTDQ?Verq8qe|S}VgKhD##itrzSnDgMR7$HC;)@HCo!>@m4+35WCJuydQn5$L@X+QY z8b*?zzBJ;MKbKnB(tizHRYsix0)mEq-qCmwwcB{CE&Q#P+xsA05AZNcQ|WgazyPQdLWD z(=uiv(c@3XZ?1%IoCVGgT|cE+u*J9Q_=0rh>M~OOV8u6VoCxdM(>h{xzTxV8(CbjG zGGBV{OK52Kv%DXFX)Z_?i{r;OKeoni*YUB9KbI$9zb~4@$oRR%k9MuK`|B%drnP_W zoj+^;^{6Y+G-*4+=ZSO|{pDc2JGxiH5uT=TA z_a6bb`lhmV53$96#rSO-zisocy`lVEfL`i(rxd9BLA_Xq(!9#91v3Tij!^N-W3fz9 z#IEX)i1Z{C3>8nXocMy;7P32{#AmbXkvTKfcoGQ7%yMpms@>Bd#?nF>SnB?jCK*dy zo6n#re!FF-5Ei+aFAK@Ss+>pNc?5XSH;@{Wx!gArhMYv@R5g*R&5oHHK9!OQlHqeA zO^61~Cg&L6Jng7xi4~uho=YodsbsbPAaob@_=z75JvTjx7m&~3#HxH-6Mbrax_>19 zh!!2<{OgNZaCOIW2CntRw!RWG=K5}MC6ft>(PfFG>5fmWD}oP9QcSY4sQ$5h2rOo{ z{+DaUlt+eZ|F8L9yozSI*8b7_%N)@|GC!06?#=`zw)IsT zxo>gdI+tjiFx$eAvpe1lM4dfJpS64I%clnW!%X`dLeo{2&8deTF~SLFpDd-9Ff%Kkjhe7KZ&9(|D z>>kBSA}s4+X@!$Q1mZxv_!iPW$UXV6+;E+=Gg9R;6FGnO`RxoKBVwA(TE(Ef0?!>- z?B~DI@@7I85kz`Qe4*UCR4)y>Zlzhq42MVN=-Grh zys)d!zx}3kS^mwxc)GpI2xQEhtVUr{4hPWrSw^E#LOM~JL#5*90CENpGZBaaT*b7A zh@$;c_74kLY?KXZ6ld*J+PS6RHMX2^4LM5&Bw-daa^jegfjJ`*F^`y-QMxzv8D_#G zG9&KpjuK*e`<=II7k~2UM`jj?oAc23yH)?29ZX{yAZPaXmFr>4^S}9n)DBzw-`4p+ z-f{Ide(gsykeACbPBzBr?YG||HkZv+R)-t=EZF%5)Nv0vbtdaXF4jh_N<2Y9jW2UO z5mH^fJ$@aXZ9x?76kmD%?eibM`$0s@G_=zg^Aog!axSu4GPSY2vWZq$D?x3IU&=?@ z{OeXCHb@=RsDn@NJ!SJ3=U zt%&ha{*bu8zH6bRU%B#UTIf0wmUCC_fND*GvbEEnx{uLRkHm(eWqu`hCC2ak4XuPE zB79yu@jZ<1uZq8qzxXlAuh8*RPULmIx#EZJM}nubF;lu9xyHm07lJ#s{5-Ip7R!ao zZN)c_rPZ|*ODq2uL%~;mYxm=4&IB?qNc`$gA2%aX*)ZftPC}xsdn_9J&LqcaqG=nN z0#{Bc;wPH&b9ocffl&LowPO;SlCrJyCtJryd_(3cP=?QJfYo-AT(Ny$uZZ9BE=%Xg zmd|XnQ1|%W9$eD0verfvlHb(uxRQ@ieB0uC$5%heo7MZO_}Joq|M+tofB9ps`Jrz~ zHp)!eg7@Qp|NQgWURp~OC{9&ZA@%+hKR2IC+twF{I=*k??*tdC5OII}^x*4m(^Y(F zhi&uk+x%}bS+=Zf6(6sO->UYNKP(FQ79Y{5(rm71CmKN&yp`WB7cyP4U4PL0bbKKM zv1SO=@FH6_gpp)@;}!Ak`{Qr(-wqz@7kw~(i%{`Ry zGX>AC?U=QV&PIc){KJ+M=~9npN5fG*I3`~PUu{ZX2pYcYSdr!TYptNveGKIO)tO;r zSc?)RP3giwb%2(E2~dl>8W;;Oh)2Eg>vrdf1Dokb^; z;#gp*ZUY$E0yeF_jOKW^fkC;6QTuD_PKIy|jhD!VSi=lCHg)jQy&|qJnc@(clsxDY4)QL=Flo(d>H7PgUu>#&2$xK46I9aEoE?X2UUnlljSVz=vbF75vH_ z!+ltuh8gtdX2a6WoN=tCQ;hdS+DxS75qWuj$EJ-9A&gT7YJH2+BSmb@ zdMV~{*bGZpMARFd2}XJdF;VA(cl#bGNWQ#8*|%Khvn|npAy}kNTHhcRE`SRmmyIK> zCYzVjoO@A*kt_=XG?FY zkh|Ha^9>2iIO>IV^XR6!FE@{#!0;FTef-b<>=EiKgtj>ZS#1A^Cr#UXz8&a|FX_U1#ecDSbaFR z*l?O)x*+eQ9Ou*d(dp*-ci+}c`jel$4;6r?YKS#9`OpQviWYS8B2{z$ zv+zT%7D~LK=BMtLdgIS;$r0oC3*NVSSWwL~k@ey&{LZsqDH=EDjb_OFo}6yZ4Id-r zUVv*J8U20~Xwlj4hVm2Iy;i3!<)z!`Y*Wp?$DfV(EfG(y6dwaacy;AhSosMzqiKGW zVW|+d=7)fh*mwZB^0#$UcZX@^w~e0Ol#ce*^ew)4@!cZp{5d*G>p?=C{!rCTo3}ElVbV+6zmaM~&;{%mWX;1f%nZWPID==N2DZd?9q2ee#O& zub1YUB|(az_bK=1X1=-*^bNaGcE=KkKys|0@qErLes1w?i=WCL*82+^uvOMWVEzLc zGWBn<{3};{EVM7S_#3xI7(r z?_YUB<`y5@`H5}(==X)rXVlEQZAh1nAMH*{J|j7EA!l&gDiZakssFd$r@gXn+x@b> zFV%3Yk*8SWPkYVyukIHIc6Z$+6S%wIHnLTk!|T+tE00Si&})5N&_?CM6nAIeIG6a2 za3+#Mpi>K0{eDBnObANPtDovwQJNr>UNG#%yUfiufq<&e+X<~-1QhipV=JnZ&+ZoA zQ-TS5e05g4?;5MHy2g)+pPQ5-sKiV!8%O{qw+$l=;I3gCU zZ0WElzHRG^ZGFM@B~_~d``smAwR;)#ZSif7zx40d_*t-D>w8@;*Z5lyY1g14r;kG^ zdDG}V4;#MBrY9C=}LX&&b8o;l3T z!X0jKhZ}9QPli#zE8uMKB6^vf+}y%R9>g%f#;|n7FRUYi`i=Y^TbX5zzg-B^B_7u2 zPuJFwyUoR9E3E}aBC7L%)oqS2c?Gc9COi(BHM8bFlYA6Zd7_{dX;Jn>k(o9#BXL9? zK{*`JT!eC=|BPz(MdNAuu^B9X>wo)uN6yH&l(AufZ|bKYV31%G?Dzy}=6vMj-QuaM*Fos&k#Z=0EQ)cWkCZZjzYiQ#*1H)7JSzMwhjiKk#Le zx!*6g`vu#U|J451`OxvHly^EM@_arg;+XU9a)-7OjCB4kMRHmK!g_*xOF*p(NK2cV zr{+wF48-Hhr7uHdF-HIWEq@!lm*(x|5@yeyys^tX1}pn1Q>g3aacF+BS@|{FDGtEO z-z0QPf#BtYC8f$0UxP#EhiT8ePUhHP^tyjb^`$A!GF;O4+m#CndSLTqt2-TKp}x*H z?Bd+(e$=zY+Nrbd#}Klx8|{rEP=2K{`{H$>LHH|wvmK9STYkRsH*7ZKrF6685+tv( z-S1RnUHNB+SovXY`C%+_CK^A*{Ji78KEGOh`TX08kMGOheolP6F1|gC?}$rnWi-)FIFPCA_gkn+5VzYH=M9*Sr2)KfX zja{0MJGjxtR`IdL@Avh3x|#O;)%Y17_s1U(#*Zz2Zt<7J#)J5_#lN?{_u@CXyT4yN zh>tD4y(T{P@oO!&@B7~S)V8OaNvZHb{1V60BNo3v{F?vR<{z(#AFqj@58~&&@%z>I zX+h0X`hI+D@z?i-tMzS-!L7d0-B7huuh&=glWqQA6F(o0f1m$cA zbJm??>8+njLH42w4EA6x6@RG%1kx$vzcUJ#r;;ZoF*V`T@<$uHdCTx>{=NMLn;@Dk zMK7Vzb{b%Ob67et6fTw8rOm*d6j3m$791c;2CE(4PtlUpFt7MpU%9+35p%9@a$9nJ z+w3@(AxncjR;i)dy`i_dZSl(#(JXkmA*-vKRHWESI=D>jsg)4jk&^|p^Cu2d zK`G>tY0I!e-C|oxk+$8D`}$hfG^Io9O4*iNUnaKw)8$Vh4m+>5Zl7y?OI^9VsC52m zeW85Il*6HMSxbf#je&=>c{%?TA5Bo#>4$|GtY|fz4Xxg=}g0ng@`ne~T zoWvMZ$)y-uLt6EeHuuuGUy2Y^t0G+hi|cN1x*wRkS&3X39QIHCm)?N{J_It}>qLX>a)8(M4AwALP zcM4BTmfsVJoPmVTOiwTDSwkGL|!g;cb5wc z5pg=*jMMn;yGy_O{`8-@`_KLt{y{;b@hkt~UvFr31jXDxczc;uxpW4B90`2+@3IZy z#`_U5AjRI>-;uE!tyvo8iR1IUl9xYYlAT?sc>1`|V@}w6kIwlIy}m@nj=WZic!3jrI4}%1spfy+E2` zbv{>uY?yG*IStFwtYqG}zHXS3&q9+Q0bo=LtmE9Q{5Pc%pvs2w+q!a_(5(9xwu+v< z>|BsLCXB^B4@{@_v5VOzLz6`4#c#*kZs)u5=Z;^x0V!kxU3cgYqx@Xosq)+Yz65bm{_m;pm+{yAs}AC; z;-l$(c`-xcu+4H<>6Q^UwB7`0J*0-p(-v1U{+*u-Q|c?DG|!cv*L5@!(>5(s8iUhP zh3988$zj$eH^Od7kZ&Drvd8572mQ~Nb}{Wzae5|k8yVom(?25k$w%!}R=}{7PfBfh#h9FPIH(4b+oweE}xb{EiUwtf5iYX9c zz88oovc&jOP+$48oc^%D)Yt2q*YQp3yIBof#b5WA0=1%7$A7rKQt8?D&o+Kr-!B$0 z@j-m1CB43Oy}p~5E0&M<{rL8(`0*gV<*s|xb(|1_l^>bJB{@IX#&3MT+#f%EMY4rk zet!JyCD}|Ges27AK3l68$)vG3I($lSzwGmGnxCot>E9Dyb*^RwLh=1UeCxA@^^Fx@ zUG}T_>1xBI|6Yp*U#%}IQLgG(CfrAGe&A9h22g1=X?&imnqke#RDO6BU*OwV>UN!BLq(uGvrFt7%2EOlSq`Dr12<|9n`p0wE4g2>%ZU zW|GrkQaMHJ@k>OMr4bc`%dISpeMXZRSgLkxA=1He-|HqL`Cmw9M#pa0a;&BsnhV8R zR)RFIajf`k;4bX|Ixo?!f))`=SwNWlP-l(oNTDvpdM*<%3bxaRxV4j{wv+2wiQT7+ z#v@4M{CE2=`-@EqzalXBfnZ->t#~fy>}4!j=7b2C544-%tKz5PbBpO^ZtYsVGPiaK zs%=&G>_8XBHqV;PxIH>8T?~0|>>HmZ0a?IX&wt%%vov^DzNAx&Zr9bkpqDf{3haM#F&@ogqYOE{S35b)8xAL-i*h(_OfuDTvYk%qA{&RPCFD{qMIGr-^a(?&x+n*uwGLLaO zolfVw-nx6tF!#iWvY15zILhlYU=}fPT#lQ^H);eXGREn2 z(foY&>4)#W`@tulePrFpge;?$&d#zByLZ1JbY_nQQSOdn#dq`D z!-vHzvZreC-#!1UaFU2hi&%yiB1$L_Q!TPQ}FBvf! zBG&dTLu>(OBv%l|t!V3@YBinM^3R+!q?P6XtW#du$~I}1=cg~N%PaU(^%trWC?Ssp zFe+}ZXEVbyH`KPQJ5+de0dICVbmhIZ9IT@YLEr9;SbX_hR(ngKBtu3!)`}bE_@;Mt zdx}w4iJtNmUj@R_(pvH5@Oyv!xy7feGTJjSJ1yGB-~M^`;)DDDetc|(ON-sR{N`!m za&P{7_gix>+CWe7eT#2f{H+wWi59+UeHY(+i{JXT{tJxXUN!z+-|hRln*VIY&EB82 zzNP+h9pAS2?XMWtZM4))rTF%m_|`t^-1gDkGGDS(Ykehx)Y{V-%Zje|XBUwPb6V;b zS&hhCokeR*0Jix3ePw@<_u|U}@0Ia+eyX;(kH6=~8n(CnSZIFQVy~{!_V!#(AAVgK z0IS3I=6|<6mNux(8!7xYV}9pn(ht>*bk)PaF@}sF@g6HG;^-K6p1N zw$i^IJ%zVYHzr!mV_0<~*<&m%A|W7(h39Sg&IbqQw6ag}j4Ek$%aHf1Rol1T%goQD z%tCxhs;&VgnAD2#Me8#$TFV!fN~HzNtN5in9VMtx$VRXJQt+Im57(04@MgeggHbxZ zTKc`djx9b3Ox--R~WkI%|QTF|$PQwnx@J1OZ%suAh}>A*H&H96G5-9 zNG38q^mQY42f7-&K`bAt17v$oyNnRr4%`%t9Dh ztZ86;mg2odGIM#V$VpRB36rY@b#TnH3j(flkky>rbi}4%)I+l#Pwh=Q;0`$b>1-s) zW20W{?r9jcS6LLVS+yB(_slG~#jL7U2@4NH!lTp#=@U9BB3MAp!kC1)2dh)zg%S;7 z*`m{Bw@&@}F-hi4>g-81s9OEnKP;ZPJeb0L)cho7omzzvq`H}Vn`?^=WRZEINK6^8 zTg;p%x6F}lkzP2;Hj}RdMjhr&sJRM1e)rwWyTlk+!H(X9T2D$Ifq|I}={bsRBH;?>G-H{;uY_ zu53}AGc(6Ia~4A|YyQXSC}2oL39t{A+&oJ2Kgr3h0<)P#OvC)-?kTJk65Wy0S=h_8 zx88a8Kl6wG8!unJsHfgnUw`p8{{4S1j?2sptZPF)vj?E*w(?@~~05V2+ru zs$$zb4jbd~lSkDz4!hnpM6wKpkvw?yx&=W5x%3KkG$V~L(w`R zk+}gIluRfmW8z(V!-XrW>JlPxGwg0=Y4kig53@&^`Np$1oaIfBhA-;FK$O zluK+9sa*%80H6HUEO`zrs63&=u{=jraUhDwl!zeHUHJ*fTu!g%jh)VB^`Q(G#x7ad zLl?7IJsEHQW`6Oz6gV|llnb!M_rBKB;3Er>t zU-8=)hVR_U4`bzrY$uu&Evus15GkwAo0Ki>y7hfs#jpM}s-@{XVM978RLtDslO5tJ z3JTA%$4AGHJ-)B_>Wa|^knuAvEdqzkKk%DRR0CDFyR}(s(dFcBq8?mzWm;tcEv*;y zo!W2>x(u8>?aHrw)fs@iyz5Feq}Feme;#1! z!zDeYA{3u}9~PdKKa0}`=6h_P@%+hOAbx!kjBK_Qw%PT07=Lv~XwtLdV~bxX09`5C zKfvZA(H&VT!7+a$x|U`CiVts4hw}eI8DKZ4wk}%p{n9G+(o`*Tdp0K(-?sRz2U94C z72miDnQgzk2!y^=maoR|*>1BMzx4D%<9FNE*SZe7$}{)Zx9mk;6~8W!Q2bioSH`dP z#RA52{viXel|P&AwO7Tb^eMN6t@lytCfYP@ZJU3tui}&JuKBSozAgDiZ7);&EA60H ztZ(1HW@~;@8rD7jypE48zH5H=^ zzTa#7t$27He`}-l`x@(|yv67I53{%$f7@D+S0dVZN7*6yUxiChN3q1Ce6__3ca)ue zcB-2Q69NMAK#W{^H`R#m_(uAo&0RREaM3IR+G>rln;{RltMaS2(QX<0?YirqPbwxIdx%zzS`4G)Ui)ioRr8Bj}9?H^x$hzzVgIqTfE%tBxhtQw&6 zuv&|_g0)dEvLQ{j2xBUYnzg3mx%!$RePDK3if8p~Z@|ZX> zkH`oF;($f68%5oLQZjOzK~9%iLaMONg=50J`hMY28XxlWj*#T@;;vS+$t_rpt~)iT zG?E^H)5&WYFkxp$IAhJf{@JGY9Oqf=u0jC_1g1&fyBC%EY|(V8#4w z=`Z*GL8|l`A~9QBp1*tiTOaw~vwSD3Z5Ff6#Pj(P+%xUPchBE`>qj5_^fw^qtjgw3 z=SNl+T->qJwx88c3Ukl77)c1@{wG8bhL~NN6mW%wI_Y-jRFW&84%0p2rv#8FL1>HB zVM?1h{WNZ0+#zu~jmTNTY$X{NGu$mAXU5BKpTGU~58Kny-R31`8WK;RyqU=Fo`2it z|5x80dL15m8)Ef5)6VB0g5pZf$};ojXBBX2Se@ z@|Q2~hBpKpr*Tq)PSp*&3EqvEwvy!49lDuX^0t?CTGKd zbX{4Fs_{(j&xFoF`}6+c$|zJE9Qf<1rh z`;_s~;G28#+hXK< z)=1?oes1yQo38l2#kVc~)&0vaX2}zYSLi`k$80cz_zv}MXMF4Uw#8rnx#DN8?*v=? zmXm#TeB6)Ele6OshSRW7+u~!3FRe7Ix-&i67K*BJj$U7_a>tkATgR94beB?-l0nz; z>qZS~Q*G;utMyH^Ht|(_Z1eNF__oDg>&x?JmxE(```-Ar#kVc~;=(tD{(Ahor&Pb+ zbF=e7E*@*=hc(8EU-Q56lk53^7~i(|+xtrsu*J_Ue)j#ikDq{sg3~+Sb$q|bj%b^I zL?|`Mh7*HWY{nlezi;PTs9)1gO3L3X5!ds-lzv_v-wA))X=C*TulczbKYw2Qp1$Io zl}^E~ zbFWmTrq7>>9~oX;x#jOkVTULb9qTPrPOPWXM4I^wGqeBp|N66&e?B}o*q#b4p6pD7AB}TDjv&b_>Ok;eH(T`?9{T5#AC3qI*%#vJ%A3`a;Q$_z2{ zG)w{s?iRK>?@{%tGNqX9Ml7BEq2|*d96HhPz$BZYB zZ|YsTY)XK&|B;uRFOOR@LjfW} zk`Jl8EtlV_SfrZnZfot8m6(uYW?&2n=yyux*c?0E8h9ZssH&*j+>%;)exxmv7>Ew8 z{b_`ENcf>$Ws@3OouBX)Z`QQsYDc%QiC&130k=+tia_4RD|m05G>$nb#E?Rw?U^?#V}wk8+3 z|M%i+7GudVZTY#1H+XgaAF?t-Ke&i(tp8XTtc9%GqZG0}B4_kg7tPOR8Po8=f7LM1(&+GWDCb5rS_g5zl z(N+(o8`JTz%}-v>kFE3t^7h{5N4?&97Wgu)<&T3c4GuXqV4ax0KffjUi*0?sH@sjVVBIIO8a7W1=|J zp^H9)$ZVa|WvGj@*conK%|OM^EBzyGr2+EP)G1nmy^J670BVB}p^isJ?9Af!$yFi| zwuD==SF>=rwkjl&D_4h)EGl^H$?PdCzmf# zA;`!ytSAdR)IGyxUnhE&ZR?u_#o54yYO;!n6B~cCP2r0rf|i)7D{(FmBMU-6dEI4+%oCNgVU@8gzDXwyl?&!vRGFwq`g)&@D+(##vz zcbXw8WGB@;NKv}1x%+Hez??YnuHY?$P!_QrH8YW;c&ScT9HWh3DQV;|cf@d~&7Ao# z_`s0Ity}H|idw95dS>JpN5ly@3RyV`^P0}Pt^6G7!k@e?YaRmI{s(c%*5_NXGt5SG z@6p!_qHy)~PVVzZ4w9<`%N&cn=k0w?neskAEo8bNj3<{{_|LS&1T1|LWt4#BEa!=s zC!5G&m{Bgh*6NJ207;Pn%!zU^oK?>XJ0c=2G9qmvGi|c@tGBVLK8*tuSFq5yKBe{3 z9SdwPfm|FA?1J5y($Uw^Sq2Y8JsCzi(Ypd2!|YG~SAJj%M7X}+R;;iE=*~QD@#u}k zo+eF>gmDiIdH29`_4BC|CNHn=fDO6KWl9Mm=K)Xna1x1ZmKIFBz&C&Xr{mq~R5?H_^I7|O5}>S4}wvnj@98+1q1QA?g0(xfm`HawIFasd7)m$N4bmVbjM!jo_dehIFF23K z`-k^A=XIXX>vA;!UEjN}IaJ)`y?{-O1n%O+k($hVT(O6*@X_R>`m`*qCq*=~h>M!t z_o}WP#Tc|iJZ+2Vl1fs#!As@{@ELZGz_!%oRJyfN^lVG(_6_%&r9M=+Q;x&gQ(Fac zx!y3Vy1KtAU1r`i)W8O#1$)Na+!P<)uhNJqzr{;LT+R^9jBO6?3Pd0(9xl4Xq>QBh zLX8-?*8~`nOegM2WQSwFqw!Bkgji}q@)9` zgS6s}!sxFDwf^vGUstz<%E&fN;wU(2F}K2$EwrR!cX>bL1|&=aQ29k{d=&gM{Jnbh7qS+|?V*HJz-PNeyt$DnUy z1XFiSMGG~gZ)++48J}AK6pmfere&Y|bQ`0D(MJ7)Aqc9fmobGEB0VF1VMB?AP+bZE zjq{FM><@D)pSIE+N+i4yy)lS$_2LykY4e<%B>1SKe`B2@_$&^}9k+7lFq?$4f<2Qj z+&%4r-3ApcC$)`tXUZ9!cWDJz-1yYJsM!EgfiQu2y0G=6gqpaGH_!+y9h(U^Vez83 zN;0K`<6dGTy?~04w|dfH%HtF|xun?2T?DO(>Rv9YENG#pA_ zsr+7@&D|^D)O*tn%_e-xSU{mxlGS701Q_<8 zLGRo?KU%v>fyo{p)ejrw&R;s=6$tq%B<-TGthwZHYA?O{VmT=a&Svnix!+y2q+(8e z_%rM0@aob;WRv%+KH7KrGG9I>%m16&=s&1h4iovX-N57hsVTI;huF?eWO199G1Xpw z0a@Fw%f3w*#xsK`%Z70%>6tI@Ea5N&bgzCX4v~8+z8F zqwXMNy^$gfj&?SBU!_HV{-V@Tb|8-MpSH?Xz8a=tN;eDB3%RPm974`Y=D0Z)Nn`LDr^j(~WOQYa-lVjd7+vEYP$_~riZ$6c4;KpUTiHiFWDRn7?Ij6?Vo1=a z4XOJsylfd=3bB(DzaYf85s>^V3a$mPy`Pd6gm#@@Q`+R>eaYapTn!8WUG9y)7xBo| zq!pM>dGYlWco81I%gMArsHUR&JCC>S?(>hMwxZuHWxBrpmVTM+_ItHfIV>M_zDIC6 z?4oRFTg5@44?#4WdsZMA$h)m03`AzTemATogG6jE7wet9xafTh`Qct9IG#)a%h^MDL3U_~p|F779Srw@gc*PzKqdp}Cr_Wt-{)ZX{D7Zb&)e6?5J^ zf{J@BjLE2S_D7#4el;tO3g#V7NHOb^C7CNY2gWfCN(Mwa6x!51aatpTRxr{VV(EW$ zoG`D+Shr~1`*ft0(Iy$R{t0OoU8kR05+$}yvfl<9A!@jg0|~qPPMD#)ozg~6tFaK; z_8hKt{KpGtLU3QIj9JU)Qj?wDtwwr^;so9pQ&rN1!qqozDq2RP3I@dJHzLltxxXW$ z-H!-@k<&VxH~k16=|p*69cj3~i(47ECY>xndVZ}C z)0Yd&1}yT<`DrmydId+1q#(C<(guS~+GVRC_Qx5cSN1%#K($Ar^(_{fbDs>V3Srm^ zx#{Jo;Z_?xVUlMk{WQI@%!hF9DI^(W3285Aou|CSsTsueurA)6Q$}lAWVj$o)BeTN z`Xbcwj9i~G1AcNb%a>snP@DZM-Ke&Ge+_BO_JWx>Av3ra2Uafo>!O05H2}~d82?fT z|0?|S_a(%A>`U0=yt=9yT1LHaPFN_{s%U+yA4(=aTaCpMUGG8RRHwO2MKE>~PgV4xZd_x(OY)Ww{IO&gg0{ftbtds*8f zSMRv$cQ#hzMc*0lY`f_T=3Dx?*_gSFf!?TrLl3t!T+!Y&Ca<~E3YEunCWW`c^~?H!1H?!yS+2}(yzZ?Ihmm_@EC#kpCTV&E=_wk zUjaJC$#5%>MBAg@&2*dJBgO)+Q0+6s&jPae13(@F#tE95)fmR)YPHM8m91D?yuL1w}=h8iC-woV+3F&WhbjV{Z>XRZn!49HYSHy+fwv{Q5 zR+xY2?!R%M@ms}=I}vp`YK(=cKkyU8p$dMQkg4-}*hJ#!!XZ|j{ajI=H1uUy#pA0s z+ozVy!f!oa9gGNa_xAKg0y)nj1-Gn~(8CZk2F{A!bQdTwiD#WW1~+>L+-kD>PZWnI zP*aOO*o)D^<*6I{iP|yRp1hBPLw-nY^H)4dQHE^i`SI{#b1kPU45g-Z16%;)&P~v* zTDPgxy^_|q#Xi&?Ulj&k;BED1e~SC(c4Wt>+E2Ij&)^g{f3@<=sn5~MXSxu^j~P=6 zYw~JK%*Zm3oLE}>oK^3hH$?C?>%G!Jv}EO`z=-}+CQCwL?u4> zQ~nDn!TT98o@IE$d+!Qjhuv)x$#CYCg*|gZ={%QLjfnUvh076;i9@TC5nAjQ>^;Uf z?omdRo%Xfp!Swl@`MJChQ*pD`mmc>E2|mZm%m+V?2VM>)eScngB`Pz+DRA`?H=6JLoy*;y?rV2#zW?S`Fqh3#tyupY`1n3WM;uhS9j z9!+P4`W%Qo>aKJltSPW<^|n-Hfevr9tWyXUy=tKuHSsVV-nz9de7?&CdP5hRfDX(O zWE(_$Lo_`L)oG^y6oGb<^C6VL74YPw-hRn-cl#E#;T8vWJcC;i)#w`7f|x?##?ZSOQToZyZy;630DQ zUq6e(Tc6O&bd?EGpTmf~2nUV;L#7o3#6T;q<4m zuV0#TrpBD_Ed7s!ki=Q)e&06y0tkhBGmX|e$I~HU8!YJfU$)`w`uZ^YN9LqI z#FudK!U^)c`0PUyZWl~#P1E>GGtS=awF%(~^mOdz_ODQ93vB)GMMpGX8J}H`Kxe)% z6#oiW#Y9_o#$@eB!&RWDLiyF}^6!e$$Jd3ap=tFX%J-XUtV6X8BF~qfwl!vI3%Bfh&UH)hV1yPU5|Fl-j9+wYQP(1jPc>=K#X%VF~&Hl4~W7a@CqG7yf z0*1=aartXqi?Gw#z6>a0&oHY^)fi)-zxq`U6TfrcQ`hWlTXBg%PT#1EMS;e6AzH$S zOIG)PVf1K4EjS%!Bk*&UZniSrsSkNv*+#@dO&mf0*XlVXjfp8d3T0qf6K zg8uEG%uO*Go7}Vb-Y<|_;O&ju0F5v531-~^vVV3LjQrWsZ5A!C`}Fwm@KAK}cu%7t z<<)||V1Gt8_^t*su7m;E#w+c&hAM z9LXD~1>D?hn8q}F1zhpC6Yh7kG_?|Jwvs_4ntXBScgN`tQS?|-jOwGbq|noiwA_WJ z8EKX1QF=n(Ech@o*5~fC-g_g}f}ZY@5{;j0t`nVIlBy3+^8D737o)s0$0?ICt0FYB zb`OwlDbXD6Qq3g^$3n+Xw6y48nc~4qBw}i#RJBAwwxXiG0S@rfp){*Uti~0!xD4k9 zQO#Ao6)Pli@E1{8EIx>ciC@p9VgU9`?a z#~-5i39k)qmnCEp8sVTZDn1Bui)T*!yIkGxL8%!I=Pns(*-D}mS)Bh11|Oq_5+a?edUV-C~0SHlOOD&2+s zp{#eF`$d&m{9@6r!_+=?U03tihG<#N__%2HWJVzkm)Bgs*i3h{&x4vf3vHI?S6n1z zG+nwyR1{BCg;1%p`1JQ&q5S#Ir@@90#Qz=ytQC$q1Ttn0tGKKb_0Ir9aqqn zl;rw4zZ+pp%4v1R*^oBtGE~{yOSZD?a%|A3+IU0iV(tl<#~q!pcC2p)(_To(G$X=J z$`Q?I7=_d41g-Jxg+V2?)0d+zWr%^gD4o;OHyf630O3iJzHfkYMI#t`m+yyj|1oFX z@9jA)ApHR2?5shq>g0Weh9+AA_$ z=cmQDH1z-rU*M5b-7d6YZy${`p;V7nK2+%*2lyCS5^q@2(cPh!_E?#volPV_{+yth zN|BLh9$SO=^Y{HXYaxhSjGmC;e5D5l77ZBp+=4#&<2kFVQE2(1!7U!otdy3f=kd~G zupOUwjC&lb1Rle<5@`N|IK3$g;dG@3oo@E;yRLPn^{e{Ahn_8h*@DcIzkHX1GxplI zM(;zvX;eN7rS_lbyZimj$F&OwD%A?)X8PpV0*c?wpfgWR|Z zZ-Hio6gcbAI>|YC%X{e|&EHJ`b@9zZzPOp-PW(2wy5 zL?^vPTg0-6dJ9r8TIf39MUKNWYWr4D`o(7wR&W#mhkqTcr2qv#zN5n|z!UDz0Fzm) zCZ&q0-8z-!aHill=t1QoP<`LC?jLJJY^An!Ppzw=-H8-7(6Z?XDa}F8b zlqE*GZ`PScFNiN&myhG?%ugxx4MOI}iwOhVZH{(Cmc}y)5JxG~2r}?9p0QZ2k8js9 zGLS0S+K+V9qVwwsQx!9nc-(%^n=?Aqqx@c%IER~uZ3lj4O;uIvYatP<8Qazqb>GWr(V$FcUBnxz%U zK~4AP8nIbRs>iFeL;>fyqCtO@&d`@R(7<3Z8RWr@4C1gR*KOc%Cu>MbVQkY)=I

  • qLejzD`Z+tH%g$+jCW&51`ciOoivb0@@Sr?!iA= zhoAKT3#>qWkbdoPFfGY=hb|m(K~4=VI*W{48NBs7MgAOqj5K2{Z}fl8dqg$(N}a2u z6s=4W=5N7jT-R!-R6Z0oHbWQ^q$|om=T;ZM*6|&dl4|CQaKhb&lvDN%J$gki$MK=RXG+PHZ>aTBa zuFG?c@oKss@Pk#{znc)6D+y_8C;LUvJP+vq3c97KOWk)u*!S6W^VWlDo3f_*lT8~B z?2PE^^%i7=5Wfqdj)RrT14b2!xVI;TvbU|%*HdTi?R8|IBu%4oDu|Fw<)REhb}F!4 z4?reo898?WDqo0p≦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ϡn-An8 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�vH#<= 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%0DC9}R7L4%Xyded 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

    NPSf_t{{W_;kng?4~ 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 z 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%0D#wO{-u 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