Clover.moe https://clover.moe Tue, 15 Sep 2026 02:44:34 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.2 https://clover.moe/wp-content/uploads/2016/04/20151213_Clover_favicon-150x150.png Clover.moe https://clover.moe 32 32 Clover’s Toy Box 26.08 https://clover.moe/2026/09/14/clovers-toy-box-26-08/ Tue, 15 Sep 2026 02:30:31 +0000 https://clover.moe/?p=2007 Clover’s Toy Box development: running QVM and WebAssembly game code.

My virtual machine interpreter now supports QVM format in addition to WebAssembly. I’ve also successfully hooked it up to a Quake 3 engine and run the Quake 3 game code in both formats using my interpreter.

What is QVM?

Quake 3’s game logic and menu code can be replaced by game modifications. This allowed for a prolific game modding community that spawned probably hundreds of game mods and many new games / total conversions. Quake 3 modding is still (somewhat) active 25 years later.

Code written in C89 is compiled to a custom format called QVM (Quake 3 Virtual Machine). This is a platform independent instruction set that can be compiled once and run on any platform. It also safer to run than platform specific code.

QVM is similar to the format’s used by Java and the C# programming languages but they depend on additional library functionality. It was exciting to see WebAssembly (WASM) develop as it is standalone like QVM and has a large ecosystem of software/tooling.

I’m not aware of a reason for new games to use QVM instead of WASM for supporting game modifications. However I was interested in implementing QVM support to better understand it.

Running QVM

I wrote a WebAssembly interpreter in November 2024 though March 2025 that passed 15,000 tests. I added reading QVM file structure and printing instructions in July 2025.

I resumed working on QVM support a year later in July 2026. I implemented the QVM instructions and added ability to run it. Easier said than done.

I got it to load the Quake 3 ui.qvm and call a function to get the UI version number, 4! The QVM depends on a lot of event function calls into the VM and import functions from the engine for drawing, getting the server list, etc to actually run. In order to focus on implementing the QVM interpreter, I replacing the entire VM subsystem of a Quake 3 engine with a shim that calls my interpreter but it uses the Quake 3 import/syscall functions.

Initially there was a black screen but it was running (yay). It kept calling “set menu” because the UI wasn’t started. Unlike WASM, I treated QVM as one function as there is only one entry point. However my interpreter aborted the “function” when leaving a function. It would call into several functions but the first time it reached the end of a function it prematurely exited the whole VM call instead of jumping back to the calling location.

I had some difficulty with the memory stack that holds arguments passed to functions and intermediates for instruction operations.

For WebAssembly I had a single memory stack with “stack-frame” (local variables and arguments) and variables pushed by instructions that is not directly accessibly by the VM code. QVM “local” instruction gets the address of the stack by hard coded offsets so it needs the stack-frames to be accessible address space. After moving the whole stack into VM space I found that stack-frames can’t be interleaved with the instruction stack.

It was stuck in an infinite loop due to the interleaved stack throwing things off that were expected at specific offset in the stack-frames. (Specifically reading the function return address; it kept jumping execution to address 0—the vmMain entry point.) Figuring out the issue involved a lot of reviewing the instructions and memory stack as text output. Over and over again with adding more debug messages.

The “enter” and “leave” instructions were kind of confusing for allocating/freeing space for local variables/args on the stack-frame. Though I eventually figured out that WASM has the same concept but it’s handled by the code directly to move the global index 0 “__stack_pointer”.

Eventually I got QVMs working and it just worked without any obscure issues.

Running WASM

I decided to compile Quake 3’s game code to WebAssembly since I had QVM working and technically had a WASM interpreter hooked up. It turned out to be sort of more difficult to run as WASM than run as QVM.

I had to work through some issues compiling Quake 3’s game logic to WASM and then fix loading WASM imports in my interpreter (functions, tables for function references). It was able to draw the menu! However it failed when loading a game level. It reported “NULL” map name and/or player model name; text formatting was broken.

I extracted the text formatting specific code from Quake 3’s bg_lib.c as a test case I could run directly and spent time reviewing the debug output. I was struggling to understand what my interpreter was doing wrong. The code was copying variadic arguments but reading the wrong values in nonsensical ways such as working for second text format but not the first. I could see it was passing an argument for “…” variadic arguments which doesn’t exist in the C code.

This is the part where I turned to AI. I explained the problem to Anthropic’s Claude large language model via duck.ai. After I sent a couple messages Claude suggested the issue was Clang’s handling of variadic arguments and that it needs to use Clang’s va_start() instead of the custom va_start() macro in Quake 3’s bg_lib.c. This indeed solved the issue after adjusting my build of Quake 3’s game code to WASM.

I had seen part of the problem while reviewing the WASM bytecode instructions and had seen code comments in the past relating to Clang varidic argument handling but I didn’t understand why Clang needs to use (it’s own) va_start() in order to put it together.

Traditionally the text format argument for variadic arguments is copied to local stack memory and the supplied arguments are stored after that. This allows using the address of the text format to find the variadic arguments after it. However Clang references the original text format argument in static memory and passes a second argument pointing to the variadic arguments stored in local stack memory. This needs Clang’s va_start() for the compiled code to access this second argument generated by Clang.

I defined “double” data type to “float” in the WASM build of Quake 3’s game code to match QVM behavior. However this broke text formatting as Clang converted float to double for variadic arguments but it only read 4 bytes. (The compiler also complained about using float in va_arg().) I limited defining double to float for the math function import declarations to work around this while still matching QVM import functions.

It could now load the map! However adding bot players to the game failed. I spent more time reviewing the debug information for the instructions.

Eventually I found something that stood out. It was using the return value from an implicit memset(). This optimization has come up before as crashing LCC (the QVM compiler) with it’s custom memset() that always returned NULL instead of the destination address. Conveniently I happened to know that the QVM imports for memset() and memcpy()—that were used by the WASM module—also return NULL. (I previously fixed this in Spearmint but it’s technically an ABI change to fix ioquake3 / Lilium Arena.)

I was able to override the return value for memset() and memcpy() in my WASM interpreter to return the destination address instead of NULL and this fixed bots! (There is also compiler flags to disable Clang apply optimizations that assume this return values and it might be worth looking into to be compatible with Quake 3’s original import functions / syscalls.)

I think I ended up spending more time reviewing the instructions and stack for running WASM than QVM but the issues were mainly related to the WASM build of the game code using Clang and not my interpreter. However in the end it worked!

Cool

It kind of hit me of how cool this was after I had both QVM and WASM builds of Quake 3’s game code running on the Quake 3 engine using my virtual machine interpreter and my reimplementation of the Quake 3 renderer. I was happy; I managed to impress myself by doing this.

My focus is to just run WASM and QVM at this stage, not be fast. Running WASM on my interpreter is slower than QVM. (I think I need to convert some things to be more direct like QVM to reduce overhead.) My QVM interpreter is slower than the original in Quake 3 itself and my renderer is missing some features and slower in some cases.

Why am I happy that I’ve objectively made a worse Quake 3 experience? It is satisfying to solve difficult problems and I can see a path to continue improving it.

]]>
Clover’s Toy Box 25.12 https://clover.moe/2026/09/09/clovers-toy-box-25-12/ Wed, 09 Sep 2026 22:55:22 +0000 https://clover.moe/?p=1976 Clover’s Toy Box development: improving SDL support and adding SDL 3.

Overview

SDL (Simple Directmedia Layer) is a library used by Clover’s Toy Box for creating the application window with an OpenGL context, playing audio, and receiving input from the keyboard, mouse, and game controllers. An OpenGL (Open Graphics Library) context is used for 3D graphics rendering.

Toy Box is now compatible with all SDL versions with OpenGL support. This allows Toy Box to use SDL 3 for the best modern operating system compatibility and new game controller support while I still continue to tinker with platforms or operating system versions that are only supported by SDL 2 and now potentially SDL 1.2 as well.

However this doesn’t make Toy Box effortlessly run on everything that SDL has been ported to. There is platform specific code, it requires figuring out building for the platform, and possibly requires implementing 3D rendering.

I use the HIDAPI library for implementing my own support for the Official Nintendo USB GameCube Controller Adapter. I had to jump through some hoops to support the HIDAPI library embedded in SDL 2, SDL 3, or (for SDL 1 or SDL 2 before 2.0.18) a standalone HIDAPI library.

(SDL 2.0.12+ and SDL 3 also supports this device but I prefer my own preexisting support that is essentially identical to my support for GameCube controllers on a real Wii or GameCube using libogc.)

All-in-One

Toy Box now supports every stable version of the SDL API since support was added for OpenGL (SDL 1.1.0+, 1.2.0+, 2.0.0+, 3.1.3+).

