Skip to main content

Overview

Cub3D uses XPM (X PixMap) textures to render realistic walls. Textures are loaded via the MiniLibX library and mapped onto wall surfaces based on the ray intersection point.

XPM Texture Format

What is XPM?

XPM (X PixMap) is an image file format that represents images as C source code. It’s human-readable and was designed for the X Window System. MiniLibX natively supports this format.

Available Textures

The Cub3D project includes several texture files:

DOOM Textures

  • doom.xpm
  • doom2.xpm
  • doom3.xpm
  • doom4.xpm

Custom Textures

  • eagle.xpm
  • pizza.xpm
  • gang.xpm
  • blen_textura.xpm

Stone Textures

  • greystone.xpm
  • purplestone.xpm

Misc Textures

  • puro_c.xpm

Texture Data Structure

Textures are stored in the t_texture structure:
cub3.h:68-77
typedef struct s_texture
{
    void	*img_ptr;
    char	*data;
    int		width;
    int		height;
    int		bpp;
    int		line_len;
    int		endian;
}	t_texture;
FieldTypeDescription
img_ptrvoid*MiniLibX image pointer
datachar*Pixel data buffer
widthintTexture width in pixels
heightintTexture height in pixels
bppintBits per pixel
line_lenintBytes per row
endianintEndianness (0=little, 1=big)

Loading Textures

Single Texture Loading

Textures are loaded using MiniLibX’s mlx_xpm_file_to_image function:
main.c:29-41
void	load_texture(t_map *mapa, t_texture *tex, char *path)
{
	tex->img_ptr = mlx_xpm_file_to_image(mapa->mlx_ptr, path,
			&tex->width, &tex->height);
	if (!tex->img_ptr)
	{
		perror("Error cargando textura");
		freeall(mapa);
		exit(1);
	}
	tex->data = mlx_get_data_addr(tex->img_ptr, &tex->bpp,
			&tex->line_len, &tex->endian);
}
1

Load image from file

mlx_xpm_file_to_image reads the XPM file and returns an image pointer. The width and height are automatically filled.
2

Error handling

If loading fails, the program exits with an error message after freeing resources.
3

Get pixel data

mlx_get_data_addr retrieves the raw pixel buffer and metadata needed for direct pixel access.

Loading All Textures

All four directional textures are loaded during initialization:
main.c:43-49
void	load_all_textures(t_map *mapa)
{
	load_texture(mapa, &mapa->tex_north, mapa->northtexture);
	load_texture(mapa, &mapa->tex_south, mapa->southtexture);
	load_texture(mapa, &mapa->tex_east, mapa->easttexture);
	load_texture(mapa, &mapa->tex_west, mapa->westtexture);
}
This is called during game initialization:
main.c:51-72
void	cubed_init(t_map *mapa)
{
	// ... initialization code ...
	mapa->mlx_ptr = mlx_init();
	load_all_textures(mapa);
	// ... window creation ...
}

Texture Selection

The correct texture is selected based on which side of the wall was hit:
raycasting.c:84-100
void	select_texture(t_map *mapa)
{
	if (mapa->side == 0)
	{
		if (mapa->ray_dir_x > 0)
			mapa->current_tex = &mapa->tex_east;
		else
			mapa->current_tex = &mapa->tex_west;
	}
	else
	{
		if (mapa->ray_dir_y > 0)
			mapa->current_tex = &mapa->tex_south;
		else
			mapa->current_tex = &mapa->tex_north;
	}
}
  • side == 0 means the ray hit a vertical wall (East or West)
  • side == 1 means the ray hit a horizontal wall (North or South)
  • The ray direction determines which specific texture to use

Texture Mapping

X-Coordinate Mapping

