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);
}