239 lines
7.2 KiB
Go
239 lines
7.2 KiB
Go
// Package page provides the interface for pages
|
|
package page
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/go-gl/gl/v3.3-core/gl"
|
|
"github.com/go-gl/mathgl/mgl32"
|
|
"github.com/veandco/go-sdl2/sdl"
|
|
"rhemawavetech.com/rhemawave/internal/logger"
|
|
"rhemawavetech.com/rhemawave/internal/shaderManager"
|
|
)
|
|
|
|
// Page defines the contract for all application screens.
|
|
type Page interface {
|
|
Init(ctx *PageCtx) error
|
|
HandleEvent(event *sdl.Event) error
|
|
Update(dt float32) error
|
|
Render() error
|
|
Destroy() error
|
|
}
|
|
|
|
// Base is a helper struct that pages can embed to get common functionality.
|
|
type Base struct {
|
|
Context *PageCtx
|
|
QuadVAO uint32
|
|
QuadVBO uint32
|
|
Projection mgl32.Mat4
|
|
ScreenWidth int32
|
|
ScreenHeight int32
|
|
ScreenCenterX int32
|
|
ScreenCenterY int32
|
|
}
|
|
|
|
// Init stores the application context and sets up common resources.
|
|
func (p *Base) Init(ctx *PageCtx) error {
|
|
p.Context = ctx
|
|
vertices := []float32{
|
|
// pos(x,y) tex(u,v)
|
|
0.0, 1.0, 0.0, 1.0,
|
|
1.0, 0.0, 1.0, 0.0,
|
|
0.0, 0.0, 0.0, 0.0,
|
|
|
|
0.0, 1.0, 0.0, 1.0,
|
|
1.0, 1.0, 1.0, 1.0,
|
|
1.0, 0.0, 1.0, 0.0,
|
|
}
|
|
|
|
W := p.Context.W
|
|
H := p.Context.H
|
|
|
|
p.Projection = mgl32.Ortho(0, float32(W), 0, float32(H), -1, 1)
|
|
p.ScreenHeight = H
|
|
p.ScreenWidth = W
|
|
p.ScreenCenterY = H / 2
|
|
p.ScreenCenterX = W / 2
|
|
|
|
gl.GenVertexArrays(1, &p.QuadVAO)
|
|
gl.GenBuffers(1, &p.QuadVBO)
|
|
gl.BindVertexArray(p.QuadVAO)
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, p.QuadVBO)
|
|
gl.BufferData(gl.ARRAY_BUFFER, len(vertices)*4, gl.Ptr(vertices), gl.STATIC_DRAW)
|
|
|
|
stride := int32(4 * 4) // Each vertex has 4 floats (pos.x, pos.y, tex.u, tex.v)
|
|
|
|
// Attribute 0: Vertex Position (vec2)
|
|
gl.EnableVertexAttribArray(0)
|
|
gl.VertexAttribPointerWithOffset(0, 2, gl.FLOAT, false, stride, 0)
|
|
|
|
// Attribute 1: Texture Coordinate (vec2)
|
|
gl.EnableVertexAttribArray(1)
|
|
gl.VertexAttribPointerWithOffset(1, 2, gl.FLOAT, false, stride, 2*4) // Offset by 2 floats
|
|
|
|
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
|
|
gl.BindVertexArray(0)
|
|
gl.Disable(gl.BLEND)
|
|
gl.UseProgram(0)
|
|
return nil
|
|
}
|
|
|
|
// Destroy cleans up the common OpenGL resources.
|
|
func (p *Base) Destroy() error {
|
|
gl.DeleteBuffers(1, &p.QuadVBO)
|
|
gl.DeleteVertexArrays(1, &p.QuadVAO)
|
|
return nil
|
|
}
|
|
|
|
// BeginTextureRendering sets up shared GL state for a batch of RenderTexture calls.
|
|
// Call this once before a series of RenderTexture calls, then EndTextureRendering after.
|
|
func (p *Base) BeginTextureRendering(shader *shaderManager.Shader) {
|
|
gl.UseProgram(shader.ProgramID)
|
|
gl.Uniform1i(shader.LocTexture, 0)
|
|
gl.ActiveTexture(gl.TEXTURE0)
|
|
gl.Enable(gl.BLEND)
|
|
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
|
gl.BindVertexArray(p.QuadVAO)
|
|
}
|
|
|
|
// EndTextureRendering restores GL state after a batch of RenderTexture calls.
|
|
func (p *Base) EndTextureRendering() {
|
|
gl.BindVertexArray(0)
|
|
gl.Disable(gl.BLEND)
|
|
gl.UseProgram(0)
|
|
}
|
|
|
|
// RenderTexture draws a given texture to the screen at a specified position and size.
|
|
// For best performance, wrap multiple calls with BeginTextureRendering/EndTextureRendering.
|
|
// This method also works standalone for one-off renders.
|
|
func (p *Base) RenderTexture(shader *shaderManager.Shader, textureID uint32, position, size mgl32.Vec2, color sdl.Color) error {
|
|
gl.UseProgram(shader.ProgramID)
|
|
gl.Uniform1i(shader.LocTexture, 0)
|
|
|
|
if shader.LocTextColor == -1 {
|
|
return fmt.Errorf("could not find 'u_textColor' uniform in shader program %d", shader.ProgramID)
|
|
}
|
|
gl.Uniform4f(shader.LocTextColor, float32(color.R)/255.0, float32(color.G)/255.0, float32(color.B)/255.0, float32(color.A)/255.0)
|
|
|
|
model := mgl32.Ident4()
|
|
model = model.Mul4(mgl32.Translate3D(position.X(), position.Y(), 0))
|
|
model = model.Mul4(mgl32.Scale3D(size.X(), size.Y(), 1))
|
|
mvp := p.Projection.Mul4(model)
|
|
gl.UniformMatrix4fv(shader.LocMVPMatrix, 1, false, &mvp[0])
|
|
|
|
gl.ActiveTexture(gl.TEXTURE0)
|
|
gl.BindTexture(gl.TEXTURE_2D, textureID)
|
|
gl.Enable(gl.BLEND)
|
|
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
|
|
|
gl.BindVertexArray(p.QuadVAO)
|
|
gl.DrawArrays(gl.TRIANGLES, 0, 6)
|
|
|
|
gl.BindVertexArray(0)
|
|
gl.Disable(gl.BLEND)
|
|
gl.UseProgram(0)
|
|
return nil
|
|
}
|
|
|
|
// RenderTextureBatched is a lightweight version of RenderTexture for use inside
|
|
// a BeginTextureRendering/EndTextureRendering block. It skips redundant state setup.
|
|
func (p *Base) RenderTextureBatched(shader *shaderManager.Shader, textureID uint32, position, size mgl32.Vec2, color sdl.Color) {
|
|
gl.Uniform4f(shader.LocTextColor, float32(color.R)/255.0, float32(color.G)/255.0, float32(color.B)/255.0, float32(color.A)/255.0)
|
|
|
|
model := mgl32.Ident4()
|
|
model = model.Mul4(mgl32.Translate3D(position.X(), position.Y(), 0))
|
|
model = model.Mul4(mgl32.Scale3D(size.X(), size.Y(), 1))
|
|
mvp := p.Projection.Mul4(model)
|
|
gl.UniformMatrix4fv(shader.LocMVPMatrix, 1, false, &mvp[0])
|
|
|
|
gl.BindTexture(gl.TEXTURE_2D, textureID)
|
|
gl.DrawArrays(gl.TRIANGLES, 0, 6)
|
|
}
|
|
|
|
// RenderSolidColorQuad draws a rectangle of a solid color to the screen.
|
|
func (p *Base) RenderSolidColorQuad(shader *shaderManager.Shader, position, size mgl32.Vec2, color sdl.Color) error {
|
|
gl.UseProgram(shader.ProgramID)
|
|
|
|
model := mgl32.Ident4()
|
|
model = model.Mul4(mgl32.Translate3D(position.X(), position.Y(), 0))
|
|
model = model.Mul4(mgl32.Scale3D(size.X(), size.Y(), 1))
|
|
mvp := p.Projection.Mul4(model)
|
|
|
|
if shader.LocMVPMatrix == -1 {
|
|
return fmt.Errorf("RenderSolidColorQuad: could not find 'u_mvpMatrix' uniform")
|
|
}
|
|
gl.UniformMatrix4fv(shader.LocMVPMatrix, 1, false, &mvp[0])
|
|
|
|
if shader.LocColor == -1 {
|
|
return fmt.Errorf("RenderSolidColorQuad: could not find 'u_color' uniform")
|
|
}
|
|
gl.Uniform4f(shader.LocColor, float32(color.R)/255.0, float32(color.G)/255.0, float32(color.B)/255.0, float32(color.A)/255.0)
|
|
|
|
gl.Enable(gl.BLEND)
|
|
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
|
|
|
gl.BindVertexArray(p.QuadVAO)
|
|
gl.DrawArrays(gl.TRIANGLES, 0, 6)
|
|
|
|
gl.BindVertexArray(0)
|
|
gl.Disable(gl.BLEND)
|
|
gl.UseProgram(0)
|
|
return nil
|
|
}
|
|
|
|
// Provide default implementations for the Page interface.
|
|
func (p *Base) HandleEvent(event sdl.Event) error { return nil }
|
|
func (p *Base) Update(dt float32) error { return nil }
|
|
func (p *Base) Render() error { return nil }
|
|
|
|
// --- Helper Functions ---
|
|
// keycodeToChar converts an SDL keycode to a rune, respecting SHIFT state.
|
|
// Returns 0 for non-printable keys.
|
|
func keycodeToChar(sym sdl.Keycode, mod uint16) rune {
|
|
shifted := mod&sdl.KMOD_SHIFT != 0
|
|
|
|
// Letters
|
|
if sym >= sdl.K_a && sym <= sdl.K_z {
|
|
ch := rune(sym)
|
|
if shifted {
|
|
ch -= 'a' - 'A'
|
|
}
|
|
logger.Info("Wifi Password entered: %v", ch)
|
|
return ch
|
|
}
|
|
|
|
// Digits and their shifted symbols
|
|
if sym >= sdl.K_0 && sym <= sdl.K_9 {
|
|
if shifted {
|
|
shiftDigits := []rune{')', '!', '@', '#', '$', '%', '^', '&', '*', '('}
|
|
return shiftDigits[sym-sdl.K_0]
|
|
}
|
|
return rune(sym)
|
|
}
|
|
|
|
// Common punctuation / symbols
|
|
type charPair struct{ normal, shifted rune }
|
|
punctuation := map[sdl.Keycode]charPair{
|
|
sdl.K_SPACE: {' ', ' '},
|
|
sdl.K_MINUS: {'-', '_'},
|
|
sdl.K_EQUALS: {'=', '+'},
|
|
sdl.K_LEFTBRACKET: {'[', '{'},
|
|
sdl.K_RIGHTBRACKET: {']', '}'},
|
|
sdl.K_BACKSLASH: {'\\', '|'},
|
|
sdl.K_SEMICOLON: {';', ':'},
|
|
sdl.K_QUOTE: {'\'', '"'},
|
|
sdl.K_BACKQUOTE: {'`', '~'},
|
|
sdl.K_COMMA: {',', '<'},
|
|
sdl.K_PERIOD: {'.', '>'},
|
|
sdl.K_SLASH: {'/', '?'},
|
|
}
|
|
if pair, ok := punctuation[sym]; ok {
|
|
if shifted {
|
|
return pair.shifted
|
|
}
|
|
return pair.normal
|
|
}
|
|
|
|
return 0
|
|
}
|