Skip to content

Game.Rendering.BatchMeshHelpers

Assembly: Assembly-CSharp (Cities: Skylines 2 runtime)
Namespace: Game.Rendering

Type: static class

Base: System.Object

Summary:
BatchMeshHelpers provides helpers to generate Unity Mesh.MeshData for batched rendering of game meshes (object meshes and net composition meshes) from ECS data. It schedules a Burst-compiled IJobParallelFor (GenerateBatchMeshJob) that reads component and buffer data (MeshData, NetCompositionMeshData, MeshVertex, MeshNormal, MeshIndex, MeshMaterial, etc.) and fills Mesh.MeshDataArray entries with vertex and index buffers, submesh descriptors and vertex attributes optimized for Cities: Skylines 2 rendering. The job contains specialized logic for constructing base geometry, building composition pieces, handling 16/32-bit index buffers, octahedral encoding of normals/tangents, height-bound sampling and special composition flags (median, roundabout, preserve shape, etc.). This utility is intended to be used from a BatchMeshSystem or similar modding system to prepare runtime mesh data for Graphics API upload.


Fields

  • private struct GenerateBatchMeshJob
    GenerateBatchMeshJob is a Burst-compiled IJobParallelFor nested inside the static class. It contains the main mesh generation logic and all working data needed to convert ECS mesh components and buffers into Mesh.MeshData. The job reads MeshData and NetCompositionMeshData components and many DynamicBuffer lookups, writes into a provided Mesh.MeshDataArray, and internally defines small POD helper structs (BasePoint, BaseLine, VertexData) and many private methods that implement the mesh construction algorithms.

  • private struct GenerateBatchMeshJob.BasePoint
    Helper POD type used by the base-building algorithm. Stores 2D position, direction, previous position and accumulated distance along base outlines.

  • private struct GenerateBatchMeshJob.BaseLine
    Simple pair of indices (start, end) into the BasePoint list representing an edge/line for base mesh generation.

  • private struct GenerateBatchMeshJob.VertexData
    Layout used to populate the Mesh.MeshData vertex buffer inside the job: position (float3), packed normal and tangent (uint), Color32, and UV0 as half4.

Properties

  • None (BatchMeshHelpers is a static helper class; the job struct has no exposed properties)

Constructors

  • None (static class — no public constructors).
    GenerateBatchMeshJob uses default value-type constructors when scheduled by the job system.