The texture’s X coordinate is determined by where exactly the ray hit the wall:
drawing.c:35-44
void	draw_wall(int x, int start_y, int end_y, t_map *mapa)
{
	double		wall_x;
	int			tex_x;

	wall_x = mapa->wall_x - floor(mapa->wall_x);
	tex_x = (int)(wall_x * mapa->current_tex->width);
	if ((mapa->side == 0 && mapa->ray_dir_x < 0)
		|| (mapa->side == 1 && mapa->ray_dir_y > 0))
		tex_x = mapa->current_tex->width - tex_x - 1;
  • wall_x is the fractional part of the hit position (0.0 to 1.0)
  • This is multiplied by texture width to get the pixel column
  • The texture is flipped for certain wall orientations to maintain consistency

Y-Coordinate Mapping

The texture’s Y coordinate is calculated for each pixel based on screen position:
drawing.c:15-33
void	calculate_and_draw_pixel(t_map *mapa, int start_y, int tex_x, int x)
{
	int		offset;
	int		color;
	int		tex_y;

	mapa->d = start_y * 256 - HEIGHT * 128 + mapa->line_height * 128;
	tex_y = (mapa->d * mapa->current_tex->height) / mapa->line_height / 256;
	if (tex_x < 0 || tex_x >= mapa->current_tex->width || tex_y < 0
		|| tex_y >= mapa->current_tex->height)
		return ;
	offset = tex_y * mapa->current_tex->line_len + tex_x
		* (mapa->current_tex->bpp / 8);
	if (offset < 0 || offset + 3 >= mapa->current_tex->height
		* mapa->current_tex->line_len)
		return ;
	color = *(int *)(mapa->current_tex->data + offset);
	put_pixel(x, start_y, color, mapa);
}
1

Calculate texture Y coordinate

Uses a scaled calculation to map screen Y position to texture Y coordinate. The * 256 is for fixed-point arithmetic precision.
2

Bounds checking

Validates that texture coordinates are within the texture dimensions to prevent buffer overflows.
3

Calculate buffer offset

Computes the byte offset in the texture data: row * line_length + column * bytes_per_pixel
4

Read and draw pixel

Reads the color from the texture buffer and draws it to the screen.

Drawing Textured Walls

The complete wall drawing process:
drawing.c:52-66
void	draw_3d_dda(int x, int start_y, int end_y, t_map *mapa)
{
	int	y;

	y = -1;
	while (++y < start_y)
		put_pixel(x, y, 0x87CEEB, mapa);
	draw_wall (x, start_y, end_y, mapa);
	y = end_y;
	while (y < HEIGHT)
	{
		put_pixel(x, y, 0x444444, mapa);
		y++;
	}
}
For each vertical line:
  1. Draw ceiling (above wall) - uses hardcoded color 0x87CEEB (sky blue)
  2. Draw textured wall section
  3. Draw floor (below wall) - uses hardcoded color 0x444444 (dark grey)
In this implementation, floor and ceiling colors are hardcoded in the rendering function rather than using the parsed map colors. To use the parsed colors, replace 0x87CEEB with mapa->ceiling_color and 0x444444 with mapa->floor_color.

Texture Storage in t_map

The main map structure stores all texture information:
cub3.h:156-161
typedef struct s_map
{
    // ...
    t_texture	tex_north;
    t_texture	tex_south;
    t_texture	tex_east;
    t_texture	tex_west;
    t_texture	*current_tex;
}		t_map;
  • Four texture structures store the directional textures
  • current_tex is a pointer to the currently selected texture during rendering

Memory Management

Textures must be properly freed when the program exits:
cub3.h:168
void		freetextures(t_map *map);
This should destroy all texture image pointers using mlx_destroy_image to prevent memory leaks.

Performance Considerations

Direct Memory Access

Pixel data is accessed directly via the data pointer for fast rendering without function call overhead.

Bounds Checking

All texture coordinate accesses include bounds checking to prevent crashes from invalid coordinates.

Fixed-Point Math

The texture mapping uses fixed-point arithmetic (* 256) for faster integer-based calculations.

Single Texture Load

Textures are loaded once at startup and reused throughout the game for efficiency.

Next Steps

Rendering

Learn how the complete frame rendering works

Map Format

Understand how textures are defined in map files