diff --git a/renderer2/generation.go b/renderer2/generation.go new file mode 100644 index 0000000..168bb11 --- /dev/null +++ b/renderer2/generation.go @@ -0,0 +1,56 @@ +package main + +import ( + "image" + "image/color" + "renderer1/hrend" +) + +func Checkerboard(cols []color.Color, size int) image.Image { + result := image.NewRGBA(image.Rect(0, 0, size, size)) + for y := range size { + for x := range size { + result.Set(x, y, cols[(x+y)%len(cols)]) + } + } + return result +} + +func FlatTerrain(size int) *hrend.ObjModel { + result := hrend.ObjModel{ + Vertices: make([]hrend.Vec3f, 0), + VTexture: make([]hrend.Vec3f, 4), + Faces: make([]hrend.Facef, 0), + } + // For the simple square terrain, there aren't a lot of texture coords... + result.VTexture[0] = hrend.Vec3f{X: 0, Y: 0, Z: 0} + result.VTexture[1] = hrend.Vec3f{X: 1, Y: 0, Z: 0} + result.VTexture[2] = hrend.Vec3f{X: 0, Y: 1, Z: 0} + result.VTexture[3] = hrend.Vec3f{X: 1, Y: 1, Z: 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)}) + } + } + // Faces are slightly different; we generate two for every "cell" inside the vertices + for z := 0; z < size*2; z++ { + for x := 0; x < size*2; x++ { + topleft := x + z*size*2 + topright := x + 1 + z*size*2 + bottomleft := x + (z+1)*size*2 + bottomright := x + 1 + (z+1)*size*2 + // remember to wind counter-clockwise + result.Faces = append(result.Faces, hrend.Facef{ + hrend.Vertex{Pos: result.Vertices[topleft], Tex: result.VTexture[0]}, + hrend.Vertex{Pos: result.Vertices[bottomleft], Tex: result.VTexture[2]}, + hrend.Vertex{Pos: result.Vertices[topright], Tex: result.VTexture[1]}, + }, hrend.Facef{ + hrend.Vertex{Pos: result.Vertices[topright], Tex: result.VTexture[1]}, + hrend.Vertex{Pos: result.Vertices[bottomleft], Tex: result.VTexture[2]}, + hrend.Vertex{Pos: result.Vertices[bottomright], Tex: result.VTexture[3]}, + }) + } + } + return &result +} diff --git a/renderer2/hrend/image.go b/renderer2/hrend/image.go index 686c742..33617ca 100644 --- a/renderer2/hrend/image.go +++ b/renderer2/hrend/image.go @@ -111,21 +111,21 @@ func NewRenderbuffer(d Framebuffer, width uint, height uint) RenderBuffer { } } -func NewTexture(texture image.Image, skip int) *SimpleFramebuffer { +func NewTexture(texture image.Image, stride int) *SimpleFramebuffer { bounds := texture.Bounds() - width := bounds.Dx() / skip - height := bounds.Dy() / skip + width := bounds.Dx() / stride + height := bounds.Dy() / stride result := NewSimpleFramebuffer(uint(width), uint(height)) wlog := math.Log2(float64(width)) hlog := math.Log2(float64(height)) if wlog != math.Floor(wlog) || hlog != math.Floor(hlog) { panic("Texture must be power of two") } - for y := bounds.Min.Y; y < bounds.Max.Y; y += skip { - for x := bounds.Min.X; x < bounds.Max.X; x += skip { + for y := bounds.Min.Y; y < bounds.Max.Y; y += stride { + for x := bounds.Min.X; x < bounds.Max.X; x += stride { col := texture.At(x, y) r, g, b, _ := col.RGBA() - result.Set(uint(x/skip), uint(y/skip), byte(r>>8), byte(g>>8), byte(b>>8)) + result.Set(uint(x/stride), uint(y/stride), byte(r>>8), byte(g>>8), byte(b>>8)) } } return result diff --git a/renderer2/hrend/imagebuffer.go b/renderer2/hrend/imagebuffer.go deleted file mode 100644 index 3044455..0000000 --- a/renderer2/hrend/imagebuffer.go +++ /dev/null @@ -1,49 +0,0 @@ -/*package hrend - -import ( - "image" -) - -// Color is in RGB (alpha not used right now) -type ImageFramebuffer struct { - Data image.Image - Width uint - Height uint -} - -// Sure hope this gets inlined... -func (fb *ImageFramebuffer) Set(x uint, y uint, r byte, g byte, b byte) { - if x >= fb.Width || y >= fb.Height { - return - } - image.New - fb.Data. - fb.Data[(x+y*fb.Width)*3] = r - fb.Data[(x+y*fb.Width)*3+1] = g - fb.Data[(x+y*fb.Width)*3+2] = b -} - -func (fb *SimpleFramebuffer) Get(x uint, y uint) (byte, byte, byte) { - if x >= fb.Width || y >= fb.Height { - return 0, 0, 0 - } - return fb.Data[(x+y*fb.Width)*3], - fb.Data[(x+y*fb.Width)*3+1], - fb.Data[(x+y*fb.Width)*3+2] -} - -func (fb *SimpleFramebuffer) GetUv(u float32, v float32) (byte, byte, byte) { - x := uint(float32(fb.Width)*u) & (fb.Width - 1) - y := uint(float32(fb.Height)*(1-v)) & (fb.Height - 1) - return fb.Data[(x+y*fb.Width)*3], - fb.Data[(x+y*fb.Width)*3+1], - fb.Data[(x+y*fb.Width)*3+2] -} - -func NewSimpleFramebuffer(width uint, height uint) *SimpleFramebuffer { - return &SimpleFramebuffer{ - Data: make([]byte, width*height*3), - Width: width, - Height: height, - } -}*/ diff --git a/renderer2/hrend/obj.go b/renderer2/hrend/obj.go index 6d48355..06220f2 100644 --- a/renderer2/hrend/obj.go +++ b/renderer2/hrend/obj.go @@ -33,6 +33,7 @@ type ObjModel struct { func ParseObj(reader io.Reader) (*ObjModel, error) { result := ObjModel{ Vertices: make([]Vec3f, 0), + VTexture: make([]Vec3f, 0), Faces: make([]Facef, 0), } breader := bufio.NewReader(reader) diff --git a/renderer2/main.go b/renderer2/main.go index 574f162..c2170fb 100644 --- a/renderer2/main.go +++ b/renderer2/main.go @@ -3,7 +3,8 @@ package main import ( "flag" "fmt" - "image" + //"image" + "image/color" "log" "math" "os" @@ -17,10 +18,11 @@ import ( ) const ( - NearClip = 0.1 + NearClip = 0.01 FarClip = 100 FOV = 90.0 ZOffset = 1.5 + YOffset = 0.5 Movement = 1.0 Rotation = 0.25 LookLock = math.Pi / 32 @@ -58,22 +60,15 @@ func must(err error) { var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") var width = flag.Int("width", 640, "width of window or frame") var height = flag.Int("width", 480, "height of window or frame") - -// var renderconfig = flag.String("renderconfig", "", "if set, rendering is written out") var renderout = flag.String("renderout", "", "If set, rendering is done to a file instead of realtime") var renderinput = flag.String("renderinput", "", "If not realtime, the inputs are taken from here.") +// var renderconfig = flag.String("renderconfig", "", "if set, rendering is written out") + func IsRealtime() bool { return *renderout == "" } -// var dozbuf = flag.Bool("zbuffer", false, "Write zbuffer instead of image") -// var p6file = flag.String("p6file", "", "Output binary ppm to given file instead") -// var fov = flag.Float64("fov", 90, "Horizontal FOV in degrees") -// var xofs = flag.Float64("xofs", 0, "Offset image by x") -// var zofs = flag.Float64("zofs", -1.5, "Offset image by z (should be negative)") -// var repeat = flag.Int("repeat", 60, "Amount of times to repeat render") - // Do next inputs, whether they come from raylib or a file func CameraInput(yaw, pitch float32) (float32, float32, hrend.Vec3f) { @@ -129,7 +124,9 @@ func main() { Width := uint(*width) Height := uint(*height) + var timer hrend.FrameTimer var fb hrend.Framebuffer + var drawFunc func() if IsRealtime() { rl.InitWindow(int32(Width), int32(Height), "Simple renderer with raylib") @@ -141,32 +138,38 @@ func main() { defer rl.UnloadImageColors(rfb.Data) defer rl.UnloadImage(rfb.Image) fb = rfb + drawFunc = func() { + rl.UpdateTexture(rfb.Texture, rfb.Data) + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) + rl.DrawTexture(rfb.Texture, 0, 0, rl.White) + rl.DrawText(fmt.Sprintf("Frame: %.2fms", timer.LastAverage.Seconds()*1000), 5, 5, 20, rl.Red) + rl.EndDrawing() + } + } else { } rb := hrend.NewRenderbuffer(fb, Width, Height) - //o, texture := loadDefault() - var thing hrend.Vec2i - log.Print(thing) - - var projection, viewport hrend.Mat44f + // Generate world + wtexraw := Checkerboard([]color.Color{color.RGBA{R: 255, G: 0, B: 0, A: 255}, color.RGBA{R: 0, G: 0, B: 255, A: 255}}, 32) + wtex := hrend.NewTexture(wtexraw, 1) + world := FlatTerrain(10) // These don't really change + var projection, viewport hrend.Mat44f projection.SetProjection(float32(FOV), float32(Width)/float32(Height), NearClip, FarClip) viewport.SetViewportSimple(int(Width), int(Height), 1) //65535) - var timer hrend.FrameTimer - - camtrans := hrend.Vec3f{X: 0, Y: 0, Z: ZOffset} + var camera hrend.Mat44f var newcamtrans hrend.Vec3f + camtrans := hrend.Vec3f{X: 0, Y: YOffset, Z: ZOffset} camup := hrend.Vec3f{X: 0, Y: 1, Z: 0} // In our system, 0 degree yaw is facing -Z, into the scene yaw := float32(0) pitch := float32(math.Pi / 2) // Start looking flat - var camera hrend.Mat44f - log.Printf("Starting render loop") for !rl.WindowShouldClose() { @@ -174,7 +177,7 @@ func main() { yaw, pitch, newcamtrans = CameraInput(yaw, pitch) camtrans = *camtrans.Add(&newcamtrans) - lookvec := camera.SetCamera(&camtrans, yaw, pitch, &camup) + _ = camera.SetCamera(&camtrans, yaw, pitch, &camup) screenmat := camera.Inverse().Multiply(&projection) screenmat = screenmat.Multiply(&viewport) @@ -186,37 +189,24 @@ func main() { } var sc [3]hrend.Vertex - for _, f := range o.Faces { - // Precompute perspective for vertices to save time. Notice Z - // is not considered: is this orthographic projection? Yeah probably... - for i := range 3 { // Triangles, bro + for _, f := range world.Faces { + for i := range 3 { sc[i] = f[i] sc[i].Pos = screenmat.MultiplyPoint3(f[i].Pos) } - l1 := f[2].Pos.Sub(&f[0].Pos) - n := l1.CrossProduct(f[1].Pos.Sub(&f[0].Pos)) - n = n.Normalize() - intensity := n.MultSimp(&lookvec) - if intensity < 0 { - intensity = 0 - } - hrend.TriangleTextured(&rb, texture, intensity, sc[0], sc[1], sc[2]) + // l1 := f[2].Pos.Sub(&f[0].Pos) + // n := l1.CrossProduct(f[1].Pos.Sub(&f[0].Pos)) + // n = n.Normalize() + // intensity := n.MultSimp(&lookvec) + // if intensity < 0 { + // intensity = 0 + // } + hrend.TriangleTextured(&rb, wtex, 1.0, sc[0], sc[1], sc[2]) //hrend.TriangleFlat(&rb, hrend.Col2Uint(byte(255*intensity), byte(255*intensity), byte(255*intensity)), sc[0].Pos, sc[1].Pos, sc[2].Pos) } - rl.UpdateTexture(fb.Texture, fb.Data) - timer.Add(time.Since(start), 10) - - if IsRealtime() { - rl.BeginDrawing() - rl.ClearBackground(rl.RayWhite) - rl.DrawTexture(fb.Texture, 0, 0, rl.White) - rl.DrawText(fmt.Sprintf("Frame: %.2fms", timer.LastAverage.Seconds()*1000), 5, 5, 20, rl.Red) - rl.EndDrawing() - } else { - - } + drawFunc() } } diff --git a/renderer2/texturegen.go b/renderer2/texturegen.go deleted file mode 100644 index 156f9a3..0000000 --- a/renderer2/texturegen.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "image" - "image/color" - //"renderer1/hrend" -) - -func Checkerboard(cols []color.Color, size int) image.Image { - result := image.NewRGBA(image.Rect(0, 0, size, size)) - for y := range size { - for x := range size { - result.Set(x, y, cols[(x+y)%len(cols)]) - } - } - return result -}