cool terrain
This commit is contained in:
parent
2915396d5e
commit
04a4ce42e8
@ -3,6 +3,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
"renderer3/hrend"
|
"renderer3/hrend"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -61,7 +64,7 @@ func Skybox() *hrend.ObjModel {
|
|||||||
vt := make([]hrend.Vec3f, 2)
|
vt := make([]hrend.Vec3f, 2)
|
||||||
f := make([]hrend.Facef, 12)
|
f := make([]hrend.Facef, 12)
|
||||||
// Assuming 1px gradient, these are the only two texture points you need
|
// Assuming 1px gradient, these are the only two texture points you need
|
||||||
vt[0] = hrend.Vec3f{X: 0, Y: 0, Z: 0}
|
vt[0] = hrend.Vec3f{X: 0, Y: 0.001, Z: 0}
|
||||||
vt[1] = hrend.Vec3f{X: 0, Y: 1, Z: 0}
|
vt[1] = hrend.Vec3f{X: 0, Y: 1, Z: 0}
|
||||||
vvt := []hrend.Vec3f{
|
vvt := []hrend.Vec3f{
|
||||||
vt[0], vt[0], vt[0], vt[0], vt[1], vt[1], vt[1], vt[1],
|
vt[0], vt[0], vt[0], vt[0], vt[1], vt[1], vt[1], vt[1],
|
||||||
@ -80,7 +83,7 @@ func Skybox() *hrend.ObjModel {
|
|||||||
// These are our 12 faces
|
// These are our 12 faces
|
||||||
fv := [][3]int{
|
fv := [][3]int{
|
||||||
{0, 2, 1}, // bottom
|
{0, 2, 1}, // bottom
|
||||||
{1, 2, 3},
|
{0, 3, 2},
|
||||||
{4, 5, 6}, // top
|
{4, 5, 6}, // top
|
||||||
{6, 7, 4},
|
{6, 7, 4},
|
||||||
{0, 1, 5}, // south
|
{0, 1, 5}, // south
|
||||||
@ -97,28 +100,6 @@ func Skybox() *hrend.ObjModel {
|
|||||||
f[i][j] = hrend.Vertex{Pos: v[face[j]], Tex: vvt[face[j]]}
|
f[i][j] = hrend.Vertex{Pos: v[face[j]], Tex: vvt[face[j]]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Now the bottom 2 faces
|
|
||||||
// f[0] = hrend.Facef{
|
|
||||||
// hrend.Vertex{Pos: v[0], Tex: vt[0]},
|
|
||||||
// hrend.Vertex{Pos: v[1], Tex: vt[0]},
|
|
||||||
// hrend.Vertex{Pos: v[2], Tex: vt[0]},
|
|
||||||
// }
|
|
||||||
// f[1] = hrend.Facef{
|
|
||||||
// hrend.Vertex{Pos: v[2], Tex: vt[0]},
|
|
||||||
// hrend.Vertex{Pos: v[3], Tex: vt[0]},
|
|
||||||
// hrend.Vertex{Pos: v[0], Tex: vt[0]},
|
|
||||||
// }
|
|
||||||
// // Top 2 faces
|
|
||||||
// f[3] = hrend.Facef{
|
|
||||||
// hrend.Vertex{Pos: v[4], Tex: vt[1]},
|
|
||||||
// hrend.Vertex{Pos: v[5], Tex: vt[1]},
|
|
||||||
// hrend.Vertex{Pos: v[6], Tex: vt[1]},
|
|
||||||
// }
|
|
||||||
// f[4] = hrend.Facef{
|
|
||||||
// hrend.Vertex{Pos: v[6], Tex: vt[1]},
|
|
||||||
// hrend.Vertex{Pos: v[7], Tex: vt[1]},
|
|
||||||
// hrend.Vertex{Pos: v[4], Tex: vt[1]},
|
|
||||||
// }
|
|
||||||
// Ugh and now the sides... so complicated
|
// Ugh and now the sides... so complicated
|
||||||
return &hrend.ObjModel{
|
return &hrend.ObjModel{
|
||||||
Vertices: v,
|
Vertices: v,
|
||||||
@ -127,23 +108,16 @@ func Skybox() *hrend.ObjModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func FlatTerrain(size int) *hrend.ObjModel {
|
// Reset all faces and regenerate them using the vertices as a square mesh
|
||||||
result := hrend.ObjModel{
|
func RegenerateSquareMesh(size int, obj *hrend.ObjModel) {
|
||||||
Vertices: make([]hrend.Vec3f, 0),
|
obj.VTexture = make([]hrend.Vec3f, 4)
|
||||||
VTexture: make([]hrend.Vec3f, 4),
|
|
||||||
Faces: make([]hrend.Facef, 0),
|
|
||||||
}
|
|
||||||
// For the simple square terrain, there aren't a lot of texture coords...
|
// For the simple square terrain, there aren't a lot of texture coords...
|
||||||
result.VTexture[0] = hrend.Vec3f{X: 0, Y: 0, Z: 0}
|
// If you want something more complicated, replace this
|
||||||
result.VTexture[1] = hrend.Vec3f{X: 1, Y: 0, Z: 0}
|
obj.VTexture[0] = hrend.Vec3f{X: 0, Y: 0, Z: 0}
|
||||||
result.VTexture[2] = hrend.Vec3f{X: 0, Y: 1, Z: 0}
|
obj.VTexture[1] = hrend.Vec3f{X: 1, Y: 0, Z: 0}
|
||||||
result.VTexture[3] = hrend.Vec3f{X: 1, Y: 1, Z: 0}
|
obj.VTexture[2] = hrend.Vec3f{X: 0, Y: 1, Z: 0}
|
||||||
// Generate all the simple vertices along the plane at y=0
|
obj.VTexture[3] = hrend.Vec3f{X: 1, Y: 1, Z: 0}
|
||||||
for z := -size; z <= size; z++ {
|
obj.Faces = nil // Clear old faces
|
||||||
for x := -size; x <= size; x++ {
|
|
||||||
result.Vertices = append(result.Vertices, hrend.Vec3f{X: float32(x), Y: 0, Z: float32(z)})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
width := size + size + 1
|
width := size + size + 1
|
||||||
// Faces are slightly different; we generate two for every "cell" inside the vertices
|
// Faces are slightly different; we generate two for every "cell" inside the vertices
|
||||||
for z := 0; z < width-1; z++ {
|
for z := 0; z < width-1; z++ {
|
||||||
@ -153,16 +127,156 @@ func FlatTerrain(size int) *hrend.ObjModel {
|
|||||||
bottomleft := x + (z+1)*width
|
bottomleft := x + (z+1)*width
|
||||||
bottomright := x + 1 + (z+1)*width
|
bottomright := x + 1 + (z+1)*width
|
||||||
// remember to wind counter-clockwise
|
// remember to wind counter-clockwise
|
||||||
result.Faces = append(result.Faces, hrend.Facef{
|
obj.Faces = append(obj.Faces, hrend.Facef{
|
||||||
hrend.Vertex{Pos: result.Vertices[topleft], Tex: result.VTexture[0]},
|
hrend.Vertex{Pos: obj.Vertices[topleft], Tex: obj.VTexture[0]},
|
||||||
hrend.Vertex{Pos: result.Vertices[bottomleft], Tex: result.VTexture[2]},
|
hrend.Vertex{Pos: obj.Vertices[bottomleft], Tex: obj.VTexture[2]},
|
||||||
hrend.Vertex{Pos: result.Vertices[topright], Tex: result.VTexture[1]},
|
hrend.Vertex{Pos: obj.Vertices[topright], Tex: obj.VTexture[1]},
|
||||||
}, hrend.Facef{
|
}, hrend.Facef{
|
||||||
hrend.Vertex{Pos: result.Vertices[topright], Tex: result.VTexture[1]},
|
hrend.Vertex{Pos: obj.Vertices[topright], Tex: obj.VTexture[1]},
|
||||||
hrend.Vertex{Pos: result.Vertices[bottomleft], Tex: result.VTexture[2]},
|
hrend.Vertex{Pos: obj.Vertices[bottomleft], Tex: obj.VTexture[2]},
|
||||||
hrend.Vertex{Pos: result.Vertices[bottomright], Tex: result.VTexture[3]},
|
hrend.Vertex{Pos: obj.Vertices[bottomright], Tex: obj.VTexture[3]},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FlatTerrain(size int) *hrend.ObjModel {
|
||||||
|
result := hrend.ObjModel{
|
||||||
|
Vertices: make([]hrend.Vec3f, 0),
|
||||||
|
VTexture: make([]hrend.Vec3f, 4),
|
||||||
|
Faces: make([]hrend.Facef, 0),
|
||||||
|
}
|
||||||
|
// Generate all the simple vertices along the plane at y=0
|
||||||
|
for z := -size; z <= size; z++ {
|
||||||
|
for x := -size; x <= size; x++ {
|
||||||
|
result.Vertices = append(result.Vertices, hrend.Vec3f{X: float32(x), Y: 0, Z: float32(z)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RegenerateSquareMesh(size, &result)
|
||||||
return &result
|
return &result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DiamondSquareTerrain(size int, roughness float32, scale float32) *hrend.ObjModel {
|
||||||
|
result := hrend.ObjModel{
|
||||||
|
Vertices: make([]hrend.Vec3f, 0),
|
||||||
|
VTexture: make([]hrend.Vec3f, 4),
|
||||||
|
Faces: make([]hrend.Facef, 0),
|
||||||
|
}
|
||||||
|
dsterra := DiamondSquare(size+size+1, float64(roughness))
|
||||||
|
// Generate all the simple vertices along the plane at y=0
|
||||||
|
for z := -size; z <= size; z++ {
|
||||||
|
for x := -size; x <= size; x++ {
|
||||||
|
//result.Vertices = append(result.Vertices, hrend.Vec3f{X: float32(x), Y: float32(float64(scale) * dsterra[0][0]), Z: float32(z)})
|
||||||
|
result.Vertices = append(result.Vertices, hrend.Vec3f{X: float32(x), Y: float32(float64(scale) * dsterra[z+size][x+size]), Z: float32(z)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RegenerateSquareMesh(size, &result)
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHATGPT -----------------------------------------
|
||||||
|
|
||||||
|
func DiamondSquare(size int, roughness float64) [][]float64 {
|
||||||
|
// Initialize the array
|
||||||
|
terrain := make([][]float64, size)
|
||||||
|
for i := range terrain {
|
||||||
|
terrain[i] = make([]float64, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seed the corners
|
||||||
|
terrain[0][0] = rand.Float64()
|
||||||
|
terrain[0][size-1] = rand.Float64()
|
||||||
|
terrain[size-1][0] = rand.Float64()
|
||||||
|
terrain[size-1][size-1] = rand.Float64()
|
||||||
|
log.Print("DS Seeded corners")
|
||||||
|
|
||||||
|
// Size of the step
|
||||||
|
stepSize := size - 1
|
||||||
|
for stepSize > 1 {
|
||||||
|
halfStep := stepSize / 2
|
||||||
|
|
||||||
|
// Diamond step
|
||||||
|
for y := halfStep; y < size; y += stepSize {
|
||||||
|
for x := halfStep; x < size; x += stepSize {
|
||||||
|
diamondStep(terrain, x, y, halfStep, roughness)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Square step
|
||||||
|
for y := 0; y < size; y += halfStep {
|
||||||
|
for x := (y + halfStep) % stepSize; x < size; x += stepSize {
|
||||||
|
squareStep(terrain, x, y, halfStep, roughness)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stepSize = halfStep
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("DS finished squares and diamonds")
|
||||||
|
|
||||||
|
// Normalize to [0, 1]
|
||||||
|
normalize(terrain)
|
||||||
|
|
||||||
|
log.Printf("DS normalize (complete)")
|
||||||
|
|
||||||
|
return terrain
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diamond step of the algorithm
|
||||||
|
func diamondStep(terrain [][]float64, x, y, halfStep int, roughness float64) {
|
||||||
|
sum := terrain[y-halfStep][x-halfStep] +
|
||||||
|
terrain[y-halfStep][x+halfStep] +
|
||||||
|
terrain[y+halfStep][x-halfStep] +
|
||||||
|
terrain[y+halfStep][x+halfStep]
|
||||||
|
|
||||||
|
avg := sum / 4
|
||||||
|
terrain[y][x] = avg + (rand.Float64()*2-1)*roughness
|
||||||
|
}
|
||||||
|
|
||||||
|
// Square step of the algorithm
|
||||||
|
func squareStep(terrain [][]float64, x, y, halfStep int, roughness float64) {
|
||||||
|
avg := 0.0
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
if x-halfStep >= 0 {
|
||||||
|
avg += terrain[y][x-halfStep]
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if x+halfStep < len(terrain) {
|
||||||
|
avg += terrain[y][x+halfStep]
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if y-halfStep >= 0 {
|
||||||
|
avg += terrain[y-halfStep][x]
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if y+halfStep < len(terrain) {
|
||||||
|
avg += terrain[y+halfStep][x]
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
avg /= float64(count)
|
||||||
|
terrain[y][x] = avg + (rand.Float64()*2-1)*roughness
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize the array to range [0, 1]
|
||||||
|
func normalize(terrain [][]float64) {
|
||||||
|
minVal, maxVal := math.Inf(1), math.Inf(-1)
|
||||||
|
for _, row := range terrain {
|
||||||
|
for _, value := range row {
|
||||||
|
if value < minVal {
|
||||||
|
minVal = value
|
||||||
|
}
|
||||||
|
if value > maxVal {
|
||||||
|
maxVal = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rangeVal := maxVal - minVal
|
||||||
|
for i, row := range terrain {
|
||||||
|
for j := range row {
|
||||||
|
terrain[i][j] = (terrain[i][j] - minVal) / rangeVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -254,6 +254,9 @@ func PerspectiveAndClip(face Facef, matrix3d *Mat44f) []Facef {
|
|||||||
// The two points that aren't a need to be the interpolated values
|
// The two points that aren't a need to be the interpolated values
|
||||||
sc[bi].Pos = LerpVec3f(sc[ai].Pos, sc[bi].Pos, tab)
|
sc[bi].Pos = LerpVec3f(sc[ai].Pos, sc[bi].Pos, tab)
|
||||||
sc[ci].Pos = LerpVec3f(sc[ai].Pos, sc[ci].Pos, tac)
|
sc[ci].Pos = LerpVec3f(sc[ai].Pos, sc[ci].Pos, tac)
|
||||||
|
sc[bi].Tex = LerpVec3f(sc[ai].Tex, sc[bi].Tex, tab)
|
||||||
|
sc[ci].Tex = LerpVec3f(sc[ai].Tex, sc[ci].Tex, tac)
|
||||||
|
|
||||||
w[bi] = LerpF32(w[ai], w[bi], tab)
|
w[bi] = LerpF32(w[ai], w[bi], tab)
|
||||||
w[ci] = LerpF32(w[ai], w[ci], tac)
|
w[ci] = LerpF32(w[ai], w[ci], tac)
|
||||||
|
|
||||||
@ -274,6 +277,7 @@ func PerspectiveAndClip(face Facef, matrix3d *Mat44f) []Facef {
|
|||||||
// tab and tac are the distance to that point itself, so a still needs
|
// tab and tac are the distance to that point itself, so a still needs
|
||||||
// to be the first value here
|
// to be the first value here
|
||||||
sct[ai].Pos = LerpVec3f(sc[ai].Pos, sc[bi].Pos, tab)
|
sct[ai].Pos = LerpVec3f(sc[ai].Pos, sc[bi].Pos, tab)
|
||||||
|
sct[ai].Tex = LerpVec3f(sc[ai].Tex, sc[bi].Tex, tab)
|
||||||
w[ai] = LerpF32(w[ai], w[bi], tab)
|
w[ai] = LerpF32(w[ai], w[bi], tab)
|
||||||
outfaces = conditionalAddTriangle(sct, w, outfaces)
|
outfaces = conditionalAddTriangle(sct, w, outfaces)
|
||||||
|
|
||||||
@ -282,18 +286,14 @@ func PerspectiveAndClip(face Facef, matrix3d *Mat44f) []Facef {
|
|||||||
// triangle. But simply replacing it will make the triangle invisible,
|
// triangle. But simply replacing it will make the triangle invisible,
|
||||||
// since it inverts the winding order (I think)
|
// since it inverts the winding order (I think)
|
||||||
sct[bi].Pos = LerpVec3f(sc[ai].Pos, sc[ci].Pos, tac)
|
sct[bi].Pos = LerpVec3f(sc[ai].Pos, sc[ci].Pos, tac)
|
||||||
|
sct[bi].Tex = LerpVec3f(sc[ai].Tex, sc[ci].Tex, tac)
|
||||||
w[bi] = LerpF32(wa, w[ci], tac)
|
w[bi] = LerpF32(wa, w[ci], tac)
|
||||||
|
|
||||||
// Now swap the a and b
|
// Now swap the a and b
|
||||||
w[ai], w[bi] = w[bi], w[ai]
|
w[ai], w[bi] = w[bi], w[ai]
|
||||||
sct[ai], sct[bi] = sct[bi], sct[ai]
|
sct[ai], sct[bi] = sct[bi], sct[ai]
|
||||||
outfaces = conditionalAddTriangle(sct, w, outfaces)
|
outfaces = conditionalAddTriangle(sct, w, outfaces)
|
||||||
|
|
||||||
/*
|
|
||||||
sc2[ai].Pos = LerpVec3f(sc2[ai].Pos, sc2[ci].Pos, tac)
|
|
||||||
w[ai] = LerpF32(w[ai], w[ci], tac)
|
|
||||||
//outfaces = conditionalAddTriangle(sc2, w, outfaces)
|
|
||||||
*/
|
|
||||||
|
|
||||||
} else if len(outers) != 3 { // Output the face itself, no modification
|
} else if len(outers) != 3 { // Output the face itself, no modification
|
||||||
outfaces = conditionalAddTriangle(sc, w, outfaces)
|
outfaces = conditionalAddTriangle(sc, w, outfaces)
|
||||||
}
|
}
|
||||||
|
@ -170,10 +170,10 @@ func main() {
|
|||||||
// Generate world
|
// Generate world
|
||||||
wtexraw := Checkerboard([]color.Color{color.RGBA{R: 0, G: 255, B: 0, A: 255}, color.RGBA{R: 50, G: 150, B: 0, A: 255}}, 32)
|
wtexraw := Checkerboard([]color.Color{color.RGBA{R: 0, G: 255, B: 0, A: 255}, color.RGBA{R: 50, G: 150, B: 0, A: 255}}, 32)
|
||||||
wtex := hrend.NewTexture(wtexraw, 1)
|
wtex := hrend.NewTexture(wtexraw, 1)
|
||||||
world := FlatTerrain(10)
|
world := DiamondSquareTerrain(32, 1, 9) // must be power of two
|
||||||
|
|
||||||
// Generate skybox
|
// Generate skybox
|
||||||
skyraw := Gradient1px(color.RGBA{R: 100, G: 100, B: 255, A: 255}, color.RGBA{R: 255, G: 255, B: 255, A: 255}, 32)
|
skyraw := Gradient1px(color.RGBA{R: 100, G: 100, B: 255, A: 255}, color.RGBA{R: 0, G: 0, B: 25, A: 255}, 32)
|
||||||
skytex := hrend.NewTexture(skyraw, 1)
|
skytex := hrend.NewTexture(skyraw, 1)
|
||||||
sky := Skybox()
|
sky := Skybox()
|
||||||
|
|
||||||
@ -189,6 +189,7 @@ func main() {
|
|||||||
objects := make([]*hrend.ObjectDef, 0)
|
objects := make([]*hrend.ObjectDef, 0)
|
||||||
objects = append(objects, hrend.NewObjectDef(world, wtex))
|
objects = append(objects, hrend.NewObjectDef(world, wtex))
|
||||||
worldobj := objects[len(objects)-1]
|
worldobj := objects[len(objects)-1]
|
||||||
|
worldobj.Pos.Y -= 3
|
||||||
worldobj.Color = hrend.Vec3f{0.0, 1.0, 0.0}
|
worldobj.Color = hrend.Vec3f{0.0, 1.0, 0.0}
|
||||||
objects = append(objects, hrend.NewObjectDef(sky, skytex)) // the actual skybox
|
objects = append(objects, hrend.NewObjectDef(sky, skytex)) // the actual skybox
|
||||||
skyobj := objects[len(objects)-1]
|
skyobj := objects[len(objects)-1]
|
||||||
|
Loading…
Reference in New Issue
Block a user