Пример #1
0
func Test(t *testing.T) {
	err := glfw.Init()
	if err != nil {
		t.Fatal(err)
	}

	defer glfw.Terminate()

	err = glfw.OpenWindow(800, 600, 8, 8, 8, 0, 0, 0, glfw.Windowed)
	if err != nil {
		t.Fatal(err)
	}

	defer glfw.CloseWindow()

	kb := New()
	kb.Bind(func() {
		fmt.Println("pressed s")
	}, "s")

	kb.Bind(func() {
		fmt.Println("pressed ctrl+s or command+s")
	}, "ctrl+s", "command+s")

	kb.Bind(func() {
		fmt.Println("pressed 't e s t'")
	}, "t e s t")

	kb.Bind(func() {
		fmt.Println("pressed '3'")
	}, "3")

	kb.Bind(func() {
		fmt.Println("pressed '#'")
	}, "#")

	kb.Bind(func() {
		glfw.CloseWindow()
	}, "escape")

	// Print available keybindings.
	fmt.Printf("Known key bindings:\n")
	for _, b := range kb.Bindings() {
		fmt.Printf(" - %q\n", b)
	}

	// Go into GLFW's main event loop.
	for glfw.WindowParam(glfw.Opened) == 1 {
		glfw.SwapBuffers()
	}
}
Пример #2
0
func main() {
	var err error
	if err = glfw.Init(); err != nil {
		log.Fatalf("%v\n", err)
		return
	}

	defer glfw.Terminate()

	if err = glfw.OpenWindow(Width, Height, 8, 8, 8, 8, 0, 8, glfw.Windowed); err != nil {
		log.Fatalf("%v\n", err)
		return
	}

	defer glfw.CloseWindow()

	glfw.SetSwapInterval(1)
	glfw.SetWindowTitle(Title)
	glfw.SetWindowSizeCallback(onResize)
	glfw.SetKeyCallback(onKey)

	if err = initGL(); err != nil {
		log.Fatalf("%v\n", err)
		return
	}

	defer destroyGL()

	running = true
	for running && glfw.WindowParam(glfw.Opened) == 1 {
		drawScene()
	}
}
Пример #3
0
// onKey handles key events.
func onKey(key, state int) {
	switch key {
	case glfw.KeyEsc:
		glfw.CloseWindow()
	case 'W':
		zoom--
	case 'S':
		zoom++
	case '1', '2', '3':
		var idx int
		switch key {
		case '1':
			idx = 0
		case '2':
			idx = 1
		case '3':
			idx = 2
		}
		if state == glfw.KeyPress {
			if rotVet[idx] == 1 {
				rotVet[idx] = 0
			} else {
				rotVet[idx] = 1
			}
		}
	}
}
Пример #4
0
func main() {
	var err error
	if err = glfw.Init(); err != nil { ///初始化环境
		log.Fatalf("%v\n", err)
		return
	}
	defer glfw.Terminate() /// 销毁环境

	if err = glfw.OpenWindow(Width, Height, 8, 8, 8, 8, 0, 8, glfw.Windowed); err != nil { ///创建窗口
		log.Fatalf("%v\n", err)
		return
	}
	defer glfw.CloseWindow() /// 销毁窗口

	glfw.SetSwapInterval(1)
	glfw.SetWindowTitle(Title)           ///设置标题
	glfw.SetWindowSizeCallback(onResize) /// 回调窗口变化
	glfw.SetKeyCallback(onKey)           ///回调按键

	initGL()

	running = true
	for running && glfw.WindowParam(glfw.Opened) == 1 {
		drawScene()
	}
}
Пример #5
0
func RunGame(title string, width, height int, init, draw func()) {
	if err := glfw.Init(); err != nil {
		fmt.Fprintf(os.Stderr, "glfw: %s\n", err)
		return
	}
	defer glfw.Terminate()

	glfw.OpenWindowHint(glfw.WindowNoResize, 1)

	if err := glfw.OpenWindow(width, height, 0, 0, 0, 0, 16, 0, glfw.Windowed); err != nil {
		fmt.Fprintf(os.Stderr, "glfw: %s\n", err)
		return
	}
	defer glfw.CloseWindow()

	glfw.SetSwapInterval(1)
	glfw.SetWindowTitle(title)

	if err := gl.Init(); err != nil {
		fmt.Fprintf(os.Stderr, "gl: %s\n", err)
	}

	if err := initScene(width, height, init); err != nil {
		fmt.Fprintf(os.Stderr, "init: %s\n", err)
		return
	}
	// defer destroyScene()

	for glfw.WindowParam(glfw.Opened) == 1 {
		drawScene(draw)
		glfw.SwapBuffers()
	}
}
Пример #6
0
func (i *IO) Init(title string, screenSize int, onCloseHandler func()) error {
	var err error

	err = glfw.Init()
	if err != nil {
		return err
	}

	err = i.Display.init(title, screenSize)
	if err != nil {
		return err
	}

	i.KeyHandler.Init(DefaultControlScheme) //TODO: allow user to define controlscheme
	glfw.SetKeyCallback(func(key, state int) {
		if state == glfw.KeyPress {
			i.KeyHandler.KeyDown(key)
		} else {
			i.KeyHandler.KeyUp(key)
		}
	})

	glfw.SetWindowCloseCallback(func() int {
		glfw.CloseWindow()
		glfw.Terminate()
		onCloseHandler()
		return 0
	})

	return nil
}
Пример #7
0
func main() {
	if err := glfw.Init(); err != nil {
		fmt.Fprintf(os.Stderr, "[e] %v\n", err)
		return
	}
	defer glfw.Terminate()

	if err := glfw.OpenWindow(appWidth, appHeight, 8, 8, 8, 8, 24, 8, glfw.Windowed); err != nil {
		fmt.Fprintf(os.Stderr, "[e] %v\n", err)
		return
	}
	defer glfw.CloseWindow()

	if err := gl.Init(); err != 0 {
		fmt.Fprintf(os.Stderr, "[e] %v\n", err)
		return
	}
	glfw.SetWindowTitle(title)

	fps := fps.NewFPS(glfw.Time())

	blocks.Init(appWidth, appHeight)
	for glfw.WindowParam(glfw.Opened) == 1 {
		blocks.Tick()
		fps.Tick(glfw.Time())
		if glfw.WindowParam(glfw.Active) == 1 {
			glfw.Sleep(0.001)
		} else {
			glfw.Sleep(0.05)
		}
	}
}
Пример #8
0
func make_window(w, h int, title string) func() {
	// Required to make sure that the OpenGL go-routine doesn't get switched
	// to another thread (=> kerblammo)
	runtime.LockOSThread()

	if err := glfw.Init(); err != nil {
		log.Panic("glfw Error:", err)
	}

	err := glfw.OpenWindow(w, h, 0, 0, 0, 0, 0, 0, glfw.Windowed)
	if err != nil {
		log.Panic("Error:", err)
	}

	if gl.Init() != 0 {
		log.Panic("gl error")
	}

	if *vsync {
		glfw.SetSwapInterval(1)
	} else {
		glfw.SetSwapInterval(0)
	}

	glfw.SetWindowTitle(title)
	glfw.SetWindowSizeCallback(Reshape)

	Init()

	return func() {
		glfw.Terminate()
		glfw.CloseWindow()
		log.Print("Cleanup")
	}
}
Пример #9
0
func main() {
	var err error
	if err = glfw.Init(); err != nil {
		log.Fatalf("%v\n", err)
		return
	}

	defer glfw.Terminate()

	if err = glfw.OpenWindow(640, 480, 8, 8, 8, 8, 0, 0, glfw.Windowed); err != nil {
		log.Fatalf("%v\n", err)
		return
	}

	defer glfw.CloseWindow()

	glfw.SetWindowTitle("Draw")
	glfw.SetSwapInterval(1)
	glfw.SetKeyCallback(onKey)
	glfw.SetMouseButtonCallback(onMouseBtn)
	glfw.SetWindowSizeCallback(onResize)

	running = true
	for running && glfw.WindowParam(glfw.Opened) == 1 {
		if mouse[0] != 0 {
			pen.lineTo(glfw.MousePos())
		} else {
			pen.moveTo(glfw.MousePos())
		}

		glfw.SwapBuffers()
	}
}
Пример #10
0
func (i *IO) Init(title string, screenSize int, onCloseHandler func()) error {
	i.KeyHandler = new(KeyHandler)
	i.Display = new(Display)
	i.ScreenOutputChannel = make(chan *types.Screen)
	i.AudioOutputChannel = make(chan int)

	if err := glfw.Init(); err != nil {
		return err
	} else if err = i.Display.init(title, screenSize); err != nil {
		return err
	}

	i.KeyHandler.Init(DefaultControlScheme) //TODO: allow user to define controlscheme
	glfw.SetKeyCallback(func(key, state int) {
		if state == glfw.KeyPress {
			i.KeyHandler.KeyDown(key)
		} else {
			i.KeyHandler.KeyUp(key)
		}
	})

	glfw.SetWindowCloseCallback(func() int {
		glfw.CloseWindow()
		glfw.Terminate()
		onCloseHandler()
		return 0
	})

	return nil
}
Пример #11
0
// Signal that we are exiting and close glfw.
func CloseWindow() {
	fmt.Printf("Exiting...\n")
	WindowOpened = false
	glfw.CloseWindow()
	glfw.Terminate()
	DestroyShaders()
}
Пример #12
0
func (_ *NgUserIO) dispose() {
	if UserIO.Window.isCreated {
		UserIO.Window.isCreated = false
		glfw.CloseWindow()
	}
	if UserIO.isGlfwInit {
		UserIO.isGlfwInit = false
		glfw.Terminate()
	}
}
Пример #13
0
func main() {
	var err error
	if err = glfw.Init(); err != nil {
		log.Fatalf("%v\n", err)
		return
	}

	defer glfw.Terminate()

	// Open window with FSAA samples (if possible).
	glfw.OpenWindowHint(glfw.FsaaSamples, 4)

	if err = glfw.OpenWindow(400, 400, 0, 0, 0, 0, 0, 0, glfw.Windowed); err != nil {
		log.Fatalf("%v\n", err)
		return
	}

	defer glfw.CloseWindow()

	glfw.SetWindowTitle("Aliasing Detector")
	glfw.SetSwapInterval(1)

	if samples := glfw.WindowParam(glfw.FsaaSamples); samples != 0 {
		fmt.Printf("Context reports FSAA is supported with %d samples\n", samples)
	} else {
		fmt.Printf("Context reports FSAA is unsupported\n")
	}

	gl.MatrixMode(gl.PROJECTION)
	glu.Perspective(0, 1, 0, 1)

	for glfw.WindowParam(glfw.Opened) == 1 {
		time := float32(glfw.Time())

		gl.Clear(gl.COLOR_BUFFER_BIT)

		gl.LoadIdentity()
		gl.Translatef(0.5, 0, 0)
		gl.Rotatef(time, 0, 0, 1)

		gl.Enable(GL_MULTISAMPLE_ARB)
		gl.Color3f(1, 1, 1)
		gl.Rectf(-0.25, -0.25, 0.25, 0.25)

		gl.LoadIdentity()
		gl.Translatef(-0.5, 0, 0)
		gl.Rotatef(time, 0, 0, 1)

		gl.Disable(GL_MULTISAMPLE_ARB)
		gl.Color3f(1, 1, 1)
		gl.Rectf(-0.25, -0.25, 0.25, 0.25)
		glfw.SwapBuffers()
	}
}
Пример #14
0
func main() {
	var err error
	if err = glfw.Init(); err != nil {
		fmt.Fprintf(os.Stderr, "[e] %v\n", err)
		return
	}

	// Ensure glfw is cleanly terminated on exit.
	defer glfw.Terminate()

	if err = glfw.OpenWindow(256, 256, 8, 8, 8, 0, 0, 0, glfw.Windowed); err != nil {
		fmt.Fprintf(os.Stderr, "[e] %v\n", err)
		return
	}

	// Ensure window is cleanly closed on exit.
	defer glfw.CloseWindow()

	// Enable vertical sync on cards that support it.
	glfw.SetSwapInterval(1)

	// Set window title
	glfw.SetWindowTitle("Simple GLFW window")

	// Hook some events to demonstrate use of callbacks.
	// These are not necessary if you don't need them.
	glfw.SetWindowSizeCallback(onResize)
	glfw.SetWindowCloseCallback(onClose)
	glfw.SetMouseButtonCallback(onMouseBtn)
	glfw.SetMouseWheelCallback(onMouseWheel)
	glfw.SetKeyCallback(onKey)
	glfw.SetCharCallback(onChar)

	// Start loop
	running := true
	for running {
		// OpenGL rendering goes here.

		// Swap front and back rendering buffers. This also implicitly calls
		// glfw.PollEvents(), so we have valid key/mouse/joystick states after
		// this. This behavior can be disabled by calling glfw.Disable with the
		// argument glfw.AutoPollEvents. You must be sure to manually call
		// PollEvents() or WaitEvents() in this case.
		glfw.SwapBuffers()

		// Break out of loop when Escape key is pressed, or window is closed.
		running = glfw.Key(glfw.KeyEsc) == 0 && glfw.WindowParam(glfw.Opened) == 1
	}
}
Пример #15
0
func (_ *NgUserIO) recreateWin() (err error) {
	winInit := &Options.Initialization.Window
	if UserIO.Window.isCreated {
		glfw.CloseWindow()
	}
	if UserIO.Window.isCreated, err = false, glfw.OpenWindow(UserIO.Window.width, UserIO.Window.height, winInit.Rbits, winInit.Gbits, winInit.Bbits, winInit.Abits, winInit.DepthBits, winInit.StencilBits, ugo.Ifi(UserIO.Window.fullscreen, glfw.Fullscreen, glfw.Windowed)); err == nil {
		UserIO.Window.width, UserIO.Window.height = glfw.WindowSize()
		UserIO.Window.isCreated = true
		UserIO.Window.SetTitle(UserIO.Window.title)
		UserIO.Window.SetSwapInterval(UserIO.Window.swap)
		glfw.SetWindowCloseCallback(glfwOnWindowClose)
		glfw.SetWindowSizeCallback(glfwOnWindowResize)
		// glfw.Disable(glfw.MouseCursor)
		glfw.Disable(glfw.AutoPollEvents)
		glfw.Disable(glfw.StickyKeys)
	}
	return
}
Пример #16
0
func (e *Emu) Close() {
	glfw.CloseWindow()
}
Пример #17
0
func (rw *RenderWindow) Close() {
	println("quit")
	glfw.CloseWindow()
	glfw.Terminate()
}
Пример #18
0
func main() {
	var err error
	var looping = true
	defer fmt.Println("EXIT")
	if err = glfw.Init(); err != nil {
		panic(err)
	}
	defer glfw.Terminate()
	glfw.OpenWindowHint(glfw.FsaaSamples, 0)
	if useStrictCoreProfile {
		glfw.OpenWindowHint(glfw.OpenGLVersionMajor, 3)
		glfw.OpenWindowHint(glfw.OpenGLVersionMinor, 2)
		glfw.OpenWindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
	}

	if err = glfw.OpenWindow(1280, 720, 8, 8, 8, 0, 24, 8, glfw.Windowed); err != nil {
		panic(err)
	}
	defer glfw.CloseWindow()
	glfw.Enable(glfw.StickyKeys)
	if !gl.Util.Init() {
		panic("Failed to initialize at least OpenGL 3.2 or higher.")
	}
	defer logLastGlError("(post loop)")
	gl.ClearColor(0.3, 0.1, 0.0, 1.0)
	gl.Enable(gl.DEPTH_TEST)
	gl.FrontFace(gl.CCW)
	gl.CullFace(gl.BACK)
	gl.Disable(gl.CULL_FACE)
	if err = compileShaders(); err != nil {
		panic(err)
	}
	setupGeometry()
	defer deleteGeometry()
	logLastGlError("(pre loop)")

	for looping {
		gl.UseProgram(shaderProg)
		if isFirstLoop {
			logLastGlError("gl.UseProgram")
		}

		gl.Viewport(0, 0, 1280, 720)
		if isFirstLoop {
			logLastGlError("gl.ViewPort")
		}

		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
		if isFirstLoop {
			logLastGlError("gl.Clear")
		}

		renderGeometry(faceTri)
		if isFirstLoop {
			logLastGlError("renderGeometry(faceTri)")
		}

		renderGeometry(faceQuad)
		if isFirstLoop {
			logLastGlError("renderGeometry(faceQuad)")
		}

		if (glfw.WindowParam(glfw.Opened) != 1) || (glfw.Key(glfw.KeyEsc) == glfw.KeyPress) {
			looping = false
		} else {
			glfw.SwapBuffers()
		}
		isFirstLoop = false
	}
	logLastGlError("post-loop")
}
Пример #19
0
func onClose() int {
	glfw.CloseWindow()
	glfw.Terminate()
	log.Println("closed")
	return 1 // return 0 to keep window open.
}
Пример #20
0
func New(width int, height int, title string) *Window {
	var err error
	window := &Window{width: width, height: height, title: title}
	// initialize logger
	logf, err := os.OpenFile("window.log", os.O_WRONLY|os.O_CREATE, 0640)
	logger := log.New(logf, "", log.Ldate|log.Ltime)

	if err = glfw.Init(); err != nil {
		fmt.Fprintf(os.Stderr, "[e] %v\n", err)
		return nil
	}

	// width, hieght, r,g,b,a (color depth bits), depth, stencil, mode
	if err = glfw.OpenWindow(window.width, window.height, 8, 8, 8, 0, 0, 0, glfw.Windowed); err != nil {
		fmt.Fprintf(os.Stderr, "[e] %v\n", err)
		return nil
	}

	onResize := func(w, h int) {
		logger.Printf("resized: %dx%d\n", w, h)
	}

	onClose := func() int {
		logger.Println("window closed\n")
		return 1 // return 0 to keep window open.
	}

	// list callback generators
	createBtnList := func(btnNext func() interface{}, device string) func(button, state int) {
		btnList := list.New()
		btnNext = func() interface{} { return btnList.Remove(btnList.Back()) }
		return func(button, state int) {
			btnList.PushFront(btnEvent{button: button, state: state})
			logger.Printf("%s button: %d, %d\n", device, button, state)
		}
	}

	createPosList := func(posNext func() interface{}, device string) func(x, y int) {
		posList := list.New()
		posNext = func() interface{} { return posList.Remove(posList.Back()) }
		return func(x, y int) {
			posList.PushFront(posEvent{x: x, y: y})
			logger.Printf("%s pos: %d, %d\n", device, x, y)
		}
	}

	createDeltaList := func(deltaNext func() interface{}, device string) func(delta int) {
		deltaList := list.New()
		deltaNext = func() interface{} { return deltaList.Remove(deltaList.Back()) }
		return func(delta int) {
			deltaList.PushFront(deltaEvent{delta: delta})
			logger.Printf("%s delta: %d\n", device, delta)
		}
	}

	glfw.SetSwapInterval(1)
	glfw.SetWindowTitle(title)

	glfw.SetWindowSizeCallback(onResize)
	glfw.SetWindowCloseCallback(onClose)
	glfw.SetMousePosCallback(createPosList(window.MousePos, "mouse pos"))
	glfw.SetMouseButtonCallback(createBtnList(window.MouseBtn, "mouse"))
	glfw.SetMouseWheelCallback(createDeltaList(window.MouseWheel, "mouse wheel"))
	glfw.SetKeyCallback(createBtnList(window.KeyEvt, "keyboard"))
	glfw.SetCharCallback(createBtnList(window.CharEvt, "char"))

	window.Start = func() {
		defer glfw.Terminate()
		defer glfw.CloseWindow()
		running := true
		for running {
			window.Render()
			glfw.SwapBuffers()
			// Break out of loop when Escape key is pressed, or window is closed.
			running = glfw.Key(glfw.KeyEsc) == 0 && glfw.WindowParam(glfw.Opened) == 1
		}
	}

	return window
}
Пример #21
0
func (w *glfwBackend) Close() {
	w.open = false
	glfw.CloseWindow()
	glfw.Terminate()
}
Пример #22
0
func (v *video) close() {
	glfw.CloseWindow()
}
Пример #23
0
// onKey handles key events.
func onKey(key, state int) {
	if key == glfw.KeyEsc {
		glfw.CloseWindow()
	}
}
Пример #24
0
func (me *window) Close() {
	glfw.CloseWindow()
}
Пример #25
0
// handleInput processes input from the input box.
func (c *CLI) handleInput(data string) {
	name, argv := parseCommand(data)
	if len(name) == 0 {
		return
	}

	switch name {
	case "exit":
		glfw.CloseWindow()

	// Breakpoints.
	case "b": // Toggle/creaete breakpoint.
		switch len(argv) {
		case 1: // b <address>
			addr, err := argv.U16(0)
			if err != nil {
				c.sourceLog.Append("Invalid address value %q: %v", argv[0], err)
				return
			}

			if c.breakpoints.Index(addr) > -1 {
				c.breakpoints.Toggle(addr)
				return
			}

			file, line, ok := debugFromAddress(addr, arch)
			if !ok {
				c.sourceLog.Append("Invalid address value %q.", argv[0])
				return
			}

			c.breakpoints.Add(addr, file, line)

		case 2: // b <sourcefile> <sourceline>
			line, err := argv.U16(1)

			if err != nil {
				c.sourceLog.Append("Invalid line number %q: %v", argv[1], err)
				return
			}

			addr, ok := addrFromSource(argv[0], line, arch)
			if !ok {
				c.sourceLog.Append("Unknown source location %s:%s", argv[0], argv[1])
				return
			}

			if c.breakpoints.Index(addr) > -1 {
				c.breakpoints.Toggle(addr)
				return
			}

			_, file := filepath.Split(argv[0])
			c.breakpoints.Add(addr, file, line)

		default:
			c.sourceLog.Append("Invalid argument count for %q. Expected 1 or 2.", name)
		}

	case "be": // Enable all breakpoints.
		c.breakpoints.Enable(true)

	case "bd": // Disable all breakpoints.
		c.breakpoints.Enable(false)

	case "bc": // Clear all breakpoints.
		c.breakpoints.Clear()

	// Memory watchers.
	case "m": // Create/remove single watcher.
		switch len(argv) {
		case 1:
			addr, err := argv.U16(0)
			if err != nil {
				c.sourceLog.Append("Invalid address value %q: %v", argv[0], err)
				return
			}

			c.watchers.Toggle(addr)

		default:
			c.sourceLog.Append("Invalid argument count for %q. Expected 1.", name)
		}

	case "mc": // Clear all watchers.
		c.watchers.Clear()

	default:
		c.sourceLog.Append("Unknown command %q.", name)
	}
}
Пример #26
0
func main() {
	var err error
	if err = glfw.Init(); err != nil {
		fmt.Fprintf(os.Stderr, "[e] %v\n", err)
		return
	}

	defer glfw.Terminate()

	w, h := 1980, 1080
	// w, h := 1280, 768
	if err = glfw.OpenWindow(w, h, 8, 8, 8, 16, 0, 32, glfw.Fullscreen); err != nil {
		fmt.Fprintf(os.Stderr, "[e] %v\n", err)
		return
	}

	defer glfw.CloseWindow()

	glfw.SetSwapInterval(1)
	glfw.SetWindowTitle("Debris")

	quadric = glu.NewQuadric()

	gl.Enable(gl.CULL_FACE)

	gl.Enable(gl.DEPTH_TEST)
	gl.DepthFunc(gl.LEQUAL)

	gl.Enable(gl.NORMALIZE)

	gl.Enable(gl.BLEND)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

	gl.ShadeModel(gl.SMOOTH)
	gl.Enable(gl.LIGHTING)

	var (
		ambient        = []float32{0.1, 0.3, 0.6, 1}
		diffuse        = []float32{1, 1, 0.5, 1}
		specular       = []float32{0.4, 0.4, 0.4, 1}
		light_position = []float32{1, 0, 0, 0}

		// mat_specular  []float32 = []float32{1, 1, 0.5, 1}
		mat_specular  = []float32{1, 1, 0.75, 1}
		mat_shininess = float32(120)
		// light_position []float32 = []float32{0.0, 0.0, 1.0, 0.0}
	)

	const (
		fov               = 1.1 // degrees
		znear             = 145
		zfar              = 155
		camera_z_offset   = -150
		camera_x_rotation = 0 // degrees
		// camera_x_rotation = 20 // degrees

		starfield_fov = 45

		faces        = 1000
		earth_radius = 1
	)

	gl.Lightfv(gl.LIGHT1, gl.AMBIENT, ambient)
	gl.Lightfv(gl.LIGHT1, gl.DIFFUSE, diffuse)
	gl.Lightfv(gl.LIGHT1, gl.SPECULAR, specular)
	gl.Lightfv(gl.LIGHT1, gl.POSITION, light_position)
	gl.Enable(gl.LIGHT1)

	mat_emission := []float32{0, 0, 0.1, 1}
	gl.Materialfv(gl.FRONT_AND_BACK, gl.EMISSION, mat_emission)
	gl.Materialfv(gl.FRONT_AND_BACK, gl.SPECULAR, mat_specular)
	gl.Materialf(gl.FRONT_AND_BACK, gl.SHININESS, mat_shininess)

	gl.ClearColor(0.02, 0.02, 0.02, 1)
	gl.ClearDepth(1)
	gl.ClearStencil(0)
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

	b := createBuffer()

	planetoids := []*Planetoid{}
	for i := 0; i < 1000; i++ {
		p := &Planetoid{
			apogee:  1.2 + rand.Float64()*0.7,
			perigee: 1.5,
			// inclination: 45,
			inclination: rand.Float64()*20 - 10,
			// inclination: 0,
			phase0:      rand.Float64() * 360,
			rising_node: rand.Float64() * 10,
			phase:       0,
			// radius:      rand.Float32()*0.05 + 0.01, //float32(r),
			radius: rand.Float32()*0.0125 + 0.005, //float32(r),
			// quadric:     glu.NewQuadric(),
			circle: b,
		}
		planetoids = append(planetoids, p)
	}

	// Initial projection matrix:

	var aspect float64

	glfw.SetWindowSizeCallback(func(w, h int) {
		gl.Viewport(0, 0, w, h)

		gl.MatrixMode(gl.PROJECTION)
		gl.LoadIdentity()
		aspect = float64(w) / float64(h)
		glu.Perspective(fov, aspect, znear, zfar)
	})

	d := float64(0)

	wireframe := false
	atmosphere := false
	polar := false
	rotating := false
	front := false
	earth := true
	cone := true
	shadowing := true
	tilt := false
	running := true

	glfw.SetKeyCallback(func(key, state int) {
		if state != glfw.KeyPress {
			// Don't act on key coming up
			return
		}

		switch key {
		case 'A':
			atmosphere = !atmosphere
		case 'C':
			cone = !cone
		case 'E':
			earth = !earth
		case 'R':
			rotating = !rotating
		case 'F':
			front = !front
			if front {
				gl.FrontFace(gl.CW)
			} else {
				gl.FrontFace(gl.CCW)
			}
		case 'S':
			shadowing = !shadowing
		case 'T':
			tilt = !tilt
		case 'W':
			wireframe = !wireframe
			method := gl.GLenum(gl.FILL)
			if wireframe {
				method = gl.LINE
			}
			gl.PolygonMode(gl.FRONT_AND_BACK, method)

		case glfw.KeyF2:
			println("Screenshot captured")
			// glh.CaptureToPng("screenshot.png")

			w, h := glh.GetViewportWH()
			im := image.NewRGBA(image.Rect(0, 0, w, h))
			glh.ClearAlpha(1)
			gl.Flush()
			glh.CaptureRGBA(im)

			go func() {
				fd, err := os.Create("screenshot.png")
				if err != nil {
					panic("Unable to open file")
				}
				defer fd.Close()

				png.Encode(fd, im)
			}()

		case 'Q', glfw.KeyEsc:
			running = !running

		case glfw.KeySpace:
			polar = !polar
		}
	})

	_ = rand.Float64

	stars := glh.NewMeshBuffer(
		glh.RenderArrays,
		glh.NewPositionAttr(3, gl.DOUBLE, gl.STATIC_DRAW),
		glh.NewColorAttr(3, gl.DOUBLE, gl.STATIC_DRAW))

	const Nstars = 50000
	points := make([]float64, 3*Nstars)
	colors := make([]float64, 3*Nstars)

	for i := 0; i < Nstars; i++ {
		const R = 1

		phi := rand.Float64() * 2 * math.Pi
		z := R * (2*rand.Float64() - 1)
		theta := math.Asin(z / R)

		points[i*3+0] = R * math.Cos(theta) * math.Cos(phi)
		points[i*3+1] = R * math.Cos(theta) * math.Sin(phi)
		points[i*3+2] = z

		const r = 0.8
		v := rand.Float64()*r + (1 - r)
		colors[i*3+0] = v
		colors[i*3+1] = v
		colors[i*3+2] = v
	}

	stars.Add(points, colors)

	render_stars := func() {
		glh.With(glh.Attrib{gl.DEPTH_BUFFER_BIT | gl.ENABLE_BIT}, func() {
			gl.Disable(gl.LIGHTING)
			gl.PointSize(1)
			gl.Color4f(1, 1, 1, 1)

			gl.Disable(gl.DEPTH_TEST)
			gl.DepthMask(false)

			stars.Render(gl.POINTS)
		})
	}

	render_scene := func() {

		// Update light position (sensitive to current modelview matrix)
		gl.Lightfv(gl.LIGHT1, gl.POSITION, light_position)
		gl.Lightfv(gl.LIGHT2, gl.POSITION, light_position)

		if earth {
			Sphere(earth_radius, faces)
		}

		unlit_points := glh.Compound(glh.Disable(gl.LIGHTING), glh.Primitive{gl.POINTS})
		glh.With(unlit_points, func() {
			gl.Vertex3d(1, 0, 0)
		})

		for _, p := range planetoids {
			const dt = 0.1 // TODO: Frame update
			p.Render(dt)
		}

		glh.With(glh.Disable(gl.LIGHTING), func() {
			// Atmosphere
			gl.Color4f(0.25, 0.25, 1, 0.1)

			if atmosphere && earth {
				Sphere(earth_radius*1.025, 100)
			}

			gl.PointSize(10)

			glh.With(glh.Primitive{gl.POINTS}, func() {
				gl.Color4f(1.75, 0.75, 0.75, 1)
				gl.Vertex3d(-1.04, 0, 0)
			})
		})

	}

	render_shadow_volume := func() {

		glh.With(glh.Attrib{
			gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.ENABLE_BIT |
				gl.POLYGON_BIT | gl.STENCIL_BUFFER_BIT,
		}, func() {

			gl.Disable(gl.LIGHTING)

			if shadowing {
				// gl.Disable(gl.DEPTH_TEST)
				gl.DepthMask(false)
				gl.DepthFunc(gl.LEQUAL)

				gl.Enable(gl.STENCIL_TEST)
				gl.ColorMask(false, false, false, false)
				gl.StencilFunc(gl.ALWAYS, 1, 0xffffffff)
			}

			shadow_volume := func() {
				const sv_length = 2
				const sv_granularity = 100
				const sv_radius = earth_radius * 1.001

				// Shadow cone
				glh.With(glh.Matrix{gl.MODELVIEW}, func() {
					gl.Rotatef(90, 1, 0, 0)
					gl.Rotatef(90, 0, -1, 0)
					gl.Color4f(0.5, 0.5, 0.5, 1)
					glu.Cylinder(quadric, sv_radius, sv_radius*1.05,
						sv_length, sv_granularity, 1)

					glu.Disk(quadric, 0, sv_radius, sv_granularity, 1)

					glh.With(glh.Matrix{gl.MODELVIEW}, func() {
						gl.Translated(0, 0, sv_length)
						glu.Disk(quadric, 0, sv_radius*1.05, sv_granularity, 1)
					})
				})

				for _, p := range planetoids {
					p.RenderShadowVolume()
				}

			}

			if cone {
				gl.FrontFace(gl.CCW)
				gl.StencilOp(gl.KEEP, gl.KEEP, gl.INCR)

				shadow_volume()

				gl.FrontFace(gl.CW)
				gl.StencilOp(gl.KEEP, gl.KEEP, gl.DECR)

				shadow_volume()
			}

			if shadowing {
				gl.StencilFunc(gl.NOTEQUAL, 0, 0xffffffff)
				gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP)

				gl.ColorMask(true, true, true, true)
				// gl.Disable(gl.STENCIL_TEST)

				gl.Disable(gl.DEPTH_TEST)

				gl.FrontFace(gl.CCW)
				// gl.Color4f(1, 0, 0, 0.75)
				gl.Color4f(0, 0, 0, 0.75)
				// gl.Color4f(1, 1, 1, 0.75)

				gl.LoadIdentity()
				gl.Translated(0, 0, camera_z_offset)
				// TODO: Figure out why this doesn't draw over the whole screen
				glh.With(glh.Disable(gl.LIGHTING), func() {
					glh.DrawQuadd(-10, -10, 20, 20)
				})

				// gl.FrontFace(gl.CW)
				// gl.Enable(gl.LIGHTING)
				// gl.Disable(gl.LIGHT1)
				// render_scene()
				// gl.Enable(gl.LIGHT1)
			}
		})
	}
	_ = render_stars

	for running {
		running = glfw.WindowParam(glfw.Opened) == 1

		glfw.SwapBuffers()

		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)

		rotation := func() {
			if tilt {
				gl.Rotated(20, 1, 0, 0)
			}

			if polar {
				gl.Rotated(90, 1, 0, 0)
			}

			gl.Rotated(d, 0, -1, 0)
		}

		// Star field

		glh.With(glh.Matrix{gl.PROJECTION}, func() {
			gl.LoadIdentity()
			glu.Perspective(starfield_fov, aspect, 0, 1)

			glh.With(glh.Matrix{gl.MODELVIEW}, func() {
				gl.LoadIdentity()
				rotation()
				render_stars()
			})
		})

		gl.MatrixMode(gl.MODELVIEW)
		gl.LoadIdentity()
		gl.Translated(0, 0, camera_z_offset)

		rotation()
		if rotating {
			d += 0.2
		}

		_ = render_scene
		render_scene()

		_ = render_shadow_volume
		render_shadow_volume()
	}
}