Methods

  • public static JobHandle GenerateMeshes(BatchMeshSystem meshSystem, NativeList<Entity> meshes, Mesh.MeshDataArray meshDataArray, JobHandle dependencies)
    Schedules the Burst-compiled GenerateBatchMeshJob as an IJobParallelFor to populate meshDataArray for the supplied list of entities. The method sets up ComponentLookup and BufferLookup handles (read-only where appropriate) from the provided meshSystem and returns the scheduled JobHandle. Callers must provide a Mesh.MeshDataArray with one entry per entity and manage Mesh.ApplyAndDisposeWritableMeshData or the equivalent upload path after the job completes.

  • GenerateBatchMeshJob.Execute(int index) : void
    Entry point of the parallel job. For each entity it:

  • If entity has MeshData component: reads vertex/index/normal/node buffers and calls GenerateObjectMesh to create a simple object mesh (including optional base geometry).
  • Else if entity has NetCompositionMeshData: reads composition pieces and materials and calls GenerateCompositionMesh to assemble multi-material composition meshes.

  • GenerateBatchMeshJob.GetMaterial(DynamicBuffer<MeshMaterial> materials, MeshMaterial pieceMaterial) : int
    Utility to find or add a MeshMaterial entry in a destination materials buffer by matching m_MaterialIndex. Returns the index in "materials" for the pieceMaterial.

  • GenerateBatchMeshJob.GenerateObjectMesh(MeshData objectMeshData, DynamicBuffer<MeshVertex> cachedVertices, DynamicBuffer<MeshNormal> cachedNormals, DynamicBuffer<MeshIndex> cachedIndices, DynamicBuffer<MeshNode> nodes, Mesh.MeshData meshData) : void
    Builds a Unity mesh for simple object MeshData. Handles optional "base" flag in MeshFlags: collects base points/lines, computes vertex and index counts, configures vertex/index buffer formats (16/32-bit indices), and writes vertices/indices by calling AddBaseVertices.

  • GenerateBatchMeshJob.AddBaseVertices(NativeList<BasePoint> basePoints, NativeList<BaseLine> baseLines, NativeArray<VertexData> vertices, NativeArray<uint> indices32, NativeArray<ushort> indices16, bool use32bitIndices, float baseOffset, ref int vertexCount, ref int indexCount) : void
    Writes final vertex data and triangle indices for the base geometry collected by AddBaseLines. Encodes normals and tangents (octahedral encoding helpers used elsewhere), sets color to white, and puts distance into UV0.x for tiling.

  • GenerateBatchMeshJob.AddBaseLines(...) : void
    Complex algorithm that scans triangle lists (optionally quadtree nodes via MeshNode buffers) to find triangle edges that intersect the base plane (a small epsilon around baseOffset). It builds unique base points, links them into base lines (edges), computes distances along connected edges and deduplicates/handles directional differences. Uses temporary NativeHashSet/NativeHashMap/NativeLists and stackalloc for traversal to be Burst-friendly and allocation-efficient. This function is the core of generating continuous base skirts for object meshes.

  • GenerateBatchMeshJob.GenerateCompositionMesh(NetCompositionMeshData compositionMeshData, DynamicBuffer<NetCompositionPiece> pieces, DynamicBuffer<MeshMaterial> materials, Mesh.MeshData meshData) : void
    Assembles complex net composition meshes (road segments, medians, sidewalks, nodes, roundabouts, etc.). Steps performed:

  • Analyze pieces and per-piece materials to compute per-material vertex and index counts, taking into account composition flags: Median, Invert, FlipMesh, HalfLength, PreserveShape, SkipBottomHalf and others.
  • Setup Mesh vertex/index buffer layout and submeshes per material.
  • Iterate pieces and copy/transform vertex attributes, encode normals/tangents, compute UV and custom per-vertex data (packed into color/masks/uv channels), optionally compute height bounds for terrain snapping features (LowerBottomToTerrain, RaiseTopToTerrain, SmoothTopNormal).
  • Write indices with either 16- or 32-bit index buffers depending on total index count.

  • GenerateBatchMeshJob.SetupGeneratedMeshAttributes(NativeArray<VertexAttributeDescriptor> attrs) : void
    Configures the VertexAttributeDescriptor array used for MeshData.SetVertexBufferParams. The layout used by the generated mesh:

  • Position: Float32 x3
  • Normal: SNorm16 x2 (octahedral)
  • Tangent: Float32 x1 (packed)
  • Color: UNorm8 x4
  • TexCoord0: Float16 x4

  • GenerateBatchMeshJob.InitializeHeightBounds(NativeArray<Bounds1> heightBounds, NetCompositionPiece compositionPiece, DynamicBuffer<MeshVertex> pieceVertices, DynamicBuffer<MeshIndex> pieceIndices) : void
    Builds a per-slice min/max height table across the piece length (used to decide if a vertex is at top/bottom and to smooth normals or snap to terrain). The height array size is 257 by default and the function rasterizes triangle edges into the slices to compute min/max heights.

  • GenerateBatchMeshJob.AddHeightBounds(NativeArray<Bounds1> heightBounds, float3 aVertex, float3 bVertex, int aIndex, int bIndex) : void
    Adds interpolated heights between indices aIndex and bIndex into the heightBounds array. Used by InitializeHeightBounds.

  • GenerateBatchMeshJob.GetHeightBounds(NativeArray<Bounds1> heightBounds, NetCompositionPiece compositionPiece, float z) : Bounds1
    Sample returns the Bounds1 entry for the z position in piece-local coordinates. Used to check if a vertex is near top/bottom to apply special flags (LowerBottomToTerrain, RaiseTopToTerrain, SmoothTopNormal).

Notes about job internals: - Normals and tangents are packed using MathUtils.NormalToOctahedral and MathUtils.TangentToOctahedral for compact storage (normal uses two-component SNorm16). - UV and additional per-vertex metadata are packed into half4 UV0 and Color32 channels following the composition format used by the game's shaders. - Many composition flags change vertex mapping, UV scaling, and index winding (FlipMesh/Inver t/Right/Median logic). - The job uses temporary NativeArray/NativeList/NativeHashMap allocations with Allocator.Temp which are created and disposed inside Execute for each parallel work item; this is safe because each job index operates on its own data slices or local containers.

Usage Example

// Example usage from within a system that owns a BatchMeshSystem and a list of entities to build:
JobHandle deps = ...; // existing job dependencies
NativeList<Entity> meshes = /* entities to generate */ ;
Mesh.MeshDataArray meshDataArray = Mesh.AllocateWritableMeshData(meshes.Length);

// schedule the mesh generation job
JobHandle handle = BatchMeshHelpers.GenerateMeshes(batchMeshSystem, meshes, meshDataArray, deps);

// later, after handle completes, apply the mesh data to Unity meshes and dispose writable data
handle.Complete();

// for each i:
// Mesh.ApplyAndDisposeWritableMeshData(meshDataArray, unityMesh);
// or call Mesh.ApplyAndDisposeWritableMeshData(meshDataArray) when all meshes are created

If you need a shorter example for a single mesh or details on how the per-vertex packing maps to shader inputs used by Cities: Skylines 2, tell me which part you want expanded (norm/tangent packing, UV/color channels, composition flags, or how to apply Mesh.MeshDataArray to Mesh instances).