MODFORGEWORLD
← Knowledge base

Paint & Textures

paint-texturesv1updated 5h ago

Paint & Textures

Part of the 7DTD Modding Knowledgebase. Covers the block paint system, texture IDs, and how to apply paint from C#.


Overview

7DTD has a paint system that lets players apply surface textures to blocks. Each face of a block can be painted independently. Paint textures are referenced by integer IDs — not by arbitrary RGB colors.

For recoloring an entity's textures at runtime (vehicle repaint from a color picker), see Runtime Texture Tinting (entity repaint) — a completely different mechanism.


Applying Paint from C#

// Paint a single face
GameManager.Instance.SetBlockTextureServer(
    position,        // Vector3i
    BlockFace.North, // which face
    (byte)textureId, // 0–255 texture ID
    0                // entity ID (0 = server/no player)
);

// Paint all faces the same
foreach (BlockFace face in new[] {
    BlockFace.Top, BlockFace.Bottom,
    BlockFace.North, BlockFace.South,
    BlockFace.East, BlockFace.West })
{
    GameManager.Instance.SetBlockTextureServer(position, face, (byte)textureId, 0);
}

Texture ID to Color Mapping

The game ships with a fixed set of paint textures referenced by integer IDs. PixelPaste maps pixel RGB values to the nearest texture ID using squared Euclidean distance in RGB space.

This mapping lives in PaintColorMap.cs. The palette was generated by averaging the RGB pixels of each texture image from the game data (painting.xml defines paint ID → texture ID; the texture PNGs live in ../7builder/game-data/textures/). All 155 non-hidden paint IDs from painting.xml are included.

To regenerate: parse painting.xml for paint ID → TextureId, load each {TextureId}.png, compute average RGB, and emit PaintEntry lines.


Nearest-Color Matching

public static int FindClosestTextureId(Color32 pixel)
{
    int bestId = 0;
    float bestDist = float.MaxValue;

    foreach (var entry in palette)
    {
        float dr = pixel.r - entry.r;
        float dg = pixel.g - entry.g;
        float db = pixel.b - entry.b;
        float dist = dr*dr + dg*dg + db*db;

        if (dist < bestDist)
        {
            bestDist = dist;
            bestId = entry.textureId;
        }
    }
    return bestId;
}

Transparency

Pixels with alpha below a threshold should be skipped (no block placed):

public static bool IsTransparent(Color32 pixel)
{
    return pixel.a < 128;
}

Gotchas

Texture IDs are bytes (0–255) — cast to byte when calling SetBlockTextureServer.

The palette covers all 155 paint IDs from vanilla painting.xml. Colors are average RGB from the actual texture PNGs, which may not perfectly match in-game appearance due to lighting/normal maps.

SetBlockTextureServer applies immediately on the server side. In singleplayer, server and client are the same process so this works fine.