bloak
This commit is contained in:
139
Assets/Blocks/Scripts/BlockRegistry.cs
Normal file
139
Assets/Blocks/Scripts/BlockRegistry.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Синглтон. Собирает все BlockDefinition, строит Texture2DArray,
|
||||
/// создаёт один shared Material для всех чанков.
|
||||
/// Вызвать Initialise() до создания любого чанка.
|
||||
/// </summary>
|
||||
public class BlockRegistry : MonoBehaviour
|
||||
{
|
||||
public static BlockRegistry Instance { get; private set; }
|
||||
|
||||
[Header("All block definitions in the game")]
|
||||
public List<BlockDefinition> definitions = new();
|
||||
|
||||
[Header("Shader")]
|
||||
public Shader voxelShader; // назначить Blocks/VoxelLit через Inspector
|
||||
|
||||
// Доступно после Initialise()
|
||||
public Material ChunkMaterial { get; private set; }
|
||||
public Texture2DArray TexArray { get; private set; }
|
||||
|
||||
private readonly Dictionary<string, BlockDefinition> _byId = new();
|
||||
private readonly Dictionary<string, BlockDefinition> _byItemId = new();
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (Instance != null) { Destroy(gameObject); return; }
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
Initialise();
|
||||
}
|
||||
|
||||
public void Initialise()
|
||||
{
|
||||
_byId.Clear();
|
||||
_byItemId.Clear();
|
||||
|
||||
if (definitions == null || definitions.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("[BlockRegistry] No BlockDefinitions assigned.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Собираем уникальные текстуры (top/bottom/side каждого блока)
|
||||
// Порядок: для блока i → top=i*3, bottom=i*3+1, side=i*3+2
|
||||
int layerCount = definitions.Count * 3;
|
||||
int res = 128; // разрешение должно совпадать с текстурами блоков
|
||||
|
||||
TexArray = new Texture2DArray(res, res, layerCount,
|
||||
TextureFormat.RGBA32, true, false);
|
||||
TexArray.filterMode = FilterMode.Point; // пиксельный стиль
|
||||
TexArray.wrapMode = TextureWrapMode.Repeat;
|
||||
|
||||
for (int i = 0; i < definitions.Count; i++)
|
||||
{
|
||||
var def = definitions[i];
|
||||
|
||||
def.topIndex = i * 3;
|
||||
def.bottomIndex = i * 3 + 1;
|
||||
def.sideIndex = i * 3 + 2;
|
||||
|
||||
CopyTexture(def.topTexture, TexArray, def.topIndex, res);
|
||||
CopyTexture(def.bottomTexture, TexArray, def.bottomIndex, res);
|
||||
CopyTexture(def.sideTexture, TexArray, def.sideIndex, res);
|
||||
|
||||
_byId[def.blockId] = def;
|
||||
|
||||
if (def.linkedItem != null)
|
||||
_byItemId[def.linkedItem.id] = def;
|
||||
}
|
||||
|
||||
TexArray.Apply(true, true); // загрузить на GPU, сделать non-readable
|
||||
|
||||
// Создаём один материал для всех чанков
|
||||
if (voxelShader == null)
|
||||
voxelShader = Shader.Find("Blocks/VoxelLit");
|
||||
|
||||
ChunkMaterial = new Material(voxelShader);
|
||||
ChunkMaterial.SetTexture("_MainTexArray", TexArray);
|
||||
|
||||
Debug.Log($"[BlockRegistry] Initialised {definitions.Count} blocks, {layerCount} texture layers.");
|
||||
}
|
||||
|
||||
// ── Lookups ───────────────────────────────────────────────────────────────
|
||||
|
||||
public BlockDefinition GetById(string blockId)
|
||||
{
|
||||
_byId.TryGetValue(blockId, out var def);
|
||||
return def;
|
||||
}
|
||||
|
||||
/// <summary>Находит BlockDefinition по id связанного ItemData (для постановки блока).</summary>
|
||||
public BlockDefinition GetByItemId(string itemDataId)
|
||||
{
|
||||
_byItemId.TryGetValue(itemDataId, out var def);
|
||||
return def;
|
||||
}
|
||||
|
||||
// ── Утилиты ───────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Копирует пиксели из src в Texture2DArray на слой layerIndex.
|
||||
/// Если src null или не того размера — заполняет пурпурным (ошибка-текстура).
|
||||
/// </summary>
|
||||
static void CopyTexture(Texture2D src, Texture2DArray dst, int layerIndex, int res)
|
||||
{
|
||||
Color[] pixels;
|
||||
|
||||
if (src == null)
|
||||
{
|
||||
// Пурпурный = "текстура не назначена"
|
||||
pixels = new Color[res * res];
|
||||
for (int p = 0; p < pixels.Length; p++)
|
||||
pixels[p] = (p / res + p) % 2 == 0 ? Color.magenta : Color.black;
|
||||
}
|
||||
else if (src.width != res || src.height != res)
|
||||
{
|
||||
Debug.LogWarning($"[BlockRegistry] {src.name} is {src.width}×{src.height}, expected {res}×{res}. Scaling.");
|
||||
// Простой fallback — масштабировать через RenderTexture
|
||||
var rt = RenderTexture.GetTemporary(res, res);
|
||||
Graphics.Blit(src, rt);
|
||||
var prev = RenderTexture.active;
|
||||
RenderTexture.active = rt;
|
||||
var resized = new Texture2D(res, res, TextureFormat.RGBA32, false);
|
||||
resized.ReadPixels(new Rect(0, 0, res, res), 0, 0);
|
||||
resized.Apply();
|
||||
RenderTexture.active = prev;
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
pixels = resized.GetPixels();
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels = src.GetPixels();
|
||||
}
|
||||
|
||||
dst.SetPixels(pixels, layerIndex);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user