Loading
Personal Project
Eurekiel: Chess3D

Chess3D is a 3D chess sandbox built entirely from scratch in C++ and DirectX 11 on top of the custom Eurekiel engine. The project replicates a full chess experience with multiplayer support, real-time Blinn-Phong rendering with normal mapping, an OBJ model loader that automatically deduces and corrects missing tangent/bitangent/normal data, and a data-driven architecture where every chess piece, board layout, and match rule is defined in XML with no recompilation needed.

Core Technical Highlights

Chess Gameplay & Rules

The chess rule engine enforces all standard movement constraints per piece type. Each ChessPieceDefinition is loaded from XML with a glyph, slide flag, and movement pattern, then the ChessMatch controller validates every move against the current board state. Pieces animate smoothly between tiles using an easing-based interpolation system, and the turn manager alternates control between factions after each valid move.

0:00
/0:00
Full chess rule enforcement with piece movement validation, turn alternation, and capture logic

When a piece is selected, the board highlights all legal destination tiles in real time. The highlight system queries the piece’s movement definition, filters out occupied-by-friendly squares, and renders translucent overlays on valid cells. This gives players immediate visual feedback before committing a move.

Legal move highlighting, where valid destination tiles are computed and displayed on piece selection

3D Raycasting & Piece Selection

Piece selection uses a 3D raycast from the camera through the mouse cursor into world space. The ray is tested against each piece’s collision volume (defined per-piece in ChessPieceDefinition.xml with radius and height), and the closest hit is selected. This same system drives the tile-targeting for move destinations.

0:00
/0:00
3D raycast-based piece selection where the ray from camera through cursor intersects piece collision volumes

Blinn-Phong Rendering Pipeline

The rendering pipeline implements Blinn-Phong shading with full normal mapping support. Each mesh samples three texture maps (diffuse, normal, and a packed specular-gloss-emissive SGE map) and computes lighting in world space using a TBN (Tangent-Bitangent-Normal) matrix constructed per vertex.

Blinn-Phong Rendering
Blinn-Phong lit scene with normal-mapped chess pieces and board, showing specular highlights and ambient lighting

The material pipeline supports per-object shader and texture assignment through the MeshComponent, which references diffuse, normal, and specular-gloss-emissive texture files. This three-map approach packs specular intensity, gloss (smoothness), and emissive into a single RGB texture, reducing draw calls while preserving material richness.

Blinn-Phong Material Maps
Material map breakdown showing diffuse color, normal map, and packed specular/gloss/emissive channels used by the Blinn-Phong shader

Shader Debug Visualization

The engine exposes 16+ shader debug modes through a ShaderDebugType enum, allowing real-time inspection of every stage in the rendering pipeline. Cycling through these modes reveals the raw data flowing through the shader, from diffuse color and UV coordinates to per-pixel normals in TBN space and world space, surface tangent/bitangent vectors, and the final lighting result.

enum class ShaderDebugType
{
    Lit = 0,           DiffuseColor,       SurfaceColor,
    UVCoords,          SurfaceTangentModelSpace,  SurfaceBitangentModelSpace,
    SurfaceNormalModelSpace,  NormalColor,  PixelNormalTBNSpace,
    PixelNormalWorldSpace,    Lighting,     SurfaceTangentWorldSpace,
    SurfaceBitangentWorldSpace, SurfaceNormalWorldSpace,
    ModelIBasisWorld,  ModelJBasisWorld,    ModelKBasisWorld
};
0:00
/0:00
Cycling through shader debug modes showing diffuse color, surface normals, TBN vectors, pixel normals in world space, and more

These debug views were essential during development for diagnosing normal mapping artifacts, verifying TBN matrix correctness, and validating the lighting model. The engine also provides higher-level DebugViewMode toggles (Lit, Unlit, Wireframe, Lighting-only) for quick visual checks.

OBJ Model Loader with TBN Correction

The OBJ loader parses .obj files into the engine’s Vertex_PCUTBN format (Position, Color, UV, Tangent, Bitangent, Normal). A key feature is its automatic deduction and correction of missing TBN components. If an OBJ file lacks tangent or bitangent data, the loader computes them from the existing normals and UV coordinates using cross-product reconstruction, ensuring every loaded model is ready for normal-mapped rendering without manual preprocessing.

OBJ Loaded Model
An OBJ model loaded into the engine with automatically computed TBN vectors, ready for Blinn-Phong shading
0:00
/0:00
OBJ model loader demonstrating automatic tangent/bitangent correction where missing TBN data is deduced from normals and UVs

Multiplayer Networking

The NetworkDispatcher implements a command-based multiplayer protocol over TCP. It supports multiple message boundary modes (NULL_TERMINATED, RAW_BYTES, LENGTH_PREFIXED) and handles message buffering across frames for incomplete packets. The server accepts up to 20 concurrent client connections.

