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")
}