Game.Rendering.MeshGroupSystem
Assembly: Game
Namespace: Game.Rendering
Type: class
Base: GameSystemBase
Summary:
MeshGroupSystem is an ECS system responsible for constructing and maintaining per-entity MeshGroup buffers for renderable prefabs. It runs a Burst-compiled IJobChunk (SetMeshGroupsJob) to pick submesh groups (and offsets) for each entity's prefab based on prefab data, entity state (humans, current vehicles, temporary/linked originals), and deterministic/random selection. The system also clears/invalidates MeshBatch entries that reference mesh groups that no longer exist. It schedules work in parallel over chunks and reacts either to MeshGroup updates (incremental) or rebuilds all mesh groups when the game finishes loading.
Fields
-
private EntityQuery m_UpdateQuery
Tracks entities with MeshGroup buffers and either Updated or BatchesUpdated markers. Used to perform incremental updates when mesh groups change. -
private EntityQuery m_AllQuery
Query matching all entities with a MeshGroup buffer. Used to rebuild every mesh group once (e.g., right after the game load flag is set). -
private bool m_Loaded
Internal flag set when the game loads (OnGameLoaded). OnUpdate consumes this flag to switch from incremental updates to a full rebuild one time. -
private TypeHandle __TypeHandle
Container of ComponentTypeHandle / BufferTypeHandle / ComponentLookup / BufferLookup used by the job. It's initialized for the current SystemState in OnCreateForCompiler and used to obtain handles inside OnUpdate.
Properties
- (none public)
This system exposes no public properties. All state is driven by ECS queries and internal fields.
Constructors
public MeshGroupSystem()
The default constructor. The system is preserved for serialization through the [Preserve] attribute in the original code; initialization work is performed in OnCreate / OnCreateForCompiler.
Methods
protected override void OnCreate()
Initializes entity queries:- m_UpdateQuery: matches entities that have MeshGroup buffer and either Updated or BatchesUpdated components (used for incremental updates).
-
m_AllQuery: matches all entities that have a MeshGroup buffer (used for a full rebuild). This is invoked once when the system is created.
-
protected override void OnGameLoaded(Context serializationContext)
Sets the m_Loaded flag to true. This causes the next OnUpdate to run a full rebuild (using m_AllQuery) rather than the incremental query. -
private bool GetLoaded()
Helper that returns true once if m_Loaded was set, and then clears m_Loaded. Used to choose whether to run full rebuild on the next update. -
protected override void OnUpdate()
Main system update: - Chooses between m_AllQuery (if GetLoaded returned true) and m_UpdateQuery.
- If query is not empty, constructs a SetMeshGroupsJob and schedules it via JobChunkExtensions.ScheduleParallel.
- Populates the job's ComponentTypeHandle / BufferTypeHandle / Lookup fields using InternalCompilerInterface.Get* wrappers and sets a RandomSeed via RandomSeed.Next().
- Attaches returned JobHandle to base.Dependency.
Notes: - The job is Burst-compiled and runs in parallel over chunks. - The job writes to MeshGroup buffers and may update MeshBatch entries (in the chunk-local buffers).
-
protected override void OnCreateForCompiler()
Called during compilation path (used by game's source generation/compilation pipeline). It assigns query placeholders and calls __TypeHandle.__AssignHandles to prepare the TypeHandle for the system state. -
private void __AssignQueries(ref SystemState state)
Internal method called by OnCreateForCompiler. In this code it creates an EntityQueryBuilder and disposes it (placeholder for generated query assignment logic). -
(Nested) SetMeshGroupsJob : IJobChunk (BurstCompile)
- Purpose: computes MeshGroup entries per entity in a chunk, using prefab submesh group data, randomization, and entity state flags. It updates the MeshGroup dynamic buffer and can modify MeshBatch buffer entries to invalidate batches that referenced removed groups.
- Key fields provided to job:
- ComponentTypeHandle
, ComponentTypeHandle , ComponentTypeHandle , ComponentTypeHandle , ComponentTypeHandle - BufferTypeHandle
, BufferTypeHandle - ComponentLookup
, ComponentLookup , ComponentLookup - BufferLookup
, BufferLookup , BufferLookup , BufferLookup - RandomSeed m_RandomSeed
- ComponentTypeHandle
-
Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask):
- Retrieves component arrays and buffer accessors for the chunk.
- Obtains a Unity.Mathematics.Random either from PseudoRandomSeed component (if present) or from m_RandomSeed seeded with chunk index.
- For each entity in the chunk:
- Reads PrefabRef to access prefab buffers:
- If SubMeshGroup buffer exists for the prefab, iterate through SubMeshGroup entries and pick a concrete sub-group based on flags and randomness.
- Compute mesh offset and color offsets based on submesh ranges and overlay presence.
- MeshGroup selection incorporates flags from Human or CurrentVehicle components (or Temp.m_Original entity's Human/CurrentVehicle if Temp is present):
- Human flags are derived via GetHumanFlags(Human) (requires cold/warm and homeless/home criteria).
- CurrentVehicle flags are derived via GetCurrentVehicleFlags(CurrentVehicle) which checks associated activity locations to allow motorcycles if driving activity is present.
- If prefab has no SubMeshGroups, a fallback MeshGroup with m_SubMeshGroup = 65535 (ushort.MaxValue) is added.
- If previous MeshGroup(s) existed, TryRemoveBatches is used to clear MeshBatch entries that referenced removed group indices (setting indices to byte.MaxValue).
- Uses a NativeList
as temporary storage when oldGroups length > 1 to compare against newGroups. - Disposes the temporary NativeList at the end of execution.
-
TryRemoveBatches(MeshGroup oldGroup, int groupIndex, DynamicBuffer
newGroups, DynamicBuffer batches): - Checks if oldGroup's submesh group still exists in newGroups; if not, iterates batches and sets m_MeshGroup, m_MeshIndex, m_TileIndex to 255 (byte.MaxValue) for batches that referenced that group index — effectively invalidating them.
-
static MeshGroupFlags GetHumanFlags(Human human):
- Maps human flags to MeshGroupFlags:
- Cold => RequireCold, otherwise RequireWarm.
- Homeless => RequireHomeless, otherwise RequireHome.
-
MeshGroupFlags GetCurrentVehicleFlags(CurrentVehicle currentVehicle):
- Starts with ForbidMotorcycle.
- If the vehicle prefab has ActivityLocationElements containing Driving activity, it clears ForbidMotorcycle and sets RequireMotorcycle.
- This requires looking up PrefabRef component for the vehicle and buffer lookup of activity locations.
-
void IJobChunk.Execute(...) — explicit interface implementation that forwards to Execute(...).
Notes about SetMeshGroupsJob: - Burst-compiled for performance. - Uses chunk-parallel scheduling (ScheduleParallel). - Operates only on data present in the chunk, using ComponentLookup/BufferLookup for cross-entity prefab buffers. - Carefully handles deterministic randomization by using PseudoRandomSeed where available or a supplied RandomSeed seeded by chunk index.
- (Nested) TypeHandle
- Container for the various ComponentTypeHandle, BufferTypeHandle and ComponentLookup/BufferLookup used by the job.
- __AssignHandles(ref SystemState state): fills all handles from the provided state (marking read-only where appropriate). This is called in OnCreateForCompiler to prepare the handles for later use.
Usage Example
// This is effectively what the system does during creation and update.
// Example: OnCreate sets up queries
[Preserve]
protected override void OnCreate()
{
base.OnCreate();
m_UpdateQuery = GetEntityQuery(new EntityQueryDesc
{
All = new ComponentType[1] { ComponentType.ReadOnly<MeshGroup>() },
Any = new ComponentType[2]
{
ComponentType.ReadOnly<Updated>(),
ComponentType.ReadOnly<BatchesUpdated>()
}
});
m_AllQuery = GetEntityQuery(ComponentType.ReadOnly<MeshGroup>());
}
// Example: simplified OnUpdate snippet showing job construction and scheduling
protected override void OnUpdate()
{
EntityQuery query = (GetLoaded() ? m_AllQuery : m_UpdateQuery);
if (!query.IsEmptyIgnoreFilter)
{
var job = new SetMeshGroupsJob
{
// assign ComponentTypeHandle/BufferTypeHandle/Lookups via internal helpers
// m_RandomSeed = RandomSeed.Next(),
// ... other fields
};
JobHandle handle = JobChunkExtensions.ScheduleParallel(job, query, base.Dependency);
base.Dependency = handle;
}
}
Notes / Modding tips: - If you add or change SubMeshGroup entries on prefabs, MeshGroupSystem will select groups on next incremental update or after a load. - MeshGroup selection is deterministic when PseudoRandomSeed component is present; otherwise it uses RandomSeed seeded per-chunk. For reproducible visuals across saves, ensure PseudoRandomSeed is populated for relevant entities. - Invalidated batches are set to 255 (byte.MaxValue) in their indices; code that consumes MeshBatch should handle this sentinel value. - The job is Burst-compiled and scheduled in parallel; avoid relying on managed or non-thread-safe operations inside similar jobs.