sequenceDiagram
    participant CA as Client A
    participant S as Server
    participant CB as Client B

    CA->>S: Connect
    CB->>S: Connect
    CA->>S: MovePiece A2 A4
    S->>S: ExecuteCommand with remote flag
    S->>CB: Broadcast state update
    CB->>CB: ExecuteCommand with remote flag

Multiplayer commands flow through the engine’s dev console system. The NetworkDispatcher receives a raw string command, appends a remote=true flag, and executes it through the same console pipeline used for local commands. This unified approach means every game action (move piece, reset board, change camera) works identically in single-player and multiplayer without separate code paths.

Data-Driven Architecture

The entire game configuration lives in XML files under Run/Data/, making it possible to modify chess rules, board layouts, piece visuals, and server settings without recompiling. Three key definition files drive the system:

ChessPieceDefinition.xml defines each piece type with its glyph, movement behavior (slide flag), collision volume, and visual components:

<ChessPieceDefinition name="Knight" glyph="N" slide="false">
    <Collision radius="0.05" height="0.6"
               collidesWithWorld="true" collidesWithActors="false"/>
    <Components>
        <Component type="MeshComponent" bakeModel="Knight" renderLit="true"
                   shader="Data/Shaders/Diffuse"
                   texture="Data\Images\FunkyBricks_d.png"
                   normal="Data\Images\FunkyBricks_n.png"
                   specGlossEmit="Data\Images\FunkyBricks_sge.png"/>
    </Components>
</ChessPieceDefinition>

ChessMatchConfig.xml defines the board layout, faction colors, camera positions, and initial piece placement using standard chess notation:

<ChessBoard texture="Data\Images\Bricks_d.png" normal="Data\Images\Bricks_n.png"
            specGlossEmit="Data\Images\Bricks_sge.png" shader="Data/Shaders/Diffuse">
    <Factions>
        <Faction display="Player 0" id="0" color="161,40,35"
                 viewPosition="4,-1.5,4" viewOrientation="90,45,0"/>
        <Faction display="Player 1" id="1" color="85,110,28"
                 viewPosition="4,9.5,4" viewOrientation="-90,45,0"/>
    </Factions>
    <ChessPieces>
        <ChessPiece name="King" position="E1" faction="0"/>
        <!-- ... full board layout ... -->
    </ChessPieces>
</ChessBoard>

GameConfig.xml controls screen resolution, world dimensions, and debug settings.

System Architecture

graph TD
    subgraph Engine["Eurekiel Engine Core"]
        RS[RenderSubsystem]
        NS[NetworkSubsystem]
        WS[WidgetSubsystem]
        LS[LoggerSubsystem]
        IS[InputSystem]
        AS[AudioSystem]
    end

    subgraph Rendering["Rendering Pipeline"]
        BP[BlinnPhong Shader]
        PP[Post-Processing Chain]
        BL[EffectBloom]
        DM[Debug Modes]
        PP --> BL
    end

    subgraph GameLogic["Chess Game Logic"]
        CM[ChessMatch]
        CB[ChessBoard]
        CP[ChessPiece]
        CM --> CB
        CM --> CP
    end

    subgraph DataDriven["Data-Driven Layer"]
        PD[ChessPieceDefinition.xml]
        MC[ChessMatchConfig.xml]
        GC[GameConfig.xml]
    end

    subgraph ActorSystem["Actor-Component System"]
        AC[Actor]
        MC2[MeshComponent]
        CC[CollisionComponent]
        AC --> MC2
        AC --> CC
    end

    RS --> BP
    RS --> PP
    BP --> DM
    DataDriven --> GameLogic
    GameLogic --> ActorSystem
    ActorSystem --> RS
    NS --> CM
    IS --> CM

Design Philosophy

Several principles guided the architecture of Chess3D:

Data Over Code — Every tunable parameter lives in XML. Piece definitions, board layouts, faction colors, camera angles, and server settings are all externalized. This separation means gameplay iteration happens in a text editor, not a compiler — the same philosophy used by production engines to empower non-programmers.

Unified Command Pipeline — Local and remote actions share a single execution path through the dev console. A move command typed locally and one received over the network both flow through ExecuteCommand() with only a remote=true flag distinguishing them. This eliminates an entire class of desync bugs and keeps the multiplayer codebase minimal.

Debug-First Rendering — The 16+ shader debug modes were not an afterthought — they were built alongside the Blinn-Phong pipeline. Being able to visualize raw normals, tangent vectors, and UV coordinates in real time made it possible to diagnose and fix TBN issues in the OBJ loader without guesswork.

Defensive Model Loading — Rather than requiring artists to export perfect data, the OBJ loader assumes incomplete input and reconstructs what’s missing. If tangents or bitangents are absent, they’re computed from normals and UVs. This robustness means any valid OBJ file renders correctly with normal mapping on first import.

Component Composition — The Actor-Component system uses template-based type-safe queries (AddComponent<T>, GetComponent<T>) to compose behavior. Chess pieces, board tiles, and debug visualizers are all Actors with different component configurations — no deep inheritance hierarchies.