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

110 lines
3.9 KiB
Go
Raw Permalink 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 绘制盒模型(脸在 −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) {
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)
}