feat: Started on palette implementation

This commit is contained in:
Nicholas Novak 2023-12-10 22:37:37 -08:00
parent 88100b58ea
commit 255475c77c
4 changed files with 89 additions and 15 deletions

26
hashserver.txt Normal file
View File

@ -0,0 +1,26 @@
goos: linux
goarch: amd64
pkg: git.nicholasnovak.io/nnovak/spatial-db
cpu: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
BenchmarkInsertClusteredPoints-8 7030724 294.0 ns/op 139 B/op 0 allocs/op
BenchmarkInsertClusteredPoints-8 5987259 198.1 ns/op 10 B/op 0 allocs/op
BenchmarkInsertClusteredPoints-8 5793637 484.0 ns/op 337 B/op 0 allocs/op
BenchmarkInsertClusteredPoints-8 5644594 207.5 ns/op 0 B/op 0 allocs/op
BenchmarkInsertClusteredPoints-8 5815582 211.0 ns/op 0 B/op 0 allocs/op
BenchmarkInsertClusteredPoints-8 5618958 211.7 ns/op 2 B/op 0 allocs/op
BenchmarkInsertClusteredPoints-8 5603455 215.2 ns/op 10 B/op 0 allocs/op
BenchmarkInsertClusteredPoints-8 5454136 223.0 ns/op 13 B/op 0 allocs/op
BenchmarkInsertClusteredPoints-8 5391891 694.1 ns/op 718 B/op 0 allocs/op
BenchmarkInsertClusteredPoints-8 5574357 228.4 ns/op 0 B/op 0 allocs/op
BenchmarkInsertSparserPoints-8 5355165 218.3 ns/op 0 B/op 0 allocs/op
BenchmarkInsertSparserPoints-8 5385463 221.0 ns/op 1 B/op 0 allocs/op
BenchmarkInsertSparserPoints-8 5265614 224.1 ns/op 2 B/op 0 allocs/op
BenchmarkInsertSparserPoints-8 5269447 229.2 ns/op 7 B/op 0 allocs/op
BenchmarkInsertSparserPoints-8 5167845 231.7 ns/op 19 B/op 0 allocs/op
BenchmarkInsertSparserPoints-8 4997976 240.3 ns/op 23 B/op 0 allocs/op
BenchmarkInsertSparserPoints-8 4914524 772.6 ns/op 1584 B/op 0 allocs/op
BenchmarkInsertSparserPoints-8 1535679 692.3 ns/op 0 B/op 0 allocs/op
BenchmarkInsertSparserPoints-8 1852569 617.0 ns/op 0 B/op 0 allocs/op
BenchmarkInsertSparserPoints-8 2007783 568.2 ns/op 1 B/op 0 allocs/op
PASS
ok git.nicholasnovak.io/nnovak/spatial-db 114.742s

View File

@ -25,7 +25,7 @@ func (hs *HashServer) SetStorageRoot(path string) {
for _, section := range data.Sections { for _, section := range data.Sections {
for blockIndex, blockState := range section.BlockStates { for blockIndex, blockState := range section.BlockStates {
pos := data.IndexToBlockPos(blockIndex) pos := data.IndexToBlockPos(blockIndex)
hs.blocks[pos] = blockState hs.blocks[pos] = section.Palette.State(blockState)
} }
} }
} }

View File

