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

498 lines
18 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
// 覆盖层渲染:破坏裂纹叠加(destroy_stage_0..9)与第一人称手持方块
// (方块交互与动画.md §4、渲染.md §5.2)。
// 与区块网格共用顶点格式(pos+uv+light)与着色器,通过 uOffset 平移定位。
package render
import (
"image"
"math"
"unsafe"
"mc/internal/mesh"
"github.com/go-gl/gl/v3.3-core/gl"
)
// cubeFaces 单位立方体 6 面角顺序(与 mesh.faceDef 一致,
// 窗口空间 CW 绕序为 FrontFace(CW) 的正面;窗口坐标 y 向上)。
var cubeFaces = [6][4][3]float32{
{{0, 1, 0}, {1, 1, 0}, {1, 1, 1}, {0, 1, 1}}, // 顶
{{0, 0, 1}, {1, 0, 1}, {1, 0, 0}, {0, 0, 0}}, // 底
{{1, 0, 0}, {1, 0, 1}, {1, 1, 1}, {1, 1, 0}}, // 东(+x)
{{0, 0, 1}, {0, 0, 0}, {0, 1, 0}, {0, 1, 1}}, // 西(-x)
{{1, 0, 1}, {0, 0, 1}, {0, 1, 1}, {1, 1, 1}}, // 南(+z)
{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}, // 北(-z)
}
// cubeVerts 按六面 UV 构建单位立方体顶点(mesh.Vertex 格式:pos+uv+light 字节,
// 光照满亮——覆盖层不受世界光照影响)。inflate 沿角外扩(裂纹盒防 z-fighting)。
func cubeVerts(uvs [6][4]float32, inflate float32) []mesh.Vertex {
var verts []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 {
// 角 UV 权重(同 mesh.cornerUV)
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(v 翻转)
cu, cv = c[2], 1-c[1]
default: // 南/北:x→u,y→v(v 翻转)
cu, cv = c[0], 1-c[1]
}
verts = append(verts, mesh.Vertex{
X: (c[0]-0.5)*(1+inflate) + 0.5,
Y: (c[1]-0.5)*(1+inflate) + 0.5,
Z: (c[2]-0.5)*(1+inflate) + 0.5,
U: u0 + (u1-u0)*cu, V: v0 + (v1-v0)*cv,
Sky: 15, Block: 15,
})
}
}
return verts
}
// cubeIndices 24 顶点 → 36 索引(每面 2 三角形)。
func cubeIndices() []uint32 {
idx := make([]uint32, 0, 36)
for base := 0; base < 24; base += 4 {
b := uint32(base)
idx = append(idx, b, b+1, b+2, b+2, b+3, b)
}
return idx
}
// uploadMesh 上传顶点/索引并绑定顶点属性(chunk 顶点布局,渲染.md §5)。
func uploadMesh(verts []mesh.Vertex, idx []uint32) (vao, vbo, ibo uint32) {
gl.GenVertexArrays(1, &vao)
gl.GenBuffers(1, &vbo)
gl.GenBuffers(1, &ibo)
gl.BindVertexArray(vao)
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
gl.BufferData(gl.ARRAY_BUFFER, len(verts)*int(unsafe.Sizeof(mesh.Vertex{})), gl.Ptr(verts), gl.STATIC_DRAW)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(idx)*4, gl.Ptr(idx), gl.STATIC_DRAW)
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.BindVertexArray(0)
return
}
// buildCubeVAO 构建立方体 VAO(顶点格式与 chunk 一致:pos+uv+light)。
// uvs 六面 UV(顺序同 cubeFaces);inflate 沿角外扩;立方体锚点为原点,边长为 1。
func buildCubeVAO(uvs [6][4]float32, inflate float32) uint32 {
vao, _, _ := uploadMesh(cubeVerts(uvs, inflate), cubeIndices())
return vao
}
// SetCrackAtlas 上传裂纹图集(10 阶段 destroy_stage)并记录各阶段 UV。
// img 为调用方打包好的图集,rects[i] 为阶段 i 的像素矩形(图集坐标系)。
func (r *Renderer) SetCrackAtlas(img image.Image, rects [10]image.Rectangle) {
if r.crackTex == 0 {
gl.GenTextures(1, &r.crackTex)
}
gl.BindTexture(gl.TEXTURE_2D, r.crackTex)
b := img.Bounds()
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, int32(b.Dx()), int32(b.Dy()), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(imageBytes(img)))
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
W, H := float32(b.Dx()), float32(b.Dy())
const inset = 0.5 // 防渗色(渲染.md §4)
for i, rc := range rects {
r.crackUVs[i] = [4]float32{
(float32(rc.Min.X) + inset) / W, (float32(rc.Min.Y) + inset) / H,
(float32(rc.Max.X) - inset) / W, (float32(rc.Max.Y) - inset) / H,
}
}
}
// DrawCrackOverlay 在目标方块 (x,y,z) 叠加 stage 阶段裂纹盒
// (方块交互与动画.md §4:0–9,六面同图,盒体略外扩防 z-fighting)。
func (r *Renderer) DrawCrackOverlay(x, y, z float32, stage int) {
if r.crackTex == 0 {
return
}
if stage < 0 {
stage = 0
}
if stage > 9 {
stage = 9
}
if r.crackCubes[stage] == 0 {
uv := r.crackUVs[stage]
r.crackCubes[stage] = buildCubeVAO([6][4]float32{uv, uv, uv, uv, uv, uv}, 0.004)
}
gl.UseProgram(r.prog)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, r.crackTex)
vpLoc := gl.GetUniformLocation(r.prog, gl.Str("uViewProj\x00"))
var vp [16]float32
multiply4(&vp, &r.proj, &r.view)
gl.UniformMatrix4fv(vpLoc, 1, false, &vp[0])
offLoc := gl.GetUniformLocation(r.prog, gl.Str("uOffset\x00"))
gl.Uniform3f(offLoc, x, y, z)
cutLoc := gl.GetUniformLocation(r.prog, gl.Str("uCutout\x00"))
gl.Uniform1f(cutLoc, 0)
billLoc := gl.GetUniformLocation(r.prog, gl.Str("uBill\x00"))
gl.Uniform2f(billLoc, 1, 0) // 非广告牌路径
uvMinLoc := gl.GetUniformLocation(r.prog, gl.Str("uUVMin\x00"))
uvMaxLoc := gl.GetUniformLocation(r.prog, gl.Str("uUVMax\x00"))
gl.Uniform2f(uvMinLoc, 0, 0)
gl.Uniform2f(uvMaxLoc, 1, 1)
tintLoc := gl.GetUniformLocation(r.prog, gl.Str("uTint\x00"))
gl.Uniform4f(tintLoc, 1, 1, 1, 1)
gl.Enable(gl.POLYGON_OFFSET_FILL)
gl.PolygonOffset(-1, -1)
gl.BindVertexArray(r.crackCubes[stage])
gl.DrawElements(gl.TRIANGLES, 36, gl.UNSIGNED_INT, gl.PtrOffset(0))
gl.BindVertexArray(0)
gl.Disable(gl.POLYGON_OFFSET_FILL)
gl.Uniform3f(offLoc, 0, 0, 0) // 复位平移(后续绘制依赖 0 偏移)
}
// SetHandCube 上传/更新手持方块立方体(六面 UV 顺序同 cubeFaces)。
// pos/scale 为视图空间下的立方体中心与边长(渲染.md §5.2)。
func (r *Renderer) SetHandCube(uvs [6][4]float32, pos [3]float32, scale float32) {
r.ClearHand()
// 直接按视图空间坐标构建顶点(顶点含 pos 偏移)
var verts []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]
}
verts = append(verts, mesh.Vertex{
X: pos[0] + (c[0]-0.5)*scale,
Y: pos[1] + (c[1]-0.5)*scale,
Z: pos[2] + (c[2]-0.5)*scale,
U: u0 + (u1-u0)*cu, V: v0 + (v1-v0)*cv,
Sky: 15, Block: 15,
})
}
}
r.handVao, r.handVbo, r.handIbo = uploadMesh(verts, cubeIndices())
}
// ClearHand 释放手持方块网格(手持物品为空时调用)。
func (r *Renderer) ClearHand() {
if r.handVao != 0 {
gl.DeleteVertexArrays(1, &r.handVao)
r.handVao = 0
}
if r.handVbo != 0 {
gl.DeleteBuffers(1, &r.handVbo)
r.handVbo = 0
}
if r.handIbo != 0 {
gl.DeleteBuffers(1, &r.handIbo)
r.handIbo = 0
}
}
// DrawHand 绘制手持方块(顶点已是视图空间,仅乘投影矩阵;tint 肤色乘数)。
func (r *Renderer) DrawHand(tint [3]float32) {
if r.handVao == 0 {
return
}
gl.UseProgram(r.prog)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, r.atlas)
vpLoc := gl.GetUniformLocation(r.prog, gl.Str("uViewProj\x00"))
gl.UniformMatrix4fv(vpLoc, 1, false, &r.proj[0])
offLoc := gl.GetUniformLocation(r.prog, gl.Str("uOffset\x00"))
gl.Uniform3f(offLoc, 0, 0, 0)
cutLoc := gl.GetUniformLocation(r.prog, gl.Str("uCutout\x00"))
gl.Uniform1f(cutLoc, 0)
billLoc := gl.GetUniformLocation(r.prog, gl.Str("uBill\x00"))
gl.Uniform2f(billLoc, 1, 0)
uvMinLoc := gl.GetUniformLocation(r.prog, gl.Str("uUVMin\x00"))
uvMaxLoc := gl.GetUniformLocation(r.prog, gl.Str("uUVMax\x00"))
gl.Uniform2f(uvMinLoc, 0, 0)
gl.Uniform2f(uvMaxLoc, 1, 1)
tintLoc := gl.GetUniformLocation(r.prog, gl.Str("uTint\x00"))
gl.Uniform4f(tintLoc, tint[0], tint[1], tint[2], 1)
gl.BindVertexArray(r.handVao)
gl.DrawElements(gl.TRIANGLES, 36, gl.UNSIGNED_INT, gl.PtrOffset(0))
gl.BindVertexArray(0)
}
// ensureHandItemQuad 惰性构建手持物品四边形 VAO(顶点每帧重传:位置随挥动变化)。
func (r *Renderer) ensureHandItemQuad() {
if r.handItemVao != 0 {
return
}
gl.GenVertexArrays(1, &r.handItemVao)
gl.GenBuffers(1, &r.handItemVbo)
gl.GenBuffers(1, &r.handItemIbo)
gl.BindVertexArray(r.handItemVao)
gl.BindBuffer(gl.ARRAY_BUFFER, r.handItemVbo)
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))
idx := []uint32{0, 1, 2, 0, 2, 3}
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, r.handItemIbo)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(idx)*4, gl.Ptr(idx), gl.STATIC_DRAW)
gl.BindVertexArray(0)
}
// DrawHandItem 绘制手持物品图标(非放置物品,如工具/食物):视图空间小四边形,
// 轻微倾斜(原版手持角度),双面、alpha test,贴实体图集内物品贴图(渲染.md §5.2)。
// pos 为视图空间中心,scale 为边长。
func (r *Renderer) DrawHandItem(uv [4]float32, pos [3]float32, scale float32) {
if r.entityTex == 0 {
return
}
r.ensureHandItemQuad()
// 四角局部坐标(-0.5..0.5),绕 Y 转 yaw、绕 X 抬 pitch(烘焙进视图空间顶点)
const yaw, pitch = -0.6, 0.25
cy, sy := float32(math.Cos(yaw)), float32(math.Sin(yaw))
cp, sp := float32(math.Cos(pitch)), float32(math.Sin(pitch))
corners := [4][2]float32{{-0.5, -0.5}, {0.5, -0.5}, {0.5, 0.5}, {-0.5, 0.5}}
verts := make([]mesh.Vertex, 0, 4)
for _, c := range corners {
lx, ly := c[0]*scale, c[1]*scale
rx := lx * cy
rz := -lx * sy
ry := ly*cp - rz*sp
rz2 := ly*sp + rz*cp
verts = append(verts, mesh.Vertex{
X: pos[0] + rx, Y: pos[1] + ry, Z: pos[2] + rz2,
U: c[0] + 0.5, V: 0.5 - c[1], // 图集 tile:v=0 为图像顶,图标保持正立
Sky: 15, Block: 15,
})
}
gl.BindBuffer(gl.ARRAY_BUFFER, r.handItemVbo)
gl.BufferData(gl.ARRAY_BUFFER, len(verts)*int(unsafe.Sizeof(mesh.Vertex{})), gl.Ptr(verts), gl.DYNAMIC_DRAW)
gl.UseProgram(r.prog)
gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, r.entityTex)
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")), 1) // alpha test 剔透明
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")), uv[0], uv[1])
gl.Uniform2f(gl.GetUniformLocation(r.prog, gl.Str("uUVMax\x00")), uv[2], uv[3])
gl.Disable(gl.CULL_FACE) // 双面(背面为图标反相)
gl.BindVertexArray(r.handItemVao)
gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, gl.PtrOffset(0))
gl.BindVertexArray(0)
gl.Enable(gl.CULL_FACE)
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)
}
}