Difference between revisions of "1K Intro"
Line 130: | Line 130: | ||
==== High Quality 1K intros for Windows ==== | ==== High Quality 1K intros for Windows ==== | ||
+ | * [https://demozoo.org/productions/361614/ Vanisher (2024)] | ||
* [https://demozoo.org/productions/255140/ Tech Power Ride (2019)] | * [https://demozoo.org/productions/255140/ Tech Power Ride (2019)] | ||
* [https://demozoo.org/productions/164449/ Escalated (2016)] | * [https://demozoo.org/productions/164449/ Escalated (2016)] |
Latest revision as of 10:12, 19 November 2024
Introduction
This category is dedicated to 1K Intros for Windows / win32.
Windows 1K Intros can be setup in C or Assembler, or a combination of both, with only the few windows calls that are used being linked in. In general writing everything in assembler should give you a little more freedom in pushing/popping your function arguments and register usage, but in the past quality intros have been written in both languages.
Graphics are usualy done using OpenGL / GLSL, but there are 1K intros out there that have used D3D/DX11 instead.
Tools
There are a couple of options for tools, but generally these tools are often used
- Visual Studio / Vscode / GCC Compiler
- Crinkler or other external packer for compression ( https://crinkler.net )
- Optional / Alternatively: NASM Assembler ( https://nasm.us/ )
- Optional: Local/Online GLSL Shader editing environment (e.g. Shadertoy, Kodelife, bonzo, etc.)
- Optional: Shader minifier ( https://github.com/laurentlb/Shader_Minifier )
Minimal intro code
Here is a step by step breakdown of what a minimal intro/framework should contain.
As a general rule of thumb: The less functions you need to use/import, the more space you have for your effect and data.
Setup (Windows)
-
ChangeDisplaySettings(&screenSettings, CDS_FULLSCREEN)
- Initialise fullscreen view -
hwnd = CreateWindow((LPCSTR)0xC018, 0, WS_POPUP | WS_VISIBLE | WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0)
- Setting up a Maximised Window -
hdc = GetDC(hwnd)
- Get DeviceContext handle -
ChoosePixelFormat(hdc, &pfd)
- Get pixelformat described by pfde -
SetPixelFormat(hDC, pf, &pfd)
- Set pixelformat -
ShowCursor(NULL)
- Hide mousecursor (usually required by compo rules)
Setup (OpenGL)
-
wglGetProcAddress(functionName)
- Get function address for an (extended) OpenGL function -
glContext = wglCreateContext(hdc)
- Create OpenGL context -
wglMakeCurrent(hdc, glContext)
- Set OpenGL Context -
shaderProgam = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &shader)
- Create and compile GLSL Shader -
glUseProgram(shaderProgram)
- Set active shader
Update Loop
-
t = GetTickCount()
- Optional: Get Current ticks -
glColor3us(t,0,0) / glColor3i(t,0,0)
- Parse time via gl_Color (saves importing uniform functions) -
glRects(-1,-1,1,1) / glRecti(-1,-1,1,1)
- Draw a single quad with current Shader -
SwapBuffers(hDC)
- Swap Buffers -
PeekMessageA(0, 0, 0, 0, PM_REMOVE)
- Optional: Unanswered messages might stack up, but in practice you should be fine in fullscreen -
GetAsyncKeyState(VK_ESCAPE)
- Checks for escape key (required by compo rules)
Closedown
-
ExitProcess(0)
- Close down application
Graphics / Effect
As far as I am aware of, most if not all 1K Windows intros use a single-stage shader setup to run everything. This means that everything including timing, shading, progression, etc. needs to happen in a single GLSL/HLSL shader, often based on a single effect, with some changes over time.
So when it comes to writing your 1K Shader/Effect, there are a couple to tricks you can utilise to get a small shader footprint.
- Make use of the benefits of (Legacy) Desktop GLSL by using integers for smaller notation
- Make use of the fixed resolution to convert gl_FragCoord to UV -1..1 (this will have your centerY slightly off-centered)
- Pass time via glColor (no uniforms used)
- Using global variables to save space
Minimal GLSL example code
float t=gl_Color.x*1e7;
void main(){
vec2 p=gl_FragCoord.xy/960-1;
gl_FragColor=vec4(sin(p.x+t),0,p.y,1);
}
Shader minifier
Whilst a shader minifier can be helpful at times, it will not save you from writing sloppy code or unthoughtful approaches. For most 4k's people can get away with sub-par optimised code and let shader-minifier do the work for you, but for 1k this is just not enough.
Sound
There are a few ways to produce sound/music in Windows 1K Intros.
Win32 Single Note MIDI
Using the built-in MIDI, this requires just 2 calls
-
midiOutOpen(midihandle, 0,0,0,0)
- To initialise the MIDI -
midiOutShortMsg(midihandle, 0x209035C0)
- 4 byte Midi message that sets instrument + plays a single note on channel 0
For a full list of Midi commands, check out this webpage: https://computermusicresource.com/MIDI.Commands.html
Win32 MIDI Player
Depending on the sound budget, you can think about including a small MIDI tune instead. Similar to the single note MIDI approach, but playing multiple notes from data on 1 or more different channels.
Bytebeat softsynth
An alternative option is to use a very simple bytebeat softsynth (Check out https://bytebeat.demozoo.org if you are not familiar with the concept). This requires an additional setup of the Win32 WaveOut callback, which can then be played and filled realtime with data.
There are 2 ways to accomplish this, the first one is using these 3 calls:
-
waveOutOpen
-
waveOutPrepareHeader
-
waveOutWrite
Which allows for realtime buffer updates and getting position using waveOutGetPosition
Or by using a 44 byte WAV header and filling the soundbuffer in advance, then using the sndPlaySoundA
call to play it as a looping sample at startup.
For a taste of what is possible with small (<256 character) bytebeat formulas, check out one of the Bytebeat Music competitions from the Lovebyte parties, for example https://demozoo.org/parties/4760/#competition_18623
Shader softsynth
With this approach you render the sound from the GLSL shader and copy that back to the soundbuffer. It takes a bit more code to setup, but depending on your usecase and complexity of the sound, the GLSL code might compress a bit better.
Compression
Microsoft Cabinet decompression
These functions allow for MSZIP (deflate) and MSLZ content to be loaded from the binary as payload at the cost of a a few more win32 function imports. While this might be a good solution for 512 byte intros, the tipping point for Crinkler compression at the moment lies at the 800-850 bytes mark give or take.
From cabinet library:
-
CreateDecompressor(0x20000002, 0, &HDecompressor)
- Create Decompressor object -
Decompress(HDecompressor, CompressedPayload, PackOutputSize, 0x11000, PackInputSize,0)
- Decompress payload
Crinkler
Crinkler is the most commonly used compression at the moment for many 1K and 4K intros. It maximizes compression by taking over the linking process, setting up the windows header for you and if needed re-arrange the different segments of the code.
It has a seperate settings for 1K intro, namely /TINYIMPORT
and /TINYHEADER
which will better match the 1K usecase.
Crinkler reports
When using the /REPORT:intro_report.html
feature, crinkler will generate a report and heatmap for your intro binary.
This is helpful to see how well your (shader)code compresses and where you can maybe gain a few more bytes.
Word about the resulting executable
Please note that when using crinkler in TINYHEADER mode, the resulting windows header will most likely trigger virus scanners / Microsoft Defender and mark it as suspicious content, which is a side-effect in recent years. Just exclude your work directory from this scan during development. Compo machines will run without these scanners too, but it is good practice to include a (slightly larger) safe version too in the final archive.
Links
Frameworks
High Quality 1K intros for Windows
- Vanisher (2024)
- Tech Power Ride (2019)
- Escalated (2016)
- Ghosts in Paradise (2016)
- Eraserhead (2016)
- Molten Core (2014)
- SPHEAR (2011)
- Mistrace (2010)
- Untracable (2009)