func NewChunk(x, z int) *RenderObject { chunkSize := 16 verticies := make([]Vertex, 0, chunkSize*chunkSize*chunkSize*6) chunkX := x * chunkSize chunkZ := z * chunkSize for ix := 0; ix < chunkSize; ix++ { for iz := 0; iz < chunkSize; iz++ { height := int(((simplexnoise.Noise2(float64(ix+chunkX)/20, float64(iz+chunkZ)/20) + 1) / 2) * float64(chunkSize)) for iy := 0; iy < height; iy++ { verticies = append(verticies, OffsetFace(0, ix+chunkX, iy, iz+chunkZ)...) verticies = append(verticies, OffsetFace(1, ix+chunkX, iy, iz+chunkZ)...) verticies = append(verticies, OffsetFace(2, ix+chunkX, iy, iz+chunkZ)...) verticies = append(verticies, OffsetFace(3, ix+chunkX, iy, iz+chunkZ)...) verticies = append(verticies, OffsetFace(4, ix+chunkX, iy, iz+chunkZ)...) verticies = append(verticies, OffsetFace(5, ix+chunkX, iy, iz+chunkZ)...) } } } ro := new(RenderObject) numVerticies := len(verticies) size := numVerticies * int(unsafe.Sizeof(verticies[0])) ro.vertexBuffer = MakeBuffer(gl.ARRAY_BUFFER, size, verticies[:numVerticies]) ro.numVerticies = numVerticies return ro }
func DoTestSimplexNoise() { var min, max float64 min, max = 1, -1 const iter = 1e7 var sum float64 for i := 0; i < iter; i++ { switch rnd := simplexnoise.Noise1(float64(i) * 1.827231321); { case rnd < min: min = rnd sum += rnd case rnd > max: max = rnd sum += rnd default: sum += rnd } } // fmt.Printf("DoTestSimplexNoise simplexnoise.Noise1 min %v, max %v, average %v\n", min, max, sum/iter) DoTestCheck("DoTestSimplexNoise simplexnoise.Noise1 min=-1.0", math.Abs(min+1) < 1e-3) DoTestCheck("DoTestSimplexNoise simplexnoise.Noise1 max=1.0", math.Abs(max-1) < 1e-3) DoTestCheck("DoTestSimplexNoise simplexnoise.Noise1 average=0.0", math.Abs(sum/iter) < 0.04) // Not very good, off by more than 3% min, max = 1, -1 sum = 0 for i := 0; i < iter; i++ { switch rnd := simplexnoise.Noise2(float64(i)*1.827411321, float64(i)*7.12312781); { case rnd < min: min = rnd sum += rnd case rnd > max: max = rnd sum += rnd default: sum += rnd } } // fmt.Printf("DoTestSimplexNoise simplexnoise.Noise2 min %v, max %v, average %v\n", min, max, sum/iter) DoTestCheck("DoTestSimplexNoise simplexnoise.Noise2 min=-1.0", math.Abs(min+1) < 1e-3) DoTestCheck("DoTestSimplexNoise simplexnoise.Noise2 max=1.0", math.Abs(max-1) < 1e-3) DoTestCheck("DoTestSimplexNoise simplexnoise.Noise2 average=0.0", math.Abs(sum/iter) < 1e-4) min, max = 1, -1 sum = 0 for i := 0; i < iter; i++ { switch rnd := simplexnoise.Noise3(float64(i)*1.827131321, float64(i)*7.12372381, float64(i)*4.923716223); { case rnd < min: min = rnd sum += rnd case rnd > max: max = rnd sum += rnd default: sum += rnd } } // fmt.Printf("DoTestSimplexNoise simplexnoise.Noise3 min %v, max %v, average %v\n", min, max, sum/iter) DoTestCheck("DoTestSimplexNoise simplexnoise.Noise3 min=-1.0", math.Abs(min+1) < 1e-3) DoTestCheck("DoTestSimplexNoise simplexnoise.Noise3 max=1.0", math.Abs(max-1) < 1e-3) DoTestCheck("DoTestSimplexNoise simplexnoise.Noise3 average=0.0", math.Abs(sum/iter) < 1e-4) }
// For a chunk at a coordinate, create it. // There is no need for a lock, as no one can access the chunk // TODO: Some algorithms depend on looking at the block above, which can only be done when inside the // chunk. If the test would require looking at another chunk, the tets skipped. This leads to some special // effects failure. func dBCreateChunk(c chunkdb.CC) *chunk { start := time.Now() ch := new(chunk) ch.rc = new(raw_chunk) ch.coord = c z1 := int(c.Z * CHUNK_SIZE) for x := int32(0); x < CHUNK_SIZE; x++ { xf := float64(x + c.X*CHUNK_SIZE) for y := int32(0); y < CHUNK_SIZE; y++ { yf := float64(y + c.Y*CHUNK_SIZE) highFreq := 20 * simplexnoise.Noise2(xf*0.016, yf*0.016) // This will generate high frequency terrain f := simplexnoise.Noise2(xf*0.0025, yf*0.0025) // Factor to modulate the high frequency amplitude lowFreq := 15 * simplexnoise.Noise2(xf*0.0013, yf*0.0013) // Low frequency terrain stoneheight := math.Floor(2.5 + highFreq*f*f + lowFreq) // Given 'height', fill in the content of the current chunk. Iterate from high 'z' to low, // to enable tests that depends on the block above. for z := CHUNK_SIZE - 1; z >= 0; z-- { if *inhibitCreateChunks { ch.rc[x][y][z] = BT_Air continue } zf := float64(z + z1) if zf > FLOATING_ISLANDS_LIM { // Use a gradial transient, or all islands would have a hard cut off. f := (1-FLOATING_ISLANDS_PROB)/CHUNK_SIZE*(zf-FLOATING_ISLANDS_LIM) + FLOATING_ISLANDS_PROB if f > 1 { f = 1 } density := f * dBdensity(xf/2, yf/2, zf) // Use a compressed layout in height if density > FLOATING_ISLANDS_PROB { if z != CHUNK_SIZE-1 && blockIsInvisible[ch.rc[x][y][z+1]] { ch.rc[x][y][z] = BT_Soil // Put grass on top } else { ch.rc[x][y][z] = BT_Stone } } else { ch.rc[x][y][z] = BT_Air } continue } density := dBdensity(xf/2, yf/2, zf) soildepth := math.Floor(2*simplexnoise.Noise2(xf*0.012, yf*0.012) + 2.8) if stoneheight > WORLD_SOIL_LEVEL { soildepth = 0 } else if soildepth+stoneheight > WORLD_SOIL_LEVEL { soildepth = WORLD_SOIL_LEVEL - stoneheight } height := stoneheight + soildepth ch.rc[x][y][z] = BT_Air if zf <= stoneheight { // Initialize with stone, may be updated below if zf > 24 { ch.rc[x][y][z] = BT_Snow } else { ch.rc[x][y][z] = BT_Stone } } else if zf <= stoneheight+soildepth { // Initialize with soil, may be updated below ch.rc[x][y][z] = BT_Soil } // Excavate some holes in the terrain. Don't let the hole go too deep const HOLEDEPTH = 50 // Max depth of hole if zf > -HOLEDEPTH && zf < HOLEDEPTH { density := dBdensity(xf, yf, zf) // This costs a lot of CPU fadeoff := 1.0 if zf >= -HOLEDEPTH && zf <= 0 { fadeoff = (HOLEDEPTH + zf) / HOLEDEPTH } else if zf < -HOLEDEPTH { fadeoff = 0 } if density*fadeoff > 0.7 { ch.rc[x][y][z] = BT_Air } } // Some special cases if below water line if zf <= 0 { if ch.rc[x][y][z] == BT_Air { ch.rc[x][y][z] = BT_Water } else if ch.rc[x][y][z] == BT_Soil { ch.rc[x][y][z] = BT_Stone } if ch.rc[x][y][z] == BT_Stone && z+z1 == 0 && blockIsInvisible[ch.rc[x][y][1]] { // Replace stone with sand if it is at water level and air above. ch.rc[x][y][0] = BT_Sand } } a := density > 0.5-CnfgCaveWidth/2 && density < 0.5+CnfgCaveWidth/2 if zf <= height && a { density2 := dBdensity(1000-xf/2, 1000-yf/2, 1000-zf) // Use a compressed layout in height b := density2 > 0.5-CnfgCaveWidth/2 && density2 < 0.5+CnfgCaveWidth/2 if b && ch.rc[x][y][z] != BT_Water { ch.rc[x][y][z] = BT_Air } } // Add some scenery if ch.rc[x][y][z] == BT_Soil && z+1 < CHUNK_SIZE && blockIsInvisible[ch.rc[x][y][z+1]] { // This is a candidate for a tree override const ( t3 = 0.0005 // Very few big trees t2 = 0.005 t1 = 0.010 tflower = 0.012 // Less flowers than tuft of grass ttuft = 0.020 ) rnd := math.Abs(simplexnoise.Noise2(xf*422.34, yf*234.123)) // Without scaling, there is a line where xf+yf==0 gives rnd=0 if rnd > t1 { continue // Not needed for the algorithm but will save a call to Noise2. } // Use a low frequency function to make less trees for some areas. lowFreq := 1 - math.Abs(simplexnoise.Noise2(xf*0.002, yf*0.002)) // The lowFreq function takes away too many trees, ease it up a little lowFreq = 1 - lowFreq*lowFreq // fmt.Printf("%.5f ", lowFreq) switch { case rnd < t3*lowFreq: ch.rc[x][y][z+1] = BT_Tree3 case rnd < t2*lowFreq: ch.rc[x][y][z+1] = BT_Tree2 case rnd < t1*lowFreq: ch.rc[x][y][z+1] = BT_Tree1 case rnd < tflower*lowFreq: ch.rc[x][y][z+1] = BT_Flowers case rnd < ttuft*lowFreq: ch.rc[x][y][z+1] = BT_Tuft } } } } } ch.compressAndChecksum() ch.touched = true delta := time.Now().Sub(start) DBCreateStats.Num++ DBCreateStats.TotTime += delta return ch }