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 Rotation = 0.25 LookLock = math.Pi / 32 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) rl.DisableCursor() 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() var thing hrend.Vec2i log.Print(thing) var projection, 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 camtrans := hrend.Vec3f{X: 0, Y: 0, 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, moverot hrend.Mat44f for !rl.WindowShouldClose() { start := time.Now() mouse := rl.GetMouseDelta() pitch += Rotation * mouse.Y / Fps yaw += Rotation * mouse.X / Fps // Need a clamp function or something if pitch < LookLock { pitch = LookLock } else if pitch > math.Pi-LookLock { pitch = math.Pi - LookLock } newcamtrans := hrend.Vec3f{X: 0, Y: 0, Z: 0} if rl.IsKeyDown(rl.KeyD) { newcamtrans.X += Movement / Fps } if rl.IsKeyDown(rl.KeyA) { newcamtrans.X -= Movement / Fps } // Moving forward moves in the negative z direction, since we FACE // the -z axis (the camera does anyway) if rl.IsKeyDown(rl.KeyW) { newcamtrans.Z -= Movement / Fps } if rl.IsKeyDown(rl.KeyS) { newcamtrans.Z += Movement / Fps } if rl.IsKeyDown(rl.KeySpace) { newcamtrans.Y += Movement / Fps } if rl.IsKeyDown(rl.KeyLeftShift) { newcamtrans.Y -= Movement / Fps } // translate the new camera movement based on the yaw moverot.SetRotationY(-yaw) newcamtrans = moverot.MultiplyPoint3(newcamtrans) camtrans = *camtrans.Add(&newcamtrans) // The camera is really just a model lookvec := camera.SetCamera(&camtrans, yaw, pitch, &camup) screenmat := camera.Inverse().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 } 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... for i := range 3 { // Triangles, bro 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]) //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() } }