Toy Box also supports all of those in a single build of the application. There isn’t separate source code control branches to maintain or separate builds that need to be verified that they can compile. (Though I do retain the ability to use traditional linking to a specific SDL version.)

Unfortunately macOS and Android need separate application builds for SDL 2 and 3 to compile against different SDKs to allow targeting older operating system versions supported by SDL 2.

Additional SDL versions

Toy Box previously only supported SDL 2 and required pretty much whatever SDL 2.x.x version it was compiled against due to making use of new features. There was many compile-time version checks but it didn’t cover everything. Now Toy Box handles all SDL 2.x.x versions by using ~70 compile-time and run-time version checks.

SDL 3 is not the most exciting upgrade for the needs of a resizable OpenGL window with a game controller, mouse, and keyboard. SDL 3 is necessary to keep up with the latest development and platform compatibility as not everything is backported to SDL 2.

New support for SDL 3 adds new keyboard keys, game controller devices and buttons, and exposes game controller battery power as a percentage instead of low, medium, full.

Currently there is only one SDL 3 version check. I only use functionality of the first stable ABI 3.1.3 preview release.

New support for SDL 1 opens the door to supporting Windows 9x/ME/2000 and older versions of Mac OS X but I haven’t looked into those yet.

Building and running SDL 1.1.0 from the year 2000 on modern Linux was kind of amazing. It works! Though it did unfortunately crash in Xorg at window shutdown (in GNOME Wayland session).

Toy Box also uses version checks to support all SDL 1.1.x and 1.2.x versions; a whole 5 version checks for the utilized functionality.

I already had a window/audio/input abstraction layer over SDL 2 and libogc. The only required extension for adding SDL 1 and 3 was exposing the parts of HIDAPI that are used for GameCube controller adapter support.

Dynamically Load SDL

Manually dynamically loading SDL with dlopen() or LoadLibrary() is required for supporting SDL 1, 2, and 3 in the same executable. However I began considering this with only SDL 2 in Spearmint. It solves issues I encountered on Windows, macOS, and Linux.

Toy Box chooses the newest SDL library; on Linux it uses either the library installed at the system level or included with the application. If SDL 3 is missing or fails to load, Toy Box will fall back to SDL 2 and then SDL 1. It’s also possible to specify which SDL version to use.

Dynamically loading SDL avoids needing to create a custom SDL build for Windows to rename the SDL.dll for 64-bit (SDL64.dll) to not conflict with the 32-bit SDL.dll.

Dynamically loading SDL and supporting any SDL version at run-time means the application can compile against the same latest SDL headers that are included in the source tree regardless of the actual library version. This avoids the issue of needing separate SDL headers for each macOS architecture’s final SDL version like in Spearmint with four sets of SDL 2 header files.

Dynamically loading SDL avoids the problem of “rpath $ORIGIN” not being supported on older Linux distributions which breaks finding libraries included with the application.

Supporting system libraries and libraries included with the application fixes the problem of Spearmint 1.0.0 bundling SDL 2.0.8 but seven years later users would be better off using the newer system library on Linux. (Technically Windows and macOS would also benefit from newer SDL and I should probably update SDL in the application more frequently.)

Supporting fallback to older SDL system libraries also means Toy Box (without bundling SDL) would not need to statically link to SDL 3 or wait for Linux distributions to provide it. It would simply continue using SDL 2.

OpenGL support

SDL 1.2

I added SDL 1.2 support and I got a black screen. SDL 1.2 didn’t enable double buffering by default. I needed to manually call glFlush() to tell the OpenGL driver to do the rendering.

I enabled double buffering for SDL 1.2 (using SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 )). I don’t expect to actually need single-buffered rendering but I added support anyway. Let’s avoid black screen problems if we can.

SDL 3

I added SDL 3 support and I got a black screen. Swapping buffers for double-buffered rendering was failing. I made my OpenGL renderer automatically switch to single-buffered rendering in this situation. This fixed rendering. (This isn’t supported by OpenGL ES or WebGL.)

For some reason SDL 3 was requiring a call to SDL_GL_MakeCurrent() before calling SDL_GL_SwapWindow(). Calling it after creating the window wasn’t good enough but it worked directly before swap buffers. It didn’t make sense why I would need to do that.

I started thinking I should make a small test case for reporting it. Though it occurred to me I was on a random development revision of SDL 3 (May 5, 2025; git commit 29d211649). Using the latest SDL 3 code solved the issue. It also worked on the initial stable version 3.2.0 and (at time of writing) newest stable 3.2.24. But sure, now my renderer handles swapping buffers failing in case it breaks. Let’s avoid black screen problems if we can.

Swap Buffers

This is more or less what I do now; it use to just call swap window before SDL 1.2 and SDL 3 randomly broke rendering. (The fallback for swapping buffers failing is only implemented for SDL 3. Prior versions do not indicate if swapping buffers fails.)

// check if double buffer is enabled at initialization
int doubleBuffer = true;
if ( Desktop_OpenGL ) {
    glGetIntegerv( GL_DOUBLEBUFFER, &doubleBuffer );
}
// ... at end of frame swap buffers or flush OpenGL commands
if ( doubleBuffer ) {
    if ( !SDL_GL_SwapWindow( window ) ) {
        if ( Desktop_OpenGL ) {
            printf( "WARNING: Swap buffers failed. Disabling double-buffered rendering.\n" );
            doubleBuffer = false;
            glDrawBuffer( GL_FRONT );
            glFlush();
        } else {
            // This should probably throw a fatal error on OpenGL ES and WebGL.
        }
    }
} else {
    glFlush();
}

SDL doesn’t solve everything

There is a port of some version of SDL to run on most computer devices. I think supporting all versions of SDL in Clover’s Toy Box is exciting! Unfortunately this doesn’t instantly port my software to everything.

To port to a new platform, I would need to adapt the platform specific code in Toy Box for file access, time, networking, window/audio/input support, potentially loading dynamic libraries, and potentially support a new 3D graphics API or alter the OpenGL support. I would also need to install the build environment and set up the the correct build commands. I would need to figure out how test it and there is a decent chance of running into unique issues.

Most of this would need to be done even if not using SDL. My SDL usage only handles window/audio/input support. This is a small part of the porting process for older limited platforms. My Wii port without SDL handles window/audio/input in ~2,000 lines with an additional ~1,500 for the 3D graphics backend and ~50 lines for networking.

No version of SDL has a 3D graphics abstraction that would support the Wii or other older (pre-Vulkan) hardware. The SDL 1.2 Wii port doesn’t scale the joystick axis to the full range expected by applications. So if I used SDL, I would still be on the hook to do most of the porting work and have to step into maintaining the SDL Wii port or try to workaround the issues. It’s simpler to only deal with my own code base.

SDL 1.2 is also under a different license (LGPL license) than SDL 2+ (zlib license). It requires ability for the user to replace the LGPL code. For closed source software such as Toy Box, SDL 1.2 is difficult or impossible to use outside typical desktop platforms like Windows, Linux, and macOS. However there isn’t a strong reason to port SDL 2 or 3 to older platforms as they don’t offer very much of the new SDL functionality.

SDL offers a lot of functionality on desktop platforms. SDL is particularly useful for supporting things that are outside my setup and testing environment. I think using SDL gives better compatibility on Linux than I could do otherwise as I don’t plan to test every X11 window manager and Wayland compositor. SDL also contains many game controller mappings (so it works without user configuration) and contains game controller drivers that provide support for more hardware than the native platform APIs.

This is to say, SDL is one tool available for adding support for a platform. It may be the best tool for the job depending on your goals. However you still have to port the application yourself to each platform and it may be simpler and/or better to just directly support a platform. So support for all SDL versions when most devices have a port of SDL is not as exciting as it sounds.

Conclusion

Clover’s Toy Box overcomes several issues with Spearmint’s platform support and transition to new SDL versions.

These changes were developed in October through December 2025 but posting about it was delayed as I had not added support for SDL 3 on Android or tested macOS yet. I still haven’t done those as of September 2026.

]]>
Clover’s Toy Box 25.09 https://clover.moe/2026/02/14/clovers-toy-box-25-09/ Sun, 15 Feb 2026 02:11:23 +0000 https://clover.moe/?p=1985 Clover’s Toy Box development: model lighting, secure networking, running WebAssembly, and more.

Overall

It’s a been a while since the last development update in September 2024. This post covers development from October 2024 to September 2025. (As written in January and February 2026.)

There was a lot less development in this time frame than usual. I burned out on reimplementing the Quake 3 renderer and stopped working on it after October 31, 2024. I started a few new things that I made good progress on but haven’t reached completion.

Completed: MD3 Normal Mapping, (Automated) Build for Android.

Progress on: Model Lighting, Secure Networking, WebAssembly Interpreter, Video Playback.

MD3 Normal Mapping

