// Package mesh 区块网格构建:面剔除 + 顶点格式 + 光照(场景/渲染.md §3)。 // // 网格在 CPU 构建(worker 池),GL 只负责上传——渲染 goroutine 不产生分配压力。 package mesh import ( "mc/internal/block" "mc/internal/chunk" ) // Vertex 顶点格式(渲染.md §5):位置 + UV + 天空/方块光各 4-bit。 type Vertex struct { X, Y, Z float32 U, V float32 Sky, Block uint8 } // ChunkMesh 一个区块的三类网格(渲染.md §6 三 pass)。 // 每面 4 顶点(四边形),渲染层用索引缓冲扩为 2 三角形(顺序 0-1-2-2-3-0)。 type ChunkMesh struct { Opaque []Vertex // 不透明(主 pass) Cutout []Vertex // alpha test(树叶/玻璃) Translucent []Vertex // 半透明(水,远→近排序 pass) } // Neighbor 邻居方块查询(跨区块面剔除用,区块管理.md §6)。 type Neighbor func(x, y, z int32) block.State // UVFunc 纹理键 → 图集 UV(归一化坐标,含 inset)。 type UVFunc func(texKey string) (u0, v0, u1, v1 float32, ok bool) // Builder 网格构建器。 type Builder struct { reg *block.Registry uv UVFunc } // NewBuilder 创建构建器。 func NewBuilder(reg *block.Registry, uv UVFunc) *Builder { return &Builder{reg: reg, uv: uv} } // face 一个面的 4 个角偏移与朝向。 type faceDef struct { dir [3]int32 corners [4][3]int32 } // faces 六面定义(角顺序与 UV 顶点顺序一致)。 var faces = [6]faceDef{ {dir: [3]int32{0, 1, 0}, corners: [4][3]int32{{0, 1, 0}, {1, 1, 0}, {1, 1, 1}, {0, 1, 1}}}, // 顶 {dir: [3]int32{0, -1, 0}, corners: [4][3]int32{{0, 0, 1}, {1, 0, 1}, {1, 0, 0}, {0, 0, 0}}}, // 底 {dir: [3]int32{1, 0, 0}, corners: [4][3]int32{{1, 0, 0}, {1, 0, 1}, {1, 1, 1}, {1, 1, 0}}}, // 东(+x) {dir: [3]int32{-1, 0, 0}, corners: [4][3]int32{{0, 0, 1}, {0, 0, 0}, {0, 1, 0}, {0, 1, 1}}}, // 西(-x) {dir: [3]int32{0, 0, 1}, corners: [4][3]int32{{1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}}, // 南(+z) {dir: [3]int32{0, 0, -1}, corners: [4][3]int32{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}, // 北(-z) } // faceTexture 面方向 → 注册表纹理面名(block.Registry.Texture 参数)。 var faceTexture = [6]string{"up", "down", "east", "west", "south", "north"} // Build 构建区块网格(面剔除:仅当邻格非完全不透明时生成该面,渲染.md §3.1)。 func (b *Builder) Build(c *chunk.Chunk, nb Neighbor) *ChunkMesh { m := &ChunkMesh{} blocks := c.Snapshot() baseX, baseZ := float32(c.CX*16), float32(c.CZ*16) for x := 0; x < 16; x++ { for y := 0; y < chunk.Height; y++ { for z := 0; z < 16; z++ { s := blocks[chunk.Index(x, y, z)] if s == block.Air { continue } var target *[]Vertex switch { case b.reg.IsTranslucent(s): target = &m.Translucent case b.reg.IsCutout(s): target = &m.Cutout default: target = &m.Opaque } wx := int32(x) + c.CX*16 wy := int32(y) wz := int32(z) + c.CZ*16 sky, blk := c.SkyLight(x, y, z), c.BlockLight(x, y, z) for fi, f := range faces { nx := wx + f.dir[0] ny := wy + f.dir[1] nz := wz + f.dir[2] neighbor := nb(nx, ny, nz) if b.reg.IsOpaque(neighbor) { continue // 面被不透明邻格遮挡(面剔除) } // 同类型邻格也剔除(水贴水不渲染内面;树叶相邻保留边缘,简化规则) if neighbor != block.Air && !b.reg.IsOpaque(neighbor) && b.reg.IsTranslucent(neighbor) == b.reg.IsTranslucent(s) && b.reg.IsTranslucent(s) { continue } texKey := b.reg.Texture(s, faceTexture[fi]) u0, v0, u1, v1, ok := b.uv(texKey) if !ok { u0, v0, u1, v1 = 0, 0, 1, 1 } for _, corner := range f.corners { *target = append(*target, Vertex{ X: baseX + float32(x) + float32(corner[0]), Y: float32(y) + float32(corner[1]), Z: baseZ + float32(z) + float32(corner[2]), U: u0 + (u1-u0)*float32(cornerUV(corner, f)[0]), V: v0 + (v1-v0)*float32(cornerUV(corner, f)[1]), Sky: sky, Block: blk, }) } } } } } return m } // cornerUV 角在面上的 UV 权重(0/1),顺序与 corners 一致。 func cornerUV(c [3]int32, f faceDef) [2]int32 { var u, v int32 switch f.dir { case [3]int32{0, 1, 0}, [3]int32{0, -1, 0}: // 顶/底:x→u,z→v u, v = c[0], c[2] case [3]int32{1, 0, 0}, [3]int32{-1, 0, 0}: // 东/西:z→u,y→v u, v = c[2], 1-c[1] default: // 南/北:x→u,y→v u, v = c[0], 1-c[1] } return [2]int32{u, v} }