Initial commit
This commit is contained in:
178
internal/world/block.go
Normal file
178
internal/world/block.go
Normal file
@ -0,0 +1,178 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"edgaru089.ml/go/gl01/internal/util/itype"
|
||||
)
|
||||
|
||||
// BlockRenderType is an enum describing the rendering process of a block
|
||||
type BlockRenderType int
|
||||
|
||||
const (
|
||||
OneTexture BlockRenderType = iota // Render with one texture of the same on all faces, "Name.png"
|
||||
ThreeTexture // Render with one texture on the top, one around the sides, and one on the bottom, "Name_top/side/bot.png,"
|
||||
SixTexture // Render with six different textures on six faces, "Name_x+/x-/y+/y-/z+/z-.png"
|
||||
CustomRendering // Rendering calls BlockAppearance.CustomRenderAppend()
|
||||
)
|
||||
|
||||
// BlockAppearance describes basic appearance of a kind of block.
|
||||
type BlockAppearance struct {
|
||||
Name string // A short name, like "stone" or "dirt", used for texture lookups
|
||||
Transparent bool // Is block transparent?
|
||||
NotSolid bool // Is block not solid, i.e., has no solid hitbox? (this makes the zero value reasonable)
|
||||
Light int // The light level it emits, 0 is none
|
||||
|
||||
Hitbox itype.Boxd // Hitbox, in block-local coordinates; empty slice means a default hitbox of 1x1x1
|
||||
|
||||
RenderType BlockRenderType // Rendering type, defaults to OneTexture (zero value)
|
||||
|
||||
// Called on render if RenderType == CustomRendering.
|
||||
//
|
||||
// Be sure to return vertexArray at the end of the function (in case it got reallocated)!!!!
|
||||
CustomRenderAppend func(
|
||||
position itype.Vec3i,
|
||||
aux int,
|
||||
data itype.Dataset,
|
||||
vertexArray []Vertex,
|
||||
) []Vertex
|
||||
}
|
||||
|
||||
// BlockBehaviour describes a kind of block of the same Major ID.
|
||||
type BlockBehaviour interface {
|
||||
|
||||
// Static returns if the Behaviour is "static", i.e., the Appearance does not
|
||||
// change with position, Minor ID or Dataset. Static Behaviours are cached
|
||||
// by the renderer and only generated once.
|
||||
//
|
||||
// Static implies RequireDataset = false and RequireBlockUpdate = false.
|
||||
Static() bool
|
||||
|
||||
// RequireDataset returns if the type of block requires a Dataset attached.
|
||||
RequireDataset() bool
|
||||
|
||||
// RequireBlockUpdate return if BlockUpdate should be called if a neighboring
|
||||
// block has changed. Blocks not requiring BlockUpdate does not change at all.
|
||||
RequireBlockUpdate() bool
|
||||
|
||||
// Appearance returns the Appearance of the block at global position Position,
|
||||
// with Minor ID aux, and Dataset data.
|
||||
//
|
||||
// If RequireDataset if false, data is nil.
|
||||
Appearance(position itype.Vec3i, aux int, data itype.Dataset) BlockAppearance
|
||||
|
||||
// BlockUpdate is called when RequireBlockUpdate is true and the block at
|
||||
// global position Position, with Minor ID aux, and Dataset data has a neighbor
|
||||
// that changed state. A block will only be updated once in a tick.
|
||||
//
|
||||
// If RequireDataset if false, data is nil.
|
||||
//
|
||||
// Return true if this block also changed state, false otherwise.
|
||||
BlockUpdate(position itype.Vec3i, aux int, data itype.Dataset) bool
|
||||
}
|
||||
|
||||
type blockBehaviourStatic struct {
|
||||
app BlockAppearance
|
||||
}
|
||||
|
||||
func (blockBehaviourStatic) Static() bool { return true }
|
||||
func (blockBehaviourStatic) RequireDataset() bool { return false }
|
||||
func (blockBehaviourStatic) RequireBlockUpdate() bool { return false }
|
||||
func (b blockBehaviourStatic) Appearance(position itype.Vec3i, aux int, data itype.Dataset) BlockAppearance {
|
||||
return b.app
|
||||
}
|
||||
func (blockBehaviourStatic) BlockUpdate(position itype.Vec3i, aux int, data itype.Dataset) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// BlockBehaviourStatic returns a Static BlockBehaviour that has the given BlockAppearance.
|
||||
func BlockBehaviourStatic(app BlockAppearance) BlockBehaviour {
|
||||
return blockBehaviourStatic{app: app}
|
||||
}
|
||||
|
||||
var behaviour map[int]BlockBehaviour = make(map[int]BlockBehaviour)
|
||||
var appearance map[int]BlockAppearance = make(map[int]BlockAppearance)
|
||||
var behaviourDoneRegister bool
|
||||
|
||||
// RegisterBlockBehaviour registers behaviour with the given id.
|
||||
//
|
||||
// If the id is already taken, or id == 0, false is returned and nothing is done.
|
||||
// Otherwise, true is returned and the block is registered.
|
||||
func RegisterBlockBehaviour(id int, b BlockBehaviour) bool {
|
||||
if _, ok := behaviour[id]; behaviourDoneRegister || id == 0 || ok {
|
||||
return false
|
||||
}
|
||||
|
||||
behaviour[id] = b
|
||||
return true
|
||||
}
|
||||
|
||||
// DoneRegisteringBlockBehaviour is to be called after Registering BlockBehaviour,
|
||||
// i.e., in Post-Init() initializations.
|
||||
func DoneRegisteringBlockBehaviour() {
|
||||
for id, b := range behaviour {
|
||||
if b.Static() {
|
||||
appearance[id] = b.Appearance(itype.Vec3i{}, 0, nil)
|
||||
}
|
||||
}
|
||||
|
||||
behaviourDoneRegister = true
|
||||
}
|
||||
|
||||
// GetBlockAppearance gets the block appearance of the given block in the fastest way possible.
|
||||
func GetBlockAppearance(position itype.Vec3i, id, aux int, data itype.Dataset) BlockAppearance {
|
||||
if app, ok := appearance[id]; ok { // Cache
|
||||
if app.Hitbox == (itype.Boxd{}) {
|
||||
app.Hitbox = itype.Boxd{
|
||||
OffX: 0, OffY: 0, OffZ: 0,
|
||||
SizeX: 1, SizeY: 1, SizeZ: 1,
|
||||
}
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
// Slow way
|
||||
b, ok := behaviour[id]
|
||||
if !ok {
|
||||
panic(fmt.Sprint("invalid block type ", id))
|
||||
}
|
||||
|
||||
app := b.Appearance(position, aux, data)
|
||||
if app.Hitbox == (itype.Boxd{}) {
|
||||
app.Hitbox = itype.Boxd{
|
||||
OffX: 0, OffY: 0, OffZ: 0,
|
||||
SizeX: 1, SizeY: 1, SizeZ: 1,
|
||||
}
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
// GetBlockBehaviour gets the block behaviour of the given id, or nil if not present.
|
||||
func GetBlockBehaviour(id int) BlockBehaviour {
|
||||
return behaviour[id]
|
||||
}
|
||||
|
||||
// Block is a structure to store and pass Blocks around.
|
||||
type Block struct {
|
||||
Id, Aux int
|
||||
Dataset itype.Dataset
|
||||
Behaviour BlockBehaviour
|
||||
}
|
||||
|
||||
// Appearance is a shortcut for Behaviour.Appearance().
|
||||
// It returns the Appearance of the block with the given parameters.
|
||||
func (b *Block) Appearance(position itype.Vec3i) BlockAppearance {
|
||||
return b.Behaviour.Appearance(position, b.Aux, b.Dataset)
|
||||
}
|
||||
|
||||
// BlockUpdate is a shortcut for Behaviour.BlockUpdate().
|
||||
// It is called when RequireBlockUpdate is true and the block at
|
||||
// global position Position, with Minor ID aux, and Dataset data has a neighbor
|
||||
// that changed state. A block will only be updated once in a tick.
|
||||
//
|
||||
// If RequireDataset if false, data is nil.
|
||||
//
|
||||
// Return true if this block also changed state, false otherwise.
|
||||
func (b *Block) BlockUpdate(position itype.Vec3i) bool {
|
||||
return b.Behaviour.BlockUpdate(position, b.Aux, b.Dataset)
|
||||
}
|
160
internal/world/block.go.new
Normal file
160
internal/world/block.go.new
Normal file
@ -0,0 +1,160 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Edgaru089/gl01/internal/util/itype"
|
||||
)
|
||||
|
||||
// BlockRenderType is an enum describing the rendering process of a block
|
||||
type BlockRenderType int
|
||||
|
||||
const (
|
||||
OneTexture BlockRenderType = iota // Render with one texture of the same on all faces, "Name.png"
|
||||
ThreeTexture // Render with one texture on the top, one around the sides, and one on the bottom, "Name_top/side/bot.png,"
|
||||
SixTexture // Render with six different textures on six faces, "Name_x+/x-/y+/y-/z+/z-.png"
|
||||
CustomRendering // Rendering calls BlockAppearance.CustomRenderAppend()
|
||||
)
|
||||
|
||||
// BlockAppearance describes basic appearance of a kind of block.
|
||||
type BlockAppearance struct {
|
||||
Name string // A short name, like "stone" or "dirt", used for texture lookups
|
||||
Transparent bool // Is block transparent?
|
||||
NotSolid bool // Is block not solid, i.e., has no solid hitbox? (this makes the zero value reasonable)
|
||||
Light int // The light level it emits, 0 is none
|
||||
|
||||
Hitbox []itype.Boxd // Hitbox, in block-local coordinates; empty slice means a default hitbox of 1x1x1
|
||||
|
||||
RenderType BlockRenderType // Rendering type, defaults to OneTexture (zero value)
|
||||
|
||||
// Called on render if RenderType == CustomRendering.
|
||||
//
|
||||
// Be sure to return vertexArray at the end of the function (in case it got reallocated)!!!!
|
||||
CustomRenderAppend func(
|
||||
position itype.Vec3i,
|
||||
aux int,
|
||||
data itype.Dataset,
|
||||
vertexArray []Vertex,
|
||||
) []Vertex
|
||||
}
|
||||
|
||||
// BlockBehaviour describes a kind of block of the same Major ID.
|
||||
type BlockBehaviour interface {
|
||||
|
||||
// Static returns if the Behaviour is "static", i.e., the Appearance does not
|
||||
// change with position, Minor ID or Dataset. Static Behaviours are cached
|
||||
// by the renderer and only generated once.
|
||||
//
|
||||
// Static implies RequireDataset = false and RequireBlockUpdate = false.
|
||||
Static() bool
|
||||
|
||||
// RequireDataset returns if the type of block requires a Dataset attached.
|
||||
RequireDataset() bool
|
||||
|
||||
// RequireBlockUpdate return if BlockUpdate should be called if a neighboring
|
||||
// block has changed. Blocks not requiring BlockUpdate does not change at all.
|
||||
RequireBlockUpdate() bool
|
||||
|
||||
// Appearance returns the Appearance of the block at global position Position,
|
||||
// with Minor ID aux, and Dataset data.
|
||||
//
|
||||
// If RequireDataset if false, data is nil.
|
||||
Appearance(position itype.Vec3i, aux int, data itype.Dataset) BlockAppearance
|
||||
|
||||
// BlockUpdate is called when RequireBlockUpdate is true and the block at
|
||||
// global position Position, with Minor ID aux, and Dataset data has a neighbor
|
||||
// that changed state. A block will only be updated once in a tick.
|
||||
//
|
||||
// If RequireDataset if false, data is nil.
|
||||
//
|
||||
// Return true if this block also changed state, false otherwise.
|
||||
BlockUpdate(position itype.Vec3i, aux int, data itype.Dataset) bool
|
||||
}
|
||||
|
||||
type blockBehaviourStatic struct {
|
||||
app BlockAppearance
|
||||
}
|
||||
|
||||
func (blockBehaviourStatic) Static() bool { return true }
|
||||
func (blockBehaviourStatic) RequireDataset() bool { return false }
|
||||
func (blockBehaviourStatic) RequireBlockUpdate() bool { return false }
|
||||
func (b blockBehaviourStatic) Appearance(position itype.Vec3i, aux int, data itype.Dataset) BlockAppearance {
|
||||
return b.app
|
||||
}
|
||||
func (blockBehaviourStatic) BlockUpdate(position itype.Vec3i, aux int, data itype.Dataset) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// BlockBehaviourStatic returns a Static BlockBehaviour that has the given BlockAppearance.
|
||||
func BlockBehaviourStatic(app BlockAppearance) BlockBehaviour {
|
||||
return blockBehaviourStatic{app: app}
|
||||
}
|
||||
|
||||
var behaviour map[int]BlockBehaviour = make(map[int]BlockBehaviour)
|
||||
var appearance map[int]BlockAppearance = make(map[int]BlockAppearance)
|
||||
var behaviourDoneRegister bool
|
||||
|
||||
// RegisterBlockBehaviour registers behaviour with the given id.
|
||||
//
|
||||
// If the id is already taken, or id == 0, false is returned and nothing is done.
|
||||
// Otherwise, true is returned and the block is registered.
|
||||
func RegisterBlockBehaviour(id int, b BlockBehaviour) bool {
|
||||
if _, ok := behaviour[id]; behaviourDoneRegister || id == 0 || ok {
|
||||
return false
|
||||
}
|
||||
|
||||
behaviour[id] = b
|
||||
return true
|
||||
}
|
||||
|
||||
// DoneRegisteringBlockBehaviour is to be called after Registering BlockBehaviour,
|
||||
// i.e., in Post-Init() initializations.
|
||||
func DoneRegisteringBlockBehaviour() {
|
||||
for id, b := range behaviour {
|
||||
if b.Static() {
|
||||
appearance[id] = b.Appearance(itype.Vec3i{}, 0, nil)
|
||||
}
|
||||
}
|
||||
|
||||
behaviourDoneRegister = true
|
||||
}
|
||||
|
||||
// GetBlockAppearance gets the block appearance of the given block in the fastest way possible.
|
||||
func GetBlockAppearance(position itype.Vec3i, id, aux int, data itype.Dataset) BlockAppearance {
|
||||
if app, ok := appearance[id]; ok { // Cache
|
||||
if len(app.Hitbox) == 0 {
|
||||
app.Hitbox = []itype.Boxd{{
|
||||
OffX: 0, OffY: 0, OffZ: 0,
|
||||
SizeX: 1, SizeY: 1, SizeZ: 1,
|
||||
}}
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
// Slow way
|
||||
b, ok := behaviour[id]
|
||||
if !ok {
|
||||
panic(fmt.Sprint("invalid block type ", id))
|
||||
}
|
||||
|
||||
app := b.Appearance(position, aux, data)
|
||||
if len(app.Hitbox) == 0 {
|
||||
app.Hitbox = []itype.Boxd{{
|
||||
OffX: 0, OffY: 0, OffZ: 0,
|
||||
SizeX: 1, SizeY: 1, SizeZ: 1,
|
||||
}}
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
// GetBlockBehaviour gets the block behaviour of the given id, or nil if not present.
|
||||
func GetBlockBehaviour(id int) BlockBehaviour {
|
||||
return behaviour[id]
|
||||
}
|
||||
|
||||
// Block is a structure to store and pass Blocks around.
|
||||
type Block struct {
|
||||
Id, Aux int
|
||||
Dataset itype.Dataset
|
||||
Behaviour BlockBehaviour
|
||||
}
|
72
internal/world/chunk.go
Normal file
72
internal/world/chunk.go
Normal file
@ -0,0 +1,72 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
ChunkSizeX = 16
|
||||
ChunkSizeY = 128
|
||||
ChunkSizeZ = 16
|
||||
)
|
||||
|
||||
type Chunk struct {
|
||||
X, Z int // Chunk coordinate in global coordinate (y is always zero)
|
||||
|
||||
Id, Aux [ChunkSizeX][ChunkSizeY][ChunkSizeZ]uint16
|
||||
|
||||
// render data kept unexported (therefore excluded from gob encoding)
|
||||
renderChanged bool
|
||||
vao, vbo uint32
|
||||
vbolen int
|
||||
vertUpdate chan []Vertex
|
||||
}
|
||||
|
||||
func (c *Chunk) SetChunkID(x, z int) {
|
||||
c.X = x
|
||||
c.Z = z
|
||||
c.renderChanged = true
|
||||
}
|
||||
|
||||
func (c *Chunk) SetBlock(x, y, z int, id int) {
|
||||
c.Id[x][y][z] = uint16(id)
|
||||
c.renderChanged = true
|
||||
}
|
||||
|
||||
// InvalidateRender should be called if a block inside the chunk has changed
|
||||
// and the chunk needs to re-rendered.
|
||||
func (c *Chunk) InvalidateRender() {
|
||||
c.renderChanged = true
|
||||
}
|
||||
|
||||
// LoadFromGob loads the chunk from a gob-encoded file.
|
||||
func (c *Chunk) LoadFromGob(file io.Reader) {
|
||||
d := gob.NewDecoder(file)
|
||||
err := d.Decode(c)
|
||||
if err != nil {
|
||||
log.Print("LoadFromGob Error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadFromGobIndexed loads the chunk from a gob-encoded file, with the
|
||||
// chunk Index overwritten.
|
||||
func (c *Chunk) LoadFromGobIndexed(file io.Reader, x, z int) {
|
||||
d := gob.NewDecoder(file)
|
||||
err := d.Decode(c)
|
||||
if err != nil {
|
||||
log.Printf("LoadFromGobIndexed(x=%d, z=%d) Error: %s", x, z, err)
|
||||
}
|
||||
c.X = x
|
||||
c.Z = z
|
||||
}
|
||||
|
||||
// WriteToGob writes the chunk to a gob-encoded file.
|
||||
func (c *Chunk) WriteToGob(file io.Writer) {
|
||||
e := gob.NewEncoder(file)
|
||||
err := e.Encode(c)
|
||||
if err != nil {
|
||||
log.Printf("WriteToGob(x=%d, z=%d) Error: %s", c.X, c.Z, err)
|
||||
}
|
||||
}
|
40
internal/world/default_blocks.go
Normal file
40
internal/world/default_blocks.go
Normal file
@ -0,0 +1,40 @@
|
||||
package world
|
||||
|
||||
const (
|
||||
BlockNil = iota
|
||||
|
||||
BlockDebug
|
||||
BlockDebugDir
|
||||
BlockDebugNonexist
|
||||
|
||||
BlockStone
|
||||
BlockDirt
|
||||
BlockGrass
|
||||
BlockBedrock
|
||||
BlockSand
|
||||
BlockLogOak
|
||||
BlockLeavesOak
|
||||
|
||||
BlockCount
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
RegisterBlockBehaviour(1, BlockBehaviourStatic(BlockAppearance{Name: "debug"}))
|
||||
RegisterBlockBehaviour(2, BlockBehaviourStatic(BlockAppearance{Name: "debug_dir", RenderType: SixTexture}))
|
||||
RegisterBlockBehaviour(3, BlockBehaviourStatic(BlockAppearance{Name: "debug_nonexist"}))
|
||||
|
||||
RegisterBlockBehaviour(4, BlockBehaviourStatic(BlockAppearance{Name: "stone"}))
|
||||
RegisterBlockBehaviour(5, BlockBehaviourStatic(BlockAppearance{Name: "dirt"}))
|
||||
RegisterBlockBehaviour(6, BlockBehaviourStatic(BlockAppearance{Name: "grass", RenderType: ThreeTexture}))
|
||||
RegisterBlockBehaviour(7, BlockBehaviourStatic(BlockAppearance{Name: "bedrock"}))
|
||||
RegisterBlockBehaviour(8, BlockBehaviourStatic(BlockAppearance{Name: "sand"}))
|
||||
RegisterBlockBehaviour(9, BlockBehaviourStatic(BlockAppearance{Name: "log_oak", RenderType: ThreeTexture}))
|
||||
RegisterBlockBehaviour(10, BlockBehaviourStatic(BlockAppearance{Name: "leaves_oak"}))
|
||||
|
||||
if BlockCount != 11 {
|
||||
panic("world.DefaultBlocks: block count not correct (check for block numbering in default_blocks.go)")
|
||||
}
|
||||
|
||||
DoneRegisteringBlockBehaviour()
|
||||
}
|
215
internal/world/render.go
Normal file
215
internal/world/render.go
Normal file
@ -0,0 +1,215 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"edgaru089.ml/go/gl01/internal/asset"
|
||||
"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)
|
||||
|
||||
c.vertUpdate = make(chan []Vertex)
|
||||
|
||||
c.renderChanged = true
|
||||
}
|
||||
|
||||
func (c *Chunk) FreeRender() {
|
||||
gl.DeleteVertexArrays(1, &c.vao)
|
||||
gl.DeleteBuffers(1, &c.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)
|
||||
}
|
||||
|
||||
// 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 := c.AppendVertex([]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 <- vert
|
||||
}()
|
||||
c.renderChanged = false
|
||||
}
|
||||
|
||||
select {
|
||||
case vert := <-c.vertUpdate:
|
||||
log.Printf("Chunk [%d,%d]: received len=%d", c.X, c.Z, len(vert))
|
||||
gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(Vertex{}))*len(vert), gl.Ptr(vert), gl.DYNAMIC_DRAW)
|
||||
c.vbolen = len(vert)
|
||||
default: // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Chunk) Render() {
|
||||
c.Bind()
|
||||
c.updateRender()
|
||||
gl.DrawArrays(gl.TRIANGLES, 0, int32(c.vbolen))
|
||||
}
|
||||
|
||||
// AppendVertex appends the chunk's global vertex into the array.
|
||||
func (c *Chunk) AppendVertex(arr []Vertex) []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)
|
||||
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 = app.CustomRenderAppend(off.Addv(i, j, k), int(c.Aux[i][j][k]), nil, arr)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
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,
|
||||
).Transparent { // face next to a solid block
|
||||
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
|
||||
}
|
204
internal/world/render.go.old
Normal file
204
internal/world/render.go.old
Normal file
@ -0,0 +1,204 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Edgaru089/gl01/internal/asset"
|
||||
"github.com/Edgaru089/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.GenBuffers(1, &c.vbo)
|
||||
|
||||
c.renderChanged = true
|
||||
}
|
||||
|
||||
func (c *Chunk) FreeRender() {
|
||||
gl.DeleteVertexArrays(1, &c.vao)
|
||||
gl.DeleteBuffers(1, &c.vbo)
|
||||
}
|
||||
|
||||
// Bind calls glBindVertexArray(vao) and glBindBuffer(vbo).
|
||||
func (c *Chunk) Bind() {
|
||||
gl.BindVertexArray(c.vao)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, c.vbo)
|
||||
}
|
||||
|
||||
// EnableVertexArrayAttrib calls glEnableVertexArrayAttrib(vao, attribId).
|
||||
func (c *Chunk) EnableVertexArrayAttrib(attribId uint32) {
|
||||
c.Bind()
|
||||
gl.EnableVertexAttribArray(attribId)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
c.vertex = c.AppendVertex(c.vertex[0:0])
|
||||
log.Printf("Chunk [%d,%d]: UpdateRender: vertex len of %d*%d = %d", c.X, c.Z, unsafe.Sizeof(Vertex{}), len(c.vertex), int(unsafe.Sizeof(Vertex{}))*len(c.vertex))
|
||||
|
||||
f, _ := os.Create("vertex.txt")
|
||||
fmt.Fprint(f, c.vertex)
|
||||
f.Close()
|
||||
|
||||
gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(Vertex{}))*len(c.vertex), gl.Ptr(c.vertex), gl.DYNAMIC_DRAW)
|
||||
c.vbolen = len(c.vertex)
|
||||
|
||||
c.renderChanged = false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Chunk) Render() {
|
||||
gl.BindVertexArray(c.vao)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, c.vbo)
|
||||
c.updateRender()
|
||||
gl.DrawArrays(gl.TRIANGLES, 0, int32(c.vbolen))
|
||||
}
|
||||
|
||||
// ChunkRenderAppend appends the chunk's global vertex into the array.
|
||||
func (c *Chunk) AppendVertex(arr []Vertex) []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)
|
||||
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 = app.CustomRenderAppend(off.Addv(i, j, k), int(c.Aux[i][j][k]), nil, arr)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
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,
|
||||
).Transparent { // face next to a solid block
|
||||
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}, 0.7},
|
||||
Vertex{off.Addv(1, 1, 0), itype.Vec3f{1, 0, 0}, itype.Vec2f{1, 0}, 0.7},
|
||||
Vertex{off.Addv(1, 1, 1), itype.Vec3f{1, 0, 0}, itype.Vec2f{0, 0}, 0.7},
|
||||
Vertex{off.Addv(1, 0, 0), itype.Vec3f{1, 0, 0}, itype.Vec2f{1, 1}, 0.7},
|
||||
Vertex{off.Addv(1, 1, 1), itype.Vec3f{1, 0, 0}, itype.Vec2f{0, 0}, 0.7},
|
||||
Vertex{off.Addv(1, 0, 1), itype.Vec3f{1, 0, 0}, itype.Vec2f{0, 1}, 0.7},
|
||||
)
|
||||
case itype.XMinus: // X-
|
||||
arr = append(arr,
|
||||
Vertex{off.Addv(0, 0, 0), itype.Vec3f{-1, 0, 0}, itype.Vec2f{0, 1}, 0.95},
|
||||
Vertex{off.Addv(0, 1, 1), itype.Vec3f{-1, 0, 0}, itype.Vec2f{1, 0}, 0.95},
|
||||
Vertex{off.Addv(0, 1, 0), itype.Vec3f{-1, 0, 0}, itype.Vec2f{0, 0}, 0.95},
|
||||
Vertex{off.Addv(0, 0, 0), itype.Vec3f{-1, 0, 0}, itype.Vec2f{0, 1}, 0.95},
|
||||
Vertex{off.Addv(0, 0, 1), itype.Vec3f{-1, 0, 0}, itype.Vec2f{1, 1}, 0.95},
|
||||
Vertex{off.Addv(0, 1, 1), itype.Vec3f{-1, 0, 0}, itype.Vec2f{1, 0}, 0.95},
|
||||
)
|
||||
case itype.YPlus: // Y+
|
||||
arr = append(arr,
|
||||
Vertex{off.Addv(0, 1, 0), itype.Vec3f{0, 1, 0}, itype.Vec2f{0, 0}, 1},
|
||||
Vertex{off.Addv(0, 1, 1), itype.Vec3f{0, 1, 0}, itype.Vec2f{0, 1}, 1},
|
||||
Vertex{off.Addv(1, 1, 0), itype.Vec3f{0, 1, 0}, itype.Vec2f{1, 0}, 1},
|
||||
Vertex{off.Addv(0, 1, 1), itype.Vec3f{0, 1, 0}, itype.Vec2f{0, 1}, 1},
|
||||
Vertex{off.Addv(1, 1, 1), itype.Vec3f{0, 1, 0}, itype.Vec2f{1, 1}, 1},
|
||||
Vertex{off.Addv(1, 1, 0), itype.Vec3f{0, 1, 0}, itype.Vec2f{1, 0}, 1},
|
||||
)
|
||||
case itype.YMinus: // Y-
|
||||
arr = append(arr,
|
||||
Vertex{off.Addv(0, 0, 0), itype.Vec3f{0, -1, 0}, itype.Vec2f{1, 0}, 0.6},
|
||||
Vertex{off.Addv(1, 0, 0), itype.Vec3f{0, -1, 0}, itype.Vec2f{0, 0}, 0.6},
|
||||
Vertex{off.Addv(1, 0, 1), itype.Vec3f{0, -1, 0}, itype.Vec2f{0, 1}, 0.6},
|
||||
Vertex{off.Addv(0, 0, 0), itype.Vec3f{0, -1, 0}, itype.Vec2f{1, 0}, 0.6},
|
||||
Vertex{off.Addv(1, 0, 1), itype.Vec3f{0, -1, 0}, itype.Vec2f{0, 1}, 0.6},
|
||||
Vertex{off.Addv(0, 0, 1), itype.Vec3f{0, -1, 0}, itype.Vec2f{1, 1}, 0.6},
|
||||
)
|
||||
case itype.ZPlus: // Z+
|
||||
arr = append(arr,
|
||||
Vertex{off.Addv(0, 0, 1), itype.Vec2f{0, 1}, 0.85},
|
||||
Vertex{off.Addv(1, 0, 1), itype.Vec2f{1, 1}, 0.85},
|
||||
Vertex{off.Addv(0, 1, 1), itype.Vec2f{0, 0}, 0.85},
|
||||
Vertex{off.Addv(1, 1, 1), itype.Vec2f{1, 0}, 0.85},
|
||||
Vertex{off.Addv(0, 1, 1), itype.Vec2f{0, 0}, 0.85},
|
||||
Vertex{off.Addv(1, 0, 1), itype.Vec2f{1, 1}, 0.85},
|
||||
)
|
||||
case itype.ZMinus: // Z-
|
||||
arr = append(arr,
|
||||
Vertex{off.Addv(0, 0, 0), itype.Vec2f{1, 1}, 0.85},
|
||||
Vertex{off.Addv(0, 1, 0), itype.Vec2f{1, 0}, 0.85},
|
||||
Vertex{off.Addv(1, 1, 0), itype.Vec2f{0, 0}, 0.85},
|
||||
Vertex{off.Addv(0, 0, 0), itype.Vec2f{1, 1}, 0.85},
|
||||
Vertex{off.Addv(1, 1, 0), itype.Vec2f{0, 0}, 0.85},
|
||||
Vertex{off.Addv(1, 0, 0), itype.Vec2f{0, 1}, 0.85},
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
126
internal/world/world.go
Normal file
126
internal/world/world.go
Normal file
@ -0,0 +1,126 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"edgaru089.ml/go/gl01/internal/util/itype"
|
||||
)
|
||||
|
||||
// World holds a number of Chunks and Entities.
|
||||
type World struct {
|
||||
Chunks map[itype.Vec2i]*Chunk
|
||||
Seed int32
|
||||
}
|
||||
|
||||
// NewWorld creates a new, empty, world with no chunks.
|
||||
func NewWorld() *World {
|
||||
var w World
|
||||
w.Chunks = make(map[itype.Vec2i]*Chunk)
|
||||
|
||||
return &w
|
||||
}
|
||||
|
||||
// The default World.
|
||||
var DefaultWorld World
|
||||
|
||||
func init() {
|
||||
DefaultWorld.Chunks = make(map[itype.Vec2i]*Chunk)
|
||||
}
|
||||
|
||||
// Chunk returns the chunk at [x, z], or nil if nonexistent.
|
||||
func (w *World) Chunk(x, z int) *Chunk {
|
||||
return w.Chunks[itype.Vec2i{x, z}]
|
||||
}
|
||||
|
||||
// SetChunk sets the chunk [x, z] to c.
|
||||
func (w *World) SetChunk(x, z int, c *Chunk) {
|
||||
if old, ok := w.Chunks[itype.Vec2i{x, z}]; ok {
|
||||
old.FreeRender()
|
||||
}
|
||||
|
||||
c.X = x
|
||||
c.Z = z
|
||||
c.renderChanged = true
|
||||
w.Chunks[itype.Vec2i{x, z}] = c
|
||||
c.InitRender()
|
||||
}
|
||||
|
||||
func BlockPosToChunk(blockpos itype.Vec3i) (x, z int) {
|
||||
if blockpos[0] >= 0 {
|
||||
x = blockpos[0] / ChunkSizeX
|
||||
} else {
|
||||
x = (blockpos[0]+1)/ChunkSizeX - 1
|
||||
}
|
||||
|
||||
if blockpos[2] >= 0 {
|
||||
z = blockpos[2] / ChunkSizeZ
|
||||
} else {
|
||||
z = (blockpos[2]+1)/ChunkSizeZ - 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func BlockPosToInChunk(blockpos itype.Vec3i) (x, y, z int) {
|
||||
if blockpos[0] >= 0 {
|
||||
x = blockpos[0] % ChunkSizeX
|
||||
} else {
|
||||
x = ChunkSizeX + (blockpos[0] % ChunkSizeX)
|
||||
if x == ChunkSizeX {
|
||||
x = 0
|
||||
}
|
||||
}
|
||||
y = blockpos[1]
|
||||
if blockpos[2] >= 0 {
|
||||
z = blockpos[2] % ChunkSizeZ
|
||||
} else {
|
||||
z = ChunkSizeZ + (blockpos[2] % ChunkSizeZ)
|
||||
if z == ChunkSizeZ {
|
||||
z = 0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Block returns the block at the position [pos], or an empty Block{} if it does not exist.
|
||||
func (w *World) Block(pos itype.Vec3i) Block {
|
||||
cx, cz := BlockPosToChunk(pos)
|
||||
cix, ciy, ciz := BlockPosToInChunk(pos)
|
||||
c, ok := w.Chunks[itype.Vec2i{cx, cz}]
|
||||
if !ok || ciy < 0 || ciy >= ChunkSizeY {
|
||||
return Block{}
|
||||
}
|
||||
return Block{
|
||||
Id: int(c.Id[cix][ciy][ciz]),
|
||||
Aux: int(c.Aux[cix][ciy][ciz]),
|
||||
Dataset: nil,
|
||||
Behaviour: GetBlockBehaviour(int(c.Id[cix][ciy][ciz])),
|
||||
}
|
||||
}
|
||||
|
||||
// LoadChunkFromGob loads (or overwrites) chunk [id.x, id.z] from the Gob-encoding file.
|
||||
func (w *World) LoadChunkFromGob(id itype.Vec2i, file io.Reader) (err error) {
|
||||
c := &Chunk{}
|
||||
if err != nil {
|
||||
return fmt.Errorf("World: load chunk (%d,%d) from gob file: %s", id[0], id[1], err)
|
||||
}
|
||||
|
||||
if old, ok := w.Chunks[id]; ok {
|
||||
old.FreeRender()
|
||||
}
|
||||
|
||||
c.LoadFromGobIndexed(file, id[0], id[1])
|
||||
w.Chunks[id] = c
|
||||
c.InitRender()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Render should be called from WorldRenderer, setting shader and texture context
|
||||
// for the Render function, which merely calls BindVertexArray and DrawArrays.
|
||||
func (w *World) Render() {
|
||||
for _, c := range w.Chunks {
|
||||
c.Render()
|
||||
}
|
||||
}
|
82
internal/world/worldgen/worldgen.go
Normal file
82
internal/world/worldgen/worldgen.go
Normal file
@ -0,0 +1,82 @@
|
||||
package worldgen
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
packworld "edgaru089.ml/go/gl01/internal/world"
|
||||
"github.com/aquilax/go-perlin"
|
||||
)
|
||||
|
||||
const (
|
||||
Alpha = 5 // Weight forming the noise sum
|
||||
Beta = 2 // harmonic scaling/spacing
|
||||
N = 3 // iterations
|
||||
|
||||
AltitudeDensity = 10 // Density of the noise in altitude
|
||||
|
||||
Sealevel = 63 // Space with Y=63 and lower should be the sea
|
||||
Highest = 72 // Highest part of the terrain
|
||||
Lowest = 56 // Lowest part of the terrain
|
||||
)
|
||||
|
||||
var perlins map[int64]*perlin.Perlin
|
||||
var perlinsLock sync.RWMutex
|
||||
|
||||
func init() {
|
||||
perlins = make(map[int64]*perlin.Perlin)
|
||||
}
|
||||
|
||||
// Chunk generates the chunk (must with chunkID set!!) with seed.
|
||||
func Chunk(chunk *packworld.Chunk, world *packworld.World, seed int64) {
|
||||
|
||||
perlinsLock.RLock()
|
||||
p, ok := perlins[seed]
|
||||
if !ok {
|
||||
perlinsLock.RUnlock()
|
||||
perlinsLock.Lock()
|
||||
p = perlin.NewPerlin(Alpha, Beta, N, seed)
|
||||
perlins[seed] = p
|
||||
perlinsLock.Unlock()
|
||||
} else {
|
||||
perlinsLock.RUnlock()
|
||||
}
|
||||
|
||||
//log.Printf("noise=%.5f", p.Noise2D(0.1, 0.1))
|
||||
|
||||
offX := packworld.ChunkSizeX * chunk.X
|
||||
offZ := packworld.ChunkSizeZ * chunk.Z
|
||||
|
||||
for x := 0; x < packworld.ChunkSizeX; x++ {
|
||||
for z := 0; z < packworld.ChunkSizeZ; z++ {
|
||||
|
||||
height := Lowest + int(float64(Highest-Lowest)*p.Noise2D(
|
||||
float64(offX+x)/AltitudeDensity,
|
||||
float64(offZ+z)/AltitudeDensity))
|
||||
//log.Printf("height = %d (noise=%.5f)", height, p.Noise2D(float64(offX+x), float64(offZ+z)))
|
||||
|
||||
// covering dirt
|
||||
chunk.Id[x][height][z] = packworld.BlockGrass
|
||||
//chunk.Id[x][height][z] = packworld.BlockDebugDir
|
||||
chunk.Id[x][height-1][z] = packworld.BlockDirt
|
||||
chunk.Id[x][height-2][z] = packworld.BlockDirt
|
||||
chunk.Id[x][height-3][z] = packworld.BlockDirt
|
||||
height -= 4
|
||||
|
||||
// stone in the middle
|
||||
for y := height; y >= 1; y-- {
|
||||
chunk.Id[x][y][z] = packworld.BlockStone
|
||||
}
|
||||
|
||||
// bedrock at the bottom
|
||||
chunk.Id[x][0][z] = packworld.BlockBedrock
|
||||
|
||||
// plant trees
|
||||
if height >= 50 && height <= 70 {
|
||||
if p.Noise2D(float64(offX+x), float64(offZ+z)) > 0.9 {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chunk.InvalidateRender()
|
||||
}
|
Reference in New Issue
Block a user