Summary: Normal maps are now supported on all model formats supported by Clover’s Toy Box. “stage normalmap” is supported in materials for compatibility with Reaction.

Reaction is a free game based on Quake 3 that utilizes the ioquake3 engine. Reaction’s knife and first-person hands/arms use MD3 models with a tangent-space normal map. Support for this was integrated into Clover’s Toy Box in October 2024.

Toy Box now has support for normal maps on vertex animated models (MD2, MD3, MDC, and TAN formats) and the ability to mark an image as a normal map using the “stage normalmap” material stage directive.

This includes the ability to generate vertex tangent and bitangent vectors for vertex animated models and the ability to setup the tangent matrix for the OpenGL accelerated vertex animated model rendering.

Specular maps are often paired with normal maps (including in Reaction). This has not been added to Toy Box yet.

Model Lighting

Summary: I worked on making model lighting match Quake 3. I made great progress on environment lighting. Model lighting, particularly from light entities, is not completely accurate and there is various issues.

Clover’s Toy Box has long had a global directional light to give a sense of space and test normal mapping on models. Toy Box also supports static lighting on Quake 3 levels (lightmaps and vertex lit surfaces).

There is work-in-progress support for replacing the global directional light with using the level’s colorful environment lighting and light entities such as weapon muzzle flash and explosions.

Quake 3 levels have a 3D light grid of the environment lighting over the area of the level and it’s interpolated between the points at a model’s lighting origin before being applied to the model.

I went through a lot to get sampling the level’s environment lighting to work correctly in Toy Box. Figuring out parsing the grid size, how to offset the grid to the correct place in 3D space, and how to interpolate light between points.

I added rendering lines with the color/direction of the light grid itself for a better idea of where the light grid is. (Quake 3 doesn’t have this but it seems pretty useful.) I also compared it to Spearmint’s opengl1 renderer using the “r_debugLight 1” config variable that prints light values at the first-person gun’s position.

Ironically I found and fixed an issue in Spearmint’s opengl1 renderer that caused the light grid to get brighter each time I switched renderers using Spearmint’s “vid_restart” command.

Quake 3 uses generic ambient and diffuse lighting for models and it should work correctly in Toy Box. This may be correct with only the environment light but light entities do not work correctly. Quake 3 combines all lights affecting the model into one light source to apply and I haven’t figured out how exactly that works.

I also haven’t addressed drawing light entities on the level itself. It’s going to be difficult to set up the light decal geometry but it wouldn’t be complicated to draw it.

I stopped working on model lighting after October 31, 2024 and it hasn’t been integrated into Toy Box yet. I hadn’t really looked it again until January 2026. The state of the code isn’t as bad as I remembered but it has issues.

Build for Android

Summary: Building for Android is now automated rather than a manual process.

I got Clover’s Toy Box working on Android a while back but the build process wasn’t completely automated and I somewhat forgot how to build it.

In October 2024 I add the SDL2’s android-project with customization to enable network access and builds of libSDL2.so for arm and arm64 to Toy Box along with a build script to put everything together. Now I can build the full Android app with a single command.

Secure Networking

Summary: Secure connections for HTTP and WebSocket now work on the Linux game client and server.

Clover’s Toy Box started out targeting OpenGL ES 2 and WebSocket to allow first-class support for running in a web browser and connecting to a game server.

However this only worked when hosting the website on the local network. Web browsers require using secure (encrypted) connection for WebSocket if the game’s HTML/JS/WASM files are hosted on the Internet.

I added support on Linux for secure connections using OpenSSL on both the game client and server. This means it’s possible to host games on the public Internet and connect from a web browser. The game client on Linux can also download files over HTTPS and connect to game servers using secure WebSocket.

I still want to add support on Windows, macOS, Android, and possibly the Wii. I want to make Linux not depend on having OpenSSL pre-installed. I’m not sure how to best address these.

WebAssembly Interpreter

Summary: I wrote an interpreter that runs WebAssembly code and passes 15,000 tests. I intend to overhaul the project for better security and performance. Though first I want to find a reasonable way to support running Quake 3’s QVM bytecode and maybe get it to pass more of the WebAssembly tests.

I worked on implementing a WebAssembly (WASM) interpreter in November 2024 though March 2025. This is standalone from Clover’s Toy Box and not integrated yet.

Source code can be compiled to a WebAssembly file (.wasm) that uses a instruction set that is independent of the operating system and CPU architecture and then it can be run with limited access to the system on any operating system and CPU. This is similar to Java, Microsoft’s .Net / C#, and Quake 3’s QVM. I’m writing code to execute the WebAsembly code. It’s kind of like emulating a CPU.

There are many WebAssembly interpreters / run-times available; there isn’t a strong reason for me to write one. However it’s something I’m interested in and it is documented and limited scope that it is feasible to do. Audio and video codecs on the other handle are more difficult to test and less interesting to implement, you know?

My WASM interpreter is passing ~15,000 tests from the reference WebAssembly interpreter repository. It is failing 200 tests related to what kind of Not-a-Number (NaN) is returned by some instructions. It would be good to ensure my interpreter is behaving correctly but I don’t think it matters for my usage in practice.

The reference WebAssembly interpreter test suite is for a Lisp program and there is no documentation or recommendation that other interpreters use the test suite. So figuring out how to run tests and check the result is part of the problem. (There is 10,000 other tests that I haven’t hook up yet. I think they’re mostly testing that invalid code is caught.)

I’m not 100% sure I’m checking the NaN results correctly for the “failing tests”. (I found some tests must allow multiple NaN types.) I tried to fix some tests and it broke others. As far as I know, no code in Toy Box or Quake 3 cares what kind of NaN it is. (I didn’t even know there was different kinds before I started this.)

My WASM interpreter should be functional for running software. Though I’ve only tested a few basic programs. My main focus was gaining understanding of how WebAssembly works. I haven’t profiled it but I expect it to be slow. I don’t expect it to catch all invalid code.

My intention is to add support for executing Quake 3’s QVM bytecode. QVM is similar to WASM; it’s a set of instructions to manipulate a memory stack and call functions in the host application. If I remember correctly, QVM has about 50 instructions while WASM has around 200 and WASM instructions have additional run-time rules that need to be handled.

It should be simpler to run QVM bytecode than WASM. However there isn’t a test suite for QVM. Notably QVM has a jump instruction to move code execution to an arbitrary location and WASM does not. (Executing QVM bytecode doesn’t mean it will run Quake 3 mods. They require specific functions in the application.)

Video Playback

Summary: Audio playback from RoQ and AVI files works in Clover’s Toy Box. I have Motion JPEG in AVI partially functional but it hasn’t been integrated yet. I haven’t started working on RoQ video decoding.

I saw that RoQ video format (used by Quake 3 and some other games) was reimplemented and used by homebrew for the Sega Genesis; “Sonic CD32X would be LIT!” (June 4, 2025). I wondered if I could implement RoQ in Clover’s Toy Box using a specification I found online. I worked on “video playback” around June-July 2025.

I implemented playing the audio from RoQ videos. However I decided to start with less complicated video decoding.

I decided to add support for Motion JPEG (MJPEG) in an .AVI container. In Motion JPEG all video frames are JPEG images. Motion JPEG has higher file size than RoQ but decoding JPEG images is already supported in Toy Box using the libjpeg-turbo library.

I implemented audio playback from AVI containers. It has the same audio header and possible formats as WAV files but it’s split up into chunks interleaved with the video frames. I reused code from my WAV reading implementation.

I got Motion JPEG in .AVI to play! However I didn’t get the audio and video to be in sync. Video files start with multiple frames of audio. I need to buffer audio samples and video frames instead of just displaying video frames immediately when reading more audio samples.

Over and out

Can’t spell clover without over. It’s so cl-over.

]]>
Web Presence https://clover.moe/2025/01/14/web-presence/ Tue, 14 Jan 2025 15:36:11 +0000 https://clover.moe/?p=1914 Changes made to the Clover.moe web presence in the last few months.

Social

You can now follow Clover.moe on 🦋 Bluesky and 🐘 Mastodon in addition to the RSS feed.

Clover.moe is the center for my creative works. I also have a personal Bluesky account for other things.

GitHub

The official Clover.moe software projects have been moved from github.com/zturtleman to github.com/clover-moe. The old addresses are redirected and still work.

Website

The Clover.moe website has changed from a live WordPress blog (which allows directly posting/editing in a web browser) to a static copy (just serving unchanging files). This is mainly for cost reasons but it also solves the occasional database connection error and mild concern about WordPress security issues.

The Clover.moe WordPress was moved from a cloud-hosted virtual private server (VPS) to a local computer. A script is used to download a static copy and fix issues. It’s then committed to a git repository and uploaded for the public static website.

