Gama C Library
Gama C API Documentation
mtl.h
Go to the documentation of this file.
1/**
2 * @file mtl.h
3 * @brief Defines structures for 3D materials and material libraries, and functions for loading MTL files.
4 *
5 * This file handles parsing and representing material data typically found
6 * in .mtl files accompanying .obj models. It includes material properties
7 * like colors, shininess, alpha, and references to texture maps.
8 */
9#pragma once
10
11#include "../color.h"
12#include "../image.h"
13#include "../str.h"
14#include "../utils.h"
15#include <ctype.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20/**
21 * @brief Represents a single 3D material with various rendering properties.
22 *
23 * This struct stores properties like diffuse color, specular color, shininess,
24 * transparency, and references to different texture maps.
25 */
26typedef struct {
27 char name[64]; /**< The name of the material. */
28 gmColor diffuse; /**< Diffuse color (Kd) of the material. */
29 gmColor specular; /**< Specular color (Ks) of the material. */
30 double shininess; /**< Shininess exponent (Ns), typically 0 to 1000. */
31 double alpha; /**< Transparency (d or Tr), 1.0 is opaque, 0.0 is fully transparent. */
32 gmColor emissive; /**< Emissive color (Ke) of the material. */
33
34 long tex_diffuse; /**< Index to the diffuse texture map in the material library's texture array (-1 if none). */
35 long tex_specular; /**< Index to the specular texture map (-1 if none). */
36 long tex_alpha; /**< Index to the alpha (dissolve) texture map (-1 if none). */
37 long tex_emissive; /**< Index to the emissive texture map (-1 if none). */
39
40/**
41 * @brief Represents a 3D texture, including its raw image data and file path.
42 */
43typedef struct {
44 gmImageData data; /**< The raw CPU-side image data for the texture. */
45 char path[256]; /**< The file path from which the texture was loaded. */
47
48/**
49 * @brief Represents a material library, typically loaded from an .mtl file.
50 *
51 * This struct contains a collection of materials and the textures they reference.
52 */
53typedef struct {
54 char name[256]; /**< The name of the material library (usually the MTL filename). */
55
56 gm3Material *materials; /**< Array of materials defined in this library. */
57 size_t n_materials; /**< Number of materials in the array. */
58
59 gm3Texture *textures; /**< Array of textures referenced by materials in this library. */
60 size_t n_textures; /**< Number of textures in the array. */
61} gm3MtlLib;
62
63/**
64 * @internal
65 * @brief Skips leading whitespace characters in a string.
66 * @param s The string to process.
67 * @return A pointer to the first non-whitespace character in the string.
68 */
69static inline char *gm3u_skip_spaces(char *s) {
70 while (*s && (*s == ' ' || *s == '\t'))
71 s++;
72 return s;
73}
74
75/**
76 * @brief Adds a texture to the material library, loading it if not already present.
77 *
78 * This function checks if a texture with the given path already exists in the
79 * `mtllib`. If so, it returns its index. Otherwise, it loads the image data,
80 * adds it to the `mtllib`'s texture array, and returns the new index.
81 *
82 * @param mtllib A pointer to the `gm3MtlLib` to add the texture to.
83 * @param path The file path of the texture to add.
84 * @return The index of the texture in the `mtllib->textures` array on success, -1 on failure.
85 */
86long gm3_mtl_add_texture(gm3MtlLib *mtllib, const char *path) {
87 for (size_t i = 0; i < mtllib->n_textures; i++)
88 if (0 == strcmp(path, mtllib->textures[i].path))
89 return i;
90 gm3Texture *tex =
91 realloc(mtllib->textures, sizeof(gm3Texture) * (mtllib->n_textures + 1));
92 if (!tex)
93 return -1;
94 mtllib->textures = tex;
95 tex = &mtllib->textures[mtllib->n_textures];
96 mtllib->n_textures++;
97
98 int ret = gm_image_data_load(&tex->data, path);
99 if (ret < 0)
100 return ret;
101 memset(tex->path, 0, sizeof(tex->path));
102 memcpy(tex->path, path, strlen(path));
103 return mtllib->n_textures - 1;
104}
105
106/**
107 * @brief Loads materials and textures from an OBJ-style .mtl file.
108 *
109 * This function parses the specified .mtl file, extracting material properties
110 * and loading any referenced textures.
111 *
112 * @param mtl_lib A pointer to the `gm3MtlLib` struct to populate with loaded data.
113 * @param path The file path to the .mtl file.
114 * @param dir The base directory for resolving relative texture paths.
115 * @return 0 on success, -1 on memory allocation failure, -2 if the file cannot be opened,
116 * or a negative value from `gm3_mtl_add_texture` on texture loading failure.
117 */
118int gm3_mtl_load(gm3MtlLib *mtl_lib, const char *path, const char *dir) {
119 mtl_lib->name[0] = '\0';
120 mtl_lib->materials = NULL;
121 mtl_lib->n_materials = 0;
122
123 FILE *f = fopen(path, "r");
124 if (!f) {
125 return -2;
126 }
127 gmu_get_filename_base(path, mtl_lib->name, sizeof(mtl_lib->name));
128 char line[512];
129 gm3Material *current = NULL;
130
131 while (fgets(line, sizeof(line), f)) {
132 char *p = gm3u_skip_spaces(line);
133 if (*p == '\0' || *p == '#')
134 continue;
135
136 if (strncmp(p, "newmtl ", 7) == 0) {
137 mtl_lib->n_materials++;
138 mtl_lib->materials = realloc(mtl_lib->materials,
139 sizeof(gm3Material) * mtl_lib->n_materials);
140 current = &mtl_lib->materials[mtl_lib->n_materials - 1];
141
142 // Initialize defaults
143 memset(current, 0, sizeof(gm3Material));
144 current->alpha = 1.0;
145 current->diffuse = gm_rgb(200, 200, 200);
146 sscanf(p + 7, "%63s", current->name);
147 } else if (current) {
148 if (strncmp(p, "Kd ", 3) == 0) {
149 float r, g, b;
150 sscanf(p + 3, "%f %f %f", &r, &g, &b);
151 current->diffuse =
152 gm_rgb((int)(r * 255), (int)(g * 255), (int)(b * 255));
153 } else if (strncmp(p, "Ks ", 3) == 0) {
154 float r, g, b;
155 sscanf(p + 3, "%f %f %f", &r, &g, &b);
156 current->specular =
157 gm_rgb((int)(r * 255), (int)(g * 255), (int)(b * 255));
158 } else if (strncmp(p, "Ns ", 3) == 0) {
159 current->shininess = atof(p + 3);
160 } else if (p[0] == 'd' && isspace(p[1])) {
161 current->alpha = atof(p + 2);
162 } else if (strncmp(p, "Tr ", 3) == 0) {
163 current->alpha = 1.0 - atof(p + 3); // Tr is transparency
164 } else if (strncmp(p, "map_Kd", 6) == 0) {
165 char tex_path[256] = {0};
166 gm3u_str_copy_eol(tex_path, gm3u_skip_spaces(p + 6), sizeof(tex_path));
167 char full_path[512];
168 snprintf(full_path, sizeof(full_path), "%s/%s", dir, tex_path);
169 long ret = gm3_mtl_add_texture(mtl_lib, full_path);
170 if (ret < 0) {
171 fclose(f);
172 return (int)ret;
173 }
174 current->tex_diffuse = ret;
175 } else if (strncmp(p, "map_Ks", 6) == 0) {
176 char tex_path[256] = {0};
177 gm3u_str_copy_eol(tex_path, gm3u_skip_spaces(p + 6), sizeof(tex_path));
178 char full_path[512];
179 snprintf(full_path, sizeof(full_path), "%s/%s", dir, tex_path);
180 long ret = gm3_mtl_add_texture(mtl_lib, full_path);
181 if (ret < 0) {
182 fclose(f);
183 return (int)ret;
184 }
185 current->tex_specular = ret;
186 } else if (strncmp(p, "map_Ke", 6) == 0) {
187 char tex_path[256] = {0};
188 gm3u_str_copy_eol(tex_path, gm3u_skip_spaces(p + 6), sizeof(tex_path));
189 char full_path[512];
190 snprintf(full_path, sizeof(full_path), "%s/%s", dir, tex_path);
191 long ret = gm3_mtl_add_texture(mtl_lib, full_path);
192 if (ret < 0) {
193 fclose(f);
194 return (int)ret;
195 }
196 current->tex_emissive = ret;
197 } else if (strncmp(p, "map_d", 5) == 0) {
198 char tex_path[256] = {0};
199 gm3u_str_copy_eol(tex_path, gm3u_skip_spaces(p + 5), sizeof(tex_path));
200 char full_path[512];
201 snprintf(full_path, sizeof(full_path), "%s/%s", dir, tex_path);
202 long ret = gm3_mtl_add_texture(mtl_lib, full_path);
203 if (ret < 0) {
204 fclose(f);
205 return (int)ret;
206 }
207 current->tex_alpha = ret;
208 }
209 }
210 }
211
212 fclose(f);
213 return 0;
214}
215
216/**
217 * @brief Finds a material by name within a loaded MTL file.
218 */
219gm3Material *gm3_mtl_find_mat(gm3MtlLib *file, const char *name, int *index) {
220
221 if (!file) {
222 if (index)
223 *index = -1;
224 return NULL;
225 }
226 if (index)
227 *index = -1;
228 for (size_t i = 0; i < file->n_materials; i++) {
229 if (strcmp(file->materials[i].name, name) == 0) {
230 if (index)
231 *index = i;
232 return &file->materials[i];
233 }
234 }
235 return NULL;
236}
237
239 if (file) {
240 if (file->materials)
241 free(file->materials);
242 free(file);
243 }
244}
uint32_t gmColor
Type definition for color values, stored as a 32-bit unsigned integer. The color components are packe...
Definition color.h:13
int32_t gm_image_data_load(gmImageData *data, const char *path)
Loads image file from disk into a gmImageData struct.
Definition image.h:29
void * realloc(void *ptr, size_t size)
Custom implementation of realloc for memory allocated by malloc (this custom version).
Definition malloc.h:236
void free(void *ptr)
Custom implementation of free for memory allocated by malloc (this custom version).
Definition malloc.h:189
gm3Material * gm3_mtl_find_mat(gm3MtlLib *file, const char *name, int *index)
Finds a material by name within a loaded MTL file.
Definition mtl.h:219
int gm3_mtl_load(gm3MtlLib *mtl_lib, const char *path, const char *dir)
Loads materials and textures from an OBJ-style .mtl file.
Definition mtl.h:118
void gm3_mtl_free(gm3MtlLib *file)
Definition mtl.h:238
long gm3_mtl_add_texture(gm3MtlLib *mtllib, const char *path)
Adds a texture to the material library, loading it if not already present.
Definition mtl.h:86
Defines a dynamic string structure and provides utility functions for its manipulation.
Represents a single 3D material with various rendering properties.
Definition mtl.h:26
long tex_diffuse
Definition mtl.h:34
long tex_emissive
Definition mtl.h:37
double alpha
Definition mtl.h:31
gmColor diffuse
Definition mtl.h:28
long tex_alpha
Definition mtl.h:36
gmColor emissive
Definition mtl.h:32
char name[64]
Definition mtl.h:27
gmColor specular
Definition mtl.h:29
double shininess
Definition mtl.h:30
long tex_specular
Definition mtl.h:35
Represents a material library, typically loaded from an .mtl file.
Definition mtl.h:53
size_t n_textures
Definition mtl.h:60
gm3Material * materials
Definition mtl.h:56
char name[256]
Definition mtl.h:54
size_t n_materials
Definition mtl.h:57
gm3Texture * textures
Definition mtl.h:59
Represents a 3D texture, including its raw image data and file path.
Definition mtl.h:43
gmImageData data
Definition mtl.h:44
char path[256]
Definition mtl.h:45
A container for raw, CPU-side image pixel data.
Definition image.h:18
Provides general utility functions for file handling and string manipulation.
void gmu_get_filename_base(const char *path, char *out_base, size_t out_size)
Extracts the base filename (filename with extension) from a full path.
Definition utils.h:24