Skip to content
tremblestarman edited this page Dec 24, 2017 · 9 revisions

技术

Schematic

使用fnbt读取Schematic文件,并将所有方块信息写入一个三维列表中。
要匹配一个方块的模型,我们只需要这个方块的类型,不需要额外的数据。
所以我们只需要使用到方块的iddata这两个基本的方块信息。

详情 - Wiki - Schematic文件格式


Json

Model类,用于定义一个方块的模型。

相关属性

public class Model
{
    public string __comment;
    public Dictionary<string, string> textures;
    public Element[] elements;

    public class Element
    {
        public float[] from;
        public float[] to;
        public Dictionary<string, Face> faces;

        public class Face
        {
            public string texture;
            public float[] uv;
        }
    }
}

详情 - Wiki - 模型

Json.NET

通过Json.NET将Model类转为Json文件。


一个方块

方块是模型中的基本单位。

模型

尽管方块的材质不尽相同,但方块的模型却很有特点。
依照方块的外观,可以分为:

  • Layer - 层级方块,以高度划分,底部完整或近似完整,没有特殊行为的方块。

    例如:石头、箱子、半砖、荷叶...

  • Connector - 连接体,能够和相邻同类方块连接的方块。

    例如:栅栏、玻璃板、红石线...

  • Column - 柱体,以高度划分,底部不完整,没有特殊行为的方块。

    例如:南瓜梗、小麦、火把...

  • Board - 板,以高度与朝向划分,竖立的且薄的,没有特殊行为的方块。

    例如:告示牌、旗帜、门...

  • Wall - 墙贴,以高度与朝向划分,悬挂的且薄的,没有特殊行为的方块。

    例如:告示牌、旗帜、可可豆...

目前只制作了层级方块和连接体

层级方块

分为8个高度、10种类型:

  • Height=1 - 流体、羊毛、雪层;
  • Height=g - 压力板、比较器、中继器、陷阱门、荷叶;
  • Height=2 - 流体、床、雪层;
  • Height=3 - 流体、雪层、阳光检测器;
  • Height=b - 流体、半砖、附魔台、雪层;
  • Height=t - 半砖;
  • Height=5 - 流体、雪层;
  • Height=6 - 流体、铁砧、雪层;
  • Height=7 - 流体、活塞臂、火、炼药锅、漏斗、雪层;
  • Height=8 - 几乎所有建筑方块、台阶。

g:Height=1(不完整), b:Height=4(底部), t:Height=4(顶部), 真实高度 = Height * 0.125

连接体

分为3个高度、3种类型:

  • Height=1 - 玻璃板、铁栅栏;
  • Height=2 - 栅栏、石墙;
  • Height=3 - 铁轨、红石线。

真实高度 = Height * 0.5

材质

每个方块根据其id和data对应一个材质。
算法最终只会选取材质中的一个像素点。

普通

用于普通材质,范围为16*16。

材质中不包含透明部分,因此在16*16范围内可以任取像素点。

x = (float)random.NextDouble() * 16;
y = (float)random.NextDouble() * 16;
return uv{x,y};

对于smooth模式,所选取的像素点是唯一的。

return uv{0,0};

透明

用于透明材质,范围为16*16。

如果Json模型的所替换的方块不是透明方块,那么引用透明材质后,透明部分会变黑。
因此在16*16范围内任取像素点后,我们还要对Alpha进行比对,排除透明的像素点。(texturePath为对应材质的路径)

bool textureError = true;
while (textureError)
    x = (float)random.NextDouble() * 16;
    y = (float)random.NextDouble() * 16;
    Bitmap compare = new Bitmap(texturePath);
    textureError = (compare.GetPixel((int)x, (int)y).A <= 8);
}
return uv{x,y};

对于smooth模式,可以通过像素点索引顺序获取一个唯一解。

Bitmap compare = new Bitmap(texturePath);
for (int mh = 0; mh < compare.Size.Height; mh++)
{
    for (int mw = 0; mw < compare.Size.Width; mw++)
    {
        if (compare.GetPixel(mh, mw).A > 8) { x = mh; y = mw; break; }
    }
    if (x > 0 && y > 0) break;
}
return uv{x,y};

ColorMap

用于关联生物群系的材质,范围为256*256。

拿树叶为例,这个方块非常特殊,它的材质本来只有黑白二色,而绿色,是根据生物群系的colormap(选取绿色)和它的材质(代表灰度)一起生成的。

如果Json模型的所替换的方块不是这些方块,在引用黑白材质后,所显示的还是黑白材质。
只有像树叶一样的某些特殊方块支持colormap,其他则不支持。

因此对于这些方块,需要另创一套算法,能从colormap中选取合适的颜色。

/// <summary>
/// 通过生物群系获取材质uv
/// </summary>
/// <param name="temp">温度</param>
/// <param name="rain">降水</param>
/// <param name="pixel_range">像素随机范围</param>
/// <param name="rich">是否使用foliage(默认为grass)</param>
/// <param name="warm">像素深度</param>
/// <returns>uv坐标</returns>
static public float[] getColorMapUV(float temp, float rain, int pixel_range = 0, bool rich = false, float warm = 0.0f)
{
    float[] uv = new[] { /*uv-x*/0.0f, /*uv-y*/0.0f };
    float uv_x = 0.0f, uv_y = 0.0f;
    var theta = (double)new Random().NextDouble() * 360;
    var d = (float)new Random().NextDouble() * pixel_range - pixel_range / 2;
    uv_x = temp + (warm / 256f) + (d / 256f) * (float)Math.Cos(theta * Math.PI / 360); uv_y = temp * rain + (warm / 256f) + (d / 256f) * (float)Math.Sin(theta * Math.PI / 360);
    if (uv_x < uv_y) { uv_x = temp + warm - (d / 256f) * (float)Math.Cosh(theta); uv_y = temp * rain + warm - (d / 256f) * (float)Math.Sinh(theta); }
    if (rich)
    {
        uv_x = 1 - uv_x;
        uv_y = 1 - uv_y;
    }
    else
    {
        var uv_t = uv_x;
        uv_x = 1 - uv_y;
        uv_y = 1 - uv_t;
    }
    uv[0] = (1 - uv_x)*16;
    uv[1] = (1 - uv_y)*16;
    return uv;
}

通过对这些属性的设置,能够使最终在colormap中所选取的颜色接近于方块在Minecraft中所显示的颜色。

详情 - Wiki - 生物群系


技术处理

  • 模型&方块&材质

将方块按照模型分类,每个方块对应一个材质。

h8.Add("137_0", "blocks/command_block_front");

"h8":方块模型, "137_0":方块id_方块data, "blocks/command_block_front":方块材质

对于一个方块,若能够忽略其data而对应同一个材质,使用'x'代替data以简化。

h8.Add("136_x", "blocks/planks_jungle");

"h8":方块模型, "137_x":方块id_忽略data, "blocks/planks_jungle":方块材质

  • 模型缩放

将模型缩放至2*2*2的大小,使其能在Minecraft中正常显示。

if (!args.Contains("unlimit"))
{
    var max = elements.Select(element => element.to.Max()).Concat(new float[] { 0 }).Max();
    var changedAmount = (max - 32.0f) / max;
    if (changedAmount > 0)
    {
        foreach (var element in elements)
        {
            for (var i = 0; i < 3; i++)
            {
                element.from[i] = Math.Max(element.from[i] - changedAmount * element.from[i], 0);
            }
            for (var i = 0; i < 3; i++)
            {
                element.to[i] = Math.Max(element.to[i] - changedAmount * element.to[i], 0);
            }
        }
    }
}