naev 0.10.4
object.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
11#include <assert.h>
12#include <libgen.h>
13#include <stddef.h>
14#include <string.h>
15#include "SDL_image.h"
16
17#include "naev.h"
20#include "object.h"
21
22#include "array.h"
23#include "camera.h"
24#include "gui.h"
25#include "log.h"
26#include "ndata.h"
27
28#if defined(_WIN32) || defined(_WIN64)
29# define strtok_r strtok_s
30#endif
31
32#define DELIM " \t\n"
33#define NAEV_ORTHO_SCALE 10.
34#define NAEV_ORTHO_DIST 9.*M_SQRT2
36typedef struct Material_ {
37 char *name;
38 GLfloat Ka[3], Kd[3], Ks[3], Ke[3];
39 GLfloat Ns, Ni, d, bm;
40 glTexture *map_Kd, *map_Ks, *map_Ke, *map_Bump;
41} Material;
42
43typedef struct Mesh_ {
44 char *name;
45 gl_vbo *vbo;
46 int num_corners;
47 int material;
48} Mesh;
49
50typedef struct Object_ {
51 Mesh *meshes;
52 Material *materials;
53 GLfloat radius;
54} Object;
55
56typedef struct {
57 GLfloat ver[3];
58 GLfloat tex[2];
59 GLfloat nor[3];
60} Vertex;
61
62static glTexture *zeroTexture = NULL;
63static glTexture *oneTexture = NULL;
64static unsigned int emptyTextureRefs= 0;
65
66static void mesh_create( Mesh **meshes, const char* name,
67 Vertex *corners, int material )
68{
69 if (array_size(corners) == 0)
70 return;
71 if (name == NULL)
72 ERR(_("No name for current part"));
73 if (material == -1)
74 ERR(_("No material for current part"));
75
76 Mesh *mesh = &array_grow(meshes);
77 mesh->name = strdup(name);
78 mesh->vbo = gl_vboCreateStatic(array_size(corners) * sizeof(Vertex), corners);
79 mesh->num_corners = array_size(corners);
80 mesh->material = material;
81 array_resize(&corners, 0);
82}
83
84static int readGLfloat( GLfloat *dest, int how_many, char **saveptr )
85{
86 char *token;
87 int num = 0;
88
89 while ((token = strtok_r(NULL, DELIM, saveptr)) != NULL) {
90 double d;
91 sscanf(token, "%lf", &d);
92 assert(num <= how_many);
93 dest[num++] = d;
94 }
95
96 assert(num == how_many);
97 return num;
98}
99
100static int readGLmaterial( GLfloat col[3], char **saveptr )
101{
102 int ret = readGLfloat( col, 3, saveptr );
103 /*
104 * Not strictly colours, so we ignore gamma, although this might not be the best idea.
105 * TODO Probably should look at what blender expects us to do.
106 for (int i=0; i<3; i++)
107 col[i] = gammaToLinear( col[i] );
108 */
109 return ret;
110}
111
112static void materials_readFromFile( const char *filename, Material **materials )
113{
114 char *filebuf, *filesaveptr, *line;
115 size_t filesize;
116 DEBUG(_("Loading material from %s"), filename);
117
118 filebuf = ndata_read( filename, &filesize );
119 if (filebuf == NULL)
120 ERR(_("Cannot open object file %s"), filename);
121
122 Material *curr = &array_back(*materials);
123
124 line = strtok_r(filebuf, "\n", &filesaveptr);
125 while (line != NULL) {
126 const char *token;
127 char *saveptr, *copy_filename, *texture_filename;
128 token = strtok_r(line, DELIM, &saveptr);
129
130 if (token == NULL) {
131 /* Missing */
132 } else if (strcmp(token, "newmtl") == 0) {
133 token = strtok_r(NULL, DELIM, &saveptr);
134 curr = &array_grow(materials);
135 curr->name = strdup(token);
136 curr->Ni = 0.;
137 curr->Ns = 0.;
138 curr->d = 1.;
139 curr->bm = 0.;
140 curr->map_Kd = curr->map_Ks = curr->map_Ke = curr->map_Bump = NULL;
141 DEBUG(_("Reading new material %s"), curr->name);
142 } else if (strcmp(token, "Ns") == 0) {
143 readGLfloat(&curr->Ns, 1, &saveptr);
144 } else if (strcmp(token, "Ni") == 0) {
145 readGLfloat(&curr->Ni, 1, &saveptr);
146 } else if (strcmp(token, "d") == 0) {
147 readGLfloat(&curr->d, 1, &saveptr);
148 } else if (strcmp(token, "Ka") == 0) {
149 readGLmaterial( curr->Ka, &saveptr );
150 } else if (strcmp(token, "Kd") == 0) {
151 readGLmaterial( curr->Kd, &saveptr );
152 } else if (strcmp(token, "Ks") == 0) {
153 readGLmaterial( curr->Ks, &saveptr );
154 } else if (strcmp(token, "Ke") == 0) {
155 readGLmaterial( curr->Ke, &saveptr );
156 } else if (strncmp(token, "map_", 4) == 0) {
157 glTexture **map = NULL;
158 if (strcmp(token, "map_Kd") == 0)
159 map = &curr->map_Kd;
160 else if (strcmp(token, "map_Ks") == 0)
161 map = &curr->map_Ks;
162 else if (strcmp(token, "map_Ke") == 0)
163 map = &curr->map_Ke;
164 else if (strcmp(token, "map_Bump") == 0)
165 map = &curr->map_Bump;
166 else
167 LOG(_("Can't understand token %s"), token);
168 /* Note: we can't tokenize the command line here; options may be follwed by a filename containing whitespace chars. */
169 if (map != NULL) {
170 char *args = strtok_r(NULL, "\n", &saveptr), *endp;
171 while (1) {
172 while (isspace(*args))
173 args++;
174 if (strncmp(args, "-bm", 3) == 0 && isspace(args[3])) {
175 args += 3;
176 curr->bm = strtof(args, &endp);
177 assert("Bad -bm argument" && endp != args);
178 args = endp;
179 }
180 else if (strncmp(args, "-s", 2) == 0 && isspace(args[2])) {
181 endp = args + 2;
182 LOG(_("-s (texture scaling) option ignored for %s"), token);
183 do {
184 args = endp;
185 (void) strtof(args, &endp);
186 }
187 while (endp != args);
188 }
189 else if (args[0] == '-')
190 ERR(_("Options not supported for %s"), token);
191 else
192 break;
193 }
194
195 /* computes the path to texture */
196 copy_filename = strdup(filename);
197 asprintf(&texture_filename, "%s/%s", dirname(copy_filename), args);
198 *map = gl_newImage(texture_filename, 0);
199 free(copy_filename);
200 free(texture_filename);
201 }
202 } else if (strcmp(token, "Ke") == 0 || strcmp(token, "illum") == 0) {
203 /* Ignore commands: [e]missive coefficient, illumination mode */
204 } else if (token[0] == '#') {
205 /* Comment */
206 } else {
207 LOG(_("Can't understand token %s"), token);
208 }
209
210 line = strtok_r(NULL, "\n", &filesaveptr);
211 }
212
213 free(filebuf);
214}
215
225Object *object_loadFromFile( const char *filename )
226{
227 GLfloat *v;
228 GLfloat *vertex = array_create(GLfloat);
229 GLfloat *texture = array_create(GLfloat);
230 GLfloat *normal = array_create(GLfloat);
231 Vertex *corners = array_create(Vertex);
232 char *filebuf, *filesaveptr, *line;
233 size_t filesize;
234
235 filebuf = ndata_read( filename, &filesize );
236 if (filebuf == NULL)
237 ERR(_("Cannot open object file %s"), filename);
238 DEBUG(_("Loading object file %s"), filename);
239
240 char *name = NULL;
241 int material = -1;
242
243 if (emptyTextureRefs++ == 0) {
244 float zero[]= {0., 0., 0., 0.};
245 float one[] = {1., 1., 1., 1.};
246 zeroTexture = gl_loadImageData( zero, 1, 1, 1, 1, "solid_zero" );
247 oneTexture = gl_loadImageData( one, 1, 1, 1, 1, "solid_white" );
248 }
249
250 Object *object = calloc(1, sizeof(Object));
251 object->meshes = array_create(Mesh);
252 object->materials = array_create(Material);
253
254 line = strtok_r(filebuf, "\n", &filesaveptr);
255 while (line != NULL) {
256 const char *token;
257 char *saveptr, *copy_filename, *material_filename;
258 token = strtok_r(line, DELIM, &saveptr);
259
260 if (token == NULL) {
261 /* Missing */
262 } else if (strcmp(token, "mtllib") == 0) {
263 while ((token = strtok_r(NULL, DELIM, &saveptr)) != NULL) {
264 /* computes the path to materials */
265 copy_filename = strdup(filename);
266 asprintf(&material_filename, "%s/%s", dirname(copy_filename), token);
267 materials_readFromFile(material_filename, &object->materials);
268 free(copy_filename);
269 free(material_filename);
270 }
271 } else if (strcmp(token, "o") == 0) {
272 mesh_create(&object->meshes, name, corners, material);
273 token = strtok_r(NULL, DELIM, &saveptr);
274 free(name), name = strdup(token);
275 } else if (strcmp(token, "v") == 0) {
276 (void)array_grow(&vertex);
277 (void)array_grow(&vertex);
278 (void)array_grow(&vertex);
279 readGLfloat(array_end(vertex) - 3, 3, &saveptr);
280 } else if (strcmp(token, "vt") == 0) {
281 (void)array_grow(&texture);
282 (void)array_grow(&texture);
283 readGLfloat(array_end(texture) - 2, 2, &saveptr);
284 } else if (strcmp(token, "vn") == 0) {
285 (void)array_grow(&normal);
286 (void)array_grow(&normal);
287 (void)array_grow(&normal);
288 readGLfloat(array_end(normal) - 3, 3, &saveptr);
289 } else if (strcmp(token, "f") == 0) {
290 int num = 0;
291 while ((token = strtok_r(NULL, DELIM, &saveptr)) != NULL) {
292 int i_v = 0, i_t = 0, i_n = 0;
293 if (sscanf(token, "%d//%d", &i_v, &i_n) < 2)
294 sscanf(token, "%d/%d/%d", &i_v, &i_t, &i_n);
295
296 assert("Vertex index out of range." && (0 < i_v && i_v <= array_size(vertex) / 3));
297 assert("Texture index out of range." && (0 <= i_t && i_t <= array_size(texture) / 2));
298 assert("Normal index out of range." && (0 < i_n && i_n <= array_size(normal) / 3));
299
300 Vertex *face = &array_grow(&corners);
301 --i_v, --i_t, --i_n;
302 memcpy(face->ver, vertex + i_v * 3, sizeof(GLfloat) * 3);
303 if (i_t >= 0)
304 memcpy(face->tex, texture + i_t * 2, sizeof(GLfloat) * 2);
305 else
306 memset(face->tex, 0, sizeof(GLfloat) * 2);
307 memcpy(face->nor, normal + i_n * 3, sizeof(GLfloat) * 3);
308 ++num;
309 }
310
311 assert("Too few or too many vertices for a face." && (num == 3));
312 } else if (strcmp(token, "usemtl") == 0) {
313 mesh_create(&object->meshes, name, corners, material);
314
315 /* a new mesh with the same name */
316 token = strtok_r(NULL, DELIM, &saveptr);
317 for (material = 0; material < array_size(object->materials); ++material)
318 if (strcmp(token, object->materials[material].name) == 0)
319 break;
320
321 if (material == array_size(object->materials))
322 ERR(_("No such material %s"), token);
323 } else if (token[0] == '#') {
324 /* Comment */
325 } else if (strcmp(token, "l") == 0 || strcmp(token, "s") == 0) {
326 /* Ignore commands: line, smoothing */
327 } else {
328 /* TODO Ignore s (smoothing), l (line) with no regrets? */
329 LOG(_("Can't understand token %s"), token);
330 }
331
332 line = strtok_r(NULL, "\n", &filesaveptr);
333 }
334
335 mesh_create(&object->meshes, name, corners, material);
336 free(name);
337
338 /* Calculate maximum mesh size (from center). */
339 for (int i=0; i<array_size(corners); i++) {
340 v = corners[i].ver;
341 object->radius = MAX( object->radius, v[0]*v[0]+v[1]*v[1]+v[2]*v[2] );
342 }
343 object->radius = sqrt( object->radius );
344
345 /* cleans up */
346 free(filebuf);
347 array_free(vertex);
348 array_free(texture);
349 array_free(normal);
350 array_free(corners);
351
352 return object;
353}
354
358void object_free( Object *object )
359{
360 if (object == NULL)
361 return;
362
363 for (int i=0; i < array_size(object->materials); ++i) {
364 Material *material = &object->materials[i];
365 free(material->name);
366 gl_freeTexture(material->map_Kd);
367 gl_freeTexture(material->map_Ke);
368 gl_freeTexture(material->map_Ks);
369 gl_freeTexture(material->map_Bump);
370 }
371
372 for (int i=0; i < array_size(object->meshes); ++i) {
373 Mesh *mesh = &object->meshes[i];
374 free(mesh->name);
375 gl_vboDestroy(mesh->vbo);
376 }
377
378 array_free(object->meshes);
379 array_free(object->materials);
380
381 if (--emptyTextureRefs == 0) {
382 gl_freeTexture(zeroTexture);
383 gl_freeTexture(oneTexture);
384 zeroTexture = oneTexture = NULL;
385 }
386
387 free(object);
388}
389
390static void object_renderMesh( const Object *object, int part, GLfloat alpha )
391{
392 const Mesh *mesh = &object->meshes[part];
393
394 /* computes relative addresses of the vertice and texture coords */
395 const int ver_offset = offsetof(Vertex, ver);
396 const int tex_offset = offsetof(Vertex, tex);
397 const int nor_offset = offsetof(Vertex, nor);
398
399 /* activates vertices and texture coords */
400 glEnableVertexAttribArray(shaders.material.vertex);
401 gl_vboActivateAttribOffset(mesh->vbo, shaders.material.vertex, ver_offset, 3, GL_FLOAT, sizeof(Vertex));
402 glEnableVertexAttribArray(shaders.material.vertex_tex);
403 gl_vboActivateAttribOffset(mesh->vbo, shaders.material.vertex_tex, tex_offset, 2, GL_FLOAT, sizeof(Vertex));
404 glEnableVertexAttribArray(shaders.material.vertex_normal);
405 gl_vboActivateAttribOffset(mesh->vbo, shaders.material.vertex_normal, nor_offset, 3, GL_FLOAT, sizeof(Vertex));
406
407 /* Set material */
408 assert("Part has no material" && (mesh->material != -1));
409 Material *material = object->materials + mesh->material;
410 //material->Kd[3] = alpha;
411
412 glUniform1f(shaders.material.Ns, material->Ns);
413 glUniform3f(shaders.material.Ka, material->Ka[0], material->Ka[1], material->Ka[2] );
414 glUniform3f(shaders.material.Kd, material->Kd[0], material->Kd[1], material->Kd[2] );
415 glUniform3f(shaders.material.Ks, material->Ks[0], material->Ks[1], material->Ks[2] );
416 glUniform3f(shaders.material.Ke, material->Ke[0], material->Ke[1], material->Ke[2] );
417 glUniform1f(shaders.material.Ni, material->Ni);
418 glUniform1f(shaders.material.d, material->d * alpha);
419 glUniform1f(shaders.material.bm, material->bm);
420
421 /* binds textures */
422 glUniform1i(shaders.material.map_Kd, 0);
423 glUniform1i(shaders.material.map_Ks, 1);
424 glUniform1i(shaders.material.map_Ke, 2);
425 glUniform1i(shaders.material.map_Bump, 3);
426 glActiveTexture(GL_TEXTURE3);
427 glBindTexture(GL_TEXTURE_2D, material->map_Bump == NULL ? zeroTexture->texture : material->map_Bump->texture);
428 glActiveTexture(GL_TEXTURE2);
429 glBindTexture(GL_TEXTURE_2D, material->map_Ke == NULL ? oneTexture->texture : material->map_Ke->texture);
430 glActiveTexture(GL_TEXTURE1);
431 glBindTexture(GL_TEXTURE_2D, material->map_Ks == NULL ? oneTexture->texture : material->map_Ks->texture);
432 /* Need TEXTURE0 to be last. */
433 glActiveTexture(GL_TEXTURE0);
434 glBindTexture(GL_TEXTURE_2D, material->map_Kd == NULL ? oneTexture->texture : material->map_Kd->texture);
435
436 glDrawArrays(GL_TRIANGLES, 0, mesh->num_corners);
437}
438
439void object_renderSolidPart( const Object *object, const Solid *solid, const char *part_name, GLfloat alpha, double scale )
440{
441 mat4 view, projection, model, ortho;
442 const GLfloat od = NAEV_ORTHO_DIST;
443 const GLfloat os = NAEV_ORTHO_SCALE / scale;
444 double x, y; //, r;
445
446 x = solid->pos.x;
447 y = solid->pos.y;
448 //r = object->radius * scale;
449
450 /*
451 // TODO fix this check to avoid rendering when not necessary
452 if ((x+r < 0.) || (x-r > SCREEN_W) ||
453 (y+r < 0.) || (y-r > SCREEN_H))
454 return;
455 */
456
457 glUseProgram(shaders.material.program);
458
459 projection = gl_gameToScreenMatrix(gl_view_matrix);
460 mat4_translate( &projection, x, y, 0. );
461 ortho = mat4_ortho(-os, os, -os, os, od, -od);
462 mat4_mul( &view, &projection, &ortho );
463 //projection = mat4_rotate(projection, M_PI/4., 1., 0., 0.);
464
465 model = mat4_identity();
466 mat4_rotate( &model, M_PI/2. + solid->dir, 0., 1., 0.);
467
468 gl_uniformMat4(shaders.material.projection, &view);
469 gl_uniformMat4(shaders.material.model, &model);
470
471 /* Actually need depth testing now. */
472 glEnable(GL_DEPTH_TEST);
473 glDepthFunc(GL_LESS);
474 glClear( GL_DEPTH_BUFFER_BIT );
475
476 for (int i=0; i < array_size(object->meshes); ++i)
477 if (strcmp(part_name, object->meshes[i].name) == 0)
478 object_renderMesh(object, i, alpha);
479
480 /* Restore defaults. */
481 glDisable(GL_DEPTH_TEST);
482 glUseProgram(0);
483 gl_checkErr();
484}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition: array.h:158
#define array_end(array)
Returns a pointer to the end of the reserved memory space.
Definition: array.h:202
#define array_resize(ptr_array, new_size)
Resizes the array to accomodate new_size elements.
Definition: array.h:112
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition: array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition: array.h:119
#define array_back(ptr_array)
Returns the last element in the array.
Definition: array.h:216
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition: array.h:93
void mat4_translate(mat4 *m, double x, double y, double z)
Translates a homogenous transformation matrix.
Definition: mat4.c:99
mat4 mat4_identity(void)
Creates an identity matrix.
Definition: mat4.c:195
void mat4_rotate(mat4 *m, double angle, double x, double y, double z)
Multiplies the given matrix by a rotation. (Follows the right-hand rule.)
Definition: mat4.c:159
void mat4_mul(mat4 *out, const mat4 *m1, const mat4 *m2)
Multiplies two matrices (out = m1 * m2).
Definition: mat4.c:35
mat4 mat4_ortho(double left, double right, double bottom, double top, double nearVal, double farVal)
Creates an orthographic projection matrix.
Definition: mat4.c:209
Header file with generic functions and naev-specifics.
#define MAX(x, y)
Definition: naev.h:39
void * ndata_read(const char *path, size_t *filesize)
Reads a file from the ndata (will be NUL terminated).
Definition: ndata.c:154
int asprintf(char **strp, const char *fmt,...)
Like sprintf(), but it allocates a large-enough string and returns the pointer in the first argument....
Definition: nstring.c:161
void object_free(Object *object)
Frees memory reserved for the object.
Definition: object.c:358
#define NAEV_ORTHO_SCALE
Definition: object.c:33
#define NAEV_ORTHO_DIST
Definition: object.c:34
Object * object_loadFromFile(const char *filename)
Loads object.
Definition: object.c:225
mat4 gl_gameToScreenMatrix(mat4 lhs)
Return a transformation which converts in-game coordinates to screen coordinates.
glTexture * gl_newImage(const char *path, const unsigned int flags)
Loads an image as a texture.
Definition: opengl_tex.c:570
void gl_freeTexture(glTexture *texture)
Frees a texture.
Definition: opengl_tex.c:755
void gl_vboDestroy(gl_vbo *vbo)
Destroys a VBO.
Definition: opengl_vbo.c:248
void gl_vboActivateAttribOffset(gl_vbo *vbo, GLuint index, GLuint offset, GLint size, GLenum type, GLsizei stride)
Activates a VBO's offset.
Definition: opengl_vbo.c:228
gl_vbo * gl_vboCreateStatic(GLsizei size, const void *data)
Creates a stream vbo.
Definition: opengl_vbo.c:181
static const double d[]
Definition: rng.c:273
Definition: object.c:43
Definition: object.c:50
Represents a solid in the game.
Definition: physics.h:17
double dir
Definition: physics.h:19
vec2 pos
Definition: physics.h:22
Reference to a spob or jump point.
Definition: object.c:56
Abstraction for rendering sprite sheets.
Definition: opengl_tex.h:34
GLuint texture
Definition: opengl_tex.h:50
Definition: mat4.h:10
double y
Definition: vec2.h:34
double x
Definition: vec2.h:33