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@3: static void setup_light(struct light *lt); 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@3: extern int verbose; nuclear@1: nuclear@8: int init_scene(struct scene *scn) nuclear@8: { nuclear@8: scn->meshes = 0; nuclear@8: scn->lights = 0; nuclear@8: nuclear@8: scn->bbox.min[0] = scn->bbox.min[1] = scn->bbox.min[2] = FLT_MAX; nuclear@8: scn->bbox.max[0] = scn->bbox.max[1] = scn->bbox.max[2] = -FLT_MAX; nuclear@8: return 0; nuclear@8: } nuclear@8: 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@10: aiProcess_GenSmoothNormals | aiProcess_SortByPType | aiProcess_FlipUVs; 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@4: if(verbose) { nuclear@4: printf("scene: %s (%d meshes, %d lights)\n", fname, aiscn->mNumMeshes, aiscn->mNumLights); nuclear@4: } nuclear@4: nuclear@3: for(i=0; imNumLights; i++) { nuclear@3: struct light *lt; nuclear@3: struct aiLight *ailt = aiscn->mLights[i]; nuclear@3: nuclear@3: if(!(lt = malloc(sizeof *lt))) { nuclear@3: perror("failed to allocate light"); nuclear@3: return -1; nuclear@3: } nuclear@3: nuclear@3: if(verbose) { nuclear@3: printf("- light(%s) ", ailt->mName.data); nuclear@3: } nuclear@3: nuclear@3: switch(ailt->mType) { nuclear@3: case aiLightSource_POINT: nuclear@3: lt->pos[0] = ailt->mPosition.x; nuclear@3: lt->pos[1] = ailt->mPosition.y; nuclear@3: lt->pos[2] = ailt->mPosition.z; nuclear@3: lt->pos[3] = 1.0f; nuclear@3: if(verbose) { nuclear@3: printf("pos(%.2f %.2f %.2f) ", lt->pos[0], lt->pos[1], lt->pos[2]); nuclear@3: } nuclear@3: break; nuclear@3: nuclear@3: case aiLightSource_DIRECTIONAL: nuclear@3: lt->pos[0] = ailt->mDirection.x; nuclear@3: lt->pos[1] = ailt->mDirection.y; nuclear@3: lt->pos[2] = ailt->mDirection.z; nuclear@3: lt->pos[3] = 0.0f; nuclear@3: if(verbose) { nuclear@3: printf("dir(%.2f %.2f %.2f) ", lt->pos[0], lt->pos[1], lt->pos[2]); nuclear@3: } nuclear@3: break; nuclear@3: nuclear@3: default: nuclear@3: fprintf(stderr, "error loading light: %s, unsupported type\n", ailt->mName.data); nuclear@3: continue; nuclear@3: } nuclear@3: color(lt->color, ailt->mColorDiffuse.r, ailt->mColorDiffuse.g, ailt->mColorDiffuse.b); nuclear@3: if(verbose) { nuclear@3: printf("col(%.2f %.2f %.2f) ", lt->color[0], lt->color[1], lt->color[2]); nuclear@3: } nuclear@3: nuclear@3: lt->cone_inner = ailt->mAngleInnerCone; nuclear@3: lt->cone_outer = ailt->mAngleOuterCone; nuclear@3: nuclear@3: lt->att[0] = ailt->mAttenuationConstant; nuclear@3: lt->att[1] = ailt->mAttenuationLinear; nuclear@3: lt->att[2] = ailt->mAttenuationQuadratic; nuclear@3: if(verbose) { nuclear@3: printf("att(%.2f %.2f %.2f)\n", lt->att[0], lt->att[1], lt->att[2]); nuclear@3: } nuclear@3: nuclear@3: lt->next = scn->lights; nuclear@3: scn->lights = lt; nuclear@3: } nuclear@3: nuclear@8: /* load meshes and calculate bounding box */ 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: 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@3: if(verbose) { nuclear@3: printf("- mesh(%s) v:%d f:%d\n", aim->mName.data, aim->mNumVertices, aim->mNumFaces); nuclear@3: } nuclear@3: 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@3: if(verbose) { nuclear@3: printf(" - texture: %s (%dx%d)\n", fname, xsz, ysz); nuclear@3: } nuclear@3: 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@3: struct light *lt = scn->lights; nuclear@3: int pass = 0; nuclear@3: nuclear@4: glEnable(GL_BLEND); nuclear@4: glDepthFunc(GL_LEQUAL); nuclear@4: nuclear@3: while(lt || pass == 0) { nuclear@3: struct mesh *m; nuclear@3: nuclear@3: setup_light(lt); nuclear@3: nuclear@4: if(pass == 0) { nuclear@4: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); nuclear@4: } else { nuclear@3: glBlendFunc(GL_SRC_ALPHA, GL_ONE); nuclear@3: glDepthMask(0); nuclear@3: } nuclear@3: nuclear@3: m = scn->meshes; nuclear@3: while(m) { nuclear@3: render_mesh(m, pass); nuclear@3: m = m->next; nuclear@3: } nuclear@3: pass++; nuclear@6: if(lt) { nuclear@6: lt = lt->next; nuclear@6: } nuclear@1: } nuclear@4: nuclear@4: glDisable(GL_BLEND); nuclear@4: glDepthMask(1); nuclear@1: } nuclear@1: nuclear@3: static void setup_light(struct light *lt) nuclear@1: { nuclear@3: if(!lt) nuclear@3: return; nuclear@3: glLightfv(GL_LIGHT0, GL_POSITION, lt->pos); nuclear@3: glLightfv(GL_LIGHT0, GL_DIFFUSE, lt->color); nuclear@7: glLightfv(GL_LIGHT0, GL_SPECULAR, lt->color); nuclear@3: glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, lt->att[0]); nuclear@3: glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, lt->att[1]); nuclear@3: glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, lt->att[2]); nuclear@3: glEnable(GL_LIGHT0); nuclear@3: } nuclear@3: nuclear@3: void render_mesh(struct mesh *m, int pass) nuclear@3: { nuclear@3: if(pass > 0) { nuclear@3: float black[] = {0, 0, 0, 0}; nuclear@3: glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, black); nuclear@3: } else { nuclear@3: glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, m->mat.kd); nuclear@3: } nuclear@3: glMaterialfv(GL_FRONT_AND_BACK, GL_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: 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: }