Beispiel #1
0
// Sets up anything that wouldn't have been loaded from disk, including
// all opengl data, and sets up finalizers for that data.
func (d *Dictionary) setupGlStuff() {
	d.dlists = make(map[string]uint32)
	d.strs = make(map[string]strBuffer)
	d.pars = make(map[string]strBuffer)
	// TODO: This finalizer is untested
	runtime.SetFinalizer(d, func(d *Dictionary) {
		render.Queue(func() {
			for _, v := range d.dlists {
				gl.DeleteLists(gl.Uint(v), 1)
			}
		})
	})

	render.Queue(func() {
		gl.Enable(gl.TEXTURE_2D)
		gl.GenTextures(1, (*gl.Uint)(&d.texture))
		gl.BindTexture(gl.TEXTURE_2D, gl.Uint(d.texture))
		gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
		glu.Build2DMipmaps(gl.TEXTURE_2D, 4, d.data.Dx, d.data.Dy, gl.RGBA, d.data.Pix)
		gl.Disable(gl.TEXTURE_2D)
	})
}
Beispiel #2
0
// Creates a LosTexture with the specified size, which must be a power of two.
func MakeLosTexture() *LosTexture {
	var lt LosTexture
	lt.pix = make([]byte, LosTextureSizeSquared)
	lt.p2d = make([][]byte, LosTextureSize)
	lt.rec = make(chan gl.Texture, 1)
	for i := 0; i < LosTextureSize; i++ {
		lt.p2d[i] = lt.pix[i*LosTextureSize : (i+1)*LosTextureSize]
	}

	render.Queue(func() {
		gl.Enable(gl.TEXTURE_2D)
		tex := gl.GenTexture()
		tex.Bind(gl.TEXTURE_2D)
		gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
		gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
		gl.TexImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, len(lt.p2d), len(lt.p2d), 0, gl.ALPHA, gl.BYTE, lt.pix)
		lt.rec <- tex
		runtime.SetFinalizer(&lt, losTextureFinalize)
	})

	return &lt
}
Beispiel #3
0
// Launch this in its own go-routine if you want to occassionally
// delete textures that haven't been used in a while.
func (m *Manager) Scavenger() {
	var unused []string
	for {
		time.Sleep(time.Minute)
		unused = unused[0:0]
		m.mutex.RLock()
		for s, d := range m.registry {
			if generation-d.accessed >= 2 {
				unused = append(unused, s)
			}
		}
		m.mutex.RUnlock()

		m.mutex.Lock()
		generation++
		if len(unused) == 0 {
			m.mutex.Unlock()
			continue
		}

		var unused_data []*Data
		for _, s := range unused {
			unused_data = append(unused_data, m.registry[s])
			m.deleted[s] = m.registry[s]
			delete(m.registry, s)
		}
		render.Queue(func() {
			for _, d := range unused_data {
				d.texture.Delete()
				d.texture = 0
			}
		})
		m.mutex.Unlock()
	}
}
Beispiel #4
0
func (m *Manager) LoadSprite(path string) (*Sprite, error) {
	// We can't run this during an init() function because it will get queued to
	// run before the opengl context is created, so we just check here and run
	// it if we haven't run it before.
	gen_tex_once.Do(func() {
		render.Queue(func() {
			gl.Enable(gl.TEXTURE_2D)
			error_texture = gl.GenTexture()
			error_texture.Bind(gl.TEXTURE_2D)
			gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE)
			gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
			gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
			gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
			gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
			pink := []byte{255, 0, 255, 255}
			glu.Build2DMipmaps(gl.TEXTURE_2D, 4, 1, 1, gl.RGBA, pink)
		})
	})

	path = filepath.Clean(path)
	err := m.loadSharedSprite(path)
	if err != nil {
		return nil, err
	}
	var s Sprite
	m.mutex.Lock()
	s.shared = m.shared[path]
	m.mutex.Unlock()
	s.anim_node = s.shared.anim_start
	s.state_node = s.shared.state_start
	return &s, nil
}
Beispiel #5
0
// Updates OpenGl with any changes that have been made to the texture.
// OpenGl calls in this method are run on the render thread
func (lt *LosTexture) Remap() {
	if !lt.ready() {
		return
	}
	render.Queue(func() {
		gl.Enable(gl.TEXTURE_2D)
		lt.tex.Bind(gl.TEXTURE_2D)
		gl.TexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, len(lt.p2d), len(lt.p2d), gl.ALPHA, lt.pix)
	})
}
Beispiel #6
0
func (s *sheet) loadRoutine() {
	ready := make(chan bool, 1)
	pixer := make(chan []byte)
	for load := range s.load_chan {
		if load {
			go s.compose(pixer)
			go func() {
				render.Queue(func() {
					s.makeTexture(pixer)
					ready <- true
				})
			}()
		} else {
			go func() {
				<-ready
				render.Queue(func() {
					s.texture.Delete()
					s.texture = 0
				})
			}()
		}
	}
}
Beispiel #7
0
func setupTextureList() {
	textureListSync.Do(func() {
		render.Queue(func() {
			textureList = gl.GenLists(1)
			gl.NewList(textureList, gl.COMPILE)
			gl.Begin(gl.QUADS)
			gl.TexCoord2d(0, 0)
			gl.Vertex2i(0, 0)

			gl.TexCoord2d(0, -1)
			gl.Vertex2i(0, 1)

			gl.TexCoord2d(1, -1)
			gl.Vertex2i(1, 1)

			gl.TexCoord2d(1, 0)
			gl.Vertex2i(1, 0)
			gl.End()
			gl.EndList()
		})
	})
}
Beispiel #8
0
func main() {
	defer func() {
		if r := recover(); r != nil {
			data := debug.Stack()
			base.Error().Printf("PANIC: %v\n", r)
			base.Error().Printf("PANIC: %s\n", string(data))
			base.CloseLog()
			fmt.Printf("PANIC: %s\n", string(data))
		}
	}()
	base.Log().Printf("Version %s", Version())
	sys.Startup()
	sound.Init()
	render.Init()
	render.Queue(func() {
		sys.CreateWindow(10, 10, wdx, wdy)
		sys.EnableVSync(true)
		err := gl.Init()
		if err != nil {
			panic(err)
		}
		gl.Enable(gl.BLEND)
		gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	})
	base.InitShaders()
	runtime.GOMAXPROCS(8)
	ui, err := gui.Make(gin.In(), gui.Dims{wdx, wdy}, filepath.Join(datadir, "fonts", "skia.ttf"))
	if err != nil {
		panic(err.Error())
	}
	loadAllRegistries()

	// TODO: Might want to be able to reload stuff, but this is sensitive because it
	// is loading textures.  We should probably redo the sprite system so that this
	// is easier to safely handle.
	game.LoadAllEntities()

	// Set up editors
	editors = map[string]house.Editor{
		"room":  house.MakeRoomEditorPanel(),
		"house": house.MakeHouseEditorPanel(),
	}
	for name, editor := range editors {
		path := base.GetStoreVal(fmt.Sprintf("last %s path", name))
		path = filepath.Join(datadir, path)
		if path != "" {
			editor.Load(path)
		}
	}
	editor_name = "room"
	editor = editors[editor_name]

	edit_mode := false
	game.Restart = func() {
		base.Log().Printf("Restarting...")
		ui.RemoveChild(game_box)
		game_box = &lowerLeftTable{gui.MakeAnchorBox(gui.Dims{1024, 768})}
		err = game.InsertStartMenu(game_box)
		if err != nil {
			panic(err)
		}
		ui.AddChild(game_box)
		base.Log().Printf("Restarted")
	}
	game.Restart()

	if base.IsDevel() {
		ui.AddChild(base.MakeConsole())
	}
	sys.Think()
	// Wait until now to create the dictionary because the render thread needs
	// to be running in advance.
	render.Queue(func() {
		ui.Draw()
	})
	render.Purge()

	var profile_output *os.File
	heap_prof_count := 0

	for key_map["quit"].FramePressCount() == 0 {
		sys.Think()
		render.Queue(func() {
			sys.SwapBuffers()
			ui.Draw()
		})
		render.Purge()

		for _, child := range game_box.GetChildren() {
			if gp, ok := child.(*game.GamePanel); ok {
				game_panel = gp
			}
		}

		if base.IsDevel() {
			if key_map["cpu profile"].FramePressCount() > 0 {
				if profile_output == nil {
					profile_output, err = os.Create(filepath.Join(datadir, "cpu.prof"))
					if err == nil {
						err = pprof.StartCPUProfile(profile_output)
						if err != nil {
							base.Log().Printf("Unable to start CPU profile: %v\n", err)
							profile_output.Close()
							profile_output = nil
						}
						base.Log().Printf("profout: %v\n", profile_output)
					} else {
						base.Log().Printf("Unable to start CPU profile: %v\n", err)
					}
				} else {
					pprof.StopCPUProfile()
					profile_output.Close()
					profile_output = nil
				}
			}

			if key_map["heap profile"].FramePressCount() > 0 {
				out, err := os.Create(filepath.Join(datadir, fmt.Sprintf("heap-%d.prof", heap_prof_count)))
				heap_prof_count++
				if err == nil {
					err = pprof.WriteHeapProfile(out)
					out.Close()
					if err != nil {
						base.Warn().Printf("Unable to write heap profile: %v", err)
					}
				} else {
					base.Warn().Printf("Unable to create heap profile: %v", err)
				}
			}

			if key_map["manual mem"].FramePressCount() > 0 {
				base.Log().Printf(memory.TotalAllocations())
			}

			if key_map["game mode"].FramePressCount()%2 == 1 {
				base.Log().Printf("Game mode change: %t", edit_mode)
				if edit_mode {
					ui.RemoveChild(editor)
					ui.AddChild(game_box)
				} else {
					ui.RemoveChild(game_box)
					ui.AddChild(editor)
				}
				edit_mode = !edit_mode

				if key_map["row up"].FramePressCount() > 0 {
					house.Num_rows += 25
				}
				if key_map["row down"].FramePressCount() > 0 {
					house.Num_rows -= 25
				}
				if key_map["steps up"].FramePressCount() > 0 {
					house.Num_steps++
				}
				if key_map["steps down"].FramePressCount() > 0 {
					house.Num_steps--
				}
				if key_map["noise up"].FramePressCount() > 0 {
					house.Noise_rate += 10
				}
				if key_map["noise down"].FramePressCount() > 0 {
					house.Noise_rate -= 10
				}
				if key_map["foo"].FramePressCount() > 0 {
					house.Foo = (house.Foo + 1) % 2
				}
			}

			if edit_mode {
				editMode()
			} else {
				gameMode()
			}
		}
		// Draw a cursor at the cursor - for testing an osx bug in glop.
		// zx, zy := gin.In().GetCursor("Mouse").Point()
		// render.Queue(func() {
		//   gl.Color4ub(255, 0, 0, 255)
		//   gl.Begin(gl.LINES)
		//   {
		//     gl.Vertex2i(int32(zx-25), int32(zy))
		//     gl.Vertex2i(int32(zx+25), int32(zy))
		//     gl.Vertex2i(int32(zx), int32(zy-25))
		//     gl.Vertex2i(int32(zx), int32(zy+25))
		//   }
		//   gl.End()
		// })
	}
}
Beispiel #9
0
func losTextureFinalize(lt *LosTexture) {
	render.Queue(func() {
		gl.Enable(gl.TEXTURE_2D)
		lt.tex.Delete()
	})
}
Beispiel #10
0
func handleLoadRequest(req loadRequest) {
	f, _ := os.Open(req.path)
	im, _, err := image.Decode(f)
	f.Close()
	if err != nil {
		return
	}
	gray := true
	dx := im.Bounds().Dx()
	dy := im.Bounds().Dy()
	for i := 0; i < dx; i++ {
		for j := 0; j < dy; j++ {
			r, g, b, _ := im.At(i, j).RGBA()
			if r != g || g != b {
				gray = false
				break
			}
		}
		if !gray {
			break
		}
	}
	var canvas draw.Image
	var pix []byte
	if gray {
		ga := NewGrayAlpha(im.Bounds())
		pix = ga.Pix
		canvas = ga
	} else {
		pix = memory.GetBlock(4 * req.data.dx * req.data.dy)
		canvas = &image.RGBA{pix, 4 * req.data.dx, im.Bounds()}
	}
	draw.Draw(canvas, im.Bounds(), im, image.Point{}, draw.Src)
	load_mutex.Lock()
	load_count += len(pix)
	manual_unlock := false
	// This prevents us from trying to send too much to opengl in a single
	// frame.  If we go over the threshold then we hold the lock until we're
	// done sending data to opengl, then other requests will be free to
	// queue up and they will run on the next frame.
	if load_count < load_threshold {
		load_mutex.Unlock()
	} else {
		manual_unlock = true
	}
	render.Queue(func() {
		{
			gl.Enable(gl.TEXTURE_2D)
			req.data.texture = gl.GenTexture()
			req.data.texture.Bind(gl.TEXTURE_2D)
			gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE)
			gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
			gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
			gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
			gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
		}
		if gray {
			glu.Build2DMipmaps(gl.TEXTURE_2D, gl.LUMINANCE_ALPHA, req.data.dx, req.data.dy, gl.LUMINANCE_ALPHA, pix)
		} else {
			glu.Build2DMipmaps(gl.TEXTURE_2D, gl.RGBA, req.data.dx, req.data.dy, gl.RGBA, pix)
		}
		memory.FreeBlock(pix)
		if manual_unlock {
			load_count = 0
			load_mutex.Unlock()
		}
	})
}
Beispiel #11
0
func InitShaders() {
	render.Queue(func() {
		vertex_shaders = make(map[string]uint32)
		fragment_shaders = make(map[string]uint32)
		shader_progs = make(map[string]uint32)
		warned_names = make(map[string]bool)
		RemoveRegistry("shaders")
		RegisterRegistry("shaders", make(map[string]*shaderDef))
		RegisterAllObjectsInDir("shaders", filepath.Join(GetDataDir(), "shaders"), ".json", "json")
		names := GetAllNamesInRegistry("shaders")
		for _, name := range names {
			// Load the shader files
			shader := Shader{Defname: name}
			GetObject("shaders", &shader)
			vdata, err := ioutil.ReadFile(filepath.Join(GetDataDir(), shader.Vertex_path))
			if err != nil {
				Error().Printf("Unable to load vertex shader '%s': %v", shader.Vertex_path, err)
				continue
			}
			fdata, err := ioutil.ReadFile(filepath.Join(GetDataDir(), shader.Fragment_path))
			if err != nil {
				Error().Printf("Unable to load fragment shader '%s': %v", shader.Fragment_path, err)
				continue
			}

			// Create the vertex shader
			vertex_id, ok := vertex_shaders[shader.Vertex_path]
			if !ok {
				vertex_id = uint32(gl.CreateShader(gl.VERTEX_SHADER))
				pointer := &vdata[0]
				length := int32(len(vdata))
				gl.ShaderSource(gl.Uint(vertex_id), 1, (**gl.Char)(unsafe.Pointer(&pointer)), (*gl.Int)(&length))
				gl.CompileShader(gl.Uint(vertex_id))
				var param int32
				gl.GetShaderiv(gl.Uint(vertex_id), gl.COMPILE_STATUS, (*gl.Int)(&param))
				if param == 0 {
					Error().Printf("Failed to compile vertex shader '%s': %v", shader.Vertex_path, param)
					continue
				}
			}

			// Create the fragment shader
			fragment_id, ok := fragment_shaders[shader.Fragment_path]
			if !ok {
				fragment_id = uint32(gl.CreateShader(gl.FRAGMENT_SHADER))
				pointer := &fdata[0]
				length := int32(len(fdata))
				gl.ShaderSource(gl.Uint(fragment_id), 1, (**gl.Char)(unsafe.Pointer(&pointer)), (*gl.Int)(&length))
				gl.CompileShader(gl.Uint(fragment_id))
				var param int32
				gl.GetShaderiv(gl.Uint(fragment_id), gl.COMPILE_STATUS, (*gl.Int)(&param))
				if param == 0 {
					Error().Printf("Failed to compile fragment shader '%s': %v", shader.Fragment_path, param)
					continue
				}
			}

			// shader successfully compiled - now link
			program_id := gl.CreateProgram()
			gl.AttachShader(program_id, gl.Uint(vertex_id))
			gl.AttachShader(program_id, gl.Uint(fragment_id))
			gl.LinkProgram(program_id)
			var param int32
			gl.GetProgramiv(program_id, gl.LINK_STATUS, (*gl.Int)(&param))
			if param == 0 {
				Error().Printf("Failed to link shader '%s': %v", shader.Name, param)
				continue
			}

			vertex_shaders[shader.Vertex_path] = vertex_id
			fragment_shaders[shader.Fragment_path] = fragment_id
			shader_progs[shader.Name] = uint32(program_id)
		}
	})
}