package main import ( "flag" "fmt" "image" "log" "math" "os" "renderer1/hrend" "runtime/pprof" // For performance profiling (unnecessary) "time" _ "image/jpeg" rl "github.com/gen2brain/raylib-go/raylib" ) const ( Width = 640 Height = 480 NearClip = 0.1 FarClip = 100 FOV = 90.0 ZOffset = -1.5 Movement = 1.0 Fps = 60 ObjectFile = "../head.obj" TextureFile = "../head.jpg" ) func must(err error) { if err != nil { panic(err) } } func loadDefault() (*hrend.ObjModel, hrend.Framebuffer) { log.Printf("Loading obj %s, texture %s", ObjectFile, TextureFile) of, err := os.Open(ObjectFile) must(err) defer of.Close() o, err := hrend.ParseObj(of) must(err) jf, err := os.Open(TextureFile) must(err) defer jf.Close() timg, _, err := image.Decode(jf) must(err) texture := hrend.NewTexture(timg, 4) return o, texture } // However flag works... idk var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") // 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") func main() { log.Printf("Program start") flag.Parse() if *cpuprofile != "" { log.Printf("CPU profiling requested, write to %s", *cpuprofile) f, err := os.Create(*cpuprofile) must(err) defer f.Close() err = pprof.StartCPUProfile(f) must(err) defer pprof.StopCPUProfile() } rl.InitWindow(Width, Height, "Simple renderer with raylib") defer rl.CloseWindow() rl.SetTargetFPS(Fps) fb := NewRaylibBuffer(Width, Height) defer rl.UnloadTexture(fb.Texture) defer rl.UnloadImageColors(fb.Data) defer rl.UnloadImage(fb.Image) rb := hrend.NewRenderbuffer(fb, Width, Height) o, texture := loadDefault() light := hrend.Vec3f{ X: 0, Y: 0, Z: -1, } var thing hrend.Vec2i log.Print(thing) var projection, worldToCamera, viewport hrend.Mat44f // These don't really change projection.SetProjection(float32(FOV), float32(Width)/float32(Height), NearClip, FarClip) viewport.SetViewportSimple(int(fb.Width), int(fb.Height), 1) //65535) log.Printf("Starting raylib loop") frameSum := 0.0 frameCount := 0 frameAverage := 0.0 xofs := float32(0.0) yofs := float32(0.0) zofs := float32(ZOffset) for !rl.WindowShouldClose() { start := time.Now() if rl.IsKeyDown(rl.KeyD) { xofs += Movement / Fps } if rl.IsKeyDown(rl.KeyA) { xofs -= Movement / Fps } if rl.IsKeyDown(rl.KeyW) { zofs += Movement / Fps } if rl.IsKeyDown(rl.KeyS) { zofs -= Movement / Fps } if rl.IsKeyDown(rl.KeySpace) { yofs -= Movement / Fps } if rl.IsKeyDown(rl.KeyLeftShift) { yofs += Movement / Fps } // This might (thought not currently) worldToCamera.SetTranslation(xofs, yofs, zofs) screenmat := worldToCamera.Multiply(&projection) screenmat = screenmat.Multiply(&viewport) for i := 0; i < Width*Height; i++ { fb.Data[i].R = 0 fb.Data[i].G = 0 fb.Data[i].B = 0 } //rl.ImageDrawRectangle(fb.Image, 0, 0, Width, Height, rl.Black) //rl.ImageClearBackground(fb.Image, rl.Black) var sc [3]hrend.Vertex rb.ResetZBuffer() for _, f := range o.Faces { // Precompute perspective for vertices to save time. Notice Z // is not considered: is this orthographic projection? Yeah probably... var fpt [3]hrend.Vec3f maxz := float32(-math.MaxFloat32) for i := range 3 { // Triangles, bro sc[i] = f[i] sc[i].Pos = screenmat.MultiplyPoint3(f[i].Pos) fpt[i] = worldToCamera.MultiplyPoint3(f[i].Pos) maxz = max(maxz, sc[i].Pos.Z) } if maxz < 0 || maxz > 1 { //log.Printf("Clipping %f %f %f", sc[0].Pos.Z, sc[1].Pos.Z, sc[2].Pos.Z) continue } l1 := fpt[2].Sub(fpt[0]) n := l1.CrossProduct(fpt[1].Sub(fpt[0])) n = n.Normalize() intensity := n.MultSimp(&light) if intensity > 0 { hrend.TriangleTextured(&rb, texture, intensity, 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) frameSum += time.Since(start).Seconds() frameCount += 1 if frameCount&0x7 == 0 { frameAverage = frameSum / float64(frameCount) frameSum = 0 frameCount = 0 } rl.BeginDrawing() rl.ClearBackground(rl.RayWhite) rl.DrawTexture(fb.Texture, 0, 0, rl.White) rl.DrawText(fmt.Sprintf("Frame: %fms", frameAverage*1000), 5, 5, 20, rl.Red) rl.EndDrawing() } }