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