Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0ab540b
docs: clarify Cake input flow and profile persistence
demoth Oct 7, 2026
bcf803c
docs: explain Cake configuration ownership and lifecycle
demoth Oct 7, 2026
7779cfd
refactor: group Cake game configuration in its own package
demoth Oct 7, 2026
a002a78
docs: clarify Cake menus, console, styles, and HUD
demoth Oct 7, 2026
cbbb65c
docs: clarify Cake asset loading and GPU model animation
demoth Oct 8, 2026
ebf8535
refactor: group Cake MD2 loading and rendering pipeline
demoth Oct 8, 2026
79d6c2d
refactor: group Cake BSP loading and rendering pipeline
demoth Oct 8, 2026
40a1cf7
docs: clarify Cake ingame runtime and sound dispatch
demoth Oct 8, 2026
36dc4b9
docs: clarify Cake effects runtime and particle rendering
demoth Oct 8, 2026
d0f5792
docs: clarify shared VFS behavior and client server integration
demoth Oct 9, 2026
625acee
docs: tidy VFS guide formatting and input link
demoth Oct 9, 2026
e72489e
docs: organize Cake guides around subsystem ownership
demoth Oct 9, 2026
2af7c31
feat: make select boxes for fixed value video settings
demoth Oct 9, 2026
6aa85b8
fix: fix selectbox vertical scroll rendering
demoth Oct 9, 2026
7f4915b
fix: escape returns to game from main menu
demoth Oct 9, 2026
38a2a31
feat: menus now scale based on resolution (virtual viewport 1280x720)
demoth Oct 9, 2026
ed9694a
feat: use common virtual screen for menu and game ui
demoth Oct 9, 2026
c1db0ec
chore: cleanup redundant functions, tests, ai stuff
demoth Oct 9, 2026
bcbc66f
chore: update .gitignore to ignore ai
demoth Oct 9, 2026
6182451
fix: include shared UI viewport source in build
demoth Oct 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions .codex/environments/environment.toml

This file was deleted.

