修复了一些bug
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user