Files
WulaFallenEmpireRW/Source/WulaFallenEmpire/SectionLayer_WulaHull.cs
Tourswen ea64ff9e6c 1
2025-12-06 00:03:01 +08:00

206 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using LudeonTK;
using RimWorld;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
{
// 定义了一个数据结构用于从XML文件中加载我们的配置。
public class WulaHullDef : Def
{
// 以下所有 public 字段都会由RimWorld的加载系统自动从XML同名节点中填充。
public ThingDef targetDef;
public ShaderTypeDef shaderType;
public string texPath_Corner_NW;
public string texPath_Corner_NE;
public string texPath_Corner_SW;
public string texPath_Corner_SE;
public string texPath_Diagonal_NW;
public string texPath_Diagonal_NE;
public string texPath_Diagonal_SW;
public string texPath_Diagonal_SE;
// 提供一个全局静态访问点,方便在代码中随时获取配置,而无需传来传去。
public static WulaHullDef Current => DefDatabase<WulaHullDef>.GetNamed("WulaHullConfig");
}
// 这是核心的渲染类继承自SectionLayer负责在地图上绘制额外的视觉效果。
public class SectionLayer_WulaHull : SectionLayer
{
public enum CornerType { None, Corner_NW, Corner_NE, Corner_SW, Corner_SE, Diagonal_NW, Diagonal_NE, Diagonal_SW, Diagonal_SE }
private static readonly Vector2[] UVs = { new Vector2(0f, 0f), new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(1f, 0f) };
[TweakValue("WulaHullCorners", 0f, 2f)]
private static float HullCornerScale = 2f;
private static CachedMaterial mat_Corner_NW, mat_Corner_NE, mat_Corner_SW, mat_Corner_SE, mat_Diagonal_NW, mat_Diagonal_NE, mat_Diagonal_SW, mat_Diagonal_SE;
private static readonly float cornerAltitude = AltitudeLayer.BuildingOnTop.AltitudeFor();
private static bool initalized;
private static readonly IntVec3[] Directions = { IntVec3.North, IntVec3.East, IntVec3.South, IntVec3.West, IntVec3.North + IntVec3.West, IntVec3.North + IntVec3.East, IntVec3.South + IntVec3.East, IntVec3.South + IntVec3.West };
private static bool[] tmpChecks = new bool[Directions.Length];
// 获取要使用的着色器。优先从XML配置中读取如果未配置则使用一个安全合理的默认值。
private static Shader WallShader => WulaHullDef.Current.shaderType?.Shader ?? ShaderDatabase.CutoutComplex;
public override bool Visible => true;
public SectionLayer_WulaHull(Section section) : base(section)
{
// 告诉游戏,当建筑、地形等发生变化时,需要重绘我们的图层。
relevantChangeTypes = (ulong)MapMeshFlagDefOf.Buildings | (ulong)MapMeshFlagDefOf.Terrain | (ulong)MapMeshFlagDefOf.Things | (ulong)MapMeshFlagDefOf.Roofs;
}
// 这是一个性能优化技巧,确保贴图和材质只在第一次使用时被加载和创建,而不是每次重绘都加载。
private static void EnsureInitialized()
{
if (!initalized)
{
initalized = true;
WulaHullDef config = WulaHullDef.Current;
mat_Corner_NW = new CachedMaterial(config.texPath_Corner_NW, WallShader);
mat_Corner_NE = new CachedMaterial(config.texPath_Corner_NE, WallShader);
mat_Corner_SW = new CachedMaterial(config.texPath_Corner_SW, WallShader);
mat_Corner_SE = new CachedMaterial(config.texPath_Corner_SE, WallShader);
mat_Diagonal_NW = new CachedMaterial(config.texPath_Diagonal_NW, WallShader);
mat_Diagonal_NE = new CachedMaterial(config.texPath_Diagonal_NE, WallShader);
mat_Diagonal_SW = new CachedMaterial(config.texPath_Diagonal_SW, WallShader);
mat_Diagonal_SE = new CachedMaterial(config.texPath_Diagonal_SE, WallShader);
}
}
// 游戏引擎会定期调用这个方法来更新和重绘我们的图层。
public override void Regenerate()
{
ClearSubMeshes(MeshParts.All);
Map map = base.Map;
TerrainGrid terrGrid = map.terrainGrid;
foreach (IntVec3 item in section.CellRect)
{
if (ShouldDrawCornerPiece(item, map, terrGrid, out var cornerType, out var color))
{
CachedMaterial material = GetMaterial(cornerType);
IntVec3 offset = GetOffset(cornerType);
bool addGravshipMask = false;
bool addIndoorMask = IsCornerIndoorMasked(item, cornerType, map);
AddQuad(material.Material, item + offset, HullCornerScale, cornerAltitude, color, addGravshipMask, addIndoorMask);
}
}
FinalizeMesh(MeshParts.All);
}
// 这是整个框架的核心判断逻辑。
// 它检查给定的坐标分析其周围8个方向的建筑来决定是否需要绘制斜角以及绘制哪种类型的斜角。
public static bool ShouldDrawCornerPiece(IntVec3 pos, Map map, TerrainGrid terrGrid, out CornerType cornerType, out Color color)
{
cornerType = CornerType.None;
color = Color.white;
if (pos.GetEdifice(map) != null) return false;
TerrainDef terrainDef = terrGrid.FoundationAt(pos);
if (terrainDef != null && terrainDef.IsSubstructure) return false;
ThingDef targetDef = WulaHullDef.Current.targetDef;
if (targetDef == null) { Log.ErrorOnce("WulaHullConfig has a null targetDef. Please check WulaHullDefs.xml.", 168168168); return false; }
for (int i = 0; i < Directions.Length; i++) { tmpChecks[i] = (pos + Directions[i]).GetEdificeSafe(map)?.def == targetDef; }
if (tmpChecks[0] && tmpChecks[3] && !tmpChecks[2] && !tmpChecks[1]) cornerType = (tmpChecks[4] ? CornerType.Corner_NW : CornerType.Diagonal_NW);
else if (tmpChecks[0] && tmpChecks[1] && !tmpChecks[2] && !tmpChecks[3]) cornerType = (tmpChecks[5] ? CornerType.Corner_NE : CornerType.Diagonal_NE);
else if (tmpChecks[2] && tmpChecks[1] && !tmpChecks[0] && !tmpChecks[3]) cornerType = (tmpChecks[6] ? CornerType.Corner_SE : CornerType.Diagonal_SE);
else if (tmpChecks[2] && tmpChecks[3] && !tmpChecks[0] && !tmpChecks[1]) cornerType = (tmpChecks[7] ? CornerType.Corner_SW : CornerType.Diagonal_SW);
if (cornerType == CornerType.None) return false;
for (int j = 0; j < 4; j++) { if (tmpChecks[j]) { color = (pos + Directions[j]).GetEdificeSafe(map).DrawColor; break; } }
return true;
}
private static CachedMaterial GetMaterial(CornerType edgeType)
{
EnsureInitialized();
switch (edgeType)
{
case CornerType.Corner_NW: return mat_Corner_NW;
case CornerType.Corner_NE: return mat_Corner_NE;
case CornerType.Corner_SW: return mat_Corner_SW;
case CornerType.Corner_SE: return mat_Corner_SE;
case CornerType.Diagonal_NW: return mat_Diagonal_NW;
case CornerType.Diagonal_NE: return mat_Diagonal_NE;
case CornerType.Diagonal_SW: return mat_Diagonal_SW;
case CornerType.Diagonal_SE: return mat_Diagonal_SE;
default: throw new System.ArgumentOutOfRangeException("edgeType", edgeType, null);
}
}
private static IntVec3 GetOffset(CornerType cornerType)
{
switch (cornerType)
{
case CornerType.Corner_NE: case CornerType.Diagonal_NE: return new IntVec3(0, 0, 0);
case CornerType.Corner_NW: case CornerType.Diagonal_NW: return new IntVec3(-1, 0, 0);
case CornerType.Corner_SE: case CornerType.Diagonal_SE: return new IntVec3(0, 0, -1);
case CornerType.Corner_SW: case CornerType.Diagonal_SW: return new IntVec3(-1, 0, -1);
default: return IntVec3.Zero;
}
}
private void AddQuad(Material mat, IntVec3 c, float scale, float altitude, Color color, bool addGravshipMask, bool addIndoorMask)
{
LayerSubMesh subMesh = GetSubMesh(mat);
AddQuad(subMesh, c.ToVector3(), scale, altitude, color);
if (addGravshipMask)
{
Texture2D srcTex = subMesh.material.mainTexture as Texture2D;
Color color2 = subMesh.material.color;
Material material = MaterialPool.MatFrom(srcTex, ShaderDatabase.GravshipMaskMasked, color2);
AddQuad(GetSubMesh(material), c.ToVector3(), scale, altitude, color);
}
if (addIndoorMask)
{
Texture2D srcTex2 = subMesh.material.mainTexture as Texture2D;
Color color3 = subMesh.material.color;
Material material2 = MaterialPool.MatFrom(srcTex2, ShaderDatabase.IndoorMaskMasked, color3);
AddQuad(GetSubMesh(material2), c.ToVector3(), scale, altitude, color);
}
}
private static void AddQuad(LayerSubMesh sm, Vector3 c, float scale, float altitude, Color color)
{
int count = sm.verts.Count;
// 添加微小的UV偏移来避免单像素黑边
const float uvOffset = 0.001f;
Vector2[] adjustedUVs = new Vector2[]
{
new Vector2(uvOffset, uvOffset),
new Vector2(uvOffset, 1f - uvOffset),
new Vector2(1f - uvOffset, 1f - uvOffset),
new Vector2(1f - uvOffset, uvOffset)
};
for (int i = 0; i < 4; i++)
{
sm.verts.Add(new Vector3(c.x + UVs[i].x * scale, altitude, c.z + UVs[i].y * scale));
sm.uvs.Add(adjustedUVs[i]); // 使用调整后的UV坐标
sm.colors.Add(color);
}
sm.tris.Add(count);
sm.tris.Add(count + 1);
sm.tris.Add(count + 2);
sm.tris.Add(count);
sm.tris.Add(count + 2);
sm.tris.Add(count + 3);
}
private static bool IsCornerIndoorMasked(IntVec3 c, CornerType cornerType, Map map)
{
switch (cornerType)
{
case CornerType.Corner_NE: case CornerType.Diagonal_NE: if (!c.Roofed(map)) return (c + IntVec3.East).Roofed(map); return true;
case CornerType.Corner_NW: case CornerType.Diagonal_NW: if (!c.Roofed(map)) return (c + IntVec3.West).Roofed(map); return true;
case CornerType.Corner_SE: case CornerType.Diagonal_SE: if (!c.Roofed(map)) return (c + IntVec3.East).Roofed(map); return true;
case CornerType.Corner_SW: case CornerType.Diagonal_SW: if (!c.Roofed(map)) return (c + IntVec3.West).Roofed(map); return true;
default: return false;
}
}
}
}