// gfxLoop is responsible for drawing things to the window. This loop must be // independent of the Chippy main loop. func gfxLoop(w *chippy.Window, r gfx.Renderer) { w.SetSize(640, 640) w.SetPositionCenter(chippy.DefaultScreen()) glr := r.(*gl2.Renderer) glr.UpdateBounds(image.Rect(0, 0, 640, 640)) // Create a camera. // Wait for the shader to load (not strictly required). onLoad := make(chan *gfx.Shader, 1) r.LoadShader(Wireframe, onLoad) go func() { <-onLoad Wireframe.RLock() if Wireframe.Loaded { fmt.Println("Shader loaded") } else { fmt.Println(string(Wireframe.Error)) } Wireframe.RUnlock() }() // Create a triangle object. triangle := gfx.NewObject() triangle.Shader = Wireframe triangle.Meshes = []*gfx.Mesh{ &gfx.Mesh{}, } triangle.Meshes[0].GenerateBary() triangle.FaceCulling = gfx.NoFaceCulling triangle.AlphaMode = gfx.AlphaToCoverage tree := ntree.New() level := 0 go func() { events := w.Events() for { e := <-events kev, ok := e.(keyboard.TypedEvent) if ok { if kev.Rune == ' ' || kev.Rune == 'b' { for i := 0; i < 1; i++ { s := random(0.1, 0.15) if kev.Rune == 'b' { s = random(0.1, 0.5) } tree.Add(s) } // Create new mesh and ask the renderer to load it. newMesh := NTreeMesh(tree, level) onLoad := make(chan *gfx.Mesh, 1) r.LoadMesh(newMesh, onLoad) <-onLoad // Swap the mesh. triangle.Lock() triangle.Meshes[0] = newMesh triangle.Unlock() } if kev.Rune == '1' || kev.Rune == '2' { if kev.Rune == '1' { level-- } else { level++ } fmt.Println("level", level) // Create new mesh and ask the renderer to load it. newMesh := NTreeMesh(tree, level) onLoad := make(chan *gfx.Mesh, 1) r.LoadMesh(newMesh, onLoad) <-onLoad // Swap the mesh. triangle.Lock() triangle.Meshes[0] = newMesh triangle.Unlock() } if kev.Rune == 's' || kev.Rune == 'S' { fmt.Println("Writing screenshot to file...") // Download the image from the graphics hardware and save // it to disk. complete := make(chan image.Image, 1) r.Download(image.Rect(0, 0, 0, 0), complete) img := <-complete // Wait for download to complete. // Save to png. f, err := os.Create("screenshot.png") if err != nil { log.Fatal(err) } err = png.Encode(f, img) if err != nil { log.Fatal(err) } fmt.Println("Wrote texture to screenshot.png") } } } }() for { // Clear the entire area (empty rectangle means "the whole area"). r.Clear(image.Rect(0, 0, 0, 0), gfx.Color{1, 1, 1, 1}) r.ClearDepth(image.Rect(0, 0, 0, 0), 1.0) // Update the rotation. dt := r.Clock().Dt() triangle.RLock() rot := triangle.Transform.Rot() if w.Keyboard.Down(keyboard.ArrowLeft) { rot.Z += 90 * dt } if w.Keyboard.Down(keyboard.ArrowRight) { rot.Z -= 90 * dt } if w.Keyboard.Down(keyboard.ArrowUp) { rot.X += 20 * dt } if w.Keyboard.Down(keyboard.ArrowDown) { rot.X -= 20 * dt } triangle.Transform.SetRot(rot) triangle.RUnlock() // Draw the triangle to the screen. r.Draw(image.Rect(0, 0, 0, 0), triangle, nil) // Render the whole frame. r.Render() } }
// gfxLoop is responsible for drawing things to the window. This loop must be // independent of the Chippy main loop. func gfxLoop(w *chippy.Window, r gfx.Renderer) { w.SetSize(640, 640) w.SetPositionCenter(chippy.DefaultScreen()) glr := r.(*gl2.Renderer) glr.UpdateBounds(image.Rect(0, 0, 640, 640)) // Create a perspective viewing frustum matrix. width, height := 640, 640 aspectRatio := float64(width) / float64(height) viewMat := gfx.ConvertMat4(math.Mat4Perspective(75.0, aspectRatio, 0.001, 1000.0)) // Create a camera. camera := &gfx.Camera{ Object: new(gfx.Object), Frustum: viewMat, } _ = camera // Create a wireframe shader. shader := &gfx.Shader{ Name: "wireframe shader", GLSLVert: wireVert, GLSLFrag: wireFrag, Inputs: make(map[string]interface{}), } // Wait for the shader to load (not strictly required). onLoad := make(chan *gfx.Shader, 1) r.LoadShader(shader, onLoad) <-onLoad if shader.Loaded { fmt.Println("Shader loaded") } else { fmt.Println(string(shader.Error)) } // Create a triangle object. triangle := &gfx.Object{ Shader: shader, State: gfx.DefaultState, Meshes: []*gfx.Mesh{ &gfx.Mesh{ Vertices: []gfx.Vec3{ // Top {0, .9, 0}, {-.9, -.9, 0}, {.9, -.9, 0}, }, Colors: []gfx.Color{ // Top {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, }, }, }, } triangle.State.FaceCulling = gfx.NoFaceCulling octree := gfx.NewOctree() update := make(chan *gfx.Object) go func() { events := w.Events() for { e := <-events kev, ok := e.(keyboard.TypedEvent) if ok { if kev.Rune == 'f' || kev.Rune == 'b' { for i := 0; i < 100; i++ { s := mySpatial{ aabb: randomAABB(0.1, 0.25), } if kev.Rune == 'b' { s = mySpatial{ aabb: randomAABB(0.1, 0.5), } } //s.aabb.Min.Z = -.1 //s.aabb.Max.Z = .1 //octree = gfx.NewOctree() octree.Add(s) //fmt.Println(s.AABB().Center(), s.AABB()) } // Create new mesh and ask the renderer to load it. newMesh := octreeMesh(octree) onLoad := make(chan *gfx.Mesh, 1) r.LoadMesh(newMesh, onLoad) <-onLoad // Take ownership of the triangle. <-update // Swap the mesh. triangle.Meshes[0] = newMesh // Give back ownership. update <- triangle } if kev.Rune == 'q' { // Take ownership of the triangle. <-update // Update rotation. v, ok := triangle.Shader.Inputs["rx"] rx := float32(0.0) if ok { rx = v.(float32) } rx += 0.1 triangle.Shader.Inputs["rx"] = rx // Give back ownership. update <- triangle } if kev.Rune == 's' || kev.Rune == 'S' { fmt.Println("Writing screenshot to file...") // Download the image from the graphics hardware and save // it to disk. complete := make(chan image.Image, 1) r.Download(image.Rect(0, 0, 0, 0), complete) img := <-complete // Wait for download to complete. // Save to png. f, err := os.Create("screenshot.png") if err != nil { log.Fatal(err) } err = png.Encode(f, img) if err != nil { log.Fatal(err) } fmt.Println("Wrote texture to screenshot.png") } } } }() triangleDrawn := make(chan *gfx.Object, 1) for { // Clear the entire area (empty rectangle means "the whole area"). r.Clear(image.Rect(0, 0, 0, 0), gfx.Color{1, 1, 1, 1}) r.ClearDepth(image.Rect(0, 0, 0, 0), 1.0) // See if someone else needs ownership of the triangle before we draw. select { case update <- triangle: // Wait for them to give ownership back. <-update default: } // Draw the triangle to the screen. r.Draw(image.Rect(0, 0, 0, 0), triangle, triangleDrawn) // Render the whole frame. r.Render() select { case <-triangleDrawn: // Allow updates to the triangle if needed. select { case update <- triangle: <-update default: } } } }
func program(gfxLoop func(w *chippy.Window, r gfx.Renderer)) { defer chippy.Exit() window := chippy.NewWindow() window.SetTitle("Azul3D") screen := chippy.DefaultScreen() window.SetPositionCenter(screen) events := window.Events() // Actually open the windows err := window.Open(screen) if err != nil { log.Fatal(err) } // All OpenGL related calls must occur in the same OS thread. runtime.LockOSThread() defer runtime.UnlockOSThread() // Choose and set a frame buffer configuration. configs := window.GLConfigs() bestConfig := chippy.GLChooseConfig(configs, chippy.GLWorstConfig, chippy.GLBestConfig) log.Println("Chosen configuration:", bestConfig) window.GLSetConfig(bestConfig) // Create the OpenGL rendering context. ctx, err := window.GLCreateContext(2, 1, chippy.GLCoreProfile, nil) if err != nil { log.Fatal(err) } // Create the OpenGL loader context. loaderCtx, err := window.GLCreateContext(2, 1, chippy.GLCoreProfile, ctx) if err != nil { log.Fatal(err) } // OpenGL rendering context must be active to create the renderer. window.GLMakeCurrent(ctx) defer window.GLMakeCurrent(nil) // Disable vertical sync. //window.GLSetVerticalSync(chippy.NoVerticalSync) // Create the renderer. r, err := gl2.New() if err != nil { log.Fatal(err) } // Start the graphics rendering loop. go gfxLoop(window, r) // Channel to signal shutdown to renderer and loader. shutdown := make(chan bool, 2) // Start event loop. go func() { cl := r.Clock() printFPS := time.Tick(1 * time.Second) for { select { case <-printFPS: window.SetTitle(fmt.Sprintf("Azul3D %vFPS (%f Avg.)", cl.FrameRate(), cl.AverageFrameRate())) case e := <-events: switch ev := e.(type) { case chippy.ResizedEvent: r.UpdateBounds(image.Rect(0, 0, ev.Width, ev.Height)) case chippy.CloseEvent, chippy.DestroyedEvent: shutdown <- true shutdown <- true return } } } }() // Start loading goroutine. go func() { // All OpenGL related calls must occur in the same OS thread. runtime.LockOSThread() defer runtime.UnlockOSThread() // OpenGL loading context must be active. window.GLMakeCurrent(loaderCtx) defer window.GLMakeCurrent(nil) for { select { case <-shutdown: return case fn := <-r.LoaderExec: fn() } } }() // Enter rendering loop. for { select { case <-shutdown: return case fn := <-r.RenderExec: if renderedFrame := fn(); renderedFrame { // Swap OpenGL buffers. window.GLSwapBuffers() } } } }