Adding modding folder with a hacked build.
This commit is contained in:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user