Some older files/pages haven’t been readded. The Spearmint web page now matches the rest of the website instead of having a standalone web page. This follows dropping the custom web page for Turtle Arena earlier last year.

Web pages have been add for other replacement game engines. Lilium Arena Classic for Quake 3 1.16n and Lilium Salvation for Dark Salvation.

]]>
Open Source Status https://clover.moe/2024/10/01/open-source-status/ Tue, 01 Oct 2024 07:26:41 +0000 https://clover.moe/?p=1855 I’m going to do my own things in my own time rather than acting as an open source software maintainer.

I’ve completed most of the things I want to do in my open source projects (Spearmint, Maverick, etc). There was of course a lot of other ideas and many are now redirected at Clover’s Toy Box. At this point I mainly work on Maverick and Quake 3 related open source projects for the sake of other people. While I like assisting, it’s not satisfying and can be stressful.

I’m withdrawing from providing user support and fulfilling requests for open source projects. I’m not interested in continuing to discuss these projects. I’ve made the Clover.moe Community Discord server be read-only after giving a month notice. I’ve thought about this for some time; I decided to do this in March.

I will continue to contribute to Quake 3 open source projects if it’s something I personally want. There is still a few loose ends I want to deal with and I may find other things in the future. So it’s not entirely the end of me working on these projects.

]]>
Clover’s Toy Box 24.09 https://clover.moe/2024/09/29/clovers-toy-box-24-09/ Sun, 29 Sep 2024 09:40:24 +0000 https://clover.moe/?p=1847 Clover’s Toy Box development: improved performance 650%*, fixed curved surfaces, mirrors/portals, and large levels.

* 650% improvement in one level on one set of hardware.

Performance

It took a couple years to get my reimplementation of the Quake 3 renderer (“Toy Box renderer for Spearmint”) to the performance of the ioquake3/Spearmint “opengl1” renderer. It very challenging to meet the performance despite using more modern OpenGL features and it was still missing many features. I also had to disable curved surfaces for it to be faster.

My renderer fell behind again after upgrading to new better hardware and adding additional features (notably Quake 3 materials).

Most of the official Quake 3 maps ran at 1000 frames per-second (FPS). The slowest case that I was aware of was at the center of the Quake 3 add-on level ct3ctf2. It ran at only 100 FPS. Using the Spearmint opengl1 renderer on ct3ctf2 is somewhere between 500 and 666 FPS and the OpenGL2 renderer is somewhere between 800 and 1000 FPS. That’s kind of disappointing for my renderer. (Higher frames per-second is better.)

After making several changes I’ve clawed my way from 100 FPS to 650 FPS at the center of ct3ctf2. A 650% improvement. (These changes will improve other levels as well but it’s not 650% everywhere.) This is hopefully only the tip of the performance ice burg.

The main goal for improving the frame rate is being able to render more content at a lower frame rate or use less power to render the same content (which is particularly relevant for mobile devices).

BeginPerformanceQuery

I reviewed what it was drawing for the ct3ctf2 level. It was uploading 300,000 vertexes and issuing 1,000 draw calls per-frame at 100 FPS.

The curved surfaces were using materials that required using the CPU vertex shader and uploaded 3x vertexes (for each material layer) each frame. Additionally the surface order interleaved flat surfaces (using static vertex buffer) and curves (using dynamic vertex buffer). This resulted in not merging them into the same draw call despite using the same materials.

Materials had for example a base texture, environment map (using CPU vertex shader), and then standalone lightmap (using CPU vertex shader).

1. BSP surface merging

I changed the Quake 3 level surface sorting to sort flat (static vertex buffer) and curved (dynamic vertex buffer) surfaces together so they can be merged into single flat or curve draw calls.

2. Standalone lightmap

Lightmaps are often merged with an adjacent layer to use multi-texture. Both layers are drawn in a single draw call. However multiply blended lightmaps cannot be merged with the additive blended environment map and some other effects. This separate lightmap layer used the CPU vertex shader to copy the lightmap texture coordinate from the multi-texture slot to the base slot.

I could do the same thing in modern OpenGL shaders by adding a texcoord source or additional shader type (both that have more over head). For OpenGL 1.x fixed-function, I would potentially need to rebind the vertex attributes per draw call for the world. It seemed like a mess to do both of these in the same backend.

Instead I made standalone lightmap layers use multi-texture with a white base image. No CPU vertex shader needed or complicated re-architecture the backend. (OpenGL 1.2 is needed for multi-texture so OpenGL 1.1 still falls back to the CPU vertex shader.)

3. Duplicate vertexes

The CPU vertex shader has to add draw calls for each layer. This added vertexes for each layer with a TODO for separating vertex upload from layers. As a quick solution, I made layers that do not require the CPU vertex shader share the same unmodified vertexes. This allowed the base and standalone lightmap layers to use the same vertexes.

4. Hardware tcGen

In the previous article on adding Quake 3 material support I talked about hardware texture coordinate generation and how it can be done in hardware. I added support to OpenGL shaders so it doesn’t need the CPU vertex shader. (I haven’t implemented it for OpenGL fixed-function and Wii yet.)

5. View frustum culling

I’ve supported Quake 3 level’s Potentially Visible Set for a while to drop rendering areas of the map. However this doesn’t work well for large open area.

I added view frustum culling to drop drawing level surfaces and models that are not in front of the camera within the view angle. I wrote most of this code in 2021 or earlier but I didn’t merge it due to a rendering issue that was apparently already resolved since then (a few specific surfaces in some maps disappeared when clearly on screen).

6. Curve tessellation

The curved surfaces in the Quake 3 level formats are Bézier curves that have 3 by 3 patch of control points to define the mathematical curve. I convert it to triangles (tessellate) in order to draw it.

I originally implement it as tessellating it each frame and at a fixed number of triangles; even if say, it’s flat square and only needs 2 triangles. I knew it was slow but it seemed easier at the time when I was trying to get it to work at all. There was an issue that some vertical rounded corners had incorrect lightmap texture coordinates. I tried unsuccessfully to fix it two or three times over the last 3 years. Improving performance was kind of on hold for this reason.

I found the lightmap texture coordinate issue while experimenting with limiting the number of rows/columns. It turns out Quake 3 levels have invalid lightmap texcoords if the control points are not equal distance apart. The vertical rounded corners are flat vertically and rounded horizontally. Quake 3 doesn’t add any rows; it’s just single triangles from the top to bottom. It doesn’t use the middle row of control points with invalid lightmap texture coordinates.

I completely rewrote the tessellation code. Curves are now tessellated at level load instead of each frame. The curves are now subdivided based on how curved it is (less triangles in most cases). Curves could have gaps between touching patches due to how they’re subdivided now. Curves are now stitched together by detect common edges and adding rows and columns to adjacent patches.

Curves are now faster to draw and match Quake 3 visually. (Though I haven’t added dynamic lower detail far away yet.)

EndPerformanceQuery

The center of ct3ctf2 has moved from uploading 300,000 vertexes per-frame to only 3,500 vertexes and from 1,000 draw calls to 500 draw calls. 100 FPS to now 650 FPS. I still have more ideas for improving performance but I got sidetracked on adding features again.

Mirrors and Portals

I previously added mirror and portal rendering support in Toy Box but it has fallen into disrepair. I hadn’t ever hooked it up for Quake 3 maps or “Toy Box renderer for Spearmint”.

I fixed the mirrors in Toy Box to handle framebuffer object support that was added ages ago and fixed OpenGL 1 clip plane rotating based on whatever GL_MODELVIEW matrix was previously set.

However remember that new view frustum culling? Yeah, the culling for the main view applied to the mirror views so mirrors didn’t draw anything behind the player. Sprites also faced the main view instead of the mirror view. I was in mirror hell for a month and a half. I didn’t want to work on it for whatever reason but felt like I shouldn’t do something else so I just didn’t work on Toy Box very much as a result.

When the level model was added to the scene it immediately performed culling and added entities for the visible geometry. I was able to add mirror/portal entities here.

In my mirror system, it drew a model the stencil buffer and only drew the mirror view in the marked area. (This allows multiple mirrors in the main view without issues.) I was hung up for a while with how to draw the surface for Quake 3 mirrors. As a initial hack I just used the Quake 3 explosion model (it’s just a square) so I could continue working on it.

If I add the mirror/portal after the CPU vertex shader processes the material, it may have the wrong surface normal for the camera. So the mirror needs to use the source surface. However the material could move around so limiting the mirror to the source surface area is not correct. Quake 3 only draws one mirror/portal view and it draws on the whole screen and then draw the level over it. That’s ultimately what I decided to do. It’s essentially what I was doing with the square model scaled up but with less steps. This allowed mirrors to work but with the wrong view culling and sprite orientation.

I changed adding the level model to just create an entity with the information and later when processing entities for a scene actually add the entities for the visible geometry and mirrors/portal surfaces. This was not entirely straight forward but it had been my on TODO list for a while.

