diff --git a/public/catalog.json b/public/catalog.json index 93948b7..a9cb30b 100644 --- a/public/catalog.json +++ b/public/catalog.json @@ -1393,7 +1393,7 @@ }, { "slug": "doom", - "introduction": "It was May 1992, and the team at id Software was riding high on the success of their groundbreaking game, Wolfenstein 3D. But John Carmack, the brilliant programmer behind the studio’s technological feats, was restless. He envisioned a new frontier in gaming—something darker, faster, and more immersive than anything players had experienced before. Alongside John Romero, the charismatic designer who would shape the game’s visceral tone, and Dave Taylor, a skilled programmer with a knack for refining systems, Carmack began work on what would become DOOM. The project was ambitious, and the stakes were high: id Software was still a small, independent studio, and failure could mean the end of their meteoric rise.\n\nThe computing world of 1993 was a landscape of constraints and ingenuity. Consumer PCs were powered by Intel 386 processors, with clock speeds measured in megahertz and memory capped at a few megabytes. Graphics cards were rudimentary, and VGA monitors displayed resolutions that seem quaint by today’s standards. Yet Carmack saw opportunity in these limitations. He developed a revolutionary 3D engine capable of rendering environments with unprecedented speed and fluidity, using techniques like binary space partitioning (BSP) to optimize performance. The game’s “2.5D” graphics—where 3D environments were populated with 2D sprites—allowed DOOM to create a sense of depth and immersion without overwhelming the hardware.\n\nThe team’s creative process was as chaotic as the game itself. Tom Hall, originally tasked with writing a science fiction narrative, clashed with Romero over the game’s direction. Hall’s vision of a complex story was ultimately discarded in favor of Romero’s focus on raw, adrenaline-pumping action. Sandy Petersen joined the team to design levels that would become iconic for their labyrinthine layouts and relentless pacing. Carmack, ever the pragmatist, famously declared, \"Story in a game is like a story in a porn movie. It’s expected to be there, but it’s not that important.\" This ethos shaped DOOM into a visceral experience where gameplay reigned supreme.\n\nWhen DOOM was released in December 1993, it was nothing short of a phenomenon. Distributed as shareware, the first episode was free to download, allowing millions of players to experience its revolutionary gameplay. Within two years, an estimated 20 million people had played DOOM, and its full version sold millions of copies. The game’s multiplayer mode—allowing players to battle each other over local networks—sparked the rise of online gaming communities and laid the foundation for modern esports. Its graphic violence and satanic imagery courted controversy, but this only fueled its notoriety and cultural impact.\n\nDOOM’s legacy is immeasurable. It redefined the first-person shooter genre, inspiring countless imitators and establishing id Software as a powerhouse in the gaming industry. Its modding capabilities fostered a vibrant community that continues to create new levels, modifications, and even entirely new games using the DOOM engine. The source code, released in 1997, became a treasure trove for developers and enthusiasts, allowing them to dissect and learn from its groundbreaking techniques. Today, DOOM is preserved in the Library of Congress and remains a touchstone for game design, a testament to the ingenuity and audacity of its creators.", + "introduction": "It was late 1992, and the offices of id Software in Mesquite, Texas buzzed with the energy of a team on the brink of revolutionizing gaming. John Carmack, the programming prodigy whose work had already pushed the boundaries of computer graphics, was deep in the creation of a new engine that promised to deliver immersive 3D environments like never before. Alongside him were John Romero, the irreverent designer with a flair for action-packed gameplay, and Dave Taylor, a versatile programmer who would help bring the project to life. Their goal was ambitious: to create a game that would not only surpass their previous hit, Wolfenstein 3D, but redefine what was possible in interactive entertainment. They called it DOOM.\n\nThe computing world of 1993 was a landscape of constraints and ingenuity. Personal computers like the IBM-compatible 386 and 486 were becoming more common, but they were far from powerful by today’s standards. With processors running at mere tens of megahertz and memory often capped at 4 or 8 megabytes, developers had to squeeze every ounce of performance from the hardware. Graphics cards were rudimentary, and the VGA standard offered only 256 colors. Yet Carmack’s new engine exploited these limitations with brilliance, using techniques like binary space partitioning (BSP) to render complex 3D environments efficiently and fixed-point arithmetic to ensure smooth gameplay even on modest systems.\n\nThe team at id Software was a mix of technical virtuosity and creative audacity. Carmack, the architect of the engine, famously prioritized technical excellence over narrative, stripping away much of the story initially envisioned by designer Tom Hall. Romero, on the other hand, infused the game with a visceral, fast-paced design that emphasized action and player empowerment. Sandy Petersen joined the team to craft levels that blended horror and challenge, while Taylor contributed to the engine’s networking capabilities, enabling multiplayer deathmatches that would become a cultural phenomenon. Together, they made key decisions that shaped DOOM’s identity, from its shareware distribution model to its unapologetically violent and demonic aesthetic.\n\nWhen DOOM was released on December 10, 1993, it was nothing short of a sensation. Within weeks, it was installed on more computers than Microsoft Windows, and its shareware model ensured that millions of players could experience the first episode for free. The game’s groundbreaking 2.5D graphics, dynamic lighting effects, and pulse-pounding soundtrack captivated audiences, while its multiplayer mode laid the foundation for online gaming as we know it. DOOM’s success was not just commercial—it became a cultural touchstone, inspiring countless imitators and establishing the first-person shooter as a dominant genre.\n\nThe legacy of DOOM is monumental. Its source code, released in 1997, became a treasure trove for developers and hobbyists, fueling a vibrant modding community and influencing game design for decades. It sparked the rise of speedrunning, birthed online gaming communities, and even attracted controversy for its graphic violence, which ignited debates about the impact of video games on society. Today, DOOM is preserved in the Library of Congress and continues to be ported to new platforms, ensuring its place as one of the most important and enduring works in the history of interactive entertainment.", "image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Doom_%E2%80%93_Game%E2%80%99s_logo.svg/330px-Doom_%E2%80%93_Game%E2%80%99s_logo.svg.png", "image_caption": "Logo of the video game (series) Doom. (Public domain)", "title": "DOOM", @@ -1727,74 +1727,89 @@ ], "highlights": [ { - "id": "bsp-rendering-backbone", - "title": "Recursive BSP Traversal: DOOM's Rendering Backbone", - "description": "DOOM used Binary Space Partitioning (BSP) trees to efficiently render its 3D environments on limited hardware. This technique split the game world into hierarchical segments, allowing the engine to quickly determine which parts of the scene were visible and needed to be drawn. BSP traversal solved the problem of rendering complex levels on 1993 consumer PCs with minimal processing power. It became a foundational technique in game development, influencing engines like Quake and Unreal, and remains a staple in modern graphics programming.", + "id": "bsp-tree-spatial-masterstroke", + "title": "The BSP Tree: DOOM's Spatial Masterstroke", + "description": "DOOM's Binary Space Partitioning (BSP) tree revolutionized real-time rendering by organizing the game's 3D environment into a hierarchical structure. This allowed the engine to efficiently determine which parts of the map were visible to the player, dramatically reducing rendering overhead. On 1993 hardware, where processing power and memory were limited, this technique enabled DOOM to deliver fast-paced gameplay with complex environments. The BSP tree became a foundational technique in game development, influencing engines like Unreal and Quake, and remains a cornerstone in modern graphics optimization.", "links": [ { - "label": "Recursive BSP traversal for rendering", + "label": "Building the BSP Tree", + "file": "p-setup-c", + "enhancement": "load-nodes-for-bsp-tree" + }, + { + "label": "Recursive Traversal for Rendering", "file": "r-bsp-c", - "enhancement": "render-bsp-node" + "enhancement": "render-bsp-node-recursive-traversal-for-rendering" } ] }, { - "id": "visplane-overdraw-solution", - "title": "The Data Structure That Solved Overdraw", - "description": "DOOM's visplane system tackled the issue of overdraw, where multiple layers of graphics could waste rendering time. By grouping pixels with similar heights and textures into 'visplanes,' the engine minimized redundant calculations and optimized floor and ceiling rendering. This innovation allowed DOOM to maintain high frame rates despite its detailed environments. The visplane concept was a clever workaround for hardware limitations and influenced subsequent optimizations in rendering engines.", + "id": "fuzzy-rendering-spectres", + "title": "The Fuzzy Trick That Made Spectres Invisible", + "description": "DOOM's 'fuzzy rendering' effect gave Spectres their ghostly, semi-invisible appearance. Instead of using transparency, which was computationally expensive on early PCs, the engine applied a pixel-blurring effect to the Spectre's sprite, creating an eerie visual distortion. This clever hack worked within the constraints of limited hardware while enhancing the game's horror atmosphere. The technique inspired similar visual effects in later games, including stealth mechanics and cloaking devices in titles like Metal Gear Solid and Halo.", "links": [ { - "label": "Visplane data structure for efficient rendering", - "file": "r-plane-c", - "enhancement": "visplane-data-structure" + "label": "Fuzzy Rendering for Spectres", + "file": "r-draw-c", + "enhancement": "fuzzy-rendering-for-spectres" } ] }, { - "id": "dynamic-lighting-on-90s-hardware", - "title": "Dynamic Lighting on 1990s Hardware", - "description": "DOOM implemented dynamic lighting effects, such as flickering firelight and strobe lights, to enhance its atmospheric environments. These effects were calculated in real-time, adding depth and immersion to the game world while running on hardware without dedicated graphics processors. Dynamic lighting not only set a new standard for visual storytelling in games but also inspired techniques used in later titles like Quake and Half-Life.", + "id": "dynamic-lighting-doom", + "title": "Dynamic Lighting in DOOM's World", + "description": "DOOM implemented dynamic lighting effects to create atmospheric environments, such as flickering firelight, strobe lights, and glowing sectors. These effects were achieved through algorithms that adjusted light levels in real-time, adding depth and tension to the game's levels. On hardware with limited graphical capabilities, this approach provided a sense of realism and immersion without requiring advanced lighting engines. Dynamic lighting became a staple in game design, influencing titles like Half-Life and modern engines like Unity and Unreal.", "links": [ { - "label": "Dynamic lighting calculations", + "label": "Dynamic Lighting Effects", "file": "r-main-c", - "enhancement": "dynamic-lighting" + "enhancement": "dynamic-light-levels" + }, + { + "label": "Firelight Flicker Algorithm", + "file": "p-lights-c", + "enhancement": "firelight-flicker-algorithm" } ] }, { - "id": "fast-fixed-point-math", - "title": "How DOOM Multiplied Without Floating-Point", - "description": "DOOM relied on fixed-point arithmetic to perform calculations quickly on CPUs lacking floating-point units. This approach used integers to simulate decimal values, enabling fast and precise operations for rendering, physics, and gameplay mechanics. Fixed-point math was crucial for achieving DOOM's smooth performance on early PCs and influenced the design of many subsequent game engines, particularly those targeting constrained hardware.", + "id": "wad-files-modding-revolution", + "title": "The WAD Files That Redefined Modding", + "description": "DOOM's use of WAD (Where's All the Data) files made it one of the first games to support extensive modding. These files stored game assets like maps, textures, and sounds in a modular format, allowing players and developers to create custom levels and content. This design empowered a vibrant modding community and led to the creation of tools like DEU and Doom Builder. The WAD system influenced future games, including Quake and Skyrim, and laid the groundwork for the modern modding ecosystem.", "links": [ { - "label": "Fixed-point multiplication optimization", - "file": "m-fixed-c", - "enhancement": "fixed-multiplication-optimization" + "label": "WAD File Support and Modding", + "file": "d-main-c", + "enhancement": "wad-file-support-and-modding" + }, + { + "label": "Dynamic WAD File Addition", + "file": "w-wad-c", + "enhancement": "wad-file-addition" } ] }, { - "id": "cheat-code-iddqd", - "title": "The Code That Made 'IDDQD' Legendary", - "description": "DOOM's cheat code system, including the iconic 'IDDQD' for invincibility, was implemented with a scrambled input mapping table to obscure the sequences. This design allowed players to discover cheats organically while ensuring they were not easily guessed. The cheat system became a cultural phenomenon, influencing how secrets and Easter eggs were integrated into games, and remains a nostalgic hallmark of gaming history.", + "id": "bfg-explosion-simulation", + "title": "The Code Behind DOOM's Iconic BFG Spray", + "description": "The BFG 9000's unique explosion mechanic made it one of DOOM's most memorable weapons. When fired, the BFG released a massive projectile that exploded into a spray of invisible rays, damaging enemies in its path. This innovative approach simulated area-of-effect damage without requiring complex collision calculations, optimizing performance on limited hardware. The BFG's design became a benchmark for over-the-top weaponry in games, influencing titles like Duke Nukem 3D and Borderlands.", "links": [ { - "label": "Cheat code validation logic", - "file": "st-stuff-c", - "enhancement": "cheat-code-responder" + "label": "BFG Explosion Simulation", + "file": "p-pspr-c", + "enhancement": "bfg-explosion-simulation" } ] }, { - "id": "modding-wad-file-hack", - "title": "The Hack That Made Modding Easy", - "description": "DOOM's use of WAD files for storing game assets revolutionized modding. By separating game data from the engine, players and developers could create custom levels, textures, and sounds without altering the core code. This modular approach not only fostered a thriving modding community but also laid the groundwork for user-generated content in games like Half-Life and Minecraft.", + "id": "recursive-sound-propagation", + "title": "How DOOM's Monsters Hear You Through Walls", + "description": "DOOM's sound propagation system allowed monsters to detect players through walls by simulating sound waves traveling across the map. This recursive algorithm checked spatial relationships between sectors, triggering enemy awareness and creating dynamic gameplay moments. On hardware with no advanced AI capabilities, this system added depth to enemy behavior and tension to encounters. The concept of sound-based AI influenced later games like Thief and The Last of Us, where audio cues play a critical role in gameplay.", "links": [ { - "label": "WAD file handling and modding support", - "file": "d-main-c", - "enhancement": "wad-file-handling" + "label": "Recursive Sound Propagation", + "file": "p-enemy-c", + "enhancement": "recursive-sound-propagation" } ] } diff --git a/public/programs/doom/am-map-c.md b/public/programs/doom/am-map-c.md index c3d69a8..2c78a08 100644 --- a/public/programs/doom/am-map-c.md +++ b/public/programs/doom/am-map-c.md @@ -9,130 +9,138 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "am-map-c" order: 19 -description: "This file implements the automap functionality in DOOM, allowing players to visualize the game world and navigate more effectively. The automap was a key feature in enhancing gameplay and spatial awareness." +description: "This file contains the automap code for DOOM, a groundbreaking feature that enhanced navigation and spatial awareness in the game." summary: - point: "Defines automap colors and constants for rendering" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Implements player-following and zooming mechanics" + - point: "Introduces player arrow and cheat arrow vector graphics" link: "https://en.wikipedia.org/wiki/Automap" link_label: "Automap" - - point: "Introduces cheat codes for revealing the entire map" - link: "https://doomwiki.org/wiki/Cheats" - link_label: "DOOM Cheats" - - point: "Handles user input for panning, zooming, and marking locations" - link: "https://doomwiki.org/wiki/Automap" - link_label: "Automap Controls" - - point: "Optimized for performance on 1990s hardware" - link: "https://en.wikipedia.org/wiki/Intel_80486" - link_label: "Intel 80486" + - point: "Implements zoom and pan functionality for the automap" + link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + link_label: "Fixed-point arithmetic" + - point: "Handles user input for automap interaction" + link: "https://en.wikipedia.org/wiki/Keyboard_input" + link_label: "Keyboard input" + - point: "Manages light levels for visual effects in automap mode" + link: "https://en.wikipedia.org/wiki/Video_game_graphics" + link_label: "Video game graphics" enhancements: - - id: "automap-color-definitions" - line_start: 323 - line_end: 336 - title: "Why DOOM's Automap Colors Were Perfect" - wikipedia_url: "https://en.wikipedia.org/wiki/Color_palette" + - id: "player-arrow-vector-graphics" + line_start: 154 + line_end: 168 + title: "Why DOOM's Automap Had Arrows" + wikipedia_url: "https://en.wikipedia.org/wiki/Automap" image_url: "" image_caption: "" - content: "This section defines the color palette used in DOOM's automap, assigning specific colors to walls, objects, and other map elements. The palette was carefully chosen to ensure visibility and contrast on CRT monitors of the early 1990s, which often had limited color fidelity. The automap colors not only served functional purposes but also contributed to the game's aesthetic consistency. John Carmack and the team at id Software prioritized usability, ensuring players could quickly distinguish map features during gameplay. The approach influenced later games, where color coding became standard in automap designs, such as in Quake and Unreal." - - id: "player-arrow-definition" - line_start: 338 - line_end: 347 - title: "The Arrow That Always Points Right" - wikipedia_url: "https://en.wikipedia.org/wiki/Vector_graphics" + content: "This section defines the vector graphics for the player's arrow in the automap. The arrow consists of multiple line segments, forming a simple yet effective representation of the player's orientation. The design prioritizes clarity, ensuring players can quickly understand their position and direction. In the early 1990s, vector graphics were commonly used for such purposes due to their low computational overhead compared to raster images. John Carmack, known for his optimization prowess, likely chose this approach to maintain performance on the limited hardware of the era, such as 386 and 486 processors. The arrow's design influenced later games that adopted similar automap systems, including titles like Diablo and Baldur's Gate, which used simple graphical cues to orient players within complex environments." + - id: "cheat-arrow-vector-graphics" + line_start: 169 + line_end: 190 + title: "The Hidden Cheat Arrow in DOOM" + wikipedia_url: "https://en.wikipedia.org/wiki/Cheats_in_video_games" image_url: "" image_caption: "" - content: "Here, the player arrow is defined as a series of vector lines, representing the player's position and orientation on the automap. This design was a clever use of minimal graphics to convey critical information. The arrow's simplicity ensured it could be rendered quickly, even on slower hardware like the Intel 80486. The cheat version of the arrow adds humorous details, reflecting the playful culture at id Software. This vector-based approach influenced later games, where minimalistic representations of players became common in tactical overlays and HUDs." - - id: "automap-initialization" + content: "The cheat_player_arrow is a more elaborate version of the player arrow, featuring additional segments and shapes. This graphic is used when the player activates certain cheats, such as revealing the entire map. The inclusion of a distinct visual for cheat mode reflects id Software's attention to detail, ensuring players could differentiate between normal and cheat-enabled states. Cheat codes were a staple of 1990s gaming, often serving as a way for developers to test features or for players to explore games in unconventional ways. The cheat arrow adds a playful touch to DOOM's automap, reinforcing the game's reputation for blending serious technical achievement with fun and accessibility." + - id: "bounding-box-calculation" line_start: 384 line_end: 422 - title: "How DOOM's Automap Finds Its Bounds" + title: "How DOOM Found the Map's Edges" wikipedia_url: "https://en.wikipedia.org/wiki/Bounding_box" image_url: "" image_caption: "" - content: "This routine calculates the bounding box of all vertices in the map, setting the zoom range for the automap. By determining the minimum and maximum coordinates, the code ensures the automap can scale appropriately to fit the entire level. This was critical for DOOM's large, complex maps, which often spanned multiple screens. The bounding box approach was efficient and became a standard technique in game development, influencing map rendering in later titles like Quake and Half-Life." - - id: "automap-user-input" - line_start: 425 - line_end: 451 - title: "The Keypresses That Control DOOM's Map" - wikipedia_url: "https://en.wikipedia.org/wiki/Keyboard_layout" - image_url: "" - image_caption: "" - content: "This section handles user input for the automap, allowing players to pan, zoom, mark locations, and toggle features like gridlines. The key bindings were designed for intuitive use, leveraging common keys like arrow keys and 'TAB'. The responsiveness of these controls was a testament to id Software's focus on player experience. The ability to mark and clear points on the map was particularly innovative, giving players a way to track objectives visually. This feature influenced later games, where interactive maps became a staple of open-world and RPG genres." - - id: "zooming-mechanics" + content: "This routine calculates the bounding box of all vertices in the map, determining the minimum and maximum coordinates. These values are used to set zoom limits, ensuring the automap can scale appropriately for maps of varying sizes. Bounding box calculations were a common technique in computer graphics, used to optimize rendering and collision detection. In DOOM, this approach allowed the automap to dynamically adapt to the geometry of each level, a necessity given the game's modular map design. The technique influenced later games that required dynamic scaling or navigation systems, including Quake and Unreal, which expanded on DOOM's innovations in spatial representation." + - id: "zoom-and-scale-adjustment" line_start: 737 line_end: 753 - title: "Zooming Into the World of DOOM" - wikipedia_url: "https://en.wikipedia.org/wiki/Zoom_lens" + title: "The Math Behind DOOM's Automap Zoom" + wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" image_url: "" image_caption: "" - content: "The zooming functionality adjusts the scaling multipliers to change the automap's view size. This dynamic scaling was essential for navigating DOOM's sprawling levels, allowing players to zoom out for a broader overview or zoom in for detailed exploration. The implementation balances performance and usability, ensuring smooth transitions even on limited hardware. This technique inspired similar zooming features in later games, particularly strategy titles like StarCraft and Civilization, where map navigation is critical." - - id: "game-tick-automap-updates" + content: "The AM_changeWindowScale function adjusts the automap's zoom level by modifying scaling multipliers. It ensures the zoom stays within predefined limits and updates the map's scale accordingly. Fixed-point arithmetic is used to perform these calculations efficiently, a necessity given the hardware constraints of the early 1990s. This method allowed DOOM to provide smooth zoom transitions without the computational overhead of floating-point operations. The technique was later adopted by other games and systems requiring precise, fast calculations, such as real-time strategy games and embedded systems in robotics." + - id: "light-level-updates" + line_start: 780 + line_end: 798 + title: "Dynamic Lighting in DOOM's Automap" + wikipedia_url: "https://en.wikipedia.org/wiki/Video_game_graphics" + image_url: "" + image_caption: "" + content: "This function updates the automap's light level at regular intervals, creating a strobing effect. The light levels cycle through predefined values, adding a dynamic visual element to the automap. Such effects were rare in early 1990s games, as they required careful optimization to avoid impacting performance. DOOM's use of dynamic lighting in the automap demonstrated id Software's commitment to enhancing the player's experience, even in secondary features like navigation. This approach influenced later games that incorporated dynamic lighting for aesthetic or gameplay purposes, including Half-Life and the Halo series." + - id: "game-tick-automap-update" line_start: 801 line_end: 826 - title: "How DOOM's automap updates every tick" - wikipedia_url: "https://en.wikipedia.org/wiki/Automap" + title: "What Happens When the Automap Updates?" + wikipedia_url: "https://doomwiki.org/wiki/Automap" image_url: "" image_caption: "" - content: "This section defines the `AM_Ticker` function, responsible for updating the automap state every game tick. It handles player-following logic, zoom adjustments, and panning changes based on user input. The function also increments the `amclock` variable, which tracks the automap's active time. In 1993, real-time updates like these were constrained by hardware limitations, requiring efficient code to avoid performance degradation. The automap feature became a hallmark of DOOM, influencing later games like Quake and Unreal, which adopted similar navigational aids." - - id: "clear-automap-frame-buffer" + content: "The `AM_Ticker` function is responsible for updating the automap on each game tick. It checks if the automap is active and increments the internal clock (`amclock`). If the player is in 'follow mode,' the map adjusts to center on the player's position. It also handles zoom changes and panning based on player input. This function reflects DOOM's focus on responsiveness, ensuring the automap remains dynamic and useful during gameplay. In 1993, real-time updates like these were computationally expensive, especially on the limited hardware of the era, but DOOM's efficient design made it possible. The automap's interactivity influenced later games like Diablo and StarCraft, which incorporated dynamic maps to enhance user experience." + - id: "clear-frame-buffer" line_start: 829 - line_end: 1063 - title: "Clearing the automap's canvas with one call" - wikipedia_url: "https://en.wikipedia.org/wiki/Framebuffer" + line_end: 835 + title: "How DOOM Clears the Automap Frame Buffer" + wikipedia_url: "https://en.wikipedia.org/wiki/Frame_buffer" image_url: "" image_caption: "" - content: "The `AM_clearFB` function uses `memset` to clear the automap's frame buffer, filling it with a uniform color. This simple yet effective approach ensures the automap starts with a clean slate before rendering new elements. In the early '90s, framebuffer manipulation was a critical technique for graphics programming, as direct pixel access allowed developers to optimize rendering for limited hardware. This method influenced the design of graphical engines in subsequent games, where efficient framebuffer handling became standard practice." - - id: "cohen-sutherland-line-clipping" - line_start: 1067 - line_end: 1110 - title: "The line-clipping algorithm that saved CPU cycles" + content: "The `AM_clearFB` function resets the automap's frame buffer to a uniform color using `memset`. This ensures the map starts with a clean slate before rendering new elements. Frame buffers were a critical part of graphics programming in the early 1990s, as they stored pixel data for rendering on the screen. By directly manipulating memory, DOOM achieved fast and efficient rendering, a necessity given the hardware constraints of the time. This approach laid the groundwork for modern graphics engines, which continue to rely on optimized memory operations for performance." + - id: "cohen-sutherland-clipping" + line_start: 838 + line_end: 969 + title: "The Algorithm That Clips the Automap Lines" wikipedia_url: "https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm" image_url: "" image_caption: "" - content: "The `AM_clipMline` function implements a modified Cohen-Sutherland line-clipping algorithm to determine whether a line segment is visible within the automap's viewport. By precalculating slopes and using bitwise operations for trivial rejection, the function achieves faster performance compared to the original algorithm. This optimization was crucial for DOOM, which had to render complex levels on hardware with limited processing power. The approach influenced later graphics engines, including those in Quake and Half-Life, where efficient clipping algorithms were essential for real-time rendering." + content: "The `AM_clipMline` function implements a modified version of the Cohen-Sutherland line clipping algorithm, optimized for DOOM's automap. It determines whether a line segment is within the visible area and clips it accordingly. The algorithm uses precalculated slopes and bitwise operations for efficiency, rejecting lines that are trivially outside the viewport. In 1993, such optimizations were critical for real-time rendering on consumer PCs, which lacked dedicated GPUs. This technique influenced later games and graphics libraries, where efficient clipping remains a cornerstone of rendering pipelines." - id: "bresenham-line-drawing" - line_start: 1112 - line_end: 1237 - title: "Drawing lines pixel by pixel with Bresenham" + line_start: 973 + line_end: 1048 + title: "The Classic Line Algorithm That Powered DOOM" wikipedia_url: "https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm" image_url: "" image_caption: "" - content: "The `AM_drawFline` function uses Bresenham's line-drawing algorithm to render lines on the automap's frame buffer. This algorithm calculates the optimal path for a line between two points, minimizing computational overhead by avoiding floating-point arithmetic. DOOM's implementation includes optimizations for speed, such as precomputing increments and using inline macros for pixel placement. Bresenham's algorithm was widely adopted in computer graphics, and its use in DOOM demonstrated how classic techniques could be adapted for real-time applications, influencing later game engines like Unreal Engine." - - id: "automap-grid-rendering" - line_start: 1239 - line_end: 1281 - title: "Rendering a grid aligned to game geometry" + content: "The `AM_drawFline` function uses Bresenham's line-drawing algorithm to render lines on the automap. This algorithm calculates pixel positions to represent a straight line between two points, minimizing computational overhead by avoiding floating-point arithmetic. DOOM's implementation includes optimizations for speed, reflecting the game's need to render complex environments on limited hardware. Bresenham's algorithm, first developed in the 1960s, remains a fundamental technique in computer graphics, influencing everything from early video games to modern rasterization methods." + - id: "draw-gridlines-automap" + line_start: 1067 + line_end: 1110 + title: "How DOOM Draws Its Automap Grid" wikipedia_url: "https://doomwiki.org/wiki/Automap" image_url: "" image_caption: "" - content: "The `AM_drawGrid` function draws a grid on the automap, aligning it with the game's floor and ceiling tiles. By calculating start and end points based on map block units, the function ensures the grid matches the underlying level geometry. This feature helped players navigate DOOM's complex levels, especially in larger maps. The automap's grid system influenced later games, such as Diablo and StarCraft, where grid-based overlays became essential for tactical gameplay and level design visualization." - - id: "player-arrow-rotation" - line_start: 1304 - line_end: 1323 - title: "Rotating the player arrow with lookup tables" - wikipedia_url: "https://en.wikipedia.org/wiki/Lookup_table" + content: "The `AM_drawGrid` function renders a grid on the automap, aligning lines with the game's floor and ceiling tiles. It calculates the start and end points for vertical and horizontal gridlines based on the player's position and the map's dimensions. This grid helps players orient themselves within the game's complex levels. In the early 1990s, such visual aids were rare in games, but DOOM's automap set a precedent for intuitive navigation systems. Modern games like Minecraft and Civilization continue to use grid-based overlays for similar purposes." + - id: "draw-walls-automap" + line_start: 1112 + line_end: 1237 + title: "Rendering Walls on DOOM's Automap" + wikipedia_url: "https://doomwiki.org/wiki/Automap" image_url: "" image_caption: "" - content: "The `AM_rotate` function rotates the player arrow on the automap using trigonometric lookup tables. By leveraging precomputed sine and cosine values, the function avoids costly runtime calculations, ensuring smooth rotation even on limited hardware. This technique was common in early 3D games, where performance constraints demanded innovative solutions. The use of lookup tables for rotation influenced later games and engines, including Quake and Unreal, where similar methods were employed for efficient transformations." - - id: "multiplayer-player-visibility" + content: "The `AM_drawWalls` function iterates over the game's LineDefs to render visible walls on the automap. It checks flags for secrets, teleporters, and level geometry changes, applying different colors to indicate their properties. This visual differentiation helps players identify key features and navigate efficiently. The automap's ability to convey such detailed information was groundbreaking in 1993, influencing later games like Quake and Unreal, which expanded on the concept of interactive maps." + - id: "draw-players-automap" + line_start: 1239 + line_end: 1281 + title: "Multiplayer Positions on DOOM's Automap" + wikipedia_url: "https://en.wikipedia.org/wiki/Multiplayer_video_game" + image_url: "" + image_caption: "" + content: "The `AM_drawPlayers` function renders player positions on the automap, using different colors to distinguish between them. In single-player mode, it draws an arrow representing the player's orientation. In multiplayer mode, it handles multiple players, reflecting DOOM's innovative support for networked gameplay. This feature helped establish DOOM as a pioneer in multiplayer gaming, influencing titles like Counter-Strike and Halo, which built on its foundation." + - id: "draw-crosshair-automap" line_start: 1325 - line_end: 1325 - title: "Color-coded players in multiplayer automap" - wikipedia_url: "https://doomwiki.org/wiki/Multiplayer" + line_end: 1329 + title: "The Single Pixel That Guides You" + wikipedia_url: "https://doomwiki.org/wiki/Automap" image_url: "" image_caption: "" - content: "The `AM_drawPlayers` function renders player arrows on the automap, assigning colors based on player identity and visibility status. In multiplayer mode, players are color-coded for easy identification, with special handling for invisibility power-ups. This feature enhanced DOOM's multiplayer experience, making it easier for players to track allies and opponents. The concept of color-coded player indicators became a standard in multiplayer games, influencing titles like Team Fortress and Overwatch." - - id: "automap-drawer" + content: "The `AM_drawCrosshair` function places a single pixel at the center of the automap, serving as a crosshair. While simple, this feature underscores DOOM's attention to detail, providing players with a clear reference point for navigation. Such minimalistic design choices contributed to the game's usability and influenced later UI designs in gaming." + - id: "automap-rendering-loop" line_start: 1331 line_end: 1348 - title: "The master function behind DOOM's automap" + title: "The Master Function That Draws It All" wikipedia_url: "https://doomwiki.org/wiki/Automap" image_url: "" image_caption: "" - content: "The `AM_Drawer` function orchestrates the rendering of the automap, calling subroutines to clear the frame buffer, draw the grid, walls, players, and other elements. It also handles special cases, such as cheating modes and crosshair rendering. This modular approach allowed DOOM's developers to maintain and extend the automap system efficiently. The automap's design inspired similar features in later games, such as the minimaps in Grand Theft Auto and The Legend of Zelda: Breath of the Wild." + content: "The `AM_Drawer` function orchestrates the rendering of the automap, calling subroutines to clear the frame buffer, draw the grid, walls, players, and marks, and update the crosshair. It represents the culmination of DOOM's automap system, integrating various components into a cohesive whole. This modular approach to rendering influenced the design of later game engines, including id Tech and Unity, which rely on similar systems to manage complex rendering tasks." --- @@ -1485,4 +1493,4 @@ void AM_Drawer (void) V_MarkRect(f_x, f_y, f_w, f_h); } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/d-main-c.md b/public/programs/doom/d-main-c.md index e05a644..8733987 100644 --- a/public/programs/doom/d-main-c.md +++ b/public/programs/doom/d-main-c.md @@ -9,114 +9,98 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "d-main-c" order: 1 -description: "Central file for initializing and managing DOOM's gameplay loop, input handling, and demo sequences." +description: "Central file for DOOM's engine initialization and gameplay management, showcasing techniques that pushed 1993 hardware to its limits." summary: - - point: "Introduces the D_DoomLoop function, the heart of DOOM's game engine." - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM (1993)" - - point: "Implements asynchronous event handling for user inputs." + - point: "Event handling system for asynchronous user inputs" link: "https://en.wikipedia.org/wiki/Event-driven_programming" link_label: "Event-driven programming" - - point: "Manages demo sequences and game mode identification." - link: "https://doomwiki.org/wiki/Game_mode" - link_label: "Game mode" - - point: "Includes code for handling WAD file loading and version detection." + - point: "Dynamic display updates with screen wipes and buffered rendering" + link: "https://en.wikipedia.org/wiki/Double_buffering" + link_label: "Double buffering" + - point: "Demo sequence management for seamless transitions" + link: "https://doomwiki.org/wiki/Demo" + link_label: "DOOM demo system" + - point: "Version identification based on WAD file availability" link: "https://doomwiki.org/wiki/WAD" - link_label: "WAD files" - - point: "Demonstrates Carmack's optimization techniques for real-time rendering and input processing." - link: "https://doomwiki.org/wiki/John_Carmack" - link_label: "John Carmack" + link_label: "WAD file format" + - point: "Response file parsing for extended command-line arguments" + link: "https://en.wikipedia.org/wiki/Command-line_interface" + link_label: "Command-line interface" enhancements: - id: "event-handling-with-d-postevent" line_start: 145 - line_end: 153 + line_end: 152 title: "Event Handling with D_PostEvent" wikipedia_url: "https://en.wikipedia.org/wiki/Event-driven_programming" image_url: "" image_caption: "" - content: "The D_PostEvent function is responsible for queuing user input events into a circular buffer. Events, such as key presses or mouse movements, are stored in the 'events' array, with the 'eventhead' index incrementing in a wraparound manner using a bitwise AND operation. This design ensures efficient memory usage and avoids buffer overflow, a critical consideration given the limited RAM of early 1990s PCs. In 1993, event-driven programming was a relatively novel concept in game development. DOOM's implementation of asynchronous event handling allowed the game to respond to user inputs without blocking other operations, such as rendering or sound processing. This approach was influenced by techniques used in operating systems and real-time applications, adapted here for a fast-paced action game. The circular buffer design became a standard in game development, influencing later engines like Quake and Unreal. It demonstrated how to balance responsiveness with performance constraints, paving the way for modern input systems in games. Developers studying DOOM's source code often cite this function as an elegant solution to input handling under tight resource constraints." - - id: "processing-inputs-d-processevents" + content: "The `D_PostEvent` function is responsible for queuing user input events into a circular buffer. These events include key presses, mouse movements, and other asynchronous inputs from the player. The function ensures that the game can process inputs efficiently without losing any data due to buffer overflow, using a wrap-around mechanism (`MAXEVENTS-1`). In 1993, this approach was critical for maintaining responsive gameplay on hardware with limited processing power. The circular buffer design was a common technique in real-time systems, allowing DOOM to handle multiple inputs while avoiding latency. This event-driven architecture influenced later game engines, including Quake, which expanded on the idea with more sophisticated input handling mechanisms." + - id: "processing-inputs-with-d-processevents" line_start: 156 line_end: 176 title: "Processing Inputs with D_ProcessEvents" - wikipedia_url: "https://en.wikipedia.org/wiki/Input/output" + wikipedia_url: "https://en.wikipedia.org/wiki/Event-driven_programming" image_url: "" image_caption: "" - content: "D_ProcessEvents iterates through the event buffer, passing each event to responder functions like M_Responder (menu handling) and G_Responder (gameplay input). If the menu consumes an event, it skips further processing. This modular approach separates input handling for different game states, ensuring that menus and gameplay can coexist seamlessly. In the early 1990s, games often struggled to manage simultaneous user inputs effectively. DOOM's approach, where events are processed in a loop and dispatched to state-specific handlers, was groundbreaking. It allowed the game to maintain high responsiveness even during intense action sequences. This technique influenced later game engines, including id Tech 2 and id Tech 3, which refined the concept further. The modularity seen here is echoed in modern engines like Unity and Unreal, where input handling is often state-dependent and event-driven. DOOM's code serves as a foundational example of how to architect input systems for real-time applications." - - id: "real-time-rendering-d-display" + content: "The `D_ProcessEvents` function processes all queued events by passing them through responder functions like `M_Responder` (menu handling) and `G_Responder` (game logic). This modular approach allows different parts of the game to handle inputs independently, a design that was ahead of its time. The function also includes a conditional check to ignore inputs during demo recording, ensuring the integrity of pre-recorded sequences. This separation of concerns—handling events, processing logic, and rendering—became a hallmark of modern game engines. The modularity seen here influenced later engines like Unreal Engine, which adopted similar principles for input handling and event propagation." + - id: "dynamic-display-updates-d-display" line_start: 192 line_end: 344 - title: "Real-Time Rendering in D_Display" - wikipedia_url: "https://en.wikipedia.org/wiki/Rendering_(computer_graphics)" + title: "Dynamic Display Updates in D_Display" + wikipedia_url: "https://en.wikipedia.org/wiki/Double_buffering" image_url: "" image_caption: "" - content: "The D_Display function orchestrates the game's rendering pipeline, handling screen wipes, HUD updates, and frame-by-frame drawing. It integrates various subsystems, such as the automap (AM_Drawer), status bar (ST_Drawer), and player view rendering (R_RenderPlayerView). The function also manages transitions between game states, ensuring smooth visual updates. In 1993, real-time rendering on consumer-grade hardware was a significant challenge. DOOM's ability to deliver fluid visuals on machines like the 386 and 486 was a testament to John Carmack's mastery of optimization. Techniques like screen wipes were not just aesthetic but also functional, helping to mask loading times and state changes. The rendering pipeline established here influenced countless games and engines. The concept of modular rendering subsystems, each responsible for a specific aspect of the visual output, became a standard practice. Modern engines like Unity and Unreal continue to use similar architectures, with rendering systems divided into layers for efficiency and maintainability. DOOM's rendering code remains a touchstone for developers studying optimization and real-time graphics." + content: "The `D_Display` function manages screen updates, including transitions between game states and rendering the player's view. It incorporates techniques like screen wipes (`wipe_StartScreen`) and buffered drawing (`I_UpdateNoBlit`) to ensure smooth visual transitions. These methods were essential for creating an immersive experience on hardware with limited graphical capabilities. The use of double buffering minimized flickering and tearing, a common issue in early PC games. This function also handles special cases like drawing the pause screen and updating the border when switching between fullscreen and windowed modes. The visual polish achieved here set a new standard for game graphics, influencing titles like Duke Nukem 3D and later 3D engines that prioritized seamless rendering." - id: "game-loop-d-doomloop" line_start: 353 line_end: 406 - title: "The Infinite Game Loop: D_DoomLoop" + title: "The Endless Game Loop in D_DoomLoop" wikipedia_url: "https://en.wikipedia.org/wiki/Game_engine" image_url: "" image_caption: "" - content: "D_DoomLoop is the core of DOOM's engine, running indefinitely to manage gameplay, input, and rendering. It synchronizes operations like sound mixing (I_UpdateSound), event processing (D_ProcessEvents), and frame updates (D_Display). The loop also adapts to single-tic or multi-tic modes, ensuring smooth performance across different hardware configurations. The concept of an infinite game loop was not new in 1993, but DOOM's implementation was particularly efficient. It balanced CPU usage with responsiveness, leveraging techniques like frame synchronization and adaptive tic processing. This design allowed DOOM to run on a wide range of hardware, from high-end PCs to modest setups. The infinite game loop became a cornerstone of game engine design, influencing engines like id Tech, Unreal Engine, and Source. It demonstrated how to structure a real-time application for maximum efficiency and scalability. Developers studying DOOM's source code often cite D_DoomLoop as a masterclass in game engine architecture." + content: "The `D_DoomLoop` function is the heart of DOOM's runtime, executing an infinite loop that drives the game. It synchronizes input processing (`I_StartFrame`), game logic updates (`G_Ticker`), and rendering (`D_Display`). This loop ensures that the game remains responsive and visually consistent, even under heavy computational loads. The function also includes debugging features, such as writing to a debug file, which were invaluable during development. The concept of a main game loop became a cornerstone of game engine design, influencing virtually every engine that followed, including Unity and Godot. DOOM's implementation demonstrated how to balance performance and complexity, a lesson that remains relevant in modern game development." - id: "demo-management-d-doadvancedemo" line_start: 449 line_end: 517 - title: "Managing Demo Sequences with D_DoAdvanceDemo" + title: "Seamless Demo Management in D_DoAdvanceDemo" wikipedia_url: "https://doomwiki.org/wiki/Demo" image_url: "" image_caption: "" - content: "D_DoAdvanceDemo cycles through DOOM's demo sequences, transitioning between gameplay demos and static screens like the title or credits. It adjusts game state variables, starts appropriate music tracks, and handles special cases for different game modes (e.g., retail vs. commercial). Demo sequences were a popular feature in early 1990s games, serving as both promotional tools and technical showcases. DOOM's implementation was particularly polished, with seamless transitions and synchronized audio. This attention to detail reflected id Software's commitment to creating an immersive experience. The demo system influenced later games, which adopted similar techniques for attract modes and gameplay previews. It also inspired the development of tools for recording and replaying gameplay, a feature now standard in competitive gaming and streaming platforms. DOOM's demo code remains a valuable resource for developers exploring automated gameplay systems." - - id: "wad-file-handling-identifyversion" + content: "The `D_DoAdvanceDemo` function cycles through demo sequences, transitioning between gameplay demos and title screens. It adjusts game state variables like `gamestate` and `demosequence` to ensure smooth transitions. This feature was crucial for showcasing DOOM's capabilities in shareware versions, enticing players to purchase the full game. The function also handles music changes (`S_StartMusic`) and screen updates (`D_PageDrawer`), creating a polished presentation. Demo systems like this became standard in game development, serving as both marketing tools and technical showcases. DOOM's demo management influenced later games, including Quake and Half-Life, which used similar systems to highlight their features." + - id: "wad-file-detection-identifyversion" line_start: 556 line_end: 716 - title: "WAD File Handling in IdentifyVersion" + title: "WAD File Detection in IdentifyVersion" wikipedia_url: "https://doomwiki.org/wiki/WAD" image_url: "" image_caption: "" - content: "IdentifyVersion determines the game's mode (shareware, registered, retail, or commercial) by checking the availability of specific WAD files. It uses platform-specific file access methods and environment variables like DOOMWADDIR to locate these files. The function also includes debugging notes, such as a malloc bug fix attributed to Shawn Green. WAD files were a revolutionary concept in game development, allowing levels, textures, and other assets to be packaged separately from the executable. This modularity enabled easy distribution of mods and expansions, fostering a vibrant community of creators. DOOM's ability to detect and load WAD files dynamically was a key factor in its longevity. The modular asset system pioneered here influenced countless games and engines. Modern engines like Unity and Unreal use similar concepts, with asset bundles and resource managers handling dynamic loading. DOOM's WAD system remains a touchstone for developers exploring extensibility and modding support." - - id: "response-file-handling-findresponsefile" + content: "The `IdentifyVersion` function determines the game's mode (shareware, registered, retail, or commercial) based on the availability of specific WAD files. It uses file access checks (`access`) to locate these files and sets global variables accordingly. This mechanism allowed DOOM to adapt its features dynamically, depending on the user's version. The function also includes humorous comments, such as \"C'est ridicule!\" when handling the French version, reflecting the developers' personality. This approach to version management influenced later games that needed to support multiple editions or expansions, such as StarCraft and The Sims. The modularity of WAD files also laid the groundwork for user-generated content and modding communities." + - id: "response-file-parsing-findresponsefile" line_start: 718 line_end: 789 - title: "Handling Response Files with FindResponseFile" + title: "Response File Parsing in FindResponseFile" wikipedia_url: "https://en.wikipedia.org/wiki/Command-line_interface" image_url: "" image_caption: "" - content: "FindResponseFile processes command-line arguments to locate and parse response files, which contain additional arguments for the game. It reads the file into memory, appends its contents to the argument list, and displays the updated arguments for debugging purposes. This feature allows users to specify complex configurations without typing lengthy command lines. In the early 1990s, command-line interfaces were a common way to configure software. Response files provided a convenient way to manage complex setups, especially for developers and advanced users. DOOM's implementation reflects id Software's focus on flexibility and user empowerment. Response files influenced later tools and engines, which adopted similar mechanisms for batch processing and configuration. They remain relevant in modern development workflows, where scripts and configuration files are used to automate tasks. DOOM's code serves as a historical example of how to balance usability with technical sophistication." - - id: "command-line-options-flexibility" - line_start: 807 - line_end: 934 - title: "How Command-Line Arguments Shaped Gameplay" + content: "The `FindResponseFile` function parses response files, which are text files containing additional command-line arguments. This feature allowed users to specify complex configurations without typing lengthy commands manually. The function reads the file into memory, splits it into individual arguments, and appends them to the existing command-line arguments. This capability was particularly useful for debugging and customizing gameplay. Response files were an innovative solution for managing command-line options, influencing later tools and engines that supported similar features. For example, Unreal Engine and Source Engine adopted comparable mechanisms for handling configuration files and startup parameters." + - id: "command-line-parameters-flexibility" + line_start: 792 + line_end: 1139 + title: "Command-Line Arguments, WAD Loading, and Demo Recording" wikipedia_url: "https://en.wikipedia.org/wiki/Command-line_interface" image_url: "" image_caption: "" - content: "This section of the code processes command-line arguments to customize gameplay. Options like '-nomonsters', '-respawn', and '-fast' allow players to modify the game's behavior, while '-deathmatch' sets up multiplayer modes. The '-turbo' option adjusts movement speed, demonstrating how DOOM catered to both casual players and advanced users. In the early 1990s, command-line interfaces were a common way to configure software, especially on DOS-based systems. John Carmack's design philosophy emphasized user control and flexibility, which was rare for games at the time. This approach influenced later games, inspiring developers to include similar customization options. Today, command-line arguments remain a staple in software development, especially in debugging and server applications." - - id: "wad-file-handling" - line_start: 937 - line_end: 945 - title: "The Hack That Made Modding Easy" - wikipedia_url: "https://en.wikipedia.org/wiki/WAD_(file_format)" - image_url: "" - image_caption: "" - content: "DOOM's support for custom WAD files revolutionized gaming by enabling user-generated content. This section adds WAD files specified via the '-file' command-line argument to the game's resource list, marking the game as 'modified.' The code even includes a hack to allow '-wart' commands to load specific maps. In the 1990s, modding was in its infancy, and DOOM's modular file structure made it a pioneer. Players could create and share custom levels, fostering a vibrant community. This openness inspired later games like Quake and Half-Life, which built on DOOM's modding legacy. Today, modding is a cornerstone of PC gaming, with tools and platforms like Steam Workshop making it accessible to millions." - - id: "subsystem-initialization" - line_start: 1009 - line_end: 1112 - title: "Why Modular Engines Win Every Time" - wikipedia_url: "https://en.wikipedia.org/wiki/Modular_programming" - image_url: "" - image_caption: "" - content: "This section initializes DOOM's subsystems, including memory management (Z_Init), graphics (V_Init), sound (S_Init), and gameplay state (P_Init). Each subsystem is modular, allowing the engine to be maintainable and adaptable. In the early 1990s, modular programming was gaining traction as developers sought ways to manage increasingly complex software. John Carmack's focus on clean, modular design ensured DOOM's engine could be extended and optimized over time. This modularity influenced later engines like Quake and Unreal, which adopted similar principles. Today, modularity is a fundamental concept in software engineering, underpinning frameworks like Unity and Unreal Engine." - - id: "game-start-logic" - line_start: 1125 - line_end: 1139 - title: "How DOOM Decides What to Do First" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + content: "The D_DoomMain startup routine processes a wide range of command-line arguments that shaped how DOOM was played and modified. Gameplay flags like -nomonsters, -turbo, and -deathmatch let players and server operators tailor sessions without touching the source code, an unusually flexible design for a 1993 retail game. The -file parameter extended this openness to content: it dynamically loaded custom WAD files at startup, which gave rise to the sprawling DOOM modding community and landmark total conversions like Aliens TC and Batman DOOM. Equally influential was the demo system, where -record and -playdemo captured and replayed complete sessions as compact input logs rather than video, making competitive runs easy to share and analyze on the dial-up networks of the era. Together these mechanisms established DOOM as a platform as much as a game, and the pattern of scriptable startup arguments with swappable data files became a template followed by Quake, Half-Life, and virtually every moddable engine that came after." + - id: "intro-loop-and-title-screen" + line_start: 1160 + line_end: 1169 + title: "The Loop That Hooked Millions" + wikipedia_url: "https://en.wikipedia.org/wiki/Video_game_user_interface" image_url: "" image_caption: "" - content: "This section determines how DOOM starts based on command-line parameters, such as recording a demo, playing a demo, loading a saved game, or starting a new game. The logic ensures that the game adapts to user preferences while maintaining a seamless experience. In 1993, this level of flexibility was rare, showcasing id Software's commitment to player agency. Carmack's efficient coding and Romero's focus on user experience combined to create a system that felt intuitive and responsive. This approach influenced later games, inspiring developers to prioritize customization and accessibility. Today, such flexibility is standard in gaming, with options for mods, demos, and save states appearing in countless titles." + content: "DOOM's intro loop and title screen are initialized here, providing a polished entry point for players. The loop includes animations, music, and demo playback, creating an engaging first impression. In 1993, many games lacked such attention to user experience, often dumping players directly into gameplay. DOOM's cinematic approach set a new standard, influencing titles like Diablo and Half-Life, which prioritized immersive menus and introductory sequences. This design choice helped establish DOOM as not just a game but a complete experience, captivating players from the moment they launched it." --- @@ -1291,4 +1275,4 @@ void D_DoomMain (void) D_DoomLoop (); // never returns } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/d-net-c.md b/public/programs/doom/d-net-c.md index dbd1c5c..2a0aaa5 100644 --- a/public/programs/doom/d-net-c.md +++ b/public/programs/doom/d-net-c.md @@ -9,84 +9,82 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "d-net-c" order: 20 -description: "This file implements DOOM's networking protocol, enabling multiplayer gameplay on early PCs." +description: "This file implements DOOM's networking logic, enabling multiplayer gameplay over modest hardware." summary: - - point: "DOOM's networking protocol was designed to work across multiple operating systems." + - point: "DOOM's multiplayer networking was designed to work on 1993-era PCs with limited resources." link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM (1993)" - - point: "The code includes mechanisms for packet retransmission and synchronization." - link: "https://en.wikipedia.org/wiki/Computer_networking" - link_label: "Computer Networking" - - point: "DOOM's multiplayer innovations influenced future online gaming protocols." + link_label: "DOOM" + - point: "The file contains routines for packet handling, synchronization, and tic-based gameplay updates." link: "https://en.wikipedia.org/wiki/Multiplayer_video_game" - link_label: "Multiplayer Video Game" + link_label: "Multiplayer video games" + - point: "Checksum and tic expansion techniques ensured reliable communication despite hardware constraints." + link: "https://en.wikipedia.org/wiki/Checksum" + link_label: "Checksum" + - point: "DOOM's network protocol influenced later multiplayer games and engines like Quake." + link: "https://en.wikipedia.org/wiki/Quake_(video_game)" + link_label: "Quake" + - point: "The source code release allowed developers to study and adapt its networking techniques." + link: "https://en.wikipedia.org/wiki/Source_code" + link_label: "Source code" enhancements: - - id: "networking-data-structures" + - id: "netbuffer-size-calculation" line_start: 86 - line_end: 86 - title: "The Data Structures That Made Multiplayer Possible" - wikipedia_url: "https://en.wikipedia.org/wiki/Data_structure" + line_end: 92 + title: "How DOOM Calculated Packet Sizes" + wikipedia_url: "https://en.wikipedia.org/wiki/Packet_(computing)" image_url: "" image_caption: "" - content: "This section defines key data structures used for DOOM's networking functionality, including `doomcom_t` and `doomdata_t`. These structures store information about the state of the network, such as player commands (`ticcmd_t`), game ticks, and node statuses. The `nettics` array tracks the progress of each player, ensuring synchronization across nodes. Multiplayer gaming in 1993 was still in its infancy, and DOOM's approach to managing state and communication was groundbreaking. John Carmack's focus on efficiency and simplicity allowed the game to run smoothly even on modest hardware. These data structures laid the groundwork for future multiplayer protocols, influencing games like Quake and Unreal Tournament." - - id: "netbuffer-checksum" + content: "The `NetbufferSize` function calculates the size of the network buffer by determining the offset of the `cmds` array within the `doomdata_t` structure. This approach relies on pointer arithmetic and assumes a specific memory layout. At the time, memory efficiency was critical, as DOOM needed to run on hardware with limited RAM and processing power. By directly calculating offsets, the developers avoided the overhead of dynamic memory management. This function reflects the low-level programming practices of the era, where developers often manipulated memory directly to optimize performance. The technique was widely studied after DOOM's source code release, influencing how later games handled network buffers in resource-constrained environments." + - id: "checksum-validation" line_start: 94 line_end: 114 - title: "How DOOM Verified Multiplayer Packets" + title: "The Checksum That Kept Multiplayer Honest" wikipedia_url: "https://en.wikipedia.org/wiki/Checksum" image_url: "" image_caption: "" - content: "The `NetbufferChecksum` function calculates a checksum to verify the integrity of network packets. This ensures that data transmitted between players is accurate and uncorrupted. The checksum algorithm uses a combination of multiplication and addition, incorporating the packet's contents and position. At the time, network reliability was a significant concern, especially on consumer-grade hardware. By implementing checksums, DOOM reduced the risk of errors disrupting gameplay. This technique became a standard practice in networking, influencing protocols used in later games and even broader applications like TCP/IP." - - id: "expand-tics" + content: "The `NetbufferChecksum` function generates a checksum for the network buffer to ensure data integrity during transmission. It uses a simple algorithm that multiplies buffer values by their indices and sums the results, masking the final value with a predefined constant. This lightweight approach was chosen to minimize computational overhead on the 386 and 486 processors common in 1993. The developers even included a conditional to disable checksums on UNIX systems due to endian issues, highlighting the challenges of cross-platform compatibility. This checksum mechanism was crucial for maintaining reliable multiplayer gameplay, preventing corrupted packets from disrupting the experience. The concept of using checksums in network protocols became standard practice, influencing later multiplayer engines like Quake and Unreal." + - id: "expand-tics-algorithm" line_start: 116 line_end: 185 - title: "Solving the Tic Synchronization Problem" - wikipedia_url: "https://en.wikipedia.org/wiki/Clock_synchronization" + title: "Expanding Tic Numbers for Multiplayer Sync" + wikipedia_url: "https://en.wikipedia.org/wiki/Multiplayer_video_game" image_url: "" image_caption: "" - content: "The `ExpandTics` function resolves synchronization issues by reconstructing full tic numbers from their lower byte. In DOOM's multiplayer protocol, only the lower byte of tic numbers is transmitted to save bandwidth. This function uses the game's current tic to infer the missing higher bytes, ensuring consistency across nodes. Synchronization was critical for maintaining the fast-paced gameplay DOOM was known for. This clever optimization reflects Carmack's ability to balance performance with functionality, and similar techniques are still used in modern game engines to handle synchronization efficiently." - - id: "hsendpacket-function" + content: "The `ExpandTics` function reconstructs full tic numbers from their low-byte representations, ensuring synchronization between players. In DOOM's network protocol, only the least significant byte of tic numbers was transmitted to save bandwidth. This function uses the current tic (`maketic`) and calculates the full tic number by accounting for potential wraparounds. The algorithm was designed to handle edge cases, such as negative deltas, and raises an error if an unexpected value is encountered. This technique reflects the constraints of early networking, where minimizing packet size was essential for performance. The approach influenced later games that used tic-based synchronization, demonstrating how clever algorithms could overcome hardware limitations." + - id: "hgetpacket-rebound-mechanism" line_start: 187 line_end: 252 - title: "How DOOM Sent Multiplayer Packets" - wikipedia_url: "https://en.wikipedia.org/wiki/Packet-switched_network" + title: "Rebounding Packets for Local Testing" + wikipedia_url: "https://en.wikipedia.org/wiki/Packet_(computing)" image_url: "" image_caption: "" - content: "The `HSendPacket` function handles the transmission of network packets to other nodes. It calculates the packet's checksum, sets flags, and sends the data through the network interface. If the packet is intended for the local node, it stores it in a rebound buffer for immediate processing. This function highlights DOOM's approach to multiplayer networking, where reliability and efficiency were paramount. By incorporating debugging capabilities, the developers could monitor and troubleshoot network issues during development. This method of packet handling influenced future multiplayer games, setting a precedent for robust network communication." - - id: "getpackets-function" + content: "The `HGetPacket` function handles incoming network packets and includes a mechanism for 'rebounding' packets locally. If the packet is intended for the local node, it is stored in a rebound buffer and marked as received without transmitting it over the network. This feature was likely added to simplify testing and debugging during development, allowing the developers to simulate network conditions without requiring multiple machines. The function also validates packet checksums and lengths, ensuring data integrity. This rebound mechanism highlights the practical considerations of game development, where debugging tools and shortcuts are essential for meeting tight deadlines. The concept of local packet handling influenced debugging practices in later multiplayer engines." + - id: "getpackets-error-handling" line_start: 260 line_end: 357 - title: "Receiving and Processing Multiplayer Packets" - wikipedia_url: "https://en.wikipedia.org/wiki/Network_packet" + title: "Error Handling in Multiplayer Packet Processing" + wikipedia_url: "https://en.wikipedia.org/wiki/Error_detection_and_correction" image_url: "" image_caption: "" - content: "The `GetPackets` function processes incoming network packets, updating the game's state based on their contents. It handles setup packets, retransmission requests, and game exit notifications. The function also checks for packet integrity using checksums and ensures packets are processed in the correct order. This robust packet handling mechanism was essential for DOOM's multiplayer experience, allowing players to interact seamlessly despite the limitations of 1990s networking hardware. The techniques used here influenced the design of network protocols in later games, contributing to the evolution of online multiplayer gaming." - - id: "netupdate-function" + content: "The `GetPackets` function processes incoming network packets, handling various error conditions such as out-of-order packets, missed tics, and checksum mismatches. It updates the command store for each player and ensures that the game state remains consistent across nodes. The function also detects when players leave the game and broadcasts this information to others. These error-handling routines were critical for maintaining a smooth multiplayer experience, especially on unreliable network connections of the early 1990s. The robust design influenced later multiplayer engines, which adopted similar techniques to handle packet loss and synchronization issues. The function's ability to gracefully handle errors contributed to DOOM's reputation for stable multiplayer gameplay." + - id: "netupdate-tic-management" line_start: 367 line_end: 445 - title: "Keeping Multiplayer Games in Sync" - wikipedia_url: "https://en.wikipedia.org/wiki/Multiplayer_video_game" - image_url: "" - image_caption: "" - content: "The `NetUpdate` function is responsible for maintaining synchronization between players in a multiplayer game. It builds new commands for the local player, sends packets to other nodes, and listens for incoming packets. This function ensures that all players are operating on the same game state, a critical requirement for fast-paced action games like DOOM. The synchronization mechanisms developed here were innovative for their time, enabling smooth multiplayer gameplay on hardware with limited processing power. This approach influenced the design of networking systems in later games, including Quake and Half-Life." - - id: "d-arbitrate-net-start" - line_start: 472 - line_end: 546 - title: "Negotiating Multiplayer Game Start" + title: "Synchronizing Tics Across Multiplayer Nodes" wikipedia_url: "https://en.wikipedia.org/wiki/Multiplayer_video_game" image_url: "" image_caption: "" - content: "The `D_ArbitrateNetStart` function establishes the initial conditions for a multiplayer game. It negotiates settings like skill level, map, and game mode between nodes. The function ensures that all players are using the same version of the game, preventing compatibility issues. This negotiation process reflects the challenges of multiplayer gaming in the early 1990s, where hardware and software differences could easily disrupt gameplay. By automating the setup process, DOOM made multiplayer gaming more accessible, paving the way for the widespread adoption of online gaming." - - id: "try-run-tics" + content: "The `NetUpdate` function builds tic commands for the local player and sends them to other nodes, ensuring synchronization across the network. It calculates the number of new tics based on the elapsed time and updates the network buffer with the latest commands. The function also handles retransmit requests and missed packets, ensuring that all players remain in sync. This tic-based approach was a cornerstone of DOOM's multiplayer design, allowing fast-paced gameplay to remain consistent across machines with varying performance. The technique influenced later games like Quake, which refined tic-based synchronization for more complex multiplayer environments." + - id: "tryruntics-game-loop" line_start: 635 line_end: 766 - title: "Balancing Real-Time Gameplay Across Nodes" - wikipedia_url: "https://en.wikipedia.org/wiki/Real-time_computing" + title: "The Game Loop That Kept DOOM in Sync" + wikipedia_url: "https://en.wikipedia.org/wiki/Game_engine" image_url: "" image_caption: "" - content: "The `TryRunTics` function ensures that all nodes in a multiplayer game remain synchronized while running game logic. It calculates the number of tics to process based on real-time and available tics, adapting dynamically to network conditions. This function also handles duplicated tics, ensuring that commands are consistent across all players. The ability to balance real-time gameplay across nodes was a significant achievement, allowing DOOM to deliver a seamless multiplayer experience. Techniques like these influenced the development of real-time networking in later games, contributing to the rise of competitive online gaming." + content: "The `TryRunTics` function is the heart of DOOM's multiplayer game loop, determining how many tics to run based on available and real tics. It ensures that all players remain synchronized by waiting for new tics if necessary and processing incoming commands. The function also adapts to slower nodes by adjusting the game time, preventing desynchronization. This adaptive game loop was a critical innovation, allowing DOOM to deliver smooth multiplayer gameplay on hardware with varying capabilities. The technique influenced the design of later game engines, which adopted similar adaptive loops to handle multiplayer synchronization. The function's ability to balance real-time gameplay with network constraints remains a hallmark of DOOM's engineering excellence." --- @@ -857,4 +855,4 @@ void TryRunTics (void) NetUpdate (); // check for new console commands } } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/f-finale-c.md b/public/programs/doom/f-finale-c.md index 3a6afba..a77a292 100644 --- a/public/programs/doom/f-finale-c.md +++ b/public/programs/doom/f-finale-c.md @@ -9,68 +9,66 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "f-finale-c" order: 21 -description: "This file orchestrates the finale sequences in DOOM, including animations, text displays, and the iconic monster cast roll." +description: "This file handles DOOM's finale animations, text displays, and character cast sequences, showcasing the game's innovative approach to immersive storytelling and visual presentation." summary: - - point: "Implements the finale stages, including text, art screens, and monster cast roll" + - point: "Dynamic text rendering for endgame sequences" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Handles game mode-specific endings for DOOM and DOOM II" - link: "https://doomwiki.org/wiki/Game_modes" - link_label: "Game Modes" - - point: "Introduces clever hacks for sprite animations and sound synchronization" - link: "https://doomwiki.org/wiki/Sprite" - link_label: "Sprite Animation in DOOM" + - point: "Character cast animation system with sound and state transitions" + link: "https://en.wikipedia.org/wiki/State_machine" + link_label: "State machine" + - point: "Custom scrolling animation for the 'Bunny Scroll' ending" + link: "https://doomwiki.org/wiki/Ending" + link_label: "DOOM Ending" + - point: "Efficient screen wipe and background rendering techniques" + link: "https://doomwiki.org/wiki/Rendering_engine" + link_label: "DOOM Rendering Engine" + - point: "Integration of music and sound effects into finale sequences" + link: "https://en.wikipedia.org/wiki/Sound_effect" + link_label: "Sound Effects" enhancements: - - id: "finale-stage-logic" - line_start: 203 - line_end: 248 - title: "How DOOM Decides Its Finale Stage" - wikipedia_url: "https://doomwiki.org/wiki/Finale" - image_url: "" - image_caption: "" - content: "This section defines the stages of the finale sequence: text display, art screens, and the monster cast roll. The variable `finalestage` acts as a state machine, transitioning between these stages based on player progress and timing. In 1993, this kind of state-driven design was common in games, as it allowed developers to create dynamic sequences without hardcoding every frame. By abstracting the stages, DOOM could adapt its finale logic for different game modes and expansions, such as DOOM II or The Ultimate DOOM. This modularity influenced later games, which adopted similar state-driven approaches for cutscenes and endgame sequences." - - id: "finale-text-selection" + - id: "start-finale-sequence" line_start: 92 line_end: 190 - title: "Dynamic Text Selection for DOOM’s Endings" - wikipedia_url: "https://doomwiki.org/wiki/Endings" + title: "How DOOM Decides Your Finale" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "This section defines pointers to various text strings (`e1text`, `e2text`, etc.) that correspond to different episodes and game modes. The finale dynamically selects the appropriate text based on the player's progress and game mode. This design reflects the modularity of DOOM's engine, which was built to accommodate expansions and modifications. By separating text definitions from the rendering logic, id Software ensured that new content could be added without altering core code. This technique became a standard in game development, enabling easier localization and content updates." - - id: "start-finale-sequence" - line_start: 92 - line_end: 190 - title: "The Code That Starts DOOM’s Finale" - wikipedia_url: "https://doomwiki.org/wiki/Finale" + content: "The `F_StartFinale` function initializes the game's finale sequence, determining the type of ending based on the game mode and episode. It sets up the background texture (`finaleflat`) and the text to display (`finaletext`) while also selecting appropriate music. This function reflects DOOM's modular design, where different game modes (e.g., DOOM I, DOOM II, shareware) and episodes dynamically influence the finale content. In 1993, this approach was groundbreaking, as it allowed developers to reuse assets and logic across multiple game versions, reducing development time while enhancing player experience. The finale sequence, with its tailored visuals and music, became a memorable part of DOOM's storytelling, influencing later games like Quake and Unreal in their use of dynamic endgame sequences." + - id: "finale-event-responder" + line_start: 194 + line_end: 200 + title: "Handling Player Input During Finales" + wikipedia_url: "https://en.wikipedia.org/wiki/Event-driven_programming" image_url: "" image_caption: "" - content: "The `F_StartFinale` function initializes the finale sequence, setting the game state to `GS_FINALE` and disabling gameplay elements like the automap. It dynamically selects the background texture (`finaleflat`) and text (`finaletext`) based on the game mode and episode. This function showcases id Software's attention to detail, ensuring that each ending feels tailored to the player's journey. The modular design allowed DOOM to support multiple game modes and expansions seamlessly. This approach influenced later games with branching narratives and dynamic endings, such as the Mass Effect series." - - id: "monster-cast-roll" - line_start: 376 - line_end: 388 - title: "The Monster Cast Roll: A DOOM Icon" - wikipedia_url: "https://doomwiki.org/wiki/Cast_roll" + content: "The `F_Responder` function processes player input during the finale sequence. It checks if the finale stage is at the character cast sequence (`finalestage == 2`) and delegates input handling to `F_CastResponder`. This design ensures that the game's finale remains interactive, allowing players to influence the sequence even during scripted animations. In the early '90s, event-driven programming was becoming a standard for interactive applications, and DOOM's implementation demonstrated how it could be applied to enhance gameplay immersion. By allowing player input during the finale, DOOM set a precedent for interactive storytelling in games, paving the way for similar features in titles like Half-Life and Bioshock." + - id: "finale-animation-ticker" + line_start: 203 + line_end: 248 + title: "Ticking Through the Finale: Timer Logic and Letter-by-Letter Text" + wikipedia_url: "https://en.wikipedia.org/wiki/Game_loop" image_url: "" image_caption: "" - content: "The `castorder` array defines the sequence of monsters displayed during the cast roll, including their names and types. This feature was a playful way for id Software to showcase the game's iconic enemies while adding a cinematic touch to the finale. The cast roll became a memorable part of DOOM's identity, influencing other games to include similar sequences, such as character or enemy showcases in fighting games and RPGs. It also highlights the developers' sense of humor, as the cast roll ends with the player character listed as 'HERO.'" - - id: "cast-animation-ticker" - line_start: 391 - line_end: 494 - title: "Animating DOOM’s Monster Cast Roll" - wikipedia_url: "https://doomwiki.org/wiki/Sprite_animation" + content: "The F_Ticker function advances the finale sequence by incrementing a counter (finalecount) and transitioning between stages, while also checking for player input to skip animations or proceed to the next level. Alongside it, F_TextWrite renders the finale text one character at a time using bitmap fonts from hu_font, calculating character positions dynamically so that letters appear gradually in a cinematic crawl. Together these two routines embody DOOM's state-driven game loop, where a central tick function updates state and a paired draw function renders the result. In the early 1990s, dynamically timed text was unusual in games, but DOOM's approach added narrative polish that influenced later titles from Final Fantasy VII to Half-Life. The counter-based stage transitions also became a template for scripted sequences in subsequent id Tech engines." + - id: "character-cast-animation" + line_start: 376 + line_end: 388 + title: "Meet the Monsters: DOOM's Cast Sequence" + wikipedia_url: "https://doomwiki.org/wiki/Ending" image_url: "" image_caption: "" - content: "The `F_CastTicker` function drives the animations for the monster cast roll, transitioning between states and synchronizing sounds. It includes clever hacks, such as manually resetting attack frames (`goto stopattack`) and handling sound effects for specific states. These techniques reflect the constraints of 1993 hardware, where developers had to optimize every frame and byte. The cast roll's fluid animation and sound synchronization were groundbreaking at the time, influencing sprite-based animation systems in later games, including platformers and 2D RPGs." - - id: "bunny-scroll-ending" - line_start: 252 - line_end: 373 - title: "The Bunny Scroll: DOOM’s Quirky Finale" - wikipedia_url: "https://doomwiki.org/wiki/Bunny_scroll" + content: "The `F_StartCast` function initializes the character cast sequence, showcasing DOOM's enemies in action. It sets up the first monster's state and forces a screen wipe to transition into the cast animation. This sequence was a novel way to celebrate the game's characters, giving players a closer look at the enemies they had faced. The cast sequence became a hallmark of DOOM's presentation style, influencing other games like Mortal Kombat and Street Fighter, which adopted similar character showcases in their endings." + - id: "bunny-scroll-animation" + line_start: 640 + line_end: 693 + title: "The Bunny Scroll and the Finale Drawer: DOOM's Parting Shot" + wikipedia_url: "https://doomwiki.org/wiki/Ending" image_url: "" image_caption: "" - content: "The `F_BunnyScroll` function renders the infamous bunny scroll sequence, where a rabbit's head appears on a pike—a darkly humorous ending to DOOM's third episode. The function uses a scrolling background and dynamically switches patches to create the animation. This sequence exemplifies id Software's playful approach to storytelling, blending horror with humor. The bunny scroll became a cult favorite among fans, inspiring Easter eggs and quirky endings in other games, such as the 'cow level' in Diablo II." + content: "F_BunnyScroll creates the scrolling rabbit-and-pig finale for Episode 3 by dynamically compositing two background patches column by column while timing the appearance of seven progressive END0-END6 frames accompanied by pistol sound cues. F_Drawer is the master dispatch that selects between text rendering, the bunny scroll, static art screens, or the character cast based on the current finalestage value, relying on W_CacheLumpName to pull cached resources efficiently. Together they represent DOOM's commitment to polished, stage-specific presentation using minimal code: a single function routes the entire finale pipeline without a heavyweight scripting layer. The bunny sequence itself was a deliberate moment of levity by id Software in an otherwise grim game, and the column-by-column scroll technique is a direct reuse of the same low-level graphics primitives driving the main renderer. This pattern of a lightweight director function delegating to specialized routines influenced how later engines structured cutscene and endgame systems." --- @@ -810,4 +808,6 @@ void F_Drawer (void) } } -``` + + +``` \ No newline at end of file diff --git a/public/programs/doom/f-wipe-c.md b/public/programs/doom/f-wipe-c.md index 8cca6ee..8735d32 100644 --- a/public/programs/doom/f-wipe-c.md +++ b/public/programs/doom/f-wipe-c.md @@ -9,68 +9,66 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "f-wipe-c" order: 18 -description: "This file implements the screen wipe effects in DOOM, a visual transition technique that added polish and immersion to the game's level changes." +description: "This file implements DOOM's screen wipe effects, a visual transition between game states that added polish and immersion to the experience." summary: - - point: "Introduces column-major transformations for faster memory access during wipes" + - point: "Introduces column-major transformations for faster memory access" link: "https://en.wikipedia.org/wiki/Column-major_order" link_label: "Column-major order" - - point: "Demonstrates a modular approach to screen wipe effects with reusable routines" - link: "https://en.wikipedia.org/wiki/Modular_programming" - link_label: "Modular programming" - - point: "Uses randomization to create visually dynamic transitions" + - point: "Uses randomized initial positions for dynamic visual effects" link: "https://en.wikipedia.org/wiki/Random_number_generation" link_label: "Random number generation" + - point: "Implements modular screen wipe routines for flexibility" + link: "https://en.wikipedia.org/wiki/Modular_programming" + link_label: "Modular programming" + - point: "Optimizes memory allocation for constrained hardware" + link: "https://en.wikipedia.org/wiki/Memory_management" + link_label: "Memory management" + - point: "Pioneered visual transitions in early 3D games" + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM" enhancements: - id: "shitty-col-major-transform" line_start: 276 line_end: 285 - title: "Why Call It 'ShittyColMajorXform'?" + title: "Why DOOM Called This 'Shitty' Transform" wikipedia_url: "https://en.wikipedia.org/wiki/Column-major_order" image_url: "" image_caption: "" - content: "This function performs a column-major transformation on a 2D array, rearranging its memory layout to optimize access patterns for certain operations. The name 'shittyColMajorXform' reflects a candid, informal naming style often seen in development teams under pressure. At the time, DOOM's developers were working on hardware with limited memory bandwidth and CPU power, so optimizing memory access was critical. Column-major order, while less intuitive for row-major programmers, could reduce cache misses and improve performance in specific scenarios. The function uses dynamic memory allocation to create a temporary buffer, performs the transformation, and then copies the result back to the original array. This technique, though labeled 'shitty,' was effective enough to be used in the game's wipe effects, demonstrating the pragmatic trade-offs developers made to meet deadlines. The approach influenced later games and engines, where memory layout optimization became a standard practice in high-performance graphics programming." + content: "This function, humorously named `wipe_shittyColMajorXform`, converts a 2D array from row-major to column-major order. The transformation rearranges memory layout to optimize access patterns for certain operations, particularly in graphics rendering. At the time, DOOM's developers were working on machines with limited memory and CPU power, where even small optimizations could have significant impacts. John Carmack, known for his technical prowess, often experimented with unconventional solutions to squeeze performance out of hardware. The name reflects the developers' frustration with the complexity or inelegance of the solution, but it was effective. Column-major order is still relevant in modern computing, particularly in scientific computing and graphics APIs like OpenGL. This function demonstrates the team's willingness to embrace low-level optimizations to achieve their goals." - id: "color-xform-initialization" - line_start: 225 - line_end: 233 - title: "Setting Up for a Smooth Transition" - wikipedia_url: "https://en.wikipedia.org/wiki/Screen_transition" + line_start: 170 + line_end: 223 + title: "Initializing the Color and Melt Screen Transitions" + wikipedia_url: "https://en.wikipedia.org/wiki/Color_mapping" image_url: "" image_caption: "" - content: "The `wipe_initColorXForm` function initializes the color transformation wipe effect by copying the starting screen into a working buffer. This setup ensures that the wipe effect begins with a clean slate, ready to interpolate between the start and end screens. In 1993, screen transitions were a novel way to enhance the visual experience of games, making level changes feel more fluid and immersive. DOOM's developers leveraged this technique to mask loading times and maintain the game's fast-paced rhythm. The function's simplicity reflects the constraints of the era, where memory and CPU cycles were precious resources. By preloading the start screen into a buffer, the game could perform incremental updates without re-reading data, a technique that influenced later real-time graphics systems." - - id: "color-xform-execution" - line_start: 225 - line_end: 233 - title: "Pixel by Pixel: How DOOM Wipes Screens" - wikipedia_url: "https://en.wikipedia.org/wiki/Double_buffering" + content: "This region contains the init functions for both of DOOM's screen-wipe modes. `wipe_initColorXForm` simply copies the starting screen into the working buffer so that the subsequent per-pixel color interpolation begins from a clean baseline. `wipe_initMelt` does considerably more: it copies the start screen, runs both the start and end buffers through the column-major reformat for cache-friendly access, then seeds each screen column with a random negative offset so columns begin their downward slide at slightly different times, producing the organic dripping look. Randomness was cheap to generate and made the effect feel handcrafted rather than mechanical. Together the two init routines reflect id Software's pragmatic design philosophy: the color transform was a straightforward palette blend that cost little to initialize, while the melt demanded upfront setup work that paid for itself by enabling the more visually striking effect DOOM became famous for." + - id: "color-xform-transition" + line_start: 71 + line_end: 125 + title: "How DOOM Made Colors Melt Together" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `wipe_doColorXForm` function executes the color transformation wipe effect, gradually interpolating pixel values between the start and end screens. It uses a loop to traverse each pixel, adjusting its value based on the difference between the current and target states. If the current pixel is brighter or darker than its target, it increments or decrements the value by a fixed amount (`ticks`), ensuring a smooth transition. This approach was a clever workaround for the lack of hardware acceleration in 1993, relying entirely on CPU calculations to produce visually appealing effects. The algorithm's simplicity and efficiency were critical for DOOM's performance on consumer-grade PCs. Later graphics engines adopted similar techniques, often enhanced with hardware support, to create seamless transitions in games and applications." - - id: "melt-initialization" - line_start: 225 - line_end: 233 - title: "Randomized Melt: A Dynamic Screen Transition" - wikipedia_url: "https://en.wikipedia.org/wiki/Screen_transition" - image_url: "" - image_caption: "" - content: "The `wipe_initMelt` function initializes the 'melt' screen wipe effect, setting up column positions and randomizing their starting states. This randomness adds a dynamic, organic feel to the transition, making it visually distinct from other wipes. The function also converts the start and end screens to column-major format, optimizing memory access for the subsequent operations. Randomization was a hallmark of DOOM's design philosophy, used not just for gameplay but also for visual effects, creating an unpredictable and engaging experience. By combining randomness with memory layout optimization, the developers achieved a balance between aesthetic appeal and performance. The melt effect became iconic, influencing later games that sought to replicate DOOM's immersive transitions." - - id: "melt-execution" - line_start: 225 - line_end: 233 - title: "Melting Pixels: A Column-Based Transition" - wikipedia_url: "https://en.wikipedia.org/wiki/Screen_transition" + content: "The `wipe_doColorXForm` function performs the actual color transformation between two screens. It iterates through each pixel, adjusting its value incrementally toward the target screen's pixel value. This creates a smooth, melting effect as one screen transitions into another. The algorithm uses a simple comparison and adjustment mechanism, ensuring that the transition is visually coherent and avoids abrupt changes. In the early 1990s, this kind of effect was computationally expensive, but DOOM's developers optimized it to run efficiently on hardware like the Intel 486. The technique inspired similar effects in later games, contributing to the evolution of graphical transitions in interactive media." + - id: "melt-transition" + line_start: 127 + line_end: 168 + title: "Simulating Pixel Columns Dripping Down" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `wipe_doMelt` function executes the 'melt' screen wipe effect, simulating columns of pixels sliding downward to reveal the next screen. It uses a combination of incremental updates and memory manipulation to achieve the effect. Each column's position is tracked, and pixels are copied from the end screen to the working buffer as the column progresses downward. The function also handles the transition from the start screen to the working buffer, ensuring a seamless visual effect. This technique was a testament to DOOM's developers' ingenuity, as they created visually striking effects with limited hardware capabilities. The melt effect became a memorable part of DOOM's aesthetic, inspiring similar transitions in later games and multimedia applications." - - id: "screenwipe-controller" + content: "The `wipe_doMelt` function executes the melt effect, moving columns of pixels downward to reveal the target screen. It uses a combination of incremental adjustments and memory copying to simulate the dripping motion. The algorithm dynamically adjusts the speed of each column based on its position, creating a visually engaging effect. This kind of transition was rare in early 3D games, as hardware constraints often limited graphical complexity. DOOM's implementation demonstrated how careful optimization and creative algorithms could achieve impressive results on modest hardware. The melt effect became a signature visual transition, influencing later games and even inspiring similar effects in modern UI design." + - id: "screen-wipe-controller" line_start: 225 - line_end: 233 - title: "The Master Switch for Screen Wipes" - wikipedia_url: "https://en.wikipedia.org/wiki/Screen_transition" + line_end: 274 + title: "The Function That Tied It All Together" + wikipedia_url: "https://en.wikipedia.org/wiki/Modular_programming" image_url: "" image_caption: "" - content: "The `wipe_ScreenWipe` function serves as the central controller for all screen wipe effects, managing their initialization, execution, and cleanup. It uses a modular approach, with an array of function pointers to handle different wipe types. This design allows for easy addition of new effects, demonstrating the developers' foresight in creating extensible systems. The function also integrates with DOOM's rendering pipeline, marking the screen area for updates and ensuring smooth transitions. Modular programming was a key principle in DOOM's development, enabling rapid iteration and experimentation. The screen wipe system, with its clean separation of concerns, influenced later game engines, where modularity became a cornerstone of design." + content: "The `wipe_ScreenWipe` function serves as the central controller for DOOM's screen wipe effects. It uses a modular approach, selecting the appropriate wipe routine based on the input parameters. This design allowed developers to easily add or modify wipe effects without disrupting the overall system. Modular programming was a forward-thinking approach in the early 1990s, enabling greater flexibility and maintainability. The function also handles initialization, execution, and cleanup, ensuring that transitions are seamless and free of artifacts. This architecture influenced later game engines, where modular systems became standard practice for managing graphical effects and other subsystems." --- @@ -376,4 +374,4 @@ wipe_ScreenWipe return !go; } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/g-game-c.md b/public/programs/doom/g-game-c.md index 0fc3d86..2a1a477 100644 --- a/public/programs/doom/g-game-c.md +++ b/public/programs/doom/g-game-c.md @@ -9,44 +9,50 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "g-game-c" order: 7 -description: "This file handles gameplay mechanics and player state management in DOOM, a game that defined the FPS genre." +description: "This file handles core gameplay mechanics and player interactions in DOOM, showcasing techniques that defined the FPS genre." summary: - - point: "Introduces ticcmd structures for player input processing" + - point: "Introduced ticcmd structures for precise input handling" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Implements player rebirth and level transitions" - link: "https://en.wikipedia.org/wiki/First-person_shooter" - link_label: "First-person shooter" - - point: "Demonstrates early techniques for multiplayer synchronization" + - point: "Implemented game state transitions like level loading and intermissions" + link: "https://en.wikipedia.org/wiki/Game_state" + link_label: "Game State" + - point: "Optimized for multiplayer and demo playback" link: "https://en.wikipedia.org/wiki/Multiplayer_video_game" - link_label: "Multiplayer video game" + link_label: "Multiplayer Gaming" + - point: "Pioneered input abstraction for keyboard, mouse, and joystick" + link: "https://en.wikipedia.org/wiki/Input_device" + link_label: "Input Devices" + - point: "Defined player rebirth and level completion mechanics" + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM Mechanics" enhancements: - - id: "game-state-and-global-variables" + - id: "cmd-checksum-validation" line_start: 218 line_end: 227 - title: "How DOOM Tracks Game State in Memory" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + title: "Why DOOM Validates Player Commands" + wikipedia_url: "https://en.wikipedia.org/wiki/Checksum" image_url: "" image_caption: "" - content: "This section defines global variables that track the state of the game, such as the current level, episode, player states, and multiplayer flags. These variables are critical for maintaining consistency across gameplay sessions and ensuring smooth transitions between levels or game modes. In 1993, memory management was a significant concern, as DOOM had to run efficiently on hardware with limited RAM (often 4–8 MB). By centralizing state management in a few key structures, the developers minimized overhead and simplified debugging. This approach influenced later games, including Quake and Unreal, which adopted similar centralized state-tracking mechanisms for multiplayer and single-player modes." - - id: "input-handling-and-ticcmd" + content: "The G_CmdChecksum function calculates a checksum for a player's ticcmd structure, ensuring the integrity of player inputs during gameplay. This was crucial in multiplayer settings where data corruption or tampering could disrupt the experience. In 1993, networked multiplayer gaming was still in its infancy, and DOOM's approach to validating commands helped set a precedent for ensuring fair play. By summing the integer values of the ticcmd structure, the game could detect inconsistencies and enforce synchronization between clients. This technique influenced later multiplayer games, which adopted similar integrity checks to prevent cheating and maintain stable connections." + - id: "input-handling-build-ticcmd" line_start: 230 line_end: 436 - title: "The Input System That Made DOOM Fast" - wikipedia_url: "https://en.wikipedia.org/wiki/First-person_shooter" + title: "How DOOM Unified Player Input" + wikipedia_url: "https://en.wikipedia.org/wiki/Input_device" image_url: "" image_caption: "" - content: "The `G_BuildTiccmd` function processes player inputs from the keyboard, mouse, and joystick, converting them into a `ticcmd` structure that represents movement, actions, and special commands. This abstraction allowed DOOM to handle input efficiently, even during complex multiplayer sessions. The function includes clever optimizations, such as double-click detection and accelerative turning, which improved responsiveness and gameplay fluidity. In an era where input devices varied widely, this system ensured compatibility and performance. The ticcmd structure became a foundational concept for input handling in later FPS engines, influencing games like Half-Life and Call of Duty." - - id: "level-loading-and-sky-textures" + content: "The G_BuildTiccmd function processes player inputs from various devices—keyboard, mouse, and joystick—and converts them into a unified ticcmd structure. This abstraction allowed DOOM to support multiple input methods seamlessly, a significant innovation at the time. The function handles movement, turning, strafing, and special actions like firing and using items, while also accounting for double-click behaviors and turbo cheats. By normalizing inputs, DOOM ensured consistent gameplay across different hardware setups. This approach influenced later games, which adopted similar input abstraction layers to accommodate diverse control schemes, paving the way for modern input handling in engines like Unity and Unreal." + - id: "level-loading-sky-textures" line_start: 444 line_end: 496 - title: "Dynamic Sky Textures: A DOOM Innovation" + title: "The Sky Texture Trick in Level Loading" wikipedia_url: "https://en.wikipedia.org/wiki/Texture_mapping" image_url: "" image_caption: "" - content: "The `G_DoLoadLevel` function initializes a new level, including setting up sky textures based on the episode and game version. This dynamic texture selection added variety and immersion to the game's environments, a novel feature at the time. The function also resets player states and clears input buffers to ensure a clean transition. In 1993, texture mapping was still a relatively new technique, and DOOM's use of dynamic textures demonstrated its technical prowess. This approach influenced later games like Duke Nukem 3D and Unreal, which expanded on dynamic environmental effects." + content: "The G_DoLoadLevel function initializes a new level, including setting up sky textures based on the current episode and game version. DOOM dynamically determines the appropriate sky texture, creating a sense of progression and atmosphere as players advance through episodes. This was a clever use of texture mapping to enhance immersion on limited hardware. The function also resets player states, clears input buffers, and prepares the game for the next stage. This modular approach to level loading influenced later game engines, which adopted similar techniques to manage assets and transitions efficiently. The dynamic sky texture selection became a hallmark of environmental storytelling in games." - id: "event-handling-responder" line_start: 499 line_end: 596 @@ -54,87 +60,87 @@ enhancements: wikipedia_url: "https://en.wikipedia.org/wiki/Event-driven_programming" image_url: "" image_caption: "" - content: "The `G_Responder` function processes events such as key presses, mouse movements, and joystick inputs, determining their impact on gameplay. It includes special cases like toggling spy mode with F12 and activating the control panel during demos. This event-driven approach allowed DOOM to respond dynamically to player actions, a key feature for its fast-paced gameplay. In the early 1990s, event-driven programming was gaining traction, and DOOM's implementation showcased its potential in real-time applications. This technique became standard in game engines like Unity and Unreal Engine." - - id: "player-rebirth-and-level-completion" - line_start: 774 - line_end: 791 - title: "The Code Behind Player Rebirth" - wikipedia_url: "https://en.wikipedia.org/wiki/Respawn_(gaming)" + content: "The G_Responder function processes player events, such as key presses, mouse movements, and joystick inputs, and determines their impact on gameplay. It handles special cases like spy mode (F12) and menu activation during demos, showcasing DOOM's event-driven programming model. By filtering and routing events to specific subsystems like the automap or status bar, the function ensures responsive and intuitive gameplay. This modular event handling influenced later games, which adopted similar architectures to manage complex input scenarios. The ability to toggle spy mode or pause the game with specific keys demonstrated DOOM's attention to user experience, a principle that remains central to game design." + - id: "game-loop-ticker" + line_start: 600 + line_end: 747 + title: "The Heartbeat of DOOM's Game Loop" + wikipedia_url: "https://en.wikipedia.org/wiki/Game_engine" image_url: "" image_caption: "" - content: "The `G_PlayerFinishLevel` function resets a player's state upon completing a level, clearing power-ups, cards, and temporary effects like invisibility or gun flashes. This ensures a consistent starting point for the next level. In the early 1990s, respawn and state-reset mechanics were still evolving, and DOOM's implementation set a precedent for handling player transitions. Games like Quake and Counter-Strike later refined these mechanics, adding features like persistent stats and inventory systems." - - id: "player-respawn-logic" - line_start: 794 - line_end: 832 - title: "How DOOM Handles Player Respawns" - wikipedia_url: "https://en.wikipedia.org/wiki/Respawn_(video_gaming)" + content: "The G_Ticker function represents the core of DOOM's game loop, processing player commands, updating game state, and handling special actions like saving and pausing. It ensures synchronization between players in multiplayer mode, checks for turbo cheats, and manages consistency checks to prevent desynchronization. The function also invokes subsystems like the automap, intermission screen, and finale sequence based on the current game state. This centralized game loop design became a blueprint for modern game engines, which rely on similar structures to coordinate gameplay mechanics. DOOM's ability to handle multiplayer consistency and special actions within the loop was groundbreaking for its time." + - id: "player-initialization" + line_start: 750 + line_end: 770 + title: "How DOOM Prepares Players for Battle" + wikipedia_url: "https://en.wikipedia.org/wiki/Player_character" + image_url: "" + image_caption: "" + content: "The G_InitPlayer function initializes player structures at the start of the game, setting up saved information and default states. It calls G_PlayerReborn to reset the player's attributes, ensuring a clean slate for gameplay. This function reflects DOOM's modular approach to player management, allowing for easy customization and extension. By separating initialization from gameplay logic, DOOM laid the groundwork for scalable player systems in later games. The concept of rebirth and default states influenced RPGs and multiplayer games, where player initialization is critical to maintaining balance and fairness." + - id: "level-completion-cleanup" + line_start: 774 + line_end: 791 + title: "What Happens When You Finish a Level" + wikipedia_url: "https://en.wikipedia.org/wiki/Level_(video_gaming)" image_url: "" image_caption: "" - content: "This section resets a player's state when they respawn, restoring health, weapons, and ammo to default values. The logic ensures players are ready to re-enter the game without carrying over unintended states from their previous life. In 1993, multiplayer gaming was still in its infancy, and DOOM's implementation of respawning was a foundational step for deathmatch gameplay. John Carmack and the team designed this system to ensure fairness and balance, critical for competitive play. The respawn logic influenced later multiplayer games, including Quake and Unreal Tournament, which expanded on these ideas with more complex respawn mechanics." - - id: "spot-check-respawn" + content: "The G_PlayerFinishLevel function resets player attributes upon completing a level, clearing powers, cards, and temporary effects like invisibility and gun flashes. This ensures players start the next level with a consistent baseline, maintaining balance and fairness. The function also cancels palette changes and bonus counts, reflecting DOOM's attention to detail in player state management. This approach influenced later games, which adopted similar cleanup routines to prepare players for new challenges. By resetting attributes, DOOM ensured a smooth transition between levels, enhancing the player's sense of progression and accomplishment." + - id: "deathmatch-spawn-logic" line_start: 891 line_end: 918 - title: "Checking Respawn Spots for Players" - wikipedia_url: "https://en.wikipedia.org/wiki/Spawn_point" + title: "How DOOM Made Deathmatch Work" + wikipedia_url: "https://en.wikipedia.org/wiki/Deathmatch" image_url: "" image_caption: "" - content: "The G_CheckSpot function determines whether a player can respawn at a specific location, ensuring the spot isn't occupied by other objects or players. This logic prevents players from spawning into inaccessible or obstructed areas, a common issue in early multiplayer games. The function also handles removing old player corpses and spawning teleportation fog effects for visual feedback. This approach reflects the team's attention to detail, ensuring smooth gameplay even in chaotic multiplayer matches. The concept of validating spawn points became a standard in multiplayer game design, influencing titles like Counter-Strike and Halo." - - id: "deathmatch-spawn-logic" + content: "The `G_DeathMatchSpawnPlayer` function is responsible for spawning players at random deathmatch spots during level load or after a death. It ensures that players are placed in valid locations by checking each spot with the `G_CheckSpot` function. If no suitable spot is found, the player is spawned at their default starting position, even if it risks getting stuck. This logic was critical for the multiplayer deathmatch mode, a groundbreaking feature of DOOM that popularized competitive online gaming. In 1993, multiplayer gaming was still in its infancy, with most games relying on local area networks or direct modem connections. DOOM's deathmatch mode became a cultural phenomenon, inspiring countless imitators and laying the groundwork for modern competitive gaming. The random spawn mechanic added unpredictability and fairness, ensuring players couldn't memorize spawn points for an unfair advantage. This approach influenced later multiplayer games, including Quake and Unreal Tournament, which refined and expanded upon DOOM's deathmatch concepts." + - id: "player-rebirth-logic" line_start: 920 line_end: 966 - title: "Randomized Deathmatch Spawn Points" - wikipedia_url: "https://en.wikipedia.org/wiki/Deathmatch" + title: "Respawning: A Second Chance in DOOM" + wikipedia_url: "https://en.wikipedia.org/wiki/Respawn_(gaming)" image_url: "" image_caption: "" - content: "This routine selects a random spawn point for players in deathmatch mode, ensuring dynamic and unpredictable gameplay. If no valid spot is found after multiple attempts, the player spawns at a default location, even if it might lead to being stuck. This randomness was a deliberate choice by the developers to enhance the chaotic nature of deathmatch gameplay. The idea of randomized spawn points influenced later multiplayer games, including Call of Duty and Battlefield, where spawn logic evolved to include dynamic adjustments based on player density and map control." - - id: "level-completion-logic" - line_start: 1019 - line_end: 1140 - title: "Transitioning Between Levels in DOOM" - wikipedia_url: "https://doomwiki.org/wiki/Intermission_screen" + content: "The `G_DoReborn` function handles player respawning after death. In single-player mode, it reloads the level from scratch, while in multiplayer deathmatch mode, it spawns the player at a random spot or their default starting position. The function also dissociates the player's corpse from their character object, ensuring clean respawning. This mechanic was essential for maintaining the fast-paced, action-packed gameplay that defined DOOM. In the early 1990s, respawning was a relatively novel concept, as many games ended upon player death. DOOM's approach allowed players to continue playing without interruption, contributing to its addictive gameplay loop. The respawning logic also supported the game's multiplayer mode, ensuring players could rejoin the action quickly. This technique became a staple in first-person shooters, influencing titles like Halo and Call of Duty, where respawning is integral to gameplay." + - id: "doom-par-times" + line_start: 969 + line_end: 983 + title: "The Speedrunning Challenge Hidden in DOOM" + wikipedia_url: "https://doomwiki.org/wiki/Par_time" image_url: "" image_caption: "" - content: "The G_DoCompleted function handles the transition between levels, including intermission screens that display player stats and par times. It also manages secret exits and special conditions for certain levels. This feature added depth to DOOM's gameplay, encouraging players to explore levels thoroughly and aim for faster completion times. The intermission screens became iconic, inspiring similar features in games like Duke Nukem 3D and Half-Life. The inclusion of par times also laid the groundwork for speedrunning as a competitive activity, which remains popular today." - - id: "save-load-game" - line_start: 1200 - line_end: 1251 - title: "The Save and Load System in DOOM" + content: "The `pars` array defines par times for DOOM levels, setting a benchmark for players to complete each level. These times were a nod to competitive speedrunning, encouraging players to optimize their gameplay and strive for faster completions. In the early 1990s, speedrunning was an emerging phenomenon, with players sharing their best times through word of mouth or printed magazines. DOOM's inclusion of par times helped formalize the concept, providing a built-in challenge for players to beat. This feature inspired a community of speedrunners who analyzed the game's mechanics to achieve record-breaking times. Today, speedrunning is a major part of gaming culture, with dedicated events like Games Done Quick showcasing players' skills. DOOM's par times were an early example of developers recognizing and supporting this competitive spirit." + - id: "save-load-game-logic" + line_start: 1191 + line_end: 1267 + title: "How DOOM Saved Your Progress" wikipedia_url: "https://en.wikipedia.org/wiki/Save_game" image_url: "" image_caption: "" - content: "This section implements the save and load functionality, allowing players to preserve their progress and resume gameplay later. Save files include player stats, level data, and game state, ensuring a seamless experience. In the early 1990s, save systems were becoming a standard feature in PC games, and DOOM's implementation was robust for its time. The ability to save anywhere in the game was particularly appreciated by players tackling its challenging levels. This system influenced later games, including RPGs like Baldur's Gate and action titles like Tomb Raider, which expanded on the concept with autosave and checkpoint systems." + content: "The `G_LoadGame` and `G_DoLoadGame` functions implement DOOM's save and load game functionality. They handle reading save files, verifying version compatibility, and restoring game state, including player stats, level progress, and world modifications. This system was crucial for allowing players to pause their adventure and resume later, a feature that became standard in gaming. In 1993, save systems varied widely across games, with some relying on passwords or limited save slots. DOOM's approach, using binary save files with version checks, ensured reliability and prevented corrupted saves. The system also supported the game's episodic structure, allowing players to revisit earlier levels without losing progress. This design influenced later games, including Quake and Half-Life, which expanded save functionality to support autosaves and checkpoints." - id: "demo-recording-playback" - line_start: 1490 - line_end: 1502 - title: "DOOM's Groundbreaking Demo System" + line_start: 1526 + line_end: 1545 + title: "The Feature That Let DOOM Share Itself" wikipedia_url: "https://doomwiki.org/wiki/Demo" image_url: "" image_caption: "" - content: "The demo recording and playback functionality allowed players to record their gameplay and share it with others, an innovative feature for its time. The system captures player inputs and game state, enabling precise playback of recorded sessions. This feature was invaluable for debugging, showcasing gameplay, and fostering a community of players who shared speedruns and strategies. DOOM's demo system influenced later games like Quake and StarCraft, which adopted similar features for competitive play and community engagement. Today, tools like Twitch and YouTube have replaced in-game demo systems, but DOOM's approach remains a landmark in gaming history." - - id: "demo-playback-initialization" - line_start: 1581 - line_end: 1619 - title: "How DOOM Initialized Demo Playback" - wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" - image_url: "" - image_caption: "" - content: "This section initializes the playback of demo files, setting key flags and variables to ensure the game enters the correct state for demo playback. The code reads values from a demo buffer to configure parameters like whether monsters are present, the console player index, and multiplayer settings. By toggling the 'precache' flag, the code avoids unnecessary resource loading during level initialization, prioritizing speed over completeness. In 1993, demo playback was a novel feature, allowing players to share their gameplay experiences and developers to showcase the game without direct interaction. At the time, the gaming landscape was shifting toward more immersive and shareable experiences. DOOM's demo system was a clever way to demonstrate the game's capabilities while also serving as a debugging and benchmarking tool for developers. John Carmack's engineering philosophy emphasized efficiency, evident in how this code minimizes overhead during demo playback. The demo system influenced later games by introducing the concept of replayable game sessions. Titles like Quake expanded on this idea, enabling competitive players to analyze matches. Today, replay systems are standard in esports and game development, with tools like Twitch and NVIDIA ShadowPlay tracing their lineage back to innovations like DOOM's demo playback." - - id: "time-demo-benchmarking" + content: "The `G_RecordDemo` and `G_BeginRecording` functions implement DOOM's demo recording feature, allowing players to capture gameplay and share it with others. These functions initialize recording, store player inputs, and manage memory allocation for demo data. Demo recording was a groundbreaking feature in 1993, providing a way for players to showcase their skills, share strategies, or debug gameplay issues. At the time, sharing gameplay often required physical media or direct observation, making DOOM's demo system a unique innovation. The feature also supported the game's competitive community, enabling players to analyze each other's techniques and improve their performance. Demo recording became a standard feature in id Software's later games, including Quake, and influenced the development of replay systems in modern games like StarCraft and Overwatch." + - id: "timedemo-performance-tool" line_start: 1621 line_end: 1633 - title: "The Benchmarking Tool Hidden in DOOM" - wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" + title: "The Tool That Measured DOOM’s Speed" + wikipedia_url: "https://en.wikipedia.org/wiki/Benchmark_(computing)" image_url: "" image_caption: "" - content: "The 'G_TimeDemo' function enables a special mode for benchmarking DOOM's performance. By disabling rendering ('nodrawers') and screen updates ('noblit'), the game focuses solely on processing gameplay logic, allowing developers to measure how many 'gametics' (game time units) are processed in real time. This was crucial for optimizing DOOM's engine on a wide range of hardware, from high-end PCs to budget systems. In the early 1990s, PC hardware varied greatly, and developers had to ensure their games ran smoothly on machines with limited processing power and memory. John Carmack's obsession with performance optimization led to tools like this, which provided precise metrics for tuning the game engine. The 'singletics' flag further simplifies the simulation, ensuring deterministic results during benchmarking. This approach to performance testing laid the groundwork for modern benchmarking tools used in game development. Techniques pioneered in DOOM influenced later engines like id Tech and Unreal Engine, which include built-in profiling and performance analysis tools. The idea of isolating specific subsystems for testing remains a cornerstone of software optimization." - - id: "demo-status-cleanup" + content: "The `G_TimeDemo` function is a performance benchmarking tool built directly into DOOM. By enabling the `-nodraw` and `-noblit` parameters, it skips rendering and screen updates, focusing solely on the game engine's ability to process gameplay logic. This allows developers to measure the raw speed of the engine without interference from graphical constraints. At the time, performance testing was critical due to the wide variety of hardware configurations in consumer PCs, ranging from high-end 486 processors to slower 386 machines. John Carmack, known for his obsession with optimization, included this feature to ensure DOOM ran smoothly across as many systems as possible. The function sets the game into a special mode (`timingdemo`) where it plays back a demo file (`defdemoname`) while recording timing data. This approach was innovative for its era, providing developers with a practical way to identify bottlenecks and optimize the engine further. The benchmarking tool influenced later game engines, which adopted similar built-in profiling features. Today, performance profiling is a standard practice in game development, with tools like Unity and Unreal Engine offering integrated solutions for measuring frame rates and resource usage. Carmack's foresight in embedding such functionality directly into the game laid the groundwork for modern performance testing methodologies." + - id: "demo-cleanup-and-looping" line_start: 1636 line_end: 1686 - title: "The Cleanup Routine That Kept DOOM Stable" - wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" + title: "How DOOM Managed Demo Playback" + wikipedia_url: "https://en.wikipedia.org/wiki/Game_engine" image_url: "" image_caption: "" - content: "The 'G_CheckDemoStatus' function handles the cleanup and transition logic after a demo finishes playing or recording. It ensures that resources are freed, flags are reset, and the game state returns to normal. For example, it releases memory allocated for the demo buffer and resets gameplay parameters like 'deathmatch' and 'nomonsters.' If a demo recording is completed, the function writes the demo data to a file and terminates recording mode. In the early 1990s, memory management was a critical concern for game developers. PCs often had limited RAM, and failing to clean up unused resources could lead to crashes or degraded performance. This routine reflects Carmack's meticulous attention to stability and efficiency, ensuring that DOOM could run reliably even during complex operations like demo playback and recording. The concept of cleaning up after temporary modes influenced later game engines, which adopted similar practices for managing resources during replay systems, level transitions, and debugging tools. Developers studying DOOM's source code have praised its clarity and robustness, and techniques like this have become standard in modern game development. The cleanup logic here directly inspired similar routines in engines like Quake's and even influenced middleware solutions for resource management in games." + content: "The `G_CheckDemoStatus` function handles the cleanup and status checks for demo playback in DOOM. This routine is called after a player dies or completes a level during demo playback, ensuring the game transitions smoothly to the next action. It performs several tasks: timing the demo (`timingdemo`), stopping playback (`demoplayback`), resetting game states like `netgame` and `deathmatch`, and advancing to the next demo (`D_AdvanceDemo`). If the demo is being recorded (`demorecording`), it writes the demo data to a file and frees the memory buffer. This meticulous management of demo states reflects Carmack's engineering philosophy of efficiency and precision. In the early 1990s, demos served multiple purposes: they were used for debugging, performance testing, and showcasing gameplay to players. The ability to record and playback demos was a novel feature that allowed developers to test specific scenarios repeatedly without manual input. This function also highlights DOOM's modular design, where different game states are cleanly separated and managed. The demo system influenced later games, particularly in competitive gaming, where replay systems became essential for analyzing matches. Titles like Quake, Counter-Strike, and StarCraft adopted demo recording and playback as standard features, enabling players to review their performance and share gameplay with others. The concept of game state management seen here remains a cornerstone of modern game engine design, ensuring smooth transitions between different modes and activities." --- @@ -1828,4 +1834,4 @@ boolean G_CheckDemoStatus (void) -``` +``` \ No newline at end of file diff --git a/public/programs/doom/i-sound-c.md b/public/programs/doom/i-sound-c.md index 2753c27..3f2a1c2 100644 --- a/public/programs/doom/i-sound-c.md +++ b/public/programs/doom/i-sound-c.md @@ -9,74 +9,66 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "i-sound-c" order: 22 -description: "This file implements the sound system for the Linux port of DOOM, showcasing how audio was handled in one of gaming's most influential titles." +description: "This file implements DOOM's sound system for Linux, showcasing how sound effects and music were handled in a groundbreaking game on modest hardware." summary: - - point: "Introduced sound mixing for multiple channels in real-time" - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Used Linux's OSS (Open Sound System) for audio output" - link: "https://en.wikipedia.org/wiki/Open_Sound_System" - link_label: "Open Sound System" - - point: "Pre-cached sound data from WAD files for performance" - link: "https://en.wikipedia.org/wiki/WAD_(file_format)" - link_label: "WAD file format" - - point: "Experimental use of Linux timer interrupts for asynchronous sound handling" + - point: "DOOM's sound system used direct hardware interaction via Linux's soundcard interface." + link: "https://en.wikipedia.org/wiki/Linux" + link_label: "Linux" + - point: "The sound mixing relied on precomputed lookup tables for efficiency." + link: "https://en.wikipedia.org/wiki/Lookup_table" + link_label: "Lookup Table" + - point: "Sound effects were padded and cached to optimize playback." + link: "https://en.wikipedia.org/wiki/Cache_(computing)" + link_label: "Cache" + - point: "Experimental timer interrupts were used for asynchronous sound updates." link: "https://en.wikipedia.org/wiki/Interrupt" - link_label: "Interrupts" - - point: "Demonstrated techniques for stereo sound separation and volume adjustment" - link: "https://en.wikipedia.org/wiki/Stereophonic_sound" - link_label: "Stereo sound" + link_label: "Interrupt" + - point: "DOOM's sound system influenced later game engines by demonstrating how to handle audio on constrained hardware." + link: "https://en.wikipedia.org/wiki/Id_Tech" + link_label: "id Tech" enhancements: - - id: "sound-buffer-setup" - line_start: 80 - line_end: 174 - title: "How DOOM Mixed Eight Channels of Sound" - wikipedia_url: "https://en.wikipedia.org/wiki/Sound_card" + - id: "safe-ioctl-convenience-wrapper" + line_start: 386 + line_end: 423 + title: "Safe ioctl: A Convenience Wrapper for Hardware" + wikipedia_url: "https://en.wikipedia.org/wiki/Ioctl" image_url: "" image_caption: "" - content: "This section defines the global sound mixing buffer and the parameters for handling multiple sound channels simultaneously. The buffer is sized to accommodate 512 samples per channel, with stereo output requiring two hardware channels. At the time, consumer-grade sound cards like the Sound Blaster were common, and DOOM's sound system was designed to work within their constraints. By mixing audio from up to eight channels into a single buffer, the game could produce complex soundscapes, such as overlapping gunfire and monster growls. This approach influenced later game engines, which adopted similar techniques for real-time sound mixing." - - id: "sound-data-loading" + content: "This function wraps the `ioctl` system call, which is used to interact directly with hardware devices in Linux. By encapsulating `ioctl` in a function that checks for errors and prints debugging information, the DOOM developers ensured more robust handling of sound hardware. At the time, Linux was still in its early years, and direct hardware interaction was often fraught with compatibility issues. By including error reporting and exiting on failure, this wrapper helped developers diagnose issues quickly during testing. This approach reflects the pragmatic mindset of id Software, who often had to work around hardware quirks to deliver their games. Wrappers like this became common practice in game development, influencing later engines and frameworks that needed to interact with hardware directly." + - id: "wad-sound-data-loading" line_start: 446 line_end: 500 - title: "The WAD File Trick for Fast Sound Access" + title: "How DOOM Loaded Sounds from WAD Files" wikipedia_url: "https://en.wikipedia.org/wiki/WAD_(file_format)" image_url: "" image_caption: "" - content: "The `getsfx` function loads sound effects from DOOM's WAD files, padding them to ensure compatibility with the mixing buffer size. This design allowed the game to pre-cache sound data, reducing latency during gameplay. WAD files were a novel format at the time, enabling developers to bundle game assets like textures, levels, and sounds into a single file. This approach not only streamlined asset management but also inspired modding communities, as fans could easily replace or add custom sounds. The concept of bundling assets in a single file became standard practice in game development." - - id: "sound-channel-management" + content: "The `getsfx` function retrieves sound data from DOOM's WAD files, which were the game's primary data storage format. It pads the sound data to ensure compatibility with the mixing buffer size, a critical step for efficient sound playback. This function also handles a fallback mechanism: if a requested sound is unavailable, it substitutes a default sound effect, ensuring the game remains functional even in edge cases. The WAD format was a pioneering approach to game asset management, allowing developers to package levels, textures, and sounds into a single file. This modularity influenced countless games and engines that followed, including Quake and Unreal Engine. The padding mechanism here reflects the constraints of the era, where sound data had to align with fixed buffer sizes for efficient processing on limited hardware." + - id: "addsfx-channel-management" line_start: 504 line_end: 513 - title: "How DOOM Prioritized Chainsaw Sounds" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - image_url: "" - image_caption: "" - content: "The `addsfx` function manages active sound channels, ensuring that only a limited number of sounds play simultaneously. It prioritizes sounds based on their age and uniqueness, with special handling for effects like the chainsaw, which are restricted to one instance at a time. This was crucial for maintaining performance on hardware with limited audio capabilities. By dynamically assigning channels and adjusting stereo separation, DOOM achieved immersive soundscapes that enhanced its gameplay. This technique influenced later games, which adopted similar methods for sound prioritization and channel management." - - id: "sound-mixing-loop" - line_start: 525 - line_end: 653 - title: "The Loop That Mixed DOOM's Audio" + title: "Managing Eight Sound Channels on Modest Hardware" wikipedia_url: "https://en.wikipedia.org/wiki/Sound_card" image_url: "" image_caption: "" - content: "The `I_UpdateSound` function is the core of DOOM's sound system, mixing audio data from all active channels into the global buffer. It clamps values to prevent distortion and handles stereo separation for left and right channels. This loop was optimized for performance, ensuring smooth audio playback even on modest hardware. The use of volume lookup tables and efficient memory access patterns minimized CPU overhead, a critical consideration in an era when processors like the Intel 486 were common. This method of real-time sound mixing became a foundational technique in game audio programming." - - id: "sound-initialization" - line_start: 692 - line_end: 729 - title: "How DOOM Configured Linux Sound Devices" - wikipedia_url: "https://en.wikipedia.org/wiki/Open_Sound_System" + content: "The `addsfx` function is responsible for adding sound effects to DOOM's internal list of active sounds. It manages up to eight sound channels, prioritizing older sounds for replacement when all channels are occupied. This function also calculates stereo separation and volume adjustments for each sound, ensuring immersive audio playback. At the time, consumer PCs had limited audio capabilities, often restricted to simple stereo output. By carefully managing channels and precomputing volume adjustments, DOOM delivered a rich auditory experience despite these constraints. The technique of prioritizing sounds and managing limited channels became a standard in game audio systems, influencing engines like id Tech and Unity. The stereo separation logic here foreshadows the advanced spatial audio techniques used in modern games." + - id: "sound-mixing-buffer-update" + line_start: 516 + line_end: 653 + title: "Mixing Sound Channels into a Global Buffer" + wikipedia_url: "https://en.wikipedia.org/wiki/Digital_audio" image_url: "" image_caption: "" - content: "The `I_InitSound` function initializes DOOM's sound system, configuring the Linux OSS (Open Sound System) for audio output. It sets parameters like sample rate, stereo mode, and fragment size, ensuring compatibility with the `/dev/dsp` device. This was a significant adaptation for the Linux port, as the original DOS version relied on different APIs. By pre-caching sound data and zeroing the mixing buffer, the function prepared the system for efficient runtime audio handling. This approach demonstrated how games could adapt to diverse operating systems, paving the way for cross-platform development." - - id: "timer-interrupts" - line_start: 915 - line_end: 937 - title: "Experimental Timer Interrupts for Sound" + content: "The `I_UpdateSound` function is the heart of DOOM's sound mixing system. It loops through all active sound channels, retrieves samples from raw sound data, adjusts them based on channel parameters, and mixes them into a global buffer. This buffer is then clamped to ensure values remain within the allowed range for 16-bit audio. The function's design reflects the constraints of the era, where real-time sound mixing had to be efficient to avoid performance bottlenecks. By precomputing volume adjustments and using a single buffer for all channels, DOOM achieved smooth audio playback on hardware with limited processing power. This approach influenced later game engines, which adopted similar techniques for mixing multiple sound sources in real time." + - id: "experimental-timer-interrupt" + line_start: 656 + line_end: 669 + title: "Using Timer Interrupts for Asynchronous Sound" wikipedia_url: "https://en.wikipedia.org/wiki/Interrupt" image_url: "" image_caption: "" - content: "This section explores the use of Linux timer interrupts to manage asynchronous sound output. The `I_SoundSetTimer` function sets up a periodic interrupt to update the sound buffer, ensuring consistent audio playback. While experimental, this technique showcased the potential of leveraging OS-level features for real-time applications. Timer interrupts were a novel approach for games at the time, as most relied on synchronous sound handling. This experimentation influenced later developers, who refined similar methods for more robust audio systems in modern engines like Unity and Unreal." + content: "The `I_HandleSoundTimer` function demonstrates an experimental use of Linux timer interrupts to update the sound buffer asynchronously. By leveraging the `SIGALRM` signal and the `ITIMER_REAL` timer, the function ensures that sound updates occur independently of the main game loop. This technique was experimental and reflects the developers' willingness to push the boundaries of what was possible on Linux at the time. Timer interrupts allowed DOOM to achieve smoother audio playback, even under heavy CPU load. While this approach was eventually replaced by more robust sound APIs, it laid the groundwork for asynchronous audio handling in games. Modern engines like Unity and Unreal use similar concepts to manage audio updates without interrupting gameplay." --- @@ -1065,4 +1057,4 @@ void I_SoundDelTimer() if ( I_SoundSetTimer( 0 ) == -1) fprintf( stderr, "I_SoundDelTimer: failed to remove interrupt. Doh!\n"); } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/i-video-c.md b/public/programs/doom/i-video-c.md index f828c2a..8ec661e 100644 --- a/public/programs/doom/i-video-c.md +++ b/public/programs/doom/i-video-c.md @@ -9,114 +9,106 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "i-video-c" order: 23 -description: "This file handles graphics initialization and rendering for DOOM on X11 systems, showcasing techniques for efficient graphics manipulation and interaction with Unix-based environments." +description: "This file handles graphics and input for DOOM's X11 port, showcasing techniques for interfacing with UNIX systems and the MIT Shared Memory extension." summary: - - point: "DOOM's X11 graphics integration relied on MIT SHM for shared memory efficiency" + - point: "DOOM's X11 port used MIT Shared Memory for efficient graphics updates." link: "https://en.wikipedia.org/wiki/MIT-SHM" - link_label: "MIT SHM" - - point: "The file includes custom handling for mouse and keyboard input in X11" + link_label: "MIT-SHM" + - point: "Key translation adapted X11 keycodes for DOOM's internal event system." link: "https://en.wikipedia.org/wiki/X_Window_System" link_label: "X Window System" - - point: "DOOM's palette manipulation optimized 256-color visuals on PseudoColor screens" - link: "https://en.wikipedia.org/wiki/Color_depth#Indexed_color" - link_label: "Indexed Color" - - point: "Scaling algorithms in this file allowed DOOM to adapt to various resolutions" + - point: "DOOM's scaling logic allowed gameplay on various resolutions." link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Shared memory management addressed Unix system pollution issues" + link_label: "DOOM (1993)" + - point: "Shared memory handling included safeguards against stale segments." link: "https://en.wikipedia.org/wiki/Shared_memory" link_label: "Shared Memory" + - point: "Graphics initialization ensured compatibility with 256-color PseudoColor screens." + link: "https://en.wikipedia.org/wiki/Color_depth" + link_label: "Color Depth" enhancements: - - id: "graphics-initialization-x11" - line_start: 193 - line_end: 346 - title: "How DOOM Used Shared Memory for Speed" - wikipedia_url: "https://en.wikipedia.org/wiki/MIT-SHM" - image_url: "" - image_caption: "" - content: "This section initializes key variables for DOOM's graphics system on X11, including shared memory (MIT SHM) and display properties. Shared memory was a critical optimization for DOOM's rendering pipeline, allowing direct access to memory buffers without costly copying operations. In 1993, Unix systems were not typically associated with high-performance gaming, but id Software leveraged the MIT SHM extension to bypass some of the limitations of X11's standard image handling. This approach reduced latency and enabled smoother gameplay on modest hardware. The use of shared memory also required careful management to avoid 'pollution'—stale shared memory segments left behind by previous processes. This technique influenced later Unix-based games and applications, which adopted similar optimizations for graphics rendering." - - id: "keyboard-input-translation" + - id: "key-translation-for-doom-input" line_start: 92 line_end: 161 - title: "Translating X11 Key Events into DOOM Commands" + title: "Key Translation for DOOM Input" wikipedia_url: "https://en.wikipedia.org/wiki/X_Window_System" image_url: "" image_caption: "" - content: "This function, `xlatekey`, translates X11 key events into DOOM's internal key codes. It maps common keys like arrows, function keys, and modifiers to DOOM-specific constants, ensuring seamless interaction between the X11 environment and the game's input system. At the time, handling input across different platforms was a challenge, as each operating system had its own conventions and APIs. By abstracting key translation, id Software made DOOM portable and adaptable to Unix systems. This approach laid groundwork for future cross-platform game development, where input abstraction became standard practice." + content: "The `xlatekey` function translates X11 keycodes into DOOM's internal key representations. This mapping converts standard keyboard inputs (like arrow keys, function keys, and alphanumeric characters) into game-specific constants used by DOOM's event system. At the time, interfacing with X11 required developers to manually handle key translations, as there was no universal abstraction layer for input. John Carmack and the team ensured that DOOM could respond to user input seamlessly, even on UNIX systems. This approach influenced later game engines, which adopted similar event-driven input handling mechanisms. It also demonstrated the importance of cross-platform compatibility, paving the way for engines like SDL to standardize input handling across operating systems." - id: "graphics-shutdown-cleanup" line_start: 163 line_end: 175 - title: "The Cleanup Routine That Prevented Crashes" - wikipedia_url: "https://en.wikipedia.org/wiki/Shared_memory" + title: "Graphics Shutdown and Cleanup" + wikipedia_url: "https://en.wikipedia.org/wiki/MIT-SHM" image_url: "" image_caption: "" - content: "The `I_ShutdownGraphics` function ensures proper cleanup of graphics resources, including detaching shared memory and releasing buffers. This was vital for Unix systems, where failing to detach shared memory could lead to resource leaks and system instability. The paranoia evident in the code (e.g., setting `image->data` to NULL) reflects the challenges of programming in environments where manual resource management was critical. This meticulous approach influenced later game engines, which adopted similar practices to ensure stability and reliability across diverse platforms." - - id: "mouse-event-handling" + content: "The `I_ShutdownGraphics` function ensures proper cleanup of resources when DOOM exits. It detaches from the X server, releases shared memory, and nullifies pointers to prevent accidental reuse. The use of MIT Shared Memory (MIT-SHM) was a cutting-edge technique in the early '90s, enabling faster graphics updates by sharing memory between the application and the X server. This function reflects the team's attention to stability and resource management, critical for a game running on diverse UNIX systems. Proper cleanup routines like this became standard practice in game development, influencing how modern engines handle resource deallocation." + - id: "event-handling-in-x11" line_start: 193 line_end: 278 - title: "How DOOM Made Mouse Input Work on X11" + title: "Event Handling in X11" wikipedia_url: "https://en.wikipedia.org/wiki/X_Window_System" image_url: "" image_caption: "" - content: "The `I_GetEvent` function processes mouse and keyboard events from the X11 system, translating them into DOOM's internal event structure. Handling mouse input was particularly tricky on X11, as the system lacked built-in support for invisible cursors or direct mouse control. DOOM's solution involved warping the pointer back to the center of the window to maintain focus, a workaround that became common in early Unix games. This section highlights the ingenuity required to adapt gaming conventions to a non-gaming operating system, paving the way for more sophisticated input handling in later Unix-based games." - - id: "screen-scaling-algorithms" + content: "The `I_GetEvent` function processes user input and system events from the X11 server, converting them into DOOM's internal event types. It handles keyboard presses, mouse movements, and button clicks, posting these events to DOOM's event queue. This design allowed DOOM to maintain its fast-paced gameplay while running on UNIX systems. The function's structure reflects the challenges of adapting a real-time game to a windowed environment, where input might be delayed or interrupted. By integrating X11's event system, DOOM demonstrated how games could leverage existing graphical environments without sacrificing responsiveness. This approach influenced later ports of games to UNIX-like systems and contributed to the development of cross-platform game engines." + - id: "screen-scaling-and-blitting" line_start: 348 line_end: 520 - title: "Scaling Pixels for Blocky Graphics Modes" + title: "Screen Scaling and Blitting" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "This section implements screen scaling algorithms to adapt DOOM's 320x200 resolution to higher resolutions by duplicating pixels. The `multiply` variable determines the scaling factor, with options for 2x, 3x, or 4x scaling. These algorithms were essential for making DOOM playable on a variety of displays, as consumer monitors in 1993 varied widely in resolution and capabilities. The blocky graphics mode, described as 'boneheaded' by developer Dave Taylor, was a pragmatic solution to hardware constraints. This technique influenced later games that used similar scaling methods to achieve compatibility across devices." - - id: "palette-uploading" + content: "The `I_FinishUpdate` function handles scaling and blitting the game screen to the X11 window. Depending on the `multiply` factor, it scales the 320x200 framebuffer to larger resolutions by duplicating pixels. This scaling logic allowed DOOM to run on various display configurations, accommodating players with different hardware setups. The function also uses the MIT-SHM extension for efficient graphics updates, reducing the overhead of transferring pixel data to the X server. This technique was innovative for its time, showcasing how Carmack and the team pushed the limits of hardware to deliver smooth gameplay. The scaling approach influenced later games and engines, which adopted similar techniques for resolution independence." + - id: "palette-management-in-x11" line_start: 537 line_end: 576 - title: "Optimizing Color Palettes for 256-Color Screens" - wikipedia_url: "https://en.wikipedia.org/wiki/Color_depth#Indexed_color" + title: "Palette Management in X11" + wikipedia_url: "https://en.wikipedia.org/wiki/Color_depth" image_url: "" image_caption: "" - content: "The `UploadNewPalette` function initializes and updates the X11 colormap with DOOM's 256-color palette. This was crucial for achieving vibrant visuals on PseudoColor screens, which were common in 1993. The function uses gamma correction tables to adjust color intensity, ensuring the game looked consistent across different monitors. This approach reflects the challenges of developing for hardware with limited color depth and influenced techniques for color management in later games, especially those targeting low-end systems." - - id: "shared-memory-management" + content: "The `UploadNewPalette` function initializes and updates the X11 colormap to match DOOM's palette. It ensures that colors are correctly displayed on 256-color PseudoColor screens, which were common in the early '90s. The function uses gamma correction tables to adjust color intensity based on user settings, enhancing the visual experience. This meticulous attention to color fidelity reflects the team's commitment to delivering an immersive experience, even on constrained hardware. Palette management was a critical aspect of game development during this era, influencing how later engines handled color rendering and compatibility with diverse display technologies." + - id: "shared-memory-allocation" line_start: 587 line_end: 689 - title: "The Battle Against Stale Shared Memory" + title: "Shared Memory Allocation for Graphics" wikipedia_url: "https://en.wikipedia.org/wiki/Shared_memory" image_url: "" image_caption: "" - content: "The `grabsharedmemory` function manages shared memory segments for DOOM's graphics buffers. It includes logic to detect and clean up stale memory left by previous processes, a common issue on Unix systems. The code even checks for other users running DOOM and warns against potential conflicts. This meticulous handling of shared memory reflects the challenges of developing multiplayer and high-performance applications on Unix, where resource management was often manual. The techniques here influenced later Unix-based games and applications, which adopted similar strategies to ensure stability and performance." - - id: "x11-window-and-shared-memory-init" + content: "The `grabsharedmemory` function allocates shared memory for DOOM's graphics, using the MIT-SHM extension to improve performance. It includes safeguards against stale memory segments, checking for existing shared memory blocks and cleaning them up if necessary. This approach reflects the challenges of working with shared resources in a multi-user UNIX environment, where leftover segments could cause conflicts. The function's robust error handling ensures stability, even in polluted systems. Shared memory techniques like this were crucial for achieving real-time performance on limited hardware. They influenced later games and applications, which adopted similar methods for efficient inter-process communication." + - id: "x11-window-initialization" line_start: 691 line_end: 914 - title: "How DOOM Built Its Linux Window and Bypassed the X Server" + title: "How DOOM Created Windows on X11" wikipedia_url: "https://en.wikipedia.org/wiki/X_Window_System" image_url: "" image_caption: "" - content: "The `I_InitGraphics` function is where DOOM's Linux port comes to life, combining two sophisticated initialization tasks into one sweep. First, it constructs an X11 window from scratch: creating a colormap, setting event masks for keyboard and optional pointer input, calling `XCreateWindow`, installing a null cursor, creating a graphics context, mapping the window, and then blocking in an event loop until an Expose event confirms the window is actually on screen and ready to accept drawing commands. X11's asynchronous model made this blocking wait essential — without it, early `XShmPutImage` calls would silently fail. Second, if the MIT-SHM extension is available on a local display, the function creates an XShm image that maps directly into memory shared between the DOOM process and the X server. This bypasses the normal `XPutImage` path, which would copy every frame's pixel data across the socket, and instead lets both sides read and write the same physical memory pages. The `grabsharedmemory` helper handles the messier side of this: scanning for stale shared-memory segments left by previous DOOM instances, warning when another user's session is still attached, and cleaning up or reusing existing segments rather than always allocating fresh ones. Together, window creation and shared-memory setup represent id Software's pragmatic approach to Unix gaming in 1993 — embrace the platform's low-level capabilities, tolerate its complexity, and extract every byte of performance available. This work helped prove that high-performance games were possible on Linux, influencing later ports and native Linux game engines." - - id: "pixel-expansion-table-init" + content: "This section initializes the main window for DOOM on X11, the graphical system used on Unix-like operating systems. It sets up attributes like colormap and border pixel, creates the window, defines a cursor, and maps the window to the screen. The code also includes logic to wait for an Expose event, ensuring the window is ready for drawing before proceeding. A notable feature is the use of XGrabPointer to restrict mouse movement to the game window, enhancing the immersive experience. In the early 1990s, X11 was a common choice for graphical applications on Unix systems, but it came with challenges like managing shared memory and handling asynchronous events. The developers leveraged these tools to create a responsive and visually engaging experience despite the limitations of the hardware. This approach influenced later Unix-based games and graphical applications, demonstrating how to optimize performance on X11 systems." + - id: "initexpand-lookup-table" line_start: 919 line_end: 925 title: "The Lookup Table That Expanded Pixels" - wikipedia_url: "https://en.wikipedia.org/wiki/Pixel_art" + wikipedia_url: "https://en.wikipedia.org/wiki/Lookup_table" image_url: "" image_caption: "" - content: "This section initializes a lookup table (`exptable`) used for pixel expansion. Each entry in the table represents a single byte expanded into a 32-bit value, replicating the pixel across four bytes. This technique was used to scale low-resolution graphics efficiently, a common challenge in the early 1990s when displays often had limited resolution but games aimed to look visually appealing. The use of lookup tables for pixel manipulation reflects the team's focus on performance, as precomputing values reduces runtime calculations. This approach is a precursor to modern GPU techniques, where precomputed data and lookup tables are used to optimize rendering pipelines. It also demonstrates the ingenuity required to achieve high-quality graphics on constrained hardware." - - id: "double-precision-pixel-expansion" + content: "The `InitExpand` function initializes a lookup table (`exptable`) that maps 8-bit values to 32-bit expanded representations. Each entry in the table combines the original value with shifted versions of itself, effectively duplicating the pixel data across four bytes. This technique was crucial for DOOM's rendering pipeline, where pixel data needed to be expanded for certain operations. Lookup tables like this were a common optimization in the 1990s, allowing developers to trade memory for speed—a valuable tradeoff given the limited processing power of consumer PCs. John Carmack and his team were known for their ability to squeeze maximum performance out of hardware, and this function is a prime example of their ingenuity. The idea of precomputing data for rapid access influenced many later graphics algorithms and game engines, where similar techniques are used to optimize rendering." + - id: "initexpand2-double-precision-table" line_start: 929 line_end: 953 title: "Building a Double-Precision Pixel Table" wikipedia_url: "https://en.wikipedia.org/wiki/Double_precision_floating-point_format" image_url: "" image_caption: "" - content: "This section builds a more complex lookup table (`exptable2`) for double-precision pixel expansion. Each entry combines two 32-bit pixel values into a 64-bit double, allowing efficient manipulation of multiple pixels at once. The use of double precision is unusual for graphics operations of the era, reflecting the team's willingness to experiment with unconventional techniques to optimize rendering. The code includes a union to reinterpret memory, a clever trick to manipulate data formats directly. This approach highlights the team's deep understanding of hardware and memory operations, as well as their commitment to squeezing every ounce of performance from the system. While double precision is less common in modern graphics pipelines, the principle of precomputing and batching operations remains a cornerstone of efficient rendering." - - id: "expand4-pixel-rendering-loop" + content: "The `InitExpand2` function constructs a more complex lookup table (`exptable2`) using double-precision floating-point values. Each entry combines two 8-bit values into a 64-bit representation, enabling efficient manipulation of pixel data. The function iterates through all possible combinations of two 8-bit values, storing the results in a union that allows direct access to the underlying binary representation. This approach reflects the team's deep understanding of hardware and memory management, as double-precision values were not typically used for pixel data at the time. The function includes debug messages (`printf`) to indicate progress, a reminder of the iterative and experimental nature of game development in the 1990s. Techniques like this paved the way for more advanced graphics systems, influencing later engines that relied on precomputed data for performance." + - id: "expand4-pixel-expansion-algorithm" line_start: 957 line_end: 1047 - title: "Rendering Four Pixels at a Time" - wikipedia_url: "https://en.wikipedia.org/wiki/Rendering_(computer_graphics)" + title: "The Algorithm That Scaled DOOM's Pixels" + wikipedia_url: "https://en.wikipedia.org/wiki/Graphics_pipeline" image_url: "" image_caption: "" - content: "The `Expand4` function is a pixel rendering loop that processes four pixels at a time using the precomputed `exptable2`. It iterates over the screen's width and height, expanding each set of four pixels into a larger representation suitable for display. The function uses pointer arithmetic and memory reinterpretation to access and manipulate pixel data efficiently. This approach was essential for achieving smooth graphics on hardware with limited processing power, as it minimized the number of operations required per frame. The use of precomputed tables and batch processing influenced later graphics techniques, including SIMD (Single Instruction, Multiple Data) operations and GPU shaders. By focusing on efficient rendering, the DOOM team set a standard for performance optimization that continues to shape game development today." + content: "The `Expand4` function performs pixel expansion using the precomputed lookup tables (`exptable2`). It processes lines of pixel data (`lineptr`) and writes expanded pixel values to an output buffer (`xline`). The function includes logic to handle groups of four pixels at a time, using bitwise operations and pointer arithmetic to access the lookup table efficiently. This method was designed to scale DOOM's visuals for different resolutions and display configurations, ensuring the game looked sharp and consistent across hardware. The use of double-precision values and careful memory alignment reflects the team's commitment to optimizing every aspect of the rendering pipeline. At a time when graphics accelerators were rare, these techniques allowed DOOM to achieve smooth, high-quality visuals on standard PCs. The algorithm's influence can be seen in modern graphics pipelines, where similar principles are applied to texture mapping, shading, and resolution scaling." --- @@ -1168,4 +1160,6 @@ Expand4 xline += step; } while (y--); } -``` + + +``` \ No newline at end of file diff --git a/public/programs/doom/info-c.md b/public/programs/doom/info-c.md index 8dc2379..daa67fd 100644 --- a/public/programs/doom/info-c.md +++ b/public/programs/doom/info-c.md @@ -9,98 +9,146 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "info-c" order: 24 -description: "This file defines the lookup tables and state machine for DOOM's entities, showcasing the game's innovative approach to animating and controlling in-game objects." +description: "This file defines the lookup tables and state transitions for DOOM's game objects, showcasing the game's innovative use of state-driven behavior in a resource-constrained environment." summary: - - point: "Defines sprite names for all entities" + - point: "Defines sprite names for all game objects" link: "https://doomwiki.org/wiki/Sprite" link_label: "Sprite" - - point: "Implements state machine for entity behavior" + - point: "Contains state transition logic for weapons and monsters" link: "https://doomwiki.org/wiki/State" link_label: "State" - - point: "Includes action functions for entity animations" - link: "https://doomwiki.org/wiki/Action_function" - link_label: "Action Function" - - point: "Uses lookup tables for efficient entity state transitions" - link: "https://en.wikipedia.org/wiki/Lookup_table" - link_label: "Lookup Table" - - point: "Demonstrates modular design for game logic" - link: "https://en.wikipedia.org/wiki/Modular_programming" - link_label: "Modular Programming" + - point: "Illustrates the use of function pointers for dynamic behaviors" + link: "https://en.wikipedia.org/wiki/Function_pointer" + link_label: "Function Pointer" + - point: "Highlights DOOM's modular design for extensibility" + link: "https://doomwiki.org/wiki/Modding" + link_label: "Modding" + - point: "Demonstrates efficient memory usage with fixed-size arrays" + link: "https://en.wikipedia.org/wiki/Array_data_structure" + link_label: "Array" enhancements: - id: "sprite-name-lookup-table" line_start: 28 line_end: 54 - title: "The Lookup Table That Named DOOM's World" + title: "The Lookup Table That Defined DOOM's World" wikipedia_url: "https://doomwiki.org/wiki/Sprite" image_url: "" image_caption: "" - content: "This section defines the `sprnames` array, a lookup table containing the names of all sprites used in DOOM. Each entry corresponds to a visual representation of entities, weapons, or effects, such as 'TROO' for the Imp or 'BFGG' for the BFG weapon. By using a compact array, the game efficiently associates sprite names with their corresponding graphical assets. In 1993, memory constraints on consumer PCs were severe, and lookup tables like this were a common technique to minimize memory usage while maintaining flexibility. John Carmack and the team at id Software leveraged this approach to streamline the game's rendering pipeline. This design influenced later games, where sprite-based systems evolved into texture atlases and object-oriented asset management. The concept of centralized sprite naming persists in modern engines like Unity and Unreal, albeit in more sophisticated forms." - - id: "action-function-declarations" + content: "This section defines the `sprnames` array, a lookup table containing the names of all sprites used in DOOM. Each entry corresponds to a graphical representation of game objects, ranging from monsters and weapons to environmental elements like lamps and barrels. The array is indexed by constants defined elsewhere in the code, allowing efficient access to sprite data during gameplay. In 1993, memory constraints were a significant challenge for developers. DOOM's designers, led by John Carmack, optimized the game's resource usage by employing fixed-size arrays and lookup tables. This approach minimized memory overhead while ensuring fast access to critical data. The sprite names themselves are concise, often just four characters long, reflecting the need to conserve space. The modularity of this design allowed DOOM to be easily modified by players and developers. Custom sprites could be added by replacing entries in the `sprnames` array, a feature that contributed to the game's enduring popularity and the rise of its modding community. Modern game engines, such as Unity and Unreal Engine, continue to use similar techniques for managing assets, albeit with far fewer constraints. This lookup table exemplifies the ingenuity required to create a visually rich and immersive experience on hardware with limited capabilities. It laid the groundwork for the efficient asset management systems used in countless games that followed." + - id: "weapon-action-function-pointers" line_start: 57 - line_end: 131 - title: "How DOOM's Entities Came to Life" - wikipedia_url: "https://doomwiki.org/wiki/Action_function" + line_end: 107 + title: "How DOOM Made Weapons Feel Alive" + wikipedia_url: "https://en.wikipedia.org/wiki/Function_pointer" image_url: "" image_caption: "" - content: "This block lists function prototypes for `action` functions, which define behaviors for DOOM's entities. Functions like `A_Light0` and `A_FirePistol` encapsulate specific actions, such as firing weapons or triggering visual effects. These functions are later linked to states in the game's state machine. At the time, this modular approach allowed id Software to rapidly iterate on gameplay mechanics without rewriting large portions of code. The concept of action functions became a staple in game development, influencing scripting systems in engines like Quake and Half-Life. Today, similar systems are implemented using event-driven programming and component-based design, enabling even greater flexibility and scalability." - - id: "state-machine-implementation" + content: "This section declares function pointers for weapon actions, such as `A_FirePistol` and `A_Raise`. These functions are dynamically invoked during gameplay to execute specific behaviors associated with weapons, like firing, reloading, or raising/lowering. By using function pointers, DOOM's developers created a flexible and extensible system for handling weapon logic. In the early 1990s, games often relied on hardcoded logic for object behaviors, which made updates and modifications cumbersome. John Carmack's decision to use function pointers was groundbreaking, enabling dynamic behavior assignment without bloating the codebase. This approach also simplified debugging and allowed for rapid iteration during development. Function pointers became a staple in game programming, influencing the design of scripting systems in engines like Quake and Unreal. Today, similar concepts are implemented using object-oriented programming and event-driven architectures, but the underlying principle remains the same: decoupling logic from data to enhance flexibility and maintainability. This design choice not only contributed to DOOM's success but also set a precedent for future games, demonstrating the power of modular and dynamic systems in creating immersive gameplay experiences." + - id: "state-transition-array" line_start: 134 line_end: 799 - title: "The State Machine That Controlled DOOM" + title: "The State Machine Behind DOOM's Monsters" wikipedia_url: "https://doomwiki.org/wiki/State" image_url: "" image_caption: "" - content: "This section defines the `states` array, the heart of DOOM's entity state machine. Each entry specifies a sprite, duration, action function, and the next state, creating a deterministic flow for entity behavior. For example, the Imp's attack sequence transitions through states with actions like `A_FaceTarget` and `A_TroopAttack`. This design allowed DOOM to simulate complex interactions with minimal computational overhead, crucial for running on 486-class PCs. The state machine concept, while not invented by id Software, was elevated here to handle fast-paced gameplay with dozens of entities simultaneously. Modern game engines still use state machines, often enhanced with hierarchical and event-driven models, to manage AI and animations. DOOM's implementation directly influenced engines like Quake and Unreal, and its principles are taught in game development courses worldwide." - - id: "state-machine-object-definitions" + content: "The `states` array defines the state machine for DOOM's game objects, including monsters, weapons, and environmental effects. Each entry specifies a sprite, duration, action function, and the next state, creating a seamless flow of animations and behaviors. For example, a monster's attack sequence transitions through multiple states, invoking functions like `A_FaceTarget` and `A_TroopAttack`. In 1993, state-driven design was a novel approach to game programming. It allowed developers to encapsulate complex behaviors in a compact and manageable format. John Carmack and his team leveraged this technique to create dynamic and responsive gameplay, ensuring that monsters and weapons felt alive despite the game's technical constraints. The state machine concept has since become a cornerstone of game development. Modern engines use similar structures, often implemented with finite state machines or behavior trees, to manage AI and object interactions. DOOM's implementation influenced games like Quake, Half-Life, and countless others, proving the effectiveness of this design pattern. This section showcases the brilliance of DOOM's architecture, where simplicity and efficiency converge to deliver a groundbreaking gaming experience. It remains a source of inspiration for developers seeking to balance complexity with performance." + - id: "state-table-for-game-objects" line_start: 800 line_end: 1102 - title: "How DOOM's State Machine Powers Its Monsters" - wikipedia_url: "https://en.wikipedia.org/wiki/Finite-state_machine" + title: "The State Table That Brought DOOM to Life" + wikipedia_url: "https://doomwiki.org/wiki/State" image_url: "" image_caption: "" - content: "This section defines a large array of state transitions for DOOM's game objects, including monsters, projectiles, and environmental elements. Each entry specifies a sprite, duration, action function, and the next state, creating a finite-state machine for each object. For example, the Cyberdemon's attack sequence transitions through multiple states, invoking functions like A_CyberAttack and A_FaceTarget to control its behavior. In 1993, this approach was groundbreaking for its simplicity and efficiency. The finite-state machine model allowed developers to encode complex behaviors using compact data structures, minimizing CPU overhead on hardware like the Intel 80486. John Carmack, known for his focus on performance, designed this system to ensure smooth gameplay even on modest PCs. This technique influenced later game engines, including id Tech 2 and id Tech 3, which expanded on state-based logic for more complex interactions. Modern engines like Unity and Unreal still use similar concepts, albeit with more abstraction and flexibility. The state machine approach remains a cornerstone of game AI and animation systems, demonstrating the lasting impact of DOOM's design." - - id: "mobjinfo-object-attributes" - line_start: 1025 - line_end: 1025 - title: "The Blueprint Behind DOOM's Monsters" - wikipedia_url: "https://en.wikipedia.org/wiki/Object-oriented_programming" + content: "This section defines the state table for DOOM's game objects, mapping sprites, animations, and actions to specific states. Each entry specifies a sprite frame, duration, action function, and the next state, enabling dynamic transitions during gameplay. For example, the Cyberdemon's attack sequence transitions through multiple states, invoking functions like `A_CyberAttack` to execute its iconic rocket barrage. In 1993, memory and processing constraints on consumer PCs were severe. John Carmack's design leveraged compact data structures like this state table to minimize runtime overhead while maximizing flexibility. The table-driven approach allowed developers to define complex behaviors without hardcoding them into the engine, a technique inspired by earlier games like Wolfenstein 3D but refined for DOOM's more ambitious scope. This modularity became a hallmark of DOOM's engine, influencing later games and engines like Quake and Unreal. The state table concept persists in modern game development, where finite state machines are used to manage AI and object behavior. DOOM's source code, released in 1997, inspired countless developers to study and adapt these techniques, cementing its legacy as a foundational text in game programming." + - id: "mobjinfo-array-for-game-objects" + line_start: 1105 + line_end: 1599 + title: "How DOOM's Enemies Got Their Stats" + wikipedia_url: "https://doomwiki.org/wiki/Thing" image_url: "" image_caption: "" - content: "The mobjinfo array defines the attributes and behaviors of DOOM's movable objects (mobj), including enemies, projectiles, and pickups. Each entry specifies properties like health, speed, radius, height, and flags for interactions (e.g., MF_SOLID for collision or MF_NOGRAVITY for floating). For example, the Arch-Vile (MT_VILE) has high health, fast speed, and a unique attack state, making it one of the game's most challenging enemies. This design reflects the constraints and priorities of 1993-era game development. Memory was limited, and performance was critical, so attributes were stored in compact data structures. Carmack's focus on modularity ensured that new objects could be added with minimal changes to the codebase. The flags system, which controls interactions like gravity and collision, was particularly innovative, allowing for diverse behaviors without duplicating logic. The mobjinfo structure influenced object-oriented programming in later game engines. Concepts like encapsulating attributes and behaviors in a single entity became standard practice, shaping engines like Unreal and Unity. The modularity of DOOM's design also inspired the modding community, enabling the creation of custom monsters and levels. This section exemplifies how thoughtful engineering can have a lasting impact on both developers and players." - - id: "monster-data-structure" - line_start: 1523 - line_end: 1547 - title: "How DOOM Packed Monsters Into Memory" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + content: "The `mobjinfo` array defines the attributes and behavior of DOOM's game objects, including players, enemies, and items. Each entry specifies properties like health, speed, mass, radius, and height, along with sound effects and state transitions for spawning, attacking, dying, and more. For instance, the Arch-Vile (MT_VILE) is given 700 health, a high speed of 15, and unique sound effects like `sfx_vilsit` and `sfx_vildth`. Flags like `MF_SOLID` and `MF_SHOOTABLE` dictate how objects interact with the environment and player actions. This design reflects Carmack's focus on modularity and efficiency. By centralizing object definitions, changes could be made easily without altering the engine's core logic. The approach also allowed for rapid iteration during development, as new enemies or items could be added by simply extending the array. The influence of this system is evident in later games and engines. Quake expanded on the concept with more complex object hierarchies, while modern engines like Unity and Unreal use similar centralized data structures for object management. The release of DOOM's source code provided a blueprint for aspiring developers, showcasing how to balance flexibility with performance in resource-constrained environments. Today, the `mobjinfo` array stands as a testament to the ingenuity that made DOOM a technical and cultural phenomenon." + - id: "spider-mastermind-definition" + line_start: 1601 + line_end: 1625 + title: "How the Spider Mastermind Came to Life" + wikipedia_url: "https://doomwiki.org/wiki/Spider_Mastermind" + image_url: "" + image_caption: "" + content: "This block defines the Spider Mastermind, one of DOOM's most iconic bosses. Attributes like spawn health (3000), speed (12), and radius (128 units) are specified alongside state transitions for spawning, attacking, and dying. The modular design allows the game to handle complex behaviors efficiently by referencing predefined states and sounds. In 1993, this approach was groundbreaking, enabling developers to define entities in a data-driven manner rather than hardcoding behaviors. The Spider Mastermind's imposing presence and challenging gameplay cemented its place in gaming history, influencing the design of boss characters in later games such as Quake and Unreal." + - id: "baby-spider-definition" + line_start: 1627 + line_end: 1651 + title: "The Baby Arachnotron's Modular Blueprint" + wikipedia_url: "https://doomwiki.org/wiki/Arachnotron" + image_url: "" + image_caption: "" + content: "The Baby Arachnotron is defined here with a spawn health of 500 and a speed of 12, making it less formidable than its larger counterpart. Its attributes include a pain chance of 128 and a missile attack state (S_BSPI_ATK1). This modular entity design allowed DOOM to scale difficulty dynamically by introducing weaker versions of monsters. The Arachnotron's design influenced the creation of enemy hierarchies in later games, such as Half-Life's alien grunts and vortigaunts, which similarly varied in strength and behavior." + - id: "cyberdemon-definition" + line_start: 1653 + line_end: 1677 + title: "The Cyberdemon: A Modular Titan" + wikipedia_url: "https://doomwiki.org/wiki/Cyberdemon" + image_url: "" + image_caption: "" + content: "The Cyberdemon, another iconic DOOM boss, is defined with a massive spawn health of 4000 and a speed of 16. Its attributes include a pain chance of 20 and a missile attack state (S_CYBER_ATK1). The modular design allows it to transition seamlessly between states like standing, running, and dying. This approach was critical for creating challenging and memorable encounters while maintaining efficient memory usage on 1993 hardware. The Cyberdemon's design influenced the creation of other towering boss characters in games like Quake and Dark Souls." + - id: "wolfenstein-soldier-definition" + line_start: 1705 + line_end: 1729 + title: "Wolfenstein Soldiers in DOOM's Universe" + wikipedia_url: "https://doomwiki.org/wiki/Wolfenstein_Soldier" image_url: "" image_caption: "" - content: "This section of the code defines the attributes and behaviors of various entities in DOOM, including monsters, projectiles, and items. Each entity is represented as a compact data structure containing fields for health, speed, size, sound effects, and state transitions. For example, the Spider Mastermind (MT_SPIDER) is defined with a spawn health of 3000, a reaction time of 8, and specific sound effects for seeing, attacking, and dying. In 1993, memory was a precious resource on consumer PCs, which often had only 4MB of RAM. John Carmack and the DOOM team designed these data structures to be highly efficient, packing all necessary information into a minimal footprint. The use of predefined constants like `FRACUNIT` allowed for fixed-point arithmetic, avoiding the computational overhead of floating-point operations on hardware without dedicated math coprocessors. This modular approach to entity definition was groundbreaking. It allowed developers to easily add or modify entities without rewriting large portions of code. The design also supported extensibility, enabling modders to create custom monsters and items by simply defining new data structures. This flexibility contributed to DOOM's longevity and its thriving modding community. The techniques used here influenced the development of modern game engines, particularly the concept of entity-component systems (ECS), where entities are composed of reusable components. Games like Quake and Unreal built on these ideas, and today, ECS is a standard in game development frameworks like Unity and Unreal Engine. Carmack's focus on efficiency and modularity continues to shape the industry, proving that constraints can drive innovation." - - id: "object-properties-table" + content: "This block defines the Wolfenstein Soldier, a nod to id Software's earlier title, Wolfenstein 3D. With a spawn health of 50 and a pain chance of 170, these enemies are weaker than most DOOM monsters but serve as a nostalgic Easter egg. Their inclusion showcases id Software's playful approach to game design and their willingness to blend elements from previous successes. This cross-pollination of ideas influenced later games, such as Easter eggs in Duke Nukem 3D and references in modern titles like DOOM Eternal." + - id: "boss-brain-definition" + line_start: 1757 + line_end: 1781 + title: "The Boss Brain: Modular Finality" + wikipedia_url: "https://doomwiki.org/wiki/Icon_of_Sin" + image_url: "" + image_caption: "" + content: "The Boss Brain, also known as the Icon of Sin, is defined here with a spawn health of 250 and a pain chance of 255. Its attributes include a unique death state (S_BRAIN_DIE1) and flags that make it shootable but stationary. This modular definition allowed DOOM to create a climactic final boss encounter without hardcoding specific behaviors. The Icon of Sin's design influenced the creation of other stationary yet interactive bosses, such as the final boss in Quake II." + - id: "projectile-definitions" + line_start: 1939 + line_end: 2067 + title: "Projectiles: Modular Weapons in Motion" + wikipedia_url: "https://doomwiki.org/wiki/Projectile" + image_url: "" + image_caption: "" + content: "This section defines various projectiles, including rockets, plasma balls, and BFG shots. Attributes like speed, radius, and damage are specified, enabling the game to handle dynamic weapon effects efficiently. For example, the rocket is defined with a speed of 20 units and a damage of 20. This modular approach allowed DOOM to simulate complex combat scenarios on limited hardware. The projectile system influenced later games like Quake, which expanded on DOOM's modular weapon mechanics to include more advanced physics and effects." + - id: "key-item-definitions" line_start: 2329 - line_end: 2353 - title: "How DOOM Encoded Its World in Numbers" - wikipedia_url: "https://doomwiki.org/wiki/Thing" + line_end: 2397 + title: "Keys and Items: Modular Progression Tools" + wikipedia_url: "https://doomwiki.org/wiki/Key" image_url: "" image_caption: "" - content: "This section of the file defines the properties of various objects (or 'things') in DOOM, ranging from items like health packs to weapons and decorative elements. Each object is described using a compact structure that includes attributes such as spawn state, health, radius, height, sound effects, and flags that control its behavior. For example, the MF_SPECIAL flag marks objects that can be picked up, while MF_COUNTITEM indicates items that contribute to the player's score. In 1993, DOOM was designed to run on consumer-grade PCs with limited memory and processing power. John Carmack and his team optimized the game by using fixed-point arithmetic (e.g., FRACUNIT) and tightly packed data structures to minimize overhead. This approach allowed DOOM to handle dozens of objects in real-time without compromising performance. The design of these object properties influenced not only DOOM but also countless games that followed. The concept of encoding game objects with compact, extensible attributes became standard practice in game development. Modern engines like Unity and Unreal use similar principles, albeit with more sophisticated systems. The flags defined here, such as MF_SPECIAL, laid the groundwork for object-oriented behavior in games, enabling dynamic interactions and emergent gameplay. DOOM's object system was studied by developers of later games like Quake and Half-Life, and its influence can be seen in the modular design of contemporary game engines. The efficiency and flexibility of this approach remain a testament to the ingenuity of Carmack and his team, who pushed the boundaries of what was possible on 1993 hardware." - - id: "static-object-definitions" - line_start: 3109 - line_end: 3133 - title: "Why DOOM's Decorations Were More Than Eye Candy" - wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" + content: "This block defines key items like the blue, red, and yellow keys, which are essential for progression in DOOM's levels. Attributes like spawn states and flags (e.g., MF_SPECIAL) ensure these items are interactable but not shootable. This modular design allowed level designers to create intricate puzzles and locked-door mechanics without additional programming. The use of keys and items as progression tools influenced later games like Resident Evil and Metroid Prime, which expanded on this concept with more complex inventory systems." + - id: "data-driven-object-properties" + line_start: 2400 + line_end: 3199 + title: "How DOOM Encoded Its World in Data" + wikipedia_url: "https://en.wikipedia.org/wiki/Data-driven_programming" image_url: "" image_caption: "" - content: "This section of the DOOM source code defines a series of static objects, referred to as 'Things,' which populate the game's levels. These objects include environmental decorations such as torches, pillars, and meat piles, each with attributes like spawn state, dimensions, and flags for behavior. For example, the 'MF_SOLID' flag ensures collision detection, while 'MF_NOGRAVITY' allows objects to float. The use of fixed-point arithmetic (e.g., '16*FRACUNIT') for dimensions reflects the era's hardware constraints, where floating-point operations were often too costly for real-time applications. In 1993, DOOM was pushing the limits of consumer PCs, which typically featured 386 or 486 processors with limited memory. John Carmack and the team at id Software had to optimize every aspect of the game to ensure smooth performance. Static objects like these were carefully designed to enhance the game's atmosphere without taxing the hardware. The inclusion of flags for behaviors like 'MF_SPAWNCEILING' and 'MF_NOGRAVITY' allowed for creative level design, enabling objects to appear suspended in mid-air or attached to ceilings. The detailed definition of these objects contributed to DOOM's immersive environments, a key factor in its success. Later games, including Quake and Unreal, built on this approach, incorporating even more complex environmental interactions. The concept of 'Things' as modular, attribute-driven entities influenced game engines like Unity and Unreal Engine, where similar systems are used to define objects in 3D worlds. DOOM's efficient handling of static objects remains a touchstone in game development, demonstrating how technical constraints can inspire creative solutions." + content: "This section of code defines the attributes of various in-game objects, known as 'Things' in DOOM's terminology. Each object is described using a structured format, specifying properties like health, size, sound effects, and behavioral flags. For example, weapons like the shotgun and chaingun are defined alongside power-ups, ammunition, and decorative elements such as lamps. The 'doomednum' field links each object to its corresponding identifier in the level editor, while flags like MF_SPECIAL determine whether the object can be picked up or interacts with the player. In the early 1990s, game developers faced significant constraints due to limited memory and processing power. DOOM's developers, led by John Carmack, adopted a data-driven approach to manage these constraints efficiently. By encoding object properties in a compact tabular format, they minimized hardcoded logic and made it easier to iterate on gameplay designs. This technique allowed DOOM to support a wide variety of objects without bloating the codebase. The modularity of this design influenced countless games that followed. Many modern engines, including Unity and Unreal Engine, use similar data-driven paradigms to define game entities. The ability to separate object definitions from game logic became a cornerstone of game development, enabling designers to tweak gameplay without requiring deep programming knowledge. DOOM's approach also inspired level editors and modding tools, empowering players to create custom content. This legacy is evident in the thriving modding community that continues to produce new levels and gameplay experiences for DOOM decades after its release." - id: "object-properties-definition" - line_start: 3915 - line_end: 3939 - title: "How DOOM's Objects Were Built to Last" + line_start: 3914 + line_end: 3938 + title: "Why DOOM's Decorations Were So Efficient" + wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(video_game)" + image_url: "" + image_caption: "" + content: "This section of the DOOM source code defines the static properties of various decorative and environmental objects, such as torches, columns, and meat piles. Each object is represented by a structure containing attributes like spawn state, health, radius, height, mass, and flags that dictate its behavior in the game world. For example, the MF_SOLID flag ensures that the object blocks player movement, while MF_NOGRAVITY allows it to float in the air. In the early 1990s, hardware constraints were a significant challenge for game developers. Consumer PCs often lacked floating-point units, so DOOM relied on fixed-point arithmetic (e.g., 16*FRACUNIT) to perform calculations efficiently. This approach allowed the game to maintain precision without taxing the CPU, enabling smooth gameplay even on modest machines like the Intel 386. John Carmack and the DOOM team designed these objects with modularity and reusability in mind. By standardizing the attributes and behaviors of decorative objects, they could quickly populate levels with visually distinct yet computationally lightweight elements. This design philosophy not only saved memory but also reduced the complexity of level creation, empowering designers to focus on creativity. The influence of this approach is evident in later games and engines. Many modern game engines, such as Unreal Engine and Unity, use similar modular systems to define object properties. The flags and attributes introduced here laid the groundwork for more sophisticated object behavior systems, including physics engines and AI-driven interactions. Developers studying DOOM's source code have praised its clarity and ingenuity, cementing its legacy as a milestone in game development." + - id: "compact-object-definitions" + line_start: 4000 + line_end: 4668 + title: "How DOOM Packed 3D Worlds into Tiny Objects" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "This section of DOOM's source code defines the attributes of various miscellaneous game objects, ranging from environmental decorations (like trees and candles) to static corpses and hanging bodies. Each object is represented as a structured block of data, specifying properties such as spawn state, health, dimensions, sound effects, and behavior flags. The flags control aspects like solidity, gravity, and spawning behavior, enabling a wide variety of object interactions within the game world. In 1993, DOOM was pushing the limits of consumer-grade PCs, which often lacked floating-point hardware. To ensure precision and performance, the developers used fixed-point arithmetic (e.g., `FRACUNIT`) for object dimensions and movement calculations. This choice allowed DOOM to run smoothly on hardware like the Intel 386 and 486 processors, which were common at the time. The modular design of these object definitions also reflects John Carmack's philosophy of simplicity and efficiency, enabling rapid iteration and extensibility. The consequence of this design is profound. By defining objects in a compact and modular format, DOOM became highly moddable, allowing players to create custom levels, objects, and behaviors. This extensibility fueled the game's longevity and established it as a cornerstone of the modding community. Techniques like fixed-point arithmetic and modular object definitions influenced later game engines, including id Software's own Quake engine and other industry standards. The flags system, in particular, became a common approach for controlling object behaviors in games, appearing in engines like Unreal Engine and Unity. Today, DOOM's design principles are studied by developers aiming to balance performance, flexibility, and simplicity in game development." + content: "This section of the code defines metadata for DOOM's in-game objects, known as 'mobjinfo' entries. Each object is described using a compact structure that specifies its physical properties (radius, height, mass), behavioral states (spawnstate, painstate, deathstate), and sound effects. These definitions are used by the game's engine to simulate interactions and render objects in the 3D environment. In 1993, memory constraints were a major challenge for game developers. Consumer PCs typically had only 4–8 MB of RAM, and DOOM needed to fit its entire game world, textures, sounds, and logic within these limits. John Carmack, the technical lead, designed these object definitions to be as compact as possible, using fixed-point arithmetic (e.g., FRACUNIT) to avoid the computational overhead of floating-point operations. This approach allowed DOOM to run smoothly on modest hardware while maintaining its fast-paced gameplay. These object definitions also laid the groundwork for DOOM's extensibility. By abstracting object properties into a table-driven format, the game became highly moddable. Players and developers could create custom levels, new objects, and entirely new gameplay experiences by modifying these definitions. This flexibility contributed to DOOM's enduring popularity and its role in establishing the modding culture in gaming. The influence of this design can be seen in later game engines, such as Quake and Unreal Engine, which adopted similar approaches to object-oriented game development. The concept of defining game objects through metadata tables became a standard practice, enabling developers to create complex and interactive worlds efficiently. Today, this technique is foundational in modern game development, powering everything from indie games to AAA titles." --- @@ -4773,4 +4821,5 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { S_NULL // raisestate } }; -``` + +``` \ No newline at end of file diff --git a/public/programs/doom/m-cheat-c.md b/public/programs/doom/m-cheat-c.md index 7e527bc..e26505a 100644 --- a/public/programs/doom/m-cheat-c.md +++ b/public/programs/doom/m-cheat-c.md @@ -9,44 +9,36 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "m-cheat-c" order: 17 -description: "This file implements cheat code handling in DOOM, showcasing clever techniques for input validation and sequence recognition." +description: "This file implements cheat code handling in DOOM, showcasing how developers ingeniously integrated secret sequences into gameplay." summary: - - point: "Implements cheat code sequence validation" - link: "https://en.wikipedia.org/wiki/Cheat_code" - link_label: "Cheat Code" - - point: "Uses a scrambled translation table for input mapping" + - point: "Cheat codes are processed using a sequence validation system." + link: "https://en.wikipedia.org/wiki/Cheating_in_video_games" + link_label: "Cheating in video games" + - point: "The cheat_xlate_table scrambles input keys for validation." + link: "https://doomwiki.org/wiki/Cheat_codes" + link_label: "DOOM cheat codes" + - point: "Cheat codes were an iconic feature of 1990s gaming culture." link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Demonstrates modular design for input handling" - link: "https://en.wikipedia.org/wiki/Modular_programming" - link_label: "Modular Programming" + link_label: "DOOM (1993 video game)" enhancements: - - id: "scrambled-input-mapping-table" - line_start: 34 - line_end: 34 - title: "The Scrambled Table That Hid Cheat Codes" - wikipedia_url: "https://en.wikipedia.org/wiki/Cheat_code" - image_url: "" - image_caption: "" - content: "Lines 34–35 define a scrambled translation table (`cheat_xlate_table`) used to map user input into a predefined sequence for cheat code validation. This table is initialized with scrambled values derived from the `SCRAMBLE` macro, which obfuscates the mapping. The purpose of this approach was to prevent players from easily guessing or brute-forcing cheat codes by analyzing the game's input handling. At the time, cheat codes were a popular feature in games, offering players secret abilities or shortcuts. However, developers often sought ways to make these codes less predictable to maintain the sense of discovery. This technique reflects the ingenuity of DOOM's developers in balancing accessibility with challenge. The scrambled table approach influenced later games, where obfuscation techniques were used to protect sensitive data or prevent tampering. It also foreshadows modern practices in cryptography and input validation." - - id: "cheat-code-sequence-validation" + - id: "cheat-validation-sequence" line_start: 37 line_end: 74 - title: "How DOOM Checked Your Cheat Codes" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + title: "How DOOM Validated Cheat Codes" + wikipedia_url: "https://doomwiki.org/wiki/Cheat_codes" image_url: "" image_caption: "" - content: "Lines 42–75 implement the `cht_CheckCheat` function, which validates user input against predefined cheat code sequences. The function uses the scrambled translation table to match keystrokes to the expected sequence. If the sequence is completed successfully, the cheat is activated. This routine cleverly handles edge cases, such as resetting the sequence if an incorrect key is pressed, and initializing the sequence pointer (`cht->p`) on first use. In 1993, cheat codes were a hallmark of gaming culture, often serving as Easter eggs or developer tools. DOOM's implementation was notable for its robustness and modularity, allowing the cheat system to integrate seamlessly with the game's input handling. The technique of sequence validation influenced later games, where cheat codes evolved into unlockable achievements or developer modes. It also highlights the meticulous attention to detail that defined DOOM's programming, contributing to its reputation as a technical masterpiece." - - id: "extracting-cheat-code-parameters" + content: "This function, `cht_CheckCheat`, is responsible for validating cheat code sequences entered by the player. It uses a `cheatseq_t` structure to track the progress of the input sequence and compares each keypress against a scrambled translation table (`cheat_xlate_table`). If the sequence matches, the cheat is activated. In the early 1990s, cheat codes were a popular feature in video games, allowing players to unlock hidden abilities, skip levels, or access debug features. DOOM's implementation was particularly clever: it scrambled the input keys using a macro called `SCRAMBLE`, ensuring that the cheat codes couldn't be easily guessed or exploited without insider knowledge. The function initializes the translation table on its first call, a design choice likely made to optimize memory usage and avoid unnecessary computation during gameplay. The cheat sequence is processed character by character, resetting if the input deviates from the expected sequence. When the end-of-sequence character (`0xff`) is reached, the cheat is successfully activated. This approach reflects the constraints of the era: DOOM had to run efficiently on hardware like the Intel 386 processor with limited memory. By using a simple state machine and a scrambled lookup table, id Software ensured that cheat code handling was both secure and performant. The cheat code system became a hallmark of DOOM, with iconic sequences like \"IDDQD\" (God mode) and \"IDKFA\" (all weapons and keys). These codes were widely shared among players, contributing to the game's cultural impact. Later games, including Quake and other first-person shooters, adopted similar systems for cheat code validation, cementing this technique as a staple of game development." + - id: "extracting-cheat-parameters" line_start: 76 line_end: 98 - title: "The Function That Read Cheat Code Secrets" - wikipedia_url: "https://en.wikipedia.org/wiki/Cheat_code" + title: "Extracting Parameters from Cheat Codes" + wikipedia_url: "https://doomwiki.org/wiki/Cheat_codes" image_url: "" image_caption: "" - content: "Lines 77–99 define the `cht_GetParam` function, which extracts parameters embedded within cheat code sequences. This function scans the sequence for a special marker (`1`) indicating the start of parameters, then copies the subsequent characters into a buffer. Parameters could represent numeric values, strings, or other data used to customize the cheat's effect. For example, a cheat might unlock a specific level or grant a set amount of resources. This design reflects the flexibility of DOOM's cheat system, allowing developers to encode complex behaviors within simple sequences. In the early 1990s, such functionality was rare, as most games used hardcoded cheats with fixed effects. DOOM's approach influenced later games that implemented parameterized cheats, enabling more dynamic and user-driven gameplay. It also demonstrates the game's modular architecture, where input handling, cheat validation, and parameter extraction were cleanly separated into distinct functions." + content: "The `cht_GetParam` function is tasked with extracting parameters embedded within cheat code sequences. For example, certain cheats might include additional data, such as a level number or a specific setting. This function reads the sequence stored in the `cheatseq_t` structure and copies the parameter data into a buffer for further processing. The function begins by locating the special marker (`1`) that signifies the start of the parameter data within the sequence. It then iterates through the sequence, copying characters to the buffer until it encounters either a null character (`0`) or the end-of-sequence marker (`0xff`). This ensures that only valid parameter data is extracted, while resetting the sequence for future use. In the context of 1993, embedding parameters within cheat codes was an innovative way to extend their functionality. For instance, a cheat might allow players to warp to a specific level by appending the level number after the main code. This design reflects the ingenuity of id Software's developers, who were constantly pushing the boundaries of what games could do within the constraints of limited hardware. The parameter extraction system influenced later games by demonstrating how cheat codes could be made more versatile. Developers of titles like Duke Nukem 3D and Unreal Tournament adopted similar techniques, enabling players to customize their gameplay experience through detailed cheat inputs. Today, while cheat codes are less common in mainstream games, their legacy lives on in modding communities and debug tools, many of which trace their lineage back to systems like this." --- @@ -149,4 +141,6 @@ cht_GetParam *buffer = 0; } -``` + + +``` \ No newline at end of file diff --git a/public/programs/doom/m-fixed-c.md b/public/programs/doom/m-fixed-c.md index 93194df..e4e03d1 100644 --- a/public/programs/doom/m-fixed-c.md +++ b/public/programs/doom/m-fixed-c.md @@ -9,44 +9,44 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "m-fixed-c" order: 15 -description: "This file implements fixed-point arithmetic routines, a critical technique for achieving fast calculations on hardware without floating-point support." +description: "This file implements fixed-point arithmetic routines, a critical technique for performance optimization in DOOM's rendering engine." summary: - - point: "Fixed-point arithmetic was used to bypass the lack of floating-point hardware on early PCs." + - point: "Fixed-point arithmetic replaced floating-point calculations for speed on 1990s hardware" link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" link_label: "Fixed-point arithmetic" - - point: "DOOM's performance relied heavily on efficient mathematical operations." - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM (1993)" - - point: "These routines highlight Carmack's focus on optimization and hardware constraints." - link: "https://en.wikipedia.org/wiki/John_Carmack" - link_label: "John Carmack" + - point: "DOOM's source code popularized fixed-point math in game development" + link: "https://doomwiki.org/wiki/Source_code" + link_label: "DOOM source code" + - point: "These routines were tailored to handle precision and overflow constraints" + link: "https://en.wikipedia.org/wiki/Integer_overflow" + link_label: "Integer overflow" enhancements: - id: "fixed-multiplication-optimization" - line_start: 40 - line_end: 48 - title: "How DOOM Multiplied Without Floating-Point" + line_start: 39 + line_end: 47 + title: "How DOOM Multiplied Without Floating Point" wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" image_url: "" image_caption: "" - content: "The `FixedMul` function performs multiplication using fixed-point arithmetic, a technique where numbers are represented as integers scaled by a constant factor (here defined by `FRACBITS`). This avoids the need for floating-point operations, which were slow or unavailable on consumer hardware in the early 1990s. By shifting the result right by `FRACBITS`, the function scales the product back to the fixed-point range. In 1993, most PCs lacked dedicated floating-point units (FPUs), and software-based floating-point calculations were prohibitively slow. John Carmack and the id Software team designed DOOM to run efficiently on such hardware, leveraging fixed-point arithmetic for critical calculations like rendering and physics. This approach was inspired by earlier games and graphics techniques but refined to meet DOOM's demanding performance goals. The use of fixed-point arithmetic in DOOM influenced countless other games and engines of the era. Developers studying DOOM's source code adopted similar techniques for their own projects, ensuring compatibility with low-cost hardware. Even today, fixed-point arithmetic remains relevant in embedded systems, mobile devices, and performance-critical applications where floating-point operations are costly or unavailable." - - id: "fixed-division-edge-case-handling" - line_start: 52 - line_end: 58 - title: "The Division That Prevented Crashes" - wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + content: "This section implements `FixedMul`, a function that performs fixed-point multiplication. Fixed-point arithmetic uses integers to represent fractional values, avoiding the computational overhead of floating-point math. In this routine, two fixed-point numbers are multiplied as 64-bit integers (`long long`), and the result is shifted right by `FRACBITS` to scale it back to the fixed-point domain. At the time DOOM was developed, consumer PCs often lacked hardware floating-point units, and even when available, floating-point operations were slower than integer math. John Carmack, DOOM's lead programmer, favored fixed-point arithmetic for its speed and predictability, enabling the game to run smoothly on modest hardware like the 386 and 486 processors. This approach was crucial for real-time rendering, where every millisecond counted. Fixed-point math became a staple in game development during the 1990s, influencing engines like Quake and Unreal. While modern hardware has made floating-point math ubiquitous, fixed-point techniques remain relevant in embedded systems and mobile devices, where performance and power efficiency are critical." + - id: "fixed-division-overflow-handling" + line_start: 56 + line_end: 64 + title: "Preventing Overflow in Fixed-Point Division" + wikipedia_url: "https://en.wikipedia.org/wiki/Integer_overflow" image_url: "" image_caption: "" - content: "The `FixedDiv` function handles division in fixed-point arithmetic, but with added safeguards to prevent catastrophic errors. If the absolute value of the numerator (`a`) shifted right by 14 bits exceeds the denominator (`b`), the function returns a predefined minimum or maximum integer value based on the signs of `a` and `b`. This prevents division by zero or overflow errors, which could crash the game. In the early 1990s, error handling was a critical concern for game developers. PCs of the era lacked robust operating systems capable of gracefully recovering from crashes, and a single unhandled exception could force players to reboot their machines. Carmack's meticulous attention to edge cases ensured DOOM's stability, even under extreme conditions. This defensive programming approach became a hallmark of id Software's coding style and influenced other developers who studied DOOM's source code. Modern game engines continue to incorporate similar safeguards, ensuring reliability across diverse hardware and software environments." - - id: "fixed-division-with-floating-point" - line_start: 59 - line_end: 79 - title: "When Fixed-Point Needed Floating-Point" - wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + content: "The `FixedDiv` function performs fixed-point division while guarding against overflow. It first checks whether the absolute value of the numerator, shifted right by 14 bits, exceeds the denominator. If so, the result would overflow the fixed-point range, and the function returns the maximum or minimum integer value depending on the sign of the inputs. This safeguard reflects the constraints of fixed-point arithmetic, where precision is limited by the number of bits allocated to the fractional part. Overflow was a common concern in 1990s game programming, as developers worked within tight hardware limits. By incorporating these checks, Carmack ensured DOOM's calculations remained stable, even under extreme conditions. This meticulous attention to edge cases contributed to the game's reputation for reliability and performance. Techniques like this influenced later engines, including Quake, which continued to refine fixed-point math for real-time applications. Today, overflow handling remains a critical aspect of software development, especially in systems programming and embedded applications." + - id: "fixed-division-with-double-precision" + line_start: 68 + line_end: 86 + title: "Switching to Double Precision for Stability" + wikipedia_url: "https://en.wikipedia.org/wiki/Double_precision_floating-point_format" image_url: "" image_caption: "" - content: "The `FixedDiv2` function provides an alternative implementation of fixed-point division, using floating-point arithmetic for intermediate calculations. By casting the numerator and denominator to `double` and scaling the result by `FRACUNIT`, the function achieves higher precision than purely integer-based methods. However, it includes a critical error check: if the result exceeds the range of a 32-bit signed integer, the function triggers an error. This hybrid approach reflects the evolving hardware landscape of the mid-1990s. By the time DOOM's source code was released, many PCs included FPUs, making floating-point calculations feasible for certain operations. Carmack's decision to incorporate floating-point arithmetic here demonstrates his pragmatism: while fixed-point was essential for performance-critical routines, floating-point offered a simpler solution for less frequent calculations. The inclusion of floating-point arithmetic in DOOM's code foreshadowed its increasing adoption in later games and engines. As hardware improved, developers transitioned away from fixed-point techniques, leveraging FPUs for more complex simulations and rendering. This shift paved the way for modern game engines like Unreal Engine and Unity, which rely heavily on floating-point math for their physics and graphics systems." + content: "The `FixedDiv2` function provides an alternative implementation of fixed-point division, using double-precision floating-point arithmetic for intermediate calculations. By casting the inputs to `double`, the routine avoids the precision and overflow issues inherent in integer math. The result is scaled back to fixed-point format by multiplying by `FRACUNIT`. This approach sacrifices some of the performance benefits of fixed-point arithmetic but ensures greater stability and accuracy. The inclusion of this routine reflects the pragmatic mindset of DOOM's developers, who balanced performance with reliability. Double precision was less common in real-time applications at the time due to its computational cost, but Carmack's willingness to use it in specific cases highlights his focus on delivering a robust experience. The error check for division by zero further underscores this commitment to stability. While fixed-point math dominated early game engines, the gradual adoption of floating-point techniques paved the way for modern graphics programming, where GPUs handle floating-point operations efficiently. This transition influenced successors like Quake and Unreal, which embraced floating-point math as hardware capabilities improved." --- @@ -137,4 +137,4 @@ FixedDiv2 I_Error("FixedDiv: divide by zero"); return (fixed_t) c; } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/m-menu-c.md b/public/programs/doom/m-menu-c.md index 870cccf..cff2d23 100644 --- a/public/programs/doom/m-menu-c.md +++ b/public/programs/doom/m-menu-c.md @@ -9,156 +9,162 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "m-menu-c" order: 25 -description: "This file implements DOOM's menu system, a critical interface for player interaction, showcasing innovative design for its era." +description: "This file implements DOOM's menu system, a crucial interface for player interaction in one of gaming's most iconic titles." summary: - - point: "DOOM's menu system was designed to be intuitive and responsive, allowing players to navigate options efficiently." - link: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" - link_label: "DOOM (1993)" - - point: "The modular menu structure reflects early object-oriented principles in C programming." - link: "https://en.wikipedia.org/wiki/C_(programming_language)" - link_label: "C Programming Language" - - point: "DOOM's menu system influenced future game UI designs, emphasizing usability and minimalism." - link: "https://en.wikipedia.org/wiki/User_interface_design" - link_label: "User Interface Design" + - point: "Defines the structure and behavior of DOOM's menus" + link: "https://en.wikipedia.org/wiki/Menu_(computing)" + link_label: "Menu (computing)" + - point: "Introduces modular menu definitions for flexibility" + link: "https://en.wikipedia.org/wiki/Modular_programming" + link_label: "Modular programming" + - point: "Handles save/load functionality for game states" + link: "https://en.wikipedia.org/wiki/Save_(video_gaming)" + link_label: "Save (video gaming)" + - point: "Includes code for 'Read This!' help screens" + link: "https://doomwiki.org/wiki/Help_screen" + link_label: "Help screen in DOOM" + - point: "Optimizes for hardware constraints of early 1990s PCs" + link: "https://en.wikipedia.org/wiki/IBM_PC_compatible" + link_label: "IBM PC compatible" enhancements: - - id: "menu-data-structures" + - id: "gamma-correction-messages" + line_start: 106 + line_end: 133 + title: "Why DOOM Had Five Gamma Levels" + wikipedia_url: "https://en.wikipedia.org/wiki/Gamma_correction" + image_url: "" + image_caption: "" + content: "This block defines a set of strings for gamma correction messages, which inform players of the brightness level they are adjusting to. Gamma correction was critical for DOOM because monitors of the early 1990s varied widely in brightness and color fidelity. By providing five discrete levels, DOOM ensured players could tailor the visual experience to their hardware. John Carmack, known for his technical precision, likely implemented this feature to address complaints about visibility in darker levels. Gamma adjustment became a standard feature in games, influencing titles like Quake and Unreal. Today, similar functionality exists in modern games under 'brightness' or 'HDR settings,' showing how DOOM anticipated user needs in an era of hardware variability." + - id: "menu-item-struct-definition" line_start: 136 line_end: 535 - title: "How DOOM Structured Its Menus" - wikipedia_url: "https://en.wikipedia.org/wiki/Data_structure" + title: "The Struct That Made Menus Modular" + wikipedia_url: "https://en.wikipedia.org/wiki/Struct_(C_programming_language)" image_url: "" image_caption: "" - content: "This section defines the core data structures for DOOM's menu system, including `menuitem_t` and `menu_t`. These structures encapsulate menu items and their properties, such as status, name, hotkey, and associated routines, as well as the overall menu layout. In 1993, this approach was considered highly modular, allowing developers to easily add or modify menus without disrupting the rest of the code. The use of function pointers (`routine`) to handle menu actions was a clever way to decouple the menu's visual representation from its behavior, a technique that would later become standard in game development. The modularity here laid the groundwork for more sophisticated UI systems in later games, such as Quake and Unreal." - - id: "main-menu-definition" + content: "This section defines the `menuitem_t` struct, which encapsulates the properties of a single menu item, including its status, name, associated action routine, and hotkey. By using a struct, DOOM's developers made the menu system modular and extensible. This approach allowed easy addition or modification of menu items without rewriting large portions of code. In the early 1990s, modular programming was gaining traction as a way to manage complexity in increasingly sophisticated software. John Carmack's use of structs here reflects his focus on clean, maintainable code. The modular menu system influenced later games, including Quake and Half-Life, which adopted similar approaches for their user interfaces." + - id: "menu-definition-main-menu" line_start: 538 line_end: 551 - title: "The Main Menu: A Gateway to DOOM" - wikipedia_url: "https://en.wikipedia.org/wiki/Menu_(computing)" + title: "Main Menu and Episode Selection: DOOM's Declarative UI Tables" + wikipedia_url: "https://doomwiki.org/wiki/Menu" image_url: "" image_caption: "" - content: "The `MainMenu` array and `MainDef` structure define DOOM's main menu, including options like New Game, Load Game, and Quit. Each menu item is associated with a function pointer, enabling dynamic behavior based on user input. This design reflects the constraints of the era, where memory and processing power were limited, necessitating efficient and straightforward implementations. The menu's layout and functionality were designed to be intuitive, ensuring players could quickly access game features. This approach influenced the design of menus in later games, emphasizing simplicity and usability." - - id: "episode-selection-menu" + content: "This block defines both the MainMenu and EpisodeMenu tables using the menuitem_t and menu_t structs, establishing the game's top-level navigation as plain data rather than procedural code. MainMenu lists New Game, Options, Load Game, Save Game, Read This, and Quit DOOM, each bound to a handler function and a hotkey character, while EpisodeMenu provides entries for the four episodes with their M_EPIx graphic patches. By encoding the entire menu hierarchy as static initializer tables, id Software made it trivially easy to reconfigure menus for shareware, registered, and commercial editions at runtime in M_Init. The episodic structure was commercially significant: DOOM shipped in chapters that players could buy incrementally, a model that prefigured modern downloadable content. This data-driven approach to UI, where menu shape is described rather than scripted, became a common pattern in later game engines." + - id: "save-game-menu" line_start: 555 line_end: 571 - title: "Selecting Episodes in DOOM" - wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" + title: "Saving Progress in the Depths of Hell" + wikipedia_url: "https://en.wikipedia.org/wiki/Save_(video_gaming)" image_url: "" image_caption: "" - content: "The `EpisodeMenu` and `EpiDef` structures define the episode selection menu, allowing players to choose between different chapters of the game. This menu reflects DOOM's episodic structure, a design choice influenced by the shareware distribution model popular in the early 1990s. By offering a free episode and charging for additional ones, id Software could reach a wide audience while monetizing the game's full experience. The episodic menu design also influenced the structure of later games, including expansions and DLCs, where content is segmented into distinct chapters or levels." - - id: "save-load-system" + content: "This block defines the save game menu, where players can select a slot to save their progress. Saving was a critical feature in DOOM, allowing players to tackle its challenging levels without fear of losing progress. The save system was designed to be straightforward, with slots represented by strings that could be updated dynamically. In the early 1990s, save systems were becoming standard in PC games, but DOOM's implementation stood out for its reliability and ease of use. This approach influenced later games, including Quake and Unreal, which expanded on the concept with autosave and checkpoint systems. Today, save systems are ubiquitous, but DOOM's simplicity remains a benchmark for usability." + - id: "quick-save-and-load" line_start: 575 line_end: 588 - title: "The Save and Load System: Persistence in DOOM" + title: "The Shortcut That Saved Players' Lives" wikipedia_url: "https://en.wikipedia.org/wiki/Save_(video_gaming)" image_url: "" image_caption: "" - content: "The `M_ReadSaveStrings`, `M_DrawLoad`, and `M_LoadSelect` functions implement DOOM's save and load system, allowing players to persist their progress. Save files are read from disk and displayed in the menu, with empty slots marked accordingly. This system was designed to be robust and user-friendly, ensuring players could easily manage their save data. The reliance on file I/O operations (`open`, `read`, `close`) reflects the low-level programming practices of the time, where developers interacted directly with the operating system. This approach influenced future games, where save systems became increasingly sophisticated, incorporating features like autosave and cloud storage." - - id: "quick-save-load" + content: "This section implements quick save and quick load functionality, allowing players to save or restore progress with minimal interruption. Quick save was a revolutionary feature in DOOM, providing a safety net during intense gameplay. John Carmack likely introduced this feature to address the game's difficulty and player frustration. Quick save became a staple in PC gaming, influencing titles like Half-Life and Skyrim. Its impact is still felt today, with modern games offering similar functionality under names like 'checkpoint' or 'autosave.' DOOM's quick save system exemplifies how small quality-of-life features can significantly enhance the gaming experience." + - id: "read-this-help-screens" line_start: 590 line_end: 603 - title: "Quick Save and Load: Speeding Up Gameplay" - wikipedia_url: "https://en.wikipedia.org/wiki/Save_(video_gaming)" + title: "The Help Screens That Explained Hell" + wikipedia_url: "https://doomwiki.org/wiki/Help_screen" image_url: "" image_caption: "" - content: "The `M_QuickSave` and `M_QuickLoad` functions provide a streamlined way for players to save and load their progress without navigating the full menu system. This feature was a response to the fast-paced nature of DOOM, where players needed to quickly resume gameplay after a mistake or interruption. Quick save/load systems became a staple in gaming, emphasizing convenience and reducing downtime. The implementation here reflects id Software's focus on player experience, ensuring the game remained engaging and accessible." - - id: "help-screens" + content: "This section defines the 'Read This!' help screens, which provided players with instructions and context for DOOM's gameplay. The screens were implemented as static images, reflecting the hardware limitations of the era. These help screens were crucial for onboarding new players, especially those unfamiliar with first-person shooters. The decision to include them highlights id Software's commitment to accessibility. The 'Read This!' screens influenced later games, which adopted in-game tutorials and tooltips to guide players. Today, onboarding has evolved into interactive tutorials and dynamic hints, but DOOM's static help screens remain a testament to early user experience design in gaming." + - id: "menu-sound-integration" line_start: 606 line_end: 625 - title: "Help Screens: Guiding Players Through DOOM" - wikipedia_url: "https://en.wikipedia.org/wiki/User_guide" + title: "How DOOM's menus got their sound" + wikipedia_url: "https://en.wikipedia.org/wiki/Sound_effect" image_url: "" image_caption: "" - content: "The `M_DrawReadThis1` and `M_DrawReadThis2` functions display help screens, providing players with instructions and credits. These screens were essential in an era where games often lacked comprehensive manuals, relying instead on in-game guidance. The use of `V_DrawPatchDirect` to render graphics reflects the low-level graphics programming typical of the time. The inclusion of help screens highlights id Software's commitment to accessibility, ensuring players could understand the game's mechanics and context. This approach influenced later games, where tutorials and in-game guides became standard features." - - id: "sound-volume-control" + content: "The `M_Sound` function sets up the sound menu in DOOM, allowing players to adjust sound-related settings. This function is a simple entry point that transitions to the `SoundDef` menu configuration. In the early 1990s, sound design in games was becoming increasingly important, with titles like DOOM leading the charge. The ability to control sound effects and music volume was a significant feature, especially given the variety of sound hardware available at the time. John Carmack and the team ensured that DOOM's audio settings were accessible and intuitive, reflecting the growing emphasis on immersive experiences. This approach influenced later games, which adopted similar menu structures for audio settings. Today, sound customization is a standard feature in game menus, and DOOM's implementation helped set that precedent." + - id: "thermometer-slider-visualization" line_start: 627 line_end: 638 - title: "How DOOM Let Players Adjust Sound Levels" - wikipedia_url: "https://en.wikipedia.org/wiki/Volume_control" + title: "The thermometer slider for sound effects" + wikipedia_url: "https://en.wikipedia.org/wiki/Graphical_user_interface" image_url: "" image_caption: "" - content: "This section implements sound volume control for both sound effects and music within DOOM's menu system. The functions `M_SfxVol` and `M_MusicVol` allow players to increase or decrease volume levels, constrained between 0 and 15. These values are then passed to `S_SetSfxVolume` and `S_SetMusicVolume`, which adjust the game's audio output. In 1993, sound cards were becoming more common in consumer PCs, but their capabilities varied widely. By providing granular control over sound levels, DOOM ensured compatibility with a range of hardware setups, from basic PC speakers to advanced sound cards like the Sound Blaster. This approach influenced later games, which adopted similar volume control mechanisms in their menus." - - id: "menu-rendering-and-episode-selection" - line_start: 640 - line_end: 653 - title: "Cached Patches and the Episode Hack Behind DOOM's New Game Menu" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + content: "The `M_SfxVol` function adjusts the sound effects volume using a thermometer-style slider. Players can increase or decrease the volume, and the changes are immediately reflected visually and audibly. This design was clever for its time, providing a clear and interactive way to modify settings. The thermometer slider was a graphical innovation that worked well on the limited resolution of 1990s monitors. It also demonstrated the team's understanding of user experience, as visual feedback was crucial for intuitive interaction. This technique influenced GUI design in games and software, where sliders became a common way to adjust settings. Modern games still use sliders for audio, graphics, and other configurations, showing the lasting impact of DOOM's menu design." + - id: "menu-sound-effects" + line_start: 1079 + line_end: 1092 + title: "The sound effects that say goodbye" + wikipedia_url: "https://en.wikipedia.org/wiki/Sound_effect" image_url: "" image_caption: "" - content: "This section handles both the visual rendering of the New Game menu and the conditional logic that controls which episodes a player can actually reach. On the rendering side, `M_DrawNewGame` uses `V_DrawPatchDirect` together with `W_CacheLumpName` to pull pre-loaded graphical patches from memory and stamp them directly into the frame buffer. There is no on-demand disk access during rendering — all assets are cached in advance, keeping menu transitions smooth even on slow hard drives typical of 1993. The same approach applied to skill-level artwork, skill names, and every other graphical element in the menu tree. On the selection side, `M_Episode` wraps episode choice with version-aware guards: shareware players trying to pick episode two see an error message and bounce back to episode one, while retail players proceed normally. A separate special case handles the fourth episode added in Ultimate DOOM, printing a blunt error comment in the code that reflects the rushed nature of the addition. This dual pattern — cache assets aggressively and gate content by edition — reflects id Software's practical approach to supporting multiple commercial releases from a single codebase. The cached-patch rendering model influenced Quake and the GoldSrc engine, and edition-specific content gating became standard practice in games that shipped in shareware, registered, and retail tiers." - - id: "menu-string-rendering" + content: "The `M_QuitResponse` function plays sound effects when a player chooses to quit DOOM. Depending on the game mode, it selects a sound from one of two predefined arrays, creating a memorable auditory experience. This feature added personality to the game, making even the act of quitting feel thematic and engaging. Sound effects were a vital part of DOOM's immersive design, and the team paid attention to every detail, including the menu interactions. This approach influenced the use of sound in game menus, where auditory feedback became a standard practice. Modern games often use sound effects to enhance menu navigation and interactions, continuing the tradition established by DOOM." + - id: "custom-font-rendering" line_start: 1251 line_end: 1270 - title: "Rendering Text in DOOM's Menus" + title: "Rendering text with custom bitmap fonts" wikipedia_url: "https://en.wikipedia.org/wiki/Bitmap_font" image_url: "" image_caption: "" - content: "The functions `M_StringWidth`, `M_StringHeight`, and `M_WriteText` calculate dimensions and render text using DOOM's bitmap font system. Each character's width and height are determined dynamically, allowing precise placement of text elements. This approach was necessary for the game's custom font, which was stored as graphical patches rather than scalable vectors. In an era when hardware lacked native text rendering capabilities, DOOM's method was both efficient and visually appealing. This technique influenced later engines, which adopted bitmap fonts for UI rendering before transitioning to vector-based systems." - - id: "menu-input-handling" + content: "The `M_StringWidth` and `M_StringHeight` functions calculate the dimensions of strings rendered with DOOM's custom bitmap font. This font was stored as graphical patches, each representing a character. The functions ensured that menu text was properly aligned and spaced, critical for the game's polished presentation. Bitmap fonts were a common choice for games of the era, as they were easy to render and fit well within the constraints of hardware like VGA graphics cards. The DOOM team leveraged this approach to create a consistent and readable menu interface. This technique influenced the use of bitmap fonts in later games, especially those targeting low-resolution displays. Even today, bitmap fonts are used in retro-style games and applications, a testament to their enduring utility." + - id: "joystick-mouse-menu-input" line_start: 1341 - line_end: 1617 - title: "How DOOM Handled Joystick and Mouse Input" + line_end: 1715 + title: "Handling joystick and mouse input in menus" wikipedia_url: "https://en.wikipedia.org/wiki/Input_device" image_url: "" image_caption: "" - content: "The `M_Responder` function processes input events from joysticks, mice, and keyboards, translating them into menu navigation commands. It uses thresholds and delays to ensure smooth interaction, such as waiting 30 milliseconds before processing rapid mouse movements. In 1993, input devices varied widely in quality and responsiveness, and DOOM's robust handling ensured compatibility across hardware. This input system influenced later games, which adopted similar techniques for handling diverse input devices. The function also includes keyboard shortcuts for quick access to menu functions, a feature that became standard in PC gaming." - - id: "menu-keyboard-navigation" - line_start: 1620 - line_end: 1712 - title: "How DOOM Made Menus Feel Responsive" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - image_url: "" - image_caption: "" - content: "This section handles keyboard input for navigating DOOM's menus. It processes arrow keys to move between menu items, the Enter key to select an option, and Escape or Backspace to exit or return to previous menus. The code ensures that invalid menu items are skipped, providing smooth navigation. Each key press triggers sound effects, enhancing user feedback. In 1993, responsive menus were critical for immersive gameplay, especially in fast-paced titles like DOOM. John Carmack and his team prioritized usability, ensuring players could quickly access settings or save/load options without frustration. This approach influenced later games, where intuitive menu systems became standard. The use of sound effects for feedback remains a common practice in modern UI design, seen in everything from video games to operating systems." - - id: "menu-initialization" + content: "The `M_Responder` function processes input events for DOOM's menu system, including keyboard, joystick, and mouse inputs. It maps these inputs to menu navigation actions, such as moving between options or selecting an item. This robust input handling was essential for supporting the diverse hardware configurations of the early 1990s. At the time, PC gaming hardware varied widely, with some players using keyboards, others using joysticks, and a growing number adopting mice. The DOOM team ensured that the menu system could accommodate all these input methods, enhancing accessibility and usability. This approach influenced future games, which adopted similar strategies to support multiple input devices. Today, flexible input handling is a standard feature in games, reflecting the legacy of early pioneers like DOOM." + - id: "menu-control-panel-activation" line_start: 1719 line_end: 1731 - title: "Starting the Control Panel: A Simple Reset" + title: "Why DOOM's Menu Was Always Ready" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `M_StartControlPanel` function initializes the main menu, setting the active menu to the main definition (`MainDef`) and restoring the last selected item. This ensures consistency when the menu is reopened. In the early 1990s, memory constraints and limited processing power required developers to carefully manage state transitions. By resetting the menu state, DOOM avoids glitches or inconsistencies, keeping the experience seamless for players. This technique of state management influenced later game engines, including id Software's own Quake engine, which expanded on these principles for more complex UI systems." - - id: "menu-drawing-routine" + content: "The `M_StartControlPanel` function activates DOOM's menu system, ensuring it is ready for user interaction. The code checks if the menu is already active to avoid redundant calls, then initializes the menu state by setting `menuactive` to 1 and pointing `currentMenu` to the main menu definition (`MainDef`). This modular approach reflects John Carmack's focus on efficiency and simplicity, hallmarks of DOOM's design. In 1993, user interfaces in games were often clunky and unresponsive, but DOOM's menu system stood out for its speed and clarity. Carmack's philosophy of 'fast and lean' coding influenced countless developers, and this function exemplifies that ethos. The modularity seen here became a standard in game development, inspiring frameworks like Unity and Unreal Engine to adopt similar principles for menu management." + - id: "menu-drawer-rendering" line_start: 1734 line_end: 1804 - title: "Rendering Menus Before the Game World" - wikipedia_url: "https://en.wikipedia.org/wiki/Computer_graphics" + title: "How DOOM Drew Menus Without Slowing Down" + wikipedia_url: "https://en.wikipedia.org/wiki/Video_game_graphics" image_url: "" image_caption: "" - content: "The `M_Drawer` function handles menu rendering, centering messages and drawing menu items and a skull cursor. It uses cached graphics (`PLAYPAL`) to minimize memory usage and improve performance. In the 1990s, efficient rendering was essential for games running on hardware like the Intel 486, which had limited graphical capabilities. DOOM's approach to menu rendering, separating it from the game world, allowed for smooth transitions and consistent visual quality. This separation of UI and game rendering influenced later engines, including Unreal Engine and Unity, which adopted similar modular designs to handle complex interfaces." - - id: "menu-clear-state" + content: "The `M_Drawer` function handles the rendering of DOOM's menu system, ensuring it appears correctly after the game view is rendered but before it is displayed to the player. It dynamically centers strings for messages, draws menu items using cached graphical assets, and animates a 'skull cursor' to indicate the current selection. This function showcases Carmack's mastery of hardware constraints; DOOM's menus were visually appealing yet computationally efficient, leveraging techniques like caching and direct memory manipulation. In the early 1990s, games often struggled to balance graphical fidelity with performance, but DOOM's approach set a new standard. The skull animation, a small but memorable detail, added personality to the interface and influenced later games like Quake and Unreal Tournament, which adopted similar visual flourishes in their menus." + - id: "menu-clear-state-reset" line_start: 1807 line_end: 1815 - title: "Clearing Menus: A Quick Escape" + title: "The Function That Closed DOOM's Menus" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `M_ClearMenus` function deactivates the menu, allowing players to return to gameplay. This simple yet effective design ensures that menus don't interfere with the game's flow. In DOOM, where action is constant, minimizing interruptions was crucial. The ability to quickly exit menus became a standard feature in games, influencing UI design in titles like Half-Life and Halo, where seamless transitions between menus and gameplay are expected." - - id: "menu-setup-next" + content: "The `M_ClearMenus` function deactivates the menu system by setting `menuactive` to 0. While simple, this function is crucial for transitioning back to gameplay seamlessly. It reflects the team's attention to detail in ensuring the menu system did not interfere with the game's fast-paced action. In the context of 1993, such transitions were often clunky in other games, but DOOM's smooth handling of menus contributed to its reputation for polish. This approach influenced later games, where menu systems became integral to gameplay flow, such as in RPGs like Baldur's Gate and action games like Half-Life." + - id: "menu-navigation-setup" line_start: 1820 line_end: 1827 - title: "Switching Menus Without Losing Context" - wikipedia_url: "https://en.wikipedia.org/wiki/User_interface" + title: "How DOOM Prepared for Menu Changes" + wikipedia_url: "https://en.wikipedia.org/wiki/Modular_programming" image_url: "" image_caption: "" - content: "The `M_SetupNextMenu` function transitions to a new menu while preserving the last selected item. This ensures continuity, allowing players to pick up where they left off. In the early 1990s, preserving context in UI design was a novel concept, as many applications reset state entirely when switching views. DOOM's approach influenced later games and software, where maintaining user context became a cornerstone of good design, seen in everything from RPGs to productivity tools." + content: "The `M_SetupNextMenu` function prepares the menu system for navigation to a new menu. It updates `currentMenu` to point to the new menu definition and sets the `itemOn` variable to the last selected item in the menu. This modular design allows for easy expansion of the menu system, a necessity given DOOM's multiple versions and expansions. In the early 1990s, modular programming was gaining traction as developers sought ways to manage increasingly complex software. DOOM's menu system exemplifies this approach, influencing later games and engines to adopt similar modular structures for UI management." - id: "menu-animation-ticker" line_start: 1830 line_end: 1840 - title: "Animating the Skull Cursor: A Simple Loop" - wikipedia_url: "https://en.wikipedia.org/wiki/Computer_animation" + title: "The Skull That Kept Moving" + wikipedia_url: "https://en.wikipedia.org/wiki/Animation" image_url: "" image_caption: "" - content: "The `M_Ticker` function animates the skull cursor by toggling between two frames every eight ticks. This adds a dynamic visual element to the menu, making it feel alive. Animation in menus was uncommon in 1993, as it required additional processing power. DOOM's use of simple frame toggling was an efficient way to achieve this effect. This technique influenced later games, where animated cursors and dynamic menus became standard, enhancing user engagement." - - id: "menu-initialization-and-mode-specific-hacks" + content: "The `M_Ticker` function handles the animation of the skull cursor in DOOM's menu system. It toggles the `whichSkull` variable to alternate between two graphical assets, creating a blinking effect, and resets the animation counter when it reaches zero. This small detail added a sense of liveliness to the menu, making it feel more dynamic and engaging. In 1993, such animations were rare in menu systems, which were often static and utilitarian. DOOM's animated menus influenced later games to incorporate more visually appealing and interactive interfaces, setting a precedent for modern UI design." + - id: "menu-initialization" line_start: 1843 line_end: 1891 - title: "Menu Initialization: Adapting to Game Modes" - wikipedia_url: "https://en.wikipedia.org/wiki/Shareware" + title: "How DOOM's Menus Adapted to Every Version" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `M_Init` function initializes the menu system, setting default values and adapting menus based on the game mode (e.g., shareware, retail). This conditional logic ensures that players see only relevant options, such as ads for additional episodes in the shareware version. In the 1990s, shareware distribution was a popular model, and DOOM's menu system reflects this, with tailored experiences for different versions. This adaptability influenced later games, where dynamic menus became a way to personalize user experiences based on game editions or DLC availability." + content: "The `M_Init` function initializes DOOM's menu system, setting up variables like `currentMenu`, `menuactive`, and `itemOn`. It also includes version-specific adjustments, such as modifying menu definitions for the commercial, shareware, registered, and retail versions of the game. This adaptability reflects the team's foresight in designing a menu system that could accommodate changes across multiple releases. In the early 1990s, game developers often struggled with version management, but DOOM's approach streamlined the process. This function influenced later games to adopt flexible initialization routines, ensuring compatibility across different platforms and versions. The modularity and adaptability seen here became a hallmark of professional game development." --- @@ -2054,4 +2060,5 @@ void M_Init (void) } } -``` + +``` \ No newline at end of file diff --git a/public/programs/doom/m-random-c.md b/public/programs/doom/m-random-c.md index b4dc5ab..3410da5 100644 --- a/public/programs/doom/m-random-c.md +++ b/public/programs/doom/m-random-c.md @@ -9,52 +9,52 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "m-random-c" order: 16 -description: "This file implements DOOM's random number generation, a critical component for gameplay unpredictability and replayability." +description: "This file implements DOOM's random number generation, a key component for gameplay unpredictability and AI behavior." summary: - - point: "Uses a precomputed lookup table for random numbers" - link: "https://en.wikipedia.org/wiki/Lookup_table" - link_label: "Lookup Table" - - point: "Implements deterministic and non-deterministic random number generators" - link: "https://en.wikipedia.org/wiki/Deterministic_system" - link_label: "Deterministic System" - - point: "Optimized for performance on 1990s consumer hardware" - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM (1993 Video Game)" + - point: "Uses a fixed lookup table for random numbers" + link: "https://en.wikipedia.org/wiki/Random_number_generation" + link_label: "Random number generation" + - point: "Two separate indices for deterministic and non-deterministic randomness" + link: "https://doomwiki.org/wiki/Random_number_generator" + link_label: "DOOM random number generator" + - point: "Influenced later game development practices for predictable randomness" + link: "https://en.wikipedia.org/wiki/Procedural_generation" + link_label: "Procedural generation" enhancements: - id: "random-number-lookup-table" - line_start: 55 - line_end: 55 - title: "The Lookup Table That Made Random Fast" - wikipedia_url: "https://en.wikipedia.org/wiki/Lookup_table" + line_start: 26 + line_end: 50 + title: "The Lookup Table That Made DOOM Unpredictable" + wikipedia_url: "https://doomwiki.org/wiki/Random_number_generator" image_url: "" image_caption: "" - content: "This section defines a 256-byte lookup table (`rndtable`) used to generate random numbers. Instead of relying on computationally expensive algorithms to produce randomness, DOOM precomputes a sequence of values and stores them in this array. By cycling through the table with a simple index increment, the game achieves fast and predictable random number generation. This approach was essential for performance on early 1990s hardware, where CPU cycles were precious, and memory access was faster than complex calculations. The table itself is hardcoded, ensuring consistency across gameplay sessions. This deterministic randomness was vital for debugging and multiplayer synchronization, as every player could experience identical random events under the same conditions. Lookup tables like this became a common optimization in games and embedded systems, influencing later titles and hardware design." - - id: "deterministic-random-generator" + content: "This section defines a static lookup table containing 256 precomputed random numbers ranging from 0 to 255. The table is used as the source for all random number generation in DOOM. By cycling through the table using indices, the game achieves a blend of deterministic and non-deterministic behavior. At the time, generating random numbers on-the-fly was computationally expensive, especially on the limited hardware of early 1990s PCs. Using a precomputed table allowed DOOM to maintain its fast-paced gameplay without taxing the CPU. The choice of 256 entries aligns with the efficiency of modulo arithmetic on binary systems, as 256 is a power of two. This approach was likely influenced by earlier games and simulations that relied on lookup tables for performance reasons. The table itself is hardcoded, ensuring consistency across different runs and platforms. This technique became a hallmark of DOOM's design, enabling predictable randomness for AI behavior and level generation while maintaining performance. Later games and engines, including Quake and Unreal, adopted similar strategies for procedural generation and AI logic, cementing the lookup table as a foundational tool in game development." + - id: "deterministic-random-function" line_start: 55 - line_end: 59 - title: "How DOOM Kept Randomness Predictable" - wikipedia_url: "https://en.wikipedia.org/wiki/Deterministic_system" + line_end: 60 + title: "Why DOOM Needed Two Random Generators" + wikipedia_url: "https://doomwiki.org/wiki/Random_number_generator" image_url: "" image_caption: "" - content: "The `P_Random` function provides deterministic random numbers by cycling through the `rndtable` using the `prndindex`. This ensures that the sequence of random numbers is reproducible, which was crucial for debugging and multiplayer consistency. In multiplayer mode, deterministic randomness allowed all players to experience identical random events, preventing desynchronization. At the time, this was a clever solution to the problem of maintaining fairness and consistency in networked gameplay, where computational resources were limited, and synchronization mechanisms were rudimentary. This deterministic approach influenced later multiplayer game engines, which adopted similar techniques to ensure synchronized gameplay across different machines." - - id: "non-deterministic-random-generator" + content: "The `P_Random` function accesses the `rndtable` using the `prndindex`, which is incremented and wrapped using a bitwise AND operation with 0xff (255). This ensures the index stays within the bounds of the table. The function is used for deterministic randomness, meaning the sequence of numbers it produces can be replicated exactly if the starting state is known. This was crucial for DOOM's multiplayer mode, where consistent behavior across networked machines was necessary to maintain synchronization. In the early 1990s, networked multiplayer gaming was still in its infancy, and ensuring deterministic behavior was a novel challenge. John Carmack and the id Software team designed this function to address that need, borrowing ideas from earlier deterministic simulation techniques. The use of deterministic randomness influenced later multiplayer game engines, where synchronization remains a critical concern. Developers studying DOOM's source code often cite `P_Random` as an elegant solution to a complex problem." + - id: "non-deterministic-random-function" line_start: 62 - line_end: 65 - title: "The Randomness That Kept DOOM Unpredictable" - wikipedia_url: "https://en.wikipedia.org/wiki/Random_number_generation" + line_end: 66 + title: "The Randomness That Kept DOOM Fresh" + wikipedia_url: "https://doomwiki.org/wiki/Random_number_generator" image_url: "" image_caption: "" - content: "The `M_Random` function generates random numbers for gameplay elements, such as enemy behavior and item drops. Unlike `P_Random`, which is deterministic, `M_Random` uses a separate index (`rndindex`) to cycle through the `rndtable`. This introduces non-deterministic randomness, adding unpredictability to the single-player experience. Players could encounter varied gameplay scenarios, enhancing replayability and immersion. In the early 1990s, this approach was innovative, as it balanced performance constraints with the need for engaging gameplay. The technique influenced later game designs, where controlled randomness became a staple for creating dynamic and memorable experiences." - - id: "resetting-random-state" + content: "The `M_Random` function is similar to `P_Random` but uses a separate index, `rndindex`, to access the `rndtable`. This function is used for non-deterministic randomness, which adds unpredictability to gameplay elements such as enemy behavior and environmental effects. Unlike `P_Random`, the sequence generated by `M_Random` is not intended to be replicated exactly, allowing for dynamic and varied experiences during gameplay. This separation of deterministic and non-deterministic randomness reflects id Software's deep understanding of game design. By isolating these two types of randomness, they ensured that DOOM could be both consistent in multiplayer and unpredictable in single-player. This design decision influenced later games that sought to balance deterministic mechanics with emergent gameplay. Developers of roguelike games and procedural generation systems often reference DOOM's approach as a foundational example." + - id: "reset-random-indices" line_start: 68 line_end: 71 - title: "Why DOOM Could Reset Randomness" - wikipedia_url: "https://en.wikipedia.org/wiki/State_(computer_science)" + title: "How DOOM Reset Its Random State" + wikipedia_url: "https://doomwiki.org/wiki/Random_number_generator" image_url: "" image_caption: "" - content: "The `M_ClearRandom` function resets the indices (`rndindex` and `prndindex`) to zero, effectively restarting the random number sequence. This was useful for ensuring consistent behavior during specific gameplay scenarios, such as restarting a level or initializing a new game. By resetting the random state, developers could guarantee predictable outcomes for testing and debugging. This design reflects the meticulous attention to detail required to develop games on constrained hardware, where reproducibility was essential for development and quality assurance. The concept of resetting random states has persisted in modern game engines, where it is used for debugging and controlled randomness in procedural generation." + content: "The `M_ClearRandom` function resets both `rndindex` and `prndindex` to zero, effectively restarting the random number sequence from the beginning. This is useful for ensuring consistent behavior during certain game states, such as restarting a level or initializing a new game. By resetting the indices, DOOM could guarantee that the same sequence of random numbers would be used, preserving the deterministic aspects of gameplay when needed. This approach reflects the constraints of early game development, where memory and computational resources were limited, and maintaining control over randomness was essential. The ability to reset random states became a common feature in game engines, enabling developers to debug and test specific scenarios reliably. Modern engines like Unity and Unreal include similar functionality for managing random states, demonstrating the lasting influence of DOOM's design." --- @@ -130,4 +130,8 @@ void M_ClearRandom (void) { rndindex = prndindex = 0; } -``` + + + + +``` \ No newline at end of file diff --git a/public/programs/doom/p-doors-c.md b/public/programs/doom/p-doors-c.md index 66dc12a..b35cb37 100644 --- a/public/programs/doom/p-doors-c.md +++ b/public/programs/doom/p-doors-c.md @@ -9,58 +9,68 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-doors-c" order: 26 -description: "This file contains the door animation logic for DOOM, a groundbreaking 1993 game that defined the FPS genre and pushed the limits of consumer hardware." +description: "This file contains the code responsible for handling door animations and mechanics in DOOM, showcasing techniques that pushed the limits of 1990s hardware." summary: - - point: "Introduces vertical door mechanics central to DOOM's level design" + - point: "Introduces vertical and sliding door mechanics, a key part of DOOM's immersive level design." link: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Implements locked door logic tied to collectible keys" - link: "https://doomwiki.org/wiki/Keys" - link_label: "DOOM Keys" - - point: "Includes abandoned sliding door code, hinting at unused features" - link: "https://doomwiki.org/wiki/Sliding_doors" - link_label: "Sliding Doors in DOOM" - - point: "Optimized for real-time gameplay on 1993 hardware" - link: "https://en.wikipedia.org/wiki/Intel_80486" - link_label: "Intel 80486" - - point: "Demonstrates modular 'thinker' system for game object behaviors" - link: "https://doomwiki.org/wiki/Thinker" - link_label: "Thinker System" + - point: "Optimizes door animations for real-time gameplay on limited hardware." + link: "https://en.wikipedia.org/wiki/Real-time_computing" + link_label: "Real-time computing" + - point: "Demonstrates modular programming with reusable door types and behaviors." + link: "https://en.wikipedia.org/wiki/Modular_programming" + link_label: "Modular programming" enhancements: + - id: "sliding-door-frame-data" + line_start: 42 + line_end: 51 + title: "Why Sliding Doors Were Abandoned" + wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" + image_url: "" + image_caption: "" + content: "This section defines sliding door frame data, including texture names for front and back frames of sliding doors. Sliding doors were intended for DOOM II but were ultimately abandoned, as noted in the comments. The decision likely stemmed from technical constraints or time pressures during development. Sliding doors would have added complexity to the rendering engine, which was already optimized for vertical movement and limited texture animations. While the sliding door concept was shelved, the modular approach to defining door types and animations influenced later games, including Quake, where more advanced door mechanics were implemented." - id: "vertical-door-mechanics" line_start: 55 - line_end: 498 - title: "How DOOM Made Doors Feel Alive" - wikipedia_url: "https://doomwiki.org/wiki/Door" + line_end: 197 + title: "The Code Behind DOOM's Vertical Doors" + wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" image_url: "" image_caption: "" - content: "The `T_VerticalDoor` function is the heart of DOOM's door mechanics, handling the opening, closing, and waiting states of vertical doors. It uses a state machine approach, where the door's behavior changes based on its current state (`direction`) and type (`normal`, `blazeRaise`, etc.). The function also integrates sound effects to enhance the player's experience, such as the distinct 'door open' and 'door close' sounds. At the time, real-time animation and interaction with the environment were rare, and this implementation helped make DOOM's world feel dynamic and immersive. In 1993, DOOM was designed to run on consumer PCs equipped with Intel 80486 processors, which had limited computational power. John Carmack's programming expertise ensured that even complex mechanics like doors were optimized for performance. The modular 'thinker' system allowed objects like doors to have their own behavior routines, making the codebase extensible and easier to manage. The vertical door mechanics became a staple in level design for FPS games, influencing titles like Quake and Half-Life. The state machine approach and modular object behavior inspired game developers to adopt similar systems for interactive environments. Today, this technique is foundational in game engines like Unity and Unreal Engine, where state-driven animations are ubiquitous." - - id: "locked-door-logic" + content: "The `T_VerticalDoor` function handles the behavior of vertical doors, including their opening, closing, and waiting states. Each door type—such as `blazeRaise` or `close30ThenOpen`—is defined with unique properties, including speed and sound effects. This modular design allowed DOOM's levels to feature varied door behaviors without duplicating code. In 1993, hardware constraints meant developers had to carefully balance functionality with performance. John Carmack's focus on efficiency ensured that even complex mechanics like doors ran smoothly on consumer PCs. The vertical door system inspired similar mechanics in later games, such as Half-Life, which expanded on interactive environments." + - id: "locked-door-checks" line_start: 200 line_end: 259 - title: "The Keys That Locked DOOM's Levels" - wikipedia_url: "https://doomwiki.org/wiki/Keys" + title: "How DOOM Checked for Keycards" + wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" image_url: "" image_caption: "" - content: "The `EV_DoLockedDoor` function implements the logic for locked doors in DOOM, requiring players to collect specific keys (blue, red, or yellow) to progress. If the player lacks the necessary key, the game provides feedback through a message and a sound effect ('oof'). This mechanic added a layer of strategy and exploration to DOOM's fast-paced gameplay, encouraging players to thoroughly explore levels. In the early '90s, key-based progression was a common design choice in adventure games, but DOOM adapted it to the FPS genre, integrating it seamlessly into its labyrinthine level design. The locked door mechanic was a collaborative effort by id Software's team, including John Romero, who focused on level design and player experience. This approach influenced countless games, from Duke Nukem 3D to modern titles like DOOM Eternal. The concept of collectible items unlocking new areas became a staple in game design, appearing in genres ranging from RPGs to platformers. The feedback system—providing both visual and auditory cues—set a precedent for clear player communication that remains standard in game design today." - - id: "spawn-door-close-in-30" + content: "The `EV_DoLockedDoor` function checks whether a player has the required keycard or skull to open a locked door. If the player lacks the necessary item, a message is displayed, and a sound effect (`sfx_oof`) is played to indicate failure. This mechanic added an element of progression and exploration to DOOM's levels, encouraging players to search for keys to advance. The use of keycards became a staple in first-person shooters, appearing in games like Duke Nukem 3D and System Shock. The implementation here is straightforward but effective, leveraging DOOM's modular design to integrate seamlessly with the game's other mechanics." + - id: "door-spawning-in-30-seconds" line_start: 501 - line_end: 521 - title: "The Door That Waited 30 Seconds" - wikipedia_url: "https://doomwiki.org/wiki/Door" + line_end: 563 + title: "The Countdown to Closing Doors" + wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" + image_url: "" + image_caption: "" + content: "The `P_SpawnDoorCloseIn30` function spawns a door that automatically closes after 30 seconds. This mechanic added dynamic elements to DOOM's levels, creating time-sensitive challenges for players. The countdown is implemented using a `topcountdown` variable, which decrements every game tick. Such time-based mechanics were innovative for 1993, as they required precise synchronization with the game's real-time engine. The concept of timed events influenced later games, including Unreal Tournament, which used similar techniques for dynamic level interactions." + - id: "sliding-door-initialization" + line_start: 565 + line_end: 602 + title: "The Sliding Door Textures That Never Were" + wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_SpawnDoorCloseIn30` function creates a door that automatically closes after 30 seconds. This mechanic added tension and urgency to DOOM's gameplay, forcing players to act quickly or risk being trapped. The countdown is implemented using the game's 'thinker' system, which schedules behaviors for objects in the game world. Timed events like this were innovative in 1993, as most games relied on static environments. The ability to dynamically alter the game world based on time was a testament to John Carmack's programming ingenuity. It showcased how DOOM's engine could handle complex interactions efficiently, even on limited hardware. Timed mechanics like these became a hallmark of interactive level design, influencing games like Portal and Half-Life. The concept of environmental changes tied to player actions or time constraints is now a common feature in modern game design, demonstrating DOOM's lasting impact on the industry." - - id: "abandoned-sliding-door-code" - line_start: 553 - line_end: 762 - title: "The Sliding Doors That Never Opened" - wikipedia_url: "https://doomwiki.org/wiki/Sliding_doors" + content: "The `P_InitSlidingDoorFrames` function initializes texture data for sliding doors, mapping texture names to numerical IDs. This system was designed for DOOM II but ultimately abandoned, as noted in the comments. The function highlights the modular approach id Software took, allowing new mechanics to be added without disrupting existing systems. While sliding doors never appeared in DOOM II, the groundwork laid here influenced later games, such as Quake, which featured more advanced door animations and interactions." + - id: "sliding-door-animation" + line_start: 624 + line_end: 707 + title: "Animating Sliding Doors Frame by Frame" + wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" image_url: "" image_caption: "" - content: "The sliding door code, marked as abandoned, reveals an intriguing glimpse into DOOM's development process. The `EV_SlidingDoor` function and related routines were intended to implement horizontally sliding doors, complete with animated textures. However, these features were ultimately unused in the final game, possibly due to time constraints or hardware limitations. Sliding doors were a novel idea in 1993, as most games featured simple vertical or static doors. The code includes logic for animating textures frame by frame and removing blocking properties from the door once fully opened. This level of detail highlights id Software's ambition to push the boundaries of environmental interactivity. Although unused in DOOM, sliding doors appeared in later games like Quake and Unreal, where more advanced engines could support such features. The concept of animated environmental objects influenced modern game engines, where dynamic textures and object states are standard. This abandoned code serves as a reminder of the iterative nature of game development and the trade-offs developers must make to meet deadlines." + content: "The `T_SlidingDoor` function animates sliding doors frame by frame, updating textures and handling transitions between opening, waiting, and closing states. This detailed animation system was ahead of its time, showcasing id Software's ambition to push the limits of level interactivity. However, sliding doors were ultimately abandoned for DOOM II, likely due to technical or time constraints. The frame-by-frame approach influenced later games, such as Quake and Unreal, which used similar techniques for animating complex objects in 3D environments." --- @@ -828,4 +838,4 @@ EV_SlidingDoor } } #endif -``` +``` \ No newline at end of file diff --git a/public/programs/doom/p-enemy-c.md b/public/programs/doom/p-enemy-c.md index 06ca618..1dc29ac 100644 --- a/public/programs/doom/p-enemy-c.md +++ b/public/programs/doom/p-enemy-c.md @@ -9,210 +9,210 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-enemy-c" order: 8 -description: "This file implements enemy AI in DOOM, showcasing groundbreaking techniques for real-time decision-making in 3D environments." +description: "This file contains the AI logic for enemies in DOOM, defining their behavior, movement, and interactions with players." summary: - - point: "Introduces recursive sound propagation for alerting enemies" + - point: "Defines enemy movement directions and lookup tables for AI navigation" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Uses direction-based movement logic for enemy pathfinding" - link: "https://en.wikipedia.org/wiki/Pathfinding" - link_label: "Pathfinding" - - point: "Implements state-based AI routines for enemy behavior" - link: "https://en.wikipedia.org/wiki/Finite-state_machine" - link_label: "Finite-state machine" - - point: "Optimizes AI decisions based on player proximity and visibility" + - point: "Implements recursive sound propagation to alert monsters" link: "https://en.wikipedia.org/wiki/Artificial_intelligence_in_video_games" link_label: "AI in video games" - - point: "Defines lookup tables for directional movement and pathfinding" - link: "https://en.wikipedia.org/wiki/Lookup_table" - link_label: "Lookup table" + - point: "Introduces melee and missile range checks for enemy attacks" + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM gameplay mechanics" + - point: "Optimizes enemy pathfinding with direction-based logic" + link: "https://en.wikipedia.org/wiki/Pathfinding" + link_label: "Pathfinding" + - point: "Includes special routines for unique enemy behaviors (e.g., Commander Keen death sequence)" + link: "https://doomwiki.org/wiki/Commander_Keen_(Doom_II)" + link_label: "Commander Keen in DOOM II" enhancements: - - id: "directional-movement-lookup-table" - line_start: 50 - line_end: 78 - title: "The Lookup Table That Guides Enemy Movement" - wikipedia_url: "https://en.wikipedia.org/wiki/Lookup_table" + - id: "enemy-direction-enum" + line_start: 170 + line_end: 191 + title: "Eight-Direction Movement and Melee Range Check" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "This section defines two lookup tables: `opposite` and `diags`, which map directional movement for enemies. These tables allow enemies to calculate their next move based on their current direction and proximity to the player. The `opposite` table helps enemies reverse direction when blocked, while `diags` facilitates diagonal movement. In 1993, lookup tables were a common optimization technique, as they avoided computationally expensive calculations during runtime. John Carmack, known for his mastery of low-level optimization, likely included these tables to ensure smooth and responsive enemy behavior on the limited hardware of the era. This approach influenced later games, where lookup tables became standard for AI pathfinding and movement logic." - - id: "recursive-sound-alert" - line_start: 104 - line_end: 165 - title: "How Noise Wakes Up DOOM's Monsters" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + content: "This region establishes two foundational pieces of DOOM's enemy AI. The `dirtype_t` enumeration gives every monster nine legal heading states: eight compass directions plus DI_NODIR for a stopped or blocked actor. Restricting movement to discrete angles matched the grid-aligned map geometry and kept pathfinding arithmetic cheap on 486-class hardware. Immediately downstream, `P_CheckMeleeRange` decides whether a melee attack is valid by computing an approximate distance with P_AproxDistance, subtracting the target's radius, and confirming line-of-sight. The MELEERANGE threshold of roughly 64 map units was tuned so melee-capable monsters like the Imp and Demon feel dangerous at close quarters without landing hits through walls or at implausible distances. These two pieces work together: the direction system drives an enemy toward the player, and the range check fires the claw once proximity is confirmed, creating the relentless up-close pressure that defined DOOM's combat feel." + - id: "recursive-sound-propagation" + line_start: 271 + line_end: 334 + title: "Sound Propagation, Monster Movement, and Enemy Navigation" + wikipedia_url: "https://en.wikipedia.org/wiki/Artificial_intelligence_in_video_games" image_url: "" image_caption: "" - content: "The `P_RecursiveSound` function propagates sound through connected sectors, alerting monsters to the player's presence. It uses recursion to traverse adjacent sectors, respecting sound-blocking lines that prevent sound from traveling through walls. This mechanic adds realism and tension to the game, as firing a weapon can wake up nearby enemies. In the early 1990s, such environmental interaction was rare in games, making DOOM's AI feel dynamic and responsive. This technique inspired similar sound-based AI systems in later games, such as Half-Life (1998), where enemies react to player actions in the environment." + content: "This region covers two complementary systems that give DOOM's monsters situational awareness and the ability to act on it. `P_RecursiveSound` floods alert information through the sector graph when a player fires: it walks two-sided lines outward from the noise source, stamping each reachable sector with the current soundtarget. Lines flagged ML_SOUNDBLOCK are treated as acoustic barriers; the traversal still crosses them but increments a soundblocks counter, so a monster two barriers away is harder to wake than one in the same room. This gave level designers a real tool for controlling alert radius without scripting. Below the sound code, `P_Move` drives a monster one step in its current movedir using the `xspeed` and `yspeed` tables and P_TryMove for collision. Floating monsters adjust their Z when blocked rather than stopping, and the function activates any special lines crossed, so enemies can open doors mid-chase. The pairing of a sector-flood alert with a table-driven step mover was an economical way to make monsters feel aware and persistent on hardware where frame budgets measured in milliseconds left little room for elaborate pathfinding." - id: "noise-alert-system" - line_start: 152 - line_end: 165 - title: "The Function That Makes Monsters Communicate" + line_start: 337 + line_end: 357 + title: "The chain reaction that wakes the horde" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "`P_NoiseAlert` is called when a monster detects a player, triggering a chain reaction that alerts other monsters in nearby sectors. It uses the `P_RecursiveSound` function to propagate the alert. This system creates a sense of interconnectedness among enemies, making the game world feel alive. In the early 1990s, AI systems were often isolated, but DOOM's approach to shared awareness among monsters was groundbreaking. This concept influenced later games like Left 4 Dead (2008), where enemy AI coordinates attacks based on player actions." - - id: "melee-range-check" - line_start: 170 - line_end: 191 - title: "How DOOM Decides If You're Too Close" - wikipedia_url: "https://en.wikipedia.org/wiki/Artificial_intelligence_in_video_games" - image_url: "" - image_caption: "" - content: "`P_CheckMeleeRange` determines whether an enemy is close enough to attack the player with a melee strike. It calculates the distance between the enemy and the player, factoring in the player's radius and checking line-of-sight. This ensures that melee attacks are realistic and only occur when the player is within reach. In 1993, such precise distance calculations were rare in games, as most relied on simple proximity checks. DOOM's implementation set a precedent for realistic enemy behavior, influencing later titles like Quake (1996) and Unreal (1998)." + content: "The `P_NoiseAlert` function alerts monsters to a player's presence when noise is detected. It sets a global `soundtarget` and triggers `P_RecursiveSound` to propagate the alert through connected sectors. This system creates dynamic gameplay moments, where a single gunshot can lead to a cascade of enemies converging on the player's location. The idea of noise-based AI was groundbreaking in 1993, adding depth to enemy behavior. Later games like Half-Life expanded on this concept, using sound to trigger complex AI routines and environmental interactions." - id: "missile-range-check" line_start: 193 - line_end: 255 - title: "The Algorithm Behind DOOM's Missile Attacks" - wikipedia_url: "https://en.wikipedia.org/wiki/Artificial_intelligence_in_video_games" - image_url: "" - image_caption: "" - content: "`P_CheckMissileRange` evaluates whether an enemy should initiate a missile attack. It checks line-of-sight, reaction time, and distance to the player, adjusting the decision based on the enemy type. For example, certain enemies like the Cyberdemon have specific distance thresholds for missile attacks. This level of detail in AI decision-making was revolutionary in 1993, as most games used simpler attack logic. By tailoring behavior to enemy types, DOOM created a diverse and challenging gameplay experience, influencing AI systems in games like Halo (2001) and Call of Duty (2003)." - - id: "enemy-movement-logic" - line_start: 271 - line_end: 334 - title: "The Code That Makes DOOM's Monsters Move" - wikipedia_url: "https://en.wikipedia.org/wiki/Pathfinding" - image_url: "" - image_caption: "" - content: "`P_Move` handles enemy movement in DOOM, calculating new positions based on direction and speed. It checks for obstacles, adjusts height for floating enemies, and opens doors if necessary. This function ensures that enemies navigate the environment effectively, creating dynamic encounters. In 1993, pathfinding algorithms were often simplistic, but DOOM's approach combined movement logic with environmental interaction, setting a new standard for AI behavior. This influenced later games like System Shock (1994) and Deus Ex (2000), which emphasized intelligent navigation in complex environments." - - id: "new-chase-direction" - line_start: 362 - line_end: 488 - title: "How DOOM's Monsters Find Their Way" - wikipedia_url: "https://en.wikipedia.org/wiki/Pathfinding" + line_end: 208 + title: "Pathfinding Lookup Tables and Missile Range Decision" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "`P_NewChaseDir` calculates a new direction for enemies to pursue the player. It considers the player's position, avoids blocked paths, and uses randomness to add unpredictability. This function ensures that enemies remain persistent and challenging, even when direct paths are unavailable. In the early 1990s, such adaptive pathfinding was rare, as most games relied on static movement patterns. DOOM's implementation influenced later titles like StarCraft (1998) and Warcraft III (2002), which used similar techniques for unit navigation." + content: "This range opens with the `opposite` and `diags` lookup tables that power P_NewChaseDir. The `opposite` array maps each of the eight movement directions to its reverse, letting a monster quickly determine which heading it should avoid when retreating or re-routing. The `diags` array maps quadrant sign combinations to diagonal headings so the chasing code can pick the most direct angle toward a target in one table lookup instead of a branch tree. Both tables exemplify the era's preference for precomputed data over runtime arithmetic. The range closes with `P_CheckMissileRange`, which decides whether a ranged attack should fire. Beyond a simple distance test it factors in reaction time, a per-type bias (the Cyberdemon and Spider Mastermind back off at extreme range; the Revenant gets a range penalty so it stays mid-field), and a probabilistic roll so that monsters do not fire every legal frame, giving players a fighting chance to dodge. Together the LUTs and the missile gate illustrate how DOOM gave each monster a distinct tactical personality through layered, cheaply evaluated rules." - id: "look-for-players" - line_start: 492 + line_start: 362 line_end: 557 - title: "How DOOM's Monsters Spot You" - wikipedia_url: "https://en.wikipedia.org/wiki/Artificial_intelligence_in_video_games" + title: "How DOOM's monsters find you" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "`P_LookForPlayers` determines whether an enemy can target a player. It checks player visibility, health, and proximity, using angle calculations to limit detection to a 180-degree field of view unless specified otherwise. This function adds realism to enemy behavior, as monsters only react to players they can see. In 1993, such nuanced AI was rare, as most games used simple proximity checks. DOOM's implementation influenced later titles like Thief: The Dark Project (1998), which emphasized stealth and enemy awareness." - - id: "keen-die-special-event" + content: "The `P_LookForPlayers` function allows enemies to scan for players within their field of view. It checks player health, visibility, and proximity, ensuring that monsters only target viable players. The function incorporates a 180-degree field-of-view check for non-'allaround' enemies, adding realism to their behavior. This mechanic contributed to DOOM's immersive gameplay, where enemies react dynamically to player actions. The idea of AI vision cones influenced later games, such as Metal Gear Solid and Far Cry, where enemy awareness became a critical gameplay element." + - id: "keen-die-special" line_start: 560 line_end: 592 - title: "The Easter Egg That Opens Doors" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_II" + title: "The Easter egg that opens a door" + wikipedia_url: "https://doomwiki.org/wiki/Commander_Keen_(Doom_II)" image_url: "" image_caption: "" - content: "`A_KeenDie` is a special function tied to DOOM II's secret map 32. When all Commander Keens are defeated, it triggers a door with tag 666 to open. This Easter egg showcases id Software's playful approach to game design, adding hidden surprises for players to discover. Such scripted events became a hallmark of id Software's games, influencing later titles like Duke Nukem 3D (1996) and Half-Life (1998), which featured interactive environments and hidden secrets." - - id: "chase-and-attack" - line_start: 666 + content: "The `A_KeenDie` function is a special routine tied to DOOM II's secret level (Map 32). When all Commander Keen enemies are defeated, it triggers a door to open using tag 666. This Easter egg reflects id Software's playful approach to game design, incorporating references to their earlier games. The function scans all active 'thinkers' (game objects) to check if any Keen enemies remain alive, showcasing DOOM's flexible object management system. Easter eggs like this became a hallmark of id Software's games, inspiring developers to include hidden secrets and references in their own titles." + - id: "chase-state-logic" + line_start: 595 line_end: 775 - title: "The Code That Keeps Monsters Aggressive" - wikipedia_url: "https://en.wikipedia.org/wiki/Artificial_intelligence_in_video_games" + title: "The relentless pursuit of DOOM's enemies" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "`A_Chase` governs enemy pursuit and attack behavior. It handles movement direction, melee attacks, missile attacks, and target acquisition. Enemies adapt their actions based on proximity, visibility, and player behavior, creating dynamic and challenging encounters. This function exemplifies DOOM's state-based AI system, which was revolutionary in 1993. By combining movement, attack logic, and environmental interaction, DOOM set a new standard for enemy behavior in games, influencing titles like Quake (1996) and Unreal Tournament (1999)." - - id: "randomized-attack-angles" - line_start: 798 - line_end: 818 - title: "Randomized Angles Make Enemies Unpredictable" + content: "The `A_Chase` function defines the behavior of enemies in their chase state, where they pursue the player aggressively. It handles reaction times, target thresholds, and movement direction, ensuring that enemies adapt to changing circumstances. The function includes checks for melee and missile attacks, making combat dynamic and unpredictable. This relentless pursuit mechanic became a defining feature of DOOM, creating tension and excitement during gameplay. The chase state influenced AI design in later FPS games, where enemies exhibit similar persistence and adaptability." + - id: "face-target" + line_start: 778 + line_end: 795 + title: "How DOOM's monsters always face you" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `A_PosAttack` function defines the attack behavior for certain enemies, introducing randomness to the angle of their attacks. By adding and subtracting random values to the base angle, the enemies' shots appear less predictable, creating a more dynamic and challenging experience for players. This randomness was a clever way to simulate human-like unpredictability in enemy behavior without requiring complex AI calculations. In 1993, this approach was particularly effective given the constraints of consumer hardware, where performance was a critical concern. The technique influenced later games by demonstrating how simple randomness could enhance gameplay realism and tension." - - id: "shotgun-style-multi-attack" + content: "The `A_FaceTarget` function ensures that enemies always face their target during attacks. It calculates the angle between the enemy and the player, adjusting for shadow effects (e.g., invisibility). This mechanic adds realism to combat, as enemies visually track the player. The function reflects DOOM's attention to detail in creating immersive enemy behavior. Similar mechanics appeared in later games, where enemies use advanced targeting systems to enhance realism and challenge players." + - id: "shotgun-attack-with-random-spread" line_start: 820 line_end: 842 - title: "Enemies Mimic Shotgun Spread Attacks" + title: "Shotgun Attack with Random Spread" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + image_url: "" + image_caption: "" + content: "The `A_SPosAttack` function simulates a shotgun attack by firing three projectiles with randomized angles and damage values. This randomness adds unpredictability to enemy attacks, making gameplay more dynamic. In 1993, DOOM's developers were working with hardware constraints like the Intel 80486 processor, which lacked modern physics engines. To achieve immersive combat, John Carmack and the team implemented simple yet effective techniques like this randomized spread. The shotgun mechanic became iconic, influencing countless FPS games, including Quake and Call of Duty." + - id: "cyberdemon-missile-launch" + line_start: 968 + line_end: 975 + title: "Cyberdemon Missile Launch" + wikipedia_url: "https://en.wikipedia.org/wiki/Cyberdemon" + image_url: "" + image_caption: "" + content: "The `A_CyberAttack` function defines the Cyberdemon's missile attack, a signature move of one of DOOM's most memorable enemies. The function ensures the Cyberdemon faces its target before launching a missile. This straightforward implementation highlights the game's focus on clear, readable code while delivering impactful gameplay. The Cyberdemon's missile attack became a hallmark of boss design in FPS games, inspiring similar mechanics in later titles like Half-Life and Halo." + - id: "tracer-smoke-and-direction-adjustment" + line_start: 1020 + line_end: 1089 + title: "Tracer Smoke and Direction Adjustment" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `A_SPosAttack` function simulates a shotgun-like attack by firing multiple projectiles in slightly different directions. This is achieved by iterating three times and applying randomized offsets to the base angle for each shot. The result is a spread pattern that mimics the behavior of a shotgun blast, adding variety to enemy attacks. This design choice reflects DOOM's emphasis on creating diverse and memorable combat encounters. The shotgun spread attack became a staple in many FPS games, influencing weapon and enemy design in titles like Quake and Half-Life." - - id: "corpse-resurrection-mechanic" + content: "The `A_Tracer` function governs the behavior of homing projectiles, such as rockets. It spawns smoke puffs for visual feedback and adjusts the projectile's angle and slope to track its target. This mechanic was groundbreaking for its time, as homing projectiles added depth to combat and required players to employ evasive maneuvers. The technique influenced later games, including Unreal Tournament and Team Fortress, where homing mechanics became a staple of weapon design." + - id: "vile-resurrection-mechanic" line_start: 1128 line_end: 1158 - title: "The Code That Raised the Dead" + title: "Vile Resurrection Mechanic" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `PIT_VileCheck` function is part of the Arch-Vile's resurrection mechanic, allowing it to bring fallen enemies back to life. This routine checks for nearby corpses that meet specific criteria, such as being stationary and having a defined 'raise state.' If a valid corpse is found, the function prepares it for resurrection by adjusting its position and flags. This mechanic added a strategic layer to gameplay, forcing players to prioritize the Arch-Vile as a high-threat target. The resurrection feature was groundbreaking for its time, influencing later games like Diablo II and Left 4 Dead, which incorporated similar mechanics to increase tension and challenge." - - id: "hellfire-targeting-system" - line_start: 1280 - line_end: 1301 - title: "Hellfire That Tracks Its Target" + content: "The `PIT_VileCheck` function enables the Arch-Vile enemy to resurrect fallen monsters. It checks for nearby corpses that meet specific criteria, such as being stationary and having a defined 'raise state.' This mechanic added a strategic layer to gameplay, forcing players to prioritize killing the Arch-Vile to prevent it from reviving enemies. Resurrection mechanics like this influenced later games, including Diablo and Dark Souls, where similar systems are used to increase challenge and tension." + - id: "mancubus-multi-directional-attack" + line_start: 1357 + line_end: 1372 + title: "Mancubus Multi-Directional Attack" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `A_VileTarget` function spawns a 'hellfire' object that tracks the Arch-Vile's target. This routine ensures the fire remains dynamically linked to the target's position, creating a visually striking and mechanically impactful attack. The use of tracer objects to maintain positional updates was a clever solution to simulate tracking behavior on limited hardware. This technique influenced later games by demonstrating how to create visually compelling effects that also serve gameplay purposes, inspiring similar mechanics in titles like Unreal Tournament and World of Warcraft." + content: "The Mancubus's attack sequence (`A_FatAttack1`, `A_FatAttack2`, `A_FatAttack3`) involves firing projectiles in multiple directions using predefined angle offsets. This design creates a unique challenge for players, as dodging requires spatial awareness and timing. The multi-directional attack was a novel concept in 1993, showcasing DOOM's ability to create varied enemy behaviors. Similar mechanics appeared in later games like Borderlands and Overwatch, where enemy attacks often involve area-of-effect patterns." - id: "skull-missile-attack" line_start: 1412 line_end: 1504 - title: "Flying Skulls as Guided Missiles" + title: "Skull Missile Attack" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `A_SkullAttack` function turns enemies into flying projectiles that home in on the player. By setting the 'skull fly' flag and calculating trajectory based on the target's position, this routine creates a unique attack pattern that combines mobility and aggression. The use of fixed-point arithmetic for movement calculations reflects the technical limitations of the era, where floating-point operations were often too costly. This mechanic added variety to enemy behavior and inspired similar features in later games, such as the homing projectiles in Metroid Prime and Halo." - - id: "lost-soul-spawning-limit" + content: "The `A_SkullAttack` function defines the behavior of Lost Souls, which fly directly at the player like missiles. The function calculates movement vectors based on the target's position, ensuring the enemy homes in accurately. This mechanic added intensity to encounters, as players had to react quickly to avoid damage. The homing attack concept influenced later games, including Metroid Prime and Destiny, where similar enemy behaviors are used to create high-stakes combat scenarios." + - id: "pain-elemental-skull-spawning" line_start: 1507 line_end: 1518 - title: "Why DOOM Limits Lost Souls to 20" + title: "Pain Elemental Skull Spawning" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `A_PainShootSkull` function spawns Lost Souls, but includes a hard limit of 20 on the number of these entities present in the level. This restriction was implemented to prevent performance degradation on the modest hardware of the early 1990s. By counting active Lost Souls and aborting spawning if the limit is exceeded, the developers ensured smooth gameplay even during chaotic encounters. This approach highlights the balance between creative design and technical constraints, a hallmark of DOOM's development. The concept of entity limits influenced later games, such as StarCraft and Age of Empires, which used similar techniques to manage performance in large-scale battles." - - id: "explosion-radius-damage" - line_start: 1594 - line_end: 1600 - title: "The Radius Attack That Shaped FPS Combat" + content: "The `A_PainAttack` function allows the Pain Elemental to spawn Lost Souls and launch them at the player. This mechanic increases the difficulty of encounters by introducing additional enemies mid-combat. The spawning behavior was innovative for its time, as it dynamically altered the battlefield and required players to adapt their strategies. Similar mechanics appeared in later games like StarCraft and XCOM, where enemy units can spawn reinforcements to shift the tide of battle." + - id: "death-sound-randomization" + line_start: 1534 + line_end: 1568 + title: "Death Sound Randomization" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `A_Explode` function handles explosion effects, dealing damage to entities within a specified radius. This routine is central to the game's explosive weapons and enemy attacks, creating impactful moments of destruction. The use of radius-based damage calculations was a novel approach at the time, contributing to the visceral feel of combat in DOOM. This technique became a standard in FPS games, influencing titles like Call of Duty and Battlefield, where explosions play a key role in gameplay dynamics." - - id: "boss-death-triggers-special-events" + content: "The `A_Scream` function randomizes the death sounds of certain enemies, adding variety to the audio experience. By selecting sounds based on the enemy type and applying randomness, the function ensures that repeated encounters feel less monotonous. This attention to detail in sound design contributed to DOOM's immersive atmosphere and influenced later games, including Half-Life and Bioshock, where audio plays a critical role in storytelling and player engagement." + - id: "boss-death-triggers-level-events" line_start: 1603 line_end: 1755 - title: "Boss Death Triggers Special Events" + title: "Boss Death: Unlocking Levels with Tags" wikipedia_url: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" image_url: "" image_caption: "" - content: "The `A_BossDeath` function handles the logic for triggering special events when a boss enemy dies. Depending on the game mode, episode, and map, specific actions are performed, such as lowering floors or opening doors. This logic ensures that defeating a boss not only signifies victory but also progresses the game by altering the environment. Written by John Carmack and team, this approach reflects the careful integration of gameplay mechanics with level design. In 1993, games often relied on scripted sequences, but DOOM's dynamic event system was groundbreaking. It allowed players to experience unique outcomes based on their actions, enhancing replayability. The concept of tying environmental changes to enemy deaths influenced later games like Quake and Half-Life, where scripted events and dynamic environments became standard." - - id: "hoof-metal-sounds-for-immersive-feedback" + content: "The `A_BossDeath` function determines whether a boss enemy's death should trigger special level events, such as opening doors or lowering floors. This routine checks the game mode, episode, and map number to ensure the correct boss type is defeated before activating these events. The function uses 'tags' (numeric identifiers for level sectors) to manipulate level geometry, creating dramatic moments like floors lowering or doors opening. In the commercial version of DOOM, this is particularly notable in Map 7 ('Dead Simple'), where defeating specific enemies triggers changes in the level. This approach reflects the ingenuity required to create dynamic gameplay on hardware with limited memory and processing power. By tying boss deaths to level progression, DOOM introduced a sense of accomplishment and narrative flow that influenced countless games afterward, including titles like Quake and Half-Life." + - id: "hoof-metal-babymetal-sound-effects" line_start: 1758 line_end: 1768 - title: "Hoof and Metal Sounds for Immersive Feedback" + title: "Sound Effects: Giving Enemies Personality" wikipedia_url: "https://en.wikipedia.org/wiki/Sound_effect" image_url: "" image_caption: "" - content: "The `A_Hoof`, `A_Metal`, and `A_BabyMetal` functions play specific sound effects when certain enemies move. These sounds—hoofbeats, metallic clanks, and baby spider footsteps—enhance the game's atmosphere and provide auditory cues to players. In the early 1990s, sound design in games was limited by hardware constraints, but DOOM leveraged its sound engine to create a rich and immersive experience. The use of sound effects as gameplay feedback became a hallmark of id Software's design philosophy, influencing countless games that followed. Modern titles like Dead Space and The Last of Us continue to use sound to heighten tension and immersion." - - id: "shotgun-reloading-sounds" - line_start: 1776 - line_end: 1804 - title: "Shotgun Reloading Sounds" - wikipedia_url: "https://doomwiki.org/wiki/Weapons_in_Doom" + content: "The `A_Hoof`, `A_Metal`, and `A_BabyMetal` functions play sound effects associated with specific enemy movements, such as hoofbeats or metallic clanking. These routines also call `A_Chase`, the AI function that drives enemy movement and behavior. By pairing distinct sounds with enemy actions, DOOM created a visceral and immersive experience, allowing players to identify threats by sound alone. This technique was groundbreaking in 1993, as sound design was often an afterthought in games of the era. The integration of sound effects into gameplay mechanics influenced later titles like Unreal Tournament and Halo, where audio cues became critical for situational awareness." + - id: "shotgun-reloading-sound-effects" + line_start: 1770 + line_end: 1810 + title: "Shotgun Reload: Audio Feedback for Tension" + wikipedia_url: "https://en.wikipedia.org/wiki/Sound_effect" image_url: "" image_caption: "" - content: "The `A_OpenShotgun2`, `A_LoadShotgun2`, and `A_CloseShotgun2` functions manage the sound effects for the double-barrel shotgun's reloading sequence. These sounds—opening, loading, and closing—add realism and satisfaction to the weapon's use. The shotgun is one of DOOM's iconic weapons, and its auditory feedback contributes to its visceral appeal. This attention to detail in weapon design set a standard for first-person shooters, influencing games like Call of Duty and Battlefield, where weapon sounds are meticulously crafted to enhance player experience." - - id: "brain-awake-targeting-mechanic" + content: "The `A_OpenShotgun2`, `A_LoadShotgun2`, and `A_CloseShotgun2` functions handle the sound effects for the double-barrel shotgun's reloading sequence. These sounds—opening, loading, and closing—add a tactile feel to the weapon, emphasizing its power and the tension of reloading during combat. The shotgun became one of DOOM's most iconic weapons, partly due to this attention to detail. The use of audio to enhance weapon mechanics influenced later FPS games, including Call of Duty and Battlefield, where weapon handling and sound design are central to gameplay." + - id: "brain-awake-targeting-system" line_start: 1812 line_end: 1839 - title: "Brain Awake: Targeting Mechanic" + title: "Brain Awake: Finding Targets for Chaos" + wikipedia_url: "https://doomwiki.org/wiki/Icon_of_Sin" + image_url: "" + image_caption: "" + content: "The `A_BrainAwake` function initializes the 'brain' enemy, known as the Icon of Sin, by locating all target spots in the level. These targets are stored in an array (`braintargets`), which the brain later uses to spawn enemies. This setup creates a dynamic battlefield where new monsters can appear unpredictably, keeping players on edge. The routine reflects DOOM's emphasis on procedural elements to enhance replayability. The Icon of Sin's mechanics inspired similar 'boss spawning' systems in games like Serious Sam and Destiny." + - id: "brain-scream-explosion-effects" + line_start: 1848 + line_end: 1870 + title: "Brain Scream: Rockets and Randomness" wikipedia_url: "https://doomwiki.org/wiki/Icon_of_Sin" image_url: "" image_caption: "" - content: "The `A_BrainAwake` function initializes the targeting system for the final boss, the Icon of Sin. It scans the game world for specific target objects (`MT_BOSSTARGET`) and stores them in an array for later use. This mechanic ensures that the boss can dynamically interact with the environment by spawning projectiles aimed at these targets. In 1993, such dynamic behavior was rare in games, which often relied on static patterns. The Icon of Sin's targeting system added unpredictability and challenge to the final encounter, influencing boss design in later games like Dark Souls, where environmental interaction plays a key role." - - id: "brain-explode-and-telefrag" - line_start: 1874 - line_end: 1892 - title: "Brain Explode and Telefrag Mechanics" - wikipedia_url: "https://doomwiki.org/wiki/Telefrag" + content: "The `A_BrainScream` function spawns rockets in a randomized pattern around the brain enemy, creating chaotic explosions. This visual and auditory spectacle serves as a dramatic signal of the brain's distress. The randomness ensures that each encounter feels unique, a hallmark of DOOM's design philosophy. This technique of combining randomness with scripted events influenced later games like Diablo II and Left 4 Dead, which use similar methods to create dynamic encounters." + - id: "spawn-fly-randomized-monster-generation" + line_start: 1935 + line_end: 1989 + title: "Spawn Fly: Procedural Monster Generation" + wikipedia_url: "https://en.wikipedia.org/wiki/Procedural_generation" image_url: "" image_caption: "" - content: "The `A_BrainExplode` and `A_SpawnFly` functions handle the spawning of monsters and the telefrag mechanic, where a spawned monster instantly kills anything occupying its spawn location. This clever use of spatial logic creates tension and unpredictability, as players must constantly adapt to new threats. The telefrag mechanic became a signature feature of DOOM, influencing multiplayer games like Quake, where telefragging became a competitive tactic. The random monster spawning adds replayability, ensuring that each playthrough feels unique. This procedural approach to gameplay design laid the groundwork for modern roguelike and sandbox games." - - id: "player-death-sounds" + content: "The `A_SpawnFly` function handles the spawning of monsters from the brain's 'flying cubes.' It first creates a teleport fog effect, then selects a monster type based on a weighted random distribution. This ensures a mix of enemies, with tougher monsters appearing less frequently. The spawned monster telefrags anything in its spot, adding a layer of unpredictability. This procedural generation technique was ahead of its time, influencing modern games like Minecraft and Spelunky, where randomness is used to create varied gameplay experiences." + - id: "player-scream-death-sound-variation" line_start: 1993 - line_end: 2003 - title: "Player Death Sounds: Health-Based Variation" - wikipedia_url: "https://doomwiki.org/wiki/Player_sounds" + line_end: 2006 + title: "Player Scream: Death Sounds with Impact" + wikipedia_url: "https://doomwiki.org/wiki/Player_character" image_url: "" image_caption: "" - content: "The `A_PlayerScream` function plays different death sounds based on the player's health at the time of death. If the player's health drops below -50 without gibbing, a unique sound (`sfx_pdiehi`) is triggered. This subtle detail adds emotional weight to player deaths and enhances immersion. In 1993, such nuanced sound design was rare, as most games used generic sound effects for all death scenarios. DOOM's approach influenced later games, where sound effects became an integral part of storytelling and gameplay feedback. Titles like Halo and Half-Life adopted similar techniques to create memorable player experiences." + content: "The `A_PlayerScream` function plays different death sounds depending on the player's health at the time of death. If the player dies with less than -50 health without gibbing, a special 'high-pitched' death sound is played. This detail adds emotional weight to player deaths, making them feel more impactful. The use of varied audio feedback for player actions and states influenced later games like Dark Souls and The Last of Us, where sound design plays a critical role in conveying the player's experience." --- @@ -2224,4 +2224,4 @@ void A_PlayerScream (mobj_t* mo) S_StartSound (mo, sound); } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/p-floor-c.md b/public/programs/doom/p-floor-c.md index fa1f8bf..c282528 100644 --- a/public/programs/doom/p-floor-c.md +++ b/public/programs/doom/p-floor-c.md @@ -9,58 +9,52 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-floor-c" order: 27 -description: "This file implements floor and staircase movement mechanics in DOOM, showcasing techniques that pushed the boundaries of level interactivity in 1993." +description: "This file implements floor movement and stair-building logic in DOOM, showcasing techniques that pushed the limits of 3D environments on early PCs." summary: - - point: "Implements floor and ceiling movement mechanics for dynamic level design" + - point: "Implements floor movement with crushing mechanics" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Introduces stair-building algorithms for vertical progression in levels" - link: "https://doomwiki.org/wiki/Stairs" - link_label: "Stairs in DOOM" - - point: "Optimizes movement calculations for performance on 1990s hardware" - link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" - link_label: "Fixed-point arithmetic" - - point: "Demonstrates modular design with reusable movement routines" - link: "https://en.wikipedia.org/wiki/Modular_programming" - link_label: "Modular programming" - - point: "Pioneered techniques for dynamic level geometry in real-time games" + - point: "Introduces staircase-building algorithms for dynamic level design" link: "https://en.wikipedia.org/wiki/Level_design" - link_label: "Level design" + link_label: "Level Design" + - point: "Optimized for hardware constraints of early 1990s PCs" + link: "https://en.wikipedia.org/wiki/History_of_personal_computers" + link_label: "History of Personal Computers" enhancements: - - id: "move-plane-crushing-check" + - id: "move-plane-crushing-mechanics" line_start: 40 line_end: 202 - title: "How DOOM Simulated Crushing Floors" + title: "The Crushing Logic Behind Moving Floors" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `T_MovePlane` function is responsible for moving floors and ceilings in DOOM, with additional logic to check for crushing objects or players. This routine adjusts the height of a sector's floor or ceiling based on the specified speed and destination, while ensuring that entities in the affected area are not crushed unless explicitly allowed. The function uses fixed-point arithmetic to perform calculations efficiently on the limited hardware of the early 1990s. At the time, consumer PCs lacked floating-point units, making fixed-point math a necessity for real-time applications like games. John Carmack, the lead programmer of DOOM, designed this routine to handle dynamic level geometry—a groundbreaking feature in 1993. The ability to move floors and ceilings in real-time added a layer of interactivity and immersion that was rare in games of the era. Carmack's approach drew inspiration from earlier games like Wolfenstein 3D but expanded on the concept by introducing vertical movement and environmental hazards. The crushing mechanic became a hallmark of DOOM's level design, allowing for creative traps and puzzles. This technique influenced later games, including Quake and Unreal, which adopted similar methods for dynamic environments. Today, the concept of moving level geometry is standard in game engines like Unity and Unreal Engine, showcasing the lasting impact of Carmack's innovations." - - id: "move-floor-destination" + content: "This section defines the `T_MovePlane` function, which handles the movement of floors and ceilings in DOOM's levels. The function checks whether the movement of a plane (floor or ceiling) would crush objects or players and adjusts accordingly. The logic accounts for direction (up or down), speed, and destination height, ensuring smooth transitions while preventing unintended gameplay consequences like players being trapped or crushed. In the early 1990s, consumer PCs were limited in processing power and memory, requiring developers to implement efficient algorithms to simulate dynamic environments. John Carmack, DOOM's lead programmer, was renowned for his ability to optimize code for these constraints. The crushing mechanics here reflect a careful balance between realism and performance, ensuring the game felt immersive without overwhelming the hardware. This approach influenced later games that incorporated dynamic environmental changes, such as Quake and Unreal. The concept of handling collisions and crush mechanics became a staple in game engines, forming the basis for more advanced physics simulations seen in modern titles like Half-Life 2 and Portal. Carmack's work here exemplifies how clever programming can overcome hardware limitations to deliver groundbreaking experiences." + - id: "move-floor-dynamic-levels" line_start: 205 line_end: 253 - title: "The Sound of Moving Floors" - wikipedia_url: "https://en.wikipedia.org/wiki/Sound_effect" + title: "Dynamic Floors: Moving the World in Real-Time" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `T_MoveFloor` function builds on `T_MovePlane` to handle floor movement specifically. It adds sound effects to enhance the player's experience, playing a movement sound (`sfx_stnmov`) periodically as the floor moves and a stopping sound (`sfx_pstop`) when the floor reaches its destination. This attention to auditory detail was part of DOOM's immersive design philosophy, ensuring that players received sensory feedback for environmental changes. In the early 1990s, sound cards were becoming more common in consumer PCs, and DOOM leveraged this hardware to create a richer gaming experience. Dave Taylor, one of DOOM's developers, worked on integrating sound effects into the game engine, ensuring that they synchronized with gameplay events like moving floors and crushing ceilings. The use of sound effects for environmental interactions influenced later games, establishing a standard for auditory feedback in dynamic environments. Games like Half-Life and System Shock expanded on this concept, using sound to convey information about the game world. Modern game engines now include built-in audio systems that automate these processes, but the principles laid out in DOOM remain foundational." - - id: "floor-types-handler" + content: "The `T_MoveFloor` function animates floor movements, either raising or lowering them to a specified destination height. It integrates sound effects to enhance the player's experience, such as the grinding noise of moving floors (`sfx_stnmov`) and the stop sound (`sfx_pstop`). The function also updates the floor's texture and special properties when the movement completes, ensuring that the visual and gameplay elements remain consistent. In 1993, DOOM's ability to dynamically alter the environment in real-time was revolutionary. Most games of the era relied on static level designs, but DOOM introduced dynamic elements that could change during gameplay. This feature added a layer of unpredictability and immersion, making levels feel alive. Dynamic environmental changes became a hallmark of modern game design, influencing titles like Duke Nukem 3D and Half-Life. The ability to modify levels in real-time also laid the groundwork for procedural generation techniques used in games like Minecraft and No Man's Sky. DOOM's implementation of dynamic floors showcased the potential of interactive environments, a concept that continues to evolve in today's gaming industry." + - id: "ev-do-floor-flexible-movement" line_start: 255 line_end: 443 - title: "Dynamic Floors: A Toolbox of Movement Types" - wikipedia_url: "https://doomwiki.org/wiki/Floor" + title: "Flexible Floor Movement: A Toolbox for Level Designers" + wikipedia_url: "https://en.wikipedia.org/wiki/Level_design" image_url: "" image_caption: "" - content: "The `EV_DoFloor` function handles various types of floor movements, from lowering floors to their lowest surrounding height to raising them at turbo speeds. This modular design allows level designers to create diverse gameplay scenarios, such as elevators, traps, and platforms. Each movement type is associated with specific parameters, like speed and destination height, enabling precise control over the game's dynamic geometry. DOOM's level design was revolutionary for its time, offering unprecedented interactivity and verticality. John Romero, the game's designer, envisioned levels that felt alive, with moving parts that responded to player actions. The modular approach in `EV_DoFloor` reflects this vision, providing a flexible framework for implementing complex level mechanics. This design philosophy influenced the development of later games and engines. Quake expanded on the concept with more advanced physics and collision detection, while modern engines like Unreal and Unity offer even greater flexibility for dynamic level design. The ability to script diverse floor movements remains a staple of game development, rooted in the innovations of DOOM." - - id: "build-stairs-algorithm" + content: "The `EV_DoFloor` function provides a versatile mechanism for handling various types of floor movements, such as lowering to the lowest surrounding floor, raising to the nearest ceiling, or turbo-lowering for faster transitions. It uses a thinker structure (`floormove_t`) to manage ongoing floor movements and ensures that each sector's properties are updated dynamically during gameplay. DOOM's level design philosophy emphasized flexibility and creativity. By providing developers with tools like `EV_DoFloor`, the game enabled intricate level designs that could adapt to player actions. This was a significant departure from the static environments of earlier games, allowing for puzzles, traps, and dynamic challenges. The modularity of this approach influenced the development of level editors and game engines, such as the Build engine used in Duke Nukem 3D and the Source engine used in Half-Life. The ability to script and control environmental changes became a standard feature in game development, empowering designers to create more engaging and interactive worlds. DOOM's legacy in this area is evident in the widespread adoption of dynamic level mechanics across the gaming industry." + - id: "ev-build-stairs-procedural-design" line_start: 448 line_end: 553 - title: "How DOOM Built Stairs in Real Time" - wikipedia_url: "https://doomwiki.org/wiki/Stairs" + title: "Building Stairs: Procedural Level Design in Action" + wikipedia_url: "https://en.wikipedia.org/wiki/Procedural_generation" image_url: "" image_caption: "" - content: "The `EV_BuildStairs` function implements an algorithm for constructing staircases dynamically during gameplay. It identifies adjacent sectors, calculates the height of each step, and moves the floors incrementally to create a staircase. This feature allowed DOOM's levels to include vertical progression, adding depth and complexity to the gameplay. In the early 1990s, real-time construction of level geometry was a novel concept. Most games relied on static environments, with little to no dynamic interaction. DOOM's stair-building algorithm was a technical achievement, leveraging fixed-point arithmetic and efficient memory management to perform these calculations on limited hardware. The algorithm's modular design also made it reusable, enabling designers to implement stairs with varying speeds and step sizes. This technique influenced later games like Quake and Unreal, which expanded on the concept with more advanced physics and rendering capabilities. Today, dynamic level geometry is a standard feature in game engines, allowing developers to create interactive environments with ease. The stair-building algorithm in DOOM represents a key milestone in the evolution of level design." + content: "The `EV_BuildStairs` function automates the creation of staircases in DOOM's levels. It calculates the height and texture of each step, ensuring consistency while allowing for variations like turbo stairs (`turbo16`) or standard stairs (`build8`). The function iterates through sectors to find adjacent areas and raises their floors incrementally, creating a staircase that connects different parts of the level. Procedural generation was not a common concept in 1993, but DOOM's stair-building logic foreshadowed its potential. By automating repetitive tasks, the function saved developers time and enabled more complex level designs. This approach also allowed for dynamic changes during gameplay, adding to the game's sense of immersion. Modern games like Spelunky and No Man's Sky have embraced procedural generation to create vast, unpredictable worlds. While DOOM's stair-building function was limited in scope, it demonstrated the power of procedural techniques in game design. The idea of automating level elements has since evolved into sophisticated algorithms capable of generating entire worlds, a testament to the enduring influence of DOOM's innovative programming." --- @@ -618,4 +612,5 @@ EV_BuildStairs } return rtn; } -``` + +``` \ No newline at end of file diff --git a/public/programs/doom/p-inter-c.md b/public/programs/doom/p-inter-c.md index 93f4f92..ae3cce7 100644 --- a/public/programs/doom/p-inter-c.md +++ b/public/programs/doom/p-inter-c.md @@ -9,82 +9,82 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-inter-c" order: 10 -description: "This file handles player interactions, collisions, and damage mechanics in DOOM, showcasing the game's innovative approach to gameplay mechanics." +description: "This file handles player interactions and object collisions in DOOM, showcasing the game's innovative approach to gameplay mechanics." summary: - - point: "Defines ammo and weapon pickup logic" + - point: "Introduces dynamic ammo and weapon management" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Implements player health and armor systems" + link_label: "DOOM (1993)" + - point: "Implements health and armor systems with constraints" + link: "https://en.wikipedia.org/wiki/Health_(gaming)" + link_label: "Health in gaming" + - point: "Pioneers item pickup mechanics that influenced FPS design" + link: "https://en.wikipedia.org/wiki/First-person_shooter" + link_label: "First-person shooter" + - point: "Includes player-specific hacks for immersive gameplay" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Introduces power-up mechanics like invulnerability" - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Handles monster death and item drops" - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Manages damage calculations and player reactions" - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" + link_label: "DOOM (1993)" + - point: "Introduces monster targeting and reaction systems" + link: "https://en.wikipedia.org/wiki/Artificial_intelligence_in_video_games" + link_label: "AI in video games" enhancements: - - id: "ammo-pickup-mechanics" - line_start: 61 - line_end: 159 - title: "Why DOOM's ammo pickups feel so rewarding" + - id: "ammo-management-dynamic-system" + line_start: 55 + line_end: 58 + title: "How DOOM Balanced Ammo Pickup" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_GiveAmmo` function governs how players acquire ammunition in DOOM. It calculates the amount of ammo added based on the game's difficulty level and ensures players never exceed their maximum ammo capacity. If a player is out of ammo, the function automatically selects a new weapon, prioritizing the most powerful available. This design ensures smooth gameplay flow, preventing players from being stuck without a viable weapon. In 1993, this was a crucial innovation, as many games of the era lacked dynamic weapon-switching mechanics. John Carmack and the team at id Software aimed to create a fast-paced experience where players could focus on action rather than inventory management. This approach influenced later FPS games like Quake and Half-Life, which adopted similar systems for seamless weapon and ammo management." - - id: "weapon-pickup-logic" - line_start: 162 - line_end: 219 - title: "The logic behind DOOM's weapon pickups" + content: "This section defines the maximum ammo capacities and clip sizes for different weapon types in DOOM. The `maxammo` array specifies the upper limits for each ammo type, while `clipammo` determines the number of rounds in a single clip. These values were carefully chosen to balance gameplay, ensuring players had enough resources to engage enemies without trivializing the challenge. In the early 1990s, FPS games were still experimenting with resource management mechanics. DOOM's approach set a precedent for future titles, influencing games like Quake and Half-Life, which adopted similar systems. By making ammo pickups dynamic and tied to clip sizes, DOOM introduced a layer of strategy to its fast-paced gameplay, encouraging players to think critically about their resource usage." + - id: "give-ammo-double-in-nightmare" + line_start: 61 + line_end: 113 + title: "Why Nightmare Mode Doubles Your Ammo" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_GiveWeapon` function handles the acquisition of weapons, ensuring players receive appropriate ammo when picking up a weapon. It differentiates between dropped weapons and those placed in the environment, granting fewer resources for dropped items. This subtle distinction reflects the game's emphasis on resource management and strategic gameplay. In multiplayer deathmatches, weapons remain available for all players, encouraging competitive play. The function also triggers sound effects and visual feedback, enhancing the player's sense of accomplishment. This system was groundbreaking in 1993, as it combined immersive feedback with practical gameplay mechanics. The influence of this design can be seen in modern FPS games like Call of Duty and Destiny, where weapon pickups are integral to gameplay." - - id: "health-and-armor-systems" + content: "The `P_GiveAmmo` function handles ammo pickups, adjusting quantities based on the game's difficulty level. In 'Nightmare' mode, ammo pickups are doubled, reflecting the increased challenge posed by faster enemies and respawning monsters. This mechanic was a clever way to balance difficulty without overwhelming players. John Carmack and John Romero designed DOOM to be accessible yet challenging, with difficulty modes catering to both casual players and hardcore enthusiasts. The decision to double ammo in 'Nightmare' mode ensured that players had the tools to survive, even as the game pushed them to their limits. This approach influenced difficulty scaling in later games, such as the 'Ultra-Violence' mode in DOOM sequels and similar mechanics in other FPS titles." + - id: "weapon-pickup-strategy" line_start: 162 line_end: 219 - title: "How DOOM balanced health and armor mechanics" + title: "Weapon Pickups, Health Caps, and Armor Thresholds" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_GiveBody` and `P_GiveArmor` functions manage player health and armor acquisition. These systems ensure players can't exceed predefined limits, maintaining balance and challenge. Health items like medikits and stimpacks restore health, while armor types provide varying levels of protection. The design reflects id Software's focus on creating a challenging yet fair gameplay experience. In the early 1990s, health and armor systems were relatively simple in games, but DOOM's implementation added depth by introducing limits and strategic choices. This approach influenced later games like Halo, which expanded on the concept with regenerating shields and health packs." - - id: "power-up-mechanics" - line_start: 162 - line_end: 219 - title: "The power-ups that defined DOOM's gameplay" + content: "P_GiveWeapon governs weapon pickups by distinguishing between dropped weapons (one ammo clip) and placed weapons (two clips), and it treats networked deathmatch games differently so that weapons left on the map are never consumed. Alongside it, P_GiveBody caps player health at 100 normally but allows bonus pickups to push it to 200, and P_GiveArmor refuses pickups that are weaker than the player’s current protection level. These three functions together define DOOM’s resource economy: players are rewarded for exploration and monster kills but cannot hoard indefinitely, maintaining tension throughout a level. The distinction between dropped and placed weapons was particularly influential in multiplayer design, since it meant enemies dropped only a fraction of the ammo a map-placed copy provided, balancing respawn loops in deathmatch. These mechanics were refined and extended in Quake, Unreal Tournament, and Counter-Strike, which all adopted variations on pickup scarcity and health caps as core design primitives." + - id: "powerup-effects-and-duration" + line_start: 223 + line_end: 241 + title: "The Secrets Behind DOOM’s Powerups" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_GivePower` function governs the acquisition of power-ups, such as invulnerability, invisibility, and berserk strength. Each power-up provides a unique advantage, encouraging players to adapt their strategies. For example, invulnerability allows players to survive intense combat, while berserk strength enhances melee attacks. The function also prevents redundant pickups, ensuring players don't waste resources. In 1993, power-ups were a staple of arcade games, but DOOM elevated their importance by integrating them into its fast-paced gameplay. This innovation influenced countless FPS titles, including Unreal Tournament and Overwatch, where power-ups play a central role in competitive play." - - id: "special-item-interactions" + content: "The `P_GivePower` function handles powerups like invulnerability, invisibility, and strength. Each powerup has a predefined duration, ensuring players must use them strategically. For example, invulnerability lasts for a fixed number of 'tics,' while invisibility modifies the player's appearance and behavior. These mechanics were revolutionary at the time, adding layers of complexity to DOOM's gameplay. Powerups were a staple of arcade games, but DOOM adapted them to the FPS genre, making them integral to its fast-paced action. This approach influenced later games like Duke Nukem 3D and Serious Sam, which expanded on the concept with more varied and creative powerups." + - id: "item-pickup-mechanics" line_start: 269 - line_end: 660 - title: "How DOOM made item pickups satisfying" + line_end: 659 + title: "How DOOM Made Item Pickups Exciting" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_TouchSpecialThing` function handles interactions with special items, such as keys, health bonuses, and weapons. It ensures items are within reach and provides feedback through sound effects and messages. This function also manages multiplayer scenarios, allowing all players to access critical items like keys. The design reflects id Software's commitment to creating an immersive experience, where every pickup feels impactful. In the early 1990s, item interactions were often simplistic, but DOOM's approach added depth and polish. This system influenced modern games like Borderlands, where item pickups are accompanied by satisfying audio-visual effects." + content: "The `P_TouchSpecialThing` function governs item pickups, from health bonuses to weapons and keys. It checks the player's proximity to the item, ensuring realistic interactions. Each item triggers a specific sound and message, enhancing the game's feedback loop. This function also handles multiplayer scenarios, ensuring items like keys are shared among players. DOOM's item pickup mechanics were meticulously designed to balance gameplay and immersion. The developers drew inspiration from arcade games, where item pickups were a core mechanic, but adapted the concept for the FPS genre. This system influenced countless games, including Quake and Unreal, which refined item pickups with more complex interactions and effects." - id: "monster-death-and-item-drops" line_start: 663 - line_end: 757 - title: "The mechanics behind DOOM's monster deaths" + line_end: 724 + title: "Why Monsters Drop Items in DOOM" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_KillMobj` function governs monster deaths and item drops. When a monster dies, it spawns items like ammo or weapons, depending on its type. This mechanic rewards players for defeating enemies, encouraging aggressive gameplay. The function also handles player deaths, ensuring a smooth transition to the post-death state. In 1993, this level of detail was rare in games, as most titles featured static death animations. DOOM's dynamic system added replayability and depth, influencing later games like Diablo, where enemies drop loot upon defeat." - - id: "damage-calculation-and-reactions" + content: "The `P_KillMobj` function handles monster deaths, including item drops. When a monster dies, it may spawn an item, such as ammo or a weapon, depending on its type. This mechanic added a layer of strategy to combat, as players could prioritize certain enemies to replenish their resources. The idea of item drops from enemies was not new, but DOOM popularized it in the FPS genre, making it a staple of modern games. Titles like Borderlands and Destiny expanded on this concept, creating elaborate loot systems that reward players for defeating enemies." + - id: "damage-calculation-and-thrust" line_start: 762 - line_end: 794 - title: "How DOOM calculated damage and reactions" + line_end: 916 + title: "How DOOM Calculated Damage and Knockback" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_DamageMobj` function calculates damage and manages reactions for both players and monsters. It considers factors like armor, invulnerability, and environmental hazards, ensuring a balanced experience. The function also handles player-specific mechanics, such as tactile feedback for the console player. In 1993, damage systems were often simplistic, but DOOM's implementation added complexity and nuance. This innovation influenced later FPS games like Counter-Strike, where damage calculations and player reactions are central to gameplay." + content: "The `P_DamageMobj` function calculates damage dealt to players and monsters, factoring in armor, invulnerability, and environmental effects. It also applies thrust, pushing targets away from the source of damage. This mechanic was crucial for DOOM's fast-paced combat, as it created dynamic interactions between players, enemies, and the environment. The developers drew inspiration from physics simulations, adapting them to the game's constraints. This approach influenced later games like Half-Life and Portal, which used physics-based mechanics to enhance gameplay. DOOM's damage and thrust calculations remain a benchmark for FPS design, showcasing the game's innovative approach to combat." --- @@ -1005,4 +1005,5 @@ P_DamageMobj } } -``` + +``` \ No newline at end of file diff --git a/public/programs/doom/p-lights-c.md b/public/programs/doom/p-lights-c.md index 2b69ce1..145c0e4 100644 --- a/public/programs/doom/p-lights-c.md +++ b/public/programs/doom/p-lights-c.md @@ -9,84 +9,92 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-lights-c" order: 28 -description: "This file implements dynamic lighting effects in DOOM, a feature that contributed to the game's immersive atmosphere and technical innovation." +description: "This file implements dynamic lighting effects in DOOM, a groundbreaking feature for immersive gameplay in 1993." summary: - - point: "Dynamic lighting effects like flickering and strobing were implemented to enhance realism." - link: "https://en.wikipedia.org/wiki/Doom_(1993_video_game)" + - point: "Introduced dynamic lighting effects for realism and atmosphere" + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Sector-based lighting allowed for efficient rendering on limited hardware." - link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" - link_label: "Fixed-point arithmetic" - - point: "The 'Thinker' system enabled modular and reusable game logic." - link: "https://doomwiki.org/wiki/Thinker" - link_label: "Thinker system in DOOM" + - point: "Used efficient algorithms to simulate lighting on limited hardware" + link: "https://en.wikipedia.org/wiki/Real-time_computing" + link_label: "Real-time computing" + - point: "Pioneered modular 'thinker' system for game logic" + link: "https://en.wikipedia.org/wiki/Game_engine" + link_label: "Game engine" enhancements: - - id: "firelight-flicker-effect" + - id: "firelight-flicker-algorithm" line_start: 35 line_end: 60 - title: "The Trick Behind Flickering Firelight" - wikipedia_url: "https://doomwiki.org/wiki/Doom_rendering_engine" + title: "Why Firelight Flickers in DOOM" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `T_FireFlicker` function simulates the flickering effect of firelight by periodically adjusting the light level of a sector. The code uses a random number generator to introduce variability, creating a convincing illusion of natural firelight. At the time, DOOM's developers were working with hardware constraints that limited graphical fidelity, so dynamic lighting effects like this were a clever way to enhance immersion without taxing the CPU. John Carmack's focus on efficient algorithms ensured that even these atmospheric touches could run smoothly on consumer-grade PCs. This technique influenced later games, including Quake and Unreal, which expanded on dynamic lighting to create even more realistic environments." + content: "The `T_FireFlicker` function simulates the flickering effect of firelight by periodically adjusting the light level of a sector. The algorithm uses a random number generator (`P_Random`) to calculate the intensity of the flicker, ensuring that the light level stays within defined bounds (`minlight` and `maxlight`). This creates an atmospheric effect that enhances the realism of DOOM's environments. In 1993, dynamic lighting was rare in games, especially on consumer hardware like the 386 and 486 PCs DOOM targeted. John Carmack's approach balanced visual fidelity with performance, leveraging efficient calculations to avoid taxing the CPU. This technique inspired later games to incorporate dynamic lighting, eventually becoming a staple of modern game engines like Unreal Engine and Unity." - id: "spawn-firelight-flicker" line_start: 64 line_end: 84 - title: "How DOOM Made Firelight Feel Alive" - wikipedia_url: "https://doomwiki.org/wiki/Sector" + title: "How DOOM Spawns Flickering Firelight" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_SpawnFireFlicker` function initializes the firelight flickering effect for a given sector. It allocates memory for a `fireflicker_t` structure, sets up the 'Thinker' system to manage periodic updates, and calculates minimum and maximum light levels. This modular approach allowed DOOM's developers to reuse the flickering logic across multiple levels without duplicating code. At the time, memory management was a critical concern, and the use of the `Z_Malloc` function reflects id Software's careful handling of resources. The idea of associating dynamic effects with game sectors became a standard practice in level design, influencing engines like Source and Unity." - - id: "broken-light-flash-effect" + content: "The `P_SpawnFireFlicker` function initializes the firelight flicker effect for a given sector. It allocates memory for a `fireflicker_t` structure, sets the sector's light level bounds, and registers the flicker as a 'thinker'—a modular unit of game logic that DOOM's engine processes during each frame. This modular design allowed DOOM to handle complex behaviors like lighting, enemy AI, and environmental effects efficiently. By resetting the sector's special attributes (`sector->special = 0`), the function ensures that the flicker effect does not interfere with other gameplay mechanics. This approach to modular game logic influenced the design of later engines, including Quake and Half-Life." + - id: "light-flash-effect" line_start: 93 line_end: 113 - title: "Simulating Broken Lights with Random Timing" - wikipedia_url: "https://doomwiki.org/wiki/Lighting" + title: "The Algorithm Behind Flashing Lights" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `T_LightFlash` function creates the effect of broken or malfunctioning lights by alternating between maximum and minimum light levels with random timing. This randomness adds unpredictability, enhancing the eerie atmosphere of DOOM's levels. The use of pseudo-random numbers (`P_Random`) was a common technique in games of the era to simulate natural or chaotic phenomena. This effect, though simple, contributed to DOOM's reputation for immersive environments and was later refined in games like Half-Life, where lighting played a critical role in storytelling and mood." + content: "The `T_LightFlash` function creates a flashing light effect by toggling a sector's light level between `maxlight` and `minlight`. The timing of the flashes is randomized using `P_Random`, adding unpredictability to the effect. This feature was used to simulate broken or malfunctioning lights in DOOM's levels, contributing to the game's eerie atmosphere. In the early 1990s, such effects were groundbreaking, as most games relied on static lighting. Carmack's implementation demonstrated how simple algorithms could produce visually striking results. This technique influenced the development of horror games like Resident Evil, where lighting plays a critical role in creating tension." - id: "spawn-light-flash" line_start: 118 line_end: 142 - title: "Automating Broken Light Effects in Levels" - wikipedia_url: "https://doomwiki.org/wiki/Sector_specials" + title: "How DOOM Spawns Flashing Light Effects" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_SpawnLightFlash` function automates the creation of broken light effects for sectors in a level. It sets up the 'Thinker' system to periodically update the light level, ensuring the effect persists throughout gameplay. By scanning sectors for special attributes after a map is loaded, DOOM's engine could dynamically apply effects without hardcoding them into the level design. This flexibility allowed level designers to focus on creativity while the engine handled technical implementation. The modularity of this system influenced later engines like Unreal Engine, which adopted similar approaches for dynamic environment effects." + content: "The `P_SpawnLightFlash` function initializes the flashing light effect for a sector. It allocates memory for a `lightflash_t` structure, sets the light level bounds, and registers the flash as a 'thinker' for periodic updates. This modular approach allowed DOOM to manage dynamic lighting effects efficiently without hardcoding behaviors into the engine. By scanning the map for sectors with special attributes, the function ensured that lighting effects were applied only where intended. This design philosophy—separating data from logic—became a cornerstone of modern game development, influencing engines like Source and CryEngine." - id: "strobe-light-effect" line_start: 151 line_end: 208 - title: "The Algorithm Behind Strobe Lighting" - wikipedia_url: "https://doomwiki.org/wiki/Doom_rendering_engine" + title: "The Strobe Light Algorithm That Defined DOOM" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `T_StrobeFlash` function implements strobe lighting by alternating between bright and dark light levels at fixed intervals. This effect was used to create dramatic and unsettling environments, particularly in DOOM's more intense levels. The function's simplicity reflects id Software's philosophy of building efficient, reusable code. Strobe lighting became a staple in horror and action games, influencing level design in titles like Resident Evil and Dead Space, where lighting is used to heighten tension and direct player focus." - - id: "spawn-strobe-light" + content: "The `T_StrobeFlash` function simulates strobe lighting by alternating a sector's light level between `maxlight` and `minlight` at regular intervals. The timing is controlled by `brighttime` and `darktime`, allowing for customization of the strobe's speed. This effect was used to create dramatic lighting in DOOM's levels, enhancing the game's visual impact. In the early 1990s, strobe lighting was rarely seen in games due to hardware limitations. Carmack's efficient implementation demonstrated how clever programming could overcome these constraints. The strobe effect later influenced the design of dynamic lighting in games like System Shock and Deus Ex." + - id: "start-light-strobing" line_start: 211 line_end: 228 - title: "Synchronizing Strobe Lights Across Levels" - wikipedia_url: "https://doomwiki.org/wiki/Sector_specials" + title: "Triggering Strobe Lights in DOOM" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_SpawnStrobeFlash` function initializes strobe lighting for a sector, with options for synchronization and speed. By allowing strobe lights to operate in sync or independently, DOOM's engine provided level designers with greater creative control. This function also demonstrates the modularity of DOOM's 'Thinker' system, which was designed to handle diverse game logic efficiently. The ability to synchronize effects across sectors influenced later engines, enabling complex environmental interactions in games like Bioshock and Portal." + content: "The `EV_StartLightStrobing` function activates strobe lighting in sectors tagged by a specific line. It iterates through all sectors in the map, checking for matching tags, and spawns strobe effects using `P_SpawnStrobeFlash`. This feature allowed level designers to create dynamic lighting effects triggered by player actions, such as entering a room or activating a switch. In 1993, this level of interactivity was rare, as most games featured static environments. DOOM's ability to dynamically alter lighting based on gameplay events set a new standard for immersion, influencing titles like Half-Life and BioShock." - id: "turn-tag-lights-off" line_start: 232 line_end: 306 - title: "How DOOM Turned Lights Off Dynamically" - wikipedia_url: "https://doomwiki.org/wiki/Lighting" + title: "How DOOM Turns Lights Off by Tag" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `EV_TurnTagLightsOff` function dynamically adjusts the light levels of sectors tagged with a specific line identifier, setting them to the minimum surrounding light level. This feature allowed for scripted events, such as lights going out when a player triggers a trap. At the time, dynamic lighting adjustments were rare in games, as most relied on static pre-rendered lighting. DOOM's ability to manipulate light levels in real-time contributed to its immersive gameplay and inspired similar mechanics in games like System Shock and Thief." + content: "The `EV_TurnTagLightsOff` function reduces the light level of all sectors tagged by a specific line to the minimum surrounding light level. It scans each sector's lines to find adjacent sectors and determines the lowest light level among them. This effect was used to create dramatic transitions, such as plunging a room into darkness after a switch is activated. In the early 1990s, such dynamic lighting effects were uncommon, as most games relied on static environments. DOOM's ability to manipulate lighting in real-time influenced the design of games like Thief, where light and shadow are integral to gameplay." - id: "glowing-light-effect" line_start: 309 line_end: 337 - title: "The Glow That Made DOOM Feel Alive" - wikipedia_url: "https://doomwiki.org/wiki/Doom_rendering_engine" + title: "The Glow Effect That Brought DOOM to Life" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `T_Glow` function creates a glowing light effect by gradually increasing and decreasing the light level of a sector. This smooth transition between brightness levels added a sense of dynamism to DOOM's environments, making them feel more alive. The function uses a simple state machine to alternate between 'up' and 'down' directions, ensuring the glow effect loops seamlessly. This technique was groundbreaking for its time and laid the groundwork for more sophisticated lighting systems in modern engines like Unity and Unreal Engine, where dynamic lighting is a core feature." + content: "The `T_Glow` function creates a glowing light effect by gradually increasing or decreasing a sector's light level. The direction of the glow alternates when the light level reaches its bounds (`minlight` or `maxlight`). This effect added a sense of dynamism to DOOM's environments, making them feel alive. In 1993, glowing lights were a novel feature, as most games featured static lighting. Carmack's implementation demonstrated how simple algorithms could produce visually compelling results. The glow effect influenced the design of lighting systems in later games, including Unreal Tournament and Quake II." + - id: "spawn-glowing-light" + line_start: 340 + line_end: 355 + title: "How DOOM Spawns Glowing Lights" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + image_url: "" + image_caption: "" + content: "The `P_SpawnGlowingLight` function initializes the glowing light effect for a sector. It allocates memory for a `glow_t` structure, sets the light level bounds, and registers the glow as a 'thinker' for periodic updates. By scanning the map for sectors with special attributes, the function ensured that glowing lights were applied only where intended. This modular approach allowed DOOM to manage dynamic lighting effects efficiently without hardcoding behaviors into the engine. The glowing light effect became a hallmark of atmospheric level design, influencing games like Quake and Unreal." --- @@ -446,4 +454,5 @@ void P_SpawnGlowingLight(sector_t* sector) sector->special = 0; } -``` + +``` \ No newline at end of file diff --git a/public/programs/doom/p-map-c.md b/public/programs/doom/p-map-c.md index 0261564..5722640 100644 --- a/public/programs/doom/p-map-c.md +++ b/public/programs/doom/p-map-c.md @@ -9,114 +9,116 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-map-c" order: 3 -description: "This file reveals the inner workings of DOOM's movement, collision, and aiming mechanics, showcasing the ingenuity required to create a fast-paced 3D game on 1993 hardware." +description: "This file reveals the mechanics behind DOOM's movement, collision detection, and navigation, showcasing techniques that pushed the limits of 1993 hardware." summary: - - point: "DOOM's movement system uses bounding boxes for collision detection." - link: "https://en.wikipedia.org/wiki/Bounding_volume" - link_label: "Bounding Volume" - - point: "Sliding mechanics allow players to navigate angled walls smoothly." + - point: "DOOM's movement and collision system was optimized for speed on 1993 hardware." + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM (1993)" + - point: "Techniques like sliding along walls and teleportation were implemented to enhance gameplay." link: "https://doomwiki.org/wiki/Movement" link_label: "DOOM Movement" - - point: "Teleportation logic includes collision handling and object removal." - link: "https://doomwiki.org/wiki/Teleport" - link_label: "DOOM Teleportation" - - point: "Special lines trigger effects when crossed during movement." - link: "https://doomwiki.org/wiki/Linedef" - link_label: "Linedef in DOOM" - - point: "Height clipping ensures monsters adapt to changing sector heights." - link: "https://doomwiki.org/wiki/Sector" - link_label: "DOOM Sector" + - point: "The file demonstrates efficient handling of special effects like pickups and environmental interactions." + link: "https://doomwiki.org/wiki/Source_code" + link_label: "DOOM Source Code" enhancements: - - id: "bounding-box-collision-detection" + - id: "pit-stomp-thing-damage-mechanic" line_start: 73 - line_end: 107 - title: "How DOOM's bounding boxes avoid chaos" - wikipedia_url: "https://en.wikipedia.org/wiki/Bounding_volume" + line_end: 176 + title: "Stomping Things and Teleportation: DOOM's Forced-Move System" + wikipedia_url: "https://doomwiki.org/wiki/Monster_behavior" image_url: "" image_caption: "" - content: "This section defines key variables for DOOM's collision detection system, including bounding boxes (`tmbbox`) and flags (`tmflags`). Bounding boxes are used to approximate the area occupied by objects, simplifying collision checks. In 1993, this approach was crucial for performance, as CPUs like the Intel 486 lacked the power to handle complex geometric calculations in real-time. John Carmack's use of bounding boxes was inspired by earlier 2D games, but he extended the concept to handle DOOM's pseudo-3D world. This technique influenced later games, including Quake and Unreal, which refined collision detection for fully 3D environments." - - id: "teleportation-mechanics" - line_start: 73 - line_end: 176 - title: "Teleportation: Killing and moving in one step" - wikipedia_url: "https://doomwiki.org/wiki/Teleport" + content: "PIT_StompThing is the iterator callback that deals 10,000 points of instant-kill damage to any shootable object that overlaps with the object being moved, and it restricts monster stomping to map 30 (the Icon of Sin boss level) so that the boss brain's missile spawners can telefrag targets without monsters accidentally destroying each other anywhere else. P_TeleportMove is the driving function that invokes PIT_StompThing across all blockmap cells covered by the destination bounding box, then unlinks and relinks the teleporting object at its new position. The pairing of these two routines created DOOM's telefrag mechanic, where teleporting into an occupied space kills the occupant rather than failing, which became a celebrated deathmatch technique. In 1993, cleanly handling arbitrary teleport destinations in a 2.5D blockmap-based world was a non-trivial engineering challenge, and this stomp-then-place approach was an elegant solution. The telefrag pattern reappeared in Quake and later arena shooters as a deliberate competitive tool." + - id: "pit-check-line-collision-detection" + line_start: 184 + line_end: 246 + title: "Collision Detection: Adjusting Heights Dynamically" + wikipedia_url: "https://doomwiki.org/wiki/Collision_detection" image_url: "" image_caption: "" - content: "The `P_TeleportMove` function handles teleportation by first removing any objects occupying the destination, then updating the teleported object's position. Teleportation was a novel mechanic in 1993, adding a layer of strategy to DOOM's gameplay. The function ensures the teleported object doesn't clip through walls or other objects, maintaining the game's fast-paced flow. Carmack's implementation was a response to the game's need for dynamic level interactions, and it laid the groundwork for similar mechanics in later titles like Quake and Half-Life, which expanded on the concept with portals and scripted events." - - id: "special-line-crossing-effects" - line_start: 810 - line_end: 1258 - title: "Triggering effects by crossing lines" - wikipedia_url: "https://doomwiki.org/wiki/Linedef" + content: "The `PIT_CheckLine` function adjusts floor and ceiling heights as objects interact with map lines. It ensures that objects do not cross one-sided or blocking lines, and tracks special lines for later processing. This dynamic adjustment was crucial for DOOM's fast-paced gameplay, allowing for seamless navigation through complex environments. In the early 1990s, collision detection was a computationally expensive process, but DOOM's implementation was optimized to run smoothly on consumer PCs. The approach influenced later engines like Quake's BSP-based collision system and Unreal Engine's dynamic environment handling." + - id: "pit-check-thing-object-interaction" + line_start: 248 + line_end: 516 + title: "Missiles, Monsters, and Special Pickups" + wikipedia_url: "https://doomwiki.org/wiki/Thing" image_url: "" image_caption: "" - content: "The `P_TryMove` function handles movement while checking for special lines that trigger effects. In DOOM, linedefs define walls and boundaries, some of which have special properties like opening doors or activating traps. This system allowed designers to create dynamic and interactive levels, a major innovation in 1993. The concept of triggering effects based on player movement became a staple in level design, influencing games like Half-Life and Portal, which used similar systems to create immersive and reactive environments." - - id: "height-clipping-for-monsters" - line_start: 1260 - line_end: 1264 - title: "How monsters adapt to changing floors" - wikipedia_url: "https://doomwiki.org/wiki/Sector" + content: "The `PIT_CheckThing` function handles interactions between objects, including missiles, monsters, and special pickups. It checks for collisions, applies damage, and processes special effects like item pickups. This function exemplifies DOOM's ability to manage diverse object behaviors within a single system. The logic for missiles avoiding their originator's species was particularly innovative, preventing unintended friendly fire. This system laid the groundwork for more sophisticated object interaction mechanics in later games, such as Quake's projectile physics and Half-Life's environmental puzzles." + - id: "p-thing-height-clip" + line_start: 519 + line_end: 557 + title: "Height Adjustment for Monsters" + wikipedia_url: "https://doomwiki.org/wiki/Height_clip" image_url: "" image_caption: "" - content: "The `P_ThingHeightClip` function ensures monsters adjust their position when the floor or ceiling height changes. This prevents them from becoming stranded or clipping through geometry. In DOOM, sectors define areas with unique floor and ceiling heights, creating the illusion of a 3D world. Height clipping was a clever workaround for the limitations of DOOM's engine, which relied on 2D maps with height data layered on top. This technique influenced later games with dynamic environments, such as Duke Nukem 3D and Half-Life, which expanded on the concept with fully destructible and interactive worlds." - - id: "slide-movement-mechanics" + content: "The `P_ThingHeightClip` function adjusts the height of monsters and objects when a sector changes height. It ensures that objects remain grounded or adjust their position to fit within the new floor and ceiling constraints. This was a critical feature for DOOM's dynamic environments, allowing for realistic interactions between objects and changing terrain. The function's ability to handle edge cases, such as floating monsters, showcased id Software's attention to detail. This technique influenced later games with dynamic environments, such as Unreal Tournament and Half-Life 2." + - id: "p-hit-slide-line" line_start: 578 line_end: 629 - title: "Sliding along walls: A kludgy masterpiece" - wikipedia_url: "https://doomwiki.org/wiki/Movement" + title: "Sliding Along Angled Walls" + wikipedia_url: "https://doomwiki.org/wiki/Slide_move" image_url: "" image_caption: "" - content: "The sliding movement system allows players to navigate angled walls smoothly, a feature critical to DOOM's fluid gameplay. The `P_SlideMove` function calculates how a player or object should adjust its trajectory when colliding with a wall. Carmack described this code as a 'kludgy mess,' but it was a pragmatic solution to a complex problem. Sliding mechanics were essential for maintaining the game's speed and responsiveness, and they influenced movement systems in later games like Quake and Unreal Tournament, which refined the concept for more advanced physics engines." - - id: "aiming-slopes-for-targeting" + content: "The `P_HitSlideLine` function calculates how a player or object should slide along a wall when colliding with it. It adjusts movement vectors based on the angle of the wall and the object's trajectory, allowing for smooth navigation around obstacles. This mechanic was essential for DOOM's fast-paced gameplay, as it prevented players from getting stuck on walls during intense combat. Sliding mechanics became a standard feature in later games, influencing titles like Quake and Unreal, which expanded on the concept with more advanced physics engines." + - id: "p-slide-move-kludgey-mess" + line_start: 632 + line_end: 807 + title: "Sliding: A Kludgy Mess That Worked" + wikipedia_url: "https://doomwiki.org/wiki/Slide_move" + image_url: "" + image_caption: "" + content: "The `P_SlideMove` function handles sliding movement when an object collides with a wall. It calculates the object's trajectory, adjusts its position, and retries the movement if necessary. The function's 'kludgy mess' comment reflects the challenges of implementing this feature on 1993 hardware, where computational resources were limited. Despite its complexity, the sliding mechanic was a critical part of DOOM's gameplay, allowing for fluid movement in tight spaces. This approach influenced later games, such as Quake and Unreal, which refined sliding mechanics with more advanced collision systems." + - id: "aiming-slopes-and-targeting-logic" line_start: 810 line_end: 892 - title: "Aiming Slopes: Precision in a 3D World" + title: "Aiming Slopes and Targeting Logic" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `PTR_AimTraverse` function dynamically calculates slopes to determine if a target can be aimed at. It checks whether the player's line of sight crosses a two-sided line or intersects a shootable object, adjusting the slope for aiming precision. In 1993, this approach was groundbreaking for simulating realistic aiming in a pseudo-3D environment. John Carmack's innovative use of fixed-point arithmetic ensured fast calculations on hardware with limited floating-point capabilities. This technique influenced later FPS games like Quake, which refined aiming mechanics further." - - id: "shooting-traverse-and-impact" + content: "The `PTR_AimTraverse` function calculates the slope and sets the target when aiming at objects or lines in the game world. It uses fixed-point arithmetic to determine whether a line or object is within the player's aiming range. This routine ensures that shots are blocked by walls, ceilings, or floors and can hit shootable objects. In 1993, this level of precision was groundbreaking for simulating realistic aiming mechanics on consumer hardware. John Carmack's use of fixed-point math avoided the performance penalties of floating-point calculations, which were slow on the 386 processors common at the time. This targeting logic laid the foundation for the immersive shooting mechanics that made DOOM so compelling. Later games like Quake refined these concepts further, introducing even more advanced collision detection and aiming systems." + - id: "shooting-traversal-and-impact-effects" line_start: 895 line_end: 1015 - title: "Shooting Traverse: Impact and Feedback" + title: "Shooting Traversal and Impact Effects" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `PTR_ShootTraverse` function simulates a projectile's path, checking for collisions with lines or objects. It handles special interactions like shooting sky textures and spawning visual effects (bullet puffs or blood). This gave DOOM its visceral feedback, making gunplay feel impactful. The function's efficient traversal algorithm allowed for real-time calculations on 1993 PCs, paving the way for modern hit detection systems in FPS games like Half-Life and Counter-Strike." - - id: "aim-line-attack" + content: "The `PTR_ShootTraverse` function handles the logic for bullets traveling through the game world, checking for collisions with lines and objects. It spawns visual effects like bullet puffs or blood splatters upon impact, enhancing the visceral experience of combat. This routine also includes a clever 'sky hack' to prevent bullets from hitting the sky texture, ensuring consistent gameplay visuals. The modular design of this function allowed DOOM to simulate complex interactions efficiently, even on limited hardware. The visual feedback provided by these effects became a hallmark of DOOM's gameplay and influenced countless first-person shooters that followed, including Half-Life and Call of Duty." + - id: "line-attack-aiming-and-testing" line_start: 1018 line_end: 1053 - title: "Line Attack: Calculating Aim in Real-Time" + title: "Line Attack: Aiming and Testing" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_AimLineAttack` function calculates the aiming slope for a projectile attack. It uses trigonometric lookups (finecosine and finesine arrays) to determine the endpoint of the attack based on the player's angle and distance. This function exemplifies Carmack's mastery of efficient algorithms, enabling smooth gameplay even on low-spec machines. The concept of line attacks influenced later games, including Unreal Tournament, which expanded aiming mechanics with advanced physics." - - id: "use-lines-for-interaction" + content: "The `P_AimLineAttack` function calculates the aiming slope for line attacks, determining whether a target is within the player's view and range. This routine uses trigonometric lookups from precomputed tables (`finecosine` and `finesine`) to optimize performance. By limiting calculations to the player's view angles, it ensures that the game remains responsive even on hardware with limited processing power. This approach reflects Carmack's mastery of optimization, which was critical for DOOM's success. The efficient implementation of aiming mechanics influenced later engines, such as the Quake engine, which expanded on these techniques to support 3D aiming and more complex physics." + - id: "use-lines-player-interaction" line_start: 1094 - line_end: 1122 - title: "Using Lines: Activating the World" - wikipedia_url: "https://doomwiki.org/wiki/Line" + line_end: 1155 + title: "Use Lines: Player Interaction" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_UseLines` function allows players to interact with special lines in the environment, such as opening doors or triggering switches. It calculates the player's position and angle to identify nearby lines within a defined range. This system was crucial for DOOM's interactive level design, enabling puzzles and dynamic environments. The concept of 'use lines' became a staple in level design, influencing games like Duke Nukem 3D and later RPGs with interactive worlds." - - id: "radius-attack-damage" + content: "The `P_UseLines` function enables players to interact with special lines in the game world, such as doors, switches, or secret walls. It calculates the player's position and angle to determine the range of interaction. This routine uses path traversal to identify lines within reach and activates their associated effects. The ability to interact with the environment added depth to DOOM's gameplay, encouraging exploration and puzzle-solving. This mechanic became a staple in first-person shooters, influencing games like System Shock and Deus Ex, which expanded environmental interaction into more complex narratives and gameplay systems." + - id: "explosions-radius-based-damage" line_start: 1158 line_end: 1250 - title: "Radius Attack: Explosions with Spatial Awareness" - wikipedia_url: "https://en.wikipedia.org/wiki/Explosion_(video_games)" + title: "Explosions: Radius-Based Damage" + wikipedia_url: "https://en.wikipedia.org/wiki/Spatial_partitioning" image_url: "" image_caption: "" - content: "The `P_RadiusAttack` function calculates damage from explosions based on distance and line of sight. It iterates over all objects within a defined radius, checking whether they are shootable and visible. This spatial awareness added realism to DOOM's gameplay, making explosions feel impactful and strategic. The radius-based damage mechanic influenced later games like Call of Duty, which refined area-of-effect calculations for grenades and other explosive weapons." - - id: "sector-height-adjustments" + content: "The `PIT_RadiusAttack` and `P_RadiusAttack` functions calculate damage from explosions based on distance and line-of-sight. These routines iterate over spatial blocks to identify objects within the blast radius, ensuring efficient computation even in large levels. The use of spatial partitioning allowed DOOM to handle complex scenarios like rocket explosions without significant performance loss. This technique became a foundational element in game design, influencing titles like Unreal Tournament and Halo, which relied on similar methods for handling area-of-effect damage." + - id: "sector-height-changes-and-crushing" line_start: 1253 line_end: 1312 - title: "Sector Height: Dynamic Environmental Changes" - wikipedia_url: "https://doomwiki.org/wiki/Sector" + title: "Sector Height Changes and Crushing" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_engine" image_url: "" image_caption: "" - content: "The `P_ChangeSector` function adjusts the floor or ceiling height of a sector, ensuring objects within the sector adapt to the new geometry. It handles edge cases like crushing objects or gibbing corpses, adding a layer of interactivity to DOOM's levels. This dynamic environment manipulation was ahead of its time, influencing games like Minecraft, which expanded on the idea of interactive terrain." + content: "The `PIT_ChangeSector` and `P_ChangeSector` functions handle adjustments to sector heights, such as raising or lowering floors and ceilings. These routines ensure that objects within the sector are repositioned appropriately, handling edge cases like crushing players or objects. The ability to dynamically modify sectors was a key feature of DOOM's level design, enabling moving platforms, elevators, and environmental hazards. This innovation influenced future engines like the Build engine (used in Duke Nukem 3D) and the Source engine (used in Half-Life 2), which expanded on dynamic level geometry to create even more interactive environments." --- @@ -1458,4 +1460,5 @@ P_ChangeSector return nofit; } -``` + +``` \ No newline at end of file diff --git a/public/programs/doom/p-maputl-c.md b/public/programs/doom/p-maputl-c.md index 4c53acb..70b4861 100644 --- a/public/programs/doom/p-maputl-c.md +++ b/public/programs/doom/p-maputl-c.md @@ -9,66 +9,82 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-maputl-c" order: 29 -description: "Utility functions for movement and collision detection in DOOM's map system, showcasing innovative techniques for efficient gameplay mechanics." +description: "Utility functions for movement and collision detection in DOOM, showcasing optimization techniques for 1990s hardware." summary: - - point: "Approximation of distances using fixed-point arithmetic" + - point: "Efficient distance approximation using bitwise operations" + link: "https://en.wikipedia.org/wiki/Bitwise_operation" + link_label: "Bitwise Operations" + - point: "Collision detection optimized for block maps" + link: "https://en.wikipedia.org/wiki/Collision_detection" + link_label: "Collision Detection" + - point: "Traversal algorithms for map blocks and intercepts" + link: "https://en.wikipedia.org/wiki/Pathfinding" + link_label: "Pathfinding" + - point: "Fixed-point arithmetic to avoid floating-point overhead" link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" - link_label: "Fixed-point arithmetic" - - point: "Efficient line and object traversal in a grid-based map" - link: "https://doomwiki.org/wiki/Blockmap" - link_label: "DOOM Blockmap" - - point: "Handling object positions dynamically within sectors and blockmaps" - link: "https://doomwiki.org/wiki/Thing" - link_label: "DOOM Thing" - - point: "Optimized routines for intercept calculations" - link: "https://doomwiki.org/wiki/Intercept" - link_label: "Intercept calculations" - - point: "Path traversal algorithm for collision detection" - link: "https://en.wikipedia.org/wiki/Line_algorithm" - link_label: "Line algorithms" + link_label: "Fixed-Point Arithmetic" + - point: "Efficient handling of game objects in spatial structures" + link: "https://en.wikipedia.org/wiki/Spatial_index" + link_label: "Spatial Index" enhancements: - - id: "approx-distance-fixed-point" + - id: "approx-distance-optimization" line_start: 39 - line_end: 56 + line_end: 57 title: "The Trick That Made Distance Fast" wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" image_url: "" image_caption: "" - content: "The `P_AproxDistance` function provides a quick estimation of distance between two points using fixed-point arithmetic. Instead of calculating the exact Euclidean distance, which involves computationally expensive square roots, this function uses a heuristic that combines the absolute differences of x and y coordinates, subtracting half of the smaller difference. This approach was crucial for DOOM, as it allowed the game to perform distance calculations rapidly on the limited hardware of the early 1990s. Fixed-point arithmetic was a common choice for games of this era, as floating-point operations were slow or unavailable on consumer-grade CPUs. John Carmack, known for his optimization prowess, likely adapted this technique from earlier game development practices, ensuring DOOM's gameplay remained smooth even on modest PCs. This method influenced later game engines, where approximations were often preferred over exact calculations for real-time performance." + content: "The `P_AproxDistance` function calculates an approximate distance between two points using fixed-point arithmetic and bitwise shifts. Instead of relying on computationally expensive square root operations, it uses a heuristic formula that combines the absolute values of the differences in x and y coordinates, subtracting a fraction of the smaller difference. This approach was crucial for performance on 1990s hardware, where floating-point operations were slow and often avoided. John Carmack, known for his optimization prowess, likely devised this method to ensure DOOM's fast-paced gameplay remained smooth even on modest machines. This technique influenced later games and engines, where similar approximations were used for collision detection and AI pathfinding." - id: "point-on-line-side" line_start: 60 - line_end: 98 - title: "How DOOM Decided Front or Back" - wikipedia_url: "https://en.wikipedia.org/wiki/Line_algorithm" + line_end: 99 + title: "Point-on-Line-Side and Bounding Box Line Classification" + wikipedia_url: "https://en.wikipedia.org/wiki/Line_(geometry)" image_url: "" image_caption: "" - content: "The `P_PointOnLineSide` function determines whether a point lies on the front or back side of a line segment. This is achieved by comparing the relative positions of the point and the line's endpoints using fixed-point arithmetic. The function is optimized for cases where the line is vertical or horizontal, avoiding unnecessary calculations. This decision-making process was vital for DOOM's collision detection and rendering system, as it helped determine visibility and interactions between objects and map geometry. The technique reflects Carmack's ability to balance precision with performance, ensuring the game could handle complex environments without sacrificing speed. Similar algorithms have been used in countless games since, forming the backbone of spatial reasoning in 2D and 3D engines." - - id: "box-on-line-side" - line_start: 99 - line_end: 151 - title: "When a Box Crosses a Line" - wikipedia_url: "https://doomwiki.org/wiki/Blockmap" + content: "These two functions form the geometric backbone of DOOM's collision and visibility tests. `P_PointOnLineSide` determines whether a single map coordinate falls on the front (0) or back (1) side of a line segment using fixed-point cross-product arithmetic. Special cases for horizontal and vertical lines short-circuit the multiply path and return immediately from sign comparisons, a micro-optimization that mattered when the function was called thousands of times per frame on a 486. `P_BoxOnLineSide` extends the idea to axis-aligned bounding boxes: it calls P_PointOnLineSide on the two corners that bracket each slope type, then returns 0, 1, or -1 to indicate entirely front, entirely back, or straddling. The precomputed slopetype field in each line_t (ST_HORIZONTAL, ST_VERTICAL, ST_POSITIVE, ST_NEGATIVE) means the switch chooses the right corner pair with no trigonometry at runtime. Together these primitives underpin every sector crossing test, blockmap query, and portal traversal in the engine, and the same sign-of-cross-product idiom reappeared in Quake and nearly every BSP-based renderer that followed." + - id: "intercept-vector-calculation" + line_start: 222 + line_end: 284 + title: "The Math Behind Line Intercepts" + wikipedia_url: "https://en.wikipedia.org/wiki/Line_intersection" image_url: "" image_caption: "" - content: "The `P_BoxOnLineSide` function extends the logic of `P_PointOnLineSide` to handle bounding boxes. It determines whether a box is entirely on one side of a line, crosses it, or is ambiguous. This function is critical for DOOM's collision detection system, especially when dealing with moving objects like enemies or projectiles. By treating lines as infinite and using precomputed slope types, the function avoids unnecessary calculations, a necessity given the hardware constraints of the time. The bounding box approach was a clever optimization, allowing DOOM to handle interactions efficiently without resorting to pixel-perfect checks. This technique influenced later games and engines, where bounding boxes became a standard tool for collision detection and spatial reasoning." - - id: "unset-thing-position" + content: "The `P_InterceptVector` function calculates the fractional intercept point where two lines intersect. This is used extensively in DOOM's traversal algorithms to determine the exact point of interaction between a trace line and map geometry. The function employs fixed-point arithmetic to avoid the overhead of floating-point calculations, a necessity for performance on early 1990s hardware. This precise mathematical approach enabled DOOM to handle complex environments with numerous intersecting lines efficiently. The technique became a standard in game development, influencing collision detection and raycasting systems in later engines like Quake and Unreal." + - id: "line-opening-calculation" + line_start: 299 + line_end: 331 + title: "Finding Openings Between Walls" + wikipedia_url: "https://en.wikipedia.org/wiki/Portal_rendering" + image_url: "" + image_caption: "" + content: "The `P_LineOpening` function calculates the open space between two-sided lines, setting values for `opentop`, `openbottom`, and `openrange`. These values represent the vertical window through which objects can pass. This calculation is essential for DOOM's movement and collision systems, ensuring that objects interact correctly with map geometry. The function's optimization hints at Carmack's philosophy of precalculating data wherever possible to minimize runtime overhead. This approach influenced portal rendering techniques and spatial partitioning in later games, enabling efficient handling of complex 3D environments." + - id: "thing-position-management" line_start: 339 line_end: 385 - title: "Unlinking Objects from the World" - wikipedia_url: "https://doomwiki.org/wiki/Thing" + title: "How DOOM Tracks Moving Objects" + wikipedia_url: "https://en.wikipedia.org/wiki/Spatial_index" image_url: "" image_caption: "" - content: "The `P_UnsetThingPosition` function removes an object (or 'thing') from the game's spatial structures, including the blockmap and sector lists. This is necessary whenever an object's position changes, ensuring the game's lookup tables remain accurate. The function handles both dynamic and static objects, checking flags to determine whether they need to be unlinked. This approach reflects DOOM's modular design, where objects are dynamically managed within a grid-based map system. The ability to efficiently update spatial data was crucial for DOOM's fast-paced gameplay, allowing objects to move seamlessly without causing lag or errors. This dynamic management system influenced later engines, where similar techniques are used to handle object interactions in real-time." - - id: "path-traverse-algorithm" + content: "The `P_UnsetThingPosition` and `P_SetThingPosition` functions manage the spatial indexing of game objects. When an object's position changes, these functions update its links in the block map and subsector structures. This ensures that collision detection and rendering systems have accurate information about object locations. The use of linked lists for spatial indexing reflects the era's constraints, where memory efficiency was as critical as processing speed. These techniques were foundational for later engines, influencing how dynamic objects are handled in real-time simulations and games." + - id: "block-map-iteration" line_start: 473 line_end: 560 + title: "Iterating Through DOOM's Spatial Grid" + wikipedia_url: "https://en.wikipedia.org/wiki/Grid-based_pathfinding" + image_url: "" + image_caption: "" + content: "The `P_BlockLinesIterator` and `P_BlockThingsIterator` functions traverse the block map, calling specific functions for each line or object within a given map block. This grid-based approach to spatial indexing allows DOOM to efficiently check for collisions and interactions within localized areas of the map. The use of validcount flags prevents redundant checks, optimizing performance. These traversal methods were pivotal for DOOM's ability to handle large, interactive environments on limited hardware. They influenced grid-based pathfinding and spatial partitioning techniques in later games and simulations." + - id: "path-traversal-algorithm" + line_start: 742 + line_end: 879 title: "Tracing Paths Through DOOM's World" - wikipedia_url: "https://en.wikipedia.org/wiki/Line_algorithm" + wikipedia_url: "https://en.wikipedia.org/wiki/Ray_casting" image_url: "" image_caption: "" - content: "The `P_PathTraverse` function traces a line through the game's blockmap, calling a traversal function for each block it intersects. This algorithm is used for collision detection, line-of-sight checks, and other spatial queries. By stepping through map blocks and sorting intercepts, the function ensures accurate results while minimizing unnecessary calculations. The early-out mechanism allows the function to terminate quickly if certain conditions are met, further optimizing performance. This path traversal algorithm showcases Carmack's ability to adapt computational geometry to the constraints of real-time gaming. It influenced later engines, where efficient spatial queries are essential for handling large, complex environments." + content: "The `P_PathTraverse` function traces a line through the map, calling a traverser function for each block it crosses. This algorithm is used for raycasting, collision detection, and determining visibility. It employs fixed-point arithmetic and precomputed spatial data to optimize performance. The function's design reflects the constraints of 1990s hardware, where efficient algorithms were necessary to achieve real-time gameplay. This path traversal technique influenced raycasting methods in later engines, becoming a cornerstone of 3D rendering and collision systems." --- @@ -952,4 +968,7 @@ P_PathTraverse // go through the sorted list return P_TraverseIntercepts ( trav, FRACUNIT ); } -``` + + + +``` \ No newline at end of file diff --git a/public/programs/doom/p-mobj-c.md b/public/programs/doom/p-mobj-c.md index 5dca880..602a286 100644 --- a/public/programs/doom/p-mobj-c.md +++ b/public/programs/doom/p-mobj-c.md @@ -9,98 +9,90 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-mobj-c" order: 9 -description: "This file handles the movement, spawning, and state transitions of objects in DOOM, forming the backbone of its dynamic gameplay." +description: "This file handles moving objects (mobjs) in DOOM, including their spawning, movement, and interactions with the environment." summary: - - point: "Introduces modular handling of moving objects (mobjs)" + - point: "Introduces state-based object handling for mobjs" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Implements state-based object behavior and transitions" - link: "https://en.wikipedia.org/wiki/Finite-state_machine" - link_label: "Finite-state machine" - - point: "Optimizes movement physics for performance on 1990s hardware" - link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" - link_label: "Fixed-point arithmetic" - - point: "Introduces respawn mechanics for nightmare difficulty and deathmatch modes" - link: "https://doom.fandom.com/wiki/Nightmare" - link_label: "Nightmare mode" - - point: "Defines missile spawning and aiming logic, enabling ranged combat" - link: "https://doom.fandom.com/wiki/Missile" + link_label: "DOOM (1993)" + - point: "Implements friction and gravity for realistic movement" + link: "https://en.wikipedia.org/wiki/Physics_engine" + link_label: "Physics engine" + - point: "Nightmare respawn mechanics for increased difficulty" + link: "https://doomwiki.org/wiki/Nightmare_skill_level" + link_label: "Nightmare skill level" + - point: "Efficient spawning and removal of objects" + link: "https://doomwiki.org/wiki/Spawn" + link_label: "Spawn mechanics" + - point: "Handles missile trajectory and collision" + link: "https://doomwiki.org/wiki/Missile" link_label: "Missile mechanics" enhancements: - - id: "mobj-state-machine" + - id: "state-driven-object-handling" line_start: 52 - line_end: 82 - title: "How DOOM Objects Change State Dynamically" + line_end: 83 + title: "State-driven object handling for mobjs" wikipedia_url: "https://en.wikipedia.org/wiki/Finite-state_machine" image_url: "" image_caption: "" - content: "This section implements the state machine for DOOM's moving objects (mobjs). Each mobj has a state that determines its current behavior, sprite, and animation frame. The function `P_SetMobjState` transitions mobjs between states, triggering actions like movement or sound effects. The use of a state machine allowed developers to manage complex behaviors efficiently, such as enemy AI or environmental effects, without hardcoding every possible interaction. In 1993, this approach was innovative for games, enabling modularity and extensibility. The state machine concept influenced later game engines, including Quake and Unreal Engine, where similar systems manage object behaviors." - - id: "missile-explosion-handling" + content: "This section defines the `P_SetMobjState` function, which transitions a moving object (mobj) between states. Each state is associated with specific properties like sprite, frame, and duration (`tics`). The function also executes action routines tied to a state, enabling dynamic behaviors such as animations or sound effects. At the time, state machines were a common way to manage game objects, but DOOM's implementation stood out for its efficiency and extensibility. John Carmack designed this system to handle complex interactions while maintaining high performance on 1993-era hardware. This approach influenced later game engines, including Quake and Unreal Engine, where state-driven object management became a standard practice." + - id: "missile-explosion-mechanics" line_start: 86 line_end: 104 - title: "The Code Behind DOOM's Exploding Missiles" - wikipedia_url: "https://doom.fandom.com/wiki/Missile" + title: "Missile explosion mechanics in DOOM" + wikipedia_url: "https://doomwiki.org/wiki/Missile" image_url: "" image_caption: "" - content: "The `P_ExplodeMissile` function handles the logic for missile explosions, a key feature in DOOM's combat system. When a missile hits an obstacle or expires, it transitions to its death state, plays a sound effect, and stops moving. This mechanic added visceral feedback to combat, making explosions feel impactful. The randomization of explosion timing (`mo->tics -= P_Random()&3`) ensured that animations felt organic, avoiding mechanical repetition. This approach to missile handling influenced later games like Duke Nukem 3D and Half-Life, which refined projectile physics and effects." - - id: "xy-movement-physics" + content: "The `P_ExplodeMissile` function handles the destruction of missiles upon impact. It resets the missile's momentum, changes its state to a death animation, and plays a sound effect if defined. This ensures a satisfying visual and auditory feedback for players. The function also adjusts the missile's lifespan (`tics`) to introduce randomness, preventing predictable behavior. This mechanic was crucial in creating the chaotic and immersive combat experience that DOOM is known for. The technique of tying visual and audio effects to object destruction influenced many later games, including Half-Life and Halo, where similar mechanics are used to enhance player engagement." + - id: "xy-movement-with-friction" line_start: 107 line_end: 240 - title: "Physics That Made DOOM Feel Fast and Fluid" - wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + title: "XY movement with friction and sliding" + wikipedia_url: "https://en.wikipedia.org/wiki/Physics_engine" image_url: "" image_caption: "" - content: "The `P_XYMovement` function calculates horizontal movement for mobjs, including players and enemies. It handles collisions, sliding, and momentum, ensuring smooth navigation through DOOM's maze-like levels. The use of fixed-point arithmetic optimized calculations for the limited CPUs of the early 1990s, such as the Intel 486. This section also includes clever hacks, like preventing missiles from exploding against sky textures (`ceilingline->backsector->ceilingpic == skyflatnum`). These optimizations and tricks became standard in game development, influencing engines like Build and Source." - - id: "z-movement-logic" + content: "The `P_XYMovement` function calculates the horizontal movement of mobjs, incorporating friction and collision detection. It uses a fixed-point arithmetic system to ensure precision on hardware without floating-point support. The function handles special cases like missiles, corpses, and airborne objects, applying different rules for momentum and stopping. Sliding mechanics allow objects to move along walls, enhancing realism. This was a groundbreaking feature in 1993, as most games of the era used simpler movement models. The sliding and friction mechanics laid the foundation for more advanced physics engines in later games like Quake and Unreal Tournament, which expanded on these ideas to simulate realistic environments." + - id: "z-movement-and-gravity" line_start: 242 line_end: 348 - title: "Vertical Movement: Gravity and Floating Enemies" - wikipedia_url: "https://en.wikipedia.org/wiki/Gravity_(game_physics)" + title: "Z movement and gravity simulation" + wikipedia_url: "https://en.wikipedia.org/wiki/Gravity_(physics)" image_url: "" image_caption: "" - content: "The `P_ZMovement` function governs vertical movement, including gravity, floating behavior, and collisions with floors and ceilings. This section highlights DOOM's ability to simulate realistic physics while accommodating fantastical elements like floating enemies (`MF_FLOAT`). It also includes player-specific effects, such as view height adjustments after hard landings (`mo->player->deltaviewheight`). These mechanics contributed to DOOM's immersive gameplay and inspired similar systems in later 3D games, including Quake and Unreal Tournament." - - id: "nightmare-respawn" + content: "The `P_ZMovement` function governs vertical movement, including gravity and collision with floors and ceilings. It adjusts the object's height (`z`) based on its momentum (`momz`) and applies gravity unless the object is flagged as floating. Special cases include missiles exploding on impact and players squatting after a hard landing. This realistic treatment of vertical movement was rare in games of the era, as most relied on simple 2D physics. John Carmack's innovative use of fixed-point arithmetic and conditional logic allowed DOOM to simulate complex environments on limited hardware. This approach influenced later games like Quake, which expanded vertical movement to include jumping and swimming." + - id: "nightmare-respawn-mechanic" line_start: 352 line_end: 408 - title: "Nightmare Mode: Monsters Never Stay Dead" - wikipedia_url: "https://doom.fandom.com/wiki/Nightmare" + title: "Nightmare respawn: Monsters never stay dead" + wikipedia_url: "https://doomwiki.org/wiki/Nightmare_skill_level" image_url: "" image_caption: "" - content: "The `P_NightmareRespawn` function implements the respawn logic for monsters in DOOM's nightmare difficulty. When a monster dies, it leaves behind a 'teleport fog' effect and eventually respawns at its original position. This mechanic added tension and replayability, as players faced relentless enemies. The respawn system was a precursor to mechanics in survival games like Left 4 Dead, where enemy persistence creates a constant challenge. DOOM's nightmare mode remains iconic, influencing difficulty design across genres." - - id: "spawn-mobj" + content: "The `P_NightmareRespawn` function implements the respawn mechanic for monsters in Nightmare difficulty. When a monster is killed, it can respawn at its original location after a delay, accompanied by teleportation effects. This mechanic was designed to increase the challenge for experienced players, making DOOM one of the first games to feature dynamic respawning. The use of visual and audio cues, such as teleport fog and sound effects, enhances the player's sense of urgency. This feature inspired similar mechanics in later games like Left 4 Dead, where respawning enemies keep players on edge." + - id: "object-spawning-system" line_start: 475 line_end: 533 - title: "The Function That Brought DOOM's World to Life" - wikipedia_url: "https://en.wikipedia.org/wiki/Object-oriented_programming" + title: "Efficient object spawning system" + wikipedia_url: "https://doomwiki.org/wiki/Spawn" image_url: "" image_caption: "" - content: "The `P_SpawnMobj` function is responsible for creating mobjs, the dynamic entities that populate DOOM's levels. It initializes properties like position, health, and state, and links the object to the game's thinker system for ongoing updates. This modular approach allowed developers to easily add new objects and behaviors, laying the groundwork for object-oriented programming in games. The concept of spawning objects dynamically influenced countless game engines, including Unity and Unreal Engine." - - id: "respawn-specials" - line_start: 574 - line_end: 630 - title: "Deathmatch Item Respawn: Keeping the Fight Alive" - wikipedia_url: "https://doom.fandom.com/wiki/Deathmatch" - image_url: "" - image_caption: "" - content: "The `P_RespawnSpecials` function handles item respawns in deathmatch mode, ensuring that players always have access to weapons and power-ups. Items respawn after a fixed delay, accompanied by visual and sound effects like teleport fog (`MT_IFOG`) and the teleport sound (`sfx_itmbk`). This mechanic was crucial for maintaining balance and pacing in multiplayer matches, influencing later games like Quake and Unreal Tournament, which refined item respawn systems for competitive play." - - id: "spawn-player" + content: "The `P_SpawnMobj` function initializes new mobjs, setting their position, state, and physical properties. It uses a memory allocation system (`Z_Malloc`) to manage resources efficiently, critical for running on hardware with limited RAM. This function also links the object to the game's spatial data structures, ensuring proper interaction with the environment. The spawning system was a key innovation in DOOM, enabling dynamic gameplay with numerous active objects. This technique influenced later engines like Source and Unreal Engine, which adopted similar systems for efficient object management." + - id: "player-spawn-mechanics" line_start: 635 line_end: 699 - title: "Player Spawning: Setting the Stage for Action" - wikipedia_url: "https://doom.fandom.com/wiki/Player" + title: "Player spawn mechanics and setup" + wikipedia_url: "https://doomwiki.org/wiki/Player" image_url: "" image_caption: "" - content: "The `P_SpawnPlayer` function initializes players when they enter a level, setting properties like health, position, and view height. It also equips players with all keycards in deathmatch mode, ensuring they can access every area. This function highlights DOOM's focus on multiplayer accessibility and level design flexibility. The concept of player spawning influenced multiplayer game design in titles like Counter-Strike and Call of Duty, where spawn points are critical for gameplay balance." - - id: "spawn-missile" + content: "The `P_SpawnPlayer` function handles player initialization when entering a level. It sets up the player's position, health, and inventory, and activates the heads-up display (HUD) and status bar. In deathmatch mode, players are given all keycards to ensure balanced gameplay. This function reflects DOOM's focus on multiplayer compatibility, a feature that helped popularize LAN gaming in the 1990s. The player spawn mechanics influenced later multiplayer games like Quake and Counter-Strike, which expanded on these ideas to support more complex game modes and player interactions." + - id: "missile-spawn-and-trajectory" line_start: 862 line_end: 926 - title: "Missile Spawning: Precision and Chaos Combined" - wikipedia_url: "https://doom.fandom.com/wiki/Missile" + title: "Missile spawn and trajectory calculation" + wikipedia_url: "https://doomwiki.org/wiki/Missile" image_url: "" image_caption: "" - content: "The `P_SpawnMissile` function creates missiles fired by enemies or players, calculating their trajectory based on the source and target positions. It incorporates randomness to simulate 'fuzzy' aiming for shadowed targets (`MF_SHADOW`) and adjusts vertical momentum for accurate targeting. This system added depth to DOOM's combat mechanics, inspiring projectile systems in later games like Unreal Tournament and Half-Life, which expanded on missile physics and targeting." + content: "The `P_SpawnMissile` function creates missiles and calculates their trajectory based on the source and target positions. It uses trigonometric functions (`finecosine` and `finesine`) for precise angle calculations, ensuring accurate movement. The function also accounts for target properties like shadow effects, introducing randomness to simulate imperfect aiming. This mechanic was crucial for DOOM's fast-paced combat, making projectiles feel dynamic and unpredictable. The missile spawning system influenced later games like Unreal Tournament and Halo, which refined projectile physics to include advanced features like homing and ricochet." --- @@ -1091,4 +1083,5 @@ P_SpawnPlayerMissile P_CheckMissileSpawn (th); } -``` + +``` \ No newline at end of file diff --git a/public/programs/doom/p-pspr-c.md b/public/programs/doom/p-pspr-c.md index 0172c6c..5bde1cd 100644 --- a/public/programs/doom/p-pspr-c.md +++ b/public/programs/doom/p-pspr-c.md @@ -9,90 +9,76 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-pspr-c" order: 30 -description: "This file handles weapon animations, state transitions, and player interactions in DOOM, showcasing the game's innovative sprite-based weapon system." +description: "This file handles weapon sprite animations and player weapon actions in DOOM, showcasing techniques that pushed the boundaries of 3D gaming on early PCs." summary: - - point: "Introduces sprite-based weapon animations for immersive gameplay" + - point: "Dynamic weapon sprite management for immersive gameplay" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Optimized weapon state management for performance on 1990s hardware" - link: "https://en.wikipedia.org/wiki/State_machine" - link_label: "State Machine" - - point: "Dynamic weapon behavior influenced by player actions and environment" - link: "https://en.wikipedia.org/wiki/First-person_shooter" - link_label: "First-person Shooter" - - point: "Pioneered modular weapon handling techniques later adopted in game engines" - link: "https://en.wikipedia.org/wiki/Game_engine" - link_label: "Game Engine" - - point: "Showcases clever use of lookup tables and fixed-point arithmetic" - link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" - link_label: "Fixed-point Arithmetic" + - point: "Efficient state-based animation system for weapons" + link: "https://en.wikipedia.org/wiki/Finite-state_machine" + link_label: "Finite-state machine" + - point: "Innovative use of pseudo-randomness for gameplay variability" + link: "https://en.wikipedia.org/wiki/Random_number_generation" + link_label: "Random number generation" enhancements: - - id: "weapon-state-machine" - line_start: 54 + - id: "weapon-sprite-state-machine" + line_start: 55 line_end: 101 - title: "How DOOM's Weapons Became State Machines" - wikipedia_url: "https://en.wikipedia.org/wiki/State_machine" + title: "How DOOM Animated Weapon Sprites" + wikipedia_url: "https://en.wikipedia.org/wiki/Finite-state_machine" image_url: "" image_caption: "" - content: "The `P_SetPsprite` function is the heart of DOOM's weapon state management system. It transitions a weapon's sprite through various states, such as firing, reloading, or being idle. Each state is defined by a `statenum_t` identifier, and the function updates the sprite's position, animation ticks, and calls any associated action routines. This modular approach allowed DOOM's developers to easily add new weapons or modify existing ones. In 1993, the concept of state machines was well-known in computer science but had rarely been applied to video games in such a structured way. John Carmack's use of this technique ensured that DOOM could handle complex weapon behaviors efficiently on the limited hardware of the era, such as 386 and 486 processors. This system influenced later games and engines, such as Quake and Unreal Engine, which adopted similar modular state-based designs for handling animations and player interactions." - - id: "weapon-bob-simulation" + content: "The `P_SetPsprite` function dynamically updates the state of a weapon sprite based on the player's actions and the game's internal state machine. Each weapon sprite is associated with a state, which includes animation frames, timing (`tics`), and optional actions. The function loops through states until a valid animation frame is found, allowing for seamless transitions between weapon animations. This approach was groundbreaking in 1993, as it enabled fluid and responsive weapon animations on hardware with limited graphical capabilities. The use of state machines for animations became a staple in game development, influencing later titles like Quake and Unreal." + - id: "weapon-bob-swing-effect" line_start: 111 line_end: 127 - title: "Simulating Weapon Bob with Fixed-point Math" - wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + title: "The Bobbing Swing That Made Weapons Feel Real" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_CalcSwing` function calculates the bobbing motion of the player's weapon based on movement speed. Using fixed-point arithmetic and sine lookup tables, it creates a smooth, immersive effect that mimics the natural sway of a weapon as the player moves. In the early 1990s, floating-point operations were computationally expensive, especially on consumer-grade CPUs. Fixed-point math was a clever workaround, allowing DOOM to achieve fluid animations without sacrificing performance. This technique became a hallmark of id Software's optimization prowess and influenced other games, such as Duke Nukem 3D and Half-Life, which also incorporated weapon bobbing for realism." + content: "The `P_CalcSwing` function calculates the bobbing motion of the player's weapon based on movement speed and level time. By using trigonometric functions (`finesine`), the weapon appears to swing naturally, enhancing the sense of immersion. This subtle detail contributed to DOOM's reputation for polished gameplay and realism. At the time, such effects were rare in games, as developers often prioritized performance over visual fidelity. The technique inspired similar implementations in later first-person shooters, including Half-Life and Call of Duty." - id: "weapon-switching-animation" line_start: 131 line_end: 153 - title: "The Animation Behind Weapon Switching" - wikipedia_url: "https://en.wikipedia.org/wiki/Animation" + title: "How DOOM Made Weapon Switching Satisfying" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_BringUpWeapon` function animates the process of switching weapons, moving the new weapon sprite from the bottom of the screen to its ready position. This visual feedback was groundbreaking in 1993, as it added a layer of realism and anticipation to gameplay. The function also plays specific sound effects, such as the chainsaw's revving noise, enhancing the sensory experience. This approach set a precedent for weapon switching in first-person shooters, influencing later games like Counter-Strike and Call of Duty, where weapon animations became integral to the player's experience." - - id: "ammo-checking-and-auto-switch" + content: "The `P_BringUpWeapon` function handles the animation of a weapon being raised from the bottom of the screen. This visual cue informs players of the weapon change and adds a tactile feel to the action. The function also plays sound effects for specific weapons, such as the chainsaw, further enhancing the experience. In an era when many games lacked such polish, DOOM's attention to detail set a new standard for player feedback and immersion. This approach influenced later games, including Duke Nukem 3D and Serious Sam." + - id: "ammo-check-and-weapon-priority" line_start: 155 line_end: 239 - title: "What Happens When You Run Out of Ammo?" - wikipedia_url: "https://en.wikipedia.org/wiki/Resource_management_(video_games)" + title: "The Logic Behind DOOM's Ammo Management" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_CheckAmmo` function ensures that players cannot fire weapons without sufficient ammunition. If ammo is depleted, the function automatically selects the next available weapon based on predefined preferences. This design choice kept gameplay fast-paced and uninterrupted, even in high-stakes scenarios. In the early 1990s, resource management in games was often clunky, requiring players to manually switch weapons. DOOM's automated system was a significant improvement, streamlining the experience and influencing later games like Halo and Borderlands, which adopted similar mechanics for seamless weapon transitions." - - id: "dynamic-weapon-bobbing" - line_start: 273 - line_end: 333 - title: "Dynamic Weapon Bobbing Based on Movement" - wikipedia_url: "https://en.wikipedia.org/wiki/First-person_shooter" - image_url: "" - image_caption: "" - content: "The `A_WeaponReady` function dynamically adjusts the weapon's position based on player movement, creating a bobbing effect that adds realism to the game. This feature was a technical marvel in 1993, as it used fixed-point arithmetic and sine tables to simulate smooth, natural motion on hardware with limited processing power. The bobbing effect became a staple of first-person shooters, influencing titles like GoldenEye 007 and Far Cry, which used similar techniques to enhance immersion." - - id: "chainsaw-melee-attack" - line_start: 497 - line_end: 542 - title: "The Chainsaw: A Brutal Melee Weapon" - wikipedia_url: "https://en.wikipedia.org/wiki/Chainsaw" - image_url: "" - image_caption: "" - content: "The `A_Saw` function handles the chainsaw's melee attack, calculating damage and determining whether the player hits a target. The chainsaw's unique behavior, including its sound effects and attack animations, made it one of DOOM's most iconic weapons. This function also adjusts the player's angle to face the target, emphasizing the visceral nature of close combat. The chainsaw's implementation influenced later games, such as Gears of War, which featured similarly brutal melee mechanics." + content: "The `P_CheckAmmo` function ensures the player has enough ammunition to fire their weapon and selects a fallback weapon if ammo is insufficient. The function prioritizes weapons based on availability and ammo type, with preferences tailored to the game's mode (e.g., shareware or commercial). This logic was essential for maintaining gameplay flow and preventing frustration. The concept of fallback weapon selection influenced later games, such as Halo and Borderlands, which expanded on the idea with dynamic weapon systems." - id: "bfg-explosion-simulation" line_start: 776 line_end: 810 - title: "The BFG: A Spray of Explosions" + title: "The Code Behind DOOM's Iconic BFG Spray" wikipedia_url: "https://en.wikipedia.org/wiki/BFG_(weapon)" image_url: "" image_caption: "" - content: "The `A_BFGSpray` function simulates the BFG's explosive attack, spawning projectiles that damage all enemies in view. This weapon was a technical showcase, demonstrating DOOM's ability to handle complex interactions between multiple objects in real-time. The BFG's design became legendary, influencing the creation of powerful weapons in games like Unreal Tournament and Destiny, where 'superweapons' are a key gameplay element." - - id: "level-initialization-weapons" + content: "The `A_BFGSpray` function simulates the BFG's unique attack, spawning explosions on every monster in view. By iterating through angles and targeting visible enemies, the function creates a devastating area-of-effect attack. This mechanic was a defining feature of the BFG weapon, making it a fan favorite. The implementation showcases DOOM's ability to balance power and strategy, as the weapon consumes significant ammo. The BFG's design influenced later games, such as Quake and Doom Eternal, which introduced their own variations of the weapon." + - id: "level-start-weapon-setup" line_start: 826 line_end: 841 - title: "Setting Up Weapons at Level Start" - wikipedia_url: "https://en.wikipedia.org/wiki/Initialization_(computer_science)" + title: "How DOOM Prepared Weapons for Each Level" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_SetupPsprites` function initializes the player's weapon sprites at the start of each level, ensuring a clean slate for gameplay. This routine removes any lingering weapon states and prepares the player's current weapon for use. In 1993, level initialization routines were critical for maintaining performance and avoiding bugs. DOOM's approach influenced later game engines, such as Unity and Unreal Engine, which adopted similar practices for object initialization." + content: "The `P_SetupPsprites` function initializes weapon sprites at the start of each level. It clears previous states and sets up the player's ready weapon, ensuring a smooth transition between gameplay sessions. This routine highlights DOOM's commitment to consistency and polish, as players never encounter graphical glitches or unresponsive controls when starting a new level. The approach influenced later games with level-based progression, such as Hexen and Unreal Tournament." + - id: "weapon-animation-tick-handler" + line_start: 846 + line_end: 876 + title: "The Ticking Heart of DOOM's Weapon System" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + image_url: "" + image_caption: "" + content: "The `P_MovePsprites` function updates weapon sprites every game tick, handling animation transitions and synchronization with player actions. By decrementing `tics` and switching states, the function ensures smooth and responsive animations. This design was crucial for DOOM's fast-paced gameplay, as players relied on immediate feedback during combat. The concept of tick-based animation systems became standard in game engines, influencing titles like Unreal Engine and Unity." --- @@ -973,4 +959,6 @@ void P_MovePsprites (player_t* player) player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; } -``` + + +``` \ No newline at end of file diff --git a/public/programs/doom/p-saveg-c.md b/public/programs/doom/p-saveg-c.md index 93fee4d..0193552 100644 --- a/public/programs/doom/p-saveg-c.md +++ b/public/programs/doom/p-saveg-c.md @@ -9,98 +9,84 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-saveg-c" order: 31 -description: "This file handles the save and load functionality for DOOM's game state, including players, world geometry, and active game objects ('thinkers'). It showcases the ingenuity required to serialize complex game states on 1990s hardware." +description: "This file implements DOOM's savegame system, allowing players to persist their progress in a groundbreaking 3D environment." summary: - - point: "Introduces padding for cross-platform save compatibility" - link: "https://en.wikipedia.org/wiki/Data_structure_alignment" - link_label: "Data structure alignment" - - point: "Efficiently serializes and deserializes player states" - link: "https://en.wikipedia.org/wiki/Serialization" - link_label: "Serialization" - - point: "Handles dynamic game objects ('thinkers') in save files" + - point: "Introduces a modular save/load system for game state" + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM (1993)" + - point: "Uses memory alignment tricks for cross-platform compatibility" + link: "https://en.wikipedia.org/wiki/Memory_alignment" + link_label: "Memory Alignment" + - point: "Handles complex game entities like players, world geometry, and active thinkers" link: "https://doomwiki.org/wiki/Thinker" - link_label: "Thinker system in DOOM" - - point: "Uses fixed-point arithmetic for world geometry serialization" - link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" - link_label: "Fixed-point arithmetic" - - point: "Demonstrates modular save/load design for extensibility" - link: "https://en.wikipedia.org/wiki/Modular_programming" - link_label: "Modular programming" + link_label: "Thinker System in DOOM" enhancements: - - id: "pad-save-pointer-for-cross-platform" - line_start: 37 - line_end: 39 - title: "Why Save Files Needed Padding" - wikipedia_url: "https://en.wikipedia.org/wiki/Data_structure_alignment" - image_url: "" - image_caption: "" - content: "This macro ensures that the save pointer (`save_p`) aligns to a 4-byte boundary, a requirement for compatibility across different architectures such as SGI and Gecko systems. In the early 1990s, hardware differences often led to subtle bugs in data serialization, as some systems required strict alignment for memory access. By padding the pointer, id Software avoided potential crashes or corrupted save files when transferring game states between platforms. This technique reflects the careful attention to cross-platform compatibility that was necessary in an era when gaming PCs varied widely in architecture. The padding approach influenced later serialization practices, especially in engines like Quake and Unreal, which also had to handle diverse hardware environments." - - id: "archive-players-game-state" + - id: "archive-players-savegame" line_start: 43 line_end: 71 - title: "How DOOM Saved Its Players" - wikipedia_url: "https://en.wikipedia.org/wiki/Serialization" + title: "How DOOM Saved Player State in 1993" + wikipedia_url: "https://doomwiki.org/wiki/Savegame" image_url: "" image_caption: "" - content: "The `P_ArchivePlayers` function serializes the state of all active players into the save file. It iterates through the `players` array, skipping inactive slots, and copies the player data into the save buffer (`save_p`). To ensure consistency, it adjusts pointers to sprite states by converting them into offsets relative to the global `states` array. This approach allowed DOOM to save complex player states, including their inventory, position, and animation frames, while maintaining portability across systems. At the time, saving such detailed game states was cutting-edge, as many games relied on simpler checkpoint systems. The technique laid the groundwork for more sophisticated save systems in later games, including RPGs and open-world titles." - - id: "unarchive-players-game-state" + content: "The `P_ArchivePlayers` function saves the state of all active players into a memory buffer for later retrieval. It iterates over the `players` array, skipping inactive players, and copies their data into the save buffer. A clever trick adjusts sprite state pointers to relative offsets, ensuring compatibility across memory layouts. This was crucial for DOOM's portability, as the game ran on multiple platforms, including SGI workstations and consumer PCs. At the time, saving game state was a technical challenge due to limited memory and the need for cross-platform compatibility. John Carmack's focus on modularity and efficiency led to this design, which influenced later games like Quake and Unreal. The modular savegame system became a standard feature in modern game engines, allowing developers to persist complex game states reliably." + - id: "unarchive-players-loadgame" line_start: 75 line_end: 107 - title: "Rebuilding Players from Save Files" - wikipedia_url: "https://en.wikipedia.org/wiki/Serialization" + title: "Rebuilding Players from Save Data" + wikipedia_url: "https://doomwiki.org/wiki/Savegame" image_url: "" image_caption: "" - content: "The `P_UnArchivePlayers` function reverses the serialization process, restoring player states from the save buffer. It carefully reconstructs pointers to sprite states and resets transient fields like `mo` (map object) and `message`. This meticulous restoration ensures that players resume their game exactly as they left it, including animations and interactions. The function also highlights the challenges of pointer-based data structures in save files, as pointers must be recalculated during deserialization. This technique influenced later game engines, which adopted similar methods for reconstructing complex game states, such as NPC behaviors and player inventories." - - id: "archive-world-geometry" + content: "The `P_UnArchivePlayers` function restores player data from a save buffer, reversing the process of `P_ArchivePlayers`. It carefully reconstructs pointers, such as sprite states, which were stored as relative offsets. This design ensured that saved games could be loaded seamlessly across different platforms and memory layouts. The function also resets transient player attributes like messages and attackers, which are not saved. In the early 1990s, savegame systems were often rudimentary, but DOOM's approach demonstrated how to handle complex game entities efficiently. This technique influenced the development of save/load systems in later games, including Quake and Half-Life, which built on DOOM's innovations to manage increasingly complex game states." + - id: "archive-world-savegame" line_start: 110 line_end: 159 - title: "Saving DOOM's World in Fixed-Point" - wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + title: "Saving the Geometry of Hell" + wikipedia_url: "https://doomwiki.org/wiki/Savegame" image_url: "" image_caption: "" - content: "The `P_ArchiveWorld` function serializes the game's world geometry, including sectors (rooms) and lines (walls). It uses fixed-point arithmetic to store heights and offsets, dividing by `FRACBITS` to convert from the internal representation to integers suitable for saving. Fixed-point arithmetic was a common choice in the 1990s, as floating-point operations were slow or unavailable on consumer CPUs. By saving only the essential attributes, such as floor textures and light levels, id Software optimized the save file size for the limited storage capacities of the era. This approach influenced later engines, which also prioritized efficient serialization of game worlds to minimize load times and disk usage." - - id: "unarchive-world-geometry" + content: "The `P_ArchiveWorld` function saves the state of the game's world, including sectors, lines, and sides. It writes attributes like floor and ceiling heights, textures, and light levels into the save buffer. This was a critical feature for DOOM's immersive environments, as it allowed players to return to their exact position in the game's labyrinthine levels. The function uses fixed-point arithmetic to store fractional values efficiently, reflecting the constraints of 1990s hardware. Saving world geometry was a novel challenge at the time, as most games were simpler and did not require such detailed persistence. DOOM's savegame system inspired similar features in later 3D games, including Duke Nukem 3D and Unreal, which expanded on this approach to handle even more complex environments." + - id: "unarchive-world-loadgame" line_start: 163 line_end: 210 - title: "Reconstructing DOOM's World from Disk" - wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + title: "Restoring Hell's Geometry from Memory" + wikipedia_url: "https://doomwiki.org/wiki/Savegame" image_url: "" image_caption: "" - content: "The `P_UnArchiveWorld` function restores the world geometry from a save file, reversing the fixed-point conversion to reconstruct heights and offsets. It also resets transient fields, such as `specialdata`, which are not saved but are required for gameplay. This function demonstrates the complexity of deserializing interconnected game elements, as sectors and lines must be restored in a way that preserves their relationships. The technique was essential for DOOM's fast-paced gameplay, allowing players to seamlessly reload their progress without noticeable delays. Similar methods were later adopted by engines like Quake and Source, which also needed to reconstruct dynamic worlds efficiently." - - id: "archive-thinkers-game-objects" + content: "The `P_UnArchiveWorld` function loads world geometry from a save buffer, reconstructing sectors, lines, and sides. It reverses the fixed-point arithmetic used during saving and resets transient attributes like special data and sound targets. This meticulous reconstruction ensures that the game's intricate levels are restored accurately, preserving the player's experience. In the early 1990s, such detailed save/load systems were rare, as most games relied on simpler checkpoint systems. DOOM's ability to persist complex environments set a new standard for 3D games, influencing titles like Hexen and System Shock, which adopted similar techniques for saving and loading detailed game worlds." + - id: "archive-thinkers-savegame" line_start: 228 line_end: 258 - title: "Saving DOOM's Dynamic Game Objects" + title: "Saving the Minds of DOOM's Monsters" wikipedia_url: "https://doomwiki.org/wiki/Thinker" image_url: "" image_caption: "" - content: "The `P_ArchiveThinkers` function serializes active game objects, known as 'thinkers,' into the save file. Thinkers include enemies, projectiles, and other dynamic entities that require ongoing updates. The function iterates through the thinker list, saving each object's state and converting pointers to offsets for portability. It also adds a terminating marker (`tc_end`) to signal the end of the thinker list. This modular approach allowed DOOM to handle a wide variety of game objects without hardcoding their behaviors into the save system. The thinker system became a hallmark of id Software's engines, influencing later games like Quake and even modern engines like Unity, which use similar component-based architectures." - - id: "unarchive-thinkers-game-objects" + content: "The `P_ArchiveThinkers` function saves the state of active 'thinkers'—game entities with ongoing behavior, such as monsters and projectiles. It iterates through the linked list of thinkers, identifying those with specific functions like `P_MobjThinker`. Each thinker's data is written to the save buffer, with pointers converted to relative offsets for portability. This design reflects John Carmack's emphasis on modularity and efficiency, allowing DOOM to manage complex interactions while saving memory. The thinker system was a groundbreaking feature, enabling dynamic and responsive gameplay. It influenced later games like Quake and Unreal, which adopted similar systems to manage AI and game logic." + - id: "unarchive-thinkers-loadgame" line_start: 262 line_end: 322 - title: "Reanimating DOOM's Thinkers from Save Files" + title: "Reanimating DOOM's Monsters from Save Files" wikipedia_url: "https://doomwiki.org/wiki/Thinker" image_url: "" image_caption: "" - content: "The `P_UnArchiveThinkers` function reconstructs dynamic game objects ('thinkers') from the save file. It clears the current thinker list, initializes new thinkers based on the saved data, and recalculates pointers to ensure proper functionality. This process includes restoring connections between objects, such as a player's link to their map object (`mo`). The function highlights the challenges of deserializing complex systems, as it must handle various thinker types and ensure their interactions are preserved. The thinker system's flexibility influenced later engines, which adopted similar designs to manage dynamic entities in games ranging from first-person shooters to strategy titles." - - id: "archive-specials-game-events" + content: "The `P_UnArchiveThinkers` function restores active thinkers from a save buffer, reconstructing their attributes and re-linking them into the game's logic. It removes existing thinkers before loading new ones, ensuring a clean slate. This function demonstrates DOOM's ability to manage complex game entities dynamically, a feature that was rare in 1993. The thinker system became a foundational concept in game development, influencing AI and entity management in titles like Half-Life and Unreal. By enabling dynamic persistence, DOOM set a precedent for how games could handle intricate gameplay systems across save/load cycles." + - id: "archive-specials-savegame" line_start: 343 line_end: 468 - title: "How DOOM Saved Its Active Events" - wikipedia_url: "https://doomwiki.org/wiki/Thinker" + title: "Preserving DOOM's Moving Floors and Flashing Lights" + wikipedia_url: "https://doomwiki.org/wiki/Savegame" image_url: "" image_caption: "" - content: "The `P_ArchiveSpecials` function serializes active game events, such as moving platforms, doors, and lighting effects, into the save file. It iterates through the thinker list, identifying special event types and saving their state. By converting sector pointers to offsets, it ensures portability across systems. This modular approach allowed DOOM to handle a wide variety of active events without hardcoding their behaviors into the save system. The ability to save and restore dynamic events was critical for maintaining gameplay continuity, especially in levels with complex interactions. This technique influenced later engines, which adopted similar methods to serialize dynamic game elements, enabling features like mid-mission saves in modern titles." - - id: "unarchive-specials-game-events" + content: "The `P_ArchiveSpecials` function saves the state of special game elements, such as moving floors, doors, and lighting effects. It iterates through the thinker list, identifying specific types of specials and writing their data to the save buffer. This function highlights DOOM's ability to handle dynamic environments, a feature that set it apart from other games of the era. By saving active specials, DOOM ensured that its levels retained their interactive elements, enhancing immersion. This approach influenced later games like Duke Nukem 3D and Unreal, which expanded on DOOM's techniques to create even more dynamic and interactive worlds." + - id: "unarchive-specials-loadgame" line_start: 471 line_end: 584 - title: "Reactivating DOOM's Special Events" - wikipedia_url: "https://doomwiki.org/wiki/Thinker" + title: "Reactivating DOOM's Dynamic Level Elements" + wikipedia_url: "https://doomwiki.org/wiki/Savegame" image_url: "" image_caption: "" - content: "The `P_UnArchiveSpecials` function restores active game events from the save file, including platforms, doors, and lighting effects. It reconstructs pointers to sectors and reinitializes thinker functions to ensure proper behavior. This process highlights the complexity of deserializing interconnected systems, as each event must be restored in a way that preserves its relationships with other game elements. The function's modular design allowed DOOM to handle a wide variety of special events, setting a precedent for extensible save systems in later engines. Games like Quake and Unreal adopted similar techniques to manage dynamic events, enabling features like scripted sequences and interactive environments." + content: "The `P_UnArchiveSpecials` function restores special game elements from a save buffer, re-linking them into the game's logic. It reconstructs attributes like sector pointers and thinker functions, ensuring that dynamic elements like moving floors and flashing lights resume their behavior. This meticulous process reflects DOOM's commitment to preserving its immersive environments across save/load cycles. The ability to persist dynamic elements was a groundbreaking feature in 1993, influencing later games like Hexen and System Shock, which adopted similar techniques to manage interactive environments. DOOM's savegame system set a new standard for how games could handle complex, dynamic worlds." --- @@ -689,4 +675,5 @@ void P_UnArchiveSpecials (void) } } -``` + +``` \ No newline at end of file diff --git a/public/programs/doom/p-setup-c.md b/public/programs/doom/p-setup-c.md index 8d13bb5..7a70882 100644 --- a/public/programs/doom/p-setup-c.md +++ b/public/programs/doom/p-setup-c.md @@ -9,82 +9,106 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-setup-c" order: 11 -description: "This file handles the setup of DOOM's game levels, including loading map data, initializing player states, and preparing lookup tables for efficient gameplay." +description: "This file handles the loading and setup of DOOM's map data, a critical part of its groundbreaking 3D engine." summary: - - point: "Efficient spatial subdivision via blockmaps" - link: "https://en.wikipedia.org/wiki/Spatial_partitioning" - link_label: "Spatial Partitioning" - - point: "Preloading map data for fast access" - link: "https://en.wikipedia.org/wiki/WAD_(file_format)" - link_label: "WAD File Format" - - point: "Dynamic setup of multiplayer starting points" - link: "https://en.wikipedia.org/wiki/Deathmatch" - link_label: "Deathmatch" - - point: "Reject matrix optimization for enemy AI" - link: "https://en.wikipedia.org/wiki/Line_of_sight" - link_label: "Line of Sight" - - point: "Level-specific resource caching" - link: "https://en.wikipedia.org/wiki/Cache_(computing)" - link_label: "Caching" + - point: "Efficient spatial data structures for collision detection" + link: "https://en.wikipedia.org/wiki/Collision_detection" + link_label: "Collision detection" + - point: "Custom memory allocation for game-specific needs" + link: "https://en.wikipedia.org/wiki/Memory_management" + link_label: "Memory management" + - point: "Preloading map data for seamless gameplay" + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM (1993 video game)" + - point: "Use of fixed-point arithmetic for performance" + link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + link_label: "Fixed-point arithmetic" + - point: "Sector-based sound propagation for immersive audio" + link: "https://en.wikipedia.org/wiki/Sound_propagation" + link_label: "Sound propagation" enhancements: - - id: "map-data-lookup-tables" + - id: "load-vertex-data-from-wad" line_start: 118 line_end: 151 - title: "How DOOM Organized Its World Data" - wikipedia_url: "https://en.wikipedia.org/wiki/WAD_(file_format)" + title: "How DOOM Converts Map Vertices to Fixed Point" + wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" image_url: "" image_caption: "" - content: "This section defines lookup tables for various map elements, including vertices, linedefs, sidedefs, and sectors. These tables are central to DOOM's ability to render and interact with its 3D world efficiently. By preloading this data into memory, the game avoids costly disk I/O during gameplay, ensuring smooth performance on the limited hardware of the early 1990s. The design reflects id Software's mastery of optimizing for constrained environments, as PCs of the era often had limited RAM and slow hard drives. This approach influenced future game engines, which adopted similar strategies for organizing and accessing world data." - - id: "blockmap-spatial-subdivision" + content: "The `P_LoadVertexes` function reads vertex data from a WAD file and converts it into the game's internal fixed-point format. This conversion ensures precision while avoiding the computational overhead of floating-point arithmetic, which was expensive on the hardware of the early 1990s. At the time, consumer PCs often lacked dedicated floating-point units, making fixed-point arithmetic a practical choice for real-time applications like games. The function allocates memory for vertex storage, loads the data, and performs the conversion using bit-shifting operations. This approach was a hallmark of John Carmack's programming style, emphasizing efficiency and direct control over hardware. Fixed-point arithmetic became a standard technique in many games of the era, influencing engines such as Build (used in Duke Nukem 3D) and later iterations of id Software's own engines." + - id: "load-segments-for-map-geometry" line_start: 155 line_end: 195 - title: "The Trick That Sped Up Collision Detection" - wikipedia_url: "https://en.wikipedia.org/wiki/Spatial_partitioning" + title: "Mapping Segments to Geometry: A Spatial Puzzle" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The blockmap system divides the game map into a grid of blocks, enabling efficient spatial subdivision for collision detection. By associating objects with specific blocks, DOOM reduces the number of checks required to determine interactions, such as whether a projectile hits a wall or an enemy. This technique was crucial for maintaining high framerates on hardware like the 486 processor, which lacked dedicated graphics acceleration. Spatial subdivision remains a cornerstone of game development, influencing techniques like quadtrees and BSP trees used in modern engines." - - id: "reject-matrix-ai-optimization" + content: "The `P_LoadSegs` function processes map segments, which are line segments that define the edges of subsectors in DOOM's map geometry. Each segment is linked to vertices, line definitions, and sidedefs, forming the building blocks of the game's spatial representation. This function also calculates angles and offsets for each segment, preparing them for rendering and collision detection. The approach reflects Carmack's focus on optimizing spatial data structures for performance. By precomputing relationships between map elements, DOOM achieves its fast-paced gameplay without sacrificing visual complexity. The segment-loading process laid the groundwork for techniques used in later engines, such as BSP trees in Quake, which further refined spatial partitioning for 3D environments." + - id: "subsectors-and-visibility" line_start: 198 line_end: 223 - title: "How DOOM Made Enemies Smarter, Faster" - wikipedia_url: "https://en.wikipedia.org/wiki/Line_of_sight" + title: "Subsectors: The Key to Efficient Visibility Checks" + wikipedia_url: "https://en.wikipedia.org/wiki/Binary_space_partitioning" image_url: "" image_caption: "" - content: "The reject matrix is a precomputed data structure used to optimize enemy AI by skipping unnecessary line-of-sight calculations. If two areas of the map are known to be disconnected, the matrix allows the game to reject visibility checks outright, saving CPU cycles. This innovation was particularly important for DOOM's fast-paced gameplay, where multiple enemies could be active simultaneously. The idea of precomputing visibility relationships influenced later games and engines, including Quake and Unreal, which expanded on this concept with more sophisticated visibility algorithms." - - id: "deathmatch-spawn-system" + content: "The `P_LoadSubsectors` function loads subsector data, which are smaller partitions of sectors used in DOOM's binary space partitioning (BSP) system. Each subsector contains a list of line segments that define its boundaries. By organizing map geometry into subsectors, DOOM's engine can efficiently determine visibility and render only the parts of the map that are within the player's view. This technique was revolutionary for its time, enabling smooth gameplay on hardware with limited processing power. BSP-based visibility checks became a cornerstone of 3D rendering in games, influencing engines like Unreal Engine and Source. Carmack's work on subsectors demonstrated how careful data organization could overcome hardware constraints and deliver groundbreaking performance." + - id: "load-sector-properties" line_start: 227 line_end: 257 - title: "Dynamic Player Spawning for Multiplayer Chaos" - wikipedia_url: "https://en.wikipedia.org/wiki/Deathmatch" + title: "Sectors: Where Geometry Meets Gameplay" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "This section defines data structures for player and deathmatch starting points. In multiplayer modes, players are spawned at random deathmatch spots, ensuring unpredictable gameplay. This system contributed to the chaotic and competitive nature of DOOM's multiplayer experience, which became a defining feature of the game. The concept of dynamic spawn points influenced later multiplayer games, including first-person shooters like Quake and Counter-Strike, where map design and spawn logic play a critical role in gameplay balance." - - id: "vertex-loading-and-conversion" - line_start: 118 - line_end: 151 - title: "Why DOOM Converted Coordinates to Fixed-Point" - wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + content: "The `P_LoadSectors` function reads sector data from the WAD file, defining properties like floor and ceiling heights, textures, and lighting levels. Sectors are the foundation of DOOM's gameplay, as they determine the physical layout of the map and interact with player movement, enemy AI, and environmental effects. By precomputing these properties, the engine ensures smooth transitions between areas and consistent rendering. The concept of sectors influenced level design in subsequent games, allowing designers to create complex environments with varied gameplay mechanics. This function highlights the interplay between technical constraints and creative possibilities, a hallmark of DOOM's design philosophy." + - id: "load-nodes-for-bsp-tree" + line_start: 260 + line_end: 294 + title: "Building the BSP Tree: DOOM's Spatial Masterstroke" + wikipedia_url: "https://en.wikipedia.org/wiki/Binary_space_partitioning" + image_url: "" + image_caption: "" + content: "The `P_LoadNodes` function constructs the nodes of DOOM's binary space partitioning (BSP) tree, a data structure that organizes the map into a hierarchy of partitions. Each node contains information about its position, direction, and bounding box, enabling efficient visibility checks and collision detection. BSP trees were a groundbreaking innovation in game engines, allowing DOOM to render complex 3D environments on hardware with limited processing power. Carmack's implementation of BSP trees set a new standard for spatial organization in games, influencing engines like Quake and Unreal. The technique remains relevant today, demonstrating the enduring impact of DOOM's technical achievements." + - id: "spawn-map-things" + line_start: 297 + line_end: 349 + title: "Spawning Monsters and Items: Gameplay Comes Alive" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + image_url: "" + image_caption: "" + content: "The `P_LoadThings` function populates the map with 'things'—a term encompassing monsters, items, and other interactive elements. Each 'thing' is loaded from the WAD file and converted into the game's internal format. The function includes logic to exclude certain monsters in non-commercial versions of the game, reflecting id Software's approach to differentiating product tiers. By centralizing the spawning process, the engine ensures consistent behavior across maps and modes. This function exemplifies DOOM's modular design, which allowed for easy customization and expansion. The concept of 'things' influenced object-oriented approaches in later game engines, paving the way for dynamic and interactive environments." + - id: "load-linedefs-and-slopes" + line_start: 352 + line_end: 431 + title: "LineDefs: Geometry Meets Functionality" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_LoadVertexes` function reads vertex data from the map file and converts coordinates to fixed-point format. Fixed-point arithmetic was chosen because it offered faster calculations compared to floating-point operations on the hardware available in 1993. This decision reflects the constraints of the era, where performance optimization often involved trading precision for speed. Fixed-point arithmetic became a staple in early 3D engines and is still used in embedded systems and mobile games where hardware constraints persist." - - id: "level-setup-sequence" + content: "The `P_LoadLineDefs` function processes line definitions, which represent the edges of sectors and define their properties. Each LineDef includes flags, special effects, and tags that link it to gameplay mechanics like doors, switches, and triggers. The function also calculates slope types for each line, enabling efficient collision detection and rendering. LineDefs are a key part of DOOM's map format, bridging the gap between geometry and gameplay. This modular approach to level design influenced subsequent games, allowing designers to create interactive environments with minimal programming effort. The concept of LineDefs remains a cornerstone of level design in modern engines." + - id: "group-lines-for-sectors" line_start: 493 line_end: 692 - title: "The Sequence That Built DOOM's Levels" - wikipedia_url: "https://en.wikipedia.org/wiki/WAD_(file_format)" + title: "Grouping Lines: Organizing the Chaos of Geometry" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + image_url: "" + image_caption: "" + content: "The `P_GroupLines` function organizes lines into sectors, creating lists of lines that belong to each sector and calculating bounding boxes for collision detection. This organization is crucial for gameplay mechanics like sound propagation and enemy AI, which rely on sector-based interactions. The function also adjusts bounding boxes to align with the map's block grid, ensuring efficient spatial queries. By precomputing these relationships, DOOM's engine achieves fast and consistent performance, even in complex maps. The concept of grouping lines into sectors influenced spatial partitioning techniques in later engines, demonstrating the importance of data organization in game design." + - id: "setup-level-data" + line_start: 581 + line_end: 692 + title: "Level Setup: Loading the World in Seconds" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_SetupLevel` function orchestrates the loading and initialization of a game level. It reads map data from WAD files, sets up blockmaps, vertices, sectors, and other structures, and initializes player states. This function demonstrates the importance of ordering in level setup, as certain data must be loaded before others to ensure consistency. The modular design of this sequence allowed for easy modification and expansion, enabling the creation of custom maps and mods by the community. This modularity contributed to DOOM's enduring legacy as one of the most modded games in history." - - id: "sprite-initialization" + content: "The `P_SetupLevel` function orchestrates the loading and initialization of all map data, including vertices, sectors, LineDefs, and 'things'. It ensures that each component is loaded in the correct order, reflecting the dependencies between data structures. The function also handles player spawning, special effects, and precaching graphics for seamless gameplay. This level setup process was a technical marvel in 1993, enabling DOOM to load complex maps in seconds on modest hardware. The modular design of `P_SetupLevel` influenced the architecture of later game engines, demonstrating how careful planning and organization could overcome hardware limitations and deliver groundbreaking performance." + - id: "initialize-gameplay-components" line_start: 696 line_end: 704 - title: "How DOOM Brought Monsters to Life" - wikipedia_url: "https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" + title: "Initializing Sprites and Animations: The Final Touch" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `P_Init` function initializes sprites, animations, and switches, preparing the visual elements of the game. Sprites were a key graphical feature in DOOM, used to represent enemies, items, and effects. By organizing sprite data efficiently, DOOM achieved a visually rich experience despite hardware limitations. This approach influenced sprite handling in later games and engines, paving the way for more advanced 2D and 3D graphics systems." + content: "The `P_Init` function initializes gameplay components like switch lists, animated textures, and sprites. These elements add visual and interactive richness to DOOM's environments, enhancing the player's immersion. By centralizing initialization, the engine ensures consistency across maps and modes. This function highlights the importance of modular design in game development, allowing for easy customization and expansion. The initialization process influenced the architecture of later engines, demonstrating how small details could contribute to the overall experience." --- @@ -793,4 +817,7 @@ void P_Init (void) P_InitPicAnims (); R_InitSprites (sprnames); } -``` + + + +``` \ No newline at end of file diff --git a/public/programs/doom/p-sight-c.md b/public/programs/doom/p-sight-c.md index ce2e885..7958a6e 100644 --- a/public/programs/doom/p-sight-c.md +++ b/public/programs/doom/p-sight-c.md @@ -9,60 +9,60 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-sight-c" order: 32 -description: "Line-of-sight and visibility checks in DOOM's engine, enabling efficient enemy AI and player interaction." +description: "This file implements line-of-sight checks in DOOM, a foundational mechanic for AI behavior and player interaction in the game." summary: - - point: "Uses a REJECT table to optimize visibility checks" - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)#Technology" - link_label: "DOOM Technology" - - point: "Implements BSP traversal for spatial calculations" + - point: "Uses BSP traversal for efficient visibility checks" link: "https://en.wikipedia.org/wiki/Binary_space_partitioning" link_label: "Binary Space Partitioning" - - point: "Introduces efficient slope-based occlusion testing" - link: "https://en.wikipedia.org/wiki/Visibility_(geometry)" - link_label: "Visibility in Geometry" + - point: "Incorporates REJECT table optimization to skip unnecessary checks" + link: "https://doomwiki.org/wiki/Reject_table" + link_label: "REJECT Table" + - point: "Demonstrates early use of fixed-point arithmetic for performance" + link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + link_label: "Fixed-Point Arithmetic" enhancements: - id: "divline-side-classification" - line_start: 280 - line_end: 285 - title: "Classifying Points: Front, Back, or On?" - wikipedia_url: "https://en.wikipedia.org/wiki/Line_(geometry)" + line_start: 279 + line_end: 284 + title: "How DOOM Decides Which Side You're On" + wikipedia_url: "https://en.wikipedia.org/wiki/Binary_space_partitioning" image_url: "" image_caption: "" - content: "The `P_DivlineSide` function classifies a point relative to a dividing line, returning whether the point is on the front side, back side, or directly on the line. This geometric classification is foundational for DOOM's spatial calculations, including visibility checks and BSP traversal. The function uses fixed-point arithmetic to perform comparisons efficiently, a necessity given the hardware constraints of the era. By determining the relative position of points, the engine can decide which subsectors to process, avoiding unnecessary calculations. This technique was inspired by computational geometry methods used in CAD software and adapted for real-time applications in gaming. The concept of dividing space into regions influenced later engines, including Quake's 3D BSP system, which extended these principles into true 3D environments." - - id: "intercept-point-calculation" - line_start: 107 + content: "This function, `P_DivlineSide`, determines whether a point lies on the front, back, or directly on a dividing line in the game's BSP tree. It uses fixed-point arithmetic to calculate the relative position of a point to a line defined by two coordinates (x, y) and directional vectors (dx, dy). At the time, fixed-point arithmetic was a common optimization in games because it avoided the computational overhead of floating-point operations on hardware like the Intel 486. The BSP tree itself was a revolutionary data structure for games, enabling efficient spatial partitioning and visibility checks. John Carmack adapted this technique from academic papers on computer graphics, tailoring it to DOOM's fast-paced gameplay and the constraints of consumer PCs. The approach influenced countless games that followed, including Quake, which refined BSP trees further for real-time 3D environments." + - id: "intercept-vector-calculation" + line_start: 104 line_end: 127 - title: "Finding Intercept Points in Fixed-Point Math" - wikipedia_url: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" + title: "The Math Behind Line Intersections in DOOM" + wikipedia_url: "https://en.wikipedia.org/wiki/Line_intersection" image_url: "" image_caption: "" - content: "The `P_InterceptVector2` function calculates the fractional intercept point between two lines, a critical operation for determining whether a line crosses a subsector. This function uses fixed-point arithmetic, a method that avoids the performance penalties of floating-point calculations on early CPUs like the Intel 486. Fixed-point math was a hallmark of DOOM's engine, enabling precise calculations with minimal computational overhead. The intercept calculation is used in visibility checks and collision detection, ensuring that DOOM's fast-paced gameplay remains smooth even in complex environments. This reliance on fixed-point arithmetic influenced later engines, such as Build (used in Duke Nukem 3D), which also prioritized performance on limited hardware. Modern engines have largely transitioned to floating-point math, but DOOM's efficient techniques remain a benchmark in game optimization history." - - id: "slope-based-occlusion-testing" + content: "The `P_InterceptVector2` function calculates the fractional point of intersection between two lines, represented as divlines. This is crucial for determining whether the player's line of sight crosses a particular boundary in the game world. The function uses fixed-point arithmetic for precision and performance, avoiding the pitfalls of floating-point inaccuracies on early PC hardware. This technique was part of DOOM's broader strategy to optimize visibility checks, ensuring smooth gameplay even on systems with limited processing power. By calculating intersections efficiently, DOOM could handle complex environments with numerous walls and objects without significant slowdowns. The method was later studied by developers of other 3D engines, influencing techniques in games like Unreal and Half-Life." + - id: "cross-subsector-visibility-check" line_start: 129 line_end: 247 - title: "Slope Calculations: DOOM's Clever Occlusion Test" - wikipedia_url: "https://en.wikipedia.org/wiki/Visibility_(geometry)" + title: "How DOOM Determines If You Can See Through a Room" + wikipedia_url: "https://doomwiki.org/wiki/Subsector" image_url: "" image_caption: "" - content: "The `P_CrossSubsector` function performs detailed visibility checks by calculating slopes to determine whether an object is occluded. It examines the geometry of subsectors, comparing floor and ceiling heights to detect potential blockers. If the slopes of the top and bottom edges of a target overlap, the line of sight is considered obstructed. This slope-based approach was a clever solution to the problem of occlusion in a 2.5D engine, where true 3D calculations were infeasible on consumer hardware. By using fixed-point arithmetic and precomputed geometry data, DOOM achieved fast and accurate visibility checks, enabling realistic enemy AI and player interactions. This technique was a precursor to more advanced occlusion culling methods used in modern engines, such as Umbra's visibility solutions in Unity and Unreal." - - id: "bsp-traversal-for-visibility" + content: "`P_CrossSubsector` checks whether the player's line of sight crosses a given subsector in the BSP tree. It iterates through all the lines in the subsector, determining if any block visibility based on their properties, such as floor and ceiling heights. The function uses the REJECT table to skip unnecessary checks, a clever optimization that precomputes visibility relationships between sectors. This approach was critical for DOOM's performance, enabling it to run smoothly on hardware with limited memory and processing power. The subsector-based visibility checks laid the groundwork for efficient spatial partitioning in later games, influencing engines like Quake and Unreal Engine." + - id: "cross-bsp-node-traversal" line_start: 251 line_end: 289 - title: "BSP Traversal: The Backbone of DOOM's World" + title: "The Recursive Algorithm That Powers DOOM's BSP Tree" wikipedia_url: "https://en.wikipedia.org/wiki/Binary_space_partitioning" image_url: "" image_caption: "" - content: "The `P_CrossBSPNode` function traverses DOOM's binary space partitioning (BSP) tree to determine whether a line crosses a given node. BSP trees were a groundbreaking spatial representation technique in the early 1990s, allowing efficient partitioning of 2D and 3D spaces. In DOOM, the BSP tree organizes the game world into convex subsectors, enabling rapid visibility checks and collision detection. The traversal algorithm recursively checks which side of a partition plane the line starts and ends on, ensuring that only relevant subsectors are processed. This approach minimized the computational overhead of rendering and AI calculations, crucial for achieving DOOM's smooth performance on 486-class CPUs. BSP trees became a standard in game development, influencing titles like Quake and Half-Life. Even modern engines like Source and Unreal incorporate similar spatial partitioning techniques for efficient scene management." + content: "`P_CrossBSPNode` is a recursive function that traverses the BSP tree to determine if the player's line of sight crosses a given node. It uses `P_DivlineSide` to decide which side of the node the line starts on and recursively checks both sides if necessary. This traversal is the backbone of DOOM's visibility system, allowing the game to efficiently determine what the player can see in complex environments. The recursive nature of the function reflects the hierarchical structure of BSP trees, which were adapted from computer graphics research for use in real-time games. The technique became a standard in game development, influencing engines like Source and Unity." - id: "reject-table-optimization" - line_start: 298 - line_end: 346 - title: "How DOOM's REJECT Table Saved CPU Cycles" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)#Technology" + line_start: 297 + line_end: 345 + title: "The Table That Made DOOM Run Faster" + wikipedia_url: "https://doomwiki.org/wiki/Reject_table" image_url: "" image_caption: "" - content: "The `P_CheckSight` function is the centerpiece of DOOM's visibility system, determining whether one object can 'see' another. It begins by consulting the REJECT table, a precomputed matrix that flags pairs of sectors as potentially unconnected. This avoids expensive geometric calculations for objects that are trivially blocked by walls or other structures. The REJECT table was generated during map compilation, leveraging the binary space partitioning (BSP) structure to precompute relationships between sectors. In the early 1990s, CPUs like the Intel 486 were limited in processing power, making such optimizations essential for maintaining DOOM's fast-paced gameplay. By skipping unnecessary checks, the REJECT table allowed DOOM to handle complex environments with dozens of enemies without overwhelming the hardware. This technique influenced later games, including Quake, which further refined spatial optimization methods. Today, similar precomputed visibility techniques are used in engines like Unreal and Unity to optimize rendering and AI." + content: "`P_CheckSight` uses the REJECT table to quickly determine if visibility checks between two sectors are unnecessary. The table precomputes relationships between sectors, allowing the game to skip expensive line-of-sight calculations for pairs of sectors that cannot possibly be connected. This optimization was essential for DOOM's performance, as it reduced the computational overhead of visibility checks in large and complex maps. The REJECT table exemplifies DOOM's innovative use of preprocessing to overcome hardware limitations, a technique that inspired similar optimizations in later games and engines. Developers studying DOOM's source code have praised this approach for its simplicity and effectiveness." --- @@ -413,4 +413,6 @@ P_CheckSight // the head node is the last node output return P_CrossBSPNode (numnodes-1); } -``` + + +``` \ No newline at end of file diff --git a/public/programs/doom/p-spec-c.md b/public/programs/doom/p-spec-c.md index 940fa53..d98a359 100644 --- a/public/programs/doom/p-spec-c.md +++ b/public/programs/doom/p-spec-c.md @@ -9,130 +9,114 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "p-spec-c" order: 33 -description: "This file implements special effects and utilities for DOOM's interactive environments, showcasing techniques that pushed the boundaries of 1990s game design." +description: "This file contains code for handling special effects in DOOM, including texture animations, sector height changes, and line triggers, showcasing techniques that pushed the limits of 1990s PC hardware." summary: - - point: "Texture animation and environmental effects are handled here." + - point: "Defines texture and flat animation structures for dynamic visuals" link: "https://en.wikipedia.org/wiki/Texture_mapping" link_label: "Texture Mapping" - - point: "Sector-based utilities enable dynamic level interactions." + - point: "Implements utilities to interact with sectors and lines in the game world" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM" - - point: "Line triggers allow scripted events like doors and teleportation." + - point: "Introduces algorithms for finding floor and ceiling heights in adjacent sectors" + link: "https://en.wikipedia.org/wiki/Level_design" + link_label: "Level Design" + - point: "Handles line triggers for events like doors, platforms, and teleportation" link: "https://en.wikipedia.org/wiki/Trigger_(game_design)" - link_label: "Trigger Design" - - point: "Efficient algorithms for finding adjacent sector properties." - link: "https://en.wikipedia.org/wiki/Algorithm" - link_label: "Algorithm" - - point: "Event handling for player and monster interactions with the environment." - link: "https://en.wikipedia.org/wiki/Event-driven_programming" - link_label: "Event-driven Programming" + link_label: "Game Triggers" + - point: "Demonstrates optimization for constrained hardware environments" + link: "https://en.wikipedia.org/wiki/Id_Software" + link_label: "id Software" enhancements: - - id: "texture-animation-structures" + - id: "animating-textures-and-planes" line_start: 55 line_end: 67 - title: "Animating Textures: A Simple Yet Effective Trick" + title: "Animated Texture Structures: anim_t and animdef_t" wikipedia_url: "https://en.wikipedia.org/wiki/Texture_mapping" image_url: "" image_caption: "" - content: "This section defines structures and data for animating textures and planes in DOOM. The `anim_t` and `animdef_t` structures specify the animation properties, such as whether the animation applies to textures or flats, the sequence of frames, and the speed of the animation. The `animdefs` array lists predefined animations, including iconic effects like flowing lava and dripping blood. In 1993, texture animation was a novel way to make environments feel alive and dynamic, especially on hardware with limited graphical capabilities. John Carmack's approach leveraged the WAD file format to define animations using sequential frames found in the game's resource files. This technique inspired later games to use similar methods for environmental effects, and it remains a fundamental concept in modern game engines like Unity and Unreal Engine." - - id: "initialize-texture-animations" + content: "This region declares two complementary structures that drive all of DOOM's animated surfaces. `animdef_t` is the read-only source record: it stores a boolean flag distinguishing textures from flats, and the WAD lump names of the first and last frames in a cycle along with a tics-per-frame speed. Because names are used rather than indices, the definition is independent of load order and entirely data-driven. `anim_t` is the runtime counterpart built from animdef_t at startup: it resolves names to lump numbers, stores the current frame counter, and records the cycle length so the tick routine can advance frames with a simple modulo. Together the two structures let level designers create effects like flowing nukage, crackling fire, and blinking lights without touching code; all they needed was a correctly named sequence of WAD lumps. This separation of static source data from live state was a forward-thinking design for 1993, and the same pattern of compile-time definitions feeding runtime objects became standard in later content-driven engines like Quake and the Unreal series." + - id: "floor-ceiling-animation-sequences" + line_start: 82 + line_end: 131 + title: "Animating Floors and Ceilings Dynamically" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + image_url: "" + image_caption: "" + content: "This block defines animation sequences for floors and ceilings using the `animdefs` array. Each entry specifies the start and end frames, animation speed, and whether the sequence applies to textures or flats. The animations include iconic visuals like flowing slime and lava, which became synonymous with DOOM's aesthetic. By leveraging the WAD file format, id Software ensured that animations could be dynamically loaded and cycled, reducing memory overhead. This technique was particularly important for the limited hardware of the early 1990s, where efficient resource management was critical. The system's flexibility allowed modders to create custom animations, contributing to DOOM's enduring legacy as a modding-friendly game. The concept of dynamically defined animations influenced the design of later games and engines, including Quake and Unreal." + - id: "p-init-pic-anims" line_start: 147 line_end: 192 - title: "Initializing Texture Animations: A WAD-driven Method" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_WAD" + title: "Initializing Texture Animations Dynamically" + wikipedia_url: "https://en.wikipedia.org/wiki/Texture_mapping" image_url: "" image_caption: "" - content: "The `P_InitPicAnims` function initializes texture animations by processing the `animdefs` array. It checks whether the start and end frames exist in the WAD file, calculates the number of frames in the animation, and assigns the animation properties to the global `anims` array. This function demonstrates how DOOM's modular design allowed developers to define animations externally via WAD files, making it easy to add or modify content. The reliance on WAD files for resource management became a hallmark of DOOM's extensibility, enabling the creation of user-generated mods and levels. This approach influenced the design of later games, including Quake and Half-Life, which adopted similar modular resource systems." - - id: "sector-utilities" + content: "The `P_InitPicAnims` function initializes texture animations by iterating through the `animdefs` array and setting up the `anims` array. It checks whether the animation applies to textures or flats, retrieves the corresponding frame indices using helper functions like `R_TextureNumForName`, and calculates the number of frames in the animation cycle. This function exemplifies id Software's approach to modular and extensible design, allowing animations to be dynamically defined and loaded from the WAD file. In the constrained environment of 1993, this system minimized memory usage while maximizing visual impact. The modularity of this design influenced later games, enabling dynamic asset loading and animation systems in engines like Source and Unreal." + - id: "sector-utilities-get-side" line_start: 196 line_end: 209 - title: "Sector Utilities: Navigating DOOM's Level Geometry" + title: "Getting Sector Sides: A Key Utility" wikipedia_url: "https://en.wikipedia.org/wiki/Level_design" image_url: "" image_caption: "" - content: "Functions like `getSide`, `getSector`, `twoSided`, and `getNextSector` provide essential utilities for navigating and querying DOOM's sector-based level geometry. These functions allow the game to determine properties of adjacent sectors, such as whether a line is two-sided or which sector lies on the other side of a line. In the early 1990s, sector-based level design was a practical solution for creating complex environments on limited hardware. DOOM's efficient handling of sector relationships enabled dynamic interactions like doors, lifts, and teleportation. This approach influenced the design of other games that used similar geometry systems, including Duke Nukem 3D and Build Engine games." - - id: "floor-and-ceiling-height-algorithms" + content: "The `getSide` function retrieves a `side_t` structure given a sector number, line number, and side index (0 or 1). This utility is essential for interacting with the game's world geometry, allowing the engine to determine properties of walls and boundaries. In DOOM, sectors and sides define the spatial layout, and efficient access to this data was critical for real-time gameplay. The function's simplicity and directness reflect the team's focus on performance and clarity, ensuring that the engine could handle complex level designs without excessive computational overhead. This approach influenced later engines, which adopted similar abstractions for handling world geometry." + - id: "p-find-lowest-floor-surrounding" line_start: 265 - line_end: 380 - title: "Finding Heights: Algorithms for Dynamic Levels" - wikipedia_url: "https://en.wikipedia.org/wiki/Algorithm" - image_url: "" - image_caption: "" - content: "The functions `P_FindLowestFloorSurrounding`, `P_FindHighestFloorSurrounding`, `P_FindLowestCeilingSurrounding`, and `P_FindHighestCeilingSurrounding` calculate floor and ceiling heights in adjacent sectors. These algorithms enable dynamic level interactions, such as raising or lowering floors and ceilings based on surrounding geometry. In DOOM, these calculations were crucial for creating realistic and responsive environments. The use of fixed-point arithmetic reflects the constraints of 1990s hardware, where floating-point operations were often too slow. These techniques laid the groundwork for more advanced geometry manipulation in later games, influencing engines like Quake's BSP-based system and Unreal Engine's dynamic level editing." - - id: "line-tag-triggers" - line_start: 429 - line_end: 444 - title: "Line Tags: The Key to Scripted Events" - wikipedia_url: "https://en.wikipedia.org/wiki/Trigger_(game_design)" - image_url: "" - image_caption: "" - content: "The `P_FindSectorFromLineTag` function retrieves the next sector associated with a line tag, enabling scripted events like opening doors or triggering teleportation. Line tags were a simple yet powerful mechanism for defining interactions in DOOM's levels. By associating tags with sectors and lines, designers could create complex behaviors without hardcoding them into the game logic. This approach was a precursor to modern event-driven programming in games, where triggers and actions are defined declaratively. Line tags influenced the scripting systems of later games, including Quake's entity-based triggers and Half-Life's input-output system." - - id: "light-level-calculations" - line_start: 1080 - line_end: 1080 - title: "Dynamic Lighting: Calculating Surrounding Light Levels" - wikipedia_url: "https://en.wikipedia.org/wiki/Lighting_(rendering)" + line_end: 288 + title: "Finding the Lowest Floor: A Level Design Tool" + wikipedia_url: "https://en.wikipedia.org/wiki/Level_design" image_url: "" image_caption: "" - content: "The `P_FindMinSurroundingLight` function calculates the minimum light level in adjacent sectors, enabling dynamic lighting effects like dimming or brightening areas based on player movement. Dynamic lighting was a significant innovation in DOOM, creating a sense of atmosphere and tension in its levels. This function showcases how Carmack's code optimized lighting calculations for performance, using integer arithmetic and efficient loops. Dynamic lighting became a staple of game design, influencing later engines like Quake and Unreal, which expanded on these techniques with real-time lighting and shadow systems." - - id: "cross-special-line-trigger" + content: "The `P_FindLowestFloorSurrounding` function calculates the lowest floor height among adjacent sectors. By iterating through the lines of a sector and checking neighboring sectors, it determines the minimum floor height. This algorithm was crucial for dynamic level interactions, such as raising platforms or creating realistic movement paths. In 1993, such calculations had to be optimized for performance, as DOOM ran on hardware with limited processing power. The function's design reflects id Software's ability to balance complexity and efficiency, enabling dynamic and immersive gameplay. Similar algorithms became standard in level design tools, influencing games like Half-Life and Unreal Tournament." + - id: "p-cross-special-line-trigger" line_start: 479 line_end: 949 - title: "Crossing the Line: Activating Special Events" - wikipedia_url: "https://en.wikipedia.org/wiki/Event-driven_programming" - image_url: "" - image_caption: "" - content: "The `P_CrossSpecialLine` function handles events triggered when a player or object crosses a line with a special property. These events include opening doors, activating lifts, and teleporting. The function checks the type of object crossing the line and the line's special property, ensuring that only valid triggers are activated. This system allowed DOOM's levels to feel interactive and responsive, with scripted events seamlessly integrated into the gameplay. The concept of line-based triggers influenced the design of later games, including Quake and Unreal, which expanded on this idea with more sophisticated event systems and scripting languages." - - id: "case-statement-special-actions" - line_start: 801 - line_end: 948 - title: "How DOOM's case statements control the world" - wikipedia_url: "https://doomwiki.org/wiki/Line_special" + title: "Crossing Lines: Triggering Events Dynamically" + wikipedia_url: "https://en.wikipedia.org/wiki/Trigger_(game_design)" image_url: "" image_caption: "" - content: "This section contains a large switch-case structure that maps specific line specials (triggered by player actions or map events) to their corresponding effects in the game world. Each case corresponds to an action, such as opening doors, raising floors, or teleporting objects. For example, case 97 triggers a teleportation event, while case 94 raises a floor and crushes anything above it. At the time, such direct mappings were a practical way to implement complex interactions in a resource-constrained environment. This approach allowed DOOM to offer a wide variety of dynamic effects without requiring extensive computational overhead. The technique influenced later games by demonstrating how simple structures could yield complex behaviors, especially in level design. Games like Quake and Unreal adopted similar methods for handling environmental interactions." - - id: "impact-specials-shooting-lines" + content: "The `P_CrossSpecialLine` function handles events triggered when a game object crosses a line with a special property. It checks the type of object (e.g., player or projectile) and the line's special code, activating corresponding actions like opening doors, teleporting, or raising platforms. This system was a cornerstone of DOOM's interactive levels, enabling dynamic gameplay and environmental storytelling. The function's modular design allowed developers to easily add new triggers, contributing to the game's flexibility and replayability. The concept of line-based triggers influenced later games, becoming a standard feature in level design and scripting systems across the industry." + - id: "impact-specials-triggered-by-shots" line_start: 953 line_end: 999 - title: "What happens when you shoot a wall?" - wikipedia_url: "https://doomwiki.org/wiki/Line_special" + title: "Impact Specials Triggered by Shots" + wikipedia_url: "https://doomwiki.org/wiki/Line_specials" image_url: "" image_caption: "" - content: "The `P_ShootSpecialLine` function handles the impact of projectiles on special lines in the game world. When a projectile hits a line with a special property, this function determines whether the line should activate an effect, such as opening a door or raising a floor. The function checks if the shooter is a player or another entity, ensuring that only valid interactions occur. This mechanic added depth to DOOM's gameplay by allowing players to interact with the environment in unexpected ways, such as shooting switches to trigger events. The idea of environmental interaction through shooting influenced later games like Half-Life, where players could manipulate objects and solve puzzles with their weapons." - - id: "player-in-special-sector" + content: "This subroutine, `P_ShootSpecialLine`, handles the activation of special line effects when a projectile impacts them. The function checks if the object interacting with the line is a player or another entity, and then executes specific actions based on the line's 'special' property. For example, shooting a line might open a door or raise a floor. This mechanism allowed DOOM to integrate environmental interaction into its fast-paced gameplay, making the world feel more reactive and alive. In 1993, such dynamic interactions were rare in games, particularly in first-person shooters. John Carmack and the id Software team leveraged these mechanics to enhance immersion and gameplay complexity. The concept of 'line specials' became a staple in level design for future games, influencing titles like Quake and Unreal, which expanded on the idea with more complex triggers and scripting systems." + - id: "player-damage-and-secret-detection" line_start: 1003 line_end: 1070 - title: "Walking through slime and secrets" - wikipedia_url: "https://doomwiki.org/wiki/Sector_special" + title: "Player Damage and Secret Detection" + wikipedia_url: "https://doomwiki.org/wiki/Sector_specials" image_url: "" image_caption: "" - content: "The `P_PlayerInSpecialSector` function checks whether the player is in a special sector and applies the corresponding effects. Special sectors include damage zones like 'hellslime' and 'nukage,' secret areas that increase the player's secret count, and finale zones that trigger level exits. This function also handles unique mechanics, such as disabling god mode in the E1M8 finale. By tying environmental hazards and rewards to specific sectors, DOOM created a more immersive and strategic gameplay experience. Players had to navigate carefully, avoiding damage zones while seeking out secrets. This sector-based system became a staple in level design for future games, influencing titles like Duke Nukem 3D and System Shock." - - id: "update-specials-animation-and-effects" + content: "The `P_PlayerInSpecialSector` function is called every frame to check if the player is in a special sector. Depending on the sector type, the player may take damage (e.g., from 'hellslime' or 'nukage') or trigger other effects like discovering a secret area. This routine also handles unique cases like the finale of Episode 1, where the player can be forced out of god mode and subjected to extreme damage. The idea of sectors with special properties was revolutionary for its time, enabling designers to craft environments with varied gameplay mechanics. This approach influenced level design in countless games, including Half-Life and Portal, where environmental storytelling and player interaction are key elements." + - id: "animating-textures-and-global-effects" line_start: 1082 line_end: 1155 - title: "Animating textures and scrolling walls" + title: "Animating Textures and Global Effects" wikipedia_url: "https://doomwiki.org/wiki/Texture_animation" image_url: "" image_caption: "" - content: "The `P_UpdateSpecials` function handles global animations and special effects, including texture animations, scrolling walls, and timed events. It updates animated textures by cycling through frames based on the game's level time and manages line specials like scrolling offsets. The function also processes button timers, resetting textures when a button's effect expires. These dynamic elements added visual variety and interactivity to DOOM's levels, making the game world feel alive. The concept of animated textures and environmental effects was groundbreaking at the time and influenced later games like Quake and Unreal, which expanded on these ideas with more advanced rendering techniques." - - id: "ev-do-donut" + content: "The `P_UpdateSpecials` function animates textures, scrolls walls, and handles timed events like level timers. It iterates through a list of animations and updates texture mappings based on the current game time. This routine also manages special line effects, such as scrolling textures, and updates button states when their timers expire. In the early 1990s, texture animation was a novel feature that added visual dynamism to game environments. DOOM's implementation of these effects demonstrated how simple techniques could create a sense of motion and life in static environments. This approach was later refined in games like Duke Nukem 3D and Quake, which introduced more advanced animation systems and scripting capabilities." + - id: "the-donut-sector-effect" line_start: 1159 line_end: 1220 - title: "The donut sector: rising slime and lowering floors" + title: "The Donut Sector Effect" wikipedia_url: "https://doomwiki.org/wiki/Donut_sector" image_url: "" image_caption: "" - content: "The `EV_DoDonut` function creates a unique environmental effect known as the 'donut sector.' It spawns a rising slime floor in one sector while lowering the floor in an adjacent sector, creating a dynamic visual and gameplay element. This effect is achieved by iterating through sectors connected to the triggering line and initializing floor movement objects. The donut sector showcases DOOM's ability to create complex environmental interactions with relatively simple code structures. This technique inspired creative level designs in later games, encouraging developers to experiment with dynamic environments and interconnected sector mechanics." - - id: "p-spawn-specials" + content: "The `EV_DoDonut` function creates a 'donut' effect in the game world, where a central sector lowers while an outer sector rises, creating a dynamic visual and gameplay element. This routine uses the 'thinker' system to manage the movement of floors, spawning two separate floor movers to achieve the desired effect. The 'donut' was a creative way to add variety to levels, showcasing id Software's ingenuity in using limited resources to create compelling environments. This technique inspired similar dynamic level features in later games, such as the moving platforms and elevators in Quake and Unreal Tournament." + - id: "spawning-special-effects-and-thinkers" line_start: 1237 - line_end: 1361 - title: "Initializing flickering lights and secret areas" - wikipedia_url: "https://doomwiki.org/wiki/Sector_special" + line_end: 1256 + title: "Spawning Special Effects and Thinkers" + wikipedia_url: "https://doomwiki.org/wiki/Thinker" image_url: "" image_caption: "" - content: "The `P_SpawnSpecials` function initializes special effects and environmental interactions after a map is loaded. It sets up flickering lights, strobe effects, glowing sectors, and secret areas, as well as timed events like doors closing after 30 seconds. This function also prepares line effects, such as scrolling textures, and resets active objects like platforms and buttons. By organizing these elements during map initialization, DOOM ensured that each level felt unique and dynamic. This approach influenced the design of scripting systems in later games, such as Unreal Engine's Kismet and Unity's event systems, which allow developers to define complex interactions during level setup." + content: "The `P_SpawnSpecials` function initializes special sector and line effects after a map is loaded. It scans for sectors with specific properties, such as flickering lights, strobe effects, or glowing lights, and spawns 'thinkers' to manage these effects during gameplay. This routine also handles command-line parameters for timed events in deathmatch mode. The 'thinker' system was a key innovation in DOOM, allowing dynamic elements to be managed efficiently. This approach influenced the development of scripting systems in later engines, such as the Unreal Engine and Source Engine, which expanded on the concept with more complex behaviors and interactions." --- @@ -1498,4 +1482,4 @@ void P_SpawnSpecials (void) // UNUSED: no horizonal sliders. // P_InitSlidingDoorFrames(); } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/r-bsp-c.md b/public/programs/doom/r-bsp-c.md index 761ed1c..f942780 100644 --- a/public/programs/doom/r-bsp-c.md +++ b/public/programs/doom/r-bsp-c.md @@ -9,66 +9,90 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "r-bsp-c" order: 12 -description: "This file implements BSP traversal and rendering, a key innovation in DOOM's real-time 3D graphics engine." +description: "This file contains the BSP traversal and rendering logic for DOOM, a foundational piece of its groundbreaking 3D graphics engine." summary: - - point: "Introduced BSP traversal for efficient rendering" + - point: "Implements BSP traversal for efficient rendering" link: "https://en.wikipedia.org/wiki/Binary_space_partitioning" link_label: "Binary Space Partitioning" - - point: "Optimized wall clipping using solid segment lists" - link: "https://doomwiki.org/wiki/Rendering_engine" - link_label: "DOOM Rendering Engine" - - point: "Handled subsector rendering and sprite addition" - link: "https://doomwiki.org/wiki/Subsector" - link_label: "Subsector Rendering" - - point: "Recursive BSP traversal for spatial division" - link: "https://en.wikipedia.org/wiki/Binary_space_partitioning" - link_label: "BSP Trees" - - point: "Efficient visibility checks using bounding boxes" - link: "https://doomwiki.org/wiki/Rendering_engine" - link_label: "Bounding Box Checks" + - point: "Optimizes visibility checks with clipping techniques" + link: "https://en.wikipedia.org/wiki/Clipping_(computer_graphics)" + link_label: "Clipping in Computer Graphics" + - point: "Pushes the limits of 1993-era consumer hardware" + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM (1993)" + - point: "Introduces recursive BSP traversal for real-time rendering" + link: "https://en.wikipedia.org/wiki/DOOM_engine" + link_label: "DOOM Engine" + - point: "Influenced modern game engines like Unreal and Unity" + link: "https://en.wikipedia.org/wiki/Game_engine" + link_label: "Game Engine" enhancements: - - id: "clear-draw-segments" + - id: "clear-drawsegs-initializes-rendering-buffer" line_start: 64 line_end: 237 - title: "Why DOOM Clears Draw Segments Before Rendering" - wikipedia_url: "https://doomwiki.org/wiki/Rendering_engine" + title: "Clear DrawSegs: Initializes Rendering Buffer" + wikipedia_url: "https://en.wikipedia.org/wiki/Double_buffering" image_url: "" image_caption: "" - content: "This function, `R_ClearDrawSegs`, resets the draw segment pointer (`ds_p`) to the beginning of the `drawsegs` array. Draw segments represent portions of walls that need to be rendered, and clearing them ensures that no stale data from previous frames interferes with the current rendering pass. In 1993, memory management was critical due to hardware constraints—DOOM had to run efficiently on machines with as little as 4MB of RAM. By reusing pre-allocated arrays like `drawsegs`, the developers avoided costly dynamic memory allocation during gameplay. This approach influenced later game engines, which adopted similar strategies for managing render lists and minimizing frame-to-frame overhead." - - id: "clip-solid-wall-segment" + content: "This function resets the draw segment buffer (`drawsegs`) by pointing the buffer pointer (`ds_p`) to the start of the array. This simple initialization step is crucial for ensuring that each frame starts with a clean slate for rendering visible line segments. In 1993, memory management was a critical concern due to limited hardware resources, and this approach reflects the careful attention to efficiency required in DOOM's design. By clearing the buffer before rendering, DOOM avoids artifacts and ensures consistent visuals. This technique of resetting buffers before use became a standard practice in graphics programming, influencing later engines like Unreal and Unity." + - id: "clip-solid-wall-segment-handles-occlusion" line_start: 96 line_end: 184 - title: "How DOOM Handles Solid Wall Clipping" - wikipedia_url: "https://en.wikipedia.org/wiki/Binary_space_partitioning" + title: "ClipSolidWallSegment: Handles Occlusion Efficiently" + wikipedia_url: "https://en.wikipedia.org/wiki/Visibility_(computer_graphics)" image_url: "" image_caption: "" - content: "The `R_ClipSolidWallSegment` function processes solid walls that block the player's view entirely. It uses a list of clip ranges (`solidsegs`) to track visible portions of the screen and inserts new segments when necessary. The algorithm ensures efficient rendering by avoiding redundant calculations for occluded areas. At the time, this was a novel solution to the problem of visibility determination in 3D environments. John Carmack's implementation of BSP trees and clipping algorithms in DOOM set a precedent for real-time rendering in games, influencing engines like Quake and Unreal. The technique also became a staple in computer graphics education, demonstrating how spatial partitioning can optimize rendering pipelines." - - id: "clip-pass-wall-segment" - line_start: 188 + content: "This function clips solid wall segments, ensuring that only visible portions of walls are rendered. It uses a range-based approach to determine visibility and updates the clipping list accordingly. In the early 1990s, visibility determination was a major challenge in real-time graphics due to the lack of hardware acceleration. John Carmack's implementation here is a masterclass in efficiency, leveraging binary space partitioning (BSP) to minimize rendering overhead. The function's careful handling of edge cases, such as overlapping or adjacent segments, reflects the precision required to achieve DOOM's smooth performance on hardware like the 486 processor. This approach influenced later games and engines, embedding BSP-based visibility checks into the DNA of real-time rendering." + - id: "clip-pass-wall-segment-renders-windows" + line_start: 192 line_end: 237 - title: "Clipping Windows: DOOM's Pass Wall Segments" - wikipedia_url: "https://doomwiki.org/wiki/Rendering_engine" + title: "ClipPassWallSegment: Renders Transparent Windows" + wikipedia_url: "https://en.wikipedia.org/wiki/Transparency_(graphic)" + image_url: "" + image_caption: "" + content: "This function clips wall segments that represent windows or other transparent surfaces. Unlike solid walls, these segments allow partial visibility of objects behind them. The function ensures that only the visible portions of these segments are rendered, avoiding unnecessary computations for occluded areas. In DOOM, this technique enabled the creation of complex environments with varied textures and transparency effects, a significant leap forward in immersive game design. The handling of transparent surfaces here laid the groundwork for more advanced techniques in later engines, such as alpha blending and shader-based transparency in modern 3D graphics." + - id: "clear-clipsegs-initializes-clipping-list" + line_start: 241 + line_end: 251 + title: "ClearClipSegs: Initializes Clipping List" + wikipedia_url: "https://en.wikipedia.org/wiki/Clipping_(computer_graphics)" + image_url: "" + image_caption: "" + content: "This function initializes the clipping list (`solidsegs`) with two default segments: one representing the leftmost edge of the screen and the other the rightmost edge. This setup ensures that all subsequent clipping operations are bounded within the visible area. In DOOM, efficient clipping was essential for maintaining high performance on limited hardware. By predefining these bounds, the engine avoids unnecessary checks and simplifies the logic for handling visibility. This approach to clipping initialization became a common optimization in real-time rendering systems, influencing techniques used in later games and engines." + - id: "add-line-clips-and-renders-visible-segments" + line_start: 253 + line_end: 355 + title: "AddLine: Clips and Renders Visible Segments" + wikipedia_url: "https://en.wikipedia.org/wiki/Backface_culling" image_url: "" image_caption: "" - content: "The `R_ClipPassWallSegment` function handles walls that act as windows, allowing partial visibility through textures. Unlike solid walls, these segments are not added to the clip list but are processed for rendering. This distinction between solid and passable walls was crucial for creating DOOM's immersive environments, where players could see through windows or openings while maintaining performance. The function's design reflects the game's reliance on efficient algorithms to manage complex scenes on limited hardware. This approach influenced later games that needed to balance visual fidelity with computational constraints, especially in early 3D engines." - - id: "check-bounding-box" + content: "The `R_AddLine` function clips a line segment to the player's view and adds any visible portions to the rendering list. It incorporates backface culling to exclude segments facing away from the player, optimizing rendering performance. This function also handles special cases like doors and windows, ensuring that only relevant geometry is processed. In the context of 1993 hardware, these optimizations were critical for achieving DOOM's fast-paced gameplay. The use of backface culling and view clipping here influenced later engines, embedding these techniques as standard practices in real-time 3D graphics." + - id: "check-bbox-tests-visibility-of-subtrees" line_start: 380 line_end: 486 - title: "The Bounding Box Trick That Saved DOOM's Frame Rate" - wikipedia_url: "https://doomwiki.org/wiki/Rendering_engine" + title: "CheckBBox: Tests Visibility of BSP Subtrees" + wikipedia_url: "https://en.wikipedia.org/wiki/Binary_space_partitioning" + image_url: "" + image_caption: "" + content: "The `R_CheckBBox` function determines whether a bounding box in the BSP tree might be visible from the player's viewpoint. It uses angle calculations and clipping checks to efficiently reject invisible subtrees, reducing the number of polygons processed during rendering. This technique was pivotal in DOOM's ability to render complex environments on limited hardware. By leveraging BSP traversal and bounding box checks, the engine minimized computational overhead, enabling smooth gameplay even on early PCs. The concept of bounding box visibility testing became a cornerstone of real-time rendering, influencing techniques in engines like Quake and Unreal." + - id: "subsector-handles-floor-ceiling-and-sprites" + line_start: 490 + line_end: 541 + title: "Subsector: Handles Floor, Ceiling, and Sprites" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_engine" image_url: "" image_caption: "" - content: "The `R_CheckBBox` function determines whether a bounding box might be visible from the player's viewpoint. It uses precomputed coordinates and angles to quickly reject portions of the scene that are outside the field of view. This optimization was essential for DOOM's performance, as it reduced the number of subsectors and walls that needed to be processed during rendering. Bounding box checks were a direct response to the hardware limitations of the era, where CPUs lacked the power to handle exhaustive visibility calculations. The technique influenced later engines, including Quake, which built on DOOM's spatial partitioning and visibility determination methods to enable even more complex 3D environments." - - id: "render-bsp-node" + content: "The `R_Subsector` function processes a single subsector in the BSP tree, determining floor and ceiling planes, adding sprites, and rendering line segments. This function encapsulates the core rendering logic for DOOM's environments, combining geometry, textures, and objects into a seamless visual experience. In 1993, this level of integration was groundbreaking, enabling DOOM to create immersive 3D worlds on consumer hardware. The subsector-based rendering approach influenced later engines, including Quake, which expanded on these ideas to support fully 3D environments." + - id: "render-bsp-node-recursive-traversal-for-rendering" line_start: 546 line_end: 577 - title: "Recursive BSP Traversal: DOOM's Rendering Backbone" + title: "RenderBSPNode: Recursive Traversal for Rendering" wikipedia_url: "https://en.wikipedia.org/wiki/Binary_space_partitioning" image_url: "" image_caption: "" - content: "The `R_RenderBSPNode` function is the heart of DOOM's BSP-based rendering system. It recursively traverses the BSP tree, rendering subsectors and checking visibility using bounding boxes. This approach allowed DOOM to efficiently divide the game world into manageable chunks, ensuring that only visible portions were processed. BSP trees were a groundbreaking innovation in 1993, enabling real-time 3D graphics on hardware that lacked dedicated GPUs. John Carmack adapted the technique from academic research and earlier games like Wolfenstein 3D, refining it to handle DOOM's more complex environments. The success of BSP traversal in DOOM directly influenced the development of Quake and other 3D engines, solidifying its place in the history of computer graphics." + content: "The `R_RenderBSPNode` function recursively traverses the BSP tree, rendering all subsectors below a given node. It uses visibility checks (`R_CheckBBox`) to avoid processing occluded subtrees, optimizing performance. This recursive approach was a key innovation in DOOM's engine, enabling efficient rendering of complex environments on limited hardware. The use of BSP traversal for real-time rendering became a defining feature of the DOOM engine, influencing subsequent engines like Quake and Unreal, which expanded on these ideas to support more advanced graphics and physics." --- @@ -650,4 +674,6 @@ void R_RenderBSPNode (int bspnum) if (R_CheckBBox (bsp->bbox[side^1])) R_RenderBSPNode (bsp->children[side^1]); } -``` + + +``` \ No newline at end of file diff --git a/public/programs/doom/r-data-c.md b/public/programs/doom/r-data-c.md index bd8c82e..db0f866 100644 --- a/public/programs/doom/r-data-c.md +++ b/public/programs/doom/r-data-c.md @@ -9,74 +9,76 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "r-data-c" order: 34 -description: "This file handles texture, sprite, and flat data preparation for rendering in DOOM, showcasing groundbreaking techniques for efficient graphics handling on 1990s hardware." +description: "This file handles texture and sprite data preparation for DOOM's rendering engine, showcasing key techniques in efficient data management and caching." summary: - - point: "DOOM's texture system combines patches into composite textures for efficient rendering." - link: "https://en.wikipedia.org/wiki/Texture_mapping" - link_label: "Texture Mapping" - - point: "The caching system minimizes memory usage by dynamically generating composite textures." - link: "https://en.wikipedia.org/wiki/Cache_(computing)" - link_label: "Caching" - - point: "Preloading graphics data ensures smooth gameplay performance on limited hardware." + - point: "Introduced texture caching to optimize rendering performance" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM" - - point: "The file demonstrates modular design, separating texture, sprite, and flat initialization." - link: "https://en.wikipedia.org/wiki/Software_design" - link_label: "Software Design" - - point: "The use of WAD files for data storage became a standard in game development." - link: "https://en.wikipedia.org/wiki/WAD_(file_format)" - link_label: "WAD File Format" + - point: "Efficiently managed limited memory resources on 1990s hardware" + link: "https://en.wikipedia.org/wiki/History_of_computer_hardware" + link_label: "History of computer hardware" + - point: "Pioneered techniques for compositing textures dynamically" + link: "https://en.wikipedia.org/wiki/Texture_mapping" + link_label: "Texture mapping" enhancements: - id: "texture-definition-structure" line_start: 221 line_end: 288 - title: "How DOOM Built Textures from Patches" + title: "Patch-Based Textures and Runtime Compositing" wikipedia_url: "https://en.wikipedia.org/wiki/Texture_mapping" image_url: "" image_caption: "" - content: "This section defines the structures used for textures in DOOM, including `mappatch_t`, `maptexture_t`, and `texture_t`. Each texture is composed of patches, which are smaller graphical elements stored in WAD files. These patches are positioned within a texture using origin coordinates and other attributes. The modular design allowed DOOM to efficiently reuse graphical assets, reducing memory consumption—a critical consideration for 1993 hardware like the Intel 486, which often had only 4–8 MB of RAM. The concept of combining smaller graphical elements into larger composite textures influenced later games and engines, including Quake and Unreal Engine, which adopted similar modular approaches to texture management." - - id: "r-draw-column-in-cache" - line_start: 221 - line_end: 288 - title: "The Routine That Cached Columns" + content: "This section defines the on-disk mappatch_t and maptexture_t structures alongside the in-memory texture_t and texpatch_t structures, establishing DOOM's two-stage texture pipeline. Wall textures are not stored as flat bitmaps but as ordered lists of patches (smaller reusable graphic lumps from the WAD) that are composited at runtime by R_GenerateComposite only when a column is first requested. The mappatch_t fields include an OBSOLETE columndirectory pointer left as a reminder of an earlier design that was replaced by the column-lookup approach in R_GenerateLookup. Reusing patches across many textures kept the shareware WAD small enough to distribute on a few floppy disks while still delivering richly varied wall surfaces throughout all nine maps. This deferred, patch-based compositing technique directly prefigured texture atlases and virtual texturing schemes used in modern engines such as Unreal Engine's UDIM and Unity's sprite atlases." + - id: "draw-column-in-cache" + line_start: 178 + line_end: 217 + title: "The Trick That Cached Texture Columns" wikipedia_url: "https://en.wikipedia.org/wiki/Cache_(computing)" image_url: "" image_caption: "" - content: "The `R_DrawColumnInCache` function clips and draws a column from a patch into a cached post. This caching mechanism was crucial for DOOM's performance, as it avoided recalculating texture data during gameplay. By storing columns in memory, the renderer could quickly access precomputed graphics data, enabling smooth frame rates even on modest hardware. John Carmack's focus on optimizing memory usage and rendering speed was a hallmark of DOOM's development, and this routine exemplifies his approach. Techniques like this laid the groundwork for efficient graphics handling in later engines, including the id Tech series." - - id: "r-generate-composite" - line_start: 221 - line_end: 288 - title: "Generating Composite Textures Dynamically" - wikipedia_url: "https://en.wikipedia.org/wiki/Texture_mapping" + content: "The `R_DrawColumnInCache` function clips and draws a column from a patch into the texture cache. By caching columns, DOOM avoided recalculating texture data during rendering, significantly improving performance. This was vital for achieving the game's fast-paced action on hardware with limited processing power. The function uses memory-efficient techniques, such as directly copying pixel data and handling edge cases where columns exceed the texture bounds. This caching approach was a precursor to modern texture atlases used in game engines like Unity and Unreal, which optimize rendering by consolidating texture data into a single memory block." + - id: "generate-column-lookup" + line_start: 292 + line_end: 373 + title: "Column Lookup Tables for Texture Optimization" + wikipedia_url: "https://en.wikipedia.org/wiki/Lookup_table" image_url: "" image_caption: "" - content: "The `R_GenerateComposite` function dynamically creates composite textures by combining patches based on the texture definition. Each column is cached, and overlapping patches are handled by generating new column data. This approach allowed DOOM to use highly detailed textures without requiring excessive memory. The function also ensured that textures were purged from zone memory once cached, maintaining efficient memory usage. This dynamic texture generation technique was innovative for its time and influenced how later engines handled texture composition and caching, including Quake and Unreal Engine." - - id: "r-init-textures" + content: "The `R_GenerateLookup` function creates lookup tables for texture columns, identifying columns covered by multiple patches and calculating their offsets. This optimization reduced the overhead of accessing texture data during rendering, as columns with single patches could be directly referenced, while those with multiple patches were cached. The function also includes error handling for missing patches and textures exceeding memory limits. Lookup tables were a common optimization in 1990s software, enabling faster access to precomputed data—a concept still widely used in modern computing, from graphics engines to database indexing." + - id: "initialize-textures" line_start: 405 line_end: 573 - title: "Initializing DOOM's Texture System" + title: "Loading and Organizing Texture Data" wikipedia_url: "https://en.wikipedia.org/wiki/WAD_(file_format)" image_url: "" image_caption: "" - content: "The `R_InitTextures` function initializes the texture list by loading texture definitions from WAD files. It handles both shareware and commercial texture lumps (`TEXTURE1` and `TEXTURE2`), ensuring compatibility across different versions of the game. The function also precomputes lookup tables for texture rendering, optimizing performance. This modular initialization process reflects DOOM's well-structured design, which separated data preparation from rendering logic. The use of WAD files for storing texture data became a standard in game development, influencing titles like Duke Nukem 3D and Half-Life." - - id: "r-init-sprite-lumps" + content: "The `R_InitTextures` function initializes the texture list by loading texture definitions from the WAD files. It reads patch names from `PNAMES` and texture definitions from `TEXTURE1` and `TEXTURE2`, organizing them into memory structures for efficient access. This process includes error checking for missing patches and calculating texture dimensions and masks. The function's design reflects the constraints of 1990s hardware, where efficient memory usage and data organization were paramount. By separating texture definitions from patch data, DOOM's engine achieved modularity and reusability, influencing file formats like Quake's PAK and later ZIP-based game archives." + - id: "initialize-flats" + line_start: 577 + line_end: 593 + title: "Flat Animation Made Simple" + wikipedia_url: "https://en.wikipedia.org/wiki/Animation" + image_url: "" + image_caption: "" + content: "The `R_InitFlats` function initializes flat textures used for floors and ceilings. It creates a translation table for global animation, enabling flat textures to cycle through predefined frames. This approach simplified the implementation of animated textures, such as moving water or lava, without requiring complex rendering logic. Animated flats became a staple of level design in DOOM and inspired similar features in later games, where dynamic environmental effects added to immersion." + - id: "initialize-sprite-lumps" line_start: 596 line_end: 625 - title: "Preloading Sprite Metadata for Speed" + title: "Sprite Dimensions Without Full Caching" wikipedia_url: "https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" image_url: "" image_caption: "" - content: "The `R_InitSpriteLumps` function preloads metadata for all sprites in the WAD file, including width, offsets, and top offsets. By caching this information, DOOM avoided the need to repeatedly load sprite headers during rendering, significantly improving performance. This optimization was essential for maintaining smooth gameplay on hardware with limited processing power. The technique of preloading sprite metadata influenced later games, which adopted similar strategies to optimize rendering pipelines." - - id: "r-precache-level" + content: "The `R_InitSpriteLumps` function calculates the width and offsets of sprite graphics without caching their full data. By extracting only the header information, DOOM's engine minimized memory usage while ensuring sprites could be rendered efficiently. This technique was crucial for managing the game's large number of sprite animations on hardware with limited RAM. It also laid the groundwork for modern sprite batching techniques, where only essential data is loaded to optimize rendering." + - id: "precache-level-assets" line_start: 742 - line_end: 844 - title: "Preloading Graphics for Seamless Gameplay" - wikipedia_url: "https://en.wikipedia.org/wiki/Cache_(computing)" + line_end: 767 + title: "Preloading Graphics for Faster Gameplay" + wikipedia_url: "https://en.wikipedia.org/wiki/Pre-rendering" image_url: "" image_caption: "" - content: "The `R_PrecacheLevel` function preloads all graphics relevant to the current level, including flats, textures, and sprites. By caching this data before gameplay begins, DOOM ensured smooth performance and minimized loading times during play. This approach was particularly important for 1993 hardware, where disk access was slow and memory was limited. The precaching technique became a standard practice in game development, influencing engines like Source and Unity, which use similar methods to optimize asset loading." + content: "The `R_PrecacheLevel` function preloads all graphics needed for a level, including flats, textures, and sprites. By caching these assets before gameplay begins, DOOM reduced loading times and ensured smooth performance during intense action sequences. This approach was particularly effective on 1990s hardware, where disk access was slow and memory was limited. Preloading became a standard practice in game development, influencing asset management techniques in engines like Unity and Unreal." --- @@ -925,4 +927,8 @@ void R_PrecacheLevel (void) } } } -``` + + + + +``` \ No newline at end of file diff --git a/public/programs/doom/r-draw-c.md b/public/programs/doom/r-draw-c.md index 605f506..ea9b63a 100644 --- a/public/programs/doom/r-draw-c.md +++ b/public/programs/doom/r-draw-c.md @@ -9,90 +9,90 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "r-draw-c" order: 13 -description: "This file contains the rendering routines for DOOM's graphics engine, showcasing techniques that pushed the limits of 1993-era hardware." +description: "This file contains the core rendering routines for DOOM, showcasing techniques that pushed the limits of 1990s hardware." summary: - - point: "Column-based rendering optimized for fixed-view angles" + - point: "DOOM's rendering optimized for fixed view orientations" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Use of lookup tables to avoid expensive calculations" - link: "https://en.wikipedia.org/wiki/Lookup_table" - link_label: "Lookup Table" - - point: "Innovative framebuffer manipulation for effects like invisibility" + - point: "Column and span rendering tailored for low memory systems" link: "https://en.wikipedia.org/wiki/Framebuffer" link_label: "Framebuffer" - - point: "Translation tables for dynamic color remapping" - link: "https://en.wikipedia.org/wiki/Color_mapping" - link_label: "Color Mapping" - - point: "Efficient handling of variable screen sizes and borders" - link: "https://en.wikipedia.org/wiki/Aspect_ratio_(image)" - link_label: "Aspect Ratio" + - point: "Innovative use of translation tables for player customization" + link: "https://doomwiki.org/wiki/Translation_table" + link_label: "Translation Table" + - point: "Fuzzy rendering technique for invisibility effects" + link: "https://doomwiki.org/wiki/Spectre" + link_label: "Spectre" + - point: "Border rendering for variable screen sizes" + link: "https://doomwiki.org/wiki/Scaled_viewport" + link_label: "Scaled Viewport" enhancements: - id: "column-rendering-optimization" line_start: 97 line_end: 206 - title: "The Trick That Made Walls Fast" + title: "R_DrawColumn: Optimized Wall Rendering and Abandoned Loop Unrolling" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `R_DrawColumn` function is responsible for rendering vertical slices of wall textures, a technique optimized for DOOM's fixed-view perspective. By leveraging lookup tables (`ylookup` and `columnofs`), the function avoids costly multiplications to calculate framebuffer addresses, instead relying on precomputed offsets. This approach is rooted in techniques used in earlier games like Wolfenstein 3D, where fixed-view angles simplified rendering calculations. In 1993, consumer PCs had limited processing power, often lacking hardware acceleration for graphics. John Carmack's decision to optimize for fixed-view angles allowed DOOM to achieve its groundbreaking speed and fluidity on modest hardware. This technique influenced later games and engines, including Quake, which built on these principles while introducing more advanced 3D rendering." - - id: "unused-loop-unrolling" - line_start: 97 - line_end: 206 - title: "The Loop Unrolling That Never Shipped" - wikipedia_url: "https://en.wikipedia.org/wiki/Loop_unrolling" + content: "This range contains the active `R_DrawColumn` function and, guarded by an #ifdef USEASM block that was never enabled in the release build, a manually unrolled variant that was explored and ultimately discarded. The shipping version walks down each vertical wall slice one pixel at a time, computing framebuffer addresses with prebuilt ylookup and columnofs tables so that every iteration reduces to a single table lookup and an indexed write instead of a multiply-add. On a 486 processor, eliminating the multiply was a meaningful win given that R_DrawColumn was one of the most-called functions in the entire engine. The shelved unrolled version processed multiple pixels per iteration to amortize loop-control overhead, a valid strategy but one that bloated the instruction cache footprint and yielded diminishing returns on the in-order integer pipelines of the era. Leaving the dead code in the source is itself historically instructive: it documents that Carmack explored the technique, measured it, and chose the simpler loop. This kind of iterative, evidence-driven optimization became a defining characteristic of id Software's engineering culture and influenced how later engine teams approached renderer micro-optimization." + - id: "low-resolution-rendering" + line_start: 210 + line_end: 252 + title: "Rendering for Blocky Mode: A Low-Res Hack" + wikipedia_url: "https://doomwiki.org/wiki/Low_detail_mode" image_url: "" image_caption: "" - content: "This section contains an unused, loop-unrolled version of the `R_DrawColumn` function. Loop unrolling is a common optimization technique that reduces the overhead of loop control by manually expanding iterations. While this approach can improve performance, it increases code size and complexity. The inclusion of this code suggests that Carmack experimented with various optimization strategies to squeeze every ounce of performance from the hardware. Ultimately, this version was not used, likely because the benefits did not outweigh the trade-offs in maintainability or memory usage. Loop unrolling remains a staple in performance-critical applications, and its presence here highlights the meticulous attention to optimization that defined DOOM's development." - - id: "fuzzy-rendering-for-invisibility" + content: "The `R_DrawColumnLow` function handles rendering in DOOM's 'blocky mode,' a low-resolution setting designed to improve performance on slower systems. By doubling pixel width and height, the game reduces the computational load, allowing it to run on hardware that might otherwise struggle. This mode was a practical solution for players with older PCs, ensuring DOOM's accessibility to a broader audience. The trade-off was a noticeable reduction in visual fidelity, but the gameplay experience remained intact. This approach reflects the era's emphasis on scalability, a principle that continues to influence game development today." + - id: "fuzzy-rendering-for-spectres" line_start: 276 line_end: 367 - title: "How DOOM Made Spectres Invisible" - wikipedia_url: "https://en.wikipedia.org/wiki/Transparency_(graphic)" + title: "The Fuzzy Trick That Made Spectres Invisible" + wikipedia_url: "https://doomwiki.org/wiki/Spectre" image_url: "" image_caption: "" - content: "The `R_DrawFuzzColumn` function creates a 'fuzzy' rendering effect by copying pixels from adjacent columns, simulating invisibility for spectres and players. This effect relies on a predefined table (`fuzzoffset`) to determine pixel offsets, combined with a black colormap to darken the image. In the early 1990s, hardware limitations made true transparency effects impractical, so developers often resorted to creative hacks like this. Carmack's implementation is a clever workaround that achieves a visually convincing result without requiring additional hardware support. The fuzzy effect became iconic, influencing later games that sought to simulate transparency or invisibility within similar constraints." - - id: "dynamic-color-remapping" + content: "The `R_DrawFuzzColumn` function implements DOOM's 'fuzzy' rendering effect, used to create the illusion of invisibility for Spectres and invisible players. By offsetting pixels horizontally and applying a specific colormap, the function creates a distorted, ghostly appearance. This technique was a clever workaround for the lack of advanced transparency effects on 1990s hardware. Spectres became one of DOOM's most iconic enemies, and the fuzzy rendering effect was widely praised for its eerie realism. The concept of manipulating pixels to simulate transparency influenced later games, including Quake and Unreal, which built upon these ideas with more advanced graphics hardware." + - id: "player-color-customization" line_start: 384 line_end: 446 - title: "The Translation Tables That Changed Colors" - wikipedia_url: "https://en.wikipedia.org/wiki/Color_mapping" + title: "How DOOM Let Players Choose Their Colors" + wikipedia_url: "https://doomwiki.org/wiki/Translation_table" image_url: "" image_caption: "" - content: "The `R_DrawTranslatedColumn` function uses translation tables to dynamically remap colors, allowing sprites to appear in different color schemes. This technique is used for player sprites and enemies like the Hell Knight, which shares the Baron of Hell's sprites but uses a brighter color palette. The translation tables are precomputed to map specific color ramps to alternate colors, enabling efficient runtime remapping. This approach reflects Carmack's focus on performance, as it avoids recalculating color mappings during gameplay. Dynamic color remapping became a standard feature in game engines, enabling customization and variety without increasing asset sizes." + content: "The `R_DrawTranslatedColumn` function uses translation tables to remap color ramps, enabling player customization and enemy variations. For example, the green color ramp of player sprites could be mapped to gray, red, or other colors, allowing multiple players to be visually distinct in multiplayer mode. This feature also allowed enemies like the Hell Knight to share sprites with the Baron of Hell, saving memory while maintaining visual diversity. The use of translation tables was an ingenious solution to the constraints of 1990s hardware, where memory was limited and every byte mattered. This technique influenced later games that sought to optimize resource usage while offering customization options." - id: "translation-table-initialization" line_start: 451 line_end: 514 - title: "Mapping Green to Gray, Brown, and Red" - wikipedia_url: "https://en.wikipedia.org/wiki/Color_mapping" + title: "Mapping Green to Gray: Translation Tables" + wikipedia_url: "https://doomwiki.org/wiki/Translation_table" image_url: "" image_caption: "" - content: "The `R_InitTranslationTables` function initializes the translation tables used for dynamic color remapping. It maps the green color ramp (used for player sprites) to gray, brown, and red, allowing for visual differentiation between players or sprite variants. The function assumes a specific structure for the PLAYPAL lump, which defines the game's color palette. This design choice reflects the constraints of the era, where memory and storage limitations required developers to maximize the utility of existing assets. Translation tables became a common feature in game engines, enabling efficient color customization and paving the way for features like team-based multiplayer color schemes." - - id: "span-rendering-for-floors-and-ceilings" + content: "The `R_InitTranslationTables` function initializes the translation tables that remap the green color ramp to gray, brown, and red. This functionality was essential for multiplayer customization and enemy differentiation. By assuming a specific structure of the PLAYPAL lump, the function avoids the need for additional data files, keeping DOOM's resource footprint small. This approach reflects id Software's focus on efficiency and adaptability, ensuring the game could run on a wide range of systems. Translation tables became a standard technique in game development, influencing sprite-based games and even modern engines that use palette-based rendering." + - id: "span-rendering-for-floors" line_start: 517 line_end: 635 - title: "The Horizontal Trick Behind DOOM's Floors" + title: "The Algorithm Behind DOOM's Floors and Ceilings" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `R_DrawSpan` function handles rendering horizontal spans for floors and ceilings. Unlike walls, which are rendered column by column, floors and ceilings are drawn as horizontal slices with constant z-depth. This method leverages DOOM's fixed-view orientation to simplify calculations, using precomputed steps to traverse texture space. The function avoids perspective-correct texture mapping, which would have been computationally expensive on 1993 hardware. Instead, it uses a faster approximation that was sufficient for the game's visual style. This technique influenced later engines, which adopted similar optimizations for rendering large flat surfaces efficiently." - - id: "framebuffer-lookup-table" + content: "The `R_DrawSpan` function handles rendering horizontal spans for floors and ceilings. Unlike walls, which are rendered as vertical columns, floors and ceilings require traversal in both texture space u and v. This function uses a simple yet effective algorithm to step through the texture at an angle, ensuring consistent z-depth. In the early 1990s, perspective-correct texture mapping was computationally expensive, so DOOM's approach prioritized speed over accuracy. This decision allowed the game to maintain high frame rates while delivering visually impressive environments. The technique influenced later engines, which refined span rendering for more complex 3D worlds." + - id: "variable-screen-size-border" line_start: 724 line_end: 810 - title: "The Lookup Table That Sped Up Pixels" - wikipedia_url: "https://en.wikipedia.org/wiki/Framebuffer" + title: "Drawing Borders for Scaled Viewports" + wikipedia_url: "https://doomwiki.org/wiki/Scaled_viewport" image_url: "" image_caption: "" - content: "The `R_InitBuffer` function creates lookup tables (`ylookup` and `columnofs`) to simplify framebuffer address calculations. By precomputing offsets for rows and columns, the function eliminates the need for multiplications during rendering, significantly improving performance. This optimization was critical for DOOM's ability to run smoothly on early 1990s hardware, where CPU cycles were precious. The use of lookup tables for address calculations became a standard technique in graphics programming, influencing the design of later engines and contributing to the industry's understanding of performance optimization." - - id: "variable-screen-size-border" - line_start: 724 - line_end: 810 - title: "How DOOM Drew Its Iconic Borders" - wikipedia_url: "https://en.wikipedia.org/wiki/Aspect_ratio_(image)" + content: "The `R_FillBackScreen` function draws borders and fills the background for variable screen sizes. This feature was crucial for supporting different resolutions and aspect ratios, ensuring DOOM could run on a wide range of hardware configurations. The function uses predefined patches to create beveled edges and decorative patterns, enhancing the game's visual appeal. This attention to detail reflects id Software's commitment to delivering a polished experience, even on lower-end systems. The concept of scalable viewports and adaptive rendering influenced later games, which continued to prioritize accessibility and compatibility across diverse hardware." + - id: "view-border-rendering" + line_start: 842 + line_end: 874 + title: "How DOOM Defined Its View Borders" + wikipedia_url: "https://doomwiki.org/wiki/Scaled_viewport" image_url: "" image_caption: "" - content: "The `R_FillBackScreen` function handles drawing the background pattern and beveled edges for variable screen sizes. It uses predefined patches (like `FLOOR7_2` and `GRNROCK`) to fill the screen and draws borders around the view window. This feature accommodates different aspect ratios and resolutions, ensuring a consistent visual experience across hardware configurations. The ability to dynamically adjust screen borders was a forward-thinking design choice, reflecting id Software's commitment to accessibility and compatibility. This technique influenced later games that needed to support diverse display setups, laying the groundwork for modern scalable UI design." + content: "The `R_DrawViewBorder` function renders the border around the game view for different window sizes. By erasing sections of the screen and marking regions for updates, the function ensures a seamless transition between gameplay and UI elements. This approach was particularly important for players using smaller viewports, as it maintained visual consistency while optimizing performance. The function exemplifies id Software's focus on scalability, a principle that influenced later engines and games aiming to support diverse hardware setups. The concept of view borders remains relevant in modern UI design, where adaptive layouts are essential for cross-platform compatibility." --- @@ -973,4 +973,4 @@ void R_DrawViewBorder (void) } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/r-main-c.md b/public/programs/doom/r-main-c.md index c25c4d4..ad36a28 100644 --- a/public/programs/doom/r-main-c.md +++ b/public/programs/doom/r-main-c.md @@ -9,66 +9,82 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "r-main-c" order: 2 -description: "This file contains the main rendering loop and setup functions for DOOM, showcasing the techniques used to efficiently render pseudo-3D environments on 1990s hardware." +description: "This file contains core rendering routines for DOOM, showcasing techniques that pushed the limits of 1990s consumer hardware." summary: - - point: "Precalculated math tables for rendering efficiency" - link: "https://en.wikipedia.org/wiki/Lookup_table" - link_label: "Lookup table" - - point: "Binary Space Partitioning (BSP) for efficient visibility determination" + - point: "Efficient pseudo-3D rendering using BSP trees" link: "https://en.wikipedia.org/wiki/Binary_space_partitioning" link_label: "Binary Space Partitioning" - - point: "Dynamic light scaling based on distance and view size" + - point: "Precomputed lookup tables for trigonometric calculations" + link: "https://en.wikipedia.org/wiki/Lookup_table" + link_label: "Lookup Table" + - point: "Dynamic view size adjustment for performance optimization" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM" - - point: "Optimized texture mapping using tangent tables" - link: "https://en.wikipedia.org/wiki/Texture_mapping" - link_label: "Texture Mapping" - - point: "Frame setup and rendering pipeline for player view" + - point: "Light level calculations based on distance and scale" + link: "https://en.wikipedia.org/wiki/Lighting_(computer_graphics)" + link_label: "Lighting in Graphics" + - point: "Rendering pipeline for player view with modular subroutines" link: "https://en.wikipedia.org/wiki/Rendering_(computer_graphics)" - link_label: "Rendering" + link_label: "Rendering Pipeline" enhancements: - - id: "field-of-view-optimization" - line_start: 46 - line_end: 97 - title: "The Trick That Flattened DOOM's Field of View" - wikipedia_url: "https://en.wikipedia.org/wiki/Field_of_view_in_video_games" + - id: "expand-bounding-box-with-point" + line_start: 135 + line_end: 151 + title: "How DOOM Expands Bounding Boxes Dynamically" + wikipedia_url: "https://en.wikipedia.org/wiki/Bounding_volume" image_url: "" image_caption: "" - content: "This section defines the field of view (FOV) and sets up the `viewangletox` lookup table, which maps view angles to screen X coordinates. The table flattens the arc of visible angles into a projection plane, allowing DOOM to efficiently render its pseudo-3D environments. At the time, consumer hardware lacked the power for true 3D rendering, so developers relied on clever tricks like this to simulate depth and perspective. By precalculating these mappings, DOOM avoided costly runtime calculations, enabling smooth gameplay even on modest PCs. This technique influenced later games, including Quake, which built on similar principles for its rendering pipeline." - - id: "bsp-and-angle-lookup-tables" - line_start: 422 - line_end: 442 - title: "BSP Trees and the Lookup Table That Replaced Trigonometry" + content: "This subroutine, `R_AddPointToBox`, dynamically adjusts a bounding box to include a given point. It checks whether the point lies outside the current bounds and updates the box's edges accordingly. Bounding boxes are crucial in DOOM's rendering pipeline for spatial partitioning and collision detection. In the early 1990s, efficient spatial calculations were essential due to limited CPU power and memory. This approach ensured that only relevant geometry was processed, saving precious cycles. Bounding boxes became a standard technique in game engines, influencing successors like Quake and Unreal Engine, which expanded on spatial partitioning concepts." + - id: "bsp-side-determination" + line_start: 158 + line_end: 210 + title: "The BSP Trick That Made DOOM Fast" wikipedia_url: "https://en.wikipedia.org/wiki/Binary_space_partitioning" image_url: "" image_caption: "" - content: "This section contains two tightly related geometric routines that together form the backbone of DOOM's spatial reasoning. The `R_PointOnSide` function is part of DOOM's Binary Space Partitioning (BSP) system, dividing the game world into hierarchical subspaces to enable fast visibility determination. By checking whether a point lies on the front or back side of a partition plane, the game rapidly decides which parts of the map are visible — skipping everything hidden behind walls without ever drawing them. BSP trees were a groundbreaking technique in the early 1990s, adapted by John Carmack from computer graphics research, and they became a cornerstone of real-time rendering. Alongside it, `R_PointToAngle` calculates the angle between two points using coordinate flipping and a precalculated `tantoangle` lookup table rather than costly trigonometric functions. On 1990s CPUs, computing `atan2` in real time was impractical, so DOOM replaced it entirely with integer arithmetic and a table lookup — every CPU cycle saved mattered. Together these two routines illustrate DOOM's core philosophy: replace expensive math with clever precomputation, and divide the world into manageable pieces. Both techniques were widely studied and adapted by later developers, influencing games like Duke Nukem 3D, Quake, and Half-Life." - - id: "texture-mapping-setup" + content: "The `R_PointOnSide` function determines which side of a partition plane a point lies on, a key operation in traversing DOOM's Binary Space Partitioning (BSP) tree. BSP trees were a revolutionary technique for rendering pseudo-3D environments efficiently, allowing DOOM to calculate visible geometry without drawing hidden surfaces. Developed by John Carmack, this method was inspired by earlier CAD software but adapted for real-time gaming. BSP trees became a cornerstone of 3D rendering in games, influencing titles like Quake and Half-Life, and remain relevant in modern graphics engines for spatial organization." + - id: "point-to-angle-calculation" + line_start: 290 + line_end: 373 + title: "The Math Behind DOOM's View Angles" + wikipedia_url: "https://en.wikipedia.org/wiki/Angle" + image_url: "" + image_caption: "" + content: "The `R_PointToAngle` function calculates the angle between the player's viewpoint and a given point in the game world. By dividing coordinates into octants and using precomputed tangent lookup tables, DOOM avoids expensive trigonometric calculations during runtime. This optimization reflects the constraints of early 1990s hardware, where floating-point operations were costly. John Carmack's use of lookup tables for angles and slopes was groundbreaking, enabling fast and smooth gameplay. This approach influenced later games and engines, which often relied on similar precomputed tables to handle geometric calculations efficiently." + - id: "texture-mapping-initialization" line_start: 540 line_end: 602 - title: "Optimized Texture Mapping with Tangent Tables" + title: "How DOOM Mapped Textures to Pixels" wikipedia_url: "https://en.wikipedia.org/wiki/Texture_mapping" image_url: "" image_caption: "" - content: "The `R_InitTextureMapping` function sets up texture mapping by using tangent tables to calculate screen coordinates for visible angles. This allows DOOM to efficiently map textures onto walls and other surfaces, creating the illusion of depth in its pseudo-3D environments. Texture mapping was a relatively new concept in the early 1990s, and DOOM's implementation pushed the boundaries of what was possible on consumer hardware. The technique influenced later games and engines, including Quake, which expanded on these ideas with true 3D rendering." - - id: "dynamic-lighting" + content: "The `R_InitTextureMapping` function initializes tables that map view angles to screen coordinates, enabling DOOM's texture mapping system. By precomputing these mappings, the game efficiently translates 3D geometry into 2D screen space, flattening the curved field of view into a linear projection. This technique was essential for achieving smooth rendering on hardware without dedicated graphics acceleration. Texture mapping in DOOM influenced the development of 3D graphics standards, including OpenGL and DirectX, and laid the groundwork for modern GPU-accelerated rendering pipelines." + - id: "dynamic-light-levels" line_start: 606 line_end: 641 - title: "Dynamic Lighting on 1990s Hardware" + title: "Dynamic Lighting in DOOM's World" + wikipedia_url: "https://en.wikipedia.org/wiki/Lighting_(computer_graphics)" + image_url: "" + image_caption: "" + content: "The `R_InitLightTables` function calculates light levels based on distance and scale, creating a dynamic lighting system that adjusts brightness as objects move closer or farther from the player. This technique added depth and atmosphere to DOOM's environments, enhancing immersion. Dynamic lighting was rare in games of the era due to computational constraints, but DOOM's implementation proved that such effects were achievable on consumer hardware. This innovation influenced later games like Quake, which expanded on dynamic lighting with real-time shadows and colored lights." + - id: "adjust-view-size" + line_start: 667 + line_end: 760 + title: "Scaling the View for Performance Gains" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `R_InitLightTables` function calculates light levels based on distance and view size, creating a dynamic lighting effect that enhances DOOM's immersive atmosphere. By scaling light intensity with distance, the game simulates realistic lighting without requiring advanced hardware capabilities. This was a significant innovation at a time when most games used static lighting. The technique influenced later engines, including the Build Engine used in Duke Nukem 3D, and laid the groundwork for more sophisticated lighting systems in modern games." - - id: "frame-setup-and-rendering" + content: "The `R_ExecuteSetViewSize` function dynamically adjusts the game's view size based on user settings or performance needs. By scaling the rendering window, DOOM could maintain smooth gameplay on a wide range of hardware, from high-end PCs to budget systems. This adaptability was crucial in an era when hardware capabilities varied significantly. The concept of adjustable view sizes influenced later games, which often included similar settings to optimize performance. It also demonstrated the importance of user-configurable graphics options, a standard feature in modern gaming." + - id: "render-player-view" line_start: 866 line_end: 897 - title: "What Happens Before DOOM Draws a Frame" + title: "The Rendering Pipeline That Defined DOOM" wikipedia_url: "https://en.wikipedia.org/wiki/Rendering_(computer_graphics)" image_url: "" image_caption: "" - content: "The `R_RenderPlayerView` function is the heart of DOOM's rendering pipeline. It sets up the player's view, clears buffers, and processes the Binary Space Partitioning (BSP) tree to determine visible geometry. The function then draws planes and masked textures, completing the frame. This modular approach to rendering was a key innovation, allowing DOOM to achieve high performance and maintainable code. The pipeline influenced the design of later engines, including id Tech 2 and id Tech 3, which powered games like Quake II and Quake III Arena." + content: "The `R_RenderPlayerView` function orchestrates DOOM's rendering pipeline, calling subroutines to clear buffers, traverse the BSP tree, draw planes, and render masked objects. This modular approach allowed the game to efficiently process and display complex scenes on limited hardware. Each step in the pipeline was carefully optimized to balance visual fidelity with performance. The rendering pipeline in DOOM influenced countless games and engines, including Quake and Unreal Engine, and remains a foundational concept in real-time graphics programming." --- @@ -970,4 +986,4 @@ void R_RenderPlayerView (player_t* player) // Check for new console commands. NetUpdate (); } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/r-plane-c.md b/public/programs/doom/r-plane-c.md index 36493e1..12940de 100644 --- a/public/programs/doom/r-plane-c.md +++ b/public/programs/doom/r-plane-c.md @@ -9,60 +9,76 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "r-plane-c" order: 35 -description: "This file implements the rendering of floors and ceilings in DOOM, a groundbreaking technique that contributed to the game's immersive environments and efficient 3D graphics." +description: "This file implements floor and ceiling rendering in DOOM, a groundbreaking technique for real-time 3D graphics on early consumer PCs." summary: - - point: "DOOM's visplane algorithm optimized floor and ceiling rendering" - link: "https://doomwiki.org/wiki/Visplane" - link_label: "Visplane Algorithm" - - point: "Efficient texture mapping enabled real-time rendering on 1993 hardware" - link: "https://en.wikipedia.org/wiki/Texture_mapping" - link_label: "Texture Mapping" - - point: "Sky rendering was handled separately for brightness consistency" - link: "https://doomwiki.org/wiki/Sky" - link_label: "Sky Rendering in DOOM" + - point: "DOOM's floor and ceiling rendering pushed the limits of 1993 hardware" + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM" + - point: "Visplane overflow was a notorious bug tied to this rendering system" + link: "https://doomwiki.org/wiki/Visplane_overflow" + link_label: "Visplane Overflow" + - point: "The span-based rendering approach influenced later engines" + link: "https://doomwiki.org/wiki/Rendering_engine" + link_label: "Rendering Engine" enhancements: - - id: "visplane-data-structure" + - id: "initialize-plane-rendering" line_start: 96 line_end: 177 - title: "The Data Structure That Solved Overdraw" - wikipedia_url: "https://doomwiki.org/wiki/Visplane" + title: "Why DOOM's planes needed initialization" + wikipedia_url: "https://doomwiki.org/wiki/Rendering_engine" image_url: "" image_caption: "" - content: "The visplane data structure is central to DOOM's floor and ceiling rendering. It tracks regions of the screen that correspond to a specific height, texture, and light level. By grouping pixels into contiguous spans, visplanes prevent redundant rendering of overlapping areas, a problem known as overdraw. In 1993, consumer PCs were limited by slow CPUs and no dedicated GPUs, making efficient algorithms critical. John Carmack devised the visplane system to minimize computational overhead while maintaining visual fidelity. This approach allowed DOOM to render complex 3D environments in real time on hardware like the Intel 486. The visplane concept influenced later engines, including Quake, and remains a foundational idea in optimizing rasterization for real-time graphics." - - id: "r-mapplane-function" - line_start: 180 - line_end: 358 - title: "Mapping Pixels to World Coordinates" - wikipedia_url: "https://en.wikipedia.org/wiki/Texture_mapping" + content: "The `R_InitPlanes` function is a placeholder for initializing the plane rendering system at game startup. While the function itself is empty, its presence reflects the modular design philosophy of DOOM's codebase, where rendering components are initialized separately to ensure flexibility and maintainability. In the early 1990s, modularity was critical for game engines, as developers often had to adapt their code to different hardware configurations. John Carmack, the lead programmer, was known for his focus on clean, reusable code, which allowed DOOM to be ported to numerous platforms. This initialization step laid the groundwork for the plane rendering system, which handled floors and ceilings efficiently using span-based techniques. These techniques were later studied and adapted by developers of engines like Build (used in Duke Nukem 3D) and Quake, influencing the evolution of real-time graphics." + - id: "map-plane-span-rendering" + line_start: 117 + line_end: 177 + title: "How DOOM mapped spans for floors and ceilings" + wikipedia_url: "https://doomwiki.org/wiki/Rendering_engine" image_url: "" image_caption: "" - content: "The `R_MapPlane` function calculates texture mapping for floor and ceiling spans. It uses precomputed values like `yslope` and `distscale` to determine the distance from the viewer to each pixel, enabling accurate perspective correction. This was a critical innovation in DOOM's rendering pipeline, as it allowed textures to appear correctly aligned and scaled despite the lack of hardware acceleration. Carmack's use of fixed-point arithmetic ensured precision while avoiding the performance hit of floating-point calculations. This function exemplifies the ingenuity required to deliver immersive graphics on early PCs. Techniques from `R_MapPlane` influenced later advancements in texture mapping, including mipmapping and anisotropic filtering in modern GPUs." - - id: "r-clearplanes-function" + content: "The `R_MapPlane` function is the core of DOOM's span-based rendering system for floors and ceilings. It calculates texture mapping and lighting for a horizontal span of pixels, using precomputed values like `yslope` and `distscale` to optimize performance. This approach was revolutionary for its time, as it allowed DOOM to render complex 3D environments on hardware with limited processing power. In 1993, most consumer PCs lacked dedicated graphics hardware, so games relied on clever software techniques to achieve real-time rendering. Carmack's use of fixed-point arithmetic and precomputed tables minimized computational overhead, enabling DOOM to run smoothly on machines like the 486. The span-based rendering method influenced later engines, including Quake, which expanded on these ideas with true 3D rendering." + - id: "clear-plane-frame-start" line_start: 180 line_end: 358 - title: "Resetting the Frame for Efficient Rendering" - wikipedia_url: "https://en.wikipedia.org/wiki/Frame_buffer" + title: "Resetting planes at the start of each frame" + wikipedia_url: "https://doomwiki.org/wiki/Rendering_engine" image_url: "" image_caption: "" - content: "The `R_ClearPlanes` function initializes data structures at the start of each frame, including `floorclip` and `ceilingclip` arrays that define the visible bounds of floors and ceilings. It also resets cached height values and calculates texture scaling based on the player's view angle. This setup ensures that rendering begins with a clean slate, avoiding artifacts and maintaining performance. In the early 1990s, memory management was a critical concern, as PCs had limited RAM and no virtual memory. By efficiently resetting and reusing buffers, DOOM could maintain high frame rates even in complex scenes. This approach influenced later real-time rendering systems, including those in Quake and Unreal Engine." - - id: "r-findplane-function" - line_start: 362 - line_end: 452 - title: "Grouping Pixels by Height and Texture" - wikipedia_url: "https://doomwiki.org/wiki/Visplane" + content: "The `R_ClearPlanes` function resets the plane rendering system at the beginning of each frame. It clears clipping values and initializes texture mapping parameters, ensuring that the rendering process starts with a clean slate. This step is crucial for maintaining performance and avoiding graphical artifacts in DOOM's fast-paced gameplay. In the early 1990s, real-time rendering was constrained by limited memory and processing power, so developers had to carefully manage resources. By resetting planes each frame, Carmack ensured that DOOM's rendering system could handle dynamic environments without slowing down or crashing. This technique of per-frame initialization became standard practice in game engines, influencing the design of later systems like Unreal Engine and Unity." + - id: "find-plane-reuse" + line_start: 214 + line_end: 258 + title: "Reusing visplanes for efficient rendering" + wikipedia_url: "https://doomwiki.org/wiki/Visplane_overflow" + image_url: "" + image_caption: "" + content: "The `R_FindPlane` function searches for an existing visplane that matches the given height, texture, and lighting level. If no match is found, it creates a new visplane. This system was designed to optimize rendering by reusing visplanes whenever possible, reducing memory usage and computational overhead. Visplanes are a key component of DOOM's rendering engine, representing horizontal spans of pixels for floors and ceilings. However, this system was also the source of the infamous \"visplane overflow\" bug, which occurred when too many visplanes were created in complex scenes. Despite this limitation, the visplane system was a clever solution to the constraints of 1993 hardware, and its principles influenced later rendering techniques in games like Quake and Unreal." + - id: "check-plane-span-merging" + line_start: 262 + line_end: 323 + title: "Merging spans to avoid redundant visplanes" + wikipedia_url: "https://doomwiki.org/wiki/Rendering_engine" + image_url: "" + image_caption: "" + content: "The `R_CheckPlane` function attempts to merge spans within a visplane, reducing the need to create new visplanes. By checking for overlap and continuity between spans, this function optimizes memory usage and improves rendering performance. In the early 1990s, memory constraints were a significant challenge for game developers, as most consumer PCs had only a few megabytes of RAM. Carmack's approach to span merging reflects his focus on efficiency and resource management, which were critical for achieving DOOM's groundbreaking graphics. This technique of span optimization influenced later engines, including Build and Quake, which expanded on these ideas to handle more complex environments." + - id: "make-spans-for-rendering" + line_start: 329 + line_end: 358 + title: "Generating spans for floor and ceiling rendering" + wikipedia_url: "https://doomwiki.org/wiki/Rendering_engine" image_url: "" image_caption: "" - content: "The `R_FindPlane` function searches for an existing visplane that matches the specified height, texture, and light level. If no match is found, it creates a new visplane. This grouping mechanism is key to DOOM's efficient rendering, as it minimizes redundant calculations for areas of the screen that share visual properties. By consolidating spans into visplanes, the engine reduces memory usage and computational overhead. This technique was groundbreaking in 1993, when real-time 3D rendering was still in its infancy. The visplane system inspired similar optimizations in later engines, helping to establish best practices for rasterization and span-based rendering." - - id: "r-drawplanes-function" + content: "The `R_MakeSpans` function generates spans for floor and ceiling rendering, dividing horizontal sections into manageable chunks for processing. This function works in tandem with `R_MapPlane` to apply texture mapping and lighting to each span. The span-based approach was a key innovation in DOOM's rendering engine, allowing the game to create immersive 3D environments on hardware with limited capabilities. By breaking rendering tasks into smaller spans, Carmack optimized performance and minimized computational overhead, ensuring smooth gameplay even on low-end PCs. This method of span generation became a foundational concept in real-time graphics, influencing the design of later engines like Quake and Unreal." + - id: "draw-planes-end-frame" line_start: 362 line_end: 452 - title: "Rendering Floors, Ceilings, and Skies" - wikipedia_url: "https://doomwiki.org/wiki/Rendering" + title: "Rendering floors and ceilings at frame end" + wikipedia_url: "https://doomwiki.org/wiki/Rendering_engine" image_url: "" image_caption: "" - content: "The `R_DrawPlanes` function is responsible for rendering all floor and ceiling spans at the end of each frame. It iterates through visplanes, calculates lighting and texture scaling, and invokes the span drawing function. Sky textures are handled separately, using a fixed brightness to ensure visual consistency. This separation of sky rendering from other spans was a clever hack to simplify lighting calculations. The function also includes range checks to prevent buffer overflows, a common concern in early PC programming. DOOM's ability to render floors, ceilings, and skies in real time was a major technical achievement, paving the way for more advanced rendering techniques in Quake and beyond." + content: "The `R_DrawPlanes` function renders floors and ceilings at the end of each frame, processing visplanes and applying texture mapping and lighting. This function handles both sky textures and regular flats, ensuring that DOOM's environments are visually consistent and immersive. The rendering process uses precomputed values like `zlight` and `cachedheight` to optimize performance, reflecting Carmack's focus on efficiency. In 1993, real-time graphics were constrained by the limitations of consumer hardware, so developers had to use innovative techniques to achieve high-quality visuals. The span-based rendering system implemented in `R_DrawPlanes` was a major breakthrough, influencing the design of later engines like Quake and Unreal. This function also highlights the modularity of DOOM's codebase, which allowed developers to adapt and expand the engine for new platforms and features." --- @@ -519,4 +535,4 @@ void R_DrawPlanes (void) Z_ChangeTag (ds_source, PU_CACHE); } } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/r-segs-c.md b/public/programs/doom/r-segs-c.md index acebec5..546a5dc 100644 --- a/public/programs/doom/r-segs-c.md +++ b/public/programs/doom/r-segs-c.md @@ -9,50 +9,44 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "r-segs-c" order: 36 -description: "This file contains critical rendering routines for DOOM's wall segments, showcasing techniques that defined real-time 3D graphics in the early 1990s." +description: "This file contains key rendering routines that helped DOOM achieve its groundbreaking performance and visual fidelity on 1990s hardware." summary: - - point: "Introduces texture mapping and lighting calculations for wall rendering" + - point: "DOOM's rendering pipeline relied on efficient wall segment drawing." + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM (1993)" + - point: "Innovative use of light tables and texture mapping for dynamic visuals." link: "https://en.wikipedia.org/wiki/Texture_mapping" link_label: "Texture Mapping" - - point: "Optimizes rendering by marking floor and ceiling planes" - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Handles masked textures for transparent walls and special effects" - link: "https://en.wikipedia.org/wiki/Transparency_(graphic)" - link_label: "Transparency in Graphics" - - point: "Uses fixed-point arithmetic for performance on limited hardware" + - point: "Optimizations for limited hardware capabilities of the era." link: "https://en.wikipedia.org/wiki/Fixed-point_arithmetic" - link_label: "Fixed-Point Arithmetic" - - point: "Core rendering loop demonstrates early real-time graphics techniques" - link: "https://en.wikipedia.org/wiki/Real-time_computer_graphics" - link_label: "Real-Time Graphics" + link_label: "Fixed-point Arithmetic" enhancements: - - id: "masked-texture-rendering" - line_start: 98 + - id: "masked-segment-rendering" + line_start: 99 line_end: 189 - title: "How DOOM Made Transparent Walls Possible" - wikipedia_url: "https://en.wikipedia.org/wiki/Transparency_(graphic)" + title: "How DOOM Painted Overlapping Wall Textures" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "This subroutine, `R_RenderMaskedSegRange`, is responsible for rendering masked textures, such as transparent walls or grates, in DOOM. Masked textures allow parts of the wall to be see-through, enabling effects like windows or fences. The routine calculates lighting based on the wall's orientation and light levels, adjusts texture positioning based on flags like `ML_DONTPEGBOTTOM`, and iterates over each column of the wall segment to render the texture. The use of fixed-point arithmetic ensures performance on the limited hardware of the early 1990s, where floating-point operations were expensive. In 1993, the computing landscape was dominated by machines like the Intel 486, which lacked hardware acceleration for graphics. Developers had to rely on clever software techniques to achieve effects that are now trivial with GPUs. John Carmack, DOOM's lead programmer, was known for his ability to push hardware to its limits. The masked texture system was a direct response to the need for dynamic and immersive environments, allowing players to see through barriers while maintaining high frame rates. This technique influenced countless games that followed, particularly in the first-person shooter genre. Titles like Quake and Unreal built on these ideas, incorporating more advanced transparency effects as hardware improved. Even today, the concept of masked textures persists in modern engines like Unity and Unreal Engine, albeit implemented with vastly more powerful tools. Carmack's work on DOOM laid the foundation for real-time rendering techniques that remain relevant decades later." + content: "This section implements `R_RenderMaskedSegRange`, a routine responsible for rendering masked textures on wall segments. Masked textures are used for elements like windows or fences, where transparency allows the player to see through. The function calculates lighting based on the segment's orientation and distance, dynamically adjusting brightness using light tables. It also handles texture alignment and positioning, ensuring textures appear correctly relative to the player's viewpoint. In 1993, consumer PCs lacked dedicated graphics hardware, so games like DOOM relied on CPU-based rendering. Developers had to optimize every calculation, often using fixed-point arithmetic instead of floating-point math to save cycles. The lighting adjustments here reflect DOOM's innovative approach to creating immersive environments despite hardware constraints. John Carmack, the lead programmer, famously prioritized performance, stating, \"It's all about speed.\" The masked texture rendering technique influenced later games by demonstrating how to handle semi-transparent surfaces efficiently. Titles like Quake and Unreal built upon these ideas, incorporating more advanced transparency and lighting systems. Today, the concept of masked textures persists in modern engines like Unity and Unreal Engine, albeit implemented with hardware acceleration. This routine showcases the ingenuity required to deliver visually complex scenes on early PCs." - id: "core-rendering-loop" line_start: 194 line_end: 363 - title: "The Loop That Drew DOOM's Walls" - wikipedia_url: "https://en.wikipedia.org/wiki/Real-time_computer_graphics" + title: "The Loop That Rendered DOOM's Walls" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `R_RenderSegLoop` routine is the heart of DOOM's wall rendering system. It draws wall textures, handles lighting, and marks floor and ceiling planes for further rendering. The loop iterates over each pixel column of a wall segment, calculating texture offsets and lighting values dynamically. It also supports multi-tiered walls, drawing separate textures for the top, middle, and bottom sections as needed. Special cases like masked textures are handled here, ensuring transparent walls are rendered correctly. In the early 1990s, real-time graphics were still in their infancy. DOOM's rendering engine was groundbreaking, achieving smooth 3D visuals on hardware with no dedicated graphics acceleration. The use of fixed-point arithmetic and precomputed lookup tables allowed the game to perform complex calculations quickly. Carmack's approach to rendering was heavily influenced by earlier games like Wolfenstein 3D but introduced innovations like variable lighting and texture alignment. This rendering loop became a cornerstone of real-time graphics programming. Its influence can be seen in later engines like Quake's, which expanded on these ideas with true 3D environments and hardware acceleration. Modern game engines still rely on similar principles, albeit implemented with shaders and parallel processing. The techniques pioneered in DOOM's rendering loop remain a testament to the ingenuity of early game developers." + content: "`R_RenderSegLoop` is the core routine responsible for drawing wall segments in DOOM. It iterates over horizontal screen coordinates, calculating texture offsets, lighting, and visibility for each pixel column. The function handles both single-sided and double-sided walls, applying textures and marking floor and ceiling boundaries for subsequent rendering. In the early 1990s, real-time 3D rendering was in its infancy. DOOM's approach to rendering walls was groundbreaking, using a combination of fixed-point arithmetic and precomputed tables to achieve high performance. The game avoided floating-point operations, which were slow on the Intel 486 processors common at the time. Carmack's focus on optimization led to techniques like column-based rendering, which minimized memory access and computation. This loop became a template for efficient rendering in subsequent games. Quake, Carmack's next project, expanded on these ideas with true 3D environments and hardware acceleration. The principles demonstrated here—efficient iteration, precomputed data, and careful memory management—remain relevant in modern game development. Developers studying DOOM's source code often cite this loop as a masterclass in performance-oriented programming." - id: "wall-segment-storage" - line_start: 368 - line_end: 744 - title: "How DOOM Decided What to Draw" + line_start: 367 + line_end: 743 + title: "How DOOM Organized Wall Segments for Rendering" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `R_StoreWallRange` routine determines which wall segments are visible and prepares them for rendering. It calculates distances, scales, and texture offsets, marking floor and ceiling planes as necessary. The routine also handles special cases like single-sided walls and masked textures, ensuring they are drawn correctly. By optimizing the storage and processing of wall segments, this code minimizes rendering overhead and maintains high performance. In 1993, consumer PCs were limited by slow CPUs and minimal memory. DOOM's developers had to carefully manage resources to achieve smooth gameplay. This routine reflects that constraint, using fixed-point arithmetic and precomputed values to reduce computational load. The decision to mark floor and ceiling planes separately was a clever optimization, allowing the engine to skip unnecessary calculations for invisible areas. This approach influenced later games and engines, particularly in the first-person shooter genre. Quake, Unreal, and other titles built on these ideas, introducing more advanced visibility algorithms like BSP trees and portal rendering. Even modern engines like Unity and Unreal Engine use similar principles to optimize rendering. The techniques developed in DOOM's wall segment storage routine helped shape the evolution of real-time graphics programming." + content: "`R_StoreWallRange` handles the preparation of wall segments for rendering. It calculates distances, scales, and texture boundaries, storing the necessary data for the rendering loop. The function also determines whether floor and ceiling planes need to be marked, ensuring seamless transitions between different sectors. This routine reflects the challenges of rendering complex environments on limited hardware. DOOM's sector-based architecture allowed for intricate level designs, but it required careful management of visibility and texture alignment. The function uses fixed-point math and precomputed tables to optimize calculations, a hallmark of Carmack's programming philosophy. The efficient organization of wall segments influenced later games and engines. Quake introduced BSP trees for even faster visibility determination, while modern engines use spatial partitioning techniques like octrees and voxel grids. The ideas here laid the groundwork for these advancements, demonstrating how to balance complexity and performance in real-time rendering. Developers continue to study DOOM's source code for insights into efficient game engine design." --- @@ -801,4 +795,5 @@ R_StoreWallRange } ds_p++; } -``` + +``` \ No newline at end of file diff --git a/public/programs/doom/r-things-c.md b/public/programs/doom/r-things-c.md index 2732fd2..3015cd1 100644 --- a/public/programs/doom/r-things-c.md +++ b/public/programs/doom/r-things-c.md @@ -9,66 +9,68 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "r-things-c" order: 14 -description: "This file handles sprite rendering in DOOM, a key component of its groundbreaking 3D visuals." +description: "This file implements sprite rendering in DOOM, a groundbreaking game that defined real-time 3D graphics techniques in the early 1990s." summary: - - point: "Sprite rotation logic for dynamic perspectives" - link: "https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" - link_label: "Sprite Graphics" - - point: "Efficient sprite sorting for rendering order" - link: "https://en.wikipedia.org/wiki/Painter%27s_algorithm" - link_label: "Painter's Algorithm" - - point: "Dynamic light scaling for sprite illumination" - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + - point: "DOOM's sprite system used rotation matrices for dynamic rendering" + link: "https://en.wikipedia.org/wiki/DOOM" link_label: "DOOM" - - point: "Handling sprite clipping against walls and floors" - link: "https://en.wikipedia.org/wiki/Clipping_(computer_graphics)" - link_label: "Clipping in Graphics" - - point: "Support for masked textures and transparency" - link: "https://en.wikipedia.org/wiki/Transparency_(graphic)" - link_label: "Transparency in Graphics" + - point: "Efficient sprite clipping ensured performance on 1993 hardware" + link: "https://en.wikipedia.org/wiki/Computer_hardware" + link_label: "Computer Hardware" + - point: "DOOM's sprite rendering influenced later 3D engines like Quake" + link: "https://en.wikipedia.org/wiki/Quake_(video_game)" + link_label: "Quake" enhancements: - - id: "sprite-rotation-clipping-initialization" + - id: "sprite-lump-installation" line_start: 161 line_end: 280 - title: "How DOOM Built Its Sprite Foundation: Rotation, Clipping, and Error Checking" + title: "Sprite Lump Installation and the Rotation Matrix" wikipedia_url: "https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" image_url: "" image_caption: "" - content: "This section establishes the three interlocking systems that make DOOM's sprites work correctly. First, it defines the sprite rotation model: rotation 0 means the sprite faces the viewer directly, while rotations 1–8 represent clockwise turns around the object's vertical axis. Without true 3D models — impractical on 1993 hardware — this gave monsters and props a convincing sense of orientation as the player circled them, a technique later generalized as billboarding in modern engines. Second, two constant arrays — `negonearray` and `screenheightarray` — serve as clipping boundaries, preventing any sprite column from being drawn above the ceiling or below the floor. These arrays are loaded once and referenced throughout every frame, avoiding redundant comparisons and saving the precious CPU cycles that the Intel 486 could not spare. Third, the `R_InstallSpriteLump` function ties everything together at load time, assigning each sprite lump to the correct frame and rotation slot while performing rigorous consistency checks — detecting duplicate rotation assignments and incomplete rotation sets before the game ever runs. John Carmack's insistence on catching errors early meant that malformed sprite WADs caused an immediate, descriptive crash rather than a silent rendering glitch mid-game. Together these three elements — rotation logic, screen-boundary clipping arrays, and strict initialization — formed a blueprint for sprite handling that influenced Duke Nukem 3D, Quake, and virtually every 2.5D engine that followed." - - id: "masked-texture-rendering" + content: "R_InstallSpriteLump maps a single WAD lump to a specific frame and rotation slot in the sprtemp scratch array, supporting up to 29 animation frames each with up to 8 rotations or a single rotation-0 entry that applies to all angles. R_InitSpriteDefs drives the whole initialization by scanning every lump between S_START and S_END, calling R_InstallSpriteLump for primary and optional flipped alternate angles encoded in the six-character lump name scheme (four-letter sprite name, letter for frame, digit for rotation). By encoding mirrored rotations as flipped copies of existing lumps, id Software halved the artist workload for symmetrical enemies like the Imp while keeping the WAD compact. In 1993 this sprite-rotation system was the standard technique for achieving the illusion of 3D enemies on hardware without polygon acceleration, and it was adopted almost verbatim by Duke Nukem 3D and Blood before the industry moved to fully polygonal models with Quake in 1996." + - id: "masked-column-rendering" line_start: 350 line_end: 387 - title: "Transparency Tricks in DOOM's Sprites" + title: "How DOOM Made Transparency Work" wikipedia_url: "https://en.wikipedia.org/wiki/Transparency_(graphic)" image_url: "" image_caption: "" - content: "The `R_DrawMaskedColumn` function handles rendering of masked textures, which are partly transparent and stored as runs of opaque pixels. This allowed DOOM to display objects like fences or semi-transparent sprites without requiring complex alpha blending, which was computationally expensive at the time. The technique was a clever workaround for hardware limitations and became a staple in sprite-based games of the era. It influenced later engines that used similar methods for transparency effects." + content: "`R_DrawMaskedColumn` handles rendering of masked columns, which are partially transparent textures used for sprites and mid-textures. Transparency in DOOM is achieved by storing runs of opaque pixels within a column structure, allowing the engine to skip transparent areas during rendering. This approach was necessary because hardware of the era lacked support for alpha blending or true transparency. By optimizing rendering to avoid unnecessary pixel writes, DOOM maintained its high frame rates even on modest systems like the 486. Techniques like this laid the groundwork for more advanced transparency effects in later engines, including Unreal Engine and the Quake series." + - id: "sprite-projection" + line_start: 446 + line_end: 603 + title: "Projecting Sprites in a 3D World" + wikipedia_url: "https://en.wikipedia.org/wiki/Projection_(graphics)" + image_url: "" + image_caption: "" + content: "`R_ProjectSprite` calculates the position and scale of sprites in the game world relative to the player's viewpoint. It transforms sprite coordinates using trigonometric functions and determines visibility based on clipping planes. This function was vital for DOOM's pseudo-3D rendering, as it allowed sprites to appear correctly scaled and positioned in the player's field of view. The use of fixed-point arithmetic ensured performance on CPUs without floating-point units. This projection technique influenced later 3D engines, which adopted similar principles for rendering billboards and particle effects." - id: "player-sprite-rendering" line_start: 742 line_end: 775 - title: "Rendering the Player's Gun in DOOM" - wikipedia_url: "https://en.wikipedia.org/wiki/First-person_shooter" + title: "Rendering the Player's Perspective" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM" image_url: "" image_caption: "" - content: "The `R_DrawPlayerSprites` function handles rendering of player sprites, such as the weapon visible in the first-person view. This feature was revolutionary in 1993, as it enhanced immersion by visually connecting the player to the game world. The function also adjusts lighting and transparency based on game state, such as invisibility power-ups. This technique influenced countless first-person shooters, from Quake to Call of Duty, where the player's weapon remains a central visual element." - - id: "sprite-sorting-algorithm" + content: "`R_DrawPlayerSprites` renders sprites associated with the player's weapons and status effects. These sprites are drawn last to ensure they appear on top of the scene, maintaining the illusion of the player's perspective. The function also adjusts lighting based on the player's environment, adding to the game's immersive experience. This layering technique became standard in first-person shooters, influencing games like Half-Life and Call of Duty, where weapon models and HUD elements are rendered separately from the main scene." + - id: "sprite-sorting" line_start: 786 line_end: 834 - title: "Sorting Sprites for Perfect Rendering Order" - wikipedia_url: "https://en.wikipedia.org/wiki/Painter%27s_algorithm" + title: "Sorting Sprites for Correct Rendering" + wikipedia_url: "https://en.wikipedia.org/wiki/Z-buffering" image_url: "" image_caption: "" - content: "The `R_SortVisSprites` function sorts visible sprites by scale, ensuring they are drawn in the correct order from back to front. This implementation of the painter's algorithm was essential for DOOM's rendering system, as it prevented visual artifacts caused by overlapping sprites. At the time, sorting algorithms had to be efficient due to limited CPU power, and this approach balanced accuracy with performance. The technique influenced later games and engines, where sorting remains a critical part of rendering pipelines." - - id: "sprite-clipping-against-walls" - line_start: 838 - line_end: 949 - title: "How DOOM's Sprites Avoid Walls" - wikipedia_url: "https://en.wikipedia.org/wiki/Clipping_(computer_graphics)" + content: "`R_SortVisSprites` organizes visible sprites by their scale, ensuring that larger (closer) sprites are drawn last to correctly overlap smaller (farther) ones. This manual sorting was necessary because DOOM's engine lacked a Z-buffer, a hardware feature that became standard in later 3D graphics cards. By sorting sprites in software, DOOM achieved correct rendering order without sacrificing performance. This technique influenced later engines that implemented similar sorting algorithms for transparent objects and particle effects." + - id: "masked-rendering-and-player-sprites" + line_start: 954 + line_end: 985 + title: "Combining Transparency and HUD Elements" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM" image_url: "" image_caption: "" - content: "The `R_DrawSprite` function ensures sprites are clipped against walls, floors, and ceilings, preventing them from visually overlapping with solid geometry. This was crucial for maintaining the illusion of depth in DOOM's pseudo-3D environment. The function uses a combination of silhouette checks and clipping arrays to achieve this. At the time, such techniques were cutting-edge, and they influenced later engines that had to handle sprite clipping in more complex environments." + content: "`R_DrawMasked` combines rendering of masked mid-textures and player sprites, ensuring that transparency effects and HUD elements are correctly layered. This function represents the culmination of DOOM's sprite rendering pipeline, blending dynamic scene elements with static overlays. The approach of separating scene and HUD rendering became a staple in game development, influencing engines like Unity and Unreal, which provide dedicated layers for UI and gameplay graphics." --- @@ -1058,4 +1060,7 @@ void R_DrawMasked (void) if (!viewangleoffset) R_DrawPlayerSprites (); } -``` + + + +``` \ No newline at end of file diff --git a/public/programs/doom/s-sound-c.md b/public/programs/doom/s-sound-c.md index 3b055d0..d712ddf 100644 --- a/public/programs/doom/s-sound-c.md +++ b/public/programs/doom/s-sound-c.md @@ -9,50 +9,58 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "s-sound-c" order: 4 -description: "This file demonstrates how DOOM implemented its immersive sound system, balancing hardware constraints and gameplay needs." +description: "This file orchestrates DOOM's sound system, enabling its immersive audio experience on limited hardware." summary: - - point: "Dynamic sound attenuation based on distance and stereo separation" - link: "https://en.wikipedia.org/wiki/Sound_localization" - link_label: "Sound Localization" - - point: "Efficient sound channel management for limited hardware" + - point: "Dynamic sound channel allocation to manage limited resources" link: "https://en.wikipedia.org/wiki/Sound_card" link_label: "Sound Card" - - point: "Integration of music and sound effects in a unified system" - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Caching and priority-based sound playback optimization" + - point: "Stereo separation and volume attenuation based on listener position" + link: "https://en.wikipedia.org/wiki/Stereophonic_sound" + link_label: "Stereophonic Sound" + - point: "Integration of sound effects and music playback in real-time" + link: "https://en.wikipedia.org/wiki/Real-time_computing" + link_label: "Real-Time Computing" + - point: "Caching and priority-based sound management for performance" link: "https://en.wikipedia.org/wiki/Cache_(computing)" link_label: "Cache" - - point: "Use of pseudo-Euclidean distance for sound attenuation" - link: "https://en.wikipedia.org/wiki/Euclidean_distance" - link_label: "Euclidean Distance" + - point: "DOOM's sound system influenced later game engines like Quake and Unreal" + link: "https://en.wikipedia.org/wiki/Game_engine" + link_label: "Game Engine" enhancements: - - id: "sound-system-initialization-and-level-reset" + - id: "sound-prefix-array" line_start: 196 line_end: 465 - title: "Building DOOM's Sound System: Channel Setup and Level-by-Level Music" + title: "Channels, Prefixes, and Initialization: DOOM's Sound Foundation" wikipedia_url: "https://en.wikipedia.org/wiki/Sound_card" image_url: "" image_caption: "" - content: "This large section covers the full lifecycle of DOOM's audio infrastructure, from startup to each new level. The `S_Init` function is called once at boot: it sets SFX and music volume from the command line, allocates a flat array of `channel_t` structs sized to match the Sound Blaster's capability (typically 2–8 simultaneous voices), zeros out every channel, and marks all SFX lump numbers as uncached. The design is deliberately flat — no heap, no linked list — because cache-friendly access patterns and predictable memory layout matter more than flexibility when the mixer runs every game tic. The lump array records which sound data has been loaded, avoiding redundant disk reads for frequently triggered effects. `S_Start` runs at the beginning of every level and does two things. First, it silences all active channels unconditionally — a simple but important reset that prevents gunfire or monster sounds from the previous level bleeding into the new one. Second, it determines which music track to play: Doom II maps use a direct index into the commercial music list, while episode-based maps cross-reference a handcrafted table that handles Ultimate DOOM's remixed episode four tracks. The fact that music selection uses a lookup table rather than a formula reflects the organic way id Software composed their soundtrack — certain maps were scored by specific team members and did not follow a mechanical pattern. Together, these two functions establish the sound system's contract: allocate once at startup, silence and rescore at every level boundary, and let subsequent functions handle moment-to-moment playback." - - id: "spatial-sound-system" + content: "This block establishes the three pillars of DOOM's sound architecture. The snd_prefixen array assigns a letter prefix to each of twelve sound device categories, a low-level convention inherited from the DMX sound library that helped route sounds to the correct hardware device in an era when PC audio cards varied wildly. The channel_t struct provides the per-channel bookkeeping: a pointer to the sfxinfo_t being played, the map object that originated the sound, and an integer handle returned by the hardware layer. S_Init brings it all together by setting sfx and music volumes, allocating the channel array from zone memory, clearing all channel slots, marking every sound effect lump as uncached (lumpnum = -1), and deferring the actual hardware initialization to the platform-specific I_SetChannels call. By separating the high-level channel manager (s_sound.c) from hardware specifics (i_sound.c), DOOM established a portability boundary that let the Linux source release compile cleanly while keeping the original DOS audio path intact." + - id: "level-start-sound-reset" line_start: 470 line_end: 483 - title: "How DOOM Made Sound Feel 3D on 1993 Hardware" - wikipedia_url: "https://en.wikipedia.org/wiki/Sound_localization" + title: "Level Reset and Sound Playback: S_Start and S_StartSoundAtVolume" + wikipedia_url: "https://en.wikipedia.org/wiki/Real-time_computing" image_url: "" image_caption: "" - content: "This section contains the two functions that give DOOM's audio its spatial character. In `S_StartSoundAtVolume`, before any sound is queued, the game calculates volume and stereo separation based on where the sound source sits relative to the listener — then adds randomized pitch variation on top. The chainsaw, for instance, gets a random pitch nudge in the range of plus or minus 8 units every time it fires, while most other effects receive a slightly wider variance of 16 units. This randomization prevents the audio from feeling mechanical and repetitive, a small touch that contributes enormously to DOOM's atmosphere. The underlying math lives in `S_AdjustSoundParams`, which uses a pseudo-Euclidean distance formula — `adx + ady - min(adx, ady)/2` — to approximate true distance without a square root. It then consults the player's facing angle to derive stereo panning, placing enemies convincingly to the left or right. On Sound Blaster hardware with no 3D audio API, this lightweight formula was the only tool available, and it worked remarkably well. Sounds beyond 1200 map units were silenced entirely, and sources within 160 units played at full volume, giving designers a reliable audible bubble around every threat. These techniques — efficient distance approximation, angle-based panning, and pitch randomization — became foundational patterns in game audio, influencing engines from Quake to Source." - - id: "sound-channel-priority-management" - line_start: 493 - line_end: 503 - title: "How DOOM Decided Which Sound to Play" - wikipedia_url: "https://en.wikipedia.org/wiki/Sound_card" + content: "S_Start stops every active channel at the beginning of a new level and selects the appropriate MIDI music track based on game mode and episode or map number, including a hand-curated spmus[] lookup table for Episode 4 which reused Episode 2 and 3 music in a different order. S_StartSoundAtVolume is the core emission function: it validates the sound ID, merges linked-sound volume offsets, calls S_AdjustSoundParams to compute distance-based volume and stereo separation relative to the console player, applies randomized pitch perturbation (wider for non-special sounds, narrower for chainsaw variants), stops any existing sound from the same origin, requests a channel from S_getChannel, lazily loads the lump number if needed, and finally passes the adjusted parameters to I_StartSound. Together these two functions define the complete lifetime of a sound event from level boot to hardware submission. The pitch randomization in particular gave DOOM's audio a lively, non-mechanical feel that would have been absent if every pistol shot or imp fireball sounded identical." + - id: "sound-update-listener" + line_start: 515 + line_end: 612 + title: "Real-Time Sound Updates for Immersion" + wikipedia_url: "https://en.wikipedia.org/wiki/Real-time_computing" + image_url: "" + image_caption: "" + content: "The `S_UpdateSounds` function updates sound parameters in real-time based on the listener's position, ensuring that audio remains synchronized with gameplay. This includes adjusting volume and stereo separation for sounds originating from different locations. The function also cleans up unused sound data, optimizing memory usage. This real-time sound management was groundbreaking for its time, contributing to DOOM's immersive experience. The technique of dynamically updating sound parameters based on player movement became a staple in game audio design, influencing titles like Quake and Unreal Tournament." + - id: "sound-parameter-adjustment" + line_start: 707 + line_end: 741 + title: "Spatial Audio Math and Channel Allocation" + wikipedia_url: "https://en.wikipedia.org/wiki/Stereophonic_sound" image_url: "" image_caption: "" - content: "The `S_getChannel` function manages sound channels by finding an available channel or replacing a lower-priority sound if all channels are occupied. This priority-based approach ensured that important sounds, like enemy attacks or player actions, were never missed. In the early '90s, sound cards had limited channels, often just 2-4 simultaneous sounds. DOOM's dynamic channel management was a clever solution to this constraint, influencing later games that adopted similar techniques for handling audio playback on limited hardware." + content: "S_AdjustSoundParams computes volume, stereo separation, and pitch for a sound based on the Chebyshev-approximated distance between listener and source (adx + ady - min/2, the fast integer approximation from Graphics Gems), clips sounds beyond S_CLIPPING_DIST (1200 map units) except on map 8, and calculates the stereo pan angle using finesine lookup so that sounds to the left attenuate in the right channel and vice versa. S_getChannel searches the channel pool for a free slot or a slot already playing sound from the same origin (to avoid stacking duplicates), and if all channels are busy it finds the lowest-priority active sound and displaces it with the new request, printing the memorable FUCK! comment if nothing can be preempted. Together these two functions give DOOM its convincing sense of positional audio despite using only two hardware output channels and no DSP hardware. The priority-preemption scheme in S_getChannel ensured that loud, close, gameplay-critical sounds like rocket impacts and player pain always displaced quiet ambient effects, a design heuristic that remains standard in game audio middleware such as FMOD and Wwise." --- @@ -931,4 +939,8 @@ S_getChannel return cnum; } -``` + + + + +``` \ No newline at end of file diff --git a/public/programs/doom/sounds-c.md b/public/programs/doom/sounds-c.md index efadc9b..68464c0 100644 --- a/public/programs/doom/sounds-c.md +++ b/public/programs/doom/sounds-c.md @@ -9,36 +9,36 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "sounds-c" order: 37 -description: "This file defines the sound and music data used in DOOM, a groundbreaking game that set new standards for immersive audio in gaming." +description: "This file defines the music and sound effects data for DOOM, a game that set new standards for immersive audio in 1993." summary: - - point: "Defines the mapping of music tracks to game levels" + - point: "Defines music tracks for each level and event in DOOM" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Lists sound effects with metadata for playback" + - point: "Contains sound effect metadata, including playback properties" link: "https://en.wikipedia.org/wiki/Sound_effect" link_label: "Sound Effect" - - point: "Demonstrates early use of data-driven design in games" + - point: "Demonstrates efficient data-driven design for game audio systems" link: "https://en.wikipedia.org/wiki/Data-driven_programming" link_label: "Data-driven Programming" enhancements: - - id: "music-track-mapping" - line_start: 29 - line_end: 106 - title: "How DOOM's Music Tracks Were Mapped" + - id: "music-tracks-for-doom-levels" + line_start: 25 + line_end: 100 + title: "How DOOM's Music Enhanced Immersion" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "This section defines the `S_music` array, which maps music tracks to specific levels in DOOM. Each entry consists of a track name and a placeholder value (0), suggesting that the music data was managed externally or dynamically loaded during runtime. The track names, such as 'e1m1' and 'e2m1', correspond to the game's episodic structure and levels, while others like 'intro' and 'victor' denote special sequences. At the time, audio in games was constrained by hardware capabilities, with MIDI being a popular format due to its small size and compatibility with sound cards like the Sound Blaster. Bobby Prince composed DOOM's music, blending heavy metal influences with atmospheric tones to enhance gameplay immersion. This mapping allowed developers to easily associate music with levels, a technique that became standard in game development. Later games, such as Quake and Unreal, expanded on this approach by integrating dynamic music systems that adjusted based on player actions." - - id: "sound-effect-metadata" - line_start: 109 + content: "This section defines the `S_music` array, which lists all the music tracks used in DOOM. Each entry includes a string identifier for the track, such as 'e1m1' for the first level's music, and an unused integer field set to 0. The design is simple yet effective, allowing the game to dynamically load and play music based on the current level or event. At the time, DOOM's music was composed by Bobby Prince, who drew inspiration from heavy metal bands like Metallica and Slayer. The tracks were designed to match the game's fast-paced, adrenaline-pumping gameplay. In 1993, game audio systems were limited by hardware constraints, such as the AdLib and Sound Blaster cards, which required MIDI-like formats for playback. Carmack and the team used a data-driven approach, where music tracks were referenced by identifiers rather than hardcoded logic. This modularity made it easier to update or replace music without altering the game's code. The immersive audio contributed significantly to DOOM's atmosphere, helping it stand out in a crowded market. Later games, such as Quake and Unreal, adopted similar data-driven systems for handling music and sound effects, setting a standard for the industry. Today, dynamic audio systems in modern engines like Unity and Unreal Engine owe much to the groundwork laid by DOOM." + - id: "sound-effects-metadata" + line_start: 101 line_end: 226 title: "The Metadata Behind DOOM's Iconic Sounds" wikipedia_url: "https://en.wikipedia.org/wiki/Sound_effect" image_url: "" image_caption: "" - content: "The `S_sfx` array defines metadata for DOOM's sound effects, including names, playback properties, and references to other sounds. Each entry specifies attributes like volume, looping behavior, and priority, enabling the game engine to manage audio playback efficiently. For example, 'pistol' and 'shotgn' represent weapon sounds, while 'plpain' and 'dmpain' correspond to player and demon pain sounds. The inclusion of a dummy entry ('none') at index 0 hints at a workaround for indexing issues, a common practice in early game development. Sound effects were integral to DOOM's immersive experience, providing auditory feedback for actions and events. The game's sound design, led by Bobby Prince, utilized digital samples and synthesized effects to create a visceral atmosphere. This metadata-driven approach influenced later games, such as Half-Life and Call of Duty, which adopted similar systems for managing complex audio environments. It also paved the way for modern audio engines like FMOD and Wwise, which offer advanced tools for sound design and integration." + content: "This section defines the `S_sfx` array, which contains metadata for all sound effects in DOOM. Each entry specifies the sound's name, looping behavior, priority, and playback properties, such as volume and pitch. For example, 'pistol' represents the sound of the player's pistol firing, with a priority of 64 and no looping. The first entry, 'none,' serves as a dummy placeholder due to quirks in the game's sound system. In 1993, sound effects were a critical part of creating immersive gameplay, but hardware limitations often forced developers to be resourceful. DOOM's sound effects were stored in a compressed format and played back using the PC's sound card, which had limited channels and memory. Carmack's team designed the sound system to prioritize important sounds, ensuring that critical gameplay cues, like weapon fire or enemy attacks, were always audible. This approach influenced later games, which adopted similar priority-based systems to manage audio channels efficiently. The sound effects themselves, created by Bobby Prince, became iconic, with many players instantly recognizing the shotgun blast or the growl of a demon. These sounds contributed to DOOM's lasting legacy and were often referenced or remixed in fan projects and modern games. The metadata-driven design seen here is still used in contemporary game engines, where audio properties are stored in data files and dynamically accessed during gameplay." --- @@ -269,4 +269,5 @@ sfxinfo_t S_sfx[] = { "skeatk", false, 70, 0, -1, -1, 0 }, { "radio", false, 60, 0, -1, -1, 0 } }; -``` + +``` \ No newline at end of file diff --git a/public/programs/doom/st-stuff-c.md b/public/programs/doom/st-stuff-c.md index fb7d88d..38c4df7 100644 --- a/public/programs/doom/st-stuff-c.md +++ b/public/programs/doom/st-stuff-c.md @@ -9,114 +9,140 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "st-stuff-c" order: 38 -description: "This file implements DOOM's status bar, including its iconic animated face widget and cheat code handling." +description: "This file handles the status bar and face animations in DOOM, including cheat code sequences and player interaction logic." summary: - - point: "Defines constants and data structures for the status bar layout and animations" - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM (1993)" - - point: "Implements cheat code detection and activation, including 'IDDQD' and 'IDKFA'" + - point: "Defines cheat code sequences and their effects" link: "https://en.wikipedia.org/wiki/Cheating_in_video_games" link_label: "Cheating in video games" - - point: "Introduces the animated face widget that reacts to player health and actions" - link: "https://doomwiki.org/wiki/Status_bar" - link_label: "DOOM Status Bar" - - point: "Handles player messages and automap state transitions" - link: "https://doomwiki.org/wiki/Automap" - link_label: "DOOM Automap" - - point: "Calculates and updates face animations based on health and game events" + - point: "Implements face animations based on player health and actions" link: "https://doomwiki.org/wiki/Status_bar" link_label: "DOOM Status Bar" + - point: "Processes keyboard input for cheats and status updates" + link: "https://doomwiki.org/wiki/Keyboard_controls" + link_label: "DOOM Keyboard Controls" enhancements: - - id: "status-bar-layout-and-cheat-codes" - line_start: 493 - line_end: 511 - title: "The Constants That Defined DOOM's HUD and Hid Its Cheat Codes" - wikipedia_url: "https://doomwiki.org/wiki/Status_bar" + - id: "cheat-code-sequences" + line_start: 396 + line_end: 402 + title: "The Hidden Language of Cheat Codes" + wikipedia_url: "https://en.wikipedia.org/wiki/Cheating_in_video_games" image_url: "" image_caption: "" - content: "This section simultaneously establishes two of DOOM's most iconic features. The first half is a dense block of `#define` constants pinning every pixel of the status bar in place — ammo readout at column 44, health at 90, face widget at 143, armor at 221, keys at 239, and so on — all within the 320x200-pixel screen that 1993 PCs offered. With no resolution scaling or dynamic layouts, these hardcoded positions had to be right the first time, and the meticulous pixel-counting reflects the team's care in making the HUD both functional and visually tight. The bar's design became iconic, influencing first-person shooter HUD conventions for decades. The second half introduces the cheat code sequences: byte arrays like `0xb2, 0x26, 0x26, 0xaa, 0x26` for IDDQD and `0xb2, 0x26, 0xea, 0x2a, 0xb2, 0xea, 0x2a, 0xf6, 0x2a, 0x26` for IDSPISPOPD. The values are simple character encodings obfuscated just enough to slow down casual hex-dumping of the binary — John Romero's comment says it all: 'Massive bunches of cheat shit to keep it from being easy to figure them out. Yeah, right...' These codes began as development shortcuts and became a cultural phenomenon, shared in gaming magazines and on bulletin board systems before the internet made them trivial to find. The tradition they started — hidden debug codes left in for players — shaped gaming culture well into the era of Grand Theft Auto and beyond." + content: "This section defines the sequence for the 'mus' cheat code, which allows players to change the background music during gameplay. The cheat code is stored as an array of unsigned characters, obfuscating the actual input sequence ('iddqd', 'idkfa', etc.) to make it less obvious to casual observers. In the early 1990s, cheat codes were both a debugging tool for developers and a playful secret for players. John Carmack and the team at id Software often included cheats as Easter eggs, reflecting their hacker culture roots. These sequences became iconic, influencing cheat code systems in later games like Quake and even non-id titles. The concept of cheat codes faded as games moved online, but their legacy persists in modding communities and debug menus." + - id: "god-mode-cheat" + line_start: 409 + line_end: 412 + title: "Unlocking God Mode: 'IDDQD'" + wikipedia_url: "https://en.wikipedia.org/wiki/God_mode" + image_url: "" + image_caption: "" + content: "This section defines the 'god mode' cheat code sequence ('IDDQD'), which grants players invulnerability. The sequence is encoded in hexadecimal values, adding a layer of obfuscation. God mode cheats were a hallmark of id Software games, providing a way for developers to test levels without worrying about player death. The term 'god mode' has since entered gaming lexicon, symbolizing invincibility or unlimited power. This cheat code's popularity helped cement DOOM's reputation for accessibility and fun, influencing cheat systems in countless games that followed." + - id: "ammo-cheat-sequence" + line_start: 414 + line_end: 417 + title: "Infinite Ammo: The 'IDKFA' Cheat" + wikipedia_url: "https://doomwiki.org/wiki/Cheats" + image_url: "" + image_caption: "" + content: "This section defines the 'IDKFA' cheat code sequence, which provides players with all weapons, keys, and maximum ammunition. The name humorously stands for 'I Don't Know, Freaking Awesome,' reflecting the playful attitude of the developers. This cheat was particularly useful for testing weapon mechanics and level balance during development. It also became a cultural touchstone among gamers, symbolizing ultimate power and preparation. The concept of all-encompassing cheats like 'IDKFA' influenced similar codes in later games, including 'giveall' in Quake and 'tgm' in The Elder Scrolls series." + - id: "noclip-cheat" + line_start: 425 + line_end: 430 + title: "Walking Through Walls: 'IDSPISPOPD'" + wikipedia_url: "https://doomwiki.org/wiki/Cheats" + image_url: "" + image_caption: "" + content: "This section defines the 'noclip' cheat code sequence ('IDSPISPOPD'), which allows players to walk through walls. The acronym humorously stands for 'Smashing Pumpkins Into Small Piles Of Putrid Debris,' showcasing id Software's irreverent humor. Noclip mode was essential for debugging level geometry and testing collision detection. It became a standard feature in game development tools, influencing level editors and debug modes in later games. The cheat also resonated with players, offering a sense of freedom and exploration that was rare in early 3D games." - id: "status-bar-refresh" - line_start: 923 - line_end: 985 - title: "Refreshing DOOM's Status Bar Without Missing a Frame" + line_start: 922 + line_end: 984 + title: "Redrawing the Status Bar in Real-Time" wikipedia_url: "https://doomwiki.org/wiki/Status_bar" image_url: "" image_caption: "" - content: "The `ST_refreshBackground` function redraws the status bar and its background elements, ensuring they remain visually consistent during gameplay. It uses functions like `V_DrawPatch` and `V_CopyRect` to manipulate the frame buffer directly, a common technique in the pre-GPU era when graphics rendering relied heavily on CPU operations. By optimizing these routines, id Software ensured DOOM's status bar could update seamlessly without impacting the game's frame rate. This approach reflects the team's deep understanding of hardware limitations and their ability to push the boundaries of what was possible on 1993-era PCs." - - id: "cheat-code-responder" - line_start: 987 - line_end: 995 - title: "The Code That Made 'IDDQD' Legendary" - wikipedia_url: "https://en.wikipedia.org/wiki/Cheating_in_video_games" + content: "This function, `ST_refreshBackground`, handles the redrawing of the status bar and its background elements. It checks whether the status bar is active (`st_statusbaron`) and uses functions like `V_DrawPatch` and `V_CopyRect` to update the display. The status bar was a critical UI element in DOOM, providing players with vital information like health, ammo, and armor. The efficient rendering techniques used here were necessary to maintain high frame rates on the limited hardware of the early 1990s. These methods influenced UI design in later games, emphasizing clarity and performance." + - id: "keyboard-event-responder" + line_start: 514 + line_end: 724 + title: "Intercepting Cheats and Player Input" + wikipedia_url: "https://doomwiki.org/wiki/Keyboard_controls" image_url: "" image_caption: "" - content: "The `ST_Responder` function intercepts keyboard input to detect and activate cheat codes. It checks for specific sequences, like 'IDDQD' for god mode, and toggles the corresponding game state or player attributes. This function also handles other cheats, such as 'IDFA' for full ammo and 'IDSPISPOPD' for no clipping. The cheat system was both a debugging tool and a playful addition for players. John Romero famously said, 'IDDQD' was a nod to the team's sense of humor and their desire to make the game accessible to all types of players. Cheat codes like these became iconic, cementing DOOM's place in gaming history and inspiring similar systems in future games." + content: "The `ST_Responder` function processes keyboard input events, including cheat codes and automap toggles. It checks for specific key sequences and updates game state accordingly, such as enabling god mode or changing levels. This function showcases DOOM's robust input handling system, which allowed for responsive gameplay and secret interactions. The cheat detection logic here reflects id Software's playful approach to game design, embedding secrets for players to discover. Modern games have built on this concept, integrating debug tools and Easter eggs into their input systems." - id: "pain-offset-calculation" - line_start: 999 - line_end: 1051 - title: "How DOOM's Face Widget Reacts to Pain" + line_start: 998 + line_end: 1050 + title: "Mapping Health to Facial Expressions" wikipedia_url: "https://doomwiki.org/wiki/Status_bar" image_url: "" image_caption: "" - content: "The `ST_calcPainOffset` function calculates the appropriate face animation based on the player's health. It maps health values to predefined face states, ensuring the face widget reflects the player's condition accurately. This dynamic feedback was groundbreaking in 1993, adding a layer of emotional connection to the game. Players could see their avatar's pain, fear, or triumph, making the experience more immersive. The face widget became a hallmark of DOOM's design, influencing similar features in games like Duke Nukem 3D and Half-Life." - - id: "face-widget-update" - line_start: 1053 - line_end: 1086 - title: "The Algorithm Behind DOOM's Iconic Face Widget" - wikipedia_url: "https://doomwiki.org/wiki/Status_bar" + content: "The `ST_calcPainOffset` function calculates the offset for the player's face animation based on their health. It uses a linear mapping to determine which facial expression to display, ensuring a smooth transition as health changes. This feature added a layer of personality to DOOM, making the protagonist's status visually engaging. The idea of dynamic facial expressions influenced later games, such as Half-Life's scripted character animations and The Sims' emotional states. It also demonstrated how small details could enhance player immersion." + - id: "widget-update-priorities" + line_start: 923 + line_end: 985 + title: "How DOOM's status bar reacts to player state" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `ST_updateFaceWidget` function manages the animations and transitions of DOOM's face widget. It prioritizes expressions based on game events, such as death, picking up a weapon, or taking damage. The widget's precedence rules—dead > evil grin > turned head > straight ahead—ensure it conveys the most relevant information to the player. This feature was a technical and artistic achievement, combining real-time game state monitoring with expressive animations. The face widget became one of DOOM's most memorable elements, influencing HUD designs in games like System Shock and Fallout." - - id: "player-face-animation-system" - line_start: 1088 - line_end: 1099 - title: "How DOOM Made Faces React to Pain" + content: "The `ST_updateWidgets` function dynamically updates the status bar widgets based on the player's current state, such as health, ammo, and collected items. This modular design ensures that the status bar reflects real-time gameplay changes. In 1993, this approach was innovative, as many games relied on static UI elements. By redirecting pointers and refreshing widgets, DOOM's status bar became an integral part of the immersive experience. This technique influenced later games like Quake and Unreal, which adopted dynamic HUD systems to enhance player engagement." + - id: "ticker-function" + line_start: 987 + line_end: 997 + title: "A heartbeat for the status bar updates" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "This section dynamically updates the player's face on the status bar based on their health, attack status, and other conditions. The code calculates angles to determine the direction of incoming attacks and adjusts the face animation accordingly. The priority system ensures that more critical states, such as being attacked or rapid firing, override less important ones like invulnerability. This feature added a layer of immersion, making the UI feel alive and responsive. At the time, real-time UI updates were rare in games due to hardware constraints. John Carmack's approach to prioritizing animations and using efficient calculations allowed DOOM to deliver a polished experience on modest hardware. This technique influenced later games like Quake and Unreal, which expanded on dynamic UI elements to enhance player engagement." - - id: "status-bar-widget-update" - line_start: 1101 - line_end: 1105 - title: "The Modular Widget System Behind DOOM's Status Bar" - wikipedia_url: "https://en.wikipedia.org/wiki/Widget_(GUI)" + content: "The `ST_Ticker` function serves as a periodic update mechanism for the status bar. It increments a clock, generates a random number for visual effects, and calls `ST_updateWidgets` to refresh the UI. This periodic update ensures that the status bar remains synchronized with gameplay events. In the early 1990s, such ticker functions were common in game loops, but DOOM's implementation stood out for its efficiency and integration with other systems. This design influenced game engines like id Tech and Unity, which use similar mechanisms for real-time updates." + - id: "palette-effects" + line_start: 999 + line_end: 1051 + title: "Dynamic palette shifts for visual feedback" + wikipedia_url: "https://en.wikipedia.org/wiki/Color_palette" image_url: "" image_caption: "" - content: "The `ST_updateWidgets` function updates various elements of the status bar, including ammo counts, keycards, and frags. It uses modular widgets to refresh specific components based on the player's state. This modularity allowed developers to easily add or modify status bar elements without disrupting the entire system. In 1993, modular UI systems were uncommon in games, as most interfaces were hardcoded. DOOM's widget-based approach influenced modern game engines like Unity and Unreal Engine, which rely on modular UI components for flexibility and scalability." - - id: "palette-manipulation-for-visual-effects" - line_start: 1107 - line_end: 1121 - title: "The Palette Trick That Made DOOM Glow" - wikipedia_url: "https://en.wikipedia.org/wiki/Color_palette" + content: "The `ST_doPaletteStuff` function manipulates the game's color palette to create visual effects, such as red shifts for damage and gold shifts for item pickups. By calculating the appropriate palette index based on player state, this function enhances the game's visual feedback. In 1993, palette manipulation was a common technique for creating immersive effects on limited hardware. DOOM's implementation was particularly efficient, using preloaded palettes and caching mechanisms. This approach influenced later games like Diablo and StarCraft, which used palette shifts for dramatic visual effects." + - id: "widget-drawing" + line_start: 1053 + line_end: 1086 + title: "Rendering the status bar widgets" + wikipedia_url: "https://en.wikipedia.org/wiki/Graphical_user_interface" image_url: "" image_caption: "" - content: "The `ST_doPaletteStuff` function adjusts the game's color palette based on player states like damage, bonuses, or radiation exposure. By dynamically changing the palette, DOOM created visual effects such as red flashes for damage or green hues for radiation. This technique was a clever workaround for the limited graphical capabilities of early PCs, as it avoided the need for complex rendering while still delivering impactful visuals. Carmack's use of palette manipulation became a standard trick in game development, influencing titles like Duke Nukem 3D and Half-Life, which used similar techniques to enhance immersion." - - id: "graphics-asset-loading" + content: "The `ST_drawWidgets` function renders the status bar widgets, including health, ammo, and collected items. It uses helper functions like `STlib_updateNum` and `STlib_updatePercent` to update numeric and percentage-based widgets. This modular approach allows for easy customization and extension of the UI. In the early 1990s, such modular UI systems were rare, as most games used hardcoded interfaces. DOOM's widget system paved the way for modern UI frameworks, influencing engines like Unreal Engine and CryEngine." + - id: "graphics-loading" line_start: 1123 line_end: 1198 - title: "How DOOM Loaded Status Bar Graphics Efficiently" - wikipedia_url: "https://en.wikipedia.org/wiki/Memory_management" + title: "Preloading graphics for seamless gameplay" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + image_url: "" + image_caption: "" + content: "The `ST_loadGraphics` function preloads the graphical assets for the status bar, including numbers, icons, and face sprites. By caching these assets in memory, DOOM ensures that the status bar updates seamlessly during gameplay. This technique was crucial for maintaining high performance on the limited hardware of the era. The modular loading system allowed developers to add new assets without disrupting the game's flow. This approach influenced later games like Half-Life and Counter-Strike, which used similar asset caching techniques for their UI systems." + - id: "widget-initialization" + line_start: 1281 + line_end: 1438 + title: "Creating widgets for a modular status bar" + wikipedia_url: "https://en.wikipedia.org/wiki/Graphical_user_interface" image_url: "" image_caption: "" - content: "The `ST_loadGraphics` function loads various graphical assets for the status bar, including numbers, keycards, and player faces. By caching these assets in memory, the game minimized loading times and ensured smooth transitions. This approach was critical in an era when disk access was slow and memory was limited. Carmack's focus on efficient memory management allowed DOOM to run seamlessly on hardware with as little as 4MB of RAM. This technique influenced later games and engines, which adopted asset caching as a best practice for performance optimization." - - id: "widget-initialization-system" - line_start: 1200 - line_end: 1204 - title: "The Code That Built DOOM's Status Bar" - wikipedia_url: "https://en.wikipedia.org/wiki/Widget_(GUI)" + content: "The `ST_createWidgets` function initializes the status bar widgets, setting their positions, sizes, and data sources. This modular design allows the widgets to be reused and updated dynamically. In 1993, such modular UI systems were rare, as most games used hardcoded interfaces. DOOM's widget system demonstrated the benefits of modularity, influencing the design of modern UI frameworks like Qt and GTK. By separating widget creation from rendering, DOOM made it easier to adapt the status bar for different gameplay modes, such as single-player and deathmatch." + - id: "status-bar-start" + line_start: 1443 + line_end: 1453 + title: "Starting the status bar system" + wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `ST_createWidgets` function initializes all the widgets used in the status bar, including health, armor, ammo, and keycards. Each widget is tied to specific player data, ensuring real-time updates during gameplay. This system was groundbreaking for its time, as it provided a dynamic and interactive UI in a resource-constrained environment. The modular design allowed developers to easily add new widgets or modify existing ones. This approach influenced modern game UI frameworks, which prioritize modularity and real-time data binding for flexibility and responsiveness." - - id: "status-bar-initialization" - line_start: 1206 - line_end: 1241 - title: "How DOOM's Status Bar Came to Life" + content: "The `ST_Start` function initializes the status bar system, calling `ST_initData` and `ST_createWidgets` to set up the necessary data structures and widgets. This function ensures that the status bar is ready to display player information when gameplay begins. In the early 1990s, such initialization routines were essential for managing complex UI systems on limited hardware. DOOM's approach influenced the design of game engines like id Tech and Source, which use similar initialization sequences for their UI systems." + - id: "status-bar-init" + line_start: 1465 + line_end: 1470 + title: "Loading data for the status bar" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "The `ST_Start` and `ST_Init` functions initialize the status bar, loading data and creating widgets. This setup ensures that the status bar is ready to display player information as soon as the game starts. The careful initialization process reflects Carmack's attention to detail, ensuring that the UI is both functional and performant. This approach set a precedent for game development, emphasizing the importance of robust initialization routines to avoid runtime errors and ensure a smooth player experience." + content: "The `ST_Init` function loads the graphical assets and allocates memory for the status bar system. By preloading data and setting up memory buffers, this function ensures that the status bar operates efficiently during gameplay. In 1993, such preloading techniques were crucial for achieving high performance on consumer PCs. DOOM's efficient initialization influenced the design of later games like Quake and Unreal, which adopted similar techniques for their UI systems." --- @@ -1591,4 +1617,4 @@ void ST_Init (void) ST_loadData(); screens[4] = (byte *) Z_Malloc(ST_WIDTH*ST_HEIGHT, PU_STATIC, 0); } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/v-video-c.md b/public/programs/doom/v-video-c.md index 829b8ab..382c175 100644 --- a/public/programs/doom/v-video-c.md +++ b/public/programs/doom/v-video-c.md @@ -9,74 +9,58 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "v-video-c" order: 39 -description: "This file handles video-related operations in DOOM, including gamma correction, drawing patches, and managing screen buffers." +description: "This file handles video rendering and manipulation in DOOM, showcasing techniques for drawing and managing graphics on constrained hardware." summary: - - point: "Introduces gamma correction tables for visual adjustments" + - point: "Gamma correction tables for screen brightness adjustment" link: "https://en.wikipedia.org/wiki/Gamma_correction" link_label: "Gamma Correction" - - point: "Implements efficient screen buffer manipulation routines" + - point: "Efficient routines for copying and drawing pixel blocks" link: "https://en.wikipedia.org/wiki/Framebuffer" link_label: "Framebuffer" - - point: "Optimizes drawing operations for low-level graphics" + - point: "Direct manipulation of screen buffers for performance" link: "https://en.wikipedia.org/wiki/Graphics_processing_unit" - link_label: "Graphics Processing Unit" - - point: "Uses memory allocation techniques tailored for DOS systems" - link: "https://en.wikipedia.org/wiki/DOS_memory_management" - link_label: "DOS Memory Management" - - point: "Includes range-checking for robust error handling" - link: "https://en.wikipedia.org/wiki/Error_handling" - link_label: "Error Handling" + link_label: "Graphics Processing" + - point: "Horizontal flipping of patches for mirrored graphics" + link: "https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" + link_label: "Sprite Graphics" + - point: "Memory allocation in low DOS memory for compatibility" + link: "https://en.wikipedia.org/wiki/Conventional_memory" + link_label: "Conventional Memory" enhancements: - id: "gamma-correction-lut" line_start: 49 line_end: 132 - title: "The Gamma Tables That Brightened DOOM" + title: "Why DOOM Needed Five Gamma Tables" wikipedia_url: "https://en.wikipedia.org/wiki/Gamma_correction" image_url: "" image_caption: "" - content: "This section defines gamma correction lookup tables (LUTs) that adjust the brightness of the game's visuals. Gamma correction is crucial for ensuring consistent visual output across different monitors, which varied widely in brightness and color reproduction in the early 1990s. The tables provide predefined mappings for pixel brightness levels, allowing players to select from five levels of gamma correction. This was a practical solution to hardware inconsistencies, as monitors of the era lacked standardized brightness controls. The tables are hardcoded, reflecting the era's preference for performance over flexibility. Gamma correction in DOOM influenced later games, which adopted similar techniques for visual customization. Today, gamma adjustment is a standard feature in graphics settings, but DOOM's implementation was among the earliest examples in gaming." - - id: "mark-dirty-rectangles" + content: "This section defines five gamma correction lookup tables, each containing 256 values. These tables adjust the brightness of the game's graphics based on user preferences or environmental conditions. Gamma correction is crucial for ensuring that the visuals are both legible and aesthetically pleasing across different monitors, which varied significantly in quality in the early 1990s. At the time, consumer CRT monitors often displayed inconsistent brightness levels, and DOOM's developers needed a way to adapt to these variations. John Carmack's decision to include multiple gamma levels reflects his meticulous attention to detail and his commitment to delivering a polished gaming experience. This approach influenced later games, which adopted similar gamma adjustment techniques to improve visual fidelity. Today, gamma correction remains a standard feature in graphics settings for games and software." + - id: "marking-dirty-rectangles" line_start: 138 line_end: 150 - title: "Marking Dirty Rectangles for Efficiency" + title: "Dirty Rectangles, Pixel Block Copies, and Patch Rendering" wikipedia_url: "https://en.wikipedia.org/wiki/Dirty_rectangle" image_url: "" image_caption: "" - content: "The `V_MarkRect` function marks rectangular regions of the screen that need to be updated. This technique, known as dirty rectangle management, minimizes the computational cost of redrawing the entire screen by focusing only on areas that have changed. In DOOM, this was critical for maintaining high frame rates on hardware with limited processing power, such as 386 and 486 PCs. By marking and tracking these regions, the game could optimize rendering operations, ensuring smooth gameplay without unnecessary overhead. This approach was widely adopted in subsequent games and graphical applications, influencing techniques in modern rendering engines like Unity and Unreal Engine." - - id: "copy-rectangles-between-screens" - line_start: 153 - line_end: 195 - title: "Copying Rectangles Across Screen Buffers" - wikipedia_url: "https://en.wikipedia.org/wiki/Framebuffer" - image_url: "" - image_caption: "" - content: "The `V_CopyRect` function copies rectangular blocks of pixels from one screen buffer to another. This operation is essential for managing multiple screen buffers, which DOOM uses to handle different views or layers of graphics. The function includes range-checking to prevent out-of-bounds errors, ensuring robust handling of screen dimensions. By leveraging memory manipulation techniques like `memcpy`, the function achieves high performance, a necessity for real-time rendering on early PCs. Screen buffer management became a foundational concept in graphics programming, influencing later developments in framebuffers and GPU architectures." - - id: "draw-patch-to-screen" + content: "This small range packs three closely related screen-management primitives. `V_MarkRect` records that a rectangular region of the framebuffer has changed and must be blitted to the display; by accumulating dirty regions rather than updating the entire screen every frame, DOOM avoided wasting bus bandwidth on pixels that had not moved, a technique borrowed from the windowing systems of the Xerox Alto and early Macintosh. `V_CopyRect` acts on those dirty regions by doing a raw byte-for-byte copy from one of DOOM's four software screen buffers to another, using pointer arithmetic rather than a library memcpy so the compiler had no opportunity to insert alignment-safety overhead on the 486. `V_DrawPatch` renders a WAD patch graphic column by column onto a target screen, skipping transparent post gaps natively encoded in the patch format; this made sprites and HUD elements automatically composited without a separate masking pass. The three functions together describe DOOM's entire 2D compositing pipeline: mark what changed, copy it between buffers, and stamp patches on top, all in software with no graphics-hardware assist beyond a final linear framebuffer write." + - id: "flipping-patches-horizontally" line_start: 198 line_end: 262 - title: "Drawing Patches: DOOM's Modular Graphics" + title: "Mirroring Sprites Without Extra Memory" wikipedia_url: "https://en.wikipedia.org/wiki/Sprite_(computer_graphics)" image_url: "" image_caption: "" - content: "The `V_DrawPatch` function renders graphical patches (sprites) onto the screen. Patches in DOOM are modular graphic elements, such as textures or UI components, stored in a column-based format. This function iterates through columns and posts within a patch, drawing pixel data directly to the screen buffer. The modular design of patches allowed DOOM to efficiently manage and render complex scenes, a key innovation in its graphics engine. The function also includes range-checking to handle errors gracefully. DOOM's patch-based rendering influenced sprite handling in later 2D and 3D games, paving the way for modular asset systems in modern engines." - - id: "draw-patch-flipped" - line_start: 264 - line_end: 326 - title: "Flipping Patches: Mirroring Graphics in DOOM" - wikipedia_url: "https://en.wikipedia.org/wiki/Mirroring_(graphics)" - image_url: "" - image_caption: "" - content: "The `V_DrawPatchFlipped` function renders patches to the screen while flipping them horizontally. This capability is useful for creating mirrored graphics, such as symmetrical UI elements or character sprites. The function adjusts column offsets to reverse the drawing order, ensuring the patch appears flipped. Mirroring graphics was a clever way to reuse assets without duplicating data, saving memory and storage space on constrained hardware. This technique became a standard practice in game development, influencing asset optimization strategies in later games and engines." - - id: "initialize-screen-buffers" + content: "The `V_DrawPatchFlipped` function renders a sprite onto the screen but flips it horizontally. This is achieved by reversing the order of columns during rendering, rather than creating a separate mirrored version of the sprite in memory. This clever trick saved memory, which was a precious resource on early PCs, and allowed for dynamic visual effects like mirrored enemies or symmetrical decorations. The function uses the same column-based rendering approach as `V_DrawPatch`, ensuring consistency and efficiency. This technique reflects Carmack's ability to balance performance and resource constraints while delivering innovative features. Mirroring sprites dynamically became a common practice in game development, influencing engines like Unity and Unreal, which offer built-in support for sprite transformations." + - id: "initializing-screen-buffers" line_start: 478 - line_end: 492 - title: "Allocating Screen Buffers in Low DOS Memory" - wikipedia_url: "https://en.wikipedia.org/wiki/DOS_memory_management" + line_end: 491 + title: "Why DOOM Allocated Memory in Low DOS" + wikipedia_url: "https://en.wikipedia.org/wiki/Conventional_memory" image_url: "" image_caption: "" - content: "The `V_Init` function initializes screen buffers by allocating memory in low DOS memory. This was a deliberate choice to optimize memory usage on PCs running MS-DOS, which had strict memory constraints. By allocating a contiguous block of memory for screen buffers, DOOM ensured efficient access and reduced fragmentation. The function divides the allocated memory into four buffers, corresponding to different views or layers in the game. Memory management was a critical aspect of programming for DOS systems, and DOOM's approach influenced techniques in other games of the era. Today, memory allocation strategies have evolved, but the principles of efficient buffer management remain relevant in modern graphics programming." + content: "The `V_Init` function initializes the screen buffers used for rendering. It allocates memory in low DOS, a region below the 640KB limit imposed by early PC architectures. This decision ensured compatibility with a wide range of systems, including those running MS-DOS, which was the dominant operating system in the early 1990s. The function divides the allocated memory into four buffers, allowing for multiple screens to be managed simultaneously. This setup was crucial for supporting features like split-screen multiplayer and efficient rendering. By carefully managing memory allocation, Carmack and his team were able to deliver a game that ran smoothly on hardware with severe limitations. The technique of optimizing memory usage for compatibility influenced later games and engines, particularly those targeting constrained platforms like handheld consoles." --- @@ -573,4 +557,4 @@ void V_Init (void) for (i=0 ; i<4 ; i++) screens[i] = base + i*SCREENWIDTH*SCREENHEIGHT; } -``` +``` \ No newline at end of file diff --git a/public/programs/doom/w-wad-c.md b/public/programs/doom/w-wad-c.md index 3a77956..192ec0b 100644 --- a/public/programs/doom/w-wad-c.md +++ b/public/programs/doom/w-wad-c.md @@ -9,76 +9,76 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "w-wad-c" order: 5 -description: "This file implements WAD file handling in DOOM, enabling the game's modular content system and paving the way for its legendary modding community." +description: "This file defines the handling of WAD files, a format central to DOOM's modding ecosystem and efficient data management." summary: - - point: "Introduced modular WAD file handling for game assets" - link: "https://en.wikipedia.org/wiki/Doom_WAD" - link_label: "Doom WAD" - - point: "Enabled dynamic loading and caching of game data" + - point: "Introduced the WAD file format for modular game data storage" + link: "https://en.wikipedia.org/wiki/WAD_(file_format)" + link_label: "WAD File Format" + - point: "Implemented caching and reload mechanisms for game assets" link: "https://en.wikipedia.org/wiki/Cache_(computing)" - link_label: "Cache" - - point: "Facilitated user-created content and modding" - link: "https://en.wikipedia.org/wiki/Mod_(video_games)" - link_label: "Game modding" + link_label: "Caching" + - point: "Optimized lump-based data access for performance on 1990s hardware" + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM" enhancements: - id: "toupper-string-conversion" line_start: 66 - line_end: 71 + line_end: 70 title: "Why DOOM Converts Strings to Uppercase" wikipedia_url: "https://en.wikipedia.org/wiki/Case_sensitivity" image_url: "" image_caption: "" - content: "This small utility function converts strings to uppercase, ensuring case-insensitive comparisons throughout the WAD handling code. In the early 1990s, case sensitivity in filenames and identifiers was a common source of bugs, especially when software needed to run on multiple operating systems with differing conventions (e.g., MS-DOS vs. UNIX). By standardizing all names to uppercase, DOOM sidesteps these issues entirely. This approach reflects John Carmack's pragmatic programming philosophy: eliminate potential pitfalls with simple, robust solutions. The technique became a standard practice in many game engines and tools, influencing how developers approached cross-platform compatibility." - - id: "file-length-detection" + content: "The `strupr` function converts a string to uppercase, ensuring case-insensitive comparisons. This was crucial for DOOM's lump-based data system, where lump names had to be matched regardless of case. In the early 1990s, case sensitivity was a common source of bugs in software, especially when dealing with file systems that varied in their handling of case (e.g., MS-DOS vs. UNIX). By standardizing all names to uppercase, DOOM avoided mismatches and ensured consistent behavior across platforms. This approach influenced later game engines, which often adopted similar case-insensitivity strategies for asset management." + - id: "file-length-calculation" line_start: 73 line_end: 137 - title: "How DOOM Measures File Sizes Without Errors" - wikipedia_url: "https://en.wikipedia.org/wiki/Stat_(system_call)" + title: "How DOOM Measured File Sizes Efficiently" + wikipedia_url: "https://en.wikipedia.org/wiki/File_system" image_url: "" image_caption: "" - content: "The `filelength` function uses the `fstat` system call to determine the size of a file. This was crucial for handling WAD files, which contain variable-length lumps of data. By relying on system-level calls, DOOM ensures accurate file size detection regardless of the underlying filesystem. In the early 1990s, developers often had to deal with quirks in file handling across different operating systems. This function reflects Carmack's focus on reliability and portability, ensuring DOOM's WAD system could function seamlessly on both MS-DOS and UNIX-based systems. The technique influenced later game engines, which adopted similar methods to handle asset files dynamically." + content: "The `filelength` function retrieves the size of a file using `fstat`. This was a direct and efficient method to determine the size of game asset files, which were often small and accessed frequently. In the constrained environment of 1993, where memory and disk I/O were bottlenecks, minimizing overhead in file operations was critical. By relying on system calls like `fstat`, DOOM ensured compatibility with various operating systems while maintaining performance. This technique became a staple in game development, influencing asset loading strategies in later engines like Quake and Unreal." - id: "extract-file-base" line_start: 140 line_end: 225 - title: "The Eight-Character Filename Limit Explained" + title: "MS-DOS Filename Extraction and Dynamic WAD File Loading" wikipedia_url: "https://en.wikipedia.org/wiki/8.3_filename" image_url: "" image_caption: "" - content: "The `ExtractFileBase` function extracts the base name of a file, limited to eight characters, and converts it to uppercase. This design stems from the 8.3 filename convention used in MS-DOS, where filenames were restricted to eight characters plus a three-character extension. By enforcing this limit, DOOM ensures compatibility with legacy systems while maintaining a consistent naming scheme for WAD lumps. The function also validates the length, throwing an error if the base name exceeds eight characters. This reflects the constraints of the era, where hardware and software limitations shaped design decisions. The eight-character limit became iconic in early PC gaming and influenced how modders named their custom assets." - - id: "wad-file-validation" - line_start: 140 - line_end: 225 - title: "How DOOM Distinguishes IWADs from PWADs" - wikipedia_url: "https://en.wikipedia.org/wiki/Doom_WAD" - image_url: "" - image_caption: "" - content: "The `W_AddFile` function is responsible for loading WAD files, validating their headers, and populating the lump directory. It distinguishes between IWADs (core game data) and PWADs (custom or user-created content). This distinction allowed DOOM to support modding while protecting the integrity of its original game assets. The function reads the WAD header to determine the number of lumps and their locations, ensuring compatibility with both official and homebrew levels. This modular approach to game content was revolutionary, enabling users to create and share custom levels. The concept of separating core assets from user-generated content influenced countless games and engines, including Quake and Unreal." - - id: "reloadable-wad-support" + content: "This range contains two tightly coupled utilities. `ExtractFileBase` strips a full path down to the bare filename stem, truncated to eight uppercase characters to conform to the MS-DOS 8.3 naming convention. Because WAD lump names were derived from filenames at build time, enforcing this limit at runtime meant that a patch WAD named e1m1extra.wad was guaranteed to produce a lump called E1M1EXTR rather than silently failing or overflowing an eight-byte name field. `W_AddFile` uses the extracted base name as part of its work: it opens a file, determines whether it is a standalone single-lump resource or a proper multi-lump WAD by inspecting the four-byte magic identifier, then appends the appropriate directory entries to the global lump table. A reload-capable path for wad files prefixed with a tilde was deliberately described in the comments as a fragile hack, a rare moment of candor that reveals the pressures of id Software's shipping schedule. Together the two functions explain why DOOM's modding scene was built around strict naming conventions and why WAD files remained backward-compatible across patches and expansion packs: the engine simply concatenated lump tables and let later entries shadow earlier ones, a scheme so effective that it survived essentially unchanged through Quake and into the Source engine." + - id: "wad-reload-mechanism" line_start: 230 line_end: 274 - title: "The Hack That Made Map Reloads Possible" - wikipedia_url: "https://en.wikipedia.org/wiki/Reload_(computing)" + title: "The Reload Hack That Enabled Map Editing" + wikipedia_url: "https://en.wikipedia.org/wiki/Mod_(video_games)" image_url: "" image_caption: "" - content: "The `W_Reload` function enables the reloading of WAD files, specifically for lumps marked as reloadable. This feature was designed to facilitate map reloads during development, allowing designers to test changes without restarting the game. However, the implementation is described as a 'fragile hack' in the comments, reflecting the challenges of adding dynamic features to a system not originally designed for them. Despite its limitations, this feature highlights DOOM's iterative development process and the team's willingness to experiment with new workflows. Reloadable assets became a standard feature in modern game engines, streamlining the development and testing of dynamic content." - - id: "wad-cache-system" + content: "The `W_Reload` function flushes reloadable lumps from memory and reloads their directory. This feature was crucial for developers and modders, as it allowed changes to map files without restarting the game. However, the comments acknowledge its fragility, highlighting the challenges of implementing dynamic asset reloading in an era of limited memory and disk speed. Despite its imperfections, this mechanism empowered the early DOOM modding community, laying the groundwork for the vibrant ecosystem of custom levels and total conversions that followed." + - id: "multiple-file-initialization" line_start: 278 - line_end: 519 - title: "How DOOM's Cache Kept Gameplay Smooth" - wikipedia_url: "https://en.wikipedia.org/wiki/Cache_(computing)" + line_end: 315 + title: "How DOOM Handled Multiple WAD Files" + wikipedia_url: "https://en.wikipedia.org/wiki/WAD_(file_format)" image_url: "" image_caption: "" - content: "The `W_CacheLumpNum` function implements a caching system for WAD lumps, ensuring frequently accessed data is stored in memory for quick retrieval. This optimization was critical for maintaining DOOM's fast-paced gameplay on limited hardware. The function checks if a lump is already cached; if not, it reads the lump into memory and tags it for future use. By minimizing disk I/O during gameplay, DOOM achieves smoother performance and shorter load times. This caching strategy influenced later game engines, which adopted similar techniques to manage large asset libraries efficiently. The concept of caching remains a cornerstone of performance optimization in modern software." - - id: "wad-profiling-tool" + content: "The `W_InitMultipleFiles` function initializes DOOM's lump system from a list of WAD files. It supports overriding lumps from earlier files, enabling modular asset management. This backward search mechanism ensured that patches or updates could replace existing assets without modifying the original files. The function's design reflects id Software's focus on flexibility and extensibility, which became a hallmark of their engines. This approach influenced later game engines, where modular file systems became standard practice for handling downloadable content and user-generated mods." + - id: "lump-name-checking" + line_start: 345 + line_end: 389 + title: "The Backward Search for Lump Names" + wikipedia_url: "https://en.wikipedia.org/wiki/Hash_table" + image_url: "" + image_caption: "" + content: "The `W_CheckNumForName` function searches for a lump by name, scanning backward through the lump directory. This ensures that later entries take precedence, allowing patches or custom WADs to override earlier assets. The function converts names to uppercase and splits them into integers for efficient comparison, reflecting the era's emphasis on performance optimization. This backward search mechanism became a standard in game engines, influencing asset management strategies in titles like Quake and Unreal Tournament." + - id: "wad-profiling" line_start: 521 line_end: 574 - title: "The Hidden Tool That Profiles WAD Usage" + title: "Tracking Lump Usage for Performance Insights" wikipedia_url: "https://en.wikipedia.org/wiki/Profiling_(computer_programming)" image_url: "" image_caption: "" - content: "The `W_Profile` function provides a diagnostic tool for analyzing WAD lump usage. It tracks which lumps are cached and their memory tags, outputting the results to a file named 'waddump.txt'. This profiling data helped the developers optimize memory usage and identify performance bottlenecks. Profiling tools like this were rare in early game development, reflecting DOOM's forward-thinking approach to performance tuning. By providing visibility into the game's inner workings, the function also laid the groundwork for modern debugging and profiling tools. Developers studying DOOM's source code often cite this function as an example of Carmack's meticulous attention to detail." + content: "The `W_Profile` function tracks the usage of lumps in memory, categorizing them as static ('S') or purgeable ('P'). This profiling data is written to a file, providing insights into memory allocation and asset usage. In the constrained environment of 1993, understanding memory behavior was critical for optimizing performance on consumer PCs. This profiling approach reflects id Software's meticulous attention to detail, ensuring DOOM ran smoothly despite its ambitious graphics and gameplay. Profiling tools like this influenced later engines, where memory management became increasingly sophisticated." --- @@ -657,4 +657,6 @@ void W_Profile (void) } fclose (f); } -``` + + +``` \ No newline at end of file diff --git a/public/programs/doom/wi-stuff-c.md b/public/programs/doom/wi-stuff-c.md index d728661..5daa703 100644 --- a/public/programs/doom/wi-stuff-c.md +++ b/public/programs/doom/wi-stuff-c.md @@ -9,188 +9,146 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "wi-stuff-c" order: 40 -description: "This file handles the intermission screens in DOOM, including animations, statistics, and map displays." +description: "This file handles the intermission screens in DOOM, showcasing level transitions, statistics, and animations that added depth to the gameplay experience." summary: - - point: "Defines animation structures for intermission screens" + - point: "Defines intermission animations and their logic" link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" link_label: "DOOM (1993)" - - point: "Introduces pixel-perfect positioning for graphics" - link: "https://en.wikipedia.org/wiki/Computer_graphics" - link_label: "Computer Graphics" - - point: "Optimizes intermission rendering using patches instead of full-screen frames" + - point: "Optimizes graphics using patches instead of full-screen frames" link: "https://en.wikipedia.org/wiki/Framebuffer" link_label: "Framebuffer" + - point: "Introduces modular animation structures for episodic maps" + link: "https://en.wikipedia.org/wiki/Animation" + link_label: "Animation" + - point: "Handles multiplayer-specific intermission details" + link: "https://en.wikipedia.org/wiki/Multiplayer_video_game" + link_label: "Multiplayer gaming" + - point: "Pushes hardware limits with efficient rendering techniques" + link: "https://en.wikipedia.org/wiki/Computer_graphics" + link_label: "Computer graphics" enhancements: - - id: "intermission-screen-constants" - line_start: 729 - line_end: 746 - title: "Constants That Define Intermission Layout" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - image_url: "" - image_caption: "" - content: "This section defines constants that control the layout and positioning of elements on the intermission screens. These include coordinates for single-player statistics, net game results, and deathmatch matrices. By using predefined constants, the developers ensured consistent rendering across different game modes and screen resolutions. In the early 1990s, screen resolutions varied widely, and DOOM's reliance on fixed pixel coordinates reflects the era's approach to graphics programming. This design choice allowed DOOM to achieve visually appealing layouts on modest hardware without requiring dynamic scaling or resolution independence. Later games, such as Quake and Unreal Tournament, would adopt more flexible systems, but DOOM's hardcoded approach remains a snapshot of early graphical design practices." - - id: "animation-data-structures" - line_start: 729 - line_end: 746 - title: "How DOOM Handles Animations" + - id: "animation-structure-for-intermission-screens" + line_start: 405 + line_end: 409 + title: "Animation Structure for Intermission Screens" wikipedia_url: "https://en.wikipedia.org/wiki/Animation" image_url: "" image_caption: "" - content: "This section introduces the data structures used to manage animations on intermission screens. The `anim_t` structure encapsulates details such as animation type, frame count, location, and timing. Animations are categorized into 'always', 'random', and 'level-specific', reflecting the game's need to balance dynamic visuals with performance constraints. In 1993, animations were a luxury on consumer-grade PCs, and DOOM's implementation showcases clever optimization. By using patches (small graphical elements) instead of full-screen frames, DOOM reduced memory usage and improved rendering speed. This technique influenced later games, which adopted similar strategies to manage animations efficiently." - - id: "world-map-node-locations" - line_start: 729 - line_end: 746 - title: "Mapping Levels to World Coordinates" + content: "This section introduces the `animenum_t` enumeration, which defines three types of animations: `ANIM_ALWAYS`, `ANIM_RANDOM`, and `ANIM_LEVEL`. These categories allow the intermission screens to display dynamic content tailored to the player's progress and the game's episodic structure. At the time, animation systems in games were often hardcoded or limited in flexibility. By creating a modular approach, DOOM's developers ensured that animations could be reused and adapted across different levels and episodes. This innovation influenced later games by encouraging developers to think of animations as modular components rather than static assets. The flexibility of this system also made it easier to port DOOM to various platforms, as the animation logic was abstracted from the hardware-specific rendering details." + - id: "point-data-for-level-maps" + line_start: 411 + line_end: 433 + title: "Intermission Screen Data: Coordinates, Animation Structures, and Episode Tables" wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" image_url: "" image_caption: "" - content: "This section defines the coordinates for level nodes on the world map for each episode. Each node corresponds to a level's position on the intermission screen, allowing players to visualize their progress. The coordinates are hardcoded, reflecting the static nature of DOOM's map design. This approach was typical of games in the early 1990s, where dynamic map generation was rare due to hardware limitations. The concept of visualizing level progression influenced later games, such as Diablo and StarCraft, which used similar techniques to enhance player immersion." - - id: "animated-background-initialization" - line_start: 502 - line_end: 529 - title: "Initializing Animated Intermission Backgrounds" - wikipedia_url: "https://en.wikipedia.org/wiki/Animation" - image_url: "" - image_caption: "" - content: "This function initializes the animated backgrounds for intermission screens based on the current episode. It sets up timing and state variables for each animation, ensuring smooth transitions and consistent behavior. The use of randomization in 'ANIM_RANDOM' animations adds variety, enhancing the visual appeal. In the context of 1993 hardware, this approach was innovative, as it balanced complexity with performance constraints. The technique of precomputing animation states influenced later games, which adopted similar methods to optimize rendering pipelines." - - id: "animated-background-rendering" - line_start: 582 - line_end: 720 - title: "Rendering Animated Backgrounds" - wikipedia_url: "https://en.wikipedia.org/wiki/Computer_graphics" + content: "This compact region contains all the static data that drives DOOM's episode intermission screens. The `point_t` struct is simply an (x, y) screen coordinate used to pin level markers and animation cells to specific pixels on the background map graphic. The runtime `anim_t` struct combines an animation type (ANIM_ALWAYS for looping effects, ANIM_RANDOM for occasional flickers, ANIM_LEVEL for progress-tied reveals), a tick period, a frame count, a point_t location, and the loaded patch pointers; every animation cell on an intermission screen is one instance of this struct. Below the structs, three arrays, `epsd0animinfo`, `epsd1animinfo`, and `epsd2animinfo`, hard-code the full animation schedules for the three episodes: Knee-Deep in the Dead, The Shores of Hell, and Inferno. The companion `NUMANIMS` array derives each episode's count with a sizeof divide, avoiding the maintenance hazard of a manually updated constant. Finally, the `anims` pointer array indexes all three episode tables by episode number so that a single runtime expression selects the correct schedule. Together these declarations replaced what could have been a maze of switch statements with a small table-driven system that is easy to read, easy to extend, and entirely content-side, illustrating how id Software separated data from code even under the time pressure of a 1993 ship date." + - id: "wi-draw-no-state" + line_start: 811 + line_end: 815 + title: "Why Draw Nothing? A Placeholder Function" + wikipedia_url: "https://en.wikipedia.org/wiki/Placeholder" image_url: "" image_caption: "" - content: "This function renders the animated backgrounds during intermission screens. It iterates through the animations for the current episode, drawing the appropriate patch for each frame. The use of patches instead of full-screen images reflects DOOM's focus on efficiency, minimizing memory usage and maximizing rendering speed. This technique was crucial for achieving smooth animations on hardware with limited graphical capabilities. The idea of modular rendering influenced later engines, such as Unreal Engine, which adopted similar strategies to manage complex scenes." - - id: "drawing-level-completion-time" - line_start: 419 - line_end: 433 - title: "Displaying Level Completion Time" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + content: "The `WI_drawNoState` function is a simple placeholder that sets a flag (`snl_pointeron`) and calls another function, `WI_drawShowNextLoc`. This function is part of the intermission screen logic but does not perform any significant drawing itself. It serves as a minimal state handler for scenarios where no specific intermission screen needs rendering. In the early 1990s, placeholder functions like this were common in game development, ensuring modularity and allowing future expansion without breaking existing code. This approach reflects John Carmack's emphasis on clean, modular programming. While the function itself is trivial, it contributes to the broader architecture of DOOM's intermission system, which influenced later games like Quake and Unreal Tournament by demonstrating how to handle transitions between gameplay states efficiently." + - id: "wi-frag-sum" + line_start: 817 + line_end: 838 + title: "Negative Frags: A Hack for Multiplayer Stats" + wikipedia_url: "https://en.wikipedia.org/wiki/Multiplayer_video_game" image_url: "" image_caption: "" - content: "This function displays the player's completion time for a level, along with the par time or a 'sucks' message if the time exceeds a certain threshold. The use of modular rendering techniques, such as drawing individual digits and symbols, reflects DOOM's optimization for limited hardware. By avoiding full-screen redraws, the game achieved high performance on early PCs. The concept of displaying performance metrics influenced later games, which adopted similar features to enhance player engagement and replayability." - - id: "deathmatch-stats-initialization" + content: "The `WI_fragSum` function calculates the total frags (kills) for a given player, excluding self-frags. Interestingly, it includes a commented-out hack for handling negative frags, a rare feature in multiplayer games of the era. This reflects the experimental nature of DOOM's multiplayer design, where edge cases like self-inflicted damage were considered. Multiplayer gaming in 1993 was still in its infancy, with DOOM pioneering networked deathmatch gameplay. The ability to track and display frags dynamically was groundbreaking, influencing later games like Quake, which expanded on DOOM's multiplayer innovations. The commented-out code hints at the iterative development process, where features were tested and sometimes abandoned based on player feedback or technical constraints." + - id: "wi-init-deathmatch-stats" line_start: 848 line_end: 873 - title: "How DOOM Tracks Deathmatch Frags" - wikipedia_url: "https://en.wikipedia.org/wiki/Deathmatch" + title: "Initializing Deathmatch: Stats from Scratch" + wikipedia_url: "https://en.wikipedia.org/wiki/Deathmatch_(gaming)" image_url: "" image_caption: "" - content: "This section initializes the data structures required for tracking deathmatch statistics, including individual frags between players and total scores. At the time, deathmatch was a novel concept, popularized by DOOM itself. The code ensures that all players in the game are accounted for and sets up a matrix to track frags between every pair of players. This approach reflects the game's focus on competitive multiplayer, which was groundbreaking in 1993. The initialization also includes a call to set up an animated background, adding visual flair to the intermission screens. This system laid the foundation for multiplayer stat tracking in later games, influencing titles like Quake and Unreal Tournament." - - id: "deathmatch-stats-update" + content: "The `WI_initDeathmatchStats` function sets up the data structures for tracking player stats in deathmatch mode, initializing arrays for frags and totals. This function reflects the modular design philosophy of DOOM, where each game mode has dedicated initialization routines. Deathmatch was a revolutionary concept in 1993, allowing players to compete directly against each other over local networks. The function ensures that all stats are reset at the start of a match, preventing carryover errors. This modular approach influenced later multiplayer games, which adopted similar initialization techniques to manage complex game states efficiently. The inclusion of animated backgrounds (`WI_initAnimatedBack`) adds visual flair, showcasing DOOM's ability to combine technical innovation with engaging aesthetics." + - id: "wi-update-deathmatch-stats" line_start: 877 line_end: 976 - title: "Dynamic Deathmatch Stats with Sound Cues" - wikipedia_url: "https://doomwiki.org/wiki/Deathmatch" + title: "Dynamic Deathmatch: Updating Frags in Real-Time" + wikipedia_url: "https://en.wikipedia.org/wiki/Real-time_computing" image_url: "" image_caption: "" - content: "This section updates deathmatch statistics dynamically during intermission screens. It uses sound effects like 'barexp' and 'pistol' to signal transitions and changes, enhancing player engagement. The code iterates through players and updates the frag matrix and totals, ensuring that changes are reflected in real-time. It also caps scores at ±99 to prevent overflow, a practical safeguard given the hardware constraints of the era. The use of sound cues and real-time updates was innovative, creating a more immersive experience. This technique influenced later multiplayer games, where dynamic scoreboards and sound effects became standard features." - - id: "netgame-stats-initialization" + content: "The `WI_updateDeathmatchStats` function dynamically updates player stats during deathmatch gameplay. It checks for changes in frags and adjusts totals accordingly, ensuring that the intermission screen reflects the latest game state. This real-time updating was a technical challenge in the early 1990s, given the limited processing power of consumer PCs. The function also includes sound effects (`S_StartSound`) to enhance the player experience, tying visual updates to auditory cues. Real-time stat tracking became a standard feature in multiplayer games, with DOOM setting the precedent. Later games like Counter-Strike and Team Fortress 2 built on this foundation, incorporating more sophisticated algorithms for tracking and displaying player performance." + - id: "wi-draw-deathmatch-stats" + line_start: 980 + line_end: 1071 + title: "Drawing Deathmatch Stats: A Visual Breakdown" + wikipedia_url: "https://en.wikipedia.org/wiki/Computer_graphics" + image_url: "" + image_caption: "" + content: "The `WI_drawDeathmatchStats` function renders the intermission screen for deathmatch mode, displaying player frags and totals in a matrix format. It uses patch graphics (`V_DrawPatch`) to create a visually appealing layout, with icons representing players and their stats. This function highlights DOOM's innovative use of computer graphics to enhance gameplay. The matrix format was a clever way to present complex data in an easily digestible form, a design choice that influenced later games like Unreal Tournament. The function also includes special handling for the local player (`me`), drawing additional icons to indicate their position. This attention to detail reflects the team's commitment to creating an immersive multiplayer experience." + - id: "wi-init-netgame-stats" line_start: 1077 line_end: 1101 - title: "Setting Up Netgame Stats for Co-op Play" - wikipedia_url: "https://doomwiki.org/wiki/Cooperative_gameplay" + title: "Netgame Stats: Preparing for Cooperative Play" + wikipedia_url: "https://en.wikipedia.org/wiki/Cooperative_gameplay" image_url: "" image_caption: "" - content: "This section initializes statistics for netgame modes, focusing on cooperative gameplay. It tracks kills, items, secrets, and frags for each player, ensuring that all aspects of performance are recorded. The code also calculates whether frags are relevant for the current game mode, optimizing memory usage. Cooperative modes were less common in 1993, but DOOM's implementation showcased its versatility in multiplayer design. This initialization routine influenced later co-op games, where detailed stat tracking became a key feature for enhancing teamwork and competition." - - id: "netgame-stats-update" + content: "The `WI_initNetgameStats` function initializes stats for netgame mode, a cooperative multiplayer experience. It resets counters for kills, items, secrets, and frags, ensuring a clean slate for each session. Cooperative gameplay was a unique feature in DOOM, allowing players to work together to complete levels. This function reflects the game's versatility, supporting both competitive and cooperative modes. The modular initialization routine influenced later games like Diablo and Left 4 Dead, which adopted similar techniques to manage multiplayer stats. The inclusion of animated backgrounds (`WI_initAnimatedBack`) adds polish, demonstrating DOOM's ability to combine technical innovation with engaging visual design." + - id: "wi-update-netgame-stats" line_start: 1105 line_end: 1256 - title: "Real-Time Updates for Cooperative Stats" - wikipedia_url: "https://doomwiki.org/wiki/Cooperative_gameplay" - image_url: "" - image_caption: "" - content: "This section dynamically updates netgame statistics during intermission screens. It uses incremental counters to animate the display of kills, items, secrets, and frags, creating a sense of progression. Sound effects like 'pistol' and 'barexp' signal changes, adding auditory feedback to the visual updates. The code ensures that stats are capped at their maximum values, preventing anomalies. This real-time update system was ahead of its time, adding depth to cooperative gameplay. It influenced later games with similar modes, such as Left 4 Dead and Borderlands, where detailed stat tracking and dynamic updates are integral to the experience." - - id: "single-player-stats-initialization" - line_start: 1317 - line_end: 1327 - title: "Preparing Stats for Solo Missions" - wikipedia_url: "https://doomwiki.org/wiki/Single-player" - image_url: "" - image_caption: "" - content: "This section sets up statistics for single-player missions, including kills, items, secrets, and time. It initializes counters to -1, a common practice to indicate uninitialized values. The code also sets up an animated background, maintaining the visual consistency of intermission screens. Single-player modes were the core of DOOM's appeal, and this initialization routine ensured that players received detailed feedback on their performance. The focus on stats influenced later single-player games, where performance tracking became a staple feature." - - id: "single-player-stats-update" - line_start: 1329 - line_end: 1433 - title: "Animating Single-Player Stats with Precision" - wikipedia_url: "https://doomwiki.org/wiki/Single-player" - image_url: "" - image_caption: "" - content: "This section updates single-player statistics during intermission screens, animating the display of kills, items, secrets, and time. It uses incremental counters to create a sense of progression and employs sound effects like 'pistol' and 'barexp' for auditory feedback. The code ensures that stats are capped at their maximum values and synchronizes time and par values for accurate display. This attention to detail enhanced the single-player experience, making players feel rewarded for their performance. The technique influenced later games with similar stat tracking, such as Resident Evil and Dark Souls." - - id: "check-for-accelerate" - line_start: 1469 - line_end: 1497 - title: "Skipping Delays with Button Presses" - wikipedia_url: "https://doomwiki.org/wiki/Controls" + title: "Cooperative Stats: Tracking Progress Together" + wikipedia_url: "https://en.wikipedia.org/wiki/Cooperative_gameplay" image_url: "" image_caption: "" - content: "This section checks for button presses to accelerate intermission screens, allowing players to skip delays. It monitors the 'attack' and 'use' buttons, setting flags to bypass animations. This feature reflects DOOM's responsiveness to player input, prioritizing user experience. The ability to skip delays became a standard feature in later games, allowing players to control pacing during transitions. It influenced titles like Half-Life and Portal, where user control over transitions enhances immersion." - - id: "load-intermission-data" - line_start: 1537 - line_end: 1705 - title: "Loading Graphics for Intermission Screens" - wikipedia_url: "https://doomwiki.org/wiki/Intermission_screen" + content: "The `WI_updateNetgameStats` function updates player stats during netgame mode, tracking kills, items, secrets, and frags. It uses incremental updates to create a sense of progression, with sound effects (`S_StartSound`) enhancing the experience. Cooperative gameplay was a novel concept in 1993, and DOOM's implementation set the standard for future games. The function ensures that stats are updated dynamically, reflecting the team's focus on real-time feedback. This approach influenced later cooperative games like Borderlands, which expanded on DOOM's stat-tracking techniques to create more immersive multiplayer experiences. The function's modular design also highlights the team's commitment to clean, maintainable code." + - id: "wi-draw-netgame-stats" + line_start: 1260 + line_end: 1313 + title: "Rendering Cooperative Stats: A Shared Victory" + wikipedia_url: "https://en.wikipedia.org/wiki/Heads-up_display" image_url: "" image_caption: "" - content: "This section loads the graphics and patches required for intermission screens, including background images and 'you are here' markers. It uses memory allocation techniques to optimize resource usage, reflecting the constraints of 1993 hardware. The code dynamically selects assets based on the game mode and episode, ensuring that intermission screens are visually consistent. This approach influenced later games, where dynamic asset loading became a key technique for optimizing performance and enhancing visual fidelity." - - id: "animation-data-loading-hacks" + content: "The `WI_drawNetgameStats` function renders the intermission screen for netgame mode, displaying player stats in a visually appealing format. It uses patch graphics (`V_DrawPatch`) and percentage displays (`WI_drawPercent`) to showcase kills, items, secrets, and frags. This function reflects DOOM's innovative use of heads-up displays (HUDs) to enhance gameplay. The layout emphasizes teamwork, highlighting each player's contributions to the group's success. This design choice influenced later cooperative games like Halo and Destiny, which adopted similar HUD techniques to foster collaboration. The function also includes special handling for the local player (`me`), adding visual cues to indicate their position. This attention to detail underscores the team's commitment to creating an engaging multiplayer experience." + - id: "animation-hacks-for-episode-logic" line_start: 1537 line_end: 1705 - title: "The Animation Hack That Saved DOOM" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + title: "The Animation Hack That Saved Episode 2" + wikipedia_url: "https://doomwiki.org/wiki/Source_code" image_url: "" image_caption: "" - content: "This section loads animation data for the intermission screens, with a notable hack to reuse animation assets for Episode 1, Level 8. The code bypasses standard loading logic by directly referencing assets from Episode 1, Level 4. This 'MONDO HACK' reflects the practical constraints of 1993 hardware, where memory was precious and reusing assets was a necessity. John Carmack and the team often prioritized performance and resource efficiency over pristine code. This approach allowed DOOM to run smoothly on consumer-grade PCs while delivering visually rich intermission screens. Such hacks were common in early game development, where developers had to creatively work around hardware limitations. The technique of reusing assets influenced later games, especially in the era of sprite-based graphics, where memory optimization was critical." - - id: "intermission-text-and-symbol-loading" - line_start: 1537 - line_end: 1705 - title: "How DOOM Loaded Its Intermission Symbols" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - image_url: "" - image_caption: "" - content: "This section loads various text and symbols used in the intermission screens, such as numbers, percent signs, and phrases like 'finished' and 'entering.' Each element is cached using the W_CacheLumpName function, ensuring efficient memory usage. The intermission screens were a crucial part of DOOM's storytelling, providing players with a sense of progression and accomplishment. The choice to cache these assets reflects the team's focus on performance, as reloading these elements repeatedly would have slowed down the game. The inclusion of specific assets like 'sucks' and 'par' also highlights DOOM's irreverent tone, which resonated with its audience. This method of caching graphical assets became standard practice in game development, influencing engines like Quake and Unreal." - - id: "multiplayer-statistics-loading" - line_start: 1537 - line_end: 1705 - title: "Multiplayer Stats: DOOM's Competitive Edge" - wikipedia_url: "https://en.wikipedia.org/wiki/Multiplayer_video_game" - image_url: "" - image_caption: "" - content: "This section loads player-specific assets for multiplayer modes, including icons and statistics. The use of sprintf to dynamically generate asset names allowed DOOM to support up to four players in deathmatch mode. Multiplayer was a groundbreaking feature in DOOM, setting the stage for competitive gaming. The ability to track individual player stats and display them visually on intermission screens added depth to the multiplayer experience. This feature was a precursor to modern multiplayer stat tracking, seen in games like Counter-Strike and Call of Duty. By laying the groundwork for competitive gaming, DOOM's multiplayer innovations helped define an entire genre." - - id: "data-unloading-and-memory-management" + content: "This section of code loads animation data for the intermission screen, with a notable hack for Episode 2's animations. The programmer bypasses a specific animation (index 8) by reusing data from another animation (index 4). This workaround avoids creating redundant resources, saving memory and simplifying logic. At the time, DOOM was designed to run on hardware with limited memory, often as low as 4MB of RAM. Such constraints demanded clever tricks like this to optimize resource usage. John Carmack, known for his technical ingenuity, likely implemented this hack to meet tight deadlines and hardware limitations. This approach reflects the era's pragmatic programming style, where functionality often trumped elegance. The hack underscores the challenges of developing games for heterogeneous PC configurations. Techniques like this influenced later game engines, including id Tech, which continued to prioritize performance optimization. Developers studying DOOM's source code often cite these hacks as examples of resourceful problem-solving under constraints." + - id: "resource-unloading-for-intermission-screen" line_start: 1707 line_end: 1769 - title: "How DOOM Freed Memory for the Next Level" + title: "How DOOM Frees Memory Between Levels" wikipedia_url: "https://en.wikipedia.org/wiki/Memory_management" image_url: "" image_caption: "" - content: "This section unloads intermission data and changes memory tags to PU_CACHE, preparing the system for the next level. Efficient memory management was critical for DOOM, as PCs in 1993 had limited RAM. By freeing unused assets and caching others, the game ensured smooth transitions between levels. The Z_ChangeTag function reflects the team's deep understanding of hardware constraints and their ability to optimize for performance. This approach influenced later game engines, which adopted similar memory management techniques to handle complex assets. The practice of dynamically managing memory tags became a hallmark of efficient game design, seen in engines like Unity and Unreal." - - id: "intermission-screen-drawing" + content: "The WI_unloadData function systematically frees or caches resources used during the intermission screen. It changes memory tags for various assets, ensuring they are available for reuse or safely discarded. This memory management approach was critical for DOOM's performance on low-end PCs. By dynamically managing resources, the game avoided crashes and ensured smooth transitions between levels. In 1993, memory management was a cornerstone of game development, as PCs lacked the abundance of RAM seen in modern systems. Carmack's team leveraged techniques like this to maximize the game's efficiency. The systematic unloading of resources influenced later engines, including Quake and Unreal Engine, which adopted similar strategies to handle large-scale environments. This function exemplifies the meticulous attention to memory optimization that defined early PC gaming." + - id: "state-based-intermission-drawing" line_start: 1771 line_end: 1792 - title: "Drawing DOOM's Intermission Screens" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + title: "Switching States: Drawing Intermission Screens" + wikipedia_url: "https://en.wikipedia.org/wiki/State_machine" image_url: "" image_caption: "" - content: "This section handles the drawing logic for intermission screens, switching between different states like StatCount, ShowNextLoc, and NoState. The intermission screens were a key part of DOOM's pacing, giving players a brief respite while providing crucial information about their performance. The modular design of WI_Drawer allowed the game to adapt its intermission logic based on the mode (single-player, deathmatch, or netgame). This flexibility was ahead of its time, showcasing the team's foresight in designing systems that could accommodate diverse gameplay scenarios. The modular approach to screen drawing influenced later games, encouraging developers to separate logic into manageable states for easier maintenance and expansion." - - id: "variable-initialization-for-intermissions" + content: "WI_Drawer uses a state machine to determine which intermission screen to draw based on the current game state. It handles deathmatch stats, network game stats, or single-player stats, depending on the context. State machines were a common programming pattern in the early '90s, especially for games, as they provided a structured way to manage complex behaviors. This design allowed DOOM to support multiple gameplay modes without duplicating code. The use of a state machine here reflects the game's modular architecture, which was ahead of its time. This approach influenced later games and engines, where state-based systems became standard for managing UI and gameplay logic. Developers studying DOOM's source code often point to its state machine implementations as a model for clean, maintainable design." + - id: "variable-initialization-for-intermission-logic" line_start: 1795 line_end: 1835 - title: "Setting Up DOOM's Intermission Variables" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + title: "Initializing Variables for Post-Level Stats" + wikipedia_url: "https://en.wikipedia.org/wiki/Variable_(computer_science)" image_url: "" image_caption: "" - content: "This section initializes variables for intermission screens, ensuring all data structures are properly set up before rendering. The code includes range-checking logic, which prevents out-of-bounds errors—a critical feature in a time when debugging tools were limited. The initialization process reflects the team's meticulous attention to detail, ensuring the game could handle edge cases like missing kill counts or secret items. By standardizing variable setup, the team created a robust foundation for intermission logic. This practice of thorough initialization became a best practice in game development, influencing later engines and frameworks." - - id: "intermission-start-sequence" + content: "WI_initVariables initializes key variables for the intermission screen, including player stats, kill counts, and item totals. It also performs range checks to ensure data integrity. This function reflects the meticulous attention to detail required in game programming, where even minor errors could cause crashes or corrupt data. In the early '90s, debugging tools were limited, so developers often relied on manual checks like these. The range-checking code highlights the team's commitment to stability, even as they pushed the limits of hardware. This initialization routine influenced later games, where robust variable handling became a hallmark of reliable software. DOOM's source code remains a touchstone for developers seeking to understand the foundations of modern game programming." + - id: "starting-intermission-screen-logic" line_start: 1837 - line_end: 1849 - title: "How DOOM Began Its Intermissions" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + line_end: 1848 + title: "The Function That Starts It All" + wikipedia_url: "https://en.wikipedia.org/wiki/Function_(computer_programming)" image_url: "" image_caption: "" - content: "This section starts the intermission sequence by initializing variables, loading data, and determining the appropriate stats logic based on the game mode. The intermission screens were a vital part of DOOM's storytelling, providing players with a sense of progression and accomplishment. By dynamically adapting the sequence to the game mode, the team ensured a seamless experience for both single-player and multiplayer scenarios. The modular design of WI_Start reflects the team's ability to balance complexity and performance, a hallmark of DOOM's development. This approach influenced later games, encouraging developers to create adaptable systems that could handle diverse gameplay scenarios." + content: "WI_Start is the entry point for the intermission screen logic. It initializes variables, loads resources, and determines which stats to display based on the gameplay mode. This function encapsulates the modular design philosophy of DOOM, where each component is self-contained and reusable. By separating initialization, resource loading, and mode-specific logic, the developers created a system that was easy to extend and debug. In the context of 1993, this modularity was a significant achievement, as many games of the era relied on monolithic codebases. WI_Start's design influenced later engines, where modularity became a cornerstone of scalability and maintainability. Developers studying DOOM's source code often cite this function as an example of clean, efficient design." --- diff --git a/public/programs/doom/z-zone-c.md b/public/programs/doom/z-zone-c.md index 2db578b..e698018 100644 --- a/public/programs/doom/z-zone-c.md +++ b/public/programs/doom/z-zone-c.md @@ -9,76 +9,76 @@ year: 1993 author: "John Carmack, John Romero, Dave Taylor" slug: "z-zone-c" order: 6 -description: "The memory management system in DOOM was a critical innovation that allowed the game to run efficiently on limited hardware, influencing countless future games and engines." +description: "This file showcases DOOM's innovative memory management system, which optimized performance on limited hardware." summary: - - point: "DOOM's memory allocation system avoided fragmentation by ensuring no contiguous free blocks existed." + - point: "DOOM's memory allocation system ensured efficient use of limited RAM." + link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + link_label: "DOOM (1993)" + - point: "Zone-based memory management avoided fragmentation and improved performance." link: "https://en.wikipedia.org/wiki/Memory_management" link_label: "Memory Management" - - point: "The 'rover' pointer streamlined allocation by tracking the next free block." - link: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" - link_label: "DOOM" - - point: "Tags enabled dynamic memory purging, a precursor to modern garbage collection techniques." - link: "https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)" - link_label: "Garbage Collection" + - point: "Techniques here influenced later game engines and memory systems." + link: "https://en.wikipedia.org/wiki/Game_engine" + link_label: "Game Engine" enhancements: - - id: "zone-memory-allocation-overview" - line_start: 31 - line_end: 55 - title: "Why DOOM Avoided Contiguous Free Blocks" - wikipedia_url: "https://en.wikipedia.org/wiki/Memory_management" - image_url: "" - image_caption: "" - content: "This section introduces DOOM's zone memory allocation system, which was designed to minimize fragmentation and maximize performance on constrained hardware. The key insight was ensuring that no two contiguous free memory blocks existed, which avoided costly coalescing operations during runtime. By maintaining a 'rover' pointer to track the next free block, the system streamlined allocation and deallocation. In the early 1990s, consumer PCs typically had limited RAM (often 4–8 MB), making efficient memory management crucial for a game as ambitious as DOOM. John Carmack, the lead programmer, drew inspiration from prior work in systems programming and adapted these techniques to fit the game's real-time requirements. This approach influenced later game engines, including Quake and Unreal, which adopted similar strategies for memory management." - - id: "clear-zone-initialization" + - id: "memzone-structure-definition" line_start: 63 line_end: 85 - title: "How DOOM Reset Memory Zones" + title: "memzone_t and Z_ClearZone: The Foundation of DOOM's Memory Arena" wikipedia_url: "https://en.wikipedia.org/wiki/Memory_management" image_url: "" image_caption: "" - content: "The `Z_ClearZone` function resets a memory zone to a single free block, effectively clearing all allocations. This was a foundational operation for initializing or resetting memory in DOOM. The function sets up the blocklist and rover pointer, ensuring the entire zone is ready for new allocations. In the early 1990s, memory management was often manual, and games needed to handle fragmentation carefully to avoid crashes or performance degradation. Carmack's approach ensured stability and predictability, even under heavy memory usage. This technique laid the groundwork for more sophisticated memory management systems in later engines, where zone-based allocation became a standard practice." - - id: "zone-initialization" + content: "The memzone_t structure is a minimal arena header: it stores the total byte size of the zone, a sentinel blocklist node that anchors the doubly-linked free/used block ring, and a rover pointer that remembers where the last allocation left off so subsequent searches start nearby rather than always from the beginning. Z_ClearZone resets any memzone_t to a pristine state by collapsing the entire allocatable region into one large free block linked through the sentinel, which allows the allocator to restart cleanly between levels without calling the OS allocator again. Consumer PCs in 1993 commonly had 4 MB of RAM, and DOS memory models made reliable dynamic allocation fragile; by reserving a single large slab at startup via I_ZoneBase and then self-managing it entirely, DOOM sidestepped system-allocator fragmentation and unpredictable latency. This arena pattern predated DOOM (it appeared in earlier id Software titles) but DOOM's clean implementation became the reference that Quake, Quake II, and many subsequent open-source game engines inherited almost unchanged." + - id: "z-init-zone-setup" line_start: 89 line_end: 115 - title: "The Function That Starts It All" - wikipedia_url: "https://en.wikipedia.org/wiki/DOOM_(1993_video_game)" + title: "The Function That Prepared DOOM's Memory Zones" + wikipedia_url: "https://en.wikipedia.org/wiki/Memory_management" image_url: "" image_caption: "" - content: "The `Z_Init` function initializes the main memory zone used by DOOM. It calls `I_ZoneBase` to allocate memory and sets up the blocklist and rover pointer. This function ensures that the entire memory zone starts as a single free block, ready for allocation. At the time, games like DOOM had to run on hardware with very limited resources, often relying on manual memory management to avoid crashes and ensure smooth gameplay. This initialization routine exemplifies Carmack's meticulous approach to performance optimization. The concept of a single contiguous memory zone influenced later game engines, which adopted similar strategies to manage memory efficiently in real-time applications." - - id: "freeing-memory-blocks" + content: "`Z_Init` initializes the main memory zone by calling `I_ZoneBase` to allocate memory and then setting up the zone's blocklist and rover pointer. This function is the starting point for DOOM's memory management system, ensuring that the game has a contiguous block of memory to work with. In the early 1990s, developers often had to write their own memory allocators to bypass the limitations of standard libraries and operating systems. John Carmack's decision to implement a custom zone-based system allowed DOOM to handle dynamic memory allocation efficiently, even on hardware with limited resources. This initialization routine laid the groundwork for the game's fast-paced performance, influencing memory allocation strategies in later games like Half-Life and Deus Ex." + - id: "z-free-memory-deallocation" line_start: 118 - line_end: 171 - title: "The Art of Freeing Memory Safely" + line_end: 359 + title: "How DOOM Freed Memory Without Fragmentation" wikipedia_url: "https://en.wikipedia.org/wiki/Memory_management" image_url: "" image_caption: "" - content: "The `Z_Free` function handles the deallocation of memory blocks in DOOM's zone memory system. It verifies the block's integrity using a unique identifier (`ZONEID`) and merges adjacent free blocks to prevent fragmentation. This approach was crucial for maintaining performance on hardware with limited RAM. In the early 1990s, memory management was a complex challenge, especially for real-time applications like games. Carmack's solution ensured stability and efficiency, avoiding the pitfalls of traditional malloc/free systems. The merging of free blocks inspired similar techniques in later engines, where memory fragmentation remained a critical concern." - - id: "malloc-allocation-strategy" + content: "`Z_Free` deallocates a memory block and merges adjacent free blocks to prevent fragmentation. This function checks the block's ID to ensure it belongs to the zone, clears the user's mark, and updates the linked list to merge contiguous free blocks. Memory fragmentation was a common problem in the 1990s, especially for programs that frequently allocated and deallocated memory. DOOM's approach ensured that free memory remained contiguous, improving allocation speed and reducing overhead. This technique influenced memory management in later game engines, particularly those designed for real-time applications, where performance is critical." + - id: "z-malloc-dynamic-allocation" line_start: 182 line_end: 288 - title: "How DOOM Allocated Memory Dynamically" + title: "The Algorithm That Made DOOM's Memory Dynamic" wikipedia_url: "https://en.wikipedia.org/wiki/Memory_management" image_url: "" image_caption: "" - content: "The `Z_Malloc` function dynamically allocates memory blocks in DOOM's zone memory system. It scans the blocklist for a free block of sufficient size, purging blocks if necessary. The function also splits oversized blocks to minimize wasted space. This allocation strategy was designed to handle the game's demanding real-time requirements while avoiding fragmentation. In 1993, dynamic memory allocation was a significant challenge for game developers, especially on hardware with limited resources. Carmack's implementation ensured that DOOM could manage memory efficiently without compromising performance. This technique influenced later engines, which adopted similar strategies for dynamic allocation in real-time applications." - - id: "heap-dump-debugging" - line_start: 321 + content: "`Z_Malloc` dynamically allocates memory within a zone, scanning for a free block of sufficient size and purging blocks if necessary. The function ensures that allocations align to 4 bytes and splits larger blocks to minimize wasted space. In the early 1990s, dynamic memory allocation was a challenge due to limited hardware resources and the lack of robust standard libraries. DOOM's custom allocator allowed the game to manage memory efficiently, even as it loaded new levels or assets. This algorithm inspired similar techniques in later game engines, such as Unreal Engine, which adopted zone-based allocation to handle complex scenes and dynamic objects." + - id: "z-dumpheap-debugging-tool" + line_start: 322 line_end: 359 - title: "Debugging Memory with Heap Dumps" + title: "The Debugging Tool That Kept DOOM Stable" wikipedia_url: "https://en.wikipedia.org/wiki/Debugging" image_url: "" image_caption: "" - content: "The `Z_DumpHeap` function provides a detailed view of the memory zone, listing each block's size, user, and tag. This debugging tool was invaluable for identifying memory issues during development. In the early 1990s, debugging tools were limited, and developers often relied on custom functions like this to diagnose problems. Carmack's inclusion of `Z_DumpHeap` reflects his commitment to robust development practices, ensuring that DOOM's memory system was both efficient and reliable. This approach influenced later engines, where detailed memory debugging became a standard feature." - - id: "check-heap-integrity" + content: "`Z_DumpHeap` prints the state of the memory zone to the console, showing block sizes, tags, and user pointers. This function was a critical debugging tool for identifying memory corruption and fragmentation issues during development. Debugging memory allocation was notoriously difficult in the 1990s, especially in performance-critical applications like games. By providing detailed insights into the memory state, `Z_DumpHeap` helped developers ensure stability and optimize performance. Similar debugging tools became standard practice in game development, influencing tools like Valgrind and custom memory profilers in modern engines." + - id: "z-checkheap-integrity-check" line_start: 395 line_end: 419 - title: "Ensuring Memory Integrity in Real-Time" + title: "How DOOM Ensured Its Memory Stayed Intact" wikipedia_url: "https://en.wikipedia.org/wiki/Memory_management" image_url: "" image_caption: "" - content: "The `Z_CheckHeap` function verifies the integrity of the memory zone, checking for issues like overlapping blocks or consecutive free blocks. This routine was critical for maintaining stability in DOOM's memory system, especially during long play sessions. In the early 1990s, memory corruption was a common cause of crashes in games, and developers had to implement rigorous checks to prevent it. Carmack's approach ensured that DOOM remained stable even under heavy memory usage, setting a precedent for robust memory management in later engines." + content: "`Z_CheckHeap` verifies the integrity of the memory zone, checking that block sizes and links are consistent. If any errors are found, the function halts execution with an error message. Memory corruption was a common issue in the 1990s, especially in programs that performed frequent dynamic allocations. By implementing rigorous integrity checks, DOOM minimized the risk of crashes and ensured reliable performance. This approach influenced later games and engines, which adopted similar checks to maintain stability in complex systems." + - id: "z-freememory-calculation" + line_start: 447 + line_end: 465 + title: "How DOOM Calculated Free Memory in Real Time" + wikipedia_url: "https://en.wikipedia.org/wiki/Memory_management" + image_url: "" + image_caption: "" + content: "`Z_FreeMemory` calculates the total amount of free memory in the zone by summing the sizes of free blocks and purgable blocks. This function provided developers with real-time insights into memory usage, helping them optimize performance. In the early 1990s, tools for monitoring memory were limited, and developers often had to write their own utilities. DOOM's ability to calculate free memory dynamically influenced later engines, which incorporated similar features to optimize resource usage in real-time applications." --- @@ -548,4 +548,5 @@ int Z_FreeMemory (void) } return free; } -``` + +``` \ No newline at end of file