修复了一些bug

This commit is contained in:
NianGao Dev
2026-09-19 14:21:08 +08:00
parent db81873409
commit 6c23eb4c62
73 changed files with 7354 additions and 2205 deletions

View File

@@ -1,8 +1,6 @@
//go:build gl
// 动物方块模型渲染(渲染.md §5.3):身体/头/腿用方块组合近似原版动物外观,
// 贴图取自实体图集内该动物的纹理格子(子矩形映射到各面)。
// 与区块共用着色器:alpha test 剔除透明、满亮不受光照影响。
// 动物方块模型渲染(渲染.md §5.3):盒模型 + 原版像素 UV,朝向 Entity.Yaw。
package render
import (
@@ -14,21 +12,7 @@ import (
"github.com/go-gl/gl/v3.3-core/gl"
)
// AnimalPart 动物模型部件(相对脚底原点,模型前向 +z)。
type AnimalPart struct {
CX, CY, CZ float32 // 部件中心
W, H, D float32 // 部件尺寸
// UV 六面在图集 tile 内归一化子矩形(u0,v0,u1,v1),顺序同 cubeFaces:
// 顶/底/东/西/南(前)/北(后)
UV [6][4]float32
}
// AnimalModel 动物模型:部件列表。
type AnimalModel struct {
Parts []AnimalPart
}
// DrawAnimalModel 在 (x,y,z)(脚底)以 yaw(0=朝 +z)绘制方块动物模型。
// DrawAnimalModel 在 (x,y,z)(脚底)以 yaw 绘制盒模型(脸在 −Z;Entity.Yaw 已 +π)。
// tile 为实体图集内动物纹理的归一化矩形;部件 UV 相对 tile 定义。
// flash=true 受击白闪(uTint 提亮)。
func (r *Renderer) DrawAnimalModel(x, y, z, yaw float32, model AnimalModel, tile [4]float32, flash bool) {

View File

@@ -67,6 +67,14 @@ func (r *Renderer) UploadChunk(cx, cz int32, m *mesh.ChunkMesh) {
r.chunks[k] = cg
}
// ClearAllChunks 切换存档时释放全部 GPU 网格。
func (r *Renderer) ClearAllChunks() {
for k, cg := range r.chunks {
cg.destroy()
delete(r.chunks, k)
}
}
// RemoveChunk 移除区块网格(区块卸载时调用)。
func (r *Renderer) RemoveChunk(cx, cz int32) {
k := chunkKey(cx, cz)

View File

@@ -308,3 +308,190 @@ func (r *Renderer) DrawHandItem(uv [4]float32, pos [3]float32, scale float32) {
gl.Uniform2f(gl.GetUniformLocation(r.prog, gl.Str("uUVMin\x00")), 0, 0)
gl.Uniform2f(gl.GetUniformLocation(r.prog, gl.Str("uUVMax\x00")), 1, 1)
}
func rotX(p [3]float32, a float32) [3]float32 {
c, s := float32(math.Cos(float64(a))), float32(math.Sin(float64(a)))
return [3]float32{p[0], p[1]*c - p[2]*s, p[1]*s + p[2]*c}
}
func rotY(p [3]float32, a float32) [3]float32 {
c, s := float32(math.Cos(float64(a))), float32(math.Sin(float64(a)))
return [3]float32{p[0]*c + p[2]*s, p[1], -p[0]*s + p[2]*c}
}
func rotZ(p [3]float32, a float32) [3]float32 {
c, s := float32(math.Cos(float64(a))), float32(math.Sin(float64(a)))
return [3]float32{p[0]*c - p[1]*s, p[0]*s + p[1]*c, p[2]}
}
func add3(a, b [3]float32) [3]float32 { return [3]float32{a[0] + b[0], a[1] + b[1], a[2] + b[2]} }
func swingChop(swing float32) float32 {
if swing <= 0 {
return 0
}
t := 1 - swing
if t < 0.45 {
return t / 0.45 * 1.15
}
return (1 - (t-0.45)/0.55) * 1.15
}
func mapSkinUV(tile [4]float32, uv [4]float32) [4]float32 {
tw, th := tile[2]-tile[0], tile[3]-tile[1]
return [4]float32{
tile[0] + uv[0]*tw, tile[1] + uv[1]*th,
tile[0] + uv[2]*tw, tile[1] + uv[3]*th,
}
}
func appendBox(verts []mesh.Vertex, uvs [6][4]float32, xf func([3]float32) [3]float32) []mesh.Vertex {
for fi, face := range cubeFaces {
u0, v0, u1, v1 := uvs[fi][0], uvs[fi][1], uvs[fi][2], uvs[fi][3]
for _, c := range face {
var cu, cv float32
switch fi {
case 0, 1:
cu, cv = c[0], c[2]
case 2, 3:
cu, cv = c[2], 1-c[1]
default:
cu, cv = c[0], 1-c[1]
}
p := xf([3]float32{c[0], c[1], c[2]})
verts = append(verts, mesh.Vertex{
X: p[0], Y: p[1], Z: p[2],
U: u0 + (u1-u0)*cu, V: v0 + (v1-v0)*cv,
Sky: 15, Block: 15,
})
}
}
return verts
}
func (r *Renderer) drawViewMesh(verts []mesh.Vertex, tex uint32, tint [3]float32, cutout float32) {
if len(verts) == 0 {
return
}
if r.animalVao == 0 {
gl.GenVertexArrays(1, &r.animalVao)
gl.GenBuffers(1, &r.animalVbo)
gl.GenBuffers(1, &r.animalIbo)
gl.BindVertexArray(r.animalVao)
gl.BindBuffer(gl.ARRAY_BUFFER, r.animalVbo)
stride := int32(unsafe.Sizeof(mesh.Vertex{}))
gl.EnableVertexAttribArray(0)
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, stride, gl.PtrOffset(0))
gl.EnableVertexAttribArray(1)
gl.VertexAttribPointer(1, 2, gl.FLOAT, false, stride, gl.PtrOffset(3*4))
gl.EnableVertexAttribArray(2)
gl.VertexAttribPointer(2, 2, gl.UNSIGNED_BYTE, false, stride, gl.PtrOffset(5*4))
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, r.animalIbo)
gl.BindVertexArray(0)
}
idx := make([]uint32, 0, len(verts)/4*6)
for base := 0; base+3 < len(verts); base += 4 {
b := uint32(base)
idx = append(idx, b, b+1, b+2, b+2, b+3, b)
}
gl.BindBuffer(gl.ARRAY_BUFFER, r.animalVbo)
gl.BufferData(gl.ARRAY_BUFFER, len(verts)*int(unsafe.Sizeof(mesh.Vertex{})), gl.Ptr(verts), gl.DYNAMIC_DRAW)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, r.animalIbo)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(idx)*4, gl.Ptr(idx), gl.DYNAMIC_DRAW)
gl.UseProgram(r.prog)
gl.ActiveTexture(gl.TEXTURE0)
if tex != 0 {
gl.BindTexture(gl.TEXTURE_2D, tex)
}
gl.UniformMatrix4fv(gl.GetUniformLocation(r.prog, gl.Str("uViewProj\x00")), 1, false, &r.proj[0])
gl.Uniform3f(gl.GetUniformLocation(r.prog, gl.Str("uOffset\x00")), 0, 0, 0)
gl.Uniform1f(gl.GetUniformLocation(r.prog, gl.Str("uCutout\x00")), cutout)
gl.Uniform4f(gl.GetUniformLocation(r.prog, gl.Str("uTint\x00")), tint[0], tint[1], tint[2], 1)
gl.Uniform2f(gl.GetUniformLocation(r.prog, gl.Str("uBill\x00")), 1, 0)
gl.Uniform2f(gl.GetUniformLocation(r.prog, gl.Str("uUVMin\x00")), 0, 0)
gl.Uniform2f(gl.GetUniformLocation(r.prog, gl.Str("uUVMax\x00")), 1, 1)
gl.BindVertexArray(r.animalVao)
gl.DrawElements(gl.TRIANGLES, int32(len(idx)), gl.UNSIGNED_INT, gl.PtrOffset(0))
gl.BindVertexArray(0)
}
// DrawFirstPerson 第一人称右手:皮肤手臂 + 可选方块/挤出物品,绕肩挥动(渲染.md §5.2)。
func (r *Renderer) DrawFirstPerson(armTile [4]float32, hasSkin bool, swing float32, blockUVs [6][4]float32, hasBlock bool, itemUV [4]float32, hasItem bool) {
shoulder := [3]float32{0.30, -0.18, -0.50}
chop := swingChop(swing)
xf := func(local [3]float32) [3]float32 {
p := rotZ(rotX(local, 0.55), -0.38)
p = rotX(p, -chop)
return add3(p, shoulder)
}
armUVs := cuboidUV(40, 16, 4, 12, 4)
if hasSkin {
for i := range armUVs {
armUVs[i] = mapSkinUV(armTile, armUVs[i])
}
}
armVerts := appendBox(nil, armUVs, func(c [3]float32) [3]float32 {
// 肩在 y=1,指尖朝 -Y;4×12×4 像素
lx := (c[0] - 0.5) * 0.10
ly := (c[1] - 1.0) * 0.30
lz := (c[2] - 0.5) * 0.10
return xf([3]float32{lx, ly, lz})
})
tint := [3]float32{1, 1, 1}
tex := r.entityTex
if !hasSkin {
tint = [3]float32{0.85, 0.65, 0.5}
if tex == 0 {
tex = r.atlas
}
}
r.drawViewMesh(armVerts, tex, tint, 0)
if hasBlock {
held := appendBox(nil, blockUVs, func(c [3]float32) [3]float32 {
p := [3]float32{(c[0] - 0.5) * 0.16, (c[1] - 0.5) * 0.16, (c[2] - 0.5) * 0.16}
p = rotY(rotX(p, 0.35), -0.55)
p = add3(p, [3]float32{0.02, -0.30, 0.04})
return xf(p)
})
r.drawViewMesh(held, r.atlas, [3]float32{1, 1, 1}, 0)
}
if hasItem && r.entityTex != 0 {
u0, v0, u1, v1 := itemUV[0], itemUV[1], itemUV[2], itemUV[3]
thick := float32(0.018)
half := float32(0.11)
corners := [4][2]float32{{-half, -half}, {half, -half}, {half, half}, {-half, half}}
var verts []mesh.Vertex
push := func(x, y, z, u, v float32) {
p := rotY(rotX([3]float32{x, y, z}, 0.25), -0.55)
p = add3(p, [3]float32{0.01, -0.30, 0.03})
p = xf(p)
verts = append(verts, mesh.Vertex{X: p[0], Y: p[1], Z: p[2], U: u, V: v, Sky: 15, Block: 15})
}
// 前
push(corners[0][0], corners[0][1], thick, u0, v1)
push(corners[1][0], corners[1][1], thick, u1, v1)
push(corners[2][0], corners[2][1], thick, u1, v0)
push(corners[3][0], corners[3][1], thick, u0, v0)
// 后
push(corners[1][0], corners[1][1], -thick, u0, v1)
push(corners[0][0], corners[0][1], -thick, u1, v1)
push(corners[3][0], corners[3][1], -thick, u1, v0)
push(corners[2][0], corners[2][1], -thick, u0, v0)
// 四边挤出(取边缘像素)
edges := [][6]float32{
{corners[0][0], corners[0][1], corners[1][0], corners[1][1], u0, v1},
{corners[1][0], corners[1][1], corners[2][0], corners[2][1], u1, (v0 + v1) / 2},
{corners[2][0], corners[2][1], corners[3][0], corners[3][1], u0, v0},
{corners[3][0], corners[3][1], corners[0][0], corners[0][1], u0, (v0 + v1) / 2},
}
for _, e := range edges {
push(e[0], e[1], thick, e[4], e[5])
push(e[2], e[3], thick, e[4], e[5])
push(e[2], e[3], -thick, e[4], e[5])
push(e[0], e[1], -thick, e[4], e[5])
}
r.drawViewMesh(verts, r.entityTex, [3]float32{1, 1, 1}, 1)
}
}

