Files
ngzz-mc/internal/mesh/mesh.go

167 lines
5.5 KiB
Go
Raw 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.
// Package mesh 区块网格构建:面剔除 + 顶点格式 + 光照(场景/渲染.md §3
//
// 网格在 CPU 构建worker 池GL 只负责上传——渲染 goroutine 不产生分配压力。
package mesh
import (
"sync"
"mc/internal/block"
"mc/internal/chunk"
)
// vertexPools 顶点切片池(性能与内存.md §2热路径零分配——渲染线程不造 mesh 垃圾)。
var vertexPools = [3]sync.Pool{
{New: func() any { s := make([]Vertex, 0, 4096); return &s }},
{New: func() any { s := make([]Vertex, 0, 1024); return &s }},
{New: func() any { s := make([]Vertex, 0, 1024); return &s }},
}
// Release 归还网格顶点缓冲到池GPU 上传后调用,性能与内存.md §8 sync.Pool 复位)。
func Release(m *ChunkMesh) {
if m == nil {
return
}
for i, s := range [3]*[]Vertex{&m.Opaque, &m.Cutout, &m.Translucent} {
if cap(*s) > 0 && cap(*s) <= 1<<20 {
vertexPools[i].Put(s)
}
*s = nil
}
}
// 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{}
// 顶点缓冲从池中取(性能与内存.md §2
ob := vertexPools[0].Get().(*[]Vertex)
cb := vertexPools[1].Get().(*[]Vertex)
tb := vertexPools[2].Get().(*[]Vertex)
*ob, *cb, *tb = (*ob)[:0], (*cb)[:0], (*tb)[:0]
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 = tb
case b.reg.IsCutout(s):
target = cb
default:
target = ob
}
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,
})
}
}
}
}
}
// 挂回结果(注意:切片头来自池,调用方上传后必须 Release
m.Opaque, m.Cutout, m.Translucent = *ob, *cb, *tb
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→uz→v
u, v = c[0], c[2]
case [3]int32{1, 0, 0}, [3]int32{-1, 0, 0}: // 东/西z→uy→v
u, v = c[2], 1-c[1]
default: // 南/北x→uy→v
u, v = c[0], 1-c[1]
}
return [2]int32{u, v}
}