gl01/internal/world/render.go

290 lines
11 KiB
Go
Raw Normal View History

2022-01-20 21:58:50 +08:00
package world
import (
"log"
"time"
"unsafe"
"edgaru089.ml/go/gl01/internal/asset"
"edgaru089.ml/go/gl01/internal/io"
2022-02-02 01:52:02 +08:00
"edgaru089.ml/go/gl01/internal/util"
2022-01-20 21:58:50 +08:00
"edgaru089.ml/go/gl01/internal/util/itype"
"github.com/go-gl/gl/all-core/gl"
)
// Vertex represents a rendering vertex in the global coordinate system.
type Vertex struct {
World itype.Vec3f // World vertex coordinate
Normal itype.Vec3f // Surface normal vector (normalized)
Texture itype.Vec2f // World Texture Atlas vertex coordinate
Light float32 // Light level of the block (0 ~ 15=blocklight, 16=sunlight)
}
func (c *Chunk) InitRender() {
gl.GenVertexArrays(1, &c.vao)
gl.BindVertexArray(c.vao)
gl.GenBuffers(1, &c.vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, c.vbo)
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.World))))
gl.VertexAttribPointer(1, 3, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.Normal))))
gl.VertexAttribPointer(2, 2, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.Texture))))
gl.VertexAttribPointer(3, 1, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.Light))))
gl.EnableVertexAttribArray(0)
gl.EnableVertexAttribArray(1)
gl.EnableVertexAttribArray(2)
gl.EnableVertexAttribArray(3)
gl.GenVertexArrays(1, &c.water.vao)
gl.BindVertexArray(c.water.vao)
gl.GenBuffers(1, &c.water.vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, c.water.vbo)
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.World))))
gl.VertexAttribPointer(1, 3, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.Normal))))
gl.VertexAttribPointer(2, 2, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.Texture))))
gl.VertexAttribPointer(3, 1, gl.FLOAT, false, int32(unsafe.Sizeof(Vertex{})), gl.PtrOffset(int(unsafe.Offsetof(Vertex{}.Light))))
gl.EnableVertexAttribArray(0)
gl.EnableVertexAttribArray(1)
gl.EnableVertexAttribArray(2)
gl.EnableVertexAttribArray(3)
c.vertUpdate = make(chan [2][]Vertex, 2)
2022-01-20 21:58:50 +08:00
c.renderChanged = true
}
func (c *Chunk) FreeRender() {
gl.DeleteVertexArrays(1, &c.vao)
gl.DeleteBuffers(1, &c.vbo)
gl.DeleteVertexArrays(1, &c.water.vao)
gl.DeleteBuffers(1, &c.water.vbo)
2022-01-20 21:58:50 +08:00
close(c.vertUpdate)
}
// Bind calls glBindVertexArray(vao) and glBindBuffer(vbo).
func (c *Chunk) Bind() {
gl.BindVertexArray(c.vao)
gl.BindBuffer(gl.ARRAY_BUFFER, c.vbo)
}
// BindWater binds the semi-transparant layer of the renderer.
func (c *Chunk) BindWater() {
gl.BindVertexArray(c.water.vao)
gl.BindBuffer(gl.ARRAY_BUFFER, c.water.vbo)
}
2022-01-20 21:58:50 +08:00
// a global vertex array for VBO updates
//var vertex []Vertex
// updateRender should be called with gl.BindBuffer(c.vbo).
func (c *Chunk) updateRender() {
if c.renderChanged {
go func() {
t := time.Now()
vert, vertWater := c.appendVertex([]Vertex{}, []Vertex{})
2022-01-20 21:58:50 +08:00
log.Printf("Chunk [%d,%d]: UpdateRender: vertex len of %d*%d = %d in %dms", c.X, c.Z, unsafe.Sizeof(Vertex{}), len(vert), int(unsafe.Sizeof(Vertex{}))*len(vert), time.Since(t).Milliseconds())
c.vertUpdate <- [2][]Vertex{vert, vertWater}
2022-01-20 21:58:50 +08:00
}()
c.renderChanged = false
}
select {
case vert := <-c.vertUpdate:
gl.BindBuffer(gl.ARRAY_BUFFER, c.vbo)
2022-02-02 01:52:02 +08:00
gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(Vertex{}))*len(vert[0]), util.Ptr(vert[0]), gl.DYNAMIC_DRAW)
gl.BindBuffer(gl.ARRAY_BUFFER, c.water.vbo)
2022-02-02 01:52:02 +08:00
gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(Vertex{}))*len(vert[1]), util.Ptr(vert[1]), gl.DYNAMIC_DRAW)
c.vbolen = len(vert[0])
c.water.vbolen = len(vert[1])
2022-01-20 21:58:50 +08:00
default: // do nothing
}
}
var checkViewOffset = []itype.Vec3d{
{0, 0, 0},
{ChunkSizeX, 0, 0},
{0, 0, ChunkSizeZ},
{ChunkSizeX, 0, ChunkSizeZ},
{0, ChunkSizeY, 0},
{ChunkSizeX, ChunkSizeY, 0},
{0, ChunkSizeY, ChunkSizeZ},
{ChunkSizeX, ChunkSizeY, ChunkSizeZ},
}
// checkView checks if the chunk is in the front-facing direction of the view.
func (c *Chunk) checkView(from, facing itype.Vec3d) bool {
off := itype.Vec3d{
float64(c.X * ChunkSizeX),
0,
float64(c.Z * ChunkSizeZ),
}
for _, check := range checkViewOffset {
ok := off.Add(check).Add(from.Negative()).Dot(facing) > 0
if ok {
return true
}
}
return false
}
2022-01-20 21:58:50 +08:00
func (c *Chunk) Render() {
if !c.checkView(io.ViewPos, io.ViewDir) || !c.checkView(io.RenderPos, io.RenderDir) {
return
}
2022-01-20 21:58:50 +08:00
c.updateRender()
c.Bind()
2022-01-20 21:58:50 +08:00
gl.DrawArrays(gl.TRIANGLES, 0, int32(c.vbolen))
}
func (c *Chunk) RenderWater() {
if !c.checkView(io.ViewPos, io.ViewDir) || !c.checkView(io.RenderPos, io.RenderDir) {
return
}
c.updateRender()
c.BindWater()
gl.DrawArrays(gl.TRIANGLES, 0, int32(c.water.vbolen))
}
// appendVertex appends the chunk's global vertex into the array.
func (c *Chunk) appendVertex(arr, arrwater []Vertex) (fin, finwater []Vertex) {
2022-01-20 21:58:50 +08:00
off := itype.Vec3i{
c.X * ChunkSizeX,
0,
c.Z * ChunkSizeZ,
}
for i := 0; i < ChunkSizeX; i++ {
for j := 0; j < ChunkSizeY; j++ {
for k := 0; k < ChunkSizeZ; k++ {
if c.Id[i][j][k] == 0 {
continue
}
2022-01-28 15:24:03 +08:00
app := GetBlockAppearance(off.Addv(i, j, k), int(c.Id[i][j][k]), int(c.Aux[i][j][k]), nil, c.world)
2022-01-20 21:58:50 +08:00
switch app.RenderType {
case OneTexture:
arr = c.appendFace(itype.XPlus, itype.Vec3i{i, j, k}, app.Name+".png", arr)
arr = c.appendFace(itype.XMinus, itype.Vec3i{i, j, k}, app.Name+".png", arr)
arr = c.appendFace(itype.YPlus, itype.Vec3i{i, j, k}, app.Name+".png", arr)
arr = c.appendFace(itype.YMinus, itype.Vec3i{i, j, k}, app.Name+".png", arr)
arr = c.appendFace(itype.ZPlus, itype.Vec3i{i, j, k}, app.Name+".png", arr)
arr = c.appendFace(itype.ZMinus, itype.Vec3i{i, j, k}, app.Name+".png", arr)
case ThreeTexture:
arr = c.appendFace(itype.XPlus, itype.Vec3i{i, j, k}, app.Name+"_side.png", arr)
arr = c.appendFace(itype.XMinus, itype.Vec3i{i, j, k}, app.Name+"_side.png", arr)
arr = c.appendFace(itype.YPlus, itype.Vec3i{i, j, k}, app.Name+"_top.png", arr)
arr = c.appendFace(itype.YMinus, itype.Vec3i{i, j, k}, app.Name+"_bot.png", arr)
arr = c.appendFace(itype.ZPlus, itype.Vec3i{i, j, k}, app.Name+"_side.png", arr)
arr = c.appendFace(itype.ZMinus, itype.Vec3i{i, j, k}, app.Name+"_side.png", arr)
case SixTexture:
arr = c.appendFace(itype.XPlus, itype.Vec3i{i, j, k}, app.Name+"_x+.png", arr)
arr = c.appendFace(itype.XMinus, itype.Vec3i{i, j, k}, app.Name+"_x-.png", arr)
arr = c.appendFace(itype.YPlus, itype.Vec3i{i, j, k}, app.Name+"_y+.png", arr)
arr = c.appendFace(itype.YMinus, itype.Vec3i{i, j, k}, app.Name+"_y-.png", arr)
arr = c.appendFace(itype.ZPlus, itype.Vec3i{i, j, k}, app.Name+"_z+.png", arr)
arr = c.appendFace(itype.ZMinus, itype.Vec3i{i, j, k}, app.Name+"_z-.png", arr)
case CustomRendering:
arr, arrwater = app.CustomRenderAppend(off.Addv(i, j, k), int(c.Aux[i][j][k]), nil, c.world, arr, arrwater)
2022-01-20 21:58:50 +08:00
}
}
}
}
return arr, arrwater
2022-01-20 21:58:50 +08:00
}
func (c *Chunk) appendFace(face itype.Direction, pos itype.Vec3i, texname string, arr []Vertex) []Vertex {
off := pos.Addv(c.X*ChunkSizeX, 0, c.Z*ChunkSizeZ).ToFloat32()
// check if we can skip this face
next := pos.Add(itype.DirectionVeci[face])
if next[0] >= 0 && next[1] >= 0 && next[2] >= 0 && next[0] < ChunkSizeX && next[1] < ChunkSizeY && next[2] < ChunkSizeZ &&
c.Id[next[0]][next[1]][next[2]] != 0 &&
!GetBlockAppearance(
next.Addv(c.X*ChunkSizeX, 0, c.Z*ChunkSizeZ),
int(c.Id[next[0]][next[1]][next[2]]),
int(c.Aux[next[0]][next[1]][next[2]]),
2022-01-28 15:24:03 +08:00
nil, c.world,
2022-01-20 21:58:50 +08:00
).Transparent { // face next to a solid block
return arr // skip!
}
2022-01-28 15:24:03 +08:00
worldnext := pos.Add(itype.DirectionVeci[face]).Addv(c.X*ChunkSizeX, 0, c.Z*ChunkSizeZ)
if block := c.world.Block(worldnext); block.Id != 0 && !block.Appearance(worldnext).Transparent {
return arr // skip!
}
2022-01-20 21:58:50 +08:00
switch face {
case itype.XPlus: // X+
arr = append(arr,
// Vertex Position Normal Vector(normalized) Texture Coord Light
Vertex{off.Addv(1, 0, 0), itype.Vec3f{1, 0, 0}, itype.Vec2f{1, 1}, 16},
Vertex{off.Addv(1, 1, 0), itype.Vec3f{1, 0, 0}, itype.Vec2f{1, 0}, 16},
Vertex{off.Addv(1, 1, 1), itype.Vec3f{1, 0, 0}, itype.Vec2f{0, 0}, 16},
Vertex{off.Addv(1, 0, 0), itype.Vec3f{1, 0, 0}, itype.Vec2f{1, 1}, 16},
Vertex{off.Addv(1, 1, 1), itype.Vec3f{1, 0, 0}, itype.Vec2f{0, 0}, 16},
Vertex{off.Addv(1, 0, 1), itype.Vec3f{1, 0, 0}, itype.Vec2f{0, 1}, 16},
)
case itype.XMinus: // X-
arr = append(arr,
Vertex{off.Addv(0, 0, 0), itype.Vec3f{-1, 0, 0}, itype.Vec2f{0, 1}, 16},
Vertex{off.Addv(0, 1, 1), itype.Vec3f{-1, 0, 0}, itype.Vec2f{1, 0}, 16},
Vertex{off.Addv(0, 1, 0), itype.Vec3f{-1, 0, 0}, itype.Vec2f{0, 0}, 16},
Vertex{off.Addv(0, 0, 0), itype.Vec3f{-1, 0, 0}, itype.Vec2f{0, 1}, 16},
Vertex{off.Addv(0, 0, 1), itype.Vec3f{-1, 0, 0}, itype.Vec2f{1, 1}, 16},
Vertex{off.Addv(0, 1, 1), itype.Vec3f{-1, 0, 0}, itype.Vec2f{1, 0}, 16},
)
case itype.YPlus: // Y+
arr = append(arr,
Vertex{off.Addv(0, 1, 0), itype.Vec3f{0, 1, 0}, itype.Vec2f{0, 0}, 16},
Vertex{off.Addv(0, 1, 1), itype.Vec3f{0, 1, 0}, itype.Vec2f{0, 1}, 16},
Vertex{off.Addv(1, 1, 0), itype.Vec3f{0, 1, 0}, itype.Vec2f{1, 0}, 16},
Vertex{off.Addv(0, 1, 1), itype.Vec3f{0, 1, 0}, itype.Vec2f{0, 1}, 16},
Vertex{off.Addv(1, 1, 1), itype.Vec3f{0, 1, 0}, itype.Vec2f{1, 1}, 16},
Vertex{off.Addv(1, 1, 0), itype.Vec3f{0, 1, 0}, itype.Vec2f{1, 0}, 16},
)
case itype.YMinus: // Y-
arr = append(arr,
Vertex{off.Addv(0, 0, 0), itype.Vec3f{0, -1, 0}, itype.Vec2f{1, 0}, 16},
Vertex{off.Addv(1, 0, 0), itype.Vec3f{0, -1, 0}, itype.Vec2f{0, 0}, 16},
Vertex{off.Addv(1, 0, 1), itype.Vec3f{0, -1, 0}, itype.Vec2f{0, 1}, 16},
Vertex{off.Addv(0, 0, 0), itype.Vec3f{0, -1, 0}, itype.Vec2f{1, 0}, 16},
Vertex{off.Addv(1, 0, 1), itype.Vec3f{0, -1, 0}, itype.Vec2f{0, 1}, 16},
Vertex{off.Addv(0, 0, 1), itype.Vec3f{0, -1, 0}, itype.Vec2f{1, 1}, 16},
)
case itype.ZPlus: // Z+
arr = append(arr,
Vertex{off.Addv(0, 0, 1), itype.Vec3f{0, 0, 1}, itype.Vec2f{0, 1}, 16},
Vertex{off.Addv(1, 0, 1), itype.Vec3f{0, 0, 1}, itype.Vec2f{1, 1}, 16},
Vertex{off.Addv(0, 1, 1), itype.Vec3f{0, 0, 1}, itype.Vec2f{0, 0}, 16},
Vertex{off.Addv(1, 1, 1), itype.Vec3f{0, 0, 1}, itype.Vec2f{1, 0}, 16},
Vertex{off.Addv(0, 1, 1), itype.Vec3f{0, 0, 1}, itype.Vec2f{0, 0}, 16},
Vertex{off.Addv(1, 0, 1), itype.Vec3f{0, 0, 1}, itype.Vec2f{1, 1}, 16},
)
case itype.ZMinus: // Z-
arr = append(arr,
Vertex{off.Addv(0, 0, 0), itype.Vec3f{0, 0, -1}, itype.Vec2f{1, 1}, 16},
Vertex{off.Addv(0, 1, 0), itype.Vec3f{0, 0, -1}, itype.Vec2f{1, 0}, 16},
Vertex{off.Addv(1, 1, 0), itype.Vec3f{0, 0, -1}, itype.Vec2f{0, 0}, 16},
Vertex{off.Addv(0, 0, 0), itype.Vec3f{0, 0, -1}, itype.Vec2f{1, 1}, 16},
Vertex{off.Addv(1, 1, 0), itype.Vec3f{0, 0, -1}, itype.Vec2f{0, 0}, 16},
Vertex{off.Addv(1, 0, 0), itype.Vec3f{0, 0, -1}, itype.Vec2f{0, 1}, 16},
)
}
texrect := asset.WorldTextureAtlas.RectNormalized(texname)
for i := len(arr) - 6; i < len(arr); i++ {
arr[i].Texture[0] = texrect.Left + arr[i].Texture[0]*texrect.Width
arr[i].Texture[1] = texrect.Top + arr[i].Texture[1]*texrect.Height
}
return arr
}