/* * Plantsim * * Version 1.0 * * Grows vines around objects in an environment. Reads in wavefront .obj files * and can output results .obj files or in Showpoly format * * Author: William Fang * * Updated: 20th October 1995 */ #include #include #include #include "plantsim.h" #include "collDetect.h" #include "collIntMat.h" #include "plants.h" #include "testDB.h" #include "database.h" #ifdef DEBUG #define DBG(s) s #else #define DBG(s) #endif Boolean new_face; ObjNode *envObjs; int lineno = 0; #ifdef STATS /* statistical variables */ Ulong boundBoxTestCount = 0; Ulong boundBoxTestHit = 0; Ulong voxelTestCount = 0; Ulong polyTestCount = 0; Ulong rayTestCount = 0; Ulong rayPolyTestCount = 0; Ulong rayVoxelCount = 0; Ulong vineSegsGenerated = 0; Ulong leavesGenerated = 0; FILE *statsFile; static void genStats(void); #endif /* variables for command line args */ OutFormat outType = Showpoly; Boolean quiet = False; Boolean verbose = False; int startFrame = 1; int endFrame = 200; int frameInc = 1; Real framePsec = 25.0; int frameNum = 0; int nextFrame = 1; /* variables for controlling growth of vine plants */ Real c1 = 0.2; Real c2 = 0.2; int c3 = 5; Real c4 = 1.5; Real c5 = 0.03; Real c6 = 0.1; Real c7 = 0.2; Real c8 = 0.4; Real c9 = 0.4; int c10 = 5; Real cylr = 0.05; int cylf = 3; Tupple gravity = {0.0, -1.0, 0.0}; Tupple lightSource = {0.0, 10.0, 0.0}; int randSeed = -1; int delay = 50; Real cylHeight = 0.0; Real growthInc = 0.1; Real leafLength = 0.5; Real leafWidth = 0.3; Ulong lastEnvObj = 0; FILE *yyin, *outFile, *inFile; char *outName = NULL; char *inName = NULL; char *scriptName = NULL; char *mtlLibName = NULL; OutFunc outFunc; static Boolean parseConfig(FILE *, int *, ObjNode *, TuppleArray *); void parseArgs(int, char **); int yyerror(char *); int main(argc, argv) int argc; char **argv; { int confLineNum = 0; int i; Ulong ui, uj, ulimit; TuppleArray sPlants; PlantNode *plants, *ptemp; VinePlant *vp; VineSeg *vs; ObjDB database; ObjNode *obj; plants = NULL; sPlants.total = 0; sPlants.v = NULL; /* parse args and read in objects */ parseArgs(argc, argv); if (outName != NULL) { if ((outFile = fopen(outName, "w")) == NULL) { fprintf(stderr, "Error: couldn't create output file %s\n", outName); exit(EX_CANTCREAT); } } else { outFile = stdout; } if (inName == NULL) { if ((inName=(char *)calloc(MAXSTRING,sizeof(char))) == NULL) { fprintf(stderr, "Error: couldn't calloc room for input filename\n"); exit(1); } strcpy(inName, "grow.config"); } if ((inFile = fopen(inName, "r")) == NULL) { fprintf(stderr, "Error: couldn't read configuration file %s\n", inName); exit(EX_NOINPUT); } if (scriptName == NULL) { if ((scriptName=(char *)calloc(MAXSTRING,sizeof(char))) == NULL) { fprintf(stderr, "Error: couldn't calloc room for script name\n"); exit(1); } strcpy(scriptName, "render.sh"); } /* set the function pointer to the correct output function */ if (outType == Showpoly) outFunc = showPolyOutputPlants; else outFunc = waveFrontOutputPlants; /* parse the configuration file */ if (!quiet) fprintf(stderr, "Reading configuration file %s\n", inName); if (!parseConfig(inFile, &confLineNum, envObjs, &sPlants)) { fprintf(stderr, "Error: syntax error at line %d in %s\n", confLineNum, inName); exit(EX_DATAERR); } if (mtlLibName == NULL) { if ((mtlLibName=(char *)calloc(MAXSTRING,sizeof(char))) == NULL) { fprintf(stderr, "Error: couldn't calloc room for mtllib name\n"); exit(1); } strcpy(mtlLibName, "tree.mtl"); } /* have a set of starting points for the growth of vine plants, so set * them up */ for (i=0; i < sPlants.total; i+=2) { if ((ptemp=(PlantNode *)malloc(sizeof(PlantNode))) == NULL) { fprintf(stderr, "Error: couldn't malloc PlantNode\n"); exit(1); } if ((vp=(VinePlant *)malloc(sizeof(VinePlant))) == NULL) { fprintf(stderr, "Error: couldn't malloc VinePlant\n"); exit(1); } if ((vs=(VineSeg *)malloc(sizeof(VineSeg))) == NULL) { fprintf(stderr, "Error: couldn't malloc VineSeg\n"); exit(1); } /* set up initial vine seg */ vs->obj = NULL; vs->scale = 0.0; vs->scFacStart = 0.0; vs->scFacEnd = 0.0; vs->p2 = sPlants.v[i]; vs->n = sPlants.v[i+1]; vs->p2.i += c1 * vs->n.i; vs->p2.j += c1 * vs->n.j; vs->p2.k += c1 * vs->n.k; vs->end = False; vs->branch = False; vs->initial = True; vs->next = False; /* set up vine plant */ vp->gsegs = vs; vp->ssegs = NULL; vp->gleaves = NULL; vp->sleaves = NULL; vp->growthInc = growthInc; vp->potHead = NULL; vp->potTail = NULL; /* set up PlantNode */ ptemp->next = plants; plants = ptemp; plants->type = Vine; plants->stopped = False; plants->pl = vp; } if (sPlants.v != NULL) free(sPlants.v); /* create canonical vine segment cylinder */ if (cylHeight <= 0.0) cylHeight = c2; createCanonicalVineSeg(cylf, cylr); /* create canonical leaf */ createCanonicalLeaf(leafLength / 4.0, leafLength, leafWidth / 2.0); /* seed the random number generator */ if (randSeed == -1) { srand((unsigned) (randSeed = (int) time(NULL))); } else { srand((unsigned) randSeed); } /* set up the database of objects */ database.objects = envObjs; database.cim = (CollIntMat *)malloc(sizeof(CollIntMat)); if (database.cim == NULL) { fprintf(stderr, "Error: couldn't malloc CollIntMat\n"); exit(1); } initCollIntMatrix(database.cim); /* all the objects that have been read in are stationary objects that don't * change, so they don't need to be tested for intersection with each * other. Setup the collision interest matrix accordingly */ for (ui=0,ulimit=envObjs->id; ui <= ulimit; ui++) { for (uj=0; uj <= ulimit; uj++) { if (!setCollIntMatrix(ui, uj, database.cim, 0)) { fprintf(stderr, "Error: couldn't set coll interest matrix\n"); exit(1); } } } /* take care of global bounding volumes for these objects. The bounding * volume values, as well as the transformation matrices for these * objects don't change with time. */ for (obj=database.objects; obj != NULL; obj=obj->next) calcBoundingBox(obj); /* The environment objects have ids between 0 and lastEnvObj. This is * useful info to help in setting the CIM, and also in out putting only * the plants */ lastEnvObj = ulimit; /* output generation information */ if (!quiet) outputInfo(stdout); /* if output type is showpoly, output the environment objects as * background */ if (outType == Showpoly) { outputInfo(outFile); fprintf(outFile, "Background\n"); for (obj=database.objects; obj != NULL; obj=obj->next) showPolyOutputObj(obj); } /* set up the frame info */ nextFrame = startFrame; /* grow the plants */ if (!quiet) fprintf(stderr, "Growing plant ...\n"); growPlants(&database, plants); if (frameNum <= endFrame) { fprintf(stderr, "Warning: finished at frame %d. Didn't reach ", frameNum); fprintf(stderr, "desired frame %d\n", endFrame); } fclose (yyin); fclose (outFile); #ifdef STATS if ((statsFile=fopen("plantsim.stats", "w")) == NULL) { fprintf(stderr, "Error: couldn't create output stats file "); fprintf(stderr, "planstsim.stats\n"); exit(EX_CANTCREAT); } genStats(); fclose (statsFile); #endif return 0; } int yyerror(s) char *s; { fprintf(stderr, "yyerror: %s ",s); fprintf(stderr, "line %d\n", lineno); } /* * Given the pointer to the configuration file, reads it in and sets up the * variables as specified in the file. Also reads in the associated * transforms for each object. * * The file consists of labels and the values assigned to them. * * Label value * c1 * c2 * c3 * c4 * c5 * c6 * c7 * c8 * c9 * c10 * VineSead * LightSource * RandomSeed * Delay * Gravity * Radius * Faces * GrowthInc * LeafLength * LeafWidth * MatLibFile * * Labels not in the file are left with their default values. * * The rest of the file contains the local to global transforms for each of * the environmental objects read in. The start of the section is marked with * the label "Transforms". Each transform is in the following format: * * T * * * * * The transforms are read in reverse order (that is the first transform read * corresponds to the last object read in). * * Params fp pointer to the configuration file * line return the line number where error occured at (if any) * objs linked list of objects * sPlants starting point for growing vine plants * * Globals see above * * Returns True if the file was read successfully, False and the line number * where an error was encountered */ static Boolean parseConfig(fp, line, objs, sPlants) FILE *fp; int *line; ObjNode *objs; TuppleArray *sPlants; { char buf[MAXSTRING+1], *token; int i, j; ObjNode *o = objs; Ulong target, count; *line = 0; target = objs->id+1; count = 0; while (fgets(buf, MAXSTRING+1, fp) != NULL) { (*line)++; if ((token=strtok(buf, " \t\n")) == NULL) /* empty line so continue */ continue; if (token[0] == '#') /* this line is a comment so continue */ continue; if (token[0] == 'c') { /* one of the constants */ if (strcmp(token, "c1") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((c1 = atof(token)) <= 0.0) return False; } else if (strcmp(token, "c2") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((c2 = atof(token)) <= 0.0) return False; } else if (strcmp(token, "c3") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((c3 = atoi(token)) < 1) return False; } else if (strcmp(token, "c4") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((c4 = atof(token)) <= 0.0) return False;; } else if (strcmp(token, "c5") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((c5 = atof(token)) < 0.0) return False;; } else if (strcmp(token, "c6") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((c6 = atof(token)) < 0.0) return False; } else if (strcmp(token, "c7") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((c7 = atof(token)) < 0.0) return False; } else if (strcmp(token, "c8") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((c8 = atof(token)) < 0.0) return False ; } else if (strcmp(token, "c9") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((c9 = atof(token)) < 0.0) return False; } else if (strcmp(token, "c10") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((c10 = atoi(token)) < 0) return False; } else { /* unknown token */ return False; } } else if (strcmp(token, "VineSead") == 0) { sPlants->total += 2; sPlants->v = (Tupple *)realloc(sPlants->v, sizeof(Tupple) * sPlants->total); if (sPlants->v == NULL) { fprintf(stderr, "Error: couldn't realloc Tupple Array\n"); exit(1); } /* read in point */ if ((token = strtok(NULL, " \t")) == NULL) return False; sPlants->v[sPlants->total-2].i = atof(token); if ((token = strtok(NULL, " \t")) == NULL) return False; sPlants->v[sPlants->total-2].j = atof(token); if ((token = strtok(NULL, " \t")) == NULL) return False; sPlants->v[sPlants->total-2].k = atof(token); /* read in normal of polygon point is sitting on */ if ((token = strtok(NULL, " \t")) == NULL) return False; sPlants->v[sPlants->total-1].i = atof(token); if ((token = strtok(NULL, " \t")) == NULL) return False; sPlants->v[sPlants->total-1].j = atof(token); if ((token = strtok(NULL, " \t")) == NULL) return False; sPlants->v[sPlants->total-1].k = atof(token); } else if (strcmp(token, "LightSource") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; lightSource.i = atof(token); if ((token = strtok(NULL, " \t")) == NULL) return False; lightSource.j = atof(token); if ((token = strtok(NULL, " \t")) == NULL) return False; lightSource.k = atof(token); } else if (strcmp(token, "RandomSeed") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; randSeed = atoi(token); } else if (strcmp(token, "Delay") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((delay = atoi(token)) < 0) return False; } else if (strcmp(token, "Gravity") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; gravity.i = atof(token); if ((token = strtok(NULL, " \t")) == NULL) return False; gravity.j = atof(token); if ((token = strtok(NULL, " \t")) == NULL) return False; gravity.k = atof(token); } else if (strcmp(token, "Radius") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((cylr = atof(token)) <= 0.0) return False; } else if (strcmp(token, "Faces") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((cylf = atoi(token)) < 3) return False; } else if (strcmp(token, "GrowthInc") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((growthInc = atof(token)) <= 0.0) return False; if (growthInc > 1.0) return False; } else if (strcmp(token, "LeafLength") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((leafLength = atof(token)) <= 0.0) return False; } else if (strcmp(token, "LeafWidth") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; if ((leafWidth = atof(token)) <= 0.0) return False; } else if (strcmp(token, "MatLibFile") == 0) { if ((token = strtok(NULL, " \t")) == NULL) return False; mtlLibName=(char *)malloc(sizeof(char)*MAXSTRING); strncpy(mtlLibName, token, MAXSTRING-1); } else if (strcmp(token, "Transforms") == 0) { /* the rest of the file contains the object transformations */ while (fgets(buf, MAXSTRING+1, fp) != NULL) { (*line)++; if ((token = strtok(buf, " \t\n")) == NULL) /* empty line */ continue; if (token[0] == '#') /* comment */ continue; if (strcmp(token, "T") != 0) return False; if (o->loc2glob == NULL) o->loc2glob = (Transform *)malloc(sizeof(Transform)); if (o->loc2glob == NULL) { fprintf(stderr, "Error: couldn't malloc Transform\n"); exit(1); } for (i=0; i < 3; i++) { (*line)++; if (fgets(buf, MAXSTRING+1, fp) == NULL) return False; if ((token = strtok(buf, " \t")) == NULL) return False; o->loc2glob->m[i][0] = atof(token); for (j=1; j < 4; j++) { if ((token = strtok(NULL, " \t")) == NULL) return False; o->loc2glob->m[i][j] = atof(token); } } count++; if (count == target) return True; o = o->next; } /* didn't read in enough transforms */ return False; } else { /* unknown token */ return False; } } if (count != target) { fprintf(stderr, "Error: Not enough Transforms specified\n"); return False; } return True; } /* * Given a file pointer, outputs the plant generation parameters * * Params fp file pointer * * Globals * * Returns nothing */ void outputInfo(fp) FILE *fp; { time_t tp = time(NULL); /* output generation information */ fprintf(fp, "# %s\n", asctime(localtime(&tp))); fprintf(fp, "# RandSeed %d\n", randSeed); fprintf(fp, "#\n# Starting Frame: %d\n", startFrame); fprintf(fp, "# Stopping Frame: %d (requested)\n", endFrame); fprintf(fp, "# Frame step: %d\n", frameInc); fprintf(fp, "#\n# c1 %f\n", c1); fprintf(fp, "# c2 %f\n", c2); fprintf(fp, "# c3 %f\n", c3); fprintf(fp, "# c4 %f\n", c4); fprintf(fp, "# c5 %f\n", c5); fprintf(fp, "# c6 %f\n", c6); fprintf(fp, "# c7 %f\n", c7); fprintf(fp, "# c8 %f\n", c8); fprintf(fp, "# c9 %f\n", c9); fprintf(fp, "# c10 %f\n", c10); fprintf(fp, "# Radius %f\n", cylr); fprintf(fp, "# faces %d\n", cylf); fprintf(fp, "# Cylinder Height %f\n", cylHeight); fprintf(fp, "# Delay %d\n", delay); fprintf(fp, "# faces %d\n", cylf); fprintf(fp, "# Growth Inc %f\n", growthInc); fprintf(fp, "# LeafLength %f\n", leafLength); fprintf(fp, "# LeafWidth %f\n", leafWidth); fprintf(fp, "# Gravity %f %f %f\n", gravity.i, gravity.j, gravity.k); fprintf(fp, "# Light Source %f %f %f\n\n", lightSource.i, lightSource.j, lightSource.k); } #ifdef STATS /* * generates and outputs the statistical counts and totals * * Params none * * Globals boundBoxTestCount number of succesful global bounding box * tests * boundBoxTestHit bounding box tests where objects did * actually intersect or contact * voxelTestCount number of voxels checked * polyTestCount number of polygon vs polygon tests * rayTestCount number of rays tested against objects * rayPolyTestCount number of polygons tested against rays * rayVoxelCount number of voxels traversed by rays * vineSegsGenerated number of vine segments generated * leavesGenerated number of leaves generated * statsFile output file for the stats * * Returns nothing */ static void genStats(void) { fprintf(statsFile, "%u\n", boundBoxTestCount); fprintf(statsFile, "%u\n", boundBoxTestHit); fprintf(statsFile, "%u\n", voxelTestCount); fprintf(statsFile, "%u\n", polyTestCount); fprintf(statsFile, "%u\n", rayTestCount); fprintf(statsFile, "%u\n", rayPolyTestCount); fprintf(statsFile, "%u\n", rayVoxelCount); fprintf(statsFile, "%u\n", vineSegsGenerated); fprintf(statsFile, "%u\n", leavesGenerated); fprintf(statsFile, "%f.2\n", (double) boundBoxTestHit / (double) boundBoxTestCount * 100.0); fprintf(statsFile, "%f.2\n", (double) voxelTestCount / (double) boundBoxTestCount * 0.5); fprintf(statsFile, "%f.2\n", (double) polyTestCount / (double) boundBoxTestCount); fprintf(statsFile, "%f.2\n", (double) rayPolyTestCount / (double) rayTestCount); fprintf(statsFile, "%f.2\n", (double) rayVoxelCount / (double) rayTestCount); } #endif