sync method

void sync()

Brings the picture up to date with the simulation.

Called after a step, once a frame. Nothing here reads the clock or decides anything: what a unit does is settled by the time this runs, and a drawing half that made decisions would be a second simulation disagreeing with the first.

Implementation

void sync() {
  // **The fog is what decides how many instances are written**, and that is
  // the cheapest form the saving could take: a unit nobody can see is not
  // culled after being encoded, it is never encoded. The measurement that
  // started this genre put the ceiling at the processor writing instances —
  // about 0.07 microseconds each, every frame — so the crowd a side cannot
  // see is exactly the part of that bill it should not be paying.
  final int? side = viewer;
  _filled.fillRange(0, _filled.length, 0);
  for (final StrategyUnit unit in simulation.units) {
    if (side != null && !_showsUnit(side, unit)) continue;
    final List<int>? dressed = _byKind[unit.type.name];
    final int which = dressed == null
        ? 0
        : dressed[unit.side % dressed.length];
    final InstancedMeshNode batch = _batches[which];
    final int drawn = _filled[which];
    if (drawn >= batch.capacity) continue;
    final Vector3 at = unit.position;
    final double lift = which == 0 ? _unitGroundLift : 0.0;
    _transform
      ..setRotationY(_headingOf(unit))
      ..setTranslationRaw(at.x, at.y + lift, at.z);
    batch
      ..setTransform(drawn, _transform)
      ..setColor(drawn, _woundOf(unit).toLinearColor());
    _filled[which] = drawn + 1;
  }
  for (var i = 0; i < _batches.length; i++) {
    _batches[i].count = _filled[i];
  }

  _syncProps(side);
  _syncDeposits(side);

  // Buildings are nodes of their own rather than instances: a batch scales
  // its copies uniformly by the engine's own account, and buildings are the
  // one thing on this map with sizes of their own.
  //
  // A building appears the first time its side's ground is uncovered and then
  // stays, because that is what "explored" means — a hall seen once is
  // remembered where it stood, even after the crowd that saw it walked home.
  for (var i = _buildings.length; i < simulation.buildings.length; i++) {
    _buildings.add(null);
  }
  for (var i = 0; i < simulation.buildings.length; i++) {
    if (_buildings[i] != null) continue;
    final Building building = simulation.buildings[i];
    if (side != null &&
        building.side != side &&
        !simulation.fog.knows(side, building.center.x, building.center.z)) {
      continue;
    }
    // A copy each, not the one material shared. It costs a handful of
    // objects and it buys the only thing a picking pass is good for here:
    // a hall the cursor is over can be lit on its own. Shared, the
    // highlight would light every hall on the map at once.
    final List<RenderMaterial>? sides = _buildingSides;
    final RenderMaterial material =
        (sides == null
                ? _buildingMaterial
                : sides[building.side % sides.length])
            .copy();
    final double height = unitSize.height * 2.5;
    final MeshNode node;
    final DeviceMesh? sharedMesh = _buildingMesh;
    if (sharedMesh != null) {
      final Vector3 natural = _buildingMeshSize!;
      final double across = building.width / natural.x;
      final double along = building.depth / natural.z;
      // A real model already stands on its own feet at Y = 0 — no lift to
      // add, only the stretch from its authored size to this building's.
      // Upright, it rises by the mean of the two ground stretches, so a
      // castle laid out a little narrower than its footprint is not also
      // squashed to the height of a shed.
      node = MeshNode(sharedMesh, material, name: building.name)
        ..setScale(
          across,
          _buildingStandsTall ? (across + along) / 2.0 : height / natural.y,
          along,
        )
        ..setPosition(
          building.center.x,
          building.center.y,
          building.center.z,
        );
    } else {
      node =
          MeshNode(
            DeviceMesh.upload(
              _device,
              CuboidShape(
                size: Vector3(building.width, height, building.depth),
              ).build(),
            ),
            material,
            name: building.name,
          )..setPosition(
            building.center.x,
            building.center.y + unitSize.height * 1.25,
            building.center.z,
          );
    }
    _buildings[i] = node;
    _scene?.add(node);
  }

  if (side != null) _syncFog(side);
}