110 lines
3.9 KiB
Go
110 lines
3.9 KiB
Go
//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)
|
||
}
|