- C 96.1%
- Makefile 3.9%
| 0_Libft_Gnl | ||
| 1_Srcs | ||
| demo_maps | ||
| minilibx-linux@b8de9b4118 | ||
| .gitignore | ||
| .gitmodules | ||
| fil_de_fer.h | ||
| Makefile | ||
| program.c | ||
| README.md | ||
This project has been created as part of the 42 curriculum by clao.
Description
This project is about building the fdf program, creating a simple wireframe model representation of a 3D landscape by linking coordinates (x, y, z) through line segments.
Fil de Fer
The fdf program opens a file of type .fdf with map coordinates written in a specific format, after parsing the coordinates, the program opens a window visualising the map in isometric projection.
User can also use a set of keyboard controls to slightly manipulate the map.
The MiniLibX library is used, a simple programming API designed for those beginning in X-Window programming in C.
Rendering
The coordinates of a landscape are stored in a .fdf file, provided as a command line parameter to the fdf program. Here is an example:
$>cat demo_maps/basictest.fdf
0 0 1 2 3 4 5 6 7 8 9
0 0 0 1 2 3 4 5 6 7 8
0 0 0 0 1 2 3 4 5 6 7
0 0 0 0 0 1 2 3 4 5 6
0 0 0 0 0 0 1 2 3 4 5
0 0 0 0 0 0 0 1 2 3 4
0 0 0 0 0 0 0 0 1 2 3
0 0 0 0 0 0 0 0 0 1 2
0 0 0 0 0 0 0 0 0 0 1
Each number represents a coordinate of the map:
- The horizontal position corresponds to its abscissa (
x). - The vertical position corresponds to its ordinate (
y). - The value corresponds to its altitude (
z).
A number can also be followed by a hexadecimal value that represents the color of the corresponding coordinate. For example, 20,0xFF0000 represents a red point of altitude 20.
In order for the map values to be processed by the fdf program, each coordinate's data should be separated by space (' '), and in each data, its altitude value and color value should be separated by a single comma (',').
Instructions
Making the fdf program:
This project uses MiniLibX as a git submodule. Clone the repo with:
git clone --recurse-submodules https://git.ivanlcl.com/ivan/fil-de-fer.git
Or if already cloned:
git submodule update --init
MiniLibX also requires: libXext, libX11, zlib.
Then build:
make
Using the fdf program:
./fdf <the_map_file.fdf>
Keyboard controls:
W,S,A,D: to shift the map's view point.T: to change the view to (top view) parallel projection.I: to change the view to isometric projection.J,K: to zoom in/out the view of the map.H,L: to rotate the view point around the map'szaxis.Q,E: to change thezvalues of the map.
Only one single key works at the same time.
Approach
Isometric projection
In a map represented in isometric projection, each of the three axes x, y, z are separated by 120°, where z axis goes straight up, x axis goes to the right and downward by 30°, y goes to the left and downward by 30°.
- For each step of
x, the projected point moves to the right bycos(30°)and downward bysin(30°). - For each step of
y, the projected point moves to the left bycos(30°)and downward bysin(30°). - For each step of
z, the projected point moves upward by1if positive, downward by1if negative.
If a coordinate is (x, 0, 0):
- The projected point appears at
(x * cos(30°), x * sin(30°))
If a coordinate is (0, y, 0):
- The projected point appears at
(y * -cos(30°), y * sin(30°))
If a coordinate is (x, y, z):
- The projected point's
xvalue equals to(x - y) * cos(30°) - The projected point's
yvalue equals to(x + y) * sin(30°) - z
By subtracting z from y, each projected point can move up or down on the z axis according to the coordinate's z value.
At the end, projection is about computing the 3D coordinates into 2D, it simulates a 3D view on a 2D screen.
Scaling the map
To ensure that every map looks "natural" and fits pleasantly within the window upon launch, the program calculates dynamic scaling factors.
- To prevent high altitudes from over-stretching, altitude values are scaled based on the dimensions of the map and the range between the highest and lowest
zpoints. A reverse proportion is implemented using a gentle logarithmic decay curve. - Before drawing, the program simulates the projected coordinates of the map's four extreme corners. These corners are used to determine a proper zoom factor that keeps the map within the window boundaries (minus a defined padding).
- The program then calculates the necessary
xandyoffsets to center the map on the window.
Coloring the line segments
If the color values of two coordinates a and b are different, each pixel plotted between the two points has its own color value to represent a gradient.
- Each pixel's color value is calculated according to its position between
aandb: closer tobthe pixel, more ofb's color fades in, whilea's color fades out. - A color is visualized through the intensity of three lights: red, green, and blue. Multiplying each light's value to a percentage determines how much the color fades in or out.
If there is no color value provided by the map file, the fdf program renders the map with the default colors defined in the fil_de_fer.h header file.
- Coordinates with altitude values close to zero are rendered close to the
MIDcolor (cyan by default). - Coordinates with altitude values above zero and close to the maximum altitude value of the map are rendered close to the
TOPcolor (white by default). - Coordinates with altitude values below zero and close to the minimum altitude value of the map are rendered close to the
LOWcolor (blue by default).
Optimization
A few optimizations are implemented to smoothen the performance, especially when using interactive controls.
- The program uses two separate MiniLibX images to prevent screen flickering when the map is updated during an interaction. The next frame is fully drawn on the off-screen image buffer before being put to the window. Images are swapped for each update.
- Instead of calculating the isometric and rotation trigonometry for every single coordinate during the drawing loop, these values are precalculated once before each rendering and reused.
- Before drawing a line segment, the program checks if both the points
aandbare outside the window boundaries. If so, the line is ignored. Likewise, only pixels that pass the bounds-check are written to the image memory address.
Resources
AI (Gemini) was used during development as a learning assistant for:
- Clarifying MiniLibX mechanics, concept of image buffers.
- Project structuring, by breaking down the project into manageable "baby steps".
- Understanding the underlying math of isometric projection.
- Discussing map scaling strategies.
- Explaining the concept behind RGB color and gradient.
- Suggestions for performance optimization.
No final code was copy-pasted as-is from AI or the references: all implementations were written, adapted, and tested by me.
References
- Introduction to the minilibX: a simple X-Window programming API in C
- Understanding the minilibX
- 42 fdf: showcase
- Fil de Fer(Fdf), the first graphical project at 42
- Getting started with the minilibx
- Pixel drawing with the minilibx
- MiniLibX | 42 Docs
- MiniLibX: Events | 42 Docs
- $Xorg: keysymdef.h
- Bresenham's line algorithm - Wikipedia
- Bresenham's Line Algorithm - Demystified Step by Step
- Graph a circle in standard form
- Isometric Projection in Game Development
- How Isometric Coordinates Work in 2D games
- Unit Circle
- Convert Degrees to Radians in C
- Rotation matrix - Wikipedia