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