Files
ngzz-mc/internal/render/animals_gl.go
NianGao Dev b0b632df05 feat(client): 手持物品渲染 + 动物方块模型 + 实体贴图 alpha test
- 手持非放置物品(工具/食物):实体图集物品图标小四边形(原版倾斜角,
  双面、alpha test),随挥动动画平移;可放置方块仍为六面立方体
- 动物(牛/猪/羊/鸡)改为方块组合模型(身体+头+腿),按原版皮肤布局
  取子矩形贴图,水平面向相机;修复动物 VAO 未捕获 EBO 导致的驱动崩溃
- 僵尸/村民/掉落物广告牌开启 alpha test(透明区域不再显黑块)
2026-08-16 23:23:30 +08:00

126 lines
4.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.
//go:build gl
// 动物方块模型渲染(渲染.md §5.3):身体/头/腿用方块组合近似原版动物外观,
// 贴图取自实体图集内该动物的纹理格子(子矩形映射到各面)。
// 与区块共用着色器:alpha test 剔除透明、满亮不受光照影响。
package render
import (
"math"
"unsafe"
"mc/internal/mesh"
"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)绘制方块动物模型。
// tile 为实体图集内动物纹理的归一化矩形;部件 UV 相对 tile 定义。
// flash=true 受击白闪(uTint 提亮)。
func (r *Renderer) DrawAnimalModel(x, y, z, yaw float32, model AnimalModel, tile [4]float32, flash bool) {
if r.entityTex == 0 || len(model.Parts) == 0 {
return
}
cosY := float32(math.Cos(float64(yaw)))
sinY := float32(math.Sin(float64(yaw)))
tw, th := tile[2]-tile[0], tile[3]-tile[1]
verts := make([]mesh.Vertex, 0, len(model.Parts)*24)
for _, p := range model.Parts {
for fi := 0; fi < 6; fi++ {
uv := p.UV[fi]
u0 := tile[0] + uv[0]*tw
v0 := tile[1] + uv[1]*th
u1 := tile[0] + uv[2]*tw
v1 := tile[1] + uv[3]*th
for _, c := range cubeFaces[fi] {
lx := (c[0]-0.5)*p.W + p.CX
ly := (c[1]-0.5)*p.H + p.CY
lz := (c[2]-0.5)*p.D + p.CZ
// yaw 绕 Y 旋转:模型 +z → (sinY, cosY)
wx := lx*cosY + lz*sinY
wz := -lx*sinY + lz*cosY
var cu, cv float32
switch fi {
case 0, 1: // 顶/底:x→u,z→v
cu, cv = c[0], c[2]
case 2, 3: // 东/西:z→u,y→v
cu, cv = c[2], 1-c[1]
default: // 南/北:x→u,y→v
cu, cv = c[0], 1-c[1]
}
verts = append(verts, mesh.Vertex{
X: x + wx, Y: y + ly, Z: z + wz,
U: u0 + (u1-u0)*cu, V: v0 + (v1-v0)*cv,
Sky: 15, Block: 15,
})
}
}
}
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))
// 索引缓冲绑定必须捕获进 VAO(ELEMENT_ARRAY_BUFFER 属 VAO 状态)
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)
}
// 数据上传须保持 VAO 绑定(避免污染其他 VAO 的 EBO 状态)
gl.BindVertexArray(r.animalVao)
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)
gl.BindTexture(gl.TEXTURE_2D, r.entityTex)
var vp [16]float32
multiply4(&vp, &r.proj, &r.view)
gl.UniformMatrix4fv(gl.GetUniformLocation(r.prog, gl.Str("uViewProj\x00")), 1, false, &vp[0])
gl.Uniform3f(gl.GetUniformLocation(r.prog, gl.Str("uOffset\x00")), 0, 0, 0) // 顶点已烘焙为世界坐标
gl.Uniform1f(gl.GetUniformLocation(r.prog, gl.Str("uCutout\x00")), 1) // alpha test:剔除纹理透明区域
if flash {
gl.Uniform4f(gl.GetUniformLocation(r.prog, gl.Str("uTint\x00")), 2.5, 2.5, 2.5, 1)
} else {
gl.Uniform4f(gl.GetUniformLocation(r.prog, gl.Str("uTint\x00")), 1, 1, 1, 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)
}