nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: nuclear@1: #include nuclear@1: nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: nuclear@1: #include nuclear@1: nuclear@1: #include "scene.h" nuclear@1: nuclear@1: static void color(float *dest, float r, float g, float b); nuclear@1: static struct mesh *create_mesh(const struct aiScene *aiscn, struct aiMesh *aim); nuclear@1: static unsigned int create_buffer(unsigned int type, void *data, size_t sz); nuclear@1: static unsigned int load_texture(const char *fname); nuclear@1: nuclear@1: nuclear@1: int load_scene(struct scene *scn, const char *fname) nuclear@1: { nuclear@1: int i, j; nuclear@1: const struct aiScene *aiscn; nuclear@1: unsigned int proc_flags = aiProcess_JoinIdenticalVertices | nuclear@1: aiProcess_PreTransformVertices | aiProcess_Triangulate | nuclear@1: aiProcess_GenNormals | aiProcess_SortByPType; nuclear@1: nuclear@1: if(!(aiscn = aiImportFile(fname, proc_flags))) { nuclear@1: fprintf(stderr, "failed to load: %s\n", fname); nuclear@1: return -1; nuclear@1: } nuclear@1: nuclear@1: scn->meshes = 0; nuclear@1: scn->lights = 0; nuclear@1: nuclear@1: scn->bbox.min[0] = scn->bbox.min[1] = scn->bbox.min[2] = FLT_MAX; nuclear@1: scn->bbox.max[0] = scn->bbox.max[1] = scn->bbox.max[2] = -FLT_MAX; nuclear@1: nuclear@1: for(i=0; imNumMeshes; i++) { nuclear@1: struct mesh *m; nuclear@1: nuclear@1: if(!(m = create_mesh(aiscn, aiscn->mMeshes[i]))) { nuclear@1: return -1; nuclear@1: } nuclear@1: nuclear@1: for(j=0; j<3; j++) { nuclear@1: if(m->bbox.min[j] < scn->bbox.min[j]) { nuclear@1: scn->bbox.min[j] = m->bbox.min[j]; nuclear@1: } nuclear@1: if(m->bbox.max[j] > scn->bbox.max[j]) { nuclear@1: scn->bbox.max[j] = m->bbox.max[j]; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: m->next = scn->meshes; nuclear@1: scn->meshes = m; nuclear@1: } nuclear@1: nuclear@1: printf("scene bounds: %.2f %.2f %.2f -> %.2f %.2f %.2f\n", scn->bbox.min[0], scn->bbox.min[1], nuclear@1: scn->bbox.min[2], scn->bbox.max[0], scn->bbox.max[1], scn->bbox.max[2]); nuclear@1: nuclear@1: aiReleaseImport(aiscn); nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: static void color(float *dest, float r, float g, float b) nuclear@1: { nuclear@1: dest[0] = r; nuclear@1: dest[1] = g; nuclear@1: dest[2] = b; nuclear@1: dest[3] = 1.0; nuclear@1: } nuclear@1: nuclear@1: static struct mesh *create_mesh(const struct aiScene *aiscn, struct aiMesh *aim) nuclear@1: { nuclear@1: int i, j; nuclear@1: struct mesh *m; nuclear@1: struct aiMaterial *mat = aiscn->mMaterials[aim->mMaterialIndex]; nuclear@1: float sstr = 1.0; nuclear@1: unsigned int sz, *idxarr, *dptr; nuclear@1: struct aiVector3D *vptr; nuclear@1: struct aiString tex_name; nuclear@1: nuclear@1: if(!(m = calloc(1, sizeof *m))) { nuclear@1: perror("failed to allocate mesh"); nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: /* default material */ nuclear@1: color(m->mat.kd, 1, 1, 1); nuclear@1: color(m->mat.ks, 0, 0, 0); nuclear@1: m->mat.shin = 60.0; nuclear@1: nuclear@1: aiGetMaterialColor(mat, AI_MATKEY_COLOR_DIFFUSE, (void*)m->mat.kd); nuclear@1: aiGetMaterialColor(mat, AI_MATKEY_COLOR_SPECULAR, (void*)m->mat.ks); nuclear@1: sz = 1; nuclear@1: aiGetMaterialFloatArray(mat, AI_MATKEY_SHININESS, &m->mat.shin, &sz); nuclear@1: sz = 1; nuclear@1: aiGetMaterialFloatArray(mat, AI_MATKEY_SHININESS_STRENGTH, &sstr, &sz); nuclear@1: color(m->mat.ks, m->mat.ks[0] * sstr, m->mat.ks[1] * sstr, m->mat.ks[2] * sstr); nuclear@1: nuclear@1: if(aiGetMaterialString(mat, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0), &tex_name) == 0) { nuclear@1: m->mat.tex = load_texture(tex_name.data); nuclear@1: } nuclear@1: nuclear@1: m->num_verts = aim->mNumVertices; nuclear@1: m->num_faces = aim->mNumFaces; nuclear@1: nuclear@1: m->bbox.min[0] = m->bbox.min[1] = m->bbox.min[2] = FLT_MAX; nuclear@1: m->bbox.max[0] = m->bbox.max[1] = m->bbox.max[2] = -FLT_MAX; nuclear@1: nuclear@1: vptr = aim->mVertices; nuclear@1: for(i=0; inum_verts; i++) { nuclear@1: if(vptr->x < m->bbox.min[0]) m->bbox.min[0] = vptr->x; nuclear@1: if(vptr->y < m->bbox.min[1]) m->bbox.min[1] = vptr->y; nuclear@1: if(vptr->z < m->bbox.min[2]) m->bbox.min[2] = vptr->z; nuclear@1: if(vptr->x > m->bbox.max[0]) m->bbox.max[0] = vptr->x; nuclear@1: if(vptr->y > m->bbox.max[1]) m->bbox.max[1] = vptr->y; nuclear@1: if(vptr->z > m->bbox.max[2]) m->bbox.max[2] = vptr->z; nuclear@1: vptr++; nuclear@1: } nuclear@1: nuclear@1: m->vert_buf = create_buffer(GL_ARRAY_BUFFER, aim->mVertices, m->num_verts * sizeof *aim->mVertices); nuclear@1: if(aim->mNormals) { nuclear@1: m->norm_buf = create_buffer(GL_ARRAY_BUFFER, aim->mNormals, m->num_verts * sizeof *aim->mNormals); nuclear@1: } nuclear@1: if(aim->mTextureCoords) { nuclear@2: m->tex_buf = create_buffer(GL_ARRAY_BUFFER, aim->mTextureCoords[0], m->num_verts * sizeof *aim->mTextureCoords[0]); nuclear@1: } nuclear@1: nuclear@1: /* indices are scattered all over the fucking place... map the buffer and collect them there directly */ nuclear@1: m->idx_buf = create_buffer(GL_ELEMENT_ARRAY_BUFFER, 0, m->num_faces * 3 * sizeof *aim->mFaces[0].mIndices); nuclear@1: dptr = idxarr = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); nuclear@1: nuclear@1: for(i=0; inum_faces; i++) { nuclear@1: for(j=0; j<3; j++) { nuclear@1: *dptr++ = aim->mFaces[i].mIndices[j]; nuclear@1: } nuclear@1: } nuclear@1: glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); nuclear@1: return m; nuclear@1: } nuclear@1: nuclear@1: static unsigned int create_buffer(unsigned int type, void *data, size_t sz) nuclear@1: { nuclear@1: unsigned int vbo; nuclear@1: glGenBuffers(1, &vbo); nuclear@1: glBindBuffer(type, vbo); nuclear@1: glBufferData(type, sz, data, GL_STATIC_DRAW); nuclear@1: return vbo; nuclear@1: } nuclear@1: nuclear@1: static unsigned int load_texture(const char *fname) nuclear@1: { nuclear@1: unsigned int tex; nuclear@1: void *pixels; nuclear@1: int xsz, ysz; nuclear@1: nuclear@1: if(!(pixels = img_load_pixels(fname, &xsz, &ysz, IMG_FMT_RGBA32))) { nuclear@1: fprintf(stderr, "failed to load image: %s\n", fname); nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: glGenTextures(1, &tex); nuclear@1: glBindTexture(GL_TEXTURE_2D, tex); nuclear@1: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@1: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@1: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); nuclear@1: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); nuclear@1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xsz, ysz, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); nuclear@1: nuclear@1: img_free_pixels(pixels); nuclear@1: return tex; nuclear@1: } nuclear@1: nuclear@1: void render_scene(struct scene *scn) nuclear@1: { nuclear@1: struct mesh *m = scn->meshes; nuclear@1: while(m) { nuclear@1: render_mesh(m); nuclear@1: m = m->next; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: void render_mesh(struct mesh *m) nuclear@1: { nuclear@1: glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, m->mat.kd); nuclear@1: glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, m->mat.ks); nuclear@1: glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, m->mat.shin); nuclear@1: nuclear@1: if(m->mat.tex) { nuclear@1: glEnable(GL_TEXTURE_2D); nuclear@1: glBindTexture(GL_TEXTURE_2D, m->mat.tex); nuclear@1: } nuclear@1: nuclear@1: /* TODO texture */ nuclear@1: nuclear@1: glBindBuffer(GL_ARRAY_BUFFER, m->vert_buf); nuclear@1: glVertexPointer(3, GL_FLOAT, 0, 0); nuclear@1: glEnableClientState(GL_VERTEX_ARRAY); nuclear@1: nuclear@1: if(m->norm_buf) { nuclear@1: glBindBuffer(GL_ARRAY_BUFFER, m->norm_buf); nuclear@1: glNormalPointer(GL_FLOAT, 0, 0); nuclear@1: glEnableClientState(GL_NORMAL_ARRAY); nuclear@1: } nuclear@1: if(m->tex_buf) { nuclear@1: glBindBuffer(GL_ARRAY_BUFFER, m->tex_buf); nuclear@1: glTexCoordPointer(3, GL_FLOAT, 0, 0); nuclear@1: glEnableClientState(GL_TEXTURE_COORD_ARRAY); nuclear@1: } nuclear@1: nuclear@1: glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->idx_buf); nuclear@1: glDrawElements(GL_TRIANGLES, m->num_faces * 3, GL_UNSIGNED_INT, 0); nuclear@1: nuclear@1: glDisableClientState(GL_VERTEX_ARRAY); nuclear@1: glDisableClientState(GL_NORMAL_ARRAY); nuclear@1: glDisableClientState(GL_TEXTURE_COORD_ARRAY); nuclear@1: nuclear@1: if(m->mat.tex) { nuclear@1: glDisable(GL_TEXTURE_2D); nuclear@1: } nuclear@1: }