func initMeshes() error { r, err := asset.Reader("meshes.obj") if err != nil { return err } objs, err := decodeObjs(r) if err != nil { return err } meshes := createMeshes(objs) meshMap := map[string]*mesh{} for i, m := range meshes { log.Printf("mesh %d: %s", i, m.id) meshMap[m.id] = m } mm := func(id string) *mesh { if err != nil { return nil } m, ok := meshMap[id] if !ok { err = fmt.Errorf("mesh not found: %s", id) return nil } return m } colorObjIDs := map[game.BlockColor]string{ game.Red: "red", game.Purple: "purple", game.Blue: "blue", game.Cyan: "cyan", game.Green: "green", game.Yellow: "yellow", } selectorMesh = mm("selector") squareMesh = mm("square") textLineMesh = mm("text_line") for c, id := range colorObjIDs { blockMeshes[c] = mm(id) fragmentMeshes[c] = [4]*mesh{ mm(id + "_north_west"), mm(id + "_north_east"), mm(id + "_south_east"), mm(id + "_south_west"), } } if err != nil { return err } return nil }
func createAssetTexture(textureUnit uint32, name string) (uint32, error) { r, err := asset.Reader(name) if err != nil { return 0, err } img, _, err := image.Decode(r) if err != nil { return 0, err } rgba := image.NewRGBA(img.Bounds()) draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src) return createTexture(textureUnit, rgba) }
// Init starts the audio system, loads sound assets, and starts the sound loop. func Init() error { if err := portaudio.Initialize(); err != nil { return err } log.Printf("PortAudio version: %d %s", portaudio.Version(), portaudio.VersionText()) var err error makeBuffer := func(name string) []int16 { if err != nil { return nil } var r io.Reader if r, err = asset.Reader(name); err != nil { return nil } var w *wav if w, err = decodeWAV(r); err != nil { return nil } log.Printf("%s: %+v", name, w) buf := make([]int16, len(w.data)/2) for i := 0; i < len(buf); i++ { buf[i] = int16(w.data[i*2]) buf[i] += int16(w.data[i*2+1]) << 8 } return buf } var soundBuffers [][]int16 for _, a := range soundAssets { soundBuffers = append(soundBuffers, makeBuffer(a)) } if err != nil { return err } soundQueue := make(chan Sound, soundQueueSize) Play = func(s Sound) { soundQueue <- s } done := make(chan bool) Terminate = func() { done <- true <-done close(done) logFatalIfErr("portaudio.Terminate", portaudio.Terminate()) } go func() { const ( numInputChannels = 0 /* zero input - no recording */ numOutputChannels = 2 /* stereo output */ sampleRate = 44100 /* samples per second */ intervalMs = 100 framesPerBuffer = sampleRate / 1000.0 * intervalMs /* len(buf) == numChannels * framesPerBuffer */ outputBufferSize = numOutputChannels * framesPerBuffer ) // Temporary buffers to read and write the next audio batch. tmpIn := make([]int16, outputBufferSize) tmpOut := make([]int16, outputBufferSize) // outputRingBuffer is a buffer that the PortAudio callback will read data from, // and that we will periodically wake up to write new data to. outputRingBuffer := newRingBuffer(outputBufferSize * 10) // process is the callback that PortAudio will call when it needs audio data. process := func(out []int16) { outputRingBuffer.pop(tmpIn) for i := 0; i < len(out); i++ { out[i] = tmpIn[i] } } stream, err := portaudio.OpenDefaultStream(numInputChannels, numOutputChannels, sampleRate, framesPerBuffer, process) logFatalIfErr("portaudio.OpenDefaultStream", err) defer func() { logFatalIfErr("stream.Close", stream.Close()) }() logFatalIfErr("stream.Start()", stream.Start()) defer func() { // stream.Stop blocks until all samples have been played. logFatalIfErr("stream.Stop", stream.Stop()) done <- true }() var active [][]int16 quit := false loop: for { select { case <-time.After(intervalMs * time.Millisecond): // Play whatever sounds are in the queue at the time. n := len(soundQueue) for i := 0; i < n; i++ { active = append(active, soundBuffers[<-soundQueue]) } // Fill temporary buffer with any active sounds buffers. for i := 0; i < len(tmpOut); i++ { // Combine active signals together. var v int16 for j := 0; j < len(active); j++ { // Remove any buffers if they have no more samples. if len(active[j]) == 0 { active = append(active[:j], active[j+1:]...) j-- continue } v += active[j][0] active[j] = active[j][1:] } tmpOut[i] = v } outputRingBuffer.push(tmpOut...) // Only quit waking until there are no more streams to play. if quit && len(active) == 0 { break loop } case <-done: close(soundQueue) // Prevent any new sounds from being scheduled. quit = true } } }() return nil }