Example #1
0
// 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()
	}
}
Example #2
0
// 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:
			}
		}
	}
}
Example #3
0
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()
			}
		}
	}
}