From 419dc667c49a209ea35163f6771e0f4bbcc9259b Mon Sep 17 00:00:00 2001 From: bloeys Date: Sat, 13 Nov 2021 23:33:50 +0400 Subject: [PATCH] Day6: MVP matrix+movement+ImGUI rendering but no events --- go.mod | 7 +- go.sum | 12 ++ main.go | 256 +++++++++++++++++++++++++++++++++-- res/shaders/imgui.frag.glsl | 13 ++ res/shaders/imgui.vert.glsl | 17 +++ res/shaders/simple.vert.glsl | 5 +- shaders/shader_program.go | 6 +- 7 files changed, 303 insertions(+), 13 deletions(-) create mode 100755 res/shaders/imgui.frag.glsl create mode 100755 res/shaders/imgui.vert.glsl diff --git a/go.mod b/go.mod index 189971f..afd1501 100755 --- a/go.mod +++ b/go.mod @@ -6,4 +6,9 @@ require github.com/veandco/go-sdl2 v0.4.10 require github.com/go-gl/gl v0.0.0-20210905235341-f7a045908259 -require github.com/bloeys/gglm v0.1.1 +require github.com/bloeys/gglm v0.2.5 + +require ( + github.com/inkyblackness/imgui-go v1.12.0 // indirect + github.com/inkyblackness/imgui-go/v4 v4.3.0 // indirect +) diff --git a/go.sum b/go.sum index d14eb5a..f28238a 100755 --- a/go.sum +++ b/go.sum @@ -1,6 +1,18 @@ github.com/bloeys/gglm v0.1.1 h1:juLE2OqobKKamMq7IKkplOJcZaM45CAcmbS0rCND3Hc= github.com/bloeys/gglm v0.1.1/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk= +github.com/bloeys/gglm v0.2.4 h1:dHw+VHTdkr1GCsFWPJ7LAiEH1Ry+p4ReGhVK0/ssopo= +github.com/bloeys/gglm v0.2.4/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk= +github.com/bloeys/gglm v0.2.5 h1:ESCk+zSeun3ogm6IEJ+CwPSf6PunszWSkRw3MmG4OCk= +github.com/bloeys/gglm v0.2.5/go.mod h1:qwJQ0WzV191wAMwlGicbfbChbKoSedMk7gFFX6GnyOk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-gl/gl v0.0.0-20210905235341-f7a045908259 h1:8q7+xl2D2qHPLTII1t4vSMNP2VKwDcn+Avf2WXvdB1A= github.com/go-gl/gl v0.0.0-20210905235341-f7a045908259/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM= +github.com/inkyblackness/imgui-go v1.12.0 h1:uaxSM5SbbqCTGEx5ig7B2J78hM3g3az4f5NC6b4J7lY= +github.com/inkyblackness/imgui-go v1.12.0/go.mod h1:S9wTWrw/HfxYPbOnqsbck9A6mxHRauv+Sy+bz5+BQwc= +github.com/inkyblackness/imgui-go/v4 v4.3.0 h1:iyAzqWXq/dG5+6ckDPhGivtrIo6AywGQMvENKzun04s= +github.com/inkyblackness/imgui-go/v4 v4.3.0/go.mod h1:g8SAGtOYUP7rYaOB2AsVKCEHmPMDmJKgt4z6d+flhb0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/veandco/go-sdl2 v0.4.10 h1:8QoD2bhWl7SbQDflIAUYWfl9Vq+mT8/boJFAUzAScgY= github.com/veandco/go-sdl2 v0.4.10/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY= diff --git a/main.go b/main.go index 40d4c81..eb9db38 100755 --- a/main.go +++ b/main.go @@ -8,20 +8,44 @@ import ( "github.com/bloeys/go-sdl-engine/res/models" "github.com/bloeys/go-sdl-engine/shaders" "github.com/go-gl/gl/v4.6-core/gl" + "github.com/inkyblackness/imgui-go/v4" "github.com/veandco/go-sdl2/sdl" ) -const ( - winWidth = 1280 - winHeight = 720 -) +//TODO: +//Resizeable window - Done +//Moving camera - Done +//FIX Ortho function +//ImGUI integration +//Asset loading +//Rework buffers package +//Interleaved or packed buffers (xyzxyzxyz OR xxxyyyzzz) +//Timing and deltatime + +type ImguiInfo struct { + imCtx *imgui.Context + + vaoID uint32 + vboID uint32 + indexBufID uint32 + texID uint32 +} var ( + winWidth int32 = 1280 + winHeight int32 = 720 + isRunning bool = true window *sdl.Window simpleShader shaders.ShaderProgram + imShader shaders.ShaderProgram bo *buffers.BufferObject + + modelMat = gglm.NewTrMatId() + projMat = &gglm.Mat4{} + + imguiInfo *ImguiInfo ) func main() { @@ -46,6 +70,7 @@ func main() { initOpenGL() loadShaders() loadBuffers() + initImGUI() simpleShader.SetAttribute("vertPos", bo, bo.VertPosBuf) simpleShader.EnableAttribute("vertPos") @@ -53,14 +78,24 @@ func main() { // simpleShader.SetAttribute("vertColor", bo, bo.ColorBuf) // simpleShader.EnableAttribute("vertColor") - modelMat := gglm.NewTrMatId() - translationMat := gglm.NewTranslationMat(gglm.NewVec3(-0.5, 0, 0)) + //Movement, scale and rotation + translationMat := gglm.NewTranslationMat(gglm.NewVec3(0, 0, 0)) scaleMat := gglm.NewScaleMat(gglm.NewVec3(0.25, 0.25, 0.25)) rotMat := gglm.NewRotMat(gglm.NewQuatEuler(gglm.NewVec3(0, 0, 0).AsRad())) modelMat.Mul(translationMat.Mul(rotMat.Mul(scaleMat))) simpleShader.SetUnifMat4("modelMat", &modelMat.Mat4) + //Moves objects into the cameras view + camPos := gglm.NewVec3(0, 0, 10) + targetPos := gglm.NewVec3(0, 0, 0) + viewMat := gglm.LookAt(camPos, targetPos, gglm.NewVec3(0, 1, 0)) + simpleShader.SetUnifMat4("viewMat", &viewMat.Mat4) + + //Perspective/Depth + projMat := gglm.Perspective(45*gglm.Deg2Rad, float32(winWidth)/float32(winHeight), 0.1, 20) + simpleShader.SetUnifMat4("projMat", projMat) + //Game loop for isRunning { @@ -87,8 +122,10 @@ func initOpenGL() { sdl.GLSetAttribute(sdl.GL_BLUE_SIZE, 8) sdl.GLSetAttribute(sdl.GL_DOUBLEBUFFER, 1) - gl.ClearColor(0, 0, 0, 1) + sdl.GLSetAttribute(sdl.GL_DEPTH_SIZE, 24) + sdl.GLSetAttribute(sdl.GL_STENCIL_SIZE, 8) + gl.ClearColor(0, 0, 0, 1) sdl.GLSetAttribute(sdl.GL_CONTEXT_PROFILE_MASK, sdl.GL_CONTEXT_PROFILE_CORE) } @@ -113,6 +150,26 @@ func loadShaders() { simpleShader.AttachShader(vertShader) simpleShader.AttachShader(fragShader) simpleShader.Link() + + //ImGUI shader + imShader, err = shaders.NewShaderProgram() + if err != nil { + logging.ErrLog.Fatalln("Failed to create new shader program. Err: ", err) + } + + imguiVertShader, err := shaders.LoadAndCompilerShader("./res/shaders/imgui.vert.glsl", shaders.VertexShaderType) + if err != nil { + logging.ErrLog.Fatalln("Failed to create new shader. Err: ", err) + } + + imguiFragShader, err := shaders.LoadAndCompilerShader("./res/shaders/imgui.frag.glsl", shaders.FragmentShaderType) + if err != nil { + logging.ErrLog.Fatalln("Failed to create new shader. Err: ", err) + } + + imShader.AttachShader(imguiVertShader) + imShader.AttachShader(imguiFragShader) + imShader.Link() } func loadBuffers() { @@ -136,7 +193,7 @@ func loadBuffers() { if err != nil { panic(err) } - logging.InfoLog.Printf("%v", objInfo.TriIndices) + // logging.InfoLog.Printf("%v", objInfo.TriIndices) vertices = objInfo.VertPos indices = objInfo.TriIndices @@ -147,6 +204,40 @@ func loadBuffers() { bo.GenBufferUint32(indices, buffers.BufUsageStatic, buffers.BufTypeIndex, buffers.DataTypeUint32) } +func initImGUI() { + + imguiInfo = &ImguiInfo{ + imCtx: imgui.CreateContext(nil), + } + + imIO := imgui.CurrentIO() + imIO.SetBackendFlags(imIO.GetBackendFlags() | imgui.BackendFlagsRendererHasVtxOffset) + + gl.GenVertexArrays(1, &imguiInfo.vaoID) + gl.GenBuffers(1, &imguiInfo.vboID) + gl.GenBuffers(1, &imguiInfo.indexBufID) + gl.GenTextures(1, &imguiInfo.texID) + + // Upload font to gpu + gl.BindTexture(gl.TEXTURE_2D, imguiInfo.texID) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) + gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0) + + image := imIO.Fonts().TextureDataAlpha8() + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RED, int32(image.Width), int32(image.Height), 0, gl.RED, gl.UNSIGNED_BYTE, image.Pixels) + + // Store our identifier + imIO.Fonts().SetTextureID(imgui.TextureID(imguiInfo.texID)) + + //Shader attributes + imShader.Activate() + imShader.EnableAttribute("Position") + imShader.EnableAttribute("UV") + imShader.EnableAttribute("Color") + imShader.Deactivate() +} + func handleInputs() { input.EventLoopStart() @@ -159,18 +250,72 @@ func handleInputs() { input.HandleKeyboardEvent(e) case *sdl.MouseButtonEvent: input.HandleMouseEvent(e) + case *sdl.WindowEvent: + + if e.Type != sdl.WINDOWEVENT_RESIZED { + continue + } + + winWidth = e.Data1 + winHeight = e.Data2 + window.SetSize(int32(winWidth), int32(winHeight)) + + projMat = gglm.Perspective(45*gglm.Deg2Rad, float32(winWidth)/float32(winHeight), 0.1, 20) + simpleShader.SetUnifMat4("projMat", projMat) + case *sdl.QuitEvent: isRunning = false } } } +var time uint64 = 0 + func runGameLogic() { + if input.KeyDown(sdl.K_w) { + modelMat.Translate(gglm.NewVec3(0, 0, -0.1)) + } + if input.KeyDown(sdl.K_s) { + modelMat.Translate(gglm.NewVec3(0, 0, 0.1)) + } + if input.KeyDown(sdl.K_d) { + modelMat.Translate(gglm.NewVec3(0.1, 0, 0)) + } + if input.KeyDown(sdl.K_a) { + modelMat.Translate(gglm.NewVec3(-0.1, 0, 00)) + } + + simpleShader.SetUnifMat4("modelMat", &modelMat.Mat4) + + //ImGUI + imIO := imgui.CurrentIO() + imIO.SetDisplaySize(imgui.Vec2{X: float32(winWidth), Y: float32(winHeight)}) + + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) + frequency := sdl.GetPerformanceFrequency() + currentTime := sdl.GetPerformanceCounter() + if time > 0 { + imIO.SetDeltaTime(float32(currentTime-time) / float32(frequency)) + } else { + imIO.SetDeltaTime(1.0 / 60.0) + } + time = currentTime + + imgui.NewFrame() + + if imgui.Button("Click Me!") { + logging.InfoLog.Println("Clicked!") + } + + // open := true + // imgui.ShowDemoWindow(&open) + imgui.Render() } func draw() { + gl.Disable(gl.SCISSOR_TEST) gl.Clear(gl.COLOR_BUFFER_BIT) simpleShader.Activate() @@ -180,5 +325,100 @@ func draw() { gl.DrawElements(gl.TRIANGLES, 36, gl.UNSIGNED_INT, gl.PtrOffset(0)) bo.Deactivate() + drawUI() + window.GLSwap() } + +func drawUI() { + + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + fbWidth, fbHeight := window.GLGetDrawableSize() + if fbWidth <= 0 || fbHeight <= 0 { + return + } + gl.Viewport(0, 0, fbWidth, fbHeight) + drawData := imgui.RenderedDrawData() + drawData.ScaleClipRects(imgui.Vec2{ + X: float32(fbWidth) / float32(winWidth), + Y: float32(fbHeight) / float32(winHeight), + }) + + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill + gl.Enable(gl.BLEND) + gl.BlendEquation(gl.FUNC_ADD) + gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) + gl.Disable(gl.CULL_FACE) + gl.Disable(gl.DEPTH_TEST) + gl.Enable(gl.SCISSOR_TEST) + gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL) + + // 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. + orthoProjection := [4][4]float32{ + {2.0 / float32(winWidth), 0.0, 0.0, 0.0}, + {0.0, 2.0 / -float32(winHeight), 0.0, 0.0}, + {0.0, 0.0, -1.0, 0.0}, + {-1.0, 1.0, 0.0, 1.0}, + } + + imShader.Activate() + + gl.Uniform1i(gl.GetUniformLocation(imShader.ID, gl.Str("Texture\x00")), 0) + + gl.UniformMatrix4fv(gl.GetUniformLocation(imShader.ID, gl.Str("ProjMtx"+"\x00")), 1, false, &orthoProjection[0][0]) + // orthoMat := gglm.Ortho(0, float32(winWidth), float32(winHeight), 0, 0.1, 20) + // imShader.SetUnifMat4("ProjMtx", &orthoMat.Mat4) + gl.BindSampler(0, 0) // Rely on combined texture/sampler state. + + // Recreate the VAO every time + // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and + // we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.) + gl.BindVertexArray(imguiInfo.vaoID) + gl.BindBuffer(gl.ARRAY_BUFFER, imguiInfo.vboID) + + vertexSize, vertexOffsetPos, vertexOffsetUv, vertexOffsetCol := imgui.VertexBufferLayout() + imShader.EnableAttribute("Position") + imShader.EnableAttribute("UV") + imShader.EnableAttribute("Color") + gl.VertexAttribPointerWithOffset(uint32(imShader.GetAttribLoc("Position")), 2, gl.FLOAT, false, int32(vertexSize), uintptr(vertexOffsetPos)) + gl.VertexAttribPointerWithOffset(uint32(imShader.GetAttribLoc("UV")), 2, gl.FLOAT, false, int32(vertexSize), uintptr(vertexOffsetUv)) + gl.VertexAttribPointerWithOffset(uint32(imShader.GetAttribLoc("Color")), 4, gl.UNSIGNED_BYTE, true, int32(vertexSize), uintptr(vertexOffsetCol)) + + indexSize := imgui.IndexBufferLayout() + drawType := gl.UNSIGNED_SHORT + if indexSize == 4 { + drawType = gl.UNSIGNED_INT + } + + // Draw + for _, list := range drawData.CommandLists() { + + vertexBuffer, vertexBufferSize := list.VertexBuffer() + gl.BindBuffer(gl.ARRAY_BUFFER, imguiInfo.vboID) + gl.BufferData(gl.ARRAY_BUFFER, vertexBufferSize, vertexBuffer, gl.STREAM_DRAW) + + indexBuffer, indexBufferSize := list.IndexBuffer() + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, imguiInfo.indexBufID) + gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, indexBufferSize, indexBuffer, gl.STREAM_DRAW) + + for _, cmd := range list.Commands() { + if cmd.HasUserCallback() { + cmd.CallUserCallback(list) + } else { + + gl.BindTexture(gl.TEXTURE_2D, imguiInfo.texID) + // gl.BindTexture(gl.TEXTURE_2D, uint32(cmd.TextureID())) + clipRect := cmd.ClipRect() + gl.Scissor(int32(clipRect.X), int32(fbHeight)-int32(clipRect.W), int32(clipRect.Z-clipRect.X), int32(clipRect.W-clipRect.Y)) + + gl.DrawElementsBaseVertex(gl.TRIANGLES, int32(cmd.ElementCount()), uint32(drawType), gl.PtrOffset(cmd.IndexOffset()*indexSize), int32(cmd.VertexOffset())) + } + } + } + + gl.BindVertexArray(imguiInfo.vaoID) + gl.BindBuffer(gl.ARRAY_BUFFER, imguiInfo.vboID) + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, imguiInfo.indexBufID) +} diff --git a/res/shaders/imgui.frag.glsl b/res/shaders/imgui.frag.glsl new file mode 100755 index 0000000..a9be556 --- /dev/null +++ b/res/shaders/imgui.frag.glsl @@ -0,0 +1,13 @@ +#version 460 + +uniform sampler2D Texture; + +in vec2 Frag_UV; +in vec4 Frag_Color; + +out vec4 Out_Color; + +void main() +{ + Out_Color = vec4(Frag_Color.rgb, Frag_Color.a * texture(Texture, Frag_UV.st).r); +} \ No newline at end of file diff --git a/res/shaders/imgui.vert.glsl b/res/shaders/imgui.vert.glsl new file mode 100755 index 0000000..6ec7d8d --- /dev/null +++ b/res/shaders/imgui.vert.glsl @@ -0,0 +1,17 @@ +#version 460 + +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); +} \ No newline at end of file diff --git a/res/shaders/simple.vert.glsl b/res/shaders/simple.vert.glsl index aab2d12..82f7199 100755 --- a/res/shaders/simple.vert.glsl +++ b/res/shaders/simple.vert.glsl @@ -5,10 +5,13 @@ in vec3 vertColor; out vec3 outColor; +//MVP = Model View Projection uniform mat4 modelMat; +uniform mat4 viewMat; +uniform mat4 projMat; void main() { outColor = vertColor; - gl_Position = modelMat * (vec4(vertPos, 1.0)); // vec4(vertPos.x, vertPos.y, vertPos.z, 1.0) + gl_Position = projMat * viewMat * modelMat * (vec4(vertPos, 1.0)); // vec4(vertPos.x, vertPos.y, vertPos.z, 1.0) } \ No newline at end of file diff --git a/shaders/shader_program.go b/shaders/shader_program.go index 4daa1ed..fc0b3ec 100755 --- a/shaders/shader_program.go +++ b/shaders/shader_program.go @@ -87,17 +87,17 @@ func (sp *ShaderProgram) SetUnifVec4(uniformName string, vec4 *gglm.Vec4) { func (sp *ShaderProgram) SetUnifMat2(uniformName string, mat2 *gglm.Mat2) { loc := gl.GetUniformLocation(sp.ID, gl.Str(uniformName+"\x00")) - gl.ProgramUniformMatrix2fv(sp.ID, loc, 1, true, &mat2.Data[0]) + gl.ProgramUniformMatrix2fv(sp.ID, loc, 1, false, &mat2.Data[0][0]) } func (sp *ShaderProgram) SetUnifMat3(uniformName string, mat3 *gglm.Mat3) { loc := gl.GetUniformLocation(sp.ID, gl.Str(uniformName+"\x00")) - gl.ProgramUniformMatrix3fv(sp.ID, loc, 1, true, &mat3.Data[0]) + gl.ProgramUniformMatrix3fv(sp.ID, loc, 1, false, &mat3.Data[0][0]) } func (sp *ShaderProgram) SetUnifMat4(uniformName string, mat4 *gglm.Mat4) { loc := gl.GetUniformLocation(sp.ID, gl.Str(uniformName+"\x00")) - gl.ProgramUniformMatrix4fv(sp.ID, loc, 1, true, &mat4.Data[0]) + gl.ProgramUniformMatrix4fv(sp.ID, loc, 1, false, &mat4.Data[0][0]) } func (sp *ShaderProgram) Delete() {