package world import ( "log" "time" "unsafe" "edgaru089.ml/go/gl01/internal/asset" "edgaru089.ml/go/gl01/internal/io" "edgaru089.ml/go/gl01/internal/util" "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) 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) 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) } // 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{}) 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} }() c.renderChanged = false } select { case vert := <-c.vertUpdate: gl.BindBuffer(gl.ARRAY_BUFFER, c.vbo) 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) 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]) 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 } func (c *Chunk) Render() { if !c.checkView(io.ViewPos, io.ViewDir) || !c.checkView(io.RenderPos, io.RenderDir) { return } c.updateRender() c.Bind() 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) { 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 } app := GetBlockAppearance(off.Addv(i, j, k), int(c.Id[i][j][k]), int(c.Aux[i][j][k]), nil, c.world) 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) } } } } return arr, arrwater } 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]]), nil, c.world, ).Transparent { // face next to a solid block return arr // skip! } 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! } 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 }