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;
  var drawn = 0;
  for (final Unit unit in simulation.units) {
    if (drawn >= _crowd.capacity) break;
    if (side != null && !_showsUnit(side, unit)) continue;
    final Vector3 at = unit.position;
    _transform.setIdentity();
    _transform.setTranslationRaw(at.x, at.y + _unitGroundLift, at.z);
    _crowd.setTransform(drawn, _transform);
    _crowd.setColor(drawn, _woundOf(unit));
    drawn++;
  }
  _crowd.count = drawn;

  // 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.centre.x, building.centre.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 Material material = _buildingMaterial.copy();
    final double height = unitSize.height * 2.5;
    final MeshNode node;
    final DeviceMesh? sharedMesh = _buildingMesh;
    if (sharedMesh != null) {
      final Vector3 natural = _buildingMeshSize!;
      // A real model already stands on its own feet at Y = 0, the same
      // convention `tool/prepare_models.py` leaves `hall.glb` in — no lift
      // to add, only the stretch from its authored size to this building's.
      node = MeshNode(sharedMesh, material, name: building.name)
        ..setScale(
          building.width / natural.x,
          height / natural.y,
          building.depth / natural.z,
        )
        ..setPosition(
          building.centre.x,
          building.centre.y,
          building.centre.z,
        );
    } else {
      node =
          MeshNode(
            DeviceMesh.upload(
              _device,
              CuboidShape(
                size: Vector3(building.width, height, building.depth),
              ).build(),
            ),
            material,
            name: building.name,
          )..setPosition(
            building.centre.x,
            building.centre.y + unitSize.height * 1.25,
            building.centre.z,
          );
    }
    _buildings[i] = node;
    _scene?.add(node);
  }

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