After this I made processing entities for a scene end with looping through the mirrors and add mirror view entities with have their own list of entities to draw. This included culling and added level models and generating sprite vertexes for the mirror view.

In Quake 3 levels mirror/portal surfaces do not directly specific the destination view. This is specified by the game code at run-time but it doesn’t directly specific which surface it’s for. How/when to connect this was logistic problem. However the bigger problem is it just specific a vector for the view directory and a bunch of options for how to set up the view axis (even though it literally passes the view direction in a view axis).

I still haven’t entirely implemented it. One option is the roll for the camera and it’s mainly used to just fix Quake 3 being terrible at setting it correctly. So currently there is an inconsistency in an add-on level that the view in upside-down in Quake 3 but right-sideup in Toy Box (this doesn’t look like it’s intentional upside-down).

This was working pretty well but things were getting unexpected culled in mirrors. I thought it might be a problem with like the matrix math for modifying the mirror view by the main view or the view frustum for culling. I spent a fair amount trying to debug it by drawing the camera location. This was a annoying problem of how do you draw the mirror view location in the main view and vice versa when I don’t easily have access to it with how this is structured? Two static variables and flipping the which you set and read.

However this wasn’t the problem at all. It turn out I was using the mirror camera location modified by the current main view location for the Quake 3 level’s Potentially Visible Set and it moved through walls and obscured parts of the level and so they were not drawn. Using the actual mirror location solved the issue.

I added support for only drawing models in the main view or in portal views so that Quake 3 player models draw in mirrors when using first person model. Now I’m just disappointed I went though all this work and Quake 3 only has like 7 unique mirrors/portals in it.

Whatever, I’m out of mirror hell for now.

Large level support

Rendering has a maximum distance. I set it fairly high but it cuts off some large levels (such as Quake 3: Team Arena mpterra[1-3] maps) and q3map2 _skybox entity. Setting it higher reduces depth precision and (for Quake 3 support) I don’t have control or ability to review all of the content to set the max depth distance to fit the content.

I added dynamic depth near and far plane by tracking the bounds of the 3D scene and then calculating the minimum and maximum distance from the camera. I haven’t added bounds tracking for all render commands yet. Though I also need it for adding view frustum culling for everything.

I had to rework the skybox drawing as it needs to have the geometry inside the max depth but also be depth value farther than everything else. I use glDepthRange( 1, 1 ) to set it to the max depth value and expand the scene bounds to include the skybox size. Though recently there seems to be some issues in mirrors. (Mirror hell doesn’t end.)

]]>
Maverick Model 3D 1.3.15 https://clover.moe/2024/08/18/maverick-model-3d-1-3-15/ Sun, 18 Aug 2024 23:46:57 +0000 https://clover.moe/?p=1819 Maverick Model 3D 1.3.15 adds Quake 3 player export to IQE format, fixes Linux Dark Mode and issues on 2023 Flatpak runtime, and fixes not updating frame count in animation mode until focus changes.

General

  • Add dark mode color scheme for model background
  • Add frames, FPS, and loop to New Animation window
  • Add Edit to animation mode for changing the same fields as New Animation window
  • Make Animation Sets window use New Animation dialog
  • Replace Rename in Animation Sets window with Edit for name, frames, FPS, and loop
  • Rename texture to material in Edit Groups window
  • Change max frames for an animation to 9999 (previously 999 but convert to frame animation was limited to 99)
  • Fix not updating frame count in animation mode until focus changes, text editing is disabled now
  • Fix ‘paste animation frame’ without selecting an animation
  • Fix misplaced “fi”s for configure
  • Fix ignoring user CXXFLAGS in configure
  • Fix compiling on GNU Hurd

Documentation

  • Add dark mode support for web browsers
  • Fix viewing in-app help in dark mode

Model Formats

  • Add Quake 3 player export for IQE

GNU/Linux

  • Add changelog to Linux appstream metadata
  • Update Flatpak runtime to org.kde.Platform 5.15-23.08
  • Don’t use Wayland unless requested (environment variable QT_QPA_PLATFORM=wayland)
  • Fix finding Qt translations in Flatpak

macOS

  • Fix configure failure on newer macOS
  • Fix installing Qt translation in the appbundle
]]>
Maverick on macOS https://clover.moe/2024/08/06/maverick-on-macos/ Tue, 06 Aug 2024 19:01:50 +0000 https://clover.moe/?p=1724 There is now macOS builds for Maverick Model 3D 1.3.13 and 1.3.14. Downloads are available on at the Maverick Model 3D web page.

How not to update Qt

The most recent build of Maverick for macOS had been 1.3.12 released in 2019. Maverick uses the Qt GUI framework. The Maverick 1.3.12 release uses Qt 5.11. I wanted to update it to Qt 5.15 but Qt 5.11 was the last version to support macOS 10.11 which my MacBook Pro is limited to.

I forked the several repositories required for building Qt and patched it to cross-compile from Linux for macOS (as it must be built using a newer macOS SDK) and readd support for macOS 10.11. Building had some hard coded paths specific to my machine and it didn’t handle new Qt rendering classes correctly for macOS 10.11.

I thought I would also use this customized Qt for other applications I develop (in Toy Box). Ultimately I dropped interest in using Qt aside from maintaining Maverick. I don’t want to maintain Qt or feel like I need to ensure the customized source code (1 GB zip) remains available long term.

Continuing to use the existing version of Qt was a problem due to sort of breaking the install. I had updated Qt to a non-working version and Homebrew package manager was in a broken state. I did get Homebrew and Qt 5.11 working again at some point.

Conclusion

There is now Maverick 1.3.13 and 1.3.14 macOS builds using Qt 5.11. I don’t think they should be any worse than using Maverick 1.3.12.

These new builds are still made on a 2008 Intel MacBook Pro. Thank you to the people who donated toward new Mac hardware for being able to update Maverick. The money is still set aside for Mac hardware whenever I have enough.

]]>
Clover’s Toy Box 24.05 https://clover.moe/2024/05/28/clovers-toy-box-24-05/ Wed, 29 May 2024 04:31:18 +0000 https://clover.moe/?p=1658 Clover’s Toy Box development: adding material support and changing future plans.

Background: Clover’s Toy Box (2017—) is my private 3D game engine from scratch project. It’s a continuation of ideas for Spearmint (2008—), my enhanced version of the Quake 3 (1999) engine. I cut my teeth modifying SRB2 / the Doom (1993) engine in 2006-2008. I’ve done computer programming for about 18 years.

Materials

About a year ago I started working on adding support for Quake 3 materials (*.shader files) to Clover’s Toy Box and Toy Box renderer for Spearmint (my private reimplementation of the Spearmint renderer using Toy Box).

Quake 3 materials define how to draw an image on a 3D model, game level surface, or in the menu. They allow for multiple blended images, animated image sequences, and dynamic effects such as scrolling an image, flashing color, or changing the position of the surface.

It’s difficult to implement the Quake 3 material system as there isn’t a complete definition of how it works, it’s complicated, and it interacts with all rendering. It kind of spiraled out into implement or fix all the rest of Quake 3 renderer features in Toy Box renderer for Spearmint.

Most of the Quake 3 material features are supported by Toy Box now. It’s missing the sky dome, fog, and a few position modifiers (deformVertexes). It has some of the additions made in Spearmint but I haven’t focused on it. However there is still many issues for rendering Quake 3 that are not directly part of the material system.

My Toy Box renderer—including the implementation of the Quake 3 material system—runs on OpenGL, OpenGL ES, and WebGL as well as the Wii console. Though the Wii console runs out of memory loading level textures (only 88 MB of RAM) and it’s missing an implementation of polygonOffset for preventing decals from flickering.

It runs on modern and legacy OpenGL. (OpenGL is a programming interface for hardware accelerated graphics rendering on a graphics card.) Toy Box is compatible with modern OpenGL 3.2+ Core Profile and streams geometry using OpenGL 4.4 persistent mapped buffers. I’m particularly proud (amused?) of the Quake 3 material system being fully implemented on legacy OpenGL 1.1 which Quake 3 supported in 1999.

(I also fixed ioquake3 and Spearmint to fully support a sky box using OpenGL 1.1 which may be useful for some Intel graphics under Windows 10 stuck with Microsoft’s generic OpenGL 1.1 driver.)

Implementation

Some parts of the material system were straight forward to add. Others not so much. I spent a lot of time testing Quake 3 format levels (official, add-ons, and other games). I found issues and made a list of them to look into. Looking into issues often found it was a different manifestation of a known issue that I hadn’t fixed yet.

I have a list of like 50 issues and not everything made it onto the list. It’s honestly not that exciting to recount. Things were broken and then I fixed them.