7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ build
assets/assets.txt
cake/lwjgl3/src/main/resources/META-INF/native-image/cake/resource-config.json
**/out/*
.kotlin/
.kotlin/

.codex
.junie
.claude
AGENTS.md
11 changes: 0 additions & 11 deletions .run/Lwjgl3GameLauncher (MacOS).run.xml

This file was deleted.

58 changes: 0 additions & 58 deletions AGENTS.md

This file was deleted.

97 changes: 21 additions & 76 deletions cake/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ Desktop platform launcher/wrapper for `core`.

Standalone desktop launcher for the Cake model viewer.

### `engine-tools`
### [`engine-tools`](engine-tools/README.md)

Standalone desktop tooling for engine asset generation workflows.

## Running
## Building and running

### Running

From repository root:

Expand All @@ -31,7 +33,7 @@ Main desktop entrypoint:

- [`Lwjgl3GameLauncher.kt`](cake-client/src/main/kotlin/org/demoth/cake/lwjgl3/Lwjgl3GameLauncher.kt)

## JVM distributions
### JVM distributions

Build portable ZIPs with Java 25:

Expand Down Expand Up @@ -68,7 +70,7 @@ under `build/jpackage/`. Use the corresponding model-viewer tasks for that app.
These outputs bundle the project Java toolchain and target the build machine's
OS and architecture. Installers remain disabled.

## Native executables
### Native executables

Graal support is opt-in. Enable the native plugin and helpers when building an
experimental native executable:
Expand All @@ -83,75 +85,18 @@ that installation; Gradle can continue running on the regular project JDK.
Native resources are detected from the runtime classpath, including repository
assets and dependency libraries. Ordinary JVM builds and ZIPs omit the Graal helpers.

## BSP World Renderer

Cake now uses a dedicated Q2PRO-inspired world BSP batch renderer by default (no legacy per-face world `ModelBatch` path).

Runtime diagnostics:

- `r_bsp_batch_debug` (default `0`) - prints throttled world/entity/sprite/beam/particle submission stats.

Q2PRO references for the target path:

- `q2pro/src/refresh/world.c` (`GL_DrawWorld`, `GL_WorldNode_r`)
- `q2pro/src/refresh/tess.c` (`GL_AddSolidFace`, `GL_DrawSolidFaces`, `GL_Flush3D`)

Profiling workflow:

1. Enable debug graphs:
- `set r_debug_drawcalls 1`
- `set r_debug_texturebindings 1`
2. Enable `r_bsp_batch_debug 1` and capture representative maps/views.
3. Enable gameplay fps overlay (Yamagi-style):
- `set cl_showfps 1` (avg fps)
- `set cl_showfps 2` (min/max/avg fps)
- `set cl_showfps 3` (adds frame-time line)

Screenshot output:

- `screenshot` command writes PNG files to `$HOME/.cake/<mod>/scrnshot/`.
- Shared VFS diagnostics commands are available in Cake too:
- `fs_files`
- `fs_mounts`
- `fs_overrides`

### Bitmap Font Tool

Generate a libGDX bitmap font (`.fnt` + `.png`) from a TTF:

- `./gradlew :cake:engine-tools:run --args="--ttf /absolute/path/MyFont.ttf --out /absolute/output/dir --name font --size 21"`

Notes:

- Uses libGDX default glyph set: `FreeTypeFontGenerator.DEFAULT_CHARS`.
- Output is intended for manual copy into engine assets.

## Client Overview

### [`Cake.kt`](core/src/main/kotlin/org/demoth/cake/Cake.kt)

App-level client entrypoint and lifecycle owner:

- initializes commands/cvars/network state,
- initializes and owns shared asset/input systems,
- switches between menu/console/game contexts,
- creates and disposes game screens.

### [`Game3dScreen.kt`](core/src/main/kotlin/org/demoth/cake/stages/ingame/Game3dScreen.kt)

In-game screen (per-level lifecycle) responsible for:

- world/entity/effects rendering,
- HUD integration,
- frame-level input consumption,
- server message processing for active gameplay.

## Module Docs

Detailed docs for specific Cake subsystems:

- [`assets`](core/src/main/kotlin/org/demoth/cake/assets/README.md)
- [`input`](core/src/main/kotlin/org/demoth/cake/input/README.md)
- [`ui`](core/src/main/kotlin/org/demoth/cake/ui/README.md)
- [`stages/ingame/hud`](core/src/main/kotlin/org/demoth/cake/stages/ingame/hud/README.md)
- [`stages/ingame/effects`](core/src/main/kotlin/org/demoth/cake/stages/ingame/effects/README.md)
## Subsystem Guides

- [Desktop video configuration](cake-client/README.md)
- [Configuration and asset ownership](core/src/main/kotlin/org/demoth/cake/config/README.md)
- [Asset resolution and loading](core/src/main/kotlin/org/demoth/cake/assets/README.md)
- [MD2 loading and rendering](core/src/main/kotlin/org/demoth/cake/assets/md2/README.md)
- [BSP loading and rendering](core/src/main/kotlin/org/demoth/cake/assets/bsp/README.md)
- [Input, bindings, and configuration persistence](core/src/main/kotlin/org/demoth/cake/input/README.md)
- [UI styles and menu architecture](core/src/main/kotlin/org/demoth/cake/ui/README.md)
- [Menu screens](core/src/main/kotlin/org/demoth/cake/stages/README.md)
- [Console](core/src/main/kotlin/org/demoth/cake/stages/console/README.md)
- [Ingame runtime](core/src/main/kotlin/org/demoth/cake/stages/ingame/README.md)
- [HUD layouts](core/src/main/kotlin/org/demoth/cake/stages/ingame/hud/README.md)
- [Effects and particles](core/src/main/kotlin/org/demoth/cake/stages/ingame/effects/README.md)
- [Shared virtual filesystem](../qcommon/vfs/README.md)
50 changes: 50 additions & 0 deletions cake/cake-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Cake Desktop Video

Video settings are archived cvars in the active profile. Changes require a full application restart.
Windowed windows can always be resized; cameras, UI and scene buffers follow
the current size immediately. The latest valid windowed size is saved with the profile, unless a
different size is explicitly pending restart. Fullscreen dimensions never replace windowed dimensions.

| Cvar | Default | Meaning |
|---|---|---|
| `vid_fullscreen` | `0` | `0` windowed, `1` fullscreen |
| `vid_windowed_width` / `vid_windowed_height` | `1024` / `768` | Window content dimensions in OS window coordinates |
| `vid_fullscreen_mode` | `desktop` | Desktop display mode or a supported `WIDTHxHEIGHT@HZ` identifier |
| `vid_fullscreen_modes` | Detected at startup | Read-only list of supported primary-monitor modes; query in the console |
| `vid_depthbits` | `24` | Requested window and scene depth precision: `16`, `24`, `32` |
| `vid_samples` | `4` | Requested window and scene MSAA samples: `0`, `2`, `4`, `8`; `0` disables |
| `vid_hidpi` | `Pixels` | `Pixels` or `Logical` coordinate units for rendering/UI/input |
| `vid_vsync` | `1` | Vertical synchronization |

For example, query `vid_fullscreen_modes`, then select an advertised mode:

```text
set vid_fullscreen 1
set vid_fullscreen_mode 1920x1080@144
writeconfig
quit
```

Modes are selected on the primary monitor. An unavailable or malformed identifier falls back to
the desktop mode with a console warning; the requested value remains saved. Display mode changes
depend on the operating system/compositor. Windowed dimensions use `vid_windowed_width` and
`vid_windowed_height`.

HiDPI changes coordinate units rather than selecting a display resolution. Logical and pixel
coordinates can match, including under X11/XWayland, so switching units may have no visible effect.
Scene buffers and screenshots use physical framebuffer dimensions in both modes.

The scene has its own depth attachment and optional multisample target, resolved before
postprocessing. Scene depth `32` requests floating-point depth; RGBA remains 8/8/8/8 and window
stencil remains 0. Unsupported scene configurations fall back with warnings, first disabling MSAA,
then trying depth24/16; if none works, rendering falls back to the window framebuffer. Actual window
formats are driver-selected. `vid_info` prints requested settings, actual display/buffer attributes,
logical/framebuffer dimensions, and pending restart values. Camera near/far planes remain 1/4096;
depth precision can improve z-fighting but does not fix coincident geometry.

An opt-in OpenGL test uses a hidden desktop window to check depth, MSAA resolve and buffer
recreation (requires a graphical session):

```sh
CAKE_VIDEO_GL_TESTS=1 ./gradlew :cake:core:test --tests '*SceneFrameBufferGlTest'
```
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package org.demoth.cake.lwjgl3

import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Graphics
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration
import com.badlogic.gdx.graphics.glutils.HdpiMode
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics
import org.demoth.cake.Cake
import org.demoth.cake.CakeHdpiMode
import org.demoth.cake.CakeDisplayMode
import org.demoth.cake.CakeStartupBootstrap
import org.demoth.cake.CakeStartupContext
import org.demoth.cake.CakeVideoModeSettings
import org.demoth.cake.CakeVideoPlatform
import org.demoth.cake.publishFullscreenModes
import org.demoth.cake.resolveFullscreenMode
import org.lwjgl.glfw.GLFW

/** Launches the desktop (LWJGL3) application. */
object Lwjgl3GameLauncher {
Expand All @@ -19,10 +25,18 @@ object Lwjgl3GameLauncher {
}

private fun createApplication(startupContext: CakeStartupContext): Lwjgl3Application {
return Lwjgl3Application(Cake(startupContext), getDefaultConfiguration(startupContext.videoMode))
return Lwjgl3Application(Cake(startupContext, Lwjgl3VideoPlatform()), getDefaultConfiguration(startupContext.videoMode))
}

private fun getDefaultConfiguration(videoMode: CakeVideoModeSettings): Lwjgl3ApplicationConfiguration {
val desktop = Lwjgl3ApplicationConfiguration.getDisplayMode()
val modes = Lwjgl3ApplicationConfiguration.getDisplayModes().toList()
publishFullscreenModes(modes.map { it.toCakeMode() })
val selected = if (videoMode.fullscreen) {
resolveFullscreenMode(videoMode.fullscreenMode, desktop.toCakeMode(), modes.map { it.toCakeMode() })
} else desktop.toCakeMode()
val displayMode = modes.firstOrNull { it.toCakeMode() == selected && it.bitsPerPixel == desktop.bitsPerPixel }
?: modes.firstOrNull { it.toCakeMode() == selected } ?: desktop
return Lwjgl3ApplicationConfiguration().apply {
setTitle("Cake Engine v1.2.1")
setOpenGLEmulation(Lwjgl3ApplicationConfiguration.GLEmulation.GL32, 3, 2)
Expand All @@ -31,17 +45,17 @@ object Lwjgl3GameLauncher {
/* g = */ 8,
/* b = */ 8,
/* a = */ 8,
/* depth = */ 24,
/* depth = */ videoMode.depthBits,
/* stencil = */ 0,
/* samples = */ 4 // anti-aliasing
/* samples = */ videoMode.samples // Scene MSAA is configured separately for postprocessing.
)

useVsync(videoMode.vsync)
setForegroundFPS(Lwjgl3ApplicationConfiguration.getDisplayMode().refreshRate)
setResizable(false)
setHdpiMode(videoMode.hdpiMode.toLwjgl3HdpiMode())
setForegroundFPS(displayMode.refreshRate)
setResizable(true)
setHdpiMode(videoMode.hdpiMode)
if (videoMode.fullscreen) {
setFullscreenMode(Lwjgl3ApplicationConfiguration.getDisplayMode())
setFullscreenMode(displayMode)
} else {
setWindowedMode(videoMode.width, videoMode.height)
}
Expand All @@ -51,9 +65,20 @@ object Lwjgl3GameLauncher {

}

private fun CakeHdpiMode.toLwjgl3HdpiMode(): HdpiMode {
return when (this) {
CakeHdpiMode.PIXELS -> HdpiMode.Pixels
CakeHdpiMode.LOGICAL -> HdpiMode.Logical

private fun Graphics.DisplayMode.toCakeMode() = CakeDisplayMode(width, height, refreshRate)

private class Lwjgl3VideoPlatform : CakeVideoPlatform {
override fun windowSize(): Pair<Int, Int> {
val width = IntArray(1)
val height = IntArray(1)
GLFW.glfwGetWindowSize((Gdx.graphics as Lwjgl3Graphics).window.windowHandle, width, height)
return width[0] to height[0]
}

override fun describe(): String {
val mode = Gdx.graphics.displayMode
val windowMode = if (Gdx.graphics.isFullscreen) "fullscreen" else "windowed"
return "Active display: ${Gdx.graphics.monitor.name}, ${mode.toCakeMode().id}, $windowMode"
}
}
1 change: 1 addition & 0 deletions cake/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dependencies {

implementation(project(":qcommon"))
testImplementation libs.gdxBackendHeadless
testImplementation libs.gdxBackendLwjgl3
testRuntimeOnly(libs.gdxPlatform) {
artifact {
classifier = 'natives-desktop'
Expand Down
Loading
Loading