@ -16,15 +16,17 @@ const (
// `ChunkData` represents the contents of a "chunk", which is a column of voxels // `ChunkData` represents the contents of a "chunk", which is a column of voxels
// in world space // in world space
type ChunkData struct { type ChunkData struct {
Pos ChunkPos `json:"pos"` // The position of the chunk, in world space
Pos ChunkPos `json:"pos"`
// The column of sections
Sections [ChunkSectionCount]ChunkSection `json:"sections"` Sections [ChunkSectionCount]ChunkSection `json:"sections"`
} }
// `ChunkSection' is a fixed-size cube that stores the data in a chunk // `ChunkSection' is a fixed-size cube that stores the data in a chunk
type ChunkSection struct { type ChunkSection struct {
// The count of full blocks in the chunk // A look-up-table of each section index to its value
BlockCount uint `json:"block_count"` Palette SectionPalette `json:"palette"`
BlockStates [16 * 16 * 16]BlockID `json:"block_states"` BlockStates [16 * 16 * 16]PaletteIndex `json:"block_states"`
} }
func rem_euclid(a, b int) int { func rem_euclid(a, b int) int {
@ -39,18 +41,12 @@ func IndexOfBlock(pos BlockPos) int {
return (baseY * chunkSliceSize) + (baseZ * 16) + baseX return (baseY * chunkSliceSize) + (baseZ * 16) + baseX
} }
func (cs *ChunkSection) UpdateBlockAtIndex(index int, targetState BlockID) {
// TODO: Keep track of the block count
cs.BlockStates[index] = targetState
}
func (cs *ChunkSection) UpdateBlock(pos BlockPos, targetState BlockID) { func (cs *ChunkSection) UpdateBlock(pos BlockPos, targetState BlockID) {
cs.BlockStates[IndexOfBlock(pos)] = targetState cs.BlockStates[IndexOfBlock(pos)] = cs.Palette.IndexFor(targetState)
} }
func (cs *ChunkSection) FetchBlock(pos BlockPos) BlockID { func (cs *ChunkSection) FetchBlock(pos BlockPos) BlockID {
return cs.BlockStates[IndexOfBlock(pos)] return cs.Palette.State(cs.BlockStates[IndexOfBlock(pos)])
} }
func (cd *ChunkData) SectionFor(pos BlockPos) *ChunkSection { func (cd *ChunkData) SectionFor(pos BlockPos) *ChunkSection {
@ -68,6 +64,8 @@ func (cd *ChunkData) IndexToBlockPos(index int) BlockPos {
} }
} }
// Conversion from Minecraft chunks
func extractPaletteIndexes(compressed int64) [16]byte { func extractPaletteIndexes(compressed int64) [16]byte {
var outputs [16]byte var outputs [16]byte
var outputIndex int var outputIndex int
@ -113,13 +111,12 @@ func (cd *ChunkData) FromMCAChunk(other save.Chunk) {
if section.BlockStates.Palette[paletteIndex].Name == "minecraft:air" { if section.BlockStates.Palette[paletteIndex].Name == "minecraft:air" {
state = Empty state = Empty
} else { } else {
cd.Sections[sectionIndex].BlockCount += 1
state = Generic state = Generic
} }
// TODO: Remove this workaround for larger bit sizes in palettes // TODO: Remove this workaround for larger bit sizes in palettes
if blockIndex < 4096 { if blockIndex < 4096 {
currentSection.BlockStates[blockIndex] = state currentSection.BlockStates[blockIndex] = currentSection.Palette.IndexFor(state)
} }
} }

51
world/palette.go Normal file
View File

@ -0,0 +1,51 @@
package world
// `SectionPalette` is a "palette", which is a sort of look-up-table (LUT) between
// an index into the LUT, and the resulting `BlockID`
//
// This palette is unique to each section, and allows ranges of blocks to be
// changed in constant time, with the downside of having to "compact" the palette
type SectionPalette []BlockID
// `Compact` removes all duplicate states from a palette and returns a new palette
func (p SectionPalette) Compact() SectionPalette {
ids := make(map[BlockID]bool)
// Filter out the duplicate block ids
for _, blockId := range p {
ids[blockId] = true
}
var np SectionPalette
for blockId := range ids {
np = append(np, blockId)
}
return np
}
// `IndexFor` returns the palette index for a specified block id
//
// If the block id does not exist, it is placed in the palette and the index
// is returned
func (p SectionPalette) IndexFor(state BlockID) PaletteIndex {
// If the state is already in the palette, return it
for index, blockId := range p {
if state == blockId {
return PaletteIndex(index)
}
}
// Otherwise, insert it into the palette and return the index
p = append(p, state)
return PaletteIndex(len(p) - 1)
}
// `State` returns the state for a palette's index
func (p SectionPalette) State(index PaletteIndex) BlockID {
return p[index]
}
type PaletteIndex byte