/*! * Compute a vector normal to two line segments. * * Computes the normal vector to the lines b-a and b-c. * * \param n Returned normal vector. * \param a Endpoint of first line. * \param b Base point of both lines. * \param c Endpoint of second line. */ void lib3ds_vector_normal(float n[3], float a[3], float b[3], float c[3]) { float p[3], q[3]; lib3ds_vector_sub(p, c, b); lib3ds_vector_sub(q, a, b); lib3ds_vector_cross(n, p, q); lib3ds_vector_normalize(n); }
/*! * Compute a vector normal to two line segments. * * Computes the normal vector to the lines b-a and b-c. * * \param n Returned normal vector. * \param a Endpoint of first line. * \param b Base point of both lines. * \param c Endpoint of second line. * * \ingroup vector */ void lib3ds_vector_normal(Lib3dsVector n, Lib3dsVector a, Lib3dsVector b, Lib3dsVector c) { Lib3dsVector p,q; lib3ds_vector_sub(p,c,b); lib3ds_vector_sub(q,a,b); lib3ds_vector_cross(n,p,q); lib3ds_vector_normalize(n); }
/*! * \ingroup matrix */ void lib3ds_matrix_camera(Lib3dsMatrix matrix, Lib3dsVector pos, Lib3dsVector tgt, Lib3dsFloat roll) { Lib3dsMatrix M,R; Lib3dsVector x, y, z; lib3ds_vector_sub(y, tgt, pos); lib3ds_vector_normalize(y); z[0] = 0; z[1] = 0; z[2] = 1.0; lib3ds_vector_cross(x, y, z); lib3ds_vector_cross(z, x, y); lib3ds_vector_normalize(x); lib3ds_vector_normalize(y); lib3ds_matrix_identity(M); M[0][0] = x[0]; M[1][0] = x[1]; M[2][0] = x[2]; M[0][1] = y[0]; M[1][1] = y[1]; M[2][1] = y[2]; M[0][2] = z[0]; M[1][2] = z[1]; M[2][2] = z[2]; lib3ds_matrix_identity(R); lib3ds_matrix_rotate_y(R, roll); lib3ds_matrix_mul(matrix, R,M); lib3ds_matrix_translate_xyz(matrix, -pos[0],-pos[1],-pos[2]); }
/*! * \ingroup tracks */ void lib3ds_lin3_key_setup(Lib3dsLin3Key *p, Lib3dsLin3Key *cp, Lib3dsLin3Key *c, Lib3dsLin3Key *cn, Lib3dsLin3Key *n) { Lib3dsVector np,nn; Lib3dsFloat ksm,ksp,kdm,kdp; int i; ASSERT(c); if (!cp) { cp=c; } if (!cn) { cn=c; } if (!p && !n) { lib3ds_vector_zero(c->ds); lib3ds_vector_zero(c->dd); return; } if (n && p) { lib3ds_tcb(&p->tcb, &cp->tcb, &c->tcb, &cn->tcb, &n->tcb, &ksm, &ksp, &kdm, &kdp); lib3ds_vector_sub(np, c->value, p->value); lib3ds_vector_sub(nn, n->value, c->value); for(i=0; i<3; ++i) { c->ds[i]=ksm*np[i] + ksp*nn[i]; c->dd[i]=kdm*np[i] + kdp*nn[i]; } } else { if (p) { lib3ds_vector_sub(np, c->value, p->value); lib3ds_vector_copy(c->ds, np); lib3ds_vector_copy(c->dd, np); } if (n) { lib3ds_vector_sub(nn, n->value, c->value); lib3ds_vector_copy(c->ds, nn); lib3ds_vector_copy(c->dd, nn); } } }
/*! * Compute a camera matrix based on position, target and roll. * * Generates a translate/rotate matrix that maps world coordinates * to camera coordinates. Resulting matrix does not include perspective * transform. * * \param matrix Destination matrix. * \param pos Camera position * \param tgt Camera target * \param roll Roll angle * * \ingroup matrix */ void lib3ds_matrix_camera(Lib3dsMatrix matrix, Lib3dsVector pos, Lib3dsVector tgt, Lib3dsFloat roll) { Lib3dsMatrix M; Lib3dsVector x, y, z; lib3ds_vector_sub(y, tgt, pos); lib3ds_vector_normalize(y); if (y[0] != 0. || y[1] != 0) { z[0] = 0; z[1] = 0; z[2] = 1.0; } else { /* Special case: looking straight up or down z axis */ z[0] = -1.0; z[1] = 0; z[2] = 0; } lib3ds_vector_cross(x, y, z); lib3ds_vector_cross(z, x, y); lib3ds_vector_normalize(x); lib3ds_vector_normalize(z); lib3ds_matrix_identity(M); M[0][0] = x[0]; M[1][0] = x[1]; M[2][0] = x[2]; M[0][1] = y[0]; M[1][1] = y[1]; M[2][1] = y[2]; M[0][2] = z[0]; M[1][2] = z[1]; M[2][2] = z[2]; lib3ds_matrix_identity(matrix); lib3ds_matrix_rotate_y(matrix, roll); lib3ds_matrix_mult(matrix, M); lib3ds_matrix_translate_xyz(matrix, -pos[0],-pos[1],-pos[2]); }
/*! * Compute a camera matrix based on position, target and roll. * * Generates a translate/rotate matrix that maps world coordinates * to camera coordinates. Resulting matrix does not include perspective * transform. * * \param matrix Destination matrix. * \param pos Camera position * \param tgt Camera target * \param roll Roll angle */ void lib3ds_matrix_camera(float matrix[4][4], float pos[3], float tgt[3], float roll) { float M[4][4]; float x[3], y[3], z[3]; lib3ds_vector_sub(y, tgt, pos); lib3ds_vector_normalize(y); if (y[0] != 0. || y[1] != 0) { z[0] = 0; z[1] = 0; z[2] = 1.0; } else { /* Special case: looking straight up or down z axis */ z[0] = -1.0; z[1] = 0; z[2] = 0; } lib3ds_vector_cross(x, y, z); lib3ds_vector_cross(z, x, y); lib3ds_vector_normalize(x); lib3ds_vector_normalize(z); lib3ds_matrix_identity(M); M[0][0] = x[0]; M[1][0] = x[1]; M[2][0] = x[2]; M[0][1] = y[0]; M[1][1] = y[1]; M[2][1] = y[2]; M[0][2] = z[0]; M[1][2] = z[1]; M[2][2] = z[2]; lib3ds_matrix_identity(matrix); lib3ds_matrix_rotate(matrix, roll, 0, 1, 0); lib3ds_matrix_mult(matrix, matrix, M); lib3ds_matrix_translate(matrix, -pos[0], -pos[1], -pos[2]); }
/*! * Calculates the vertex normals corresponding to the smoothing group * settings for each face of a mesh. * * \param mesh A pointer to the mesh to calculate the normals for. * \param normals A pointer to a buffer to store the calculated * normals. The buffer must have the size: * 3*3*sizeof(float)*mesh->nfaces. * * To allocate the normal buffer do for example the following: * \code * Lib3dsVector *normals = malloc(3*3*sizeof(float)*mesh->nfaces); * \endcode * * To access the normal of the i-th vertex of the j-th face do the * following: * \code * normals[3*j+i] * \endcode */ void lib3ds_mesh_calculate_vertex_normals(Lib3dsMesh *mesh, float (*normals)[3]) { Lib3dsFaces **fl; Lib3dsFaces *fa; int i, j; if (!mesh->nfaces) { return; } fl = (Lib3dsFaces**)calloc(sizeof(Lib3dsFaces*), mesh->nvertices); fa = (Lib3dsFaces*)malloc(sizeof(Lib3dsFaces) * 3 * mesh->nfaces); for (i = 0; i < mesh->nfaces; ++i) { for (j = 0; j < 3; ++j) { Lib3dsFaces* l = &fa[3*i+j]; float p[3], q[3], n[3]; float len, weight; l->index = i; l->next = fl[mesh->faces[i].index[j]]; fl[mesh->faces[i].index[j]] = l; lib3ds_vector_sub(p, mesh->vertices[mesh->faces[i].index[j<2? j + 1 : 0]], mesh->vertices[mesh->faces[i].index[j]]); lib3ds_vector_sub(q, mesh->vertices[mesh->faces[i].index[j>0? j - 1 : 2]], mesh->vertices[mesh->faces[i].index[j]]); lib3ds_vector_cross(n, p, q); len = lib3ds_vector_length(n); if (len > 0) { weight = (float)atan2(len, lib3ds_vector_dot(p, q)); lib3ds_vector_scalar_mul(l->normal, n, weight / len); } else { lib3ds_vector_zero(l->normal); } } } for (i = 0; i < mesh->nfaces; ++i) { Lib3dsFace *f = &mesh->faces[i]; for (j = 0; j < 3; ++j) { float n[3]; Lib3dsFaces *p; Lib3dsFace *pf; assert(mesh->faces[i].index[j] < mesh->nvertices); if (f->smoothing_group) { unsigned smoothing_group = f->smoothing_group; lib3ds_vector_zero(n); for (p = fl[mesh->faces[i].index[j]]; p; p = p->next) { pf = &mesh->faces[p->index]; if (pf->smoothing_group & f->smoothing_group) smoothing_group |= pf->smoothing_group; } for (p = fl[mesh->faces[i].index[j]]; p; p = p->next) { pf = &mesh->faces[p->index]; if (smoothing_group & pf->smoothing_group) { lib3ds_vector_add(n, n, p->normal); } } } else { lib3ds_vector_copy(n, fa[3*i+j].normal); } lib3ds_vector_normalize(n); lib3ds_vector_copy(normals[3*i+j], n); } } free(fa); free(fl); }
/*! * Main display function; called whenever the scene needs to be redrawn. */ static void display(void) { Lib3dsNode *c,*t; Lib3dsFloat fov, roll; float near, far, dist; float *campos; float *tgt; Lib3dsMatrix M; Lib3dsCamera *cam; Lib3dsVector v; Lib3dsNode *p; if( file != NULL && file->background.solid.use ) glClearColor(file->background.solid.col[0], file->background.solid.col[1], file->background.solid.col[2], 1.); /* TODO: fog */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if( anti_alias ) glEnable(GL_POLYGON_SMOOTH); else glDisable(GL_POLYGON_SMOOTH); if (!file) { return; } glLightModelfv(GL_LIGHT_MODEL_AMBIENT, file->ambient); c=lib3ds_file_node_by_name(file, camera, LIB3DS_CAMERA_NODE); t=lib3ds_file_node_by_name(file, camera, LIB3DS_TARGET_NODE); if( t != NULL ) tgt = t->data.target.pos; if( c != NULL ) { fov = c->data.camera.fov; roll = c->data.camera.roll; campos = c->data.camera.pos; } if ((cam = lib3ds_file_camera_by_name(file, camera)) == NULL) return; near = cam->near_range; far = cam->far_range; if (c == NULL || t == NULL) { if( c == NULL ) { fov = cam->fov; roll = cam->roll; campos = cam->position; } if( t == NULL ) tgt = cam->target; } glMatrixMode(GL_PROJECTION); glLoadIdentity(); /* KLUDGE alert: OpenGL can't handle a near clip plane of zero, * so if the camera's near plane is zero, we give it a small number. * In addition, many .3ds files I've seen have a far plane that's * much too close and the model gets clipped away. I haven't found * a way to tell OpenGL not to clip the far plane, so we move it * further away. A factor of 10 seems to make all the models I've * seen visible. */ if( near <= 0. ) near = far * .001; gluPerspective( fov, 1.0*gl_width/gl_height, near, far); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotatef(-90, 1.0,0,0); /* User rotates the view about the target point */ lib3ds_vector_sub(v, tgt, campos); dist = lib3ds_vector_length(v); glTranslatef(0.,dist, 0.); glRotatef(view_rotx, 1., 0., 0.); glRotatef(view_roty, 0., 1., 0.); glRotatef(view_rotz, 0., 0., 1.); glTranslatef(0.,-dist, 0.); lib3ds_matrix_camera(M, campos, tgt, roll); glMultMatrixf(&M[0][0]); /* Lights. Set them from light nodes if possible. If not, use the * light objects directly. */ { static const GLfloat a[] = {0.0f, 0.0f, 0.0f, 1.0f}; static GLfloat c[] = {1.0f, 1.0f, 1.0f, 1.0f}; static GLfloat p[] = {0.0f, 0.0f, 0.0f, 1.0f}; Lib3dsLight *l; int li=GL_LIGHT0; for (l=file->lights; l; l=l->next) { glEnable(li); light_update(l); c[0] = l->color[0]; c[1] = l->color[1]; c[2] = l->color[2]; glLightfv(li, GL_AMBIENT, a); glLightfv(li, GL_DIFFUSE, c); glLightfv(li, GL_SPECULAR, c); p[0] = l->position[0]; p[1] = l->position[1]; p[2] = l->position[2]; glLightfv(li, GL_POSITION, p); if (l->spot_light) { p[0] = l->spot[0] - l->position[0]; p[1] = l->spot[1] - l->position[1]; p[2] = l->spot[2] - l->position[2]; glLightfv(li, GL_SPOT_DIRECTION, p); } ++li; } } if( show_object ) { for (p=file->nodes; p!=0; p=p->next) { render_node(p); } } if( show_bounds ) draw_bounds(tgt); if( show_cameras ) { for( cam = file->cameras; cam != NULL; cam = cam->next ) { lib3ds_matrix_camera(M, cam->position, cam->target, cam->roll); lib3ds_matrix_inv(M); glPushMatrix(); glMultMatrixf(&M[0][0]); glScalef(size/20, size/20, size/20); glCallList(cameraList); glPopMatrix(); } } if( show_lights ) { Lib3dsLight *light; for( light = file->lights; light != NULL; light = light->next ) draw_light(light->position, light->color); glMaterialfv(GL_FRONT, GL_EMISSION, black); } glutSwapBuffers(); }