View File

@@ -48,6 +48,10 @@ type Renderer struct {
proj [16]float32
view [16]float32
fbW, fbH int // framebuffer 像素(渲染.md §5.4)
fullscr bool
restX, restY, restW, restH int
}
// chunkGL 一个区块的 GPU 资源。
@@ -71,7 +75,7 @@ func New(title string, width, height int) (*Renderer, error) {
glfw.WindowHint(glfw.ContextVersionMajor, 3)
glfw.WindowHint(glfw.ContextVersionMinor, 3)
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
glfw.WindowHint(glfw.Resizable, glfw.False)
glfw.WindowHint(glfw.Resizable, glfw.True)
glfw.WindowHint(glfw.Samples, 0)
win, err := glfw.CreateWindow(width, height, title, nil, nil)
@@ -87,7 +91,26 @@ func New(title string, width, height int) (*Renderer, error) {
return nil, fmt.Errorf("render.New gl.Init: %w", err)
}
r := &Renderer{window: win, chunks: make(map[uint64]*chunkGL)}
r := &Renderer{window: win, chunks: make(map[uint64]*chunkGL), restW: width, restH: height}
fw, fh := win.GetFramebufferSize()
if fw < 1 {
fw = width
}
if fh < 1 {
fh = height
}
r.fbW, r.fbH = fw, fh
gl.Viewport(0, 0, int32(fw), int32(fh))
win.SetFramebufferSizeCallback(func(_ *glfw.Window, w, h int) {
if w < 1 {
w = 1
}
if h < 1 {
h = 1
}
r.fbW, r.fbH = w, h
gl.Viewport(0, 0, int32(w), int32(h))
})
if err := r.initShaders(); err != nil {
glfw.Terminate()
return nil, err
@@ -114,9 +137,50 @@ func (r *Renderer) ShouldClose() bool { return r.window.ShouldClose() }
// BeginFrame 处理窗口事件并清屏(每帧开始,渲染.md §3.2 主循环)。
func (r *Renderer) BeginFrame() {
glfw.PollEvents()
w, h := r.window.GetFramebufferSize()
if w < 1 {
w = 1
}
if h < 1 {
h = 1
}
if w != r.fbW || h != r.fbH {
r.fbW, r.fbH = w, h
gl.Viewport(0, 0, int32(w), int32(h))
}
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
}
// FramebufferSize 当前 framebuffer 像素尺寸(渲染.md §5.4)。
func (r *Renderer) FramebufferSize() (int, int) { return r.fbW, r.fbH }
// ToggleFullscreen F11 / Alt+Enter 切换独占全屏(渲染.md §5.4)。必须在 GL 线程调用。
func (r *Renderer) ToggleFullscreen() {
if r.fullscr {
r.window.SetMonitor(nil, r.restX, r.restY, r.restW, r.restH, 0)
r.fullscr = false
return
}
r.restX, r.restY = r.window.GetPos()
r.restW, r.restH = r.window.GetSize()
if r.restW < 1 {
r.restW = 1280
}
if r.restH < 1 {
r.restH = 720
}
mon := glfw.GetPrimaryMonitor()
if mon == nil {
return
}
mode := mon.GetVideoMode()
if mode == nil {
return
}
r.window.SetMonitor(mon, 0, 0, mode.Width, mode.Height, mode.RefreshRate)
r.fullscr = true
}
// EndFrame 交换缓冲(每帧结束)。
func (r *Renderer) EndFrame() { r.window.SwapBuffers() }