package mesh import ( "path/filepath" "testing" "mc/internal/block" "mc/internal/chunk" ) // loadReg 加载注册表。 func loadReg(t *testing.T) *block.Registry { t.Helper() r, err := block.Load(filepath.Join("..", "..", "assets", "config", "blocks.json")) if err != nil { t.Fatalf("加载注册表失败: %v", err) } return r } // airNb 邻居查询:全部视为空气(不剔除任何面)。 func airNb(x, y, z int32) block.State { return block.Air } // dummyUV 固定 UV(测试不关心贴图坐标)。 func dummyUV(key string) (float32, float32, float32, float32, bool) { return 0, 0, 1, 1, true } // TestPoolReuse 顶点池复用:Build → Release → 再次 Build 不崩溃且结果正确(性能与内存.md §2)。 func TestPoolReuse(t *testing.T) { reg := loadReg(t) stone, _ := reg.ID("stone") c := chunk.New(0, 0) c.Fill(0, 0, 0, block.NewState(stone, 0)) b := NewBuilder(reg, dummyUV) for i := 0; i < 3; i++ { m := b.Build(c, airNb) if len(m.Opaque) != 24 { t.Fatalf("第 %d 次构建顶点数异常: %d", i, len(m.Opaque)) } Release(m) } } // TestSingleBlock 单方块 6 面全生成:24 顶点(6 面 × 4 顶点四边形)。 func TestSingleBlock(t *testing.T) { reg := loadReg(t) stone, _ := reg.ID("stone") c := chunk.New(0, 0) c.Fill(0, 0, 0, block.NewState(stone, 0)) c.SetBlockLight(0, 0, 0, 10) b := NewBuilder(reg, dummyUV) m := b.Build(c, airNb) if len(m.Opaque) != 24 { t.Fatalf("不透明顶点期望 24,实际 %d", len(m.Opaque)) } if len(m.Cutout) != 0 || len(m.Translucent) != 0 { t.Fatalf("其他通道应为空: cutout=%d translucent=%d", len(m.Cutout), len(m.Translucent)) } // 顶点光照传递 for _, v := range m.Opaque { if v.Block != 10 { t.Fatalf("顶点方块光期望 10,实际 %d", v.Block) } } } // TestFaceCulling 相邻不透明方块剔除共享面:2 块相邻 → 各 5 面 → 40 顶点。 func TestFaceCulling(t *testing.T) { reg := loadReg(t) stone, _ := reg.ID("stone") c := chunk.New(0, 0) c.Fill(0, 0, 0, block.NewState(stone, 0)) c.Fill(1, 0, 0, block.NewState(stone, 0)) // 邻居查询:跨区块查空气,区块内查实际方块 nb := func(x, y, z int32) block.State { if x < 0 || x >= 16 || y < 0 || y >= chunk.Height || z < 0 || z >= 16 { return block.Air } return c.Block(int(x), int(y), int(z)) } b := NewBuilder(reg, dummyUV) m := b.Build(c, nb) if len(m.Opaque) != 40 { t.Fatalf("面剔除后顶点期望 40,实际 %d", len(m.Opaque)) } } // TestChannels 透明分类:火把→cutout,水→translucent(渲染.md §6)。 func TestChannels(t *testing.T) { reg := loadReg(t) torch, _ := reg.ID("torch") water, _ := reg.ID("water") c := chunk.New(0, 0) c.Fill(0, 0, 0, block.NewState(torch, 0)) c.Fill(2, 0, 0, block.NewState(water, 0)) b := NewBuilder(reg, dummyUV) m := b.Build(c, airNb) if len(m.Cutout) != 24 { t.Fatalf("火把 cutout 顶点期望 24,实际 %d", len(m.Cutout)) } if len(m.Translucent) != 24 { t.Fatalf("水 translucent 顶点期望 24,实际 %d", len(m.Translucent)) } if len(m.Opaque) != 0 { t.Fatalf("不应有不透明顶点,实际 %d", len(m.Opaque)) } }