Files
ngzz-mc/internal/render/animals_gl.go
2026-09-19 14:21:08 +08:00

110 lines
3.9 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):盒模型 + 原版像素 UV朝向 Entity.Yaw。
package render
import (
"math"
"unsafe"
"mc/internal/mesh"
"github.com/go-gl/gl/v3.3-core/gl"
)
// DrawAnimalModel 在 (x,y,z)(脚底)以 yaw 绘制盒模型(脸在 ZEntity.Yaw 已 +π)。
// 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→uz→v
cu, cv = c[0], c[2]
case 2, 3: // 东/西z→uy→v
cu, cv = c[2], 1-c[1]
default: // 南/北x→uy→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))
// 索引缓冲绑定必须捕获进 VAOELEMENT_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)
}