Adding modding folder with a hacked build.

This commit is contained in:
ml
2026-09-26 08:53:08 +02:00
parent cdb960dc71
commit c91080aba7
755 changed files with 50424 additions and 0 deletions
+420
View File
@@ -0,0 +1,420 @@
package gui
import (
"fmt"
"path/filepath"
"time"
"github.com/inkyblackness/imgui-go/v3"
"github.com/inkyblackness/hacked/ui/opengl"
)
type fontFileError struct {
Filename string
}
func (err fontFileError) Error() string {
return fmt.Sprintf("could not load font '%s'", err.Filename)
}
// ContextParameters describes how to create the context.
type ContextParameters struct {
// ConfigDir is used to store the UI state in.
ConfigDir string
// FontFile is the filename of a .TTF file to load instead of using default.
FontFile string
// FontSize is the requested size of the font. Defaults to 12.
FontSize float32
}
// BitmapTextureQuery resolves the texture and the palette to be used for a bitmap.
type BitmapTextureQuery func(id imgui.TextureID) (palette uint32, texture uint32)
// Context describes a scope for a graphical user interface.
// It is based on ImGui.
type Context struct {
imguiContext *imgui.Context
window opengl.Window
lastRenderTime time.Time
mouseButtonWasDown [3]bool
mouseButtonIsDown [3]bool
fontTexture uint32
shaderHandle uint32
attribLocationType int32
attribLocationTex int32
attribLocationPal int32
attribLocationProjMtx int32
attribLocationPosition int32
attribLocationUV int32
attribLocationColor int32
vboHandle uint32
elementsHandle uint32
}
// NewContext initializes a new UI context based on the provided OpenGL window.
func NewContext(window opengl.Window, param ContextParameters) (context *Context, err error) {
imgui.SetAssertHandler(nil)
context = &Context{
imguiContext: imgui.CreateContext(nil),
window: window,
}
imgui.CurrentIO().SetIniFilename(filepath.Join(param.ConfigDir, "imgui.ini"))
err = context.createDeviceObjects(param)
if err != nil {
context.Destroy()
context = nil
}
return
}
// Destroy cleans up the resources of the graphical user interface.
func (context *Context) Destroy() {
context.destroyDeviceObjects(context.window.OpenGL())
context.imguiContext.Destroy()
}
// MouseButtonChanged is called when the state of a button has changed.
func (context *Context) MouseButtonChanged(buttonIndex int, down bool) {
if (buttonIndex >= 0) && (buttonIndex < len(context.mouseButtonIsDown)) {
context.mouseButtonIsDown[buttonIndex] = down
if down {
context.mouseButtonWasDown[buttonIndex] = down
}
}
}
// IsUsingMouse returns true if the UI is using the mouse.
// The application should not process mouse events in this case.
func (context Context) IsUsingMouse() bool {
return imgui.CurrentIO().WantCaptureMouse()
}
// IsUsingKeyboard returns true if the UI is currently capturing keyboard input.
// The application should not process keyboard input events in this case.
func (context Context) IsUsingKeyboard() bool {
return imgui.CurrentIO().WantTextInput()
}
// NewFrame must be called at the start of rendering.
func (context *Context) NewFrame() {
io := imgui.CurrentIO()
windowWidth, windowHeight := context.window.Size()
io.SetDisplaySize(imgui.Vec2{X: float32(windowWidth), Y: float32(windowHeight)})
now := time.Now()
if !context.lastRenderTime.IsZero() {
elapsed := now.Sub(context.lastRenderTime)
io.SetDeltaTime(float32(elapsed.Seconds()))
}
context.lastRenderTime = now
for i := 0; i < len(context.mouseButtonWasDown); i++ {
down := context.mouseButtonWasDown[i] || context.mouseButtonIsDown[i]
io.SetMouseButtonDown(i, down)
context.mouseButtonWasDown[i] = false
}
imgui.NewFrame()
}
// Render must be called at the end of rendering.
func (context *Context) Render(bitmapTextureQuery BitmapTextureQuery) {
imgui.Render()
context.renderDrawData(imgui.RenderedDrawData(), bitmapTextureQuery)
}
func (context *Context) createDeviceObjects(param ContextParameters) (err error) {
gl := context.window.OpenGL()
glslVersion := "#version 150"
vertexShaderSource := glslVersion + `
uniform mat4 ProjMtx;
in vec2 Position;
in vec2 UV;
in vec4 Color;
out vec2 Frag_UV;
out vec4 Frag_Color;
void main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = ProjMtx * vec4(Position.xy,0,1);
}
`
fragmentShaderSource := glslVersion + `
uniform int ImageType;
uniform sampler2D Texture;
uniform sampler2D Palette;
in vec2 Frag_UV;
in vec4 Frag_Color;
out vec4 Out_Color;
void main()
{
if (ImageType == 1)
{
vec4 pixel = texture(Texture, Frag_UV.st);
Out_Color = Frag_Color * texture(Palette, vec2(pixel.r, 0.5));
}
else if (ImageType == 2)
{
Out_Color = texture(Texture, Frag_UV.st) * Frag_Color;
}
else
{
Out_Color = vec4(Frag_Color.rgb, Frag_Color.a * texture( Texture, Frag_UV.st).r);
}
}
`
context.shaderHandle, err = opengl.LinkNewStandardProgram(gl, vertexShaderSource, fragmentShaderSource)
if err != nil {
return
}
context.attribLocationType = gl.GetUniformLocation(context.shaderHandle, "ImageType")
context.attribLocationTex = gl.GetUniformLocation(context.shaderHandle, "Texture")
context.attribLocationPal = gl.GetUniformLocation(context.shaderHandle, "Palette")
context.attribLocationProjMtx = gl.GetUniformLocation(context.shaderHandle, "ProjMtx")
context.attribLocationPosition = gl.GetAttribLocation(context.shaderHandle, "Position")
context.attribLocationUV = gl.GetAttribLocation(context.shaderHandle, "UV")
context.attribLocationColor = gl.GetAttribLocation(context.shaderHandle, "Color")
buffers := gl.GenBuffers(2)
context.vboHandle = buffers[0]
context.elementsHandle = buffers[1]
return context.createFontsTexture(gl, param)
}
func (context *Context) createFontsTexture(gl opengl.OpenGL, param ContextParameters) error {
io := imgui.CurrentIO()
fontAtlas := io.Fonts()
if len(param.FontFile) > 0 {
fontSize := float32(16.0)
if param.FontSize > 0.0 {
fontSize = param.FontSize
}
font := fontAtlas.AddFontFromFileTTF(param.FontFile, fontSize)
if font == imgui.DefaultFont {
return fontFileError{Filename: param.FontFile}
}
}
image := fontAtlas.TextureDataAlpha8()
context.fontTexture = gl.GenTextures(1)[0]
gl.BindTexture(opengl.TEXTURE_2D, context.fontTexture)
gl.TexParameteri(opengl.TEXTURE_2D, opengl.TEXTURE_MIN_FILTER, opengl.LINEAR)
gl.TexParameteri(opengl.TEXTURE_2D, opengl.TEXTURE_MAG_FILTER, opengl.LINEAR)
gl.PixelStorei(opengl.UNPACK_ROW_LENGTH, 0)
gl.TexImage2D(opengl.TEXTURE_2D, 0, opengl.RED, int32(image.Width), int32(image.Height),
0, opengl.RED, opengl.UNSIGNED_BYTE, image.Pixels)
io.Fonts().SetTextureID(TextureIDForSimpleTexture(context.fontTexture))
gl.BindTexture(opengl.TEXTURE_2D, 0)
return nil
}
func (context *Context) destroyDeviceObjects(gl opengl.OpenGL) {
if context.vboHandle != 0 {
gl.DeleteBuffers([]uint32{context.vboHandle})
}
context.vboHandle = 0
if context.elementsHandle != 0 {
gl.DeleteBuffers([]uint32{context.elementsHandle})
}
context.elementsHandle = 0
if context.shaderHandle != 0 {
gl.DeleteProgram(context.shaderHandle)
}
context.shaderHandle = 0
if context.fontTexture != 0 {
gl.DeleteTextures([]uint32{context.fontTexture})
imgui.CurrentIO().Fonts().SetTextureID(0)
context.fontTexture = 0
}
}
func (context *Context) renderDrawData(drawData imgui.DrawData, bitmapTextureQuery BitmapTextureQuery) {
gl := context.window.OpenGL()
displayWidth, displayHeight := context.window.Size()
// Backup GL state
var lastActiveTexture int32
gl.GetIntegerv(opengl.ACTIVE_TEXTURE, &lastActiveTexture)
gl.ActiveTexture(opengl.TEXTURE0)
var lastProgram int32
gl.GetIntegerv(opengl.CURRENT_PROGRAM, &lastProgram)
var lastTexture int32
gl.GetIntegerv(opengl.TEXTURE_BINDING_2D, &lastTexture)
var lastSampler int32
gl.GetIntegerv(opengl.SAMPLER_BINDING, &lastSampler)
var lastArrayBuffer int32
gl.GetIntegerv(opengl.ARRAY_BUFFER_BINDING, &lastArrayBuffer)
var lastElementArrayBuffer int32
gl.GetIntegerv(opengl.ELEMENT_ARRAY_BUFFER_BINDING, &lastElementArrayBuffer)
var lastVertexArray int32
gl.GetIntegerv(opengl.VERTEX_ARRAY_BINDING, &lastVertexArray)
var lastPolygonMode [2]int32
gl.GetIntegerv(opengl.POLYGON_MODE, &lastPolygonMode[0])
var lastViewport [4]int32
gl.GetIntegerv(opengl.VIEWPORT, &lastViewport[0])
var lastScissorBox [4]int32
gl.GetIntegerv(opengl.SCISSOR_BOX, &lastScissorBox[0])
var lastBlendSrcRgb int32
gl.GetIntegerv(opengl.BLEND_SRC_RGB, &lastBlendSrcRgb)
var lastBlendDstRgb int32
gl.GetIntegerv(opengl.BLEND_DST_RGB, &lastBlendDstRgb)
var lastBlendSrcAlpha int32
gl.GetIntegerv(opengl.BLEND_SRC_ALPHA, &lastBlendSrcAlpha)
var lastBlendDstAlpha int32
gl.GetIntegerv(opengl.BLEND_DST_ALPHA, &lastBlendDstAlpha)
var lastBlendEquationRgb int32
gl.GetIntegerv(opengl.BLEND_EQUATION_RGB, &lastBlendEquationRgb)
var lastBlendEquationAlpha int32
gl.GetIntegerv(opengl.BLEND_EQUATION_ALPHA, &lastBlendEquationAlpha)
lastEnableBlend := gl.IsEnabled(opengl.BLEND)
lastEnableCullFace := gl.IsEnabled(opengl.CULL_FACE)
lastEnableDepthTest := gl.IsEnabled(opengl.DEPTH_TEST)
lastEnableScissorTest := gl.IsEnabled(opengl.SCISSOR_TEST)
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
gl.Enable(opengl.BLEND)
gl.BlendEquation(opengl.FUNC_ADD)
gl.BlendFunc(opengl.SRC_ALPHA, opengl.ONE_MINUS_SRC_ALPHA)
gl.Disable(opengl.CULL_FACE)
gl.Disable(opengl.DEPTH_TEST)
gl.Enable(opengl.SCISSOR_TEST)
gl.PolygonMode(opengl.FRONT_AND_BACK, opengl.FILL)
// Setup viewport, orthographic projection matrix
gl.Viewport(0, 0, int32(displayWidth), int32(displayHeight))
orthoProjection := [16]float32{
2.0 / float32(displayWidth), 0.0, 0.0, 0.0,
0.0, 2.0 / float32(-displayHeight), 0.0, 0.0,
0.0, 0.0, -1.0, 0.0,
-1.0, 1.0, 0.0, 1.0,
}
gl.UseProgram(context.shaderHandle)
gl.Uniform1i(context.attribLocationTex, 0)
gl.Uniform1i(context.attribLocationPal, 1)
gl.UniformMatrix4fv(context.attribLocationProjMtx, false, &orthoProjection)
gl.BindSampler(0, 0) // Rely on combined texture/sampler state.
vaoHandle := gl.GenVertexArrays(1)[0]
gl.BindVertexArray(vaoHandle)
gl.BindBuffer(opengl.ARRAY_BUFFER, context.vboHandle)
gl.EnableVertexAttribArray(uint32(context.attribLocationPosition))
gl.EnableVertexAttribArray(uint32(context.attribLocationUV))
gl.EnableVertexAttribArray(uint32(context.attribLocationColor))
vertexSize, vertexOffsetPos, vertexOffsetUv, vertexOffsetCol := imgui.VertexBufferLayout()
gl.VertexAttribOffset(uint32(context.attribLocationPosition), 2, opengl.FLOAT, false, int32(vertexSize), vertexOffsetPos)
gl.VertexAttribOffset(uint32(context.attribLocationUV), 2, opengl.FLOAT, false, int32(vertexSize), vertexOffsetUv)
gl.VertexAttribOffset(uint32(context.attribLocationColor), 4, opengl.UNSIGNED_BYTE, true, int32(vertexSize), vertexOffsetCol)
indexSize := imgui.IndexBufferLayout()
drawType := opengl.UNSIGNED_SHORT
if indexSize == 4 {
drawType = opengl.UNSIGNED_INT
}
// Draw
for _, list := range drawData.CommandLists() {
var indexBufferOffset uintptr
vertexBuffer, vertexBufferSize := list.VertexBuffer()
gl.BindBuffer(opengl.ARRAY_BUFFER, context.vboHandle)
gl.BufferData(opengl.ARRAY_BUFFER, vertexBufferSize, vertexBuffer, opengl.STREAM_DRAW)
indexBuffer, indexBufferSize := list.IndexBuffer()
gl.BindBuffer(opengl.ELEMENT_ARRAY_BUFFER, context.elementsHandle)
gl.BufferData(opengl.ELEMENT_ARRAY_BUFFER, indexBufferSize, indexBuffer, opengl.STREAM_DRAW)
for _, cmd := range list.Commands() {
if cmd.HasUserCallback() {
cmd.CallUserCallback(list)
} else {
textureID := cmd.TextureID()
imageType := ImageTypeFromID(textureID)
gl.Uniform1i(context.attribLocationType, int32(imageType))
switch imageType {
case ImageTypeSimpleTexture:
gl.ActiveTexture(opengl.TEXTURE0 + uint32(0))
gl.BindTexture(opengl.TEXTURE_2D, uint32(textureID))
case ImageTypeColorTexture:
gl.ActiveTexture(opengl.TEXTURE0 + uint32(0))
gl.BindTexture(opengl.TEXTURE_2D, uint32(textureID))
case ImageTypeBitmapTexture:
palette, bitmap := bitmapTextureQuery(textureID)
gl.ActiveTexture(opengl.TEXTURE0 + uint32(0))
gl.BindTexture(opengl.TEXTURE_2D, bitmap)
gl.ActiveTexture(opengl.TEXTURE0 + uint32(1))
gl.BindTexture(opengl.TEXTURE_2D, palette)
default:
gl.ActiveTexture(opengl.TEXTURE0 + uint32(0))
gl.BindTexture(opengl.TEXTURE_2D, 0)
gl.ActiveTexture(opengl.TEXTURE0 + uint32(1))
gl.BindTexture(opengl.TEXTURE_2D, 0)
}
clipRect := cmd.ClipRect()
gl.Scissor(int32(clipRect.X), int32(displayHeight)-int32(clipRect.W), int32(clipRect.Z-clipRect.X), int32(clipRect.W-clipRect.Y))
gl.DrawElements(opengl.TRIANGLES, int32(cmd.ElementCount()), uint32(drawType), indexBufferOffset)
}
indexBufferOffset += uintptr(cmd.ElementCount() * indexSize)
}
}
gl.DeleteVertexArrays([]uint32{vaoHandle})
// Restore modified GL state
gl.UseProgram(uint32(lastProgram))
gl.BindTexture(opengl.TEXTURE_2D, uint32(lastTexture))
gl.BindSampler(0, uint32(lastSampler))
gl.ActiveTexture(uint32(lastActiveTexture))
gl.BindVertexArray(uint32(lastVertexArray))
gl.BindBuffer(opengl.ARRAY_BUFFER, uint32(lastArrayBuffer))
gl.BindBuffer(opengl.ELEMENT_ARRAY_BUFFER, uint32(lastElementArrayBuffer))
gl.BlendEquationSeparate(uint32(lastBlendEquationRgb), uint32(lastBlendEquationAlpha))
gl.BlendFuncSeparate(uint32(lastBlendSrcRgb), uint32(lastBlendDstRgb), uint32(lastBlendSrcAlpha), uint32(lastBlendDstAlpha))
if lastEnableBlend {
gl.Enable(opengl.BLEND)
} else {
gl.Disable(opengl.BLEND)
}
if lastEnableCullFace {
gl.Enable(opengl.CULL_FACE)
} else {
gl.Disable(opengl.CULL_FACE)
}
if lastEnableDepthTest {
gl.Enable(opengl.DEPTH_TEST)
} else {
gl.Disable(opengl.DEPTH_TEST)
}
if lastEnableScissorTest {
gl.Enable(opengl.SCISSOR_TEST)
} else {
gl.Disable(opengl.SCISSOR_TEST)
}
gl.PolygonMode(opengl.FRONT_AND_BACK, uint32(lastPolygonMode[0]))
gl.Viewport(lastViewport[0], lastViewport[1], lastViewport[2], lastViewport[3])
gl.Scissor(lastScissorBox[0], lastScissorBox[1], lastScissorBox[2], lastScissorBox[3])
}
// SetMousePosition must be called to report the current mouse position.
func (context *Context) SetMousePosition(x, y float32) {
imgui.CurrentIO().SetMousePosition(imgui.Vec2{X: x, Y: y})
}
// MouseScroll must be called to report the current scroll delta.
func (context *Context) MouseScroll(dx, dy float32) {
imgui.CurrentIO().AddMouseWheelDelta(dx, dy)
}
+30
View File
@@ -0,0 +1,30 @@
package gui
import "github.com/inkyblackness/imgui-go/v3"
// ImageType is an identifier how an image is to be rendered.
type ImageType byte
const (
// ImageTypeSimpleTexture identifies textures that use the image key as OpenGL texture handle.
ImageTypeSimpleTexture ImageType = 0
// ImageTypeBitmapTexture identifies bitmap textures.
ImageTypeBitmapTexture ImageType = 1
// ImageTypeColorTexture identifies textures with own color information.
ImageTypeColorTexture ImageType = 2
)
// TextureIDForSimpleTexture returns a TextureID with ImageTypeSimpleTexture.
func TextureIDForSimpleTexture(handle uint32) imgui.TextureID {
return imgui.TextureID(ImageTypeSimpleTexture)<<56 | imgui.TextureID(handle)
}
// TextureIDForColorTexture returns a TextureID with ImageTypeSimpleTexture.
func TextureIDForColorTexture(handle uint32) imgui.TextureID {
return imgui.TextureID(ImageTypeColorTexture)<<56 | imgui.TextureID(handle)
}
// ImageTypeFromID returns the image type the given texture ID specifies.
func ImageTypeFromID(id imgui.TextureID) ImageType {
return ImageType(id >> 56)
}
+13
View File
@@ -0,0 +1,13 @@
package gui
// ModalState represents a modal dialog.
type ModalState interface {
// Render renders the dialog.
Render()
}
// FileHandler is accepting filenames.
type FileHandler interface {
// HandleFiles is called for any dropped files.
HandleFiles(names []string)
}
@@ -0,0 +1,6 @@
package gui
// ModalStateMachine allows to set a new state.
type ModalStateMachine interface {
SetState(state ModalState)
}
@@ -0,0 +1,28 @@
package gui
// ModalStateWrapper is a wrapper over a series of modal popup states.
// It implements both ModalState and ModalStateMachine to allow nested states, if necessary.
type ModalStateWrapper struct {
State ModalState
}
// SetState sets the new state.
func (machine *ModalStateWrapper) SetState(state ModalState) {
machine.State = state
}
// Render renders the current state.
func (machine *ModalStateWrapper) Render() {
if machine.State != nil {
machine.State.Render()
}
}
// HandleFiles forwards the given filenames to the current state.
func (machine *ModalStateWrapper) HandleFiles(filenames []string) {
handler, isHandler := machine.State.(FileHandler)
if !isHandler {
return
}
handler.HandleFiles(filenames)
}
+42
View File
@@ -0,0 +1,42 @@
package gui
import (
"github.com/inkyblackness/imgui-go/v3"
)
// StepSliderIntV creates a SliderInt with additional buttons to make single digit steps.
// min and max parameters are inclusive. Returns true on change.
func StepSliderIntV(label string, value *int, min, max int, format string) bool {
imgui.PushID(label)
changed := false
if imgui.Button("-") && (*value > min) {
*value--
changed = true
}
innerSpacing := imgui.CurrentStyle().ItemInnerSpacing()
imgui.SameLineV(0, innerSpacing.X)
if imgui.Button("+") && (*value < max) {
*value++
changed = true
}
imgui.SameLineV(0, innerSpacing.X)
value32 := int32(*value)
if imgui.SliderIntV(label, &value32, int32(min), int32(max), format, imgui.SlidersFlagsNone) {
switch {
case int(value32) < min:
*value = min
case int(value32) > max:
*value = max
default:
*value = int(value32)
}
changed = true
}
imgui.PopID()
return changed
}
// StepSliderInt calls StepSliderIntV(label, value, min, max, "%d").
func StepSliderInt(label string, value *int, min, max int) bool {
return StepSliderIntV(label, value, min, max, "%d")
}
+95
View File
@@ -0,0 +1,95 @@
package input
// Key describes a named key on the keyboard. These are keys which are
// unspecific to layout or language, or are universal.
// Or, described in another way: keys that don't end up as printable characters.
type Key int
// Constants for commonly known named keys.
const (
KeyEnter = Key(300)
KeyEscape = Key(301)
KeyBackspace = Key(302)
KeyTab = Key(303)
KeyDown = Key(310)
KeyLeft = Key(311)
KeyRight = Key(312)
KeyUp = Key(313)
KeyDelete = Key(320)
KeyEnd = Key(321)
KeyHome = Key(322)
KeyInsert = Key(323)
KeyPageDown = Key(324)
KeyPageUp = Key(325)
KeyAlt = Key(330)
KeyControl = Key(331)
KeyShift = Key(332)
KeySuper = Key(333)
KeyPause = Key(340)
KeyPrintScreen = Key(341)
KeyCapsLock = Key(342)
KeyScrollLock = Key(343)
KeyF1 = Key(351)
KeyF10 = Key(360)
KeyF11 = Key(361)
KeyF12 = Key(362)
KeyF13 = Key(363)
KeyF14 = Key(364)
KeyF15 = Key(365)
KeyF16 = Key(366)
KeyF17 = Key(367)
KeyF18 = Key(368)
KeyF19 = Key(369)
KeyF2 = Key(352)
KeyF20 = Key(370)
KeyF21 = Key(371)
KeyF22 = Key(372)
KeyF23 = Key(373)
KeyF24 = Key(374)
KeyF25 = Key(375)
KeyF3 = Key(353)
KeyF4 = Key(354)
KeyF5 = Key(355)
KeyF6 = Key(356)
KeyF7 = Key(357)
KeyF8 = Key(358)
KeyF9 = Key(359)
KeyCopy = Key(380)
KeyCut = Key(381)
KeyPaste = Key(382)
KeyUndo = Key(390)
KeyRedo = Key(391)
KeySave = Key(400)
KeyNew = Key(401)
KeyA = Key(410)
KeyC = Key(411)
KeyV = Key(412)
KeyX = Key(413)
)
var keyToModifier = map[Key]Modifier{
KeyShift: ModShift,
KeyControl: ModControl,
KeyAlt: ModAlt,
KeySuper: ModSuper,
}
// AsModifier returns the modifier equivalent for the key - if applicable.
func (key Key) AsModifier() Modifier {
mod, isModifier := keyToModifier[key]
if !isModifier {
mod = ModNone
}
return mod
}
+34
View File
@@ -0,0 +1,34 @@
package input
// Modifier is a collection of currently pressed modifier keys.
type Modifier uint32
// Constants for common modifier.
const (
ModNone = Modifier(0x00000000)
ModShift = Modifier(0x00000001)
ModControl = Modifier(0x00000002)
ModAlt = Modifier(0x00000004)
ModSuper = Modifier(0x00000008)
)
// IsClear returns true if nothing is set.
func (mod Modifier) IsClear() bool {
return mod == ModNone
}
// With returns a combination of this modifier and the given one.
func (mod Modifier) With(other Modifier) Modifier {
return Modifier(uint32(mod) | uint32(other))
}
// Without returns the result of this modifier without the given one.
func (mod Modifier) Without(other Modifier) Modifier {
return Modifier(uint32(mod) & ^uint32(other))
}
// Has returns true if the given modifier is included in this set.
// This modifier can have more keys set than the requested and still return true.
func (mod Modifier) Has(other Modifier) bool {
return (uint32(mod) | uint32(other)) == uint32(mod)
}
+41
View File
@@ -0,0 +1,41 @@
package input_test
import (
"testing"
"github.com/inkyblackness/hacked/ui/input"
"github.com/stretchr/testify/assert"
)
func TestHasReturnsTrueForThemselves(t *testing.T) {
mods := []input.Modifier{input.ModShift, input.ModControl, input.ModAlt, input.ModSuper}
for _, mod := range mods {
assert.True(t, mod.Has(mod))
}
}
func TestHasReturnsFalseForDifferentModifiers(t *testing.T) {
mods := []input.Modifier{input.ModShift, input.ModControl, input.ModAlt, input.ModSuper}
for index, mod := range mods {
nextMod := mods[(index+1)%len(mods)]
assert.False(t, mod.Has(nextMod))
}
}
func TestHasReturnsFalseForSetsIncludingOthers(t *testing.T) {
mods := []input.Modifier{input.ModShift, input.ModControl, input.ModAlt, input.ModSuper}
for index, mod := range mods {
nextMod := mods[(index+1)%len(mods)]
assert.False(t, mod.Has(mod.With(nextMod)))
}
}
func TestWithoutReturnsReduction(t *testing.T) {
mod := input.ModShift.With(input.ModAlt).Without(input.ModShift)
assert.Equal(t, input.ModAlt, mod)
}
+34
View File
@@ -0,0 +1,34 @@
package input
import "strings"
type shortcut struct {
keyName string
modifier Modifier
key Key
}
var shortcuts = []shortcut{
{keyName: "c", modifier: ModControl, key: KeyCopy},
{keyName: "x", modifier: ModControl, key: KeyCut},
{keyName: "v", modifier: ModControl, key: KeyPaste},
{keyName: "z", modifier: ModControl, key: KeyUndo},
{keyName: "z", modifier: ModControl.With(ModShift), key: KeyRedo},
{keyName: "y", modifier: ModControl, key: KeyRedo},
{keyName: "s", modifier: ModControl, key: KeySave},
{keyName: "n", modifier: ModControl, key: KeyNew},
}
// ResolveShortcut tries to map the given name and modifier combination to a
// known (common) shortcut key. For instance, Ctrl+C is KeyCopy.
func ResolveShortcut(keyName string, modifier Modifier) (key Key, knownKey bool) {
lowercaseName := strings.ToLower(keyName)
for _, entry := range shortcuts {
if (entry.keyName == lowercaseName) && (entry.modifier == modifier) {
knownKey = true
key = entry.key
}
}
return
}
+22
View File
@@ -0,0 +1,22 @@
package input_test
import (
"testing"
"github.com/inkyblackness/hacked/ui/input"
"github.com/stretchr/testify/assert"
)
func TestResolveShortcutReturnsValuesOfKnownCombo(t *testing.T) {
key, knownKey := input.ResolveShortcut("c", input.ModControl)
assert.True(t, knownKey)
assert.Equal(t, input.KeyCopy, key)
}
func TestResolveShortcutReturnsFalseForUnknownCombo(t *testing.T) {
_, knownKey := input.ResolveShortcut("l", input.ModControl.With(input.ModShift))
assert.False(t, knownKey)
}
+101
View File
@@ -0,0 +1,101 @@
package input
// StickyKeyListener is the listener interface for receiving key events.
type StickyKeyListener interface {
// KeyPress is called for a pressed key.
KeyPress(key Key, modifier Modifier)
// KeyRelease is called for a released key.
KeyRelease(key Key, modifier Modifier)
// Modifier is called when the currently active modifier changed.
Modifier(modifier Modifier)
}
// StickyKeyBuffer is a buffer to keep track of several identically named keys.
// Keys can be reported being pressed or released. Their state will be forwarded
// to a StickyKeyListener instance. If a specific key is reported to be pressed
// more than once, the listener will have received the down state only once.
type StickyKeyBuffer struct {
pressedKeys map[Key]int
pressedModifier map[Modifier]int
activeModifier Modifier
listener StickyKeyListener
}
// NewStickyKeyBuffer returns a new instance of a sticky key buffer.
func NewStickyKeyBuffer(listener StickyKeyListener) *StickyKeyBuffer {
buffer := &StickyKeyBuffer{
pressedKeys: make(map[Key]int),
pressedModifier: make(map[Modifier]int),
activeModifier: ModNone,
listener: listener,
}
return buffer
}
// ActiveModifier returns the currently pressed modifier set.
func (buffer *StickyKeyBuffer) ActiveModifier() Modifier {
return buffer.activeModifier
}
// KeyDown registers a pressed key state. Multiple down states can be
// registered for the same key and result in only one key event.
func (buffer *StickyKeyBuffer) KeyDown(key Key, modifier Modifier) {
keyAsModifier := key.AsModifier()
updatedModifier := modifier.With(keyAsModifier)
oldCount := buffer.pressedKeys[key]
buffer.pressedKeys[key] = oldCount + 1
if oldCount == 0 {
buffer.listener.KeyPress(key, updatedModifier)
}
buffer.setActiveModifier(updatedModifier)
if keyAsModifier != ModNone {
buffer.pressedModifier[keyAsModifier]++
}
}
// KeyUp registers a released key state. Multiple up states can be registered
// for the same key, as long as enough down states were reported.
func (buffer *StickyKeyBuffer) KeyUp(key Key, modifier Modifier) {
updatedModifier := modifier
keyAsModifier := key.AsModifier()
if keyAsModifier != ModNone {
oldCount := buffer.pressedModifier[keyAsModifier]
if oldCount > 0 {
buffer.pressedModifier[keyAsModifier] = oldCount - 1
if oldCount == 1 {
updatedModifier = updatedModifier.Without(keyAsModifier)
}
}
}
oldCount := buffer.pressedKeys[key]
if oldCount > 0 {
buffer.pressedKeys[key] = oldCount - 1
if oldCount == 1 {
buffer.listener.KeyRelease(key, updatedModifier)
}
}
buffer.setActiveModifier(updatedModifier)
}
// ReleaseAll notifies the listener of the reset of all modifiers.
// Key states are reset to accept new down states.
func (buffer *StickyKeyBuffer) ReleaseAll() {
buffer.pressedKeys = make(map[Key]int)
buffer.setActiveModifier(ModNone)
}
func (buffer *StickyKeyBuffer) setActiveModifier(modifier Modifier) {
if buffer.activeModifier != modifier {
for mod := range buffer.pressedModifier {
if !modifier.Has(mod) {
buffer.pressedModifier[mod] = 0
}
}
buffer.activeModifier = modifier
buffer.listener.Modifier(buffer.activeModifier)
}
}
@@ -0,0 +1,261 @@
package input_test
import (
"testing"
"github.com/inkyblackness/hacked/ui/input"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
type keyEvent struct {
event string
key input.Key
mod input.Modifier
}
type testingStickyKeysListener struct {
eventMap map[input.Key][]keyEvent
events []keyEvent
}
func (listener *testingStickyKeysListener) KeyPress(key input.Key, modifier input.Modifier) {
listener.addEvent(keyEvent{event: "P", key: key, mod: modifier})
}
func (listener *testingStickyKeysListener) KeyRelease(key input.Key, modifier input.Modifier) {
listener.addEvent(keyEvent{event: "R", key: key, mod: modifier})
}
func (listener *testingStickyKeysListener) Modifier(modifier input.Modifier) {
listener.addEvent(keyEvent{event: "M", key: 0, mod: modifier})
}
func (listener *testingStickyKeysListener) addEvent(event keyEvent) {
listener.eventMap[event.key] = append(listener.eventMap[event.key], event)
listener.events = append(listener.events, event)
}
type StickyKeyBufferSuite struct {
suite.Suite
buffer *input.StickyKeyBuffer
listener *testingStickyKeysListener
}
func TestProviderBackedStoreSuite(t *testing.T) {
suite.Run(t, new(StickyKeyBufferSuite))
}
func (suite *StickyKeyBufferSuite) SetupTest() {
suite.listener = &testingStickyKeysListener{
eventMap: make(map[input.Key][]keyEvent)}
suite.buffer = input.NewStickyKeyBuffer(suite.listener)
}
func (suite *StickyKeyBufferSuite) TestRegularEventsAreForwarded_A() {
suite.buffer.KeyDown(input.KeyF1, input.ModNone)
suite.buffer.KeyUp(input.KeyF1, input.ModNone)
suite.buffer.KeyDown(input.KeyF2, input.ModNone)
suite.buffer.KeyUp(input.KeyF2, input.ModNone)
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyF1, mod: input.ModNone},
{event: "R", key: input.KeyF1, mod: input.ModNone},
}, suite.listener.eventMap[input.KeyF1])
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyF2, mod: input.ModNone},
{event: "R", key: input.KeyF2, mod: input.ModNone},
}, suite.listener.eventMap[input.KeyF2])
}
func (suite *StickyKeyBufferSuite) TestRegularEventsAreForwarded_B() {
suite.buffer.KeyDown(input.KeyF1, input.ModNone)
suite.buffer.KeyDown(input.KeyF2, input.ModNone)
suite.buffer.KeyUp(input.KeyF2, input.ModNone)
suite.buffer.KeyUp(input.KeyF1, input.ModNone)
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyF1, mod: input.ModNone},
{event: "R", key: input.KeyF1, mod: input.ModNone},
}, suite.listener.eventMap[input.KeyF1])
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyF2, mod: input.ModNone},
{event: "R", key: input.KeyF2, mod: input.ModNone},
}, suite.listener.eventMap[input.KeyF2])
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyF1, mod: input.ModNone},
{event: "P", key: input.KeyF2, mod: input.ModNone},
{event: "R", key: input.KeyF2, mod: input.ModNone},
{event: "R", key: input.KeyF1, mod: input.ModNone},
}, suite.listener.events)
}
func (suite *StickyKeyBufferSuite) TestIdenticalKeysAreReportedOnlyOnce() {
suite.buffer.KeyDown(input.KeyF1, input.ModNone)
suite.buffer.KeyDown(input.KeyF1, input.ModNone)
suite.buffer.KeyDown(input.KeyF1, input.ModNone)
suite.buffer.KeyUp(input.KeyF1, input.ModNone)
suite.buffer.KeyUp(input.KeyF1, input.ModNone)
suite.buffer.KeyUp(input.KeyF1, input.ModNone)
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyF1, mod: input.ModNone},
{event: "R", key: input.KeyF1, mod: input.ModNone},
}, suite.listener.eventMap[input.KeyF1])
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyF1, mod: input.ModNone},
{event: "R", key: input.KeyF1, mod: input.ModNone},
}, suite.listener.events)
}
func (suite *StickyKeyBufferSuite) TestSuperfluousReleasesAreIgnored() {
suite.buffer.KeyDown(input.KeyF1, input.ModNone)
suite.buffer.KeyUp(input.KeyF1, input.ModNone)
suite.buffer.KeyUp(input.KeyF1, input.ModNone)
suite.buffer.KeyUp(input.KeyF1, input.ModNone)
suite.buffer.KeyDown(input.KeyF2, input.ModNone)
suite.buffer.KeyDown(input.KeyF1, input.ModNone)
suite.buffer.KeyUp(input.KeyF1, input.ModNone)
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyF1, mod: input.ModNone},
{event: "R", key: input.KeyF1, mod: input.ModNone},
{event: "P", key: input.KeyF1, mod: input.ModNone},
{event: "R", key: input.KeyF1, mod: input.ModNone},
}, suite.listener.eventMap[input.KeyF1])
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyF1, mod: input.ModNone},
{event: "R", key: input.KeyF1, mod: input.ModNone},
{event: "P", key: input.KeyF2, mod: input.ModNone},
{event: "P", key: input.KeyF1, mod: input.ModNone},
{event: "R", key: input.KeyF1, mod: input.ModNone},
}, suite.listener.events)
}
func (suite *StickyKeyBufferSuite) TestReleaseAllResetsRegularKeys() {
suite.buffer.KeyDown(input.KeyEnter, input.ModNone)
suite.buffer.KeyDown(input.KeyEnter, input.ModNone)
suite.buffer.KeyDown(input.KeyTab, input.ModNone)
suite.buffer.ReleaseAll()
suite.buffer.KeyDown(input.KeyTab, input.ModNone)
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyEnter, mod: input.ModNone},
}, suite.listener.eventMap[input.KeyEnter])
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyTab, mod: input.ModNone},
{event: "P", key: input.KeyTab, mod: input.ModNone},
}, suite.listener.eventMap[input.KeyTab])
}
func (suite *StickyKeyBufferSuite) TestModifierEventsAreConvertedToModifierStates_DownAugmented() {
suite.buffer.KeyDown(input.KeyShift, input.ModShift)
suite.buffer.KeyDown(input.KeyAlt, input.ModShift.With(input.ModAlt))
suite.buffer.KeyUp(input.KeyAlt, input.ModShift)
suite.buffer.KeyUp(input.KeyShift, input.ModNone)
assert.Equal(suite.T(), 2, len(suite.listener.eventMap[input.KeyShift]))
assert.Equal(suite.T(), 2, len(suite.listener.eventMap[input.KeyAlt]))
assert.Equal(suite.T(),
[]keyEvent{
{event: "P", key: input.KeyShift, mod: input.ModShift},
{event: "M", key: 0, mod: input.ModShift},
{event: "P", key: input.KeyAlt, mod: input.ModShift.With(input.ModAlt)},
{event: "M", key: 0, mod: input.ModShift.With(input.ModAlt)},
{event: "R", key: input.KeyAlt, mod: input.ModShift},
{event: "M", key: 0, mod: input.ModShift},
{event: "R", key: input.KeyShift, mod: input.ModNone},
{event: "M", key: 0, mod: input.ModNone},
},
suite.listener.events)
}
func (suite *StickyKeyBufferSuite) TestModifierEventsAreConvertedToModifierStates_UpAugmented() {
suite.buffer.KeyDown(input.KeyShift, input.ModNone)
suite.buffer.KeyDown(input.KeyAlt, input.ModShift)
suite.buffer.KeyUp(input.KeyAlt, input.ModShift.With(input.ModAlt))
suite.buffer.KeyUp(input.KeyShift, input.ModShift)
assert.Equal(suite.T(), 2, len(suite.listener.eventMap[input.KeyShift]))
assert.Equal(suite.T(), 2, len(suite.listener.eventMap[input.KeyAlt]))
assert.Equal(suite.T(),
[]keyEvent{
{event: "P", key: input.KeyShift, mod: input.ModShift},
{event: "M", key: 0, mod: input.ModShift},
{event: "P", key: input.KeyAlt, mod: input.ModShift.With(input.ModAlt)},
{event: "M", key: 0, mod: input.ModShift.With(input.ModAlt)},
{event: "R", key: input.KeyAlt, mod: input.ModShift},
{event: "M", key: 0, mod: input.ModShift},
{event: "R", key: input.KeyShift, mod: input.ModNone},
{event: "M", key: 0, mod: input.ModNone},
},
suite.listener.events)
}
func (suite *StickyKeyBufferSuite) TestReleaseAllReleasesModifiers() {
suite.buffer.KeyDown(input.KeyShift, input.ModNone)
suite.buffer.KeyDown(input.KeyShift, input.ModNone)
suite.buffer.KeyDown(input.KeyAlt, input.ModShift)
suite.buffer.ReleaseAll()
assert.Equal(suite.T(),
[]keyEvent{
{event: "P", key: input.KeyShift, mod: input.ModShift},
{event: "M", key: 0, mod: input.ModShift},
{event: "P", key: input.KeyAlt, mod: input.ModShift.With(input.ModAlt)},
{event: "M", key: 0, mod: input.ModShift.With(input.ModAlt)},
{event: "M", key: 0, mod: input.ModNone},
},
suite.listener.events)
}
func (suite *StickyKeyBufferSuite) TestActiveModifierReturnsCurrentModifier() {
suite.buffer.KeyDown(input.KeyShift, input.ModNone)
suite.buffer.KeyDown(input.KeyControl, input.ModShift)
suite.buffer.KeyDown(input.KeyAlt, input.ModShift.With(input.ModControl))
suite.buffer.KeyUp(input.KeyControl, input.ModShift.With(input.ModControl).With(input.ModAlt))
assert.Equal(suite.T(), input.ModShift.With(input.ModAlt), suite.buffer.ActiveModifier())
}
func (suite *StickyKeyBufferSuite) TestModifierAreUpdatedOnOtherKeys_A() {
suite.buffer.KeyDown(input.KeyShift, input.ModShift)
suite.buffer.KeyDown(input.KeyTab, input.ModNone)
assert.Equal(suite.T(),
[]keyEvent{
{event: "P", key: input.KeyShift, mod: input.ModShift},
{event: "M", key: 0, mod: input.ModShift},
{event: "P", key: input.KeyTab, mod: input.ModNone},
{event: "M", key: 0, mod: input.ModNone},
},
suite.listener.events)
}
func (suite *StickyKeyBufferSuite) TestModifierAreUpdatedOnOtherKeys_B() {
suite.buffer.KeyDown(input.KeyTab, input.ModNone)
suite.buffer.KeyDown(input.KeyShift, input.ModShift)
suite.buffer.KeyUp(input.KeyTab, input.ModNone)
assert.Equal(suite.T(),
[]keyEvent{
{event: "P", key: input.KeyTab, mod: input.ModNone},
{event: "P", key: input.KeyShift, mod: input.ModShift},
{event: "M", key: 0, mod: input.ModShift},
{event: "R", key: input.KeyTab, mod: input.ModNone},
{event: "M", key: 0, mod: input.ModNone},
},
suite.listener.events)
}
func (suite *StickyKeyBufferSuite) TestModifierAreUpdatedOnOtherKeysWithoutThemselves() {
suite.buffer.KeyDown(input.KeyShift, input.ModShift)
suite.buffer.KeyDown(input.KeyShift, input.ModShift)
suite.buffer.KeyUp(input.KeyShift, input.ModShift)
assert.Equal(suite.T(), []keyEvent{
{event: "P", key: input.KeyShift, mod: input.ModShift},
{event: "M", key: 0, mod: input.ModShift},
}, suite.listener.events)
}
+8
View File
@@ -0,0 +1,8 @@
package input
const (
// MousePrimary specifies the primary mouse button (by default, the left one).
MousePrimary = uint32(1)
// MouseSecondary specifies the secondary mouse button (by default, the right one).
MouseSecondary = uint32(2)
)
+73
View File
@@ -0,0 +1,73 @@
package native
import (
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/inkyblackness/hacked/ui/input"
)
var keyMap = map[glfw.Key]input.Key{
glfw.KeyEnter: input.KeyEnter,
glfw.KeyKPEnter: input.KeyEnter,
glfw.KeyEscape: input.KeyEscape,
glfw.KeyBackspace: input.KeyBackspace,
glfw.KeyTab: input.KeyTab,
glfw.KeyDown: input.KeyDown,
glfw.KeyLeft: input.KeyLeft,
glfw.KeyRight: input.KeyRight,
glfw.KeyUp: input.KeyUp,
glfw.KeyDelete: input.KeyDelete,
glfw.KeyEnd: input.KeyEnd,
glfw.KeyHome: input.KeyHome,
glfw.KeyInsert: input.KeyInsert,
glfw.KeyPageDown: input.KeyPageDown,
glfw.KeyPageUp: input.KeyPageUp,
glfw.KeyLeftAlt: input.KeyAlt,
glfw.KeyLeftControl: input.KeyControl,
glfw.KeyLeftShift: input.KeyShift,
glfw.KeyLeftSuper: input.KeySuper,
glfw.KeyRightAlt: input.KeyAlt,
glfw.KeyRightControl: input.KeyControl,
glfw.KeyRightShift: input.KeyShift,
glfw.KeyRightSuper: input.KeySuper,
glfw.KeyPause: input.KeyPause,
glfw.KeyPrintScreen: input.KeyPrintScreen,
glfw.KeyCapsLock: input.KeyCapsLock,
glfw.KeyScrollLock: input.KeyScrollLock,
glfw.KeyF1: input.KeyF1,
glfw.KeyF10: input.KeyF10,
glfw.KeyF11: input.KeyF11,
glfw.KeyF12: input.KeyF12,
glfw.KeyF13: input.KeyF13,
glfw.KeyF14: input.KeyF14,
glfw.KeyF15: input.KeyF15,
glfw.KeyF16: input.KeyF16,
glfw.KeyF17: input.KeyF17,
glfw.KeyF18: input.KeyF18,
glfw.KeyF19: input.KeyF19,
glfw.KeyF2: input.KeyF2,
glfw.KeyF20: input.KeyF20,
glfw.KeyF21: input.KeyF21,
glfw.KeyF22: input.KeyF22,
glfw.KeyF23: input.KeyF23,
glfw.KeyF24: input.KeyF24,
glfw.KeyF25: input.KeyF25,
glfw.KeyF3: input.KeyF3,
glfw.KeyF4: input.KeyF4,
glfw.KeyF5: input.KeyF5,
glfw.KeyF6: input.KeyF6,
glfw.KeyF7: input.KeyF7,
glfw.KeyF8: input.KeyF8,
glfw.KeyF9: input.KeyF9,
glfw.KeyA: input.KeyA,
glfw.KeyC: input.KeyC,
glfw.KeyV: input.KeyV,
glfw.KeyX: input.KeyX,
}
+378
View File
@@ -0,0 +1,378 @@
package native
import (
"strings"
"unsafe"
"github.com/go-gl/gl/v3.2-core/gl"
)
// OpenGL wraps the native GL API into a common interface.
type OpenGL struct {
}
// NewOpenGL initializes the Gl bindings and returns an OpenGL instance.
func NewOpenGL() *OpenGL {
opengl := &OpenGL{}
if err := gl.Init(); err != nil {
panic(err)
}
return opengl
}
// ActiveTexture implements the opengl.OpenGL interface.
func (native *OpenGL) ActiveTexture(texture uint32) {
gl.ActiveTexture(texture)
}
// AttachShader implements the opengl.OpenGL interface.
func (native *OpenGL) AttachShader(program uint32, shader uint32) {
gl.AttachShader(program, shader)
}
// BindAttribLocation implements the opengl.OpenGL interface.
func (native *OpenGL) BindAttribLocation(program uint32, index uint32, name string) {
gl.BindAttribLocation(program, index, gl.Str(name+"\x00"))
}
// BindBuffer implements the opengl.OpenGL interface.
func (native *OpenGL) BindBuffer(target uint32, buffer uint32) {
gl.BindBuffer(target, buffer)
}
// BindFramebuffer implements the opengl.OpenGL interface.
func (native *OpenGL) BindFramebuffer(target uint32, buffer uint32) {
gl.BindFramebuffer(target, buffer)
}
// BindRenderbuffer implements the opengl.OpenGL interface.
func (native *OpenGL) BindRenderbuffer(target uint32, buffer uint32) {
gl.BindRenderbuffer(target, buffer)
}
// BindSampler implements the opengl.OpenGL interface.
func (native *OpenGL) BindSampler(unit uint32, sampler uint32) {
gl.BindSampler(unit, sampler)
}
// BindTexture implements the opengl.OpenGL interface.
func (native *OpenGL) BindTexture(target uint32, texture uint32) {
gl.BindTexture(target, texture)
}
// BindVertexArray implements the opengl.OpenGL interface.
func (native *OpenGL) BindVertexArray(array uint32) {
gl.BindVertexArray(array)
}
// BlendEquation implements the opengl.OpenGL interface.
func (native *OpenGL) BlendEquation(mode uint32) {
gl.BlendEquation(mode)
}
// BlendEquationSeparate implements the opengl.OpenGL interface.
func (native *OpenGL) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
gl.BlendEquationSeparate(modeRGB, modeAlpha)
}
// BlendFunc implements the opengl.OpenGL interface.
func (native *OpenGL) BlendFunc(sfactor uint32, dfactor uint32) {
gl.BlendFunc(sfactor, dfactor)
}
// BlendFuncSeparate implements the opengl.OpenGL interface.
func (native *OpenGL) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
gl.BlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha)
}
// BufferData implements the opengl.OpenGL interface.
func (native *OpenGL) BufferData(target uint32, size int, data interface{}, usage uint32) {
dataPtr, isPtr := data.(unsafe.Pointer)
if isPtr {
gl.BufferData(target, size, dataPtr, usage)
} else {
gl.BufferData(target, size, gl.Ptr(data), usage)
}
}
// CheckFramebufferStatus implements the opengl.OpenGL interface.
func (native *OpenGL) CheckFramebufferStatus(target uint32) uint32 {
return gl.CheckFramebufferStatus(target)
}
// Clear implements the opengl.OpenGL interface.
func (native *OpenGL) Clear(mask uint32) {
gl.Clear(mask)
}
// ClearColor implements the opengl.OpenGL interface.
func (native *OpenGL) ClearColor(red float32, green float32, blue float32, alpha float32) {
gl.ClearColor(red, green, blue, alpha)
}
// CompileShader implements the opengl.OpenGL interface.
func (native *OpenGL) CompileShader(shader uint32) {
gl.CompileShader(shader)
}
// CreateProgram implements the opengl.OpenGL interface.
func (native *OpenGL) CreateProgram() uint32 {
return gl.CreateProgram()
}
// CreateShader implements the opengl.OpenGL interface.
func (native *OpenGL) CreateShader(shaderType uint32) uint32 {
return gl.CreateShader(shaderType)
}
// DeleteBuffers implements the opengl.OpenGL interface.
func (native *OpenGL) DeleteBuffers(buffers []uint32) {
gl.DeleteBuffers(int32(len(buffers)), &buffers[0])
}
// DeleteFramebuffers implements the opengl.OpenGL interface.
func (native *OpenGL) DeleteFramebuffers(buffers []uint32) {
gl.DeleteFramebuffers(int32(len(buffers)), &buffers[0])
}
// DeleteProgram implements the opengl.OpenGL interface.
func (native *OpenGL) DeleteProgram(program uint32) {
gl.DeleteProgram(program)
}
// DeleteShader implements the opengl.OpenGL interface.
func (native *OpenGL) DeleteShader(shader uint32) {
gl.DeleteShader(shader)
}
// DeleteTextures implements the opengl.OpenGL interface.
func (native *OpenGL) DeleteTextures(textures []uint32) {
gl.DeleteTextures(int32(len(textures)), &textures[0])
}
// DeleteVertexArrays implements the opengl.OpenGL interface.
func (native *OpenGL) DeleteVertexArrays(arrays []uint32) {
gl.DeleteVertexArrays(int32(len(arrays)), &arrays[0])
}
// Disable implements the opengl.OpenGL interface.
func (native *OpenGL) Disable(capability uint32) {
gl.Disable(capability)
}
// DrawArrays implements the opengl.OpenGL interface.
func (native *OpenGL) DrawArrays(mode uint32, first int32, count int32) {
gl.DrawArrays(mode, first, count)
}
// DrawBuffers implements the opengl.OpenGL interface.
func (native *OpenGL) DrawBuffers(buffers []uint32) {
gl.DrawBuffers(int32(len(buffers)), &buffers[0])
}
// DrawElements implements the opengl.OpenGL interface.
func (native *OpenGL) DrawElements(mode uint32, count int32, elementType uint32, indices uintptr) {
gl.DrawElements(mode, count, elementType, unsafe.Pointer(indices)) // nolint: govet,gas
}
// Enable implements the opengl.OpenGL interface.
func (native *OpenGL) Enable(capability uint32) {
gl.Enable(capability)
}
// EnableVertexAttribArray implements the opengl.OpenGL interface.
func (native *OpenGL) EnableVertexAttribArray(index uint32) {
gl.EnableVertexAttribArray(index)
}
// FramebufferRenderbuffer implements the opengl.OpenGL interface.
func (native *OpenGL) FramebufferRenderbuffer(target uint32, attachment uint32, renderbuffertarget uint32, renderbuffer uint32) {
gl.FramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer)
}
// FramebufferTexture implements the opengl.OpenGL interface.
func (native *OpenGL) FramebufferTexture(target uint32, attachment uint32, texture uint32, level int32) {
gl.FramebufferTexture(target, attachment, texture, level)
}
// GenerateMipmap implements the opengl.OpenGL interface.
func (native *OpenGL) GenerateMipmap(target uint32) {
gl.GenerateMipmap(target)
}
// GenBuffers implements the opengl.OpenGL interface.
func (native *OpenGL) GenBuffers(n int32) []uint32 {
buffers := make([]uint32, n)
gl.GenBuffers(n, &buffers[0])
return buffers
}
// GenFramebuffers implements the opengl.OpenGL interface.
func (native *OpenGL) GenFramebuffers(n int32) []uint32 {
buffers := make([]uint32, n)
gl.GenFramebuffers(n, &buffers[0])
return buffers
}
// GenRenderbuffers implements the opengl.OpenGL interface.
func (native *OpenGL) GenRenderbuffers(n int32) []uint32 {
buffers := make([]uint32, n)
gl.GenRenderbuffers(n, &buffers[0])
return buffers
}
// GenTextures implements the opengl.OpenGL interface.
func (native *OpenGL) GenTextures(n int32) []uint32 {
ids := make([]uint32, n)
gl.GenTextures(n, &ids[0])
return ids
}
// GenVertexArrays implements the opengl.OpenGL interface.
func (native *OpenGL) GenVertexArrays(n int32) []uint32 {
ids := make([]uint32, n)
gl.GenVertexArrays(n, &ids[0])
return ids
}
// GetAttribLocation implements the opengl.OpenGL interface.
func (native *OpenGL) GetAttribLocation(program uint32, name string) int32 {
return gl.GetAttribLocation(program, gl.Str(name+"\x00"))
}
// GetError implements the opengl.OpenGL interface.
func (native *OpenGL) GetError() uint32 {
return gl.GetError()
}
// GetIntegerv implements the opengl.OpenGL interface.
func (native *OpenGL) GetIntegerv(name uint32, data *int32) {
gl.GetIntegerv(name, data)
}
// GetProgramInfoLog implements the opengl.OpenGL interface.
func (native *OpenGL) GetProgramInfoLog(program uint32) string {
logLength := native.GetProgramParameter(program, gl.INFO_LOG_LENGTH)
log := strings.Repeat("\x00", int(logLength+1))
gl.GetProgramInfoLog(program, logLength, nil, gl.Str(log))
return log
}
// GetProgramParameter implements the opengl.OpenGL interface.
func (native *OpenGL) GetProgramParameter(program uint32, param uint32) int32 {
result := int32(0)
gl.GetProgramiv(program, param, &result)
return result
}
// GetShaderInfoLog implements the opengl.OpenGL interface.
func (native *OpenGL) GetShaderInfoLog(shader uint32) string {
logLength := native.GetShaderParameter(shader, gl.INFO_LOG_LENGTH)
log := strings.Repeat("\x00", int(logLength+1))
gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
return log
}
// GetShaderParameter implements the opengl.OpenGL interface.
func (native *OpenGL) GetShaderParameter(shader uint32, param uint32) int32 {
result := int32(0)
gl.GetShaderiv(shader, param, &result)
return result
}
// GetUniformLocation implements the opengl.OpenGL interface.
func (native *OpenGL) GetUniformLocation(program uint32, name string) int32 {
return gl.GetUniformLocation(program, gl.Str(name+"\x00"))
}
// IsEnabled implements the OpenGL interface.
func (native *OpenGL) IsEnabled(capability uint32) bool {
return gl.IsEnabled(capability)
}
// LinkProgram implements the opengl.OpenGL interface.
func (native *OpenGL) LinkProgram(program uint32) {
gl.LinkProgram(program)
}
// PixelStorei implements the OpenGL interface.
func (native *OpenGL) PixelStorei(name uint32, param int32) {
gl.PixelStorei(name, param)
}
// PolygonMode implements the opengl.OpenGL interface.
func (native *OpenGL) PolygonMode(face uint32, mode uint32) {
gl.PolygonMode(face, mode)
}
// ReadPixels implements the opengl.OpenGL interface.
func (native *OpenGL) ReadPixels(x int32, y int32, width int32, height int32, format uint32, pixelType uint32, pixels interface{}) {
gl.ReadPixels(x, y, width, height, format, pixelType, gl.Ptr(pixels))
}
// RenderbufferStorage implements the opengl.OpenGL interface.
func (native *OpenGL) RenderbufferStorage(target uint32, internalFormat uint32, width int32, height int32) {
gl.RenderbufferStorage(target, internalFormat, width, height)
}
// Scissor implements the opengl.OpenGL interface.
func (native *OpenGL) Scissor(x, y int32, width, height int32) {
gl.Scissor(x, y, width, height)
}
// ShaderSource implements the opengl.OpenGL interface.
func (native *OpenGL) ShaderSource(shader uint32, source string) {
csources, free := gl.Strs(source + "\x00")
defer free()
gl.ShaderSource(shader, 1, csources, nil)
}
// TexImage2D implements the opengl.OpenGL interface.
func (native *OpenGL) TexImage2D(target uint32, level int32, internalFormat uint32, width int32, height int32,
border int32, format uint32, xtype uint32, pixels interface{}) {
ptr, isPointer := pixels.(unsafe.Pointer)
if isPointer {
gl.TexImage2D(target, level, int32(internalFormat), width, height, border, format, xtype, ptr)
} else {
gl.TexImage2D(target, level, int32(internalFormat), width, height, border, format, xtype, gl.Ptr(pixels))
}
}
// TexParameteri implements the opengl.OpenGL interface.
func (native *OpenGL) TexParameteri(target uint32, pname uint32, param int32) {
gl.TexParameteri(target, pname, param)
}
// Uniform1i implements the opengl.OpenGL interface.
func (native *OpenGL) Uniform1i(location int32, value int32) {
gl.Uniform1i(location, value)
}
// Uniform4fv implements the opengl.OpenGL interface.
func (native *OpenGL) Uniform4fv(location int32, value *[4]float32) {
gl.Uniform4fv(location, 1, &value[0])
}
// UniformMatrix4fv implements the opengl.OpenGL interface.
func (native *OpenGL) UniformMatrix4fv(location int32, transpose bool, value *[16]float32) {
count := int32(1)
gl.UniformMatrix4fv(location, count, transpose, &value[0])
}
// UseProgram implements the opengl.OpenGL interface.
func (native *OpenGL) UseProgram(program uint32) {
gl.UseProgram(program)
}
// VertexAttribOffset implements the opengl.OpenGL interface.
func (native *OpenGL) VertexAttribOffset(index uint32, size int32, attribType uint32, normalized bool, stride int32, offset int) {
gl.VertexAttribPointer(index, size, attribType, normalized, stride, gl.PtrOffset(offset))
}
// Viewport implements the opengl.OpenGL interface.
func (native *OpenGL) Viewport(x int32, y int32, width int32, height int32) {
gl.Viewport(x, y, width, height)
}
+288
View File
@@ -0,0 +1,288 @@
package native
import (
"time"
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/inkyblackness/hacked/ui/input"
"github.com/inkyblackness/hacked/ui/opengl"
)
var buttonsByIndex = map[glfw.MouseButton]uint32{
glfw.MouseButton1: input.MousePrimary,
glfw.MouseButton2: input.MouseSecondary}
// OpenGLWindow represents a native OpenGL surface.
type OpenGLWindow struct {
opengl.WindowEventDispatcher
keyBuffer *input.StickyKeyBuffer
glfwWindow *glfw.Window
glWrapper *OpenGL
titleBase string
titleSuffix string
projectModified bool
framesPerSecond float64
frameTime time.Duration
nextRenderTick time.Time
}
// NewOpenGLWindow tries to initialize the OpenGL environment and returns a
// new window instance.
func NewOpenGLWindow(title string, framesPerSecond float64) (*OpenGLWindow, error) {
err := glfw.Init()
if err != nil {
return nil, err
}
glfw.WindowHint(glfw.Resizable, 1)
glfw.WindowHint(glfw.Decorated, 1)
glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI)
glfw.WindowHint(glfw.ContextVersionMajor, 3)
glfw.WindowHint(glfw.ContextVersionMinor, 2)
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
glfw.WindowHint(glfw.DoubleBuffer, glfw.True)
glfwWindow, err := glfw.CreateWindow(1280, 720, title, nil, nil)
if err != nil {
return nil, err
}
glfwWindow.MakeContextCurrent()
glfw.SwapInterval(0)
window := &OpenGLWindow{
WindowEventDispatcher: opengl.NullWindowEventDispatcher(),
glfwWindow: glfwWindow,
glWrapper: NewOpenGL(),
titleBase: title,
framesPerSecond: framesPerSecond,
frameTime: time.Duration(int64(float64(time.Second) / framesPerSecond)),
nextRenderTick: time.Now()}
window.keyBuffer = input.NewStickyKeyBuffer(window.StickyKeyListener())
glfwWindow.SetCursorPosCallback(window.onCursorPos)
glfwWindow.SetMouseButtonCallback(window.onMouseButton)
glfwWindow.SetScrollCallback(window.onMouseScroll)
glfwWindow.SetFramebufferSizeCallback(window.onFramebufferResize)
glfwWindow.SetKeyCallback(window.onKey)
glfwWindow.SetCharCallback(window.onChar)
glfwWindow.SetDropCallback(window.onDrop)
glfwWindow.SetCloseCallback(window.onClosing)
return window, nil
}
// ShouldClose returns true if the user requested the window to close.
func (window *OpenGLWindow) ShouldClose() bool {
return window.glfwWindow.ShouldClose()
}
// Close closes the window and releases its resources.
func (window *OpenGLWindow) Close() {
window.CallClosed()
window.glfwWindow.Destroy()
glfw.Terminate()
}
// StateSnapshot returns the current state of the window.
func (window OpenGLWindow) StateSnapshot() opengl.WindowState {
var state opengl.WindowState
if window.glfwWindow.GetAttrib(glfw.Maximized) != 0 {
state.Maximized = true
// restore window to retrieve restored details, otherwise the maximized details will be read.
_ = window.glfwWindow.Restore()
}
state.Left, state.Top = window.glfwWindow.GetPos()
state.Width, state.Height = window.glfwWindow.GetSize()
if state.Maximized {
_ = window.glfwWindow.Maximize()
}
return state
}
// RestoreState puts the window into the same state when StateSnapshot() was called.
func (window OpenGLWindow) RestoreState(state opengl.WindowState) {
_ = window.glfwWindow.Restore()
window.glfwWindow.SetPos(state.Left, state.Top)
window.glfwWindow.SetSize(state.Width, state.Height)
if state.Maximized {
_ = window.glfwWindow.Maximize()
}
}
// ClipboardString returns the current value of the clipboard, if it is compatible with UTF-8.
func (window OpenGLWindow) ClipboardString() (string, error) {
return window.glfwWindow.GetClipboardString()
}
// SetClipboardString sets the current value of the clipboard as UTF-8 string.
func (window OpenGLWindow) SetClipboardString(value string) {
window.glfwWindow.SetClipboardString(value)
}
// Update must be called from within the main thread as often as possible.
func (window *OpenGLWindow) Update() {
glfw.PollEvents()
now := time.Now()
delta := now.Sub(window.nextRenderTick)
if delta.Nanoseconds() < 0 {
// detected a change of wallclock time into the past; realign
delta = window.frameTime
window.nextRenderTick = now
}
if delta.Nanoseconds() >= window.frameTime.Nanoseconds() {
window.glfwWindow.MakeContextCurrent()
window.CallRender()
window.glfwWindow.SwapBuffers()
framesCovered := delta.Nanoseconds() / window.frameTime.Nanoseconds()
window.nextRenderTick = window.nextRenderTick.Add(time.Duration(framesCovered * window.frameTime.Nanoseconds()))
}
}
// OpenGL returns the OpenGL API.
func (window *OpenGLWindow) OpenGL() opengl.OpenGL {
return window.glWrapper
}
// Size returns the dimension of the frame buffer of this window.
func (window *OpenGLWindow) Size() (width int, height int) {
return window.glfwWindow.GetFramebufferSize()
}
// SetCursorVisible toggles the visibility of the cursor.
func (window *OpenGLWindow) SetCursorVisible(visible bool) {
if visible {
window.glfwWindow.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
} else {
window.glfwWindow.SetInputMode(glfw.CursorMode, glfw.CursorHidden)
}
}
// SetFullScreen toggles the windowed mode.
func (window *OpenGLWindow) SetFullScreen(on bool) {
if on {
monitor := glfw.GetPrimaryMonitor()
videoMode := monitor.GetVideoMode()
window.glfwWindow.SetMonitor(monitor, 0, 0, videoMode.Width, videoMode.Height, glfw.DontCare)
} else {
window.glfwWindow.SetMonitor(nil, 0, 0, 1280, 720, glfw.DontCare)
}
}
// SetTitleSuffix appends a text to the current window title.
func (window *OpenGLWindow) SetTitleSuffix(value string) {
window.titleSuffix = value
window.updateTitle()
}
// SetProjectModified sets an indicator in the window frame that the project has not been saved.
func (window *OpenGLWindow) SetProjectModified(modified bool) {
window.projectModified = modified
window.updateTitle()
}
func (window *OpenGLWindow) updateTitle() {
title := window.titleBase
if len(window.titleSuffix) > 0 {
title += " - " + window.titleSuffix
}
if window.projectModified {
title += " (unsaved changes)"
}
window.glfwWindow.SetTitle(title)
}
// SetCloseRequest sets the should-close property of the window.
func (window *OpenGLWindow) SetCloseRequest(shouldClose bool) {
window.glfwWindow.SetShouldClose(shouldClose)
}
func (window *OpenGLWindow) onClosing(rawWindow *glfw.Window) {
window.CallClosing()
}
func (window *OpenGLWindow) onFramebufferResize(rawWindow *glfw.Window, width int, height int) {
window.CallResize(width, height)
}
func (window *OpenGLWindow) onCursorPos(rawWindow *glfw.Window, x float64, y float64) {
window.CallOnMouseMove(float32(x), float32(y))
}
func (window *OpenGLWindow) onMouseButton(rawWindow *glfw.Window, rawButton glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
button, knownButton := buttonsByIndex[rawButton]
if knownButton {
modifier := window.mapModifier(mods)
if action == glfw.Press {
window.CallOnMouseButtonDown(button, modifier)
} else if action == glfw.Release {
window.CallOnMouseButtonUp(button, modifier)
}
}
}
func (window *OpenGLWindow) onMouseScroll(rawWindow *glfw.Window, dx float64, dy float64) {
window.CallOnMouseScroll(float32(dx), float32(dy))
}
func (window *OpenGLWindow) onKey(rawWindow *glfw.Window, glfwKey glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
modifier := window.mapModifier(mods)
key, knownKey := keyMap[glfwKey]
if knownKey {
window.reportKey(action, key, modifier)
}
keyName := glfw.GetKeyName(glfwKey, scancode)
key, knownKey = input.ResolveShortcut(keyName, modifier)
if knownKey {
window.reportKey(action, key, modifier)
}
}
func (window *OpenGLWindow) reportKey(action glfw.Action, key input.Key, modifier input.Modifier) {
switch action {
case glfw.Press:
window.keyBuffer.KeyDown(key, modifier)
case glfw.Repeat:
window.keyBuffer.KeyUp(key, modifier)
window.keyBuffer.KeyDown(key, modifier)
case glfw.Release:
window.keyBuffer.KeyUp(key, modifier)
}
}
func (window *OpenGLWindow) onChar(rawWindow *glfw.Window, char rune) {
window.CallCharCallback(char)
}
func (window *OpenGLWindow) mapModifier(mods glfw.ModifierKey) input.Modifier {
modifier := input.ModNone
if (mods & glfw.ModControl) != 0 {
modifier = modifier.With(input.ModControl)
}
if (mods & glfw.ModShift) != 0 {
modifier = modifier.With(input.ModShift)
}
if (mods & glfw.ModAlt) != 0 {
modifier = modifier.With(input.ModAlt)
}
if (mods & glfw.ModSuper) != 0 {
modifier = modifier.With(input.ModSuper)
}
return modifier
}
func (window *OpenGLWindow) onDrop(rawWindow *glfw.Window, filePaths []string) {
window.CallFileDropCallback(filePaths)
}
+34
View File
@@ -0,0 +1,34 @@
package native
import (
"runtime"
"time"
"github.com/inkyblackness/hacked/ui/opengl"
)
// Run creates a native OpenGL window, initializes it with the given function and
// then runs the event loop until the window shall be closed.
func Run(initializer func(opengl.Window) error, title string, framesPerSecond float64) error {
runtime.LockOSThread()
window, err := NewOpenGLWindow(title, framesPerSecond)
if err != nil {
return err
}
defer window.Close()
err = initializer(window)
if err != nil {
return err
}
ticker := time.NewTicker(time.Millisecond)
for !window.ShouldClose() {
<-ticker.C
window.Update()
}
ticker.Stop()
return nil
}
@@ -0,0 +1,18 @@
package opengl
// CompileNewShader creates a shader of given type and compiles the provided source.
func CompileNewShader(gl OpenGL, shaderType uint32, source string) (shader uint32, err error) {
shader = gl.CreateShader(shaderType)
gl.ShaderSource(shader, source)
gl.CompileShader(shader)
compileStatus := gl.GetShaderParameter(shader, COMPILE_STATUS)
if compileStatus == 0 {
err = ShaderError{Log: gl.GetShaderInfoLog(shader)}
gl.DeleteShader(shader)
shader = 0
}
return
}
+63
View File
@@ -0,0 +1,63 @@
package opengl
// DebuggingEntryFunc is a function to be called on function entry.
type DebuggingEntryFunc func(name string, param ...interface{})
// DebuggingExitFunc is a function to be called after an OpenGL function has returned.
// result may be several parameters for functions with multiple return values.
type DebuggingExitFunc func(name string, result ...interface{})
// DebuggingErrorFunc is a function which is called if a previous OpenGL call caused an error state.
type DebuggingErrorFunc func(name string, errorCodes []uint32)
// DebugBuilder is a builder for an OpenGL implementation for debugging.
type DebugBuilder struct {
wrapped OpenGL
onEntry DebuggingEntryFunc
onExit DebuggingExitFunc
onError DebuggingErrorFunc
}
// NewDebugBuilder wraps the provided OpenGL instance and returns a new builder
// instance.
func NewDebugBuilder(wrapped OpenGL) *DebugBuilder {
builder := &DebugBuilder{
wrapped: wrapped,
onEntry: func(string, ...interface{}) {},
onExit: func(string, ...interface{}) {},
onError: func(string, []uint32) {}}
return builder
}
// Build creates a new instance of the debugging OpenGL implementation.
// The builder can be reused to create another instance with different parameters.
func (builder *DebugBuilder) Build() OpenGL {
opengl := &debuggingOpenGL{
gl: builder.wrapped,
onEntry: builder.onEntry,
onExit: builder.onExit,
onError: builder.onError}
return opengl
}
// OnEntry registers a callback function to be called before an OpenGL function is called.
func (builder *DebugBuilder) OnEntry(callback DebuggingEntryFunc) *DebugBuilder {
builder.onEntry = callback
return builder
}
// OnExit registers a callback function to be called after an OpenGL function returned.
func (builder *DebugBuilder) OnExit(callback DebuggingExitFunc) *DebugBuilder {
builder.onExit = callback
return builder
}
// OnError registers a callback function to be called when an OpenGL function set an error state.
func (builder *DebugBuilder) OnError(callback DebuggingErrorFunc) *DebugBuilder {
builder.onError = callback
return builder
}
+490
View File
@@ -0,0 +1,490 @@
package opengl
type debuggingOpenGL struct {
gl OpenGL
onEntry DebuggingEntryFunc
onExit DebuggingExitFunc
onError DebuggingErrorFunc
}
func (debugging *debuggingOpenGL) recordEntry(name string, param ...interface{}) {
debugging.onEntry(name, param...)
}
func (debugging *debuggingOpenGL) recordExit(name string, result ...interface{}) {
var errorCodes []uint32
for errorCode := debugging.gl.GetError(); errorCode != NO_ERROR; errorCode = debugging.gl.GetError() {
errorCodes = append(errorCodes, errorCode)
}
debugging.onExit(name, result...)
if len(errorCodes) > 0 {
debugging.onError(name, errorCodes)
}
}
// ActiveTexture implements the opengl.OpenGL interface.
func (debugging *debuggingOpenGL) ActiveTexture(texture uint32) {
debugging.recordEntry("ActiveTexture", texture)
debugging.gl.ActiveTexture(texture)
debugging.recordExit("ActiveTexture")
}
// AttachShader implements the OpenGL interface.
func (debugging *debuggingOpenGL) AttachShader(program uint32, shader uint32) {
debugging.recordEntry("AttachShader", program, shader)
debugging.gl.AttachShader(program, shader)
debugging.recordExit("AttachShader")
}
// BindAttribLocation implements the OpenGL interface.
func (debugging *debuggingOpenGL) BindAttribLocation(program uint32, index uint32, name string) {
debugging.recordEntry("BindAttribLocation", program, index, name)
debugging.gl.BindAttribLocation(program, index, name)
debugging.recordExit("BindAttribLocation")
}
// BindBuffer implements the OpenGL interface.
func (debugging *debuggingOpenGL) BindBuffer(target uint32, buffer uint32) {
debugging.recordEntry("BindBuffer", target, buffer)
debugging.gl.BindBuffer(target, buffer)
debugging.recordExit("BindBuffer")
}
// BindFramebuffer implements the OpenGL interface.
func (debugging *debuggingOpenGL) BindFramebuffer(target uint32, buffer uint32) {
debugging.recordEntry("BindFramebuffer", target, buffer)
debugging.gl.BindFramebuffer(target, buffer)
debugging.recordExit("BindFramebuffer")
}
// BindRenderbuffer implements the OpenGL interface.
func (debugging *debuggingOpenGL) BindRenderbuffer(target uint32, buffer uint32) {
debugging.recordEntry("BindRenderbuffer", target, buffer)
debugging.gl.BindRenderbuffer(target, buffer)
debugging.recordExit("BindRenderbuffer")
}
// BindSampler implements the OpenGL interface.
func (debugging *debuggingOpenGL) BindSampler(unit uint32, sampler uint32) {
debugging.recordEntry("BindSampler", unit, sampler)
debugging.gl.BindSampler(unit, sampler)
debugging.recordExit("BindSampler")
}
// BindTexture implements the OpenGL interface.
func (debugging *debuggingOpenGL) BindTexture(target uint32, texture uint32) {
debugging.recordEntry("BindTexture", target, texture)
debugging.gl.BindTexture(target, texture)
debugging.recordExit("BindTexture")
}
// BindVertexArray implements the OpenGL interface.
func (debugging *debuggingOpenGL) BindVertexArray(array uint32) {
debugging.recordEntry("BindVertexArray", array)
debugging.gl.BindVertexArray(array)
debugging.recordExit("BindVertexArray")
}
// BlendEquation implements the OpenGL interface.
func (debugging *debuggingOpenGL) BlendEquation(mode uint32) {
debugging.recordEntry("BlendEquation", mode)
debugging.gl.BlendEquation(mode)
debugging.recordExit("BlendEquation")
}
// BlendEquationSeparate implements the OpenGL interface.
func (debugging *debuggingOpenGL) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) {
debugging.recordEntry("BlendEquationSeparate", modeRGB, modeAlpha)
debugging.gl.BlendEquationSeparate(modeRGB, modeAlpha)
debugging.recordExit("BlendEquationSeparate")
}
// BlendFunc implements the OpenGL interface.
func (debugging *debuggingOpenGL) BlendFunc(sfactor uint32, dfactor uint32) {
debugging.recordEntry("BlendFunc", sfactor, dfactor)
debugging.gl.BlendFunc(sfactor, dfactor)
debugging.recordExit("BlendFunc")
}
// BlendFuncSeparate implements the OpenGL interface.
func (debugging *debuggingOpenGL) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) {
debugging.recordEntry("BlendFuncSeparate", srcRGB, dstRGB, srcAlpha, dstAlpha)
debugging.gl.BlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha)
debugging.recordExit("BlendFuncSeparate")
}
// BufferData implements the OpenGL interface.
func (debugging *debuggingOpenGL) BufferData(target uint32, size int, data interface{}, usage uint32) {
debugging.recordEntry("BufferData", target, size, data, usage)
debugging.gl.BufferData(target, size, data, usage)
debugging.recordExit("BufferData")
}
// CheckFramebufferStatus implements the OpenGL interface.
func (debugging *debuggingOpenGL) CheckFramebufferStatus(target uint32) uint32 {
debugging.recordEntry("CheckFramebufferStatus", target)
result := debugging.gl.CheckFramebufferStatus(target)
debugging.recordExit("CheckFramebufferStatus")
return result
}
// Clear implements the OpenGL interface.
func (debugging *debuggingOpenGL) Clear(mask uint32) {
debugging.recordEntry("Clear", mask)
debugging.gl.Clear(mask)
debugging.recordExit("Clear")
}
// ClearColor implements the OpenGL interface.
func (debugging *debuggingOpenGL) ClearColor(red float32, green float32, blue float32, alpha float32) {
debugging.recordEntry("ClearColor", red, green, blue, alpha)
debugging.gl.ClearColor(red, green, blue, alpha)
debugging.recordExit("ClearColor")
}
// CompileShader implements the OpenGL interface.
func (debugging *debuggingOpenGL) CompileShader(shader uint32) {
debugging.recordEntry("CompileShader", shader)
debugging.gl.CompileShader(shader)
debugging.recordExit("CompileShader")
}
// CreateProgram implements the OpenGL interface.
func (debugging *debuggingOpenGL) CreateProgram() uint32 {
debugging.recordEntry("CreateProgram")
result := debugging.gl.CreateProgram()
debugging.recordExit("CreateProgram", result)
return result
}
// CreateShader implements the OpenGL interface.
func (debugging *debuggingOpenGL) CreateShader(shaderType uint32) uint32 {
debugging.recordEntry("CreateShader", shaderType)
result := debugging.gl.CreateShader(shaderType)
debugging.recordExit("CreateShader", result)
return result
}
// DeleteBuffers implements the OpenGL interface.
func (debugging *debuggingOpenGL) DeleteBuffers(buffers []uint32) {
debugging.recordEntry("DeleteBuffers", buffers)
debugging.gl.DeleteBuffers(buffers)
debugging.recordExit("DeleteBuffers")
}
// DeleteFramebuffers implements the OpenGL interface.
func (debugging *debuggingOpenGL) DeleteFramebuffers(buffers []uint32) {
debugging.recordEntry("DeleteFramebuffers", buffers)
debugging.gl.DeleteFramebuffers(buffers)
debugging.recordExit("DeleteFramebuffers")
}
// DeleteProgram implements the OpenGL interface.
func (debugging *debuggingOpenGL) DeleteProgram(program uint32) {
debugging.recordEntry("DeleteProgram", program)
debugging.gl.DeleteProgram(program)
debugging.recordExit("DeleteProgram")
}
// DeleteShader implements the OpenGL interface.
func (debugging *debuggingOpenGL) DeleteShader(shader uint32) {
debugging.recordEntry("DeleteShader", shader)
debugging.gl.DeleteShader(shader)
debugging.recordExit("DeleteShader")
}
// DeleteTextures implements the OpenGL interface.
func (debugging *debuggingOpenGL) DeleteTextures(textures []uint32) {
debugging.recordEntry("DeleteTextures", textures)
debugging.gl.DeleteTextures(textures)
debugging.recordExit("DeleteTextures")
}
// DeleteVertexArrays implements the OpenGL interface.
func (debugging *debuggingOpenGL) DeleteVertexArrays(arrays []uint32) {
debugging.recordEntry("DeleteVertexArrays", arrays)
debugging.gl.DeleteVertexArrays(arrays)
debugging.recordExit("DeleteVertexArrays")
}
// Disable implements the OpenGL interface.
func (debugging *debuggingOpenGL) Disable(capability uint32) {
debugging.recordEntry("Disable", capability)
debugging.gl.Disable(capability)
debugging.recordExit("Disable")
}
// DrawArrays implements the OpenGL interface.
func (debugging *debuggingOpenGL) DrawArrays(mode uint32, first int32, count int32) {
debugging.recordEntry("DrawArrays", first, count)
debugging.gl.DrawArrays(mode, first, count)
debugging.recordExit("DrawArrays")
}
// DrawBuffers implements the OpenGL interface.
func (debugging *debuggingOpenGL) DrawBuffers(buffers []uint32) {
debugging.recordEntry("DrawBuffers", buffers)
debugging.gl.DrawBuffers(buffers)
debugging.recordExit("DrawBuffers")
}
// DrawElements implements the OpenGL interface.
func (debugging *debuggingOpenGL) DrawElements(mode uint32, count int32, elementType uint32, indices uintptr) {
debugging.recordEntry("DrawElements", mode, count, elementType, indices)
debugging.gl.DrawElements(mode, count, elementType, indices)
debugging.recordExit("DrawElements")
}
// Enable implements the OpenGL interface.
func (debugging *debuggingOpenGL) Enable(capability uint32) {
debugging.recordEntry("Enable", capability)
debugging.gl.Enable(capability)
debugging.recordExit("Enable")
}
// EnableVertexAttribArray implements the OpenGL interface.
func (debugging *debuggingOpenGL) EnableVertexAttribArray(index uint32) {
debugging.recordEntry("EnableVertexAttribArray", index)
debugging.gl.EnableVertexAttribArray(index)
debugging.recordExit("EnableVertexAttribArray")
}
// FramebufferRenderbuffer implements the OpenGL interface.
func (debugging *debuggingOpenGL) FramebufferRenderbuffer(target uint32, attachment uint32, renderbuffertarget uint32, renderbuffer uint32) {
debugging.recordEntry("FramebufferRenderbuffer", target, attachment, renderbuffertarget, renderbuffer)
debugging.gl.FramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer)
debugging.recordExit("FramebufferRenderbuffer")
}
// FramebufferTexture implements the OpenGL interface.
func (debugging *debuggingOpenGL) FramebufferTexture(target uint32, attachment uint32, texture uint32, level int32) {
debugging.recordEntry("FramebufferTexture", target, attachment, texture, level)
debugging.gl.FramebufferTexture(target, attachment, texture, level)
debugging.recordExit("FramebufferTexture")
}
// GenerateMipmap implements the opengl.OpenGL interface.
func (debugging *debuggingOpenGL) GenerateMipmap(target uint32) {
debugging.recordEntry("GenerateMipmap", target)
debugging.gl.GenerateMipmap(target)
debugging.recordExit("GenerateMipmap")
}
// GenBuffers implements the OpenGL interface.
func (debugging *debuggingOpenGL) GenBuffers(n int32) []uint32 {
debugging.recordEntry("GenBuffers", n)
result := debugging.gl.GenBuffers(n)
debugging.recordExit("GenBuffers", result)
return result
}
// GenFramebuffers implements the OpenGL interface.
func (debugging *debuggingOpenGL) GenFramebuffers(n int32) []uint32 {
debugging.recordEntry("GenFramebuffers", n)
result := debugging.gl.GenFramebuffers(n)
debugging.recordExit("GenFramebuffers", result)
return result
}
// GenRenderbuffers implements the OpenGL interface.
func (debugging *debuggingOpenGL) GenRenderbuffers(n int32) []uint32 {
debugging.recordEntry("GenRenderbuffers", n)
result := debugging.gl.GenRenderbuffers(n)
debugging.recordExit("GenRenderbuffers", result)
return result
}
// GenTextures implements the opengl.OpenGL interface.
func (debugging *debuggingOpenGL) GenTextures(n int32) []uint32 {
debugging.recordEntry("GenTextures", n)
result := debugging.gl.GenTextures(n)
debugging.recordExit("GenTextures", result)
return result
}
// GenVertexArrays implements the OpenGL interface.
func (debugging *debuggingOpenGL) GenVertexArrays(n int32) []uint32 {
debugging.recordEntry("GenVertexArrays", n)
result := debugging.gl.GenVertexArrays(n)
debugging.recordExit("GenVertexArrays", result)
return result
}
// GetAttribLocation implements the OpenGL interface.
func (debugging *debuggingOpenGL) GetAttribLocation(program uint32, name string) int32 {
debugging.recordEntry("GetAttribLocation", program, name)
result := debugging.gl.GetAttribLocation(program, name)
debugging.recordExit("GetAttribLocation", result)
return result
}
// GetError implements the OpenGL interface.
func (debugging *debuggingOpenGL) GetError() uint32 {
debugging.recordEntry("GetError")
result := debugging.gl.GetError()
debugging.recordExit("GetError", result)
return result
}
// GetIntegerv implements the OpenGL interface.
func (debugging *debuggingOpenGL) GetIntegerv(name uint32, data *int32) {
debugging.recordEntry("GetIntegerv", name, data)
debugging.gl.GetIntegerv(name, data)
debugging.recordExit("GetIntegerv")
}
// GetProgramInfoLog implements the OpenGL interface.
func (debugging *debuggingOpenGL) GetProgramInfoLog(program uint32) string {
debugging.recordEntry("GetProgramInfoLog", program)
result := debugging.gl.GetProgramInfoLog(program)
debugging.recordExit("GetProgramInfoLog", result)
return result
}
// GetProgramParameter implements the OpenGL interface.
func (debugging *debuggingOpenGL) GetProgramParameter(program uint32, param uint32) int32 {
debugging.recordEntry("GetProgramParameter", program, param)
result := debugging.gl.GetProgramParameter(program, param)
debugging.recordExit("GetProgramParameter", result)
return result
}
// GetShaderInfoLog implements the OpenGL interface.
func (debugging *debuggingOpenGL) GetShaderInfoLog(shader uint32) string {
debugging.recordEntry("GetShaderInfoLog", shader)
result := debugging.gl.GetShaderInfoLog(shader)
debugging.recordExit("GetShaderInfoLog", result)
return result
}
// GetShaderParameter implements the OpenGL interface.
func (debugging *debuggingOpenGL) GetShaderParameter(shader uint32, param uint32) int32 {
debugging.recordEntry("GetShaderParameter", shader, param)
result := debugging.gl.GetShaderParameter(shader, param)
debugging.recordExit("GetShaderParameter", result)
return result
}
// GetUniformLocation implements the OpenGL interface.
func (debugging *debuggingOpenGL) GetUniformLocation(program uint32, name string) int32 {
debugging.recordEntry("GetUniformLocation", program, name)
result := debugging.gl.GetUniformLocation(program, name)
debugging.recordExit("GetUniformLocation", result)
return result
}
// IsEnabled implements the OpenGL interface.
func (debugging *debuggingOpenGL) IsEnabled(capability uint32) bool {
debugging.recordEntry("IsEnabled", capability)
result := debugging.gl.IsEnabled(capability)
debugging.recordExit("IsEnabled", result)
return result
}
// LinkProgram implements the OpenGL interface.
func (debugging *debuggingOpenGL) LinkProgram(program uint32) {
debugging.recordEntry("LinkProgram", program)
debugging.gl.LinkProgram(program)
debugging.recordExit("LinkProgram")
}
// PixelStorei implements the OpenGL interface.
func (debugging *debuggingOpenGL) PixelStorei(name uint32, param int32) {
debugging.recordEntry("PixelStorei", name, param)
debugging.gl.PixelStorei(name, param)
debugging.recordExit("PixelStorei")
}
// PolygonMode implements the OpenGL interface.
func (debugging *debuggingOpenGL) PolygonMode(face uint32, mode uint32) {
debugging.recordEntry("PolygonMode", face, mode)
debugging.gl.PolygonMode(face, mode)
debugging.recordExit("PolygonMode")
}
// ReadPixels implements the OpenGL interface.
func (debugging *debuggingOpenGL) ReadPixels(x int32, y int32, width int32, height int32, format uint32, pixelType uint32, pixels interface{}) {
debugging.recordEntry("ReadPixels", x, y, width, height, format, pixelType, pixels)
debugging.gl.ReadPixels(x, y, width, height, format, pixelType, pixels)
debugging.recordExit("ReadPixels")
}
// RenderbufferStorage implements the opengl.OpenGL interface.
func (debugging *debuggingOpenGL) RenderbufferStorage(target uint32, internalFormat uint32, width int32, height int32) {
debugging.recordEntry("RenderbufferStorage", target, internalFormat, width, height)
debugging.gl.RenderbufferStorage(target, internalFormat, width, height)
debugging.recordExit("RenderbufferStorage")
}
// Scissor implements the OpenGL interface.
func (debugging *debuggingOpenGL) Scissor(x, y int32, width, height int32) {
debugging.recordEntry("Scissor", x, y, width, height)
debugging.gl.Scissor(x, y, width, height)
debugging.recordExit("Scissor")
}
// ShaderSource implements the OpenGL interface.
func (debugging *debuggingOpenGL) ShaderSource(shader uint32, source string) {
debugging.recordEntry("ShaderSource", shader, source)
debugging.gl.ShaderSource(shader, source)
debugging.recordExit("ShaderSource")
}
// TexImage2D implements the opengl.OpenGL interface.
func (debugging *debuggingOpenGL) TexImage2D(target uint32, level int32, internalFormat uint32, width int32, height int32,
border int32, format uint32, xtype uint32, pixels interface{}) {
debugging.recordEntry("TexImage2D", target, level, internalFormat, width, height, border, format, xtype, pixels)
debugging.gl.TexImage2D(target, level, internalFormat, width, height, border, format, xtype, pixels)
debugging.recordExit("TexImage2D")
}
// TexParameteri implements the opengl.OpenGL interface.
func (debugging *debuggingOpenGL) TexParameteri(target uint32, pname uint32, param int32) {
debugging.recordEntry("TexParameteri", target, pname, param)
debugging.gl.TexParameteri(target, pname, param)
debugging.recordExit("TexParameteri")
}
// Uniform1i implements the opengl.OpenGL interface.
func (debugging *debuggingOpenGL) Uniform1i(location int32, value int32) {
debugging.recordEntry("Uniform1i", location, value)
debugging.gl.Uniform1i(location, value)
debugging.recordExit("Uniform1i")
}
// Uniform4fv implements the opengl.OpenGL interface.
func (debugging *debuggingOpenGL) Uniform4fv(location int32, value *[4]float32) {
debugging.recordEntry("Uniform4fv", location, value)
debugging.gl.Uniform4fv(location, value)
debugging.recordExit("Uniform4fv")
}
// UniformMatrix4fv implements the OpenGL interface.
func (debugging *debuggingOpenGL) UniformMatrix4fv(location int32, transpose bool, value *[16]float32) {
debugging.recordEntry("UniformMatrix4fv", location, transpose, value)
debugging.gl.UniformMatrix4fv(location, transpose, value)
debugging.recordExit("UniformMatrix4fv")
}
// UseProgram implements the OpenGL interface.
func (debugging *debuggingOpenGL) UseProgram(program uint32) {
debugging.recordEntry("UseProgram", program)
debugging.gl.UseProgram(program)
debugging.recordExit("UseProgram")
}
// VertexAttribOffset implements the OpenGL interface.
func (debugging *debuggingOpenGL) VertexAttribOffset(index uint32, size int32, attribType uint32, normalized bool, stride int32, offset int) {
debugging.recordEntry("VertexAttribOffset", index, size, attribType, normalized, stride, offset)
debugging.gl.VertexAttribOffset(index, size, attribType, normalized, stride, offset)
debugging.recordExit("VertexAttribOffset")
}
// Viewport implements the OpenGL interface.
func (debugging *debuggingOpenGL) Viewport(x int32, y int32, width int32, height int32) {
debugging.recordEntry("Viewport", x, y, width, height)
debugging.gl.Viewport(x, y, width, height)
debugging.recordExit("Viewport")
}
+54
View File
@@ -0,0 +1,54 @@
package opengl
import (
"fmt"
)
var errorStrings = map[uint32]string{
NO_ERROR: "NO_ERROR",
INVALID_ENUM: "INVALID_ENUM",
INVALID_VALUE: "INVALID_VALUE",
INVALID_OPERATION: "INVALID_OPERATION",
STACK_OVERFLOW: "STACK_OVERFLOW",
STACK_UNDERFLOW: "STACK_UNDERFLOW",
OUT_OF_MEMORY: "OUT_OF_MEMORY",
INVALID_FRAMEBUFFER_OPERATION: "INVALID_FRAMEBUFFER_OPERATION"}
// ErrorString returns a readable version of the provided error code. If the
// code is unknown, the textual representation of the hexadecimal value is
// returned.
func ErrorString(errorCode uint32) string {
value, exists := errorStrings[errorCode]
if !exists {
value = fmt.Sprintf("0x%04X", errorCode)
}
return value
}
// ShaderError describes a problem with a log.
type ShaderError struct {
Log string
}
// Error returns the log.
func (err ShaderError) Error() string {
return err.Log
}
// NamedShaderError is an error with a name.
type NamedShaderError struct {
Name string
Nested error
}
// Error returns the name with the nested error information.
func (err NamedShaderError) Error() string {
return fmt.Sprintf("%s failed: %v", err.Name, err.Nested)
}
// Unwrap returns the nested error.
func (err NamedShaderError) Unwrap() error {
return err.Nested
}
+90
View File
@@ -0,0 +1,90 @@
package opengl
// OpenGL describes an Open GL interface usable for all environments of this
// application.
type OpenGL interface {
ActiveTexture(texture uint32)
AttachShader(program uint32, shader uint32)
BindAttribLocation(program uint32, index uint32, name string)
BindBuffer(target uint32, buffer uint32)
BindFramebuffer(target uint32, buffer uint32)
BindRenderbuffer(target uint32, buffer uint32)
BindSampler(unit uint32, sampler uint32)
BindTexture(target uint32, texture uint32)
BindVertexArray(array uint32)
BlendEquation(mode uint32)
BlendEquationSeparate(modeRGB uint32, modeAlpha uint32)
BlendFunc(sfactor uint32, dfactor uint32)
BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32)
BufferData(target uint32, size int, data interface{}, usage uint32)
CheckFramebufferStatus(target uint32) uint32
Clear(mask uint32)
ClearColor(red float32, green float32, blue float32, alpha float32)
CompileShader(shader uint32)
CreateProgram() uint32
CreateShader(shaderType uint32) uint32
DeleteBuffers(buffers []uint32)
DeleteFramebuffers(buffers []uint32)
DeleteProgram(program uint32)
DeleteShader(shader uint32)
DeleteTextures(textures []uint32)
DeleteVertexArrays(arrays []uint32)
Disable(capability uint32)
DrawArrays(mode uint32, first int32, count int32)
DrawBuffers(buffers []uint32)
DrawElements(mode uint32, count int32, elementType uint32, indices uintptr)
Enable(capability uint32)
EnableVertexAttribArray(index uint32)
FramebufferRenderbuffer(target uint32, attachment uint32, renderbuffertarget uint32, renderbuffer uint32)
FramebufferTexture(target uint32, attachment uint32, texture uint32, level int32)
GenerateMipmap(target uint32)
GenBuffers(n int32) []uint32
GenFramebuffers(n int32) []uint32
GenRenderbuffers(n int32) []uint32
GenTextures(n int32) []uint32
GenVertexArrays(n int32) []uint32
GetAttribLocation(program uint32, name string) int32
GetError() uint32
GetIntegerv(name uint32, data *int32)
GetShaderInfoLog(shader uint32) string
GetShaderParameter(shader uint32, param uint32) int32
GetProgramInfoLog(program uint32) string
GetProgramParameter(program uint32, param uint32) int32
GetUniformLocation(program uint32, name string) int32
IsEnabled(capability uint32) bool
LinkProgram(program uint32)
PixelStorei(name uint32, param int32)
PolygonMode(face uint32, mode uint32)
ReadPixels(x int32, y int32, width int32, height int32, format uint32, pixelType uint32, pixels interface{})
RenderbufferStorage(target uint32, internalFormat uint32, width int32, height int32)
Scissor(x, y int32, width, height int32)
ShaderSource(shader uint32, source string)
TexImage2D(target uint32, level int32, internalFormat uint32, width int32, height int32,
border int32, format uint32, xtype uint32, pixels interface{})
TexParameteri(target uint32, pname uint32, param int32)
Uniform1i(location int32, value int32)
Uniform4fv(location int32, value *[4]float32)
UniformMatrix4fv(location int32, transpose bool, value *[16]float32)
UseProgram(program uint32)
VertexAttribOffset(index uint32, size int32, attribType uint32, normalized bool, stride int32, offset int)
Viewport(x int32, y int32, width int32, height int32)
}
+35
View File
@@ -0,0 +1,35 @@
package opengl
// LinkNewProgram creates a new shader program based on the provided shaders.
func LinkNewProgram(gl OpenGL, shaders ...uint32) (program uint32, err error) {
program = gl.CreateProgram()
for _, shader := range shaders {
gl.AttachShader(program, shader)
}
gl.LinkProgram(program)
if gl.GetProgramParameter(program, LINK_STATUS) == 0 {
err = ShaderError{Log: gl.GetProgramInfoLog(program)}
gl.DeleteProgram(program)
program = 0
}
return
}
// LinkNewStandardProgram creates a new shader based on two shader sources.
func LinkNewStandardProgram(gl OpenGL, vertexShaderSource, fragmentShaderSource string) (program uint32, err error) {
vertexShader, vertexErr := CompileNewShader(gl, VERTEX_SHADER, vertexShaderSource)
if vertexErr != nil {
return 0, NamedShaderError{Name: "vertexShader", Nested: vertexErr}
}
defer gl.DeleteShader(vertexShader)
fragmentShader, fragmentErr := CompileNewShader(gl, FRAGMENT_SHADER, fragmentShaderSource)
if fragmentErr != nil {
return 0, NamedShaderError{Name: "fragmentShader", Nested: fragmentErr}
}
defer gl.DeleteShader(fragmentShader)
return LinkNewProgram(gl, vertexShader, fragmentShader)
}
+25
View File
@@ -0,0 +1,25 @@
package opengl
import (
mgl "github.com/go-gl/mathgl/mgl32"
)
// UniformLocation represents a uniform parameter for a shader program.
type UniformLocation int32
// Matrix4Uniform represents one uniform parameter for a 4x4 matrix.
type Matrix4Uniform UniformLocation
// Set stores the provided matrix as the current uniform value.
func (location Matrix4Uniform) Set(gl OpenGL, matrix *mgl.Mat4) {
matrixArray := (*[16]float32)(matrix)
gl.UniformMatrix4fv(int32(location), false, matrixArray)
}
// Vector4Uniform represents one uniform parameter for a 4-dimensional vector.
type Vector4Uniform UniformLocation
// Set stores the provided 4-dimensional vector as the current uniform value.
func (location Vector4Uniform) Set(gl OpenGL, value *[4]float32) {
gl.Uniform4fv(int32(location), value)
}
@@ -0,0 +1,62 @@
package opengl
// AttributeSetter is a callback function for a VertexArrayObject.
type AttributeSetter func(OpenGL)
// VertexArrayObject registers common vertex properties which are pre-set
// before regular rendering loops.
// Depending on the supported level of the Open GL API, these settings are
// either buffered (using a VAO - vertex array object), or replayed for
// every render call.
type VertexArrayObject struct {
gl OpenGL
program uint32
handle uint32
setter AttributeSetter
}
// NewVertexArrayObject returns a new instance.
func NewVertexArrayObject(gl OpenGL, program uint32) *VertexArrayObject {
vao := &VertexArrayObject{
gl: gl,
program: program,
handle: gl.GenVertexArrays(1)[0],
setter: func(OpenGL) {}}
return vao
}
// Dispose drops any associated resources.
func (vao *VertexArrayObject) Dispose() {
vao.gl.DeleteVertexArrays([]uint32{vao.handle})
vao.handle = 0
}
// WithSetter registers the setter function for this object.
// Depending on the support of the OpenGL API, this setter may be called
// once immediately, or for each render call.
func (vao *VertexArrayObject) WithSetter(setter AttributeSetter) {
if vao.handle != 0 {
vao.OnShader(func() { setter(vao.gl) })
}
vao.setter = setter
}
// OnShader executes the provided task with an activated shader program
// and the vertex array object initialized.
func (vao *VertexArrayObject) OnShader(task func()) {
gl := vao.gl
gl.UseProgram(vao.program)
gl.BindVertexArray(vao.handle)
defer func() {
gl.BindVertexArray(0)
gl.UseProgram(0)
}()
if vao.handle == 0 {
vao.setter(gl)
}
task()
}
+147
View File
@@ -0,0 +1,147 @@
package opengl
import (
"encoding/json"
"io/ioutil"
"github.com/inkyblackness/hacked/ui/input"
)
// ClosingCallback is the function to handle close requests by the user.
type ClosingCallback func()
// ClosedCallback is the function to clean up resources when the window is being closed.
type ClosedCallback func()
// RenderCallback is the function to receive render events. When the callback
// returns, the window will swap the internal buffer.
type RenderCallback func()
// MouseMoveCallback is the function to receive the current mouse coordinate while moving.
// Movement is reported while the cursor is within the client area of the window, and
// beyond the window as long as at least one captured button is pressed.
// Reported values are with sub-pixel precision, if possible.
type MouseMoveCallback func(x float32, y float32)
// MouseButtonCallback is the function to receive button up/down events.
// An Up event is sent for every reported Down event, even if the mouse cursor is outside
// the client area.
type MouseButtonCallback func(buttonMask uint32, modifier input.Modifier)
// MouseScrollCallback is the function to receive scroll events.
// Delta values are right-hand oriented: positive values go right/down/far.
type MouseScrollCallback func(dx float32, dy float32)
// ResizeCallback is called for a change of window dimensions.
type ResizeCallback func(width int, height int)
// CharCallback is called for typing a character.
type CharCallback func(char rune)
// KeyCallback is called for pressing or releasing a key on the keyboard.
type KeyCallback func(key input.Key, modifier input.Modifier)
// ModifierCallback is called when the currently active modifier changed.
type ModifierCallback func(modifier input.Modifier)
// FileDropCallback is called when one or more files were dropped into the window.
type FileDropCallback func(filePaths []string)
// WindowState describes how the window is currently presented to the user.
type WindowState struct {
// Maximized is set to true if the window is currently resized to fill the screen.
Maximized bool
// Top marks the screen position of the upper border of the window.
Top int
// Left marks the screen position of the left border of the window.
Left int
// Width marks the width of the non-maximized window.
Width int
// Height marks the height of the non-maximized window.
Height int
}
// WindowStateFromFile attempts to load the state from a file with given name.
// If this does not work, the given default state will be returned.
func WindowStateFromFile(filename string, defaultState WindowState) WindowState {
data, err := ioutil.ReadFile(filename)
if err != nil {
return defaultState
}
var state WindowState
err = json.Unmarshal(data, &state)
if err != nil {
return defaultState
}
return state
}
// SaveTo stores the state in a file with given filename.
func (state WindowState) SaveTo(filename string) error {
bytes, err := json.MarshalIndent(state, "", " ")
if err != nil {
return err
}
return ioutil.WriteFile(filename, bytes, 0640)
}
// Window represents an OpenGL render surface.
type Window interface {
// StateSnapshot returns the current state of the window.
StateSnapshot() WindowState
// RestoreState puts the window into the same state when StateSnapshot() was called.
RestoreState(state WindowState)
// ClipboardString returns the current value of the clipboard, if it is compatible with UTF-8.
ClipboardString() (string, error)
// SetClipboardString sets the current value of the clipboard as UTF-8 string.
SetClipboardString(value string)
// OnClosing registers a callback function which shall be called when the user requests to close the window.
OnClosing(callback ClosingCallback)
// OnClosed registers a callback function which shall be called when the window is being closed.
OnClosed(callback ClosedCallback)
// SetCloseRequest notifies the window system whether the window shall be closed.
// It can be called during the callback of OnClosing() to abort a close request by the user.
SetCloseRequest(shouldClose bool)
// OpenGL returns the OpenGL API wrapper for this window.
OpenGL() OpenGL
// OnRender registers a callback function which shall be called to update the scene.
OnRender(callback RenderCallback)
// OnResize registers a callback function for sizing events.
OnResize(callback ResizeCallback)
// Size returns the dimensions of the window display area in pixel.
Size() (width int, height int)
// SetFullScreen sets the full screen state of the window.
SetFullScreen(on bool)
// SetTitleSuffix appends a text to the current window title.
SetTitleSuffix(value string)
// SetProjectModified sets an indicator in the window frame that the project has not been saved.
SetProjectModified(modified bool)
// SetCursorVisible controls whether the mouse cursor is currently visible.
SetCursorVisible(visible bool)
// OnMouseMove registers a callback function for mouse move events.
OnMouseMove(callback MouseMoveCallback)
// OnMouseButtonDown registers a callback function for mouse button down events.
OnMouseButtonDown(callback MouseButtonCallback)
// OnMouseButtonUp registers a callback function for mouse button up events.
OnMouseButtonUp(callback MouseButtonCallback)
// OnMouseScroll registers a callback function for mouse scroll events.
OnMouseScroll(callback MouseScrollCallback)
// OnKeyPress registers a callback function for key events.
OnKeyPress(callback KeyCallback)
// OnKeyRelease registers a callback function for key events.
OnKeyRelease(callback KeyCallback)
// OnModifier registers a callback function for change of modifier events.
OnModifier(callback ModifierCallback)
// OnCharCallback registers a callback function for typed characters.
OnCharCallback(callback CharCallback)
// OnFileDropCallback registers a callback function for dropped files.
OnFileDropCallback(callback FileDropCallback)
}
@@ -0,0 +1,128 @@
package opengl
import (
"github.com/inkyblackness/hacked/ui/input"
)
type keyDeferrer struct {
window *WindowEventDispatcher
}
func (def *keyDeferrer) KeyPress(key input.Key, modifier input.Modifier) {
def.window.CallKeyPress(key, modifier)
}
func (def *keyDeferrer) KeyRelease(key input.Key, modifier input.Modifier) {
def.window.CallKeyRelease(key, modifier)
}
func (def *keyDeferrer) Modifier(modifier input.Modifier) {
def.window.CallModifier(modifier)
}
// WindowEventDispatcher implements the common, basic functionality of Window.
type WindowEventDispatcher struct {
CallClosing ClosingCallback
CallClosed ClosedCallback
CallRender RenderCallback
CallResize ResizeCallback
CallOnMouseMove MouseMoveCallback
CallOnMouseButtonUp MouseButtonCallback
CallOnMouseButtonDown MouseButtonCallback
CallOnMouseScroll MouseScrollCallback
CallModifier ModifierCallback
CallKeyPress KeyCallback
CallKeyRelease KeyCallback
CallCharCallback CharCallback
CallFileDropCallback FileDropCallback
}
// NullWindowEventDispatcher returns an initialized instance with empty callbacks.
func NullWindowEventDispatcher() WindowEventDispatcher {
return WindowEventDispatcher{
CallClosing: func() {},
CallClosed: func() {},
CallRender: func() {},
CallResize: func(int, int) {},
CallOnMouseMove: func(float32, float32) {},
CallOnMouseButtonUp: func(uint32, input.Modifier) {},
CallOnMouseButtonDown: func(uint32, input.Modifier) {},
CallOnMouseScroll: func(float32, float32) {},
CallKeyPress: func(input.Key, input.Modifier) {},
CallKeyRelease: func(input.Key, input.Modifier) {},
CallModifier: func(input.Modifier) {},
CallCharCallback: func(rune) {},
CallFileDropCallback: func([]string) {},
}
}
// StickyKeyListener returns an instance of a listener acting as an adapter
// for the key-down/-up callbacks.
func (window *WindowEventDispatcher) StickyKeyListener() input.StickyKeyListener {
return &keyDeferrer{window: window}
}
// OnClosing implements the Window interface.
func (window *WindowEventDispatcher) OnClosing(callback ClosingCallback) {
window.CallClosing = callback
}
// OnClosed implements the Window interface.
func (window *WindowEventDispatcher) OnClosed(callback ClosedCallback) {
window.CallClosed = callback
}
// OnRender implements the Window interface.
func (window *WindowEventDispatcher) OnRender(callback RenderCallback) {
window.CallRender = callback
}
// OnResize implements the Window interface.
func (window *WindowEventDispatcher) OnResize(callback ResizeCallback) {
window.CallResize = callback
}
// OnMouseMove implements the Window interface.
func (window *WindowEventDispatcher) OnMouseMove(callback MouseMoveCallback) {
window.CallOnMouseMove = callback
}
// OnMouseButtonDown implements the Window interface.
func (window *WindowEventDispatcher) OnMouseButtonDown(callback MouseButtonCallback) {
window.CallOnMouseButtonDown = callback
}
// OnMouseButtonUp implements the Window interface.
func (window *WindowEventDispatcher) OnMouseButtonUp(callback MouseButtonCallback) {
window.CallOnMouseButtonUp = callback
}
// OnMouseScroll implements the Window interface.
func (window *WindowEventDispatcher) OnMouseScroll(callback MouseScrollCallback) {
window.CallOnMouseScroll = callback
}
// OnKeyPress implements the Window interface.
func (window *WindowEventDispatcher) OnKeyPress(callback KeyCallback) {
window.CallKeyPress = callback
}
// OnKeyRelease implements the Window interface.
func (window *WindowEventDispatcher) OnKeyRelease(callback KeyCallback) {
window.CallKeyRelease = callback
}
// OnModifier implements the WindowEventDispatcher interface.
func (window *WindowEventDispatcher) OnModifier(callback ModifierCallback) {
window.CallModifier = callback
}
// OnCharCallback implements the Window interface.
func (window *WindowEventDispatcher) OnCharCallback(callback CharCallback) {
window.CallCharCallback = callback
}
// OnFileDropCallback implements the Window interface.
func (window *WindowEventDispatcher) OnFileDropCallback(callback FileDropCallback) {
window.CallFileDropCallback = callback
}
+156
View File
@@ -0,0 +1,156 @@
package opengl
// Buffer Bits
// nolint: golint,megacheck
const (
DEPTH_BUFFER_BIT uint32 = 0x00000100
STENCIL_BUFFER_BIT = 0x00000400
COLOR_BUFFER_BIT = 0x00004000
)
// Draw Types
// nolint: golint,megacheck
const (
POINTS uint32 = 0x0000
LINES = 0x0001
LINE_LOOP = 0x0002
LINE_STRIP = 0x0003
TRIANGLES = 0x0004
TRIANGLE_STRIP = 0x0005
TRIANGLE_FAN = 0x0006
)
// Shader Types
// nolint: golint,megacheck
const (
FRAGMENT_SHADER uint32 = 0x8B30
VERTEX_SHADER = 0x8B31
)
// Status Values
// nolint: golint,megacheck
const (
COMPILE_STATUS uint32 = 0x8B81
LINK_STATUS = 0x8B82
)
// Buffer Types
// nolint: golint,megacheck
const (
ARRAY_BUFFER uint32 = 0x8892
ELEMENT_ARRAY_BUFFER = 0x8893
)
// Framebuffer Types
// nolint: golint,megacheck
const (
FRAMEBUFFER = 0x8D40
RENDERBUFFER = 0x8D41
DEPTH_COMPONENT24 = 0x81A6
COLOR_ATTACHMENT0 = 0x8CE0
DEPTH_ATTACHMENT = 0x8D00
)
// Draw Types
// nolint: golint,megacheck
const (
STREAM_DRAW uint32 = 0x88E0
STATIC_DRAW = 0x88E4
DYNAMIC_DRAW = 0x88E8
)
// Features
// nolint: golint,megacheck
const (
BLEND uint32 = 0x0BE2
DEPTH_TEST = 0x0B71
CULL_FACE = 0x0B44
SCISSOR_TEST = 0x0C11
NICEST = 0x1102
LINE_SMOOTH = 0x0B20
LINE_SMOOTH_HINT = 0x0C52
ACTIVE_TEXTURE = 0x84E0
CURRENT_PROGRAM = 0x8B8D
TEXTURE_BINDING_2D = 0x8069
SAMPLER_BINDING = 0x8919
ARRAY_BUFFER_BINDING = 0x8894
ELEMENT_ARRAY_BUFFER_BINDING = 0x8895
VERTEX_ARRAY_BINDING = 0x85B5
POLYGON_MODE = 0x0B40
VIEWPORT = 0x0BA2
SCISSOR_BOX = 0x0C10
BLEND_SRC_RGB = 0x80C9
BLEND_DST_RGB = 0x80C8
BLEND_SRC_ALPHA = 0x80CB
BLEND_DST_ALPHA = 0x80CA
BLEND_EQUATION_RGB = 0x8009
BLEND_EQUATION_ALPHA = 0x883D
FRONT_AND_BACK = 0x0408
FILL = 0x1B02
)
// Alpha constants
// nolint: golint,megacheck
const (
SRC_ALPHA uint32 = 0x0302
ONE_MINUS_SRC_ALPHA = 0x0303
ONE_MINUS_SRC_COLOR = 0x0301
FUNC_ADD = 0x8006
)
// Data Types
// nolint: golint,megacheck
const (
BYTE uint32 = 0x1400
UNSIGNED_BYTE = 0x1401
SHORT = 0x1402
UNSIGNED_SHORT = 0x1403
INT = 0x1404
UNSIGNED_INT = 0x1405
FLOAT = 0x1406
)
// Texture Constants
// nolint: golint,megacheck
const (
TEXTURE_2D uint32 = 0x0DE1
TEXTURE0 = 0x84C0
NEAREST = 0x2600
TEXTURE_MAG_FILTER = 0x2800
TEXTURE_MIN_FILTER = 0x2801
UNPACK_ROW_LENGTH = 0x0CF2
LINEAR = 0x2601
)
// Errors
// nolint: golint,megacheck
const (
NO_ERROR uint32 = 0
INVALID_ENUM = 0x0500
INVALID_VALUE = 0x0501
INVALID_OPERATION = 0x0502
STACK_OVERFLOW = 0x0503
STACK_UNDERFLOW = 0x0504
OUT_OF_MEMORY = 0x0505
INVALID_FRAMEBUFFER_OPERATION = 0x0506
)
// Color Types
// nolint: golint,megacheck
const (
ALPHA uint32 = 0x1906
RGBA = 0x1908
RED = 0x1903
RGBA8 = 0x8058
R8 = 0x8229
)