The material system can kind of be broken down into five categories:

  • Material file parsing, implicit keyword behavior, and sort order.
  • Changing OpenGL rendering state.
  • Changing vertex attributes (position, normal, color, texture coordinates).
  • Changing vertex attributes but behavior very specific to Quake 3.
  • Special handling for the sky and fog volumes.

Material parsing

The Toy Box Quake 3 material loader handles various implicit behavior. If a material has “rgbGen vertex”, it default to using “alphaGen vertex”. However “rgbGen exactVertex” uses opaque alpha which is inconsistent. Using image blending disables writing depth which is to prevent surfaces behind it drawing over it. There is several things that affect the implicit sort order. It’s just a lot of random stuff to fill out the full Quake 3 material definitions correctly.

The Quake 3 material definitions are converted to a separate material system that has some additional features and different implementation of some features. My intention is to create a new material file format that doesn’t depend on Quake 3’s implicit behavior.

OpenGL state

Changing the OpenGL rendering state was mostly straight forward to add. Many of the material keywords are easy to infer how they directly map to the OpenGL API. Face culling, blend modes, alpha test, depth test, depth write, and polygon offset.

Though colors being floating-point (i.e., 1.0) in the material definition and converted to an 8-bit integer (i.e., 255) when it’s loaded was not obvious and affected alpha testing for conditional transparency.

A material for an unofficial Quake 3 add-on level (xccc_dm4) specified using alpha 0.5 and set the alpha test to greater-or-equal to 0.5. This caused transparency to flicker because OpenGL rendering doesn’t have perfect precision and alpha values vary slightly above and below 0.5.

Quake 3 converts the alpha 0.5 to 8-bit integer (0.5 × 255 = 127.5) and rounds it to 127 and then the OpenGL driver compares 127 ÷ 255 as a floating-point value 0.498039216 with alpha test reference 0.5. This way there is no flickering. Though it doesn’t draw at all as the alpha is always lower than 0.5. This is apparently what the creator intended as it’s not visible in a video they made of the level either.

So I convert the floating-point value from the Quake 3 material to an 8-bit integer and then back to a floating-point value as that’s what I’m using for colors in Toy Box.

Vertex attributes

There are vertex attribute generators/modifiers for vertex position, normal (direction), texture coordinates, and color. I implemented all of them in the CPU vertex shader (it’s just a function that outputs new vertexes). This way they work on all graphics APIs (OpenGL fixed-function and shaders, Wii, …).

The CPU vertex shader which I started quite some time ago (see Toy Box 22.04 § “OpenGL 1.1 fixed-function rendering”) had to be expanded to allow for multiple material layers with separate generated/modified vertex attributes and to support many new effects.

I also finally replaced the initial CPU vertex shader specific to OpenGL 1.x (low-level, non-VBO compatible) with the API-independent code. Now OpenGL fixed-function rendering can use persistent mapped buffers and vertex buffer objects.

I mark material layers for which attributes need to be processed on the CPU and then tell the GPU to just use the submitted values for that attribute. This allows mixing CPU and GPU vertex attribute handling. For example, processing the position modifiers on the CPU and using the GPU to apply the texture matrix for texture coordinate modifiers.

Most of the Quake 3 position modifiers need to be applied per-vertex (opposed to a matrix multiply). This is always done in the CPU vertex shader as it isn’t compatible with graphics APIs for the Wii or OpenGL 1. They could be implemented using OpenGL 2 GLSL shaders but it’s very specific to each position modifier. I’m trying to keep the graphics backends kind of generic.

Currently if any attribute requires the CPU vertex shader it results in animating the model on the CPU instead of using OpenGL 2 GPU skeletal or frame animation. This is kind of a disappointment for “skeletal models with mixed CPU and GPU vertex attributes” as the performance is probably largely affected by animation. Though I haven’t considered what cases it would be beneficial to optimize this for; a lot of the CPU-only effects use the vertex position.

Vertex colors

I split up some of the Quake 3 material keywords so they could be handled in a more general way.

Spearmint has twelve RGB vertex color generators. In Toy Box the color generators are split up into four base color generators (white, vertex, one minus vertex, model lighting) and five color modifiers (constant color, waveform for color intensity, entity color, one minus entity color, and underbright to counteract Quake 3 scene overbright).

This replaces having lightingDiffuse and lightingDiffuseEntity for model lighting without and with entity color, and const, wave, and colorWave for white with constant color, a waveform for color intensity, and both.

It’s easy to tell the Wii to use white or vertex color but one minus vertex and model lighting use the CPU vertex shader to generate the color arrays. The color modifiers can all be combined and applied using the Wii GPU. Most of the color generators are GPU accelerated on the Wii. One minus vertex which is rarely used (I haven’t actually checked if it’s used by anything). Eventually I need to figure out GPU model lighting for supporting normal maps (per-pixel light direction).

OpenGL 2 GLSL can do all of the base types + apply the color modifier on the GPU. Though I currently have one minus vertex disabled because I’m not sure it’s worth supporting it in the backend.

OpenGL 1 can only do white and vertex color (like the Wii) but it can only apply color modifiers on the GPU if it uses white and not vertex colors or model lighting. So the renderer just marks the material layers using vertex colors or model lighting to use the CPU vertex shader for color modifiers. Model lighting already uses the CPU vertex shader so it doesn’t really affect much.

Vertex texture coordinates

Spearmint has six texture coordinate generators (base texture, lightmap, vector w/ matrix, cel-shading, two types of reflection-mapping) and in Toy Box they’re split up into a five base generators (base texture, lightmap, position, normal, and reflect) and a matrix if needed. Reflect is shared by the two reflection mapping methods for Quake 3 and Raven Software’s Quake 3 based games.

This doesn’t reduce a lot but it does make it easier to implement GPU support as a base type + texture matrix. I haven’t added them yet but OpenGL 1.3 fixed-function and 2.0 GLSL can support all of them. The Wii has GPU support for all generators except reflect.

I tested generating reflect vectors and storing them in the vertex normals on OpenGL for using reflect via the normal generator and it works. This may be a way for the Wii to utilize CPU reflect with GPU texture matrix. Eventually Wii can have at least partial GPU support for all of the texture coordinate generators.

Vertex attribute conclusion

The Toy Box material handling is being designed around GPU color multiply and texture matrix multiply. Quake 3 has more specific handling for combining some generators and modifiers to allow for better CPU optimization. Scrolling a texture only needs two additions per-vertex, not a full matrix multiply.

My understanding is that the Wii always does a texture matrix multiply. It can be set to an identity matrix but it can’t be disabled. So it may not require extra processing to use a texture matrix multiply aside from the data transfer of the matrix to the GPU.

Very specific behavior

Some of the material keywords are very specific to Quake 3 and it would be very difficult to accurately remake them without basing it on the math from Quake 3. Math can be patented but not copyrighted. So it seems that using these equations would not be copyright infringement. Though they aren’t really necessary for creating a new game and I’d probably leave them out of a new game.

Keywords such as tcMod turb for turbulent lava texture scroll and alphaGen lightingSpecular for slightly non-standard specular with a hard coded light origin. I haven’t decided if I should try to remake the sky dome texture coordinate generator and so the sky dome is currently missing. Quake 3 uses these on basically every official level.

Future plans

I had thought about releasing closed-source commercial software (content tools, game engine) compatible with Quake 3 data formats as a way to try to monetize Toy Box. Though it’s very unrealistic to generate the level of revenue I would like it to and it would probably just become a burden.

I’m no longer planning to release any software based on Toy Box supporting Quake 3 data formats. I’m not planning to pursue selling or releasing a content application or game engine. I’m also not planning to reprogram and release Turtle Arena (without bots) on Toy Box.

I’m satisfied with what I’ve accomplished in Clover’s Toy Box so far and I intend to continue developing it. Currently the only planned software to release based on Toy Box is original games (which I don’t actually have plans for).

I want to move in the direction of focusing on art with the end goal being images and videos instead of a full game. Though that’s not necessarily related to Toy Box and there is a lot of stuff I need to deal with before that.

]]>
Flexible HUD release 8 https://clover.moe/2024/03/31/flexible-hud-release-8/ Sun, 31 Mar 2024 22:07:27 +0000 https://clover.moe/?p=1644 ZTM’s Flexible HUD mod for ioquake3 release 8 has a new Git repository, updates to newer ioquake3, and adds a config file for very high quality graphics settings.

New Git Repo

The source code has moved from a flexible_hud branch and tags in zturtleman/ioq3 repository to a new zturtleman/flexible-hud-for-ioq3 repository.

Flexible HUD was treated as a one-off throw away project in 2013. The source code for first two releases was previously missing from the Git repo. They had “git diff” patches in the Flexible HUD pk3 downloads. Now the code is committed and tagged in the Git repo.

ioquake3

I merged in the latest changes from ioquake3 (2019 to 2024). It includes these changes I made in 2019 to 2021 that affect the mod code:

  • Restore bots crushing unseen player on q3tourney6 in non-CTF (restores the original harder difficulty of the final boss)
  • Fix team orders menu not listing clients with lower clientnums (it wasn’t possible to order bots that joined before you on a server using the Q3 menu)
  • Fix duplicate team join center print for bots and g_teamAutoJoin (too many messages could cause an error and kick players off the server)
  • Fix lightning gun handling for corpses and single player podiums (this shouldn’t affect anything)

Config

I added my 2018 config file for applying Spearmint’s “Very High Quality” graphics settings to ioquake3. The Flexible HUD web page says how to apply it.

This was previously a standalone download. I don’t remember where if anywhere it’s linked. It seems relevant to users of Flexible HUD so I included it.

Rounding it out

The Flexible HUD downloads and ztm-ioq3-veryhighquality.cfg are now hosted on GitHub instead of clover.moe/downloads. This was actually the main motivation for all of this. I think about moving to a static website and I don’t want to add a bunch of zip files to the website Git repository.

]]>
Open Source in 2023 https://clover.moe/2024/01/02/open-source-in-2023/ Wed, 03 Jan 2024 02:59:06 +0000 https://clover.moe/?p=1582 My open source software contributions in 2023 to SDL, Quake 3 based games, and Maverick Model 3D.

SDL

Simple DirectMedia Layer is a cross-platform abstraction layer for window creation, graphics initialize, audio, input, etc that is used by most games on Linux. (SDL source code)

I mentioned custom window decoration “hit testing” resizing issues at the end of my Toy Box on Wayland post. Someone fixed Linux (X11 and Wayland) to display cursors when hovering over resizable areas. It assumed the cursors were double arrows like on Windows and KDE. Resize left and right both had a single arrow pointing right on GNOME. I fixed it to have the correct cursors on GNOME using 8 cursors with each resize direction.

I helped explain an issue with Nintendo GameCube Adapter controller mapping so it could be fixed on Windows and Android.

I try to read most of the SDL 3 commits by following an RSS feed. I pointed out a few minor issues as commit comments.

ioquake3

ioquake3 is a project to maintain and extend the source code for the 1999 first-person shooter Quake III Arena. (ioq3 source code)

I made 25 posts to help solve issues on the ioquake3 forum.

Platform

I updated SDL libraries for Windows and macOS from SDL 2.0.14 to SDL 2.24.0. I cross-compiled mingw-w64 and macOS libraries on Linux. I did have to use Windows to build the MSVC libraries.

I made it so macOS can be built separate for “legacy” Universal Bundle (x86/x86_64/powerpc) and “modern” Universal Bundle 2 (x86_64/Apple Sillicon). Legacy macOS App Bundle was updated to SDL 2.0.22 as newer versions (jumping to SDL 2.24.0) dropped support for macOS 10.6. (Building for Apple Sillicon was added by Tom Kidd in 2022.)

I fixed ioquake3 failing to start from macOS terminal due to how the URI scheme handler support was added.

I updated the Windows “NSIS installer” script. ioquake3 and games based on it tend to just use a .zip file instead of an installer. It would be useful to use the installer as it adds integration for the “quake3://connect/127.0.0.1” URI scheme handler to Windows.

QVM

QVM (Quake Virtual Machine) is a cross-platform bytecode format used by Quake 3 that allows game modifications to be compiled on one platform and run on all platforms. (A similar recent technology being WebAssembly.)

I fixed compiling QVMs on Linux if the source code has Windows line endings. MS-DOS used CR+LF to end lines in text files (Carriage return, line feed; commands for a text printer). However Unix-like platforms used only LF. The Windows file API in text mode automatically reads CR+LF as LF but other platforms did not and caused the QVM tools to fail with strange syntax errors.

I made it so QVMs are compiled on all platforms by default even if they do not have a run-time QVM just-in-time compiler. The QVM interpreter works on all platforms. This made QVMs be built on Linux/macOS ARM64 by default.

OpenGL2 renderer

Compared to the opengl1 renderer—which closely resembles the original Quake 3 renderer—the ioquake3 OpenGL2 render has a lot of issues and (in my opinion) poor design decisions. I think it’s too much work to fix it. However when prompted about issues, it’s not that I can’t fix them…

I fixed the edge border for smaller view size (cg_viewsize) being drawn to an framebuffer object and not blit to the screen when using HDR or framebuffer multisample anti-alias with post-process. This was fixed to draw to the screen directly like other 2D drawing.

I fixed updating the loading screen with r_cubeMapping 1. The changes for the previous issue caused Quake 3’s drawing all images to the screen—to ensure the driver loads them—to be visible. However it shouldn’t be visible because the loading screen draws over it. It turns out generating cube maps for the level left the culling state set to hiding the front of polygons and the loading screen was culled until the level is loaded to reset the culling state. This was broken for years but it seemed like it was just really fast after loading the level.

I fixed the developer option to clear the screen to magenta (r_clear 1) before rendering when using HDR or framebuffer multisample anti-alias. This makes it obvious when some part of the screen isn’t drawn or is transparent and the previous frame is visible (hall of mirrors effect or random flashing between two different frames; it’s just bad).

I fixed framebuffer multisample anti-alias on AMD Windows driver. The driver incorrectly requires GL_EXT_direct_state_access extension to bind a renderbuffer for it to be valid. This should only be required for OpenGL Core context direct state access or GL_ARB_direct_state_access extension. Though yes, ioquake3 should probably stop using the EXT extension in an OpenGL Core context.

I fixed q3map2 lightstyles effects for dynamic pulsing lightmaps with r_mergeLightmaps 1 and r_sunlightMode 1.

r_mergeLightmaps 1 combines internal 128×128 pixel lightmaps into a larger atlas and modifies the geometry texture concordances. This broke materials using both internal and external lightmap and materials using texture concordance offset with internal lightmaps (two obscure things I didn’t know q3map2 did). I corrected external lightmap to apply a transform to convert the texture concordances back to the original and made offset for internal lightmaps use the scale of the texture atlas.

r_sunlightMode 1 changed lightmap stages in materials to support cascading sun shadows by using white image modulated by the lightmap texture. However it didn’t work with some blend mode that use the alpha of the lightmap texture. I fixed it to only apply to normal lightmap stage blend modes.

I fixed parsing q3gl2_sun without two additional “optional” tokens. Quake 3’s text parser has an option to not allow line breaks but it still sets the text pointer to after the line break. So effectively it can only check for an optional token once. And then must not check for any more because it will be on the next line. I fixed parsing q3gl_sun to only check for additional optional tokens if the previous optional tokens existed. (I previously fixed the parser in Spearmint to stop at end of line so it doesn’t have this problem but I’m more concerned about compatibility with weird content in ioquake3.)

World of Padman

World of Padman is a freeware first-person shooter based on the ioquake3 engine. (WoP source code)

My widescreen HUD support from “ZTM’s Flexible HUD for ioq3” was merged into World of Padman for version 1.7. I helped fix two widescreen issues with health station icons (distance independent 2D sprites on the HUD) and lens flares.

Some of the OpenGL2 renderer changes in ioquake3 were for World of Padman or at request of one of the World of Padman developers. q3map2 lightstyles effects are used by the wop_trashmap level.

Q3Rally

Q3Rally is a freeware racing and third-person car shooter based on the ioquake3 engine. (Q3Rally source code)

Some changes I made include:

  • Bots can drive around more race maps now (“Fix bots going in reverse for no reason in races”)
  • Fixed spectator observer camera to rotate smoothly
  • Fixed intermission view angles
  • Fixed players in race have an invisible chainsaw
  • Fixed client error dropping to menu when player dies if sv_maxclients is too high (“Fix out of range death events”)
  • Updated to latest ioquake3
  • Various other bug fixes

I fixed game network compatibility and enabled ARM64 support for the new Q3Rally Flatpak package for Linux.

Spearmint

Spearmint is my enhanced engine and game-logic based on ioquake3. (engine source code, game-logic source code)

opengl1 renderer

I fixed two issues in the opengl1 renderer due to adding changes from Wolfenstein: Enemy Territory.

Wolfenstein: Enemy Territory added far plane culling. The level BSP node bounds doesn’t include surfaces for q3map2 skybox portal hack (_skybox entity) so the far plane wasn’t set far enough and the skybox scene was cut off. Instead the bounds of surfaces in the BSP node should be used, which Wolfenstein: Enemy Territory also added.

Wolfenstein: Enemy Territory fixed surface culling for two sided level surfaces but this broke snow flakes in rgoer_seasons user created level for Quake 3 using “deformVertexes move” material feature to change the surface position. I changed back to Quake 3 behavior of not using exact view culling for two sided surfaces.

OpenGL2 renderer

I fixed bullet/explosion marks on doors and moving platforms in splitscreen player views. (It was pointed out to me that I fixed this in opengl1 years ago but I forgot to apply it to the OpenGL2 renderer.)

Game-Logic

I added an option to disable view kick when receiving damage.

I added setting the frame rate in the graphics options menu.

Maverick Model 3D

Maverick Model 3D is a 3D model editor and animator that I maintain that supports some game specific formats. It’s based on Misfit Model 3D that ceased development. (Maverick source code)

I developed/released Maverick Model 3D 1.3.14 in April.

In addition to that:

I fixed a couple issues for macOS and made GitHub Actions build and upload macOS build. If you’re logged into to GitHub, the macOS build can be downloaded from the bottom of the “Actions summary” page for each commit. I haven’t tested it as it requires newer macOS than I have access to.

I fixed a couple issues that I found through the Debian package tracker several years ago. CXXFLAGS are now respected by configure and it compiles for GNU Hurd. Though OpenGL—required for displaying the model—didn’t work in the Debian 2023 GNU Hurd virtual machine image.

I added support for exporting a Quake 3 player model with three separate models (head, upper, lower) to IQE format which can be converted to IQM for use with ioquake3 and derivative projects.

IQM uses skeletal animation which allows for less memory usage and better animation quality compared to Quake 3’s MD3 format that stores lower precision vertex positions for each frame and interpolates vertexes in a straight line between frames which may cause meshes to deform. Though IQM may be slower to draw.

I got IQE Quake 3 player model export working in 2018 (as reference in this ioquake3 forum thread where I was working on improving performance and fixing issues with ioquake3’s IQM support). I wanted to move Quake 3 player model export out on the individual format exporters but that never happened. So now it’s in IQE exporter.

P.S.

Most of these changes are a result of interacting with people rather than my own ideas or done for the sake of hypothetical other people.

If you found this useful, consider donating on Ko-fi (no account required).

]]>
Toy Box on Wii https://clover.moe/2023/09/29/toy-box-on-wii/ Fri, 29 Sep 2023 21:42:21 +0000 https://clover.moe/?p=1485 Clover’s Toy Box development: porting to the Wii console.

Wii

I ported Clover’s Toy Box to the Nintendo Wii console. It supports rendering, audio output, networking, Wii/GameCube controllers, and USB keyboard and mouse.

I used the Wii homebrew SDK (devkitPPC, libogc, and satellite libraries) which does most of the difficult work.

Wii Remote pointing on the screen (IR sensor) is supported but I haven’t integrated support for the accelerometer motion sensor or Wii Motion Pus (gyro sensor).

The Wii Remote speaker is not support by the Wii homebrew SDK. Using Wii Motion Plus with Nunchuk extension isn’t support by the homebrew SDK either. So adding support for those would be more involved.

Getting the renderer working was easier than I expected. I mainly had to implement the functions to draw triangles/lines and upload textures (not obvious how for RGBA8) to get something to display. There wasn’t any set up needed beyond the example Wii video init. The GX graphics API is very similar to fixed-function OpenGL. In some cases separate OpenGL functions are combined as a single GX function or slightly lower-level.

I added support for blend modes, depth test/write, alpha test, cull modes, multi-texture (lightmaps), texture-less (color only) rendering, vertex color, color multiply, and texture coordinates matrix. There is no support for normal maps and frame post-processing yet.

The ground work started in 2020 with getting Toy Box to compile for GameCube/Wii with stubbed out functionality and adding OpenGL 1.1 support in 2022 partially to make it easier to add fixed-function rendering on the Wii.

I need to improve performance. In some parts of the Turtle Arena Subway level it runs at 60 frames per-second. If it renders the whole map it drops to 20 frames per-second which is way too low for the level size.

I need to better handle allocating memory and out of memory errors. The Wii only has 88 MB of RAM (which is split into two separate parts) and I basically pretend Toy Box will never run out of memory.

GameCube

The GameCube and Wii consoles are similar. The graphics features are actually identical between the two consoles. The GameCube CPU/GPU are slower and it only has 27 MB of RAM. The Wii has a bunch of extra stuff added like internal flash memory, Bluetooth, etc.

I keep Toy Box able to compile for GameCube as well. However I don’t know an easy way to add data files for the GameCube build like placing them on the Wii’s SD card. So it just draws flat colored triangles and lines in the menu. I don’t have a way to run homebrew on a physical GameCube but it runs on the Dolphin emulator. (Technically the GameCube build should run on the Wii but I haven’t tested it.)

GX RGBA8 format

GameCube/Wii 8-bit RGBA format uses 4×4 pixel block tiles (similar to S3TC) with alpha and red stored in 32 bytes and then green and blue in next 32 bytes. I think I found this referenced somewhere in YAGCD (Yet Another GameCube Documentation) and had to dig into some asset converter to figure it out.

4×4 tile linear sequential memory (spaces / line break for readability):

ARARARAR ARARARAR ARARARAR ARARARAR

GBGBGBGB GBGBGBGB GBGBGBGB GBGBGBGB

It’s the pixel colors left-to-right top-to-bottom for the 4×4 block with alpha and red and then separately for green and blue. Additional blocks continue after this to make a 8×4 image or whatever size.

Other formats like RGB565 seem to work as commonly expected (not using tile blocks).

Doom

I have a private fork of the original Doom source code for Linux that uses Clover’s Toy Box. I updated it to run on the Wii.

My Doom fork is mainly for messing with “porting an old game” to use code from Clover’s Toy Box but Doom is already really portable to many platforms that there wasn’t much to do. There is already multiple versions of Doom for the Wii available, this isn’t anything new.

I fixed compiling the Doom big endian byte-swap functions, disabled Doom networking and audio which don’t compile for Wii, added uploading the Doom software rendered game frame as a GX texture, and added remapping controller buttons to Doom key values. And it basically worked.

Pressing the key to move backward, moved forward and it was faster than the forward key. The forward_move and side_move input command was a “char” value. It turns out on the Wii, char defaults to unsigned (range 0 to 255, instead of -128 to 127) so forward_move = -25 became 231 (256 – 25).

To avoid potential issues with using the homebrew SDK libraries, I opted to change Doom input to use “signed char” instead of overriding the default for “char” with a compiler option.

It supports Wii and GameCube controller joysticks/buttons and USB mouse and keyboard but pointing the Wii Remote for aiming isn’t supported.

For Doom on all platforms, I would need to fix audio and improve input integration.

Interlude

Following a Twitter trend, I made a top 25 games list.

It was hard to think of 25. There are other games but it’s been a long time since I played them or thought about them. I mostly replaced playing games with software dev and watching TV shows a decade ago.

(For whatever reason I included with the demo of Metroid Prime Hunters that was bundled with the Nintento DS. It’s kind of a weird game selection.)

Image created on topsters.org

I first played seven of the games on the GameCube or Wii.

  • PC: 9 games
  • GameCube: 4 games
  • Wii: 3 games
  • PlayStation 3: 3 games
  • PS Vita: 2 games
  • Nintendo DS: 1 game
  • Nintendo 3DS: 1 game
  • SEGA Genesis: 1 game
  • SEGA Dreamcast: 1 game

What this doesn’t represent well is that I may have played 75 games across the GameCube and Wii consoles compared to 5 or less per-consoles since then (PS3, PS4, PS Vita, 3DS) and I haven’t played as many new games on PC (Steam) since then either.

Why?

The simple answer is I ported Toy Box to the Wii because I wanted to even though there is no real use case anymore.

[This post was original titled Clover’s Toy Box 23.04 but I put off rewriting the Why? section for months to not be very long and off-topic.]

Summer of 2006

I started programming in the summer of 2006 by modifying Sonic Robo Blast 2 based on the Doom engine. I followed the online hype of the GameCube successor—the Revolution—that became the Wii released in November 2006. I was also looking forward to the Ninja Turtles 2003 TV series resuming in the fall, continuations of Sonic and TMNT 2003 game series on the Wii, and some other things eventually happening.

On the one hand, I was bored in the summer of 2006 so I started trying to make a game. On the other hand, I was excited looking forward to several things.

However nothing I was looking forward to met my expectations and some didn’t happen at all.

I wanted to make my own successor to the Ninja Turtle GameCube games that would run on the Wii. This a subject that I continue to work on over the years; whether it be Turtle Arena, controller support on desktop computers, or trying to run software on the Wii.

The Eternal Summer of 2006

I sarcastically refer to Clover’s Toy Box as taking place in “the eternal summer of 2006”. It was a point of excitement and looking forward to the possibility of the future.

And so, I ported Clover’s Toy Box (and Doom) to the Wii because I can.

Though I’m kind of bored.

]]>