view3d

view src/scene.c @ 6:cfd22a190353

fixed crash when there aren't any lights
author John Tsiombikas <nuclear@mutantstargoat.com>
date Fri, 20 Jan 2012 04:03:04 +0200
parents 58ddd42848f9
children 75e4377f3cdc
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <float.h>
5 #include <GL/glew.h>
7 #include <assimp/assimp.h>
8 #include <assimp/aiScene.h>
9 #include <assimp/aiPostProcess.h>
11 #include <imago2.h>
13 #include "scene.h"
15 static void setup_light(struct light *lt);
16 static void color(float *dest, float r, float g, float b);
17 static struct mesh *create_mesh(const struct aiScene *aiscn, struct aiMesh *aim);
18 static unsigned int create_buffer(unsigned int type, void *data, size_t sz);
19 static unsigned int load_texture(const char *fname);
21 extern int verbose;
23 int load_scene(struct scene *scn, const char *fname)
24 {
25 int i, j;
26 const struct aiScene *aiscn;
27 unsigned int proc_flags = aiProcess_JoinIdenticalVertices |
28 aiProcess_PreTransformVertices | aiProcess_Triangulate |
29 aiProcess_GenNormals | aiProcess_SortByPType | aiProcess_FlipUVs;
31 if(!(aiscn = aiImportFile(fname, proc_flags))) {
32 fprintf(stderr, "failed to load: %s\n", fname);
33 return -1;
34 }
36 if(verbose) {
37 printf("scene: %s (%d meshes, %d lights)\n", fname, aiscn->mNumMeshes, aiscn->mNumLights);
38 }
40 scn->meshes = 0;
41 scn->lights = 0;
43 for(i=0; i<aiscn->mNumLights; i++) {
44 struct light *lt;
45 struct aiLight *ailt = aiscn->mLights[i];
47 if(!(lt = malloc(sizeof *lt))) {
48 perror("failed to allocate light");
49 return -1;
50 }
52 if(verbose) {
53 printf("- light(%s) ", ailt->mName.data);
54 }
56 switch(ailt->mType) {
57 case aiLightSource_POINT:
58 lt->pos[0] = ailt->mPosition.x;
59 lt->pos[1] = ailt->mPosition.y;
60 lt->pos[2] = ailt->mPosition.z;
61 lt->pos[3] = 1.0f;
62 if(verbose) {
63 printf("pos(%.2f %.2f %.2f) ", lt->pos[0], lt->pos[1], lt->pos[2]);
64 }
65 break;
67 case aiLightSource_DIRECTIONAL:
68 lt->pos[0] = ailt->mDirection.x;
69 lt->pos[1] = ailt->mDirection.y;
70 lt->pos[2] = ailt->mDirection.z;
71 lt->pos[3] = 0.0f;
72 if(verbose) {
73 printf("dir(%.2f %.2f %.2f) ", lt->pos[0], lt->pos[1], lt->pos[2]);
74 }
75 break;
77 default:
78 fprintf(stderr, "error loading light: %s, unsupported type\n", ailt->mName.data);
79 continue;
80 }
81 color(lt->color, ailt->mColorDiffuse.r, ailt->mColorDiffuse.g, ailt->mColorDiffuse.b);
82 if(verbose) {
83 printf("col(%.2f %.2f %.2f) ", lt->color[0], lt->color[1], lt->color[2]);
84 }
86 lt->cone_inner = ailt->mAngleInnerCone;
87 lt->cone_outer = ailt->mAngleOuterCone;
89 lt->att[0] = ailt->mAttenuationConstant;
90 lt->att[1] = ailt->mAttenuationLinear;
91 lt->att[2] = ailt->mAttenuationQuadratic;
92 if(verbose) {
93 printf("att(%.2f %.2f %.2f)\n", lt->att[0], lt->att[1], lt->att[2]);
94 }
96 lt->next = scn->lights;
97 scn->lights = lt;
98 }
100 scn->bbox.min[0] = scn->bbox.min[1] = scn->bbox.min[2] = FLT_MAX;
101 scn->bbox.max[0] = scn->bbox.max[1] = scn->bbox.max[2] = -FLT_MAX;
103 for(i=0; i<aiscn->mNumMeshes; i++) {
104 struct mesh *m;
106 if(!(m = create_mesh(aiscn, aiscn->mMeshes[i]))) {
107 return -1;
108 }
110 for(j=0; j<3; j++) {
111 if(m->bbox.min[j] < scn->bbox.min[j]) {
112 scn->bbox.min[j] = m->bbox.min[j];
113 }
114 if(m->bbox.max[j] > scn->bbox.max[j]) {
115 scn->bbox.max[j] = m->bbox.max[j];
116 }
117 }
119 m->next = scn->meshes;
120 scn->meshes = m;
121 }
123 if(verbose) {
124 printf("scene bounds: %.2f %.2f %.2f -> %.2f %.2f %.2f\n", scn->bbox.min[0], scn->bbox.min[1],
125 scn->bbox.min[2], scn->bbox.max[0], scn->bbox.max[1], scn->bbox.max[2]);
126 }
128 aiReleaseImport(aiscn);
129 return 0;
130 }
132 static void color(float *dest, float r, float g, float b)
133 {
134 dest[0] = r;
135 dest[1] = g;
136 dest[2] = b;
137 dest[3] = 1.0;
138 }
140 static struct mesh *create_mesh(const struct aiScene *aiscn, struct aiMesh *aim)
141 {
142 int i, j;
143 struct mesh *m;
144 struct aiMaterial *mat = aiscn->mMaterials[aim->mMaterialIndex];
145 float sstr = 1.0;
146 unsigned int sz, *idxarr, *dptr;
147 struct aiVector3D *vptr;
148 struct aiString tex_name;
150 if(!(m = calloc(1, sizeof *m))) {
151 perror("failed to allocate mesh");
152 return 0;
153 }
155 if(verbose) {
156 printf("- mesh(%s) v:%d f:%d\n", aim->mName.data, aim->mNumVertices, aim->mNumFaces);
157 }
159 /* default material */
160 color(m->mat.kd, 1, 1, 1);
161 color(m->mat.ks, 0, 0, 0);
162 m->mat.shin = 60.0;
164 aiGetMaterialColor(mat, AI_MATKEY_COLOR_DIFFUSE, (void*)m->mat.kd);
165 aiGetMaterialColor(mat, AI_MATKEY_COLOR_SPECULAR, (void*)m->mat.ks);
166 sz = 1;
167 aiGetMaterialFloatArray(mat, AI_MATKEY_SHININESS, &m->mat.shin, &sz);
168 sz = 1;
169 aiGetMaterialFloatArray(mat, AI_MATKEY_SHININESS_STRENGTH, &sstr, &sz);
170 color(m->mat.ks, m->mat.ks[0] * sstr, m->mat.ks[1] * sstr, m->mat.ks[2] * sstr);
172 if(aiGetMaterialString(mat, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0), &tex_name) == 0) {
173 m->mat.tex = load_texture(tex_name.data);
174 }
176 m->num_verts = aim->mNumVertices;
177 m->num_faces = aim->mNumFaces;
179 m->bbox.min[0] = m->bbox.min[1] = m->bbox.min[2] = FLT_MAX;
180 m->bbox.max[0] = m->bbox.max[1] = m->bbox.max[2] = -FLT_MAX;
182 vptr = aim->mVertices;
183 for(i=0; i<m->num_verts; i++) {
184 if(vptr->x < m->bbox.min[0]) m->bbox.min[0] = vptr->x;
185 if(vptr->y < m->bbox.min[1]) m->bbox.min[1] = vptr->y;
186 if(vptr->z < m->bbox.min[2]) m->bbox.min[2] = vptr->z;
187 if(vptr->x > m->bbox.max[0]) m->bbox.max[0] = vptr->x;
188 if(vptr->y > m->bbox.max[1]) m->bbox.max[1] = vptr->y;
189 if(vptr->z > m->bbox.max[2]) m->bbox.max[2] = vptr->z;
190 vptr++;
191 }
193 m->vert_buf = create_buffer(GL_ARRAY_BUFFER, aim->mVertices, m->num_verts * sizeof *aim->mVertices);
194 if(aim->mNormals) {
195 m->norm_buf = create_buffer(GL_ARRAY_BUFFER, aim->mNormals, m->num_verts * sizeof *aim->mNormals);
196 }
197 if(aim->mTextureCoords) {
198 m->tex_buf = create_buffer(GL_ARRAY_BUFFER, aim->mTextureCoords[0], m->num_verts * sizeof *aim->mTextureCoords[0]);
199 }
201 /* indices are scattered all over the fucking place... map the buffer and collect them there directly */
202 m->idx_buf = create_buffer(GL_ELEMENT_ARRAY_BUFFER, 0, m->num_faces * 3 * sizeof *aim->mFaces[0].mIndices);
203 dptr = idxarr = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
205 for(i=0; i<m->num_faces; i++) {
206 for(j=0; j<3; j++) {
207 *dptr++ = aim->mFaces[i].mIndices[j];
208 }
209 }
210 glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
211 return m;
212 }
214 static unsigned int create_buffer(unsigned int type, void *data, size_t sz)
215 {
216 unsigned int vbo;
217 glGenBuffers(1, &vbo);
218 glBindBuffer(type, vbo);
219 glBufferData(type, sz, data, GL_STATIC_DRAW);
220 return vbo;
221 }
223 static unsigned int load_texture(const char *fname)
224 {
225 unsigned int tex;
226 void *pixels;
227 int xsz, ysz;
229 if(!(pixels = img_load_pixels(fname, &xsz, &ysz, IMG_FMT_RGBA32))) {
230 fprintf(stderr, "failed to load image: %s\n", fname);
231 return 0;
232 }
234 if(verbose) {
235 printf(" - texture: %s (%dx%d)\n", fname, xsz, ysz);
236 }
238 glGenTextures(1, &tex);
239 glBindTexture(GL_TEXTURE_2D, tex);
240 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
241 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
242 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
243 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
244 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xsz, ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
246 img_free_pixels(pixels);
247 return tex;
248 }
250 void render_scene(struct scene *scn)
251 {
252 struct light *lt = scn->lights;
253 int pass = 0;
255 glEnable(GL_BLEND);
256 glDepthFunc(GL_LEQUAL);
258 while(lt || pass == 0) {
259 struct mesh *m;
261 setup_light(lt);
263 if(pass == 0) {
264 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
265 } else {
266 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
267 glDepthMask(0);
268 }
270 m = scn->meshes;
271 while(m) {
272 render_mesh(m, pass);
273 m = m->next;
274 }
275 pass++;
276 if(lt) {
277 lt = lt->next;
278 }
279 }
281 glDisable(GL_BLEND);
282 glDepthMask(1);
283 }
285 static void setup_light(struct light *lt)
286 {
287 if(!lt)
288 return;
289 glLightfv(GL_LIGHT0, GL_POSITION, lt->pos);
290 glLightfv(GL_LIGHT0, GL_DIFFUSE, lt->color);
291 glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, lt->att[0]);
292 glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, lt->att[1]);
293 glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, lt->att[2]);
294 glEnable(GL_LIGHT0);
295 }
297 void render_mesh(struct mesh *m, int pass)
298 {
299 if(pass > 0) {
300 float black[] = {0, 0, 0, 0};
301 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, black);
302 } else {
303 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, m->mat.kd);
304 }
305 glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, m->mat.kd);
306 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, m->mat.ks);
307 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, m->mat.shin);
309 if(m->mat.tex) {
310 glEnable(GL_TEXTURE_2D);
311 glBindTexture(GL_TEXTURE_2D, m->mat.tex);
312 }
314 glBindBuffer(GL_ARRAY_BUFFER, m->vert_buf);
315 glVertexPointer(3, GL_FLOAT, 0, 0);
316 glEnableClientState(GL_VERTEX_ARRAY);
318 if(m->norm_buf) {
319 glBindBuffer(GL_ARRAY_BUFFER, m->norm_buf);
320 glNormalPointer(GL_FLOAT, 0, 0);
321 glEnableClientState(GL_NORMAL_ARRAY);
322 }
323 if(m->tex_buf) {
324 glBindBuffer(GL_ARRAY_BUFFER, m->tex_buf);
325 glTexCoordPointer(3, GL_FLOAT, 0, 0);
326 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
327 }
329 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->idx_buf);
330 glDrawElements(GL_TRIANGLES, m->num_faces * 3, GL_UNSIGNED_INT, 0);
332 glDisableClientState(GL_VERTEX_ARRAY);
333 glDisableClientState(GL_NORMAL_ARRAY);
334 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
336 if(m->mat.tex) {
337 glDisable(GL_TEXTURE_2D);
338 }
339 }