2018-12-28 02:28:03 +00:00
package imgui_backend
2018-12-27 22:35:06 +00:00
import (
"unsafe"
"github.com/FooSoft/imgui-go"
"github.com/go-gl/gl/v2.1/gl"
"github.com/veandco/go-sdl2/sdl"
)
2018-12-28 17:26:05 +00:00
var singleton struct {
2018-12-31 21:10:58 +00:00
context * imgui . Context
refCount int
2018-12-28 02:28:03 +00:00
}
2018-12-31 21:10:58 +00:00
func ( c * Context ) BeginFrame ( ) error {
2018-12-27 22:35:06 +00:00
// Setup display size (every frame to accommodate for window resizing)
2018-12-28 02:28:03 +00:00
io := imgui . CurrentIO ( )
2018-12-31 21:10:58 +00:00
io . SetDisplaySize ( imgui . Vec2 {
X : float32 ( c . displaySize . X ) ,
Y : float32 ( c . displaySize . Y ) ,
} )
2018-12-27 22:35:06 +00:00
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
frequency := sdl . GetPerformanceFrequency ( )
currentTime := sdl . GetPerformanceCounter ( )
2018-12-31 21:10:58 +00:00
if c . lastTime > 0 {
io . SetDeltaTime ( float32 ( currentTime - c . lastTime ) / float32 ( frequency ) )
2018-12-27 22:35:06 +00:00
} else {
io . SetDeltaTime ( 1.0 / 60.0 )
}
2018-12-31 21:10:58 +00:00
c . lastTime = currentTime
2018-12-27 22:35:06 +00:00
// If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
x , y , state := sdl . GetMouseState ( )
for i , button := range [ ] uint32 { sdl . BUTTON_LEFT , sdl . BUTTON_RIGHT , sdl . BUTTON_MIDDLE } {
2018-12-31 21:10:58 +00:00
io . SetMouseButtonDown ( i , c . buttonsDown [ i ] || ( state & sdl . Button ( button ) ) != 0 )
c . buttonsDown [ i ] = false
2018-12-27 22:35:06 +00:00
}
io . SetMousePosition ( imgui . Vec2 { X : float32 ( x ) , Y : float32 ( y ) } )
2018-12-28 02:28:03 +00:00
imgui . NewFrame ( )
return nil
2018-12-27 22:35:06 +00:00
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
2018-12-31 21:10:58 +00:00
func ( c * Context ) ProcessEvent ( event sdl . Event ) ( bool , error ) {
2018-12-27 22:35:06 +00:00
switch io := imgui . CurrentIO ( ) ; event . GetType ( ) {
case sdl . MOUSEWHEEL :
wheelEvent := event . ( * sdl . MouseWheelEvent )
var deltaX , deltaY float32
if wheelEvent . X > 0 {
deltaX ++
} else if wheelEvent . X < 0 {
deltaX --
}
if wheelEvent . Y > 0 {
deltaY ++
} else if wheelEvent . Y < 0 {
deltaY --
}
2018-12-28 02:28:03 +00:00
return true , nil
2018-12-27 22:35:06 +00:00
case sdl . MOUSEBUTTONDOWN :
buttonEvent := event . ( * sdl . MouseButtonEvent )
switch buttonEvent . Button {
case sdl . BUTTON_LEFT :
2018-12-31 21:10:58 +00:00
c . buttonsDown [ 0 ] = true
2018-12-27 22:35:06 +00:00
break
case sdl . BUTTON_RIGHT :
2018-12-31 21:10:58 +00:00
c . buttonsDown [ 1 ] = true
2018-12-27 22:35:06 +00:00
break
case sdl . BUTTON_MIDDLE :
2018-12-31 21:10:58 +00:00
c . buttonsDown [ 2 ] = true
2018-12-27 22:35:06 +00:00
break
}
2018-12-28 02:28:03 +00:00
return true , nil
2018-12-27 22:35:06 +00:00
case sdl . TEXTINPUT :
inputEvent := event . ( * sdl . TextInputEvent )
io . AddInputCharacters ( string ( inputEvent . Text [ : ] ) )
2018-12-28 02:28:03 +00:00
return true , nil
2018-12-27 22:35:06 +00:00
case sdl . KEYDOWN :
keyEvent := event . ( * sdl . KeyboardEvent )
io . KeyPress ( int ( keyEvent . Keysym . Scancode ) )
modState := int ( sdl . GetModState ( ) )
io . KeyShift ( modState & sdl . KMOD_LSHIFT , modState & sdl . KMOD_RSHIFT )
io . KeyCtrl ( modState & sdl . KMOD_LCTRL , modState & sdl . KMOD_RCTRL )
io . KeyAlt ( modState & sdl . KMOD_LALT , modState & sdl . KMOD_RALT )
case sdl . KEYUP :
keyEvent := event . ( * sdl . KeyboardEvent )
io . KeyRelease ( int ( keyEvent . Keysym . Scancode ) )
modState := int ( sdl . GetModState ( ) )
io . KeyShift ( modState & sdl . KMOD_LSHIFT , modState & sdl . KMOD_RSHIFT )
io . KeyCtrl ( modState & sdl . KMOD_LCTRL , modState & sdl . KMOD_RCTRL )
io . KeyAlt ( modState & sdl . KMOD_LALT , modState & sdl . KMOD_RALT )
2018-12-28 02:28:03 +00:00
return true , nil
2018-12-27 22:35:06 +00:00
}
2018-12-28 02:28:03 +00:00
return false , nil
2018-12-27 22:35:06 +00:00
}
// OpenGL2 Render function.
2018-12-28 17:26:05 +00:00
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL singleton explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
2018-12-31 21:10:58 +00:00
func ( c * Context ) EndFrame ( ) error {
2018-12-31 19:46:12 +00:00
imgui . Render ( )
drawData := imgui . RenderedDrawData ( )
2018-12-28 02:28:03 +00:00
drawData . ScaleClipRects ( imgui . Vec2 {
2018-12-31 21:10:58 +00:00
X : float32 ( c . bufferSize . X ) / float32 ( c . displaySize . X ) ,
Y : float32 ( c . bufferSize . Y ) / float32 ( c . displaySize . Y ) ,
2018-12-28 02:28:03 +00:00
} )
2018-12-27 22:35:06 +00:00
// We are using the OpenGL fixed pipeline to make the example code simpler to read!
2018-12-28 17:26:05 +00:00
// Setup render singleton: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill.
2018-12-27 22:35:06 +00:00
var lastTexture int32
gl . GetIntegerv ( gl . TEXTURE_BINDING_2D , & lastTexture )
var lastPolygonMode [ 2 ] int32
gl . GetIntegerv ( gl . POLYGON_MODE , & lastPolygonMode [ 0 ] )
var lastViewport [ 4 ] int32
gl . GetIntegerv ( gl . VIEWPORT , & lastViewport [ 0 ] )
var lastScissorBox [ 4 ] int32
gl . GetIntegerv ( gl . SCISSOR_BOX , & lastScissorBox [ 0 ] )
gl . PushAttrib ( gl . ENABLE_BIT | gl . COLOR_BUFFER_BIT | gl . TRANSFORM_BIT )
gl . Enable ( gl . BLEND )
gl . BlendFunc ( gl . SRC_ALPHA , gl . ONE_MINUS_SRC_ALPHA )
gl . Disable ( gl . CULL_FACE )
gl . Disable ( gl . DEPTH_TEST )
gl . Disable ( gl . LIGHTING )
gl . Disable ( gl . COLOR_MATERIAL )
gl . Enable ( gl . SCISSOR_TEST )
gl . EnableClientState ( gl . VERTEX_ARRAY )
gl . EnableClientState ( gl . TEXTURE_COORD_ARRAY )
gl . EnableClientState ( gl . COLOR_ARRAY )
gl . Enable ( gl . TEXTURE_2D )
gl . PolygonMode ( gl . FRONT_AND_BACK , gl . FILL )
// You may want this if using this code in an OpenGL 3+ context where shaders may be bound
// gl.UseProgram(0)
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
2018-12-31 21:10:58 +00:00
gl . Viewport ( 0 , 0 , int32 ( c . bufferSize . X ) , int32 ( c . bufferSize . Y ) )
2018-12-27 22:35:06 +00:00
gl . MatrixMode ( gl . PROJECTION )
gl . PushMatrix ( )
gl . LoadIdentity ( )
2018-12-31 21:10:58 +00:00
gl . Ortho ( 0 , float64 ( c . displaySize . X ) , float64 ( c . displaySize . Y ) , 0 , - 1 , 1 )
2018-12-27 22:35:06 +00:00
gl . MatrixMode ( gl . MODELVIEW )
gl . PushMatrix ( )
gl . LoadIdentity ( )
vertexSize , vertexOffsetPos , vertexOffsetUv , vertexOffsetCol := imgui . VertexBufferLayout ( )
indexSize := imgui . IndexBufferLayout ( )
drawType := gl . UNSIGNED_SHORT
if indexSize == 4 {
drawType = gl . UNSIGNED_INT
}
// Render command lists
for _ , commandList := range drawData . CommandLists ( ) {
vertexBuffer , _ := commandList . VertexBuffer ( )
indexBuffer , _ := commandList . IndexBuffer ( )
indexBufferOffset := uintptr ( indexBuffer )
gl . VertexPointer ( 2 , gl . FLOAT , int32 ( vertexSize ) , unsafe . Pointer ( uintptr ( vertexBuffer ) + uintptr ( vertexOffsetPos ) ) )
gl . TexCoordPointer ( 2 , gl . FLOAT , int32 ( vertexSize ) , unsafe . Pointer ( uintptr ( vertexBuffer ) + uintptr ( vertexOffsetUv ) ) )
gl . ColorPointer ( 4 , gl . UNSIGNED_BYTE , int32 ( vertexSize ) , unsafe . Pointer ( uintptr ( vertexBuffer ) + uintptr ( vertexOffsetCol ) ) )
for _ , command := range commandList . Commands ( ) {
if command . HasUserCallback ( ) {
command . CallUserCallback ( commandList )
} else {
clipRect := command . ClipRect ( )
2018-12-31 21:10:58 +00:00
gl . Scissor ( int32 ( clipRect . X ) , int32 ( c . bufferSize . Y ) - int32 ( clipRect . W ) , int32 ( clipRect . Z - clipRect . X ) , int32 ( clipRect . W - clipRect . Y ) )
2018-12-27 22:35:06 +00:00
gl . BindTexture ( gl . TEXTURE_2D , uint32 ( command . TextureID ( ) ) )
gl . DrawElements ( gl . TRIANGLES , int32 ( command . ElementCount ( ) ) , uint32 ( drawType ) , unsafe . Pointer ( indexBufferOffset ) )
}
indexBufferOffset += uintptr ( command . ElementCount ( ) * indexSize )
}
}
// Restore modified state
gl . DisableClientState ( gl . COLOR_ARRAY )
gl . DisableClientState ( gl . TEXTURE_COORD_ARRAY )
gl . DisableClientState ( gl . VERTEX_ARRAY )
gl . BindTexture ( gl . TEXTURE_2D , uint32 ( lastTexture ) )
gl . MatrixMode ( gl . MODELVIEW )
gl . PopMatrix ( )
gl . MatrixMode ( gl . PROJECTION )
gl . PopMatrix ( )
gl . PopAttrib ( )
gl . PolygonMode ( gl . FRONT , uint32 ( lastPolygonMode [ 0 ] ) )
gl . PolygonMode ( gl . BACK , uint32 ( lastPolygonMode [ 1 ] ) )
gl . Viewport ( lastViewport [ 0 ] , lastViewport [ 1 ] , lastViewport [ 2 ] , lastViewport [ 3 ] )
gl . Scissor ( lastScissorBox [ 0 ] , lastScissorBox [ 1 ] , lastScissorBox [ 2 ] , lastScissorBox [ 3 ] )
2018-12-28 02:28:03 +00:00
return nil
2018-12-27 22:35:06 +00:00
}