|
|
|
@@ -2,8 +2,6 @@ package page
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/go-gl/gl/v3.3-core/gl"
|
|
|
|
|
"github.com/go-gl/mathgl/mgl32"
|
|
|
|
@@ -30,8 +28,8 @@ type Home struct {
|
|
|
|
|
emailDisplay StringItem
|
|
|
|
|
licenseKeyDisplay StringItem
|
|
|
|
|
hasLicenseInfo bool
|
|
|
|
|
lastDisplayUpdate time.Time
|
|
|
|
|
baseFontSize int // cached from db at init
|
|
|
|
|
baseFontSize int
|
|
|
|
|
isDownloadComplete bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Init performs basic page initialization and creation of static assets.
|
|
|
|
@@ -82,6 +80,12 @@ func (p *Home) Init(ctx *PageCtx) error {
|
|
|
|
|
// Create download channel and message
|
|
|
|
|
p.downloadProgressChan = make(chan download.DownloadStatus)
|
|
|
|
|
|
|
|
|
|
// Initialize message
|
|
|
|
|
p.message, err = NewStringItem("Status: Initializing...", font, colors.White)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("unable to create initial message texture: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create license info textures if data exists
|
|
|
|
|
if p.Context.CfgPtr.EmailAddress != "" || p.Context.CfgPtr.LicenseKey != "" {
|
|
|
|
|
p.hasLicenseInfo = true
|
|
|
|
@@ -113,44 +117,33 @@ func (p *Home) Init(ctx *PageCtx) error {
|
|
|
|
|
|
|
|
|
|
// Get base font size from database
|
|
|
|
|
baseFontSize := p.Context.CfgPtr.StandardFontSize
|
|
|
|
|
|
|
|
|
|
p.baseFontSize = baseFontSize
|
|
|
|
|
|
|
|
|
|
// Scale the note text
|
|
|
|
|
fontSize := int32(32)
|
|
|
|
|
scale := float32(fontSize) / float32(baseFontSize)
|
|
|
|
|
p.note.Scale(scale * 0.75)
|
|
|
|
|
p.note.W = int32(float32(p.note.W) * scale * 0.75)
|
|
|
|
|
p.note.H = int32(float32(p.note.H) * scale * 0.75)
|
|
|
|
|
|
|
|
|
|
// Scale license info texts
|
|
|
|
|
if p.hasLicenseInfo {
|
|
|
|
|
licenseInfoScale := float32(28) / float32(baseFontSize)
|
|
|
|
|
p.licenseInfoHeading.Scale(licenseInfoScale)
|
|
|
|
|
p.licenseInfoHeading.W = int32(float32(p.licenseInfoHeading.W) * licenseInfoScale)
|
|
|
|
|
p.licenseInfoHeading.H = int32(float32(p.licenseInfoHeading.H) * licenseInfoScale)
|
|
|
|
|
if p.emailDisplay.ID != 0 {
|
|
|
|
|
p.emailDisplay.Scale(licenseInfoScale * 0.85)
|
|
|
|
|
p.emailDisplay.W = int32(float32(p.emailDisplay.W) * licenseInfoScale * 0.85)
|
|
|
|
|
p.emailDisplay.H = int32(float32(p.emailDisplay.H) * licenseInfoScale * 0.85)
|
|
|
|
|
}
|
|
|
|
|
if p.licenseKeyDisplay.ID != 0 {
|
|
|
|
|
p.licenseKeyDisplay.Scale(licenseInfoScale * 0.85)
|
|
|
|
|
p.licenseKeyDisplay.W = int32(float32(p.licenseKeyDisplay.W) * licenseInfoScale * 0.85)
|
|
|
|
|
p.licenseKeyDisplay.H = int32(float32(p.licenseKeyDisplay.H) * licenseInfoScale * 0.85)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
audioFileNames := []string{
|
|
|
|
|
"OriginalAudio.wav",
|
|
|
|
|
"174Hz.wav",
|
|
|
|
|
"396Hz.wav",
|
|
|
|
|
"417Hz.wav",
|
|
|
|
|
"432Hz.wav",
|
|
|
|
|
"639Hz.wav",
|
|
|
|
|
"741Hz.wav",
|
|
|
|
|
"852Hz.wav",
|
|
|
|
|
"963Hz.wav",
|
|
|
|
|
"1111Hz.wav",
|
|
|
|
|
"14Hz_Binaural.wav",
|
|
|
|
|
"40Hz_Binaural.wav",
|
|
|
|
|
"AlphaWaves.wav",
|
|
|
|
|
"PinealGland.wav",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
go download.DownloadAudioFiles(p.downloadProgressChan, audioFileNames, filepath.Join(ctx.CfgPtr.AssetsDir, "audio"))
|
|
|
|
|
// Scale message text
|
|
|
|
|
messageScale := float32(28) / float32(baseFontSize)
|
|
|
|
|
p.message.W = int32(float32(p.message.W) * messageScale)
|
|
|
|
|
p.message.H = int32(float32(p.message.H) * messageScale)
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
@@ -162,20 +155,44 @@ func (p *Home) HandleEvent(event *sdl.Event) error {
|
|
|
|
|
|
|
|
|
|
// Update updates animations and changing textures and other changes using `dt` which represents a time delta in float32.
|
|
|
|
|
func (p *Home) Update(dt float32) error {
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case progressUpdate := <-p.downloadProgressChan:
|
|
|
|
|
if progressUpdate.Err != nil {
|
|
|
|
|
p.message, err = NewStringItem(progressUpdate.Msg, p.font, colors.White)
|
|
|
|
|
case progressUpdate, ok := <-p.Context.MessageChannel:
|
|
|
|
|
if !ok {
|
|
|
|
|
// Channel is closed, set message to READY
|
|
|
|
|
newMsg, err := NewStringItem("Status: READY", p.font, colors.Green)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
messageScale := float32(28) / float32(p.baseFontSize)
|
|
|
|
|
newMsg.W = int32(float32(newMsg.W) * messageScale)
|
|
|
|
|
newMsg.H = int32(float32(newMsg.H) * messageScale)
|
|
|
|
|
p.message = newMsg
|
|
|
|
|
p.isDownloadComplete = true
|
|
|
|
|
} else if progressUpdate.Err != nil {
|
|
|
|
|
// Handle error
|
|
|
|
|
newMsg, err := NewStringItem(fmt.Sprintf("Status: %s", progressUpdate.Err.Error), p.font, colors.Red)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
messageScale := float32(28) / float32(p.baseFontSize)
|
|
|
|
|
newMsg.W = int32(float32(newMsg.W) * messageScale)
|
|
|
|
|
newMsg.H = int32(float32(newMsg.H) * messageScale)
|
|
|
|
|
p.message = newMsg
|
|
|
|
|
} else {
|
|
|
|
|
p.message, err = NewStringItem(progressUpdate.Err.Error(), p.font, colors.Red)
|
|
|
|
|
// Update progress message
|
|
|
|
|
percentComplete := float64(progressUpdate.Cur) / float64(progressUpdate.Max) * 100
|
|
|
|
|
msg := fmt.Sprintf("Status: Downloading assets %.1f%% Complete", percentComplete)
|
|
|
|
|
newMsg, err := NewStringItem(msg, p.font, colors.White)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
messageScale := float32(28) / float32(p.baseFontSize)
|
|
|
|
|
newMsg.W = int32(float32(newMsg.W) * messageScale)
|
|
|
|
|
newMsg.H = int32(float32(newMsg.H) * messageScale)
|
|
|
|
|
p.message = newMsg
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return nil
|
|
|
|
|
// No update, do nothing
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
@@ -184,6 +201,7 @@ func (p *Home) Render() error {
|
|
|
|
|
baseFontSize := p.baseFontSize
|
|
|
|
|
sm := shaderManager.Get()
|
|
|
|
|
verticalMargin := int32(20)
|
|
|
|
|
licenseInfoMargin := int32(10)
|
|
|
|
|
|
|
|
|
|
// 1) Calculate scales
|
|
|
|
|
titleFontSize := int32(64)
|
|
|
|
@@ -205,9 +223,26 @@ func (p *Home) Render() error {
|
|
|
|
|
promptH := float32(p.prompt.H) * bodyScale
|
|
|
|
|
promptDim := mgl32.Vec2{promptW, promptH}
|
|
|
|
|
|
|
|
|
|
// p.note already scaled in Init via p.note.Scale(scale*0.75)
|
|
|
|
|
noteDim := p.note.RawDimensions()
|
|
|
|
|
noteW, noteH := noteDim[0], noteDim[1]
|
|
|
|
|
// Note dimensions (already scaled in Init)
|
|
|
|
|
noteW := float32(p.note.W)
|
|
|
|
|
noteH := float32(p.note.H)
|
|
|
|
|
|
|
|
|
|
// Message dimensions (already scaled in Init/Update)
|
|
|
|
|
messageW := float32(p.message.W)
|
|
|
|
|
messageH := float32(p.message.H)
|
|
|
|
|
|
|
|
|
|
// License info dimensions (already scaled in Init)
|
|
|
|
|
licenseHeadingW := float32(p.licenseInfoHeading.W)
|
|
|
|
|
licenseHeadingH := float32(p.licenseInfoHeading.H)
|
|
|
|
|
var emailDisplayW, emailDisplayH, licenseKeyDisplayW, licenseKeyDisplayH float32
|
|
|
|
|
if p.emailDisplay.ID != 0 {
|
|
|
|
|
emailDisplayW = float32(p.emailDisplay.W)
|
|
|
|
|
emailDisplayH = float32(p.emailDisplay.H)
|
|
|
|
|
}
|
|
|
|
|
if p.licenseKeyDisplay.ID != 0 {
|
|
|
|
|
licenseKeyDisplayW = float32(p.licenseKeyDisplay.W)
|
|
|
|
|
licenseKeyDisplayH = float32(p.licenseKeyDisplay.H)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3) Calculate total height of the vertical stack
|
|
|
|
|
totalH := int32(titleH) + verticalMargin +
|
|
|
|
@@ -218,7 +253,7 @@ func (p *Home) Render() error {
|
|
|
|
|
// 4) Y-up layout: start from the TOP of the centered stack and step DOWN
|
|
|
|
|
top := p.Base.ScreenCenterY + totalH/2
|
|
|
|
|
|
|
|
|
|
titleY := top - int32(titleH) // Title sits at the top
|
|
|
|
|
titleY := top - int32(titleH)
|
|
|
|
|
versionY := titleY - verticalMargin - int32(versionH)
|
|
|
|
|
p.prompt.Y = versionY - verticalMargin - int32(promptH)
|
|
|
|
|
p.note.Y = p.prompt.Y - verticalMargin - int32(noteH)
|
|
|
|
@@ -234,23 +269,8 @@ func (p *Home) Render() error {
|
|
|
|
|
|
|
|
|
|
// 6) Position license info at bottom center
|
|
|
|
|
if p.hasLicenseInfo {
|
|
|
|
|
licenseInfoMargin := int32(10)
|
|
|
|
|
bottomMargin := int32(20)
|
|
|
|
|
|
|
|
|
|
// License info items are already scaled in Init
|
|
|
|
|
licenseHeadingDim := p.licenseInfoHeading.RawDimensions()
|
|
|
|
|
licenseHeadingW, licenseHeadingH := licenseHeadingDim[0], licenseHeadingDim[1]
|
|
|
|
|
|
|
|
|
|
var emailDisplayH, licenseKeyDisplayH float32
|
|
|
|
|
if p.emailDisplay.ID != 0 {
|
|
|
|
|
emailDisplayDim := p.emailDisplay.RawDimensions()
|
|
|
|
|
emailDisplayH = emailDisplayDim[1]
|
|
|
|
|
}
|
|
|
|
|
if p.licenseKeyDisplay.ID != 0 {
|
|
|
|
|
licenseKeyDisplayDim := p.licenseKeyDisplay.RawDimensions()
|
|
|
|
|
licenseKeyDisplayH = licenseKeyDisplayDim[1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate total height of license info block
|
|
|
|
|
licenseInfoTotalH := int32(licenseHeadingH)
|
|
|
|
|
if p.emailDisplay.ID != 0 {
|
|
|
|
@@ -265,8 +285,6 @@ func (p *Home) Render() error {
|
|
|
|
|
|
|
|
|
|
// License key (bottom-most if it exists)
|
|
|
|
|
if p.licenseKeyDisplay.ID != 0 {
|
|
|
|
|
licenseKeyDisplayDim := p.licenseKeyDisplay.RawDimensions()
|
|
|
|
|
licenseKeyDisplayW := licenseKeyDisplayDim[0]
|
|
|
|
|
p.licenseKeyDisplay.X = p.Base.ScreenCenterX - int32(licenseKeyDisplayW/2)
|
|
|
|
|
p.licenseKeyDisplay.Y = currentY
|
|
|
|
|
currentY += int32(licenseKeyDisplayH) + licenseInfoMargin
|
|
|
|
@@ -274,8 +292,6 @@ func (p *Home) Render() error {
|
|
|
|
|
|
|
|
|
|
// Email
|
|
|
|
|
if p.emailDisplay.ID != 0 {
|
|
|
|
|
emailDisplayDim := p.emailDisplay.RawDimensions()
|
|
|
|
|
emailDisplayW := emailDisplayDim[0]
|
|
|
|
|
p.emailDisplay.X = p.Base.ScreenCenterX - int32(emailDisplayW/2)
|
|
|
|
|
p.emailDisplay.Y = currentY
|
|
|
|
|
currentY += int32(emailDisplayH) + licenseInfoMargin
|
|
|
|
@@ -286,6 +302,26 @@ func (p *Home) Render() error {
|
|
|
|
|
p.licenseInfoHeading.Y = currentY
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 7) Position message between license info and note/prompt
|
|
|
|
|
if p.hasLicenseInfo {
|
|
|
|
|
// Calculate the top of the license info block
|
|
|
|
|
licenseInfoTopY := p.licenseInfoHeading.Y + int32(licenseHeadingH)
|
|
|
|
|
if p.emailDisplay.ID != 0 {
|
|
|
|
|
licenseInfoTopY += int32(emailDisplayH) + licenseInfoMargin
|
|
|
|
|
}
|
|
|
|
|
if p.licenseKeyDisplay.ID != 0 {
|
|
|
|
|
licenseInfoTopY += int32(licenseKeyDisplayH) + licenseInfoMargin
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Center the message vertically between license info and note
|
|
|
|
|
availableSpace := p.note.Y - licenseInfoTopY - verticalMargin
|
|
|
|
|
p.message.Y = licenseInfoTopY + availableSpace/2 - int32(messageH/2)
|
|
|
|
|
} else {
|
|
|
|
|
// If no license info, place message below note
|
|
|
|
|
p.message.Y = p.note.Y - verticalMargin - int32(messageH)
|
|
|
|
|
}
|
|
|
|
|
p.message.X = p.Base.ScreenCenterX - int32(messageW/2)
|
|
|
|
|
|
|
|
|
|
// 8) Get shader and render
|
|
|
|
|
textShader, ok := sm.Get("text")
|
|
|
|
|
if !ok {
|
|
|
|
@@ -295,17 +331,26 @@ func (p *Home) Render() error {
|
|
|
|
|
|
|
|
|
|
p.Base.RenderTexture(textShader, p.title.ID, titlePos, titleDim, color)
|
|
|
|
|
p.Base.RenderTexture(textShader, p.version.ID, versionPos, versionDim, color)
|
|
|
|
|
p.Base.RenderTexture(textShader, p.prompt.ID, p.prompt.Position(), promptDim, color)
|
|
|
|
|
p.Base.RenderTexture(textShader, p.note.ID, p.note.Position(), p.note.RawDimensions(), color)
|
|
|
|
|
p.Base.RenderTexture(textShader, p.prompt.ID, mgl32.Vec2{float32(p.prompt.X), float32(p.prompt.Y)}, promptDim, color)
|
|
|
|
|
p.Base.RenderTexture(textShader, p.note.ID, mgl32.Vec2{float32(p.note.X), float32(p.note.Y)}, mgl32.Vec2{noteW, noteH}, color)
|
|
|
|
|
|
|
|
|
|
// Render message
|
|
|
|
|
if p.message.ID != 0 {
|
|
|
|
|
msgColor := colors.White
|
|
|
|
|
if p.isDownloadComplete {
|
|
|
|
|
msgColor = colors.Green
|
|
|
|
|
}
|
|
|
|
|
p.Base.RenderTexture(textShader, p.message.ID, mgl32.Vec2{float32(p.message.X), float32(p.message.Y)}, mgl32.Vec2{messageW, messageH}, msgColor)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Render license info if it exists
|
|
|
|
|
if p.hasLicenseInfo {
|
|
|
|
|
p.Base.RenderTexture(textShader, p.licenseInfoHeading.ID, p.licenseInfoHeading.Position(), p.licenseInfoHeading.RawDimensions(), color)
|
|
|
|
|
p.Base.RenderTexture(textShader, p.licenseInfoHeading.ID, mgl32.Vec2{float32(p.licenseInfoHeading.X), float32(p.licenseInfoHeading.Y)}, mgl32.Vec2{licenseHeadingW, licenseHeadingH}, color)
|
|
|
|
|
if p.emailDisplay.ID != 0 {
|
|
|
|
|
p.Base.RenderTexture(textShader, p.emailDisplay.ID, p.emailDisplay.Position(), p.emailDisplay.RawDimensions(), color)
|
|
|
|
|
p.Base.RenderTexture(textShader, p.emailDisplay.ID, mgl32.Vec2{float32(p.emailDisplay.X), float32(p.emailDisplay.Y)}, mgl32.Vec2{emailDisplayW, emailDisplayH}, color)
|
|
|
|
|
}
|
|
|
|
|
if p.licenseKeyDisplay.ID != 0 {
|
|
|
|
|
p.Base.RenderTexture(textShader, p.licenseKeyDisplay.ID, p.licenseKeyDisplay.Position(), p.licenseKeyDisplay.RawDimensions(), color)
|
|
|
|
|
p.Base.RenderTexture(textShader, p.licenseKeyDisplay.ID, mgl32.Vec2{float32(p.licenseKeyDisplay.X), float32(p.licenseKeyDisplay.Y)}, mgl32.Vec2{licenseKeyDisplayW, licenseKeyDisplayH}, color)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -318,6 +363,7 @@ func (p *Home) Destroy() error {
|
|
|
|
|
gl.DeleteTextures(1, &p.version.ID)
|
|
|
|
|
gl.DeleteTextures(1, &p.prompt.ID)
|
|
|
|
|
gl.DeleteTextures(1, &p.note.ID)
|
|
|
|
|
gl.DeleteTextures(1, &p.message.ID)
|
|
|
|
|
|
|
|
|
|
if p.hasLicenseInfo {
|
|
|
|
|
gl.DeleteTextures(1, &p.licenseInfoHeading.ID)
|
|
|
|
|