/* * A Cascading Reaction-Diffusion simulator for PLY objects. (rdcascade) * * for usage info, run program without any parameters * */ #include #include #include #include "rply.h" /* R-D constants and arrays */ GLfloat DA = 0.04199; GLfloat DI = 0.229; GLfloat S = 0.003; GLfloat S2 = 0.02; GLfloat *BETA; GLfloat *cA; GLfloat *cI; GLfloat *oldCA; GLfloat *oldCI; GLfloat *activator; /* 3d object data arrays */ GLfloat **vertices; GLfloat **normals; GLfloat **diffusion; long **faces; long **neighbours; /* just some vars for extracting data from PLY (could be done better) */ long vertex=0, axis=0, face=0, fvertex=0, home=0, neighb=0; GLfloat maxv = -100; GLfloat minv = 100; /* angles and stuff for rotating and zooming the object */ GLfloat xangle = 0.0; GLfloat yangle = 0.0; GLfloat zangle = 0.0; GLfloat zoom = 1.5; GLfloat atX = 0.0; GLfloat atY = 0.0; /* global for number of vertices and faces */ long nvertices,nfaces; e_ply_type vertex_t, face_length_t, face_value_t; /* GLUT window identifier and viewport size (so we see whole object)*/ static int win; GLfloat view; /*** extra variables for cascading */ int cascaded=0; GLfloat convergence=0, oldconvergence; int convcounter=0,convcount=0,convstandby=0; /* input PLY filename */ char *filename; char *nbsfile; /* just some global variables for stuff that should be implemented better */ int plyHasNeighbs=0, plyHasNormals=0, plyHasDiff=0; int runningRD = 1; /* * * Display usage info * */ void printUsage() { printf("Reaction-Diffusion Simulator (Cascading)\n\nusage:\trdcascade [-p ]\n\n"); printf("\tOptions:\n\t-p\t\t- Use R-D parameters in \n\t\t\t [If not specified defaults are used]\n\n"); printf("\tControls:\n\t\tRotate on X axis: 'a' and 'd'\n\t\tRotate on Y axis: 's' and 'w'\n\t\tRotate on Z axis: 'z' and 'c'\n\t\tZoom in and out: '+', '-'\n\t\tMove object: Left, Right, Up and Down arrows\n\t\tReset view: 'r'\n\t\tCascade: Spacebar\n\t\tPause/resume simulation: 'p'\n\t\tExport concentration levels: 'e'\n\t\tQuit: 'q'\n\n"); printf("\tParameter file format:\n\t\t\t\t- Activator diffusion constant (~0.04)\n\t\t\t\t- Inhibitor diffusion constant (~0.2)\n\t\t\t\t- Initial S value (~0.002)\n\t\t\t\t- 2nd run S value (cascading) (~0.01)\n\n"); printf("If the PLY input file does not contain additional data about vertex normals and neighbour cells, this will be generated before R-D simulation begins. In this case a PLY file with the extra data is stored at _nbs.ply, and this file should be used in future to save on computation time.\n\n"); } /* * * The Reaction-Diffusion Jazz * */ void updateConc() /* * updateConc(): calculate the new values of activator (cA) and inhibitor (cI) * concentrations for each cell, using the R-D equations. */ { long i; GLfloat iR, aR, iD, aD; /* run through each triangular face (cell) */ for (i=1;i= convcount) { //printf("convergence: %f\n", (float)convergence); convcounter=0; /* test if change is less than previous iterations (need threshold because it drops slightly at beginning */ if ((oldconvergence - convergence) >= 2) { convstandby=1; printf("Change is dropping, standby to cascade...\n"); } /* see if we are starting to rise again after dropping a bit, if so trigger cascade */ if ((convstandby) && (oldconvergence < convergence)) doCascade(); oldconvergence=convergence; convergence=0.0; } } } glutPostRedisplay(); } void face3(long face) /* * face3(face): Render the triangular polygon indexed to in the faces array by * input parameter face. Colour is set according to activator[] value for the face. * The glMaterial and glNormal calls are required for lighting. */ { GLfloat colour[] = {activator[face], activator[face], activator[face]}; // - colour - {activator[face]/1.4, activator[face]/2.0, 0}; GLfloat material[] = {activator[face], activator[face], activator[face], 1.0}; // - colour - {activator[face]/1.4, activator[face]/2.0, 0, 1.0}; glColor3fv(colour); glMaterialfv(GL_FRONT, GL_SPECULAR, material); glMaterialfv(GL_FRONT, GL_AMBIENT, material); glMaterialfv(GL_FRONT, GL_DIFFUSE, material); glBegin(GL_POLYGON); glNormal3fv(normals[faces[face][0]]); glVertex3fv(vertices[faces[face][0]]); glNormal3fv(normals[faces[face][1]]); glVertex3fv(vertices[faces[face][1]]); glNormal3fv(normals[faces[face][2]]); glVertex3fv(vertices[faces[face][2]]); glEnd(); } void display(void) /* * display(): GLUT display callback, it sets up the view, and rotation of the scene, * then it renders all the polygons, and swaps buffers (we're using double-buffering). */ { long i; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Set up the modelview transformation. */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //gluLookAt(6.0, 4.0, 14.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); gluLookAt(0, 0, zoom * view, atX, atY, 0.0, 0.0, 1.0, 0.0); glRotatef(xangle, 1.0, 0.0, 0.0); glRotatef(yangle, 0.0, 1.0, 0.0); glRotatef(zangle, 0.0, 0.0, 1.0); for (i=0;i maxv) maxv = vertices[vertex][axis]; if (vertices[vertex][axis] < minv) minv = vertices[vertex][axis]; axis++; if (eol) { vertex++; axis=0; } return 1; } static int normal_cb(p_ply_argument argument) /* * normal_cb(argument): RPly normal callback. Store the normal for each vertex. */ { long eol; ply_get_argument_user_data(argument, NULL, &eol); normals[vertex-1][axis] = (GLfloat)ply_get_argument_value(argument); axis++; if (eol) { //vertex++; axis=0; } return 1; } static int face_cb(p_ply_argument argument) /* * face_cb(argument): RPly face callback. Store vertex indices for each face. */ { long length, value_index; p_ply_property property; ply_get_argument_property(argument, &property, &length, &value_index); ply_get_property_info(property, NULL, NULL, &face_length_t, &face_value_t); switch (value_index) { case 0: case 1: faces[face][fvertex++] = (long)ply_get_argument_value(argument); break; case 2: faces[face][fvertex++] = (long)ply_get_argument_value(argument); face++; fvertex=0; break; default: break; } return 1; } static int neighbours_cb(p_ply_argument argument) /* * neighbours_cb(argument): RPly neighbour callback. Retrieves the neighbour indices. * home variable indexs current cell, neighb indexs its neighbours. Bit of a cludge to * cover the case where neighbours ar found but not diffusion weightings. */ { long value_index; ply_get_argument_property(argument, NULL, NULL, &value_index); switch (value_index) { case 0: case 1: neighbours[home][neighb++] = (long)ply_get_argument_value(argument); break; case 2: neighbours[home][neighb++] = (long)ply_get_argument_value(argument); if (plyHasDiff == 0) home++; neighb=0; break; default: break; } return 1; } static int diffusion_cb(p_ply_argument argument) /* * diffusion_cb(argument): RPly diffusion weightings callback. */ { long value_index; ply_get_argument_property(argument, NULL, NULL, &value_index); switch (value_index) { case 0: case 1: diffusion[home][neighb++] = (GLfloat)ply_get_argument_value(argument); break; case 2: diffusion[home][neighb++] = (GLfloat)ply_get_argument_value(argument); home++; neighb=0; break; default: break; } return 1; } int outputPLY(char *filename) /* * outputPLY(filename): calls all the RPly output functions in correct order to write * a PLY file with all additional data (neighbours, normals, diffusion weightings). */ { int i,j; p_ply ply; ply = ply_create(filename, PLY_ASCII, NULL); /* add vertex element header info */ if (!ply_add_element(ply, "vertex", nvertices)) printf("cant add vertex element\n"); ply_add_scalar_property(ply, "x", vertex_t); ply_add_scalar_property(ply, "y", vertex_t); ply_add_scalar_property(ply, "z", vertex_t); ply_add_scalar_property(ply, "xnorm", vertex_t); ply_add_scalar_property(ply, "ynorm", vertex_t); ply_add_scalar_property(ply, "znorm", vertex_t); /* add faces element info */ if (!ply_add_element(ply, "face", nfaces)) printf("cant add face element\n"); ply_add_list_property(ply, "vertex_indices", face_length_t, face_value_t); /* add neighbours element info */ if (!ply_add_element(ply, "neighbours", nfaces)) printf("cant add neighbours element\n"); ply_add_list_property(ply, "vertex_indices", face_length_t, face_value_t); ply_add_list_property(ply, "diffusion_weighting", face_length_t, vertex_t); /* add comment saying created by our prog and added neighbours */ ply_add_comment(ply, "Extra neighbour and vertex normal data generated for Reaction-Diffusion simulations"); /* write the header */ if (!ply_write_header(ply)) printf("cant write header\n"); /* write the properties one by one */ for (i=0;i