hcsg.cpp | hcsg_modeler.cpp | collision.cpp | camera.cpp | vector.cpp | main.cpp | ||||||
hcsg.h | hcsg_modeler.h | collision.h | camera.h | vector.h |
// ************************************************************************** // HCSG Demo // (c) Bernie Freidin, 1999-2000 // ************************************************************************** #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include "strtok_r.h" #include "hcsg_modeler.h" #ifndef PI #define PI 3.1415926535897932384626433832795 #endif // **************************************************************************** // === SHAPES ================================================================= // **************************************************************************** enum CSG_SHAPETYPE { CSG_SHAPETYPE_UNDEFINED = 0, CSG_SHAPETYPE_POLYGON }; struct shape_t { int type; char name[24]; int vertcount; vec2 *vert; shape_t *next; }; // **************************************************************************** // === COORDS ================================================================= // **************************************************************************** struct coords_t { mat4 transform2; // final transformation mat4 transform1; // stack transformation mat4 local; // local transformation vec3 minv; // bounding box lower corner vec3 maxv; // bounding box upper corner mat4 axis; // axis transform }; // **************************************************************************** // === RECURSION ============================================================== // **************************************************************************** struct recurs_t { int counter; int marker; }; // **************************************************************************** // === EVENTS ================================================================= // **************************************************************************** struct event_t { int type; // uses CSG_OPTYPE int depth; coords_t coords; shape_t *shape; event_t *next; }; // ********************************************************* // Notes on EVENT structure: // // When a brush is added or subtracted, an EVENT entry is // created and added into the event list. A pointer to the // current (or specified) brush is stored alongside a -copy- // of the current coordinate frame. // // When PUSHCOORDS or POPCOORDS actions are invoked, an // EVENT entry is created and added into the event list. A // -copy- of the current coordinate frame is stored in the // EVENT entry, but the brush field is left as NULL. // // This makes it easy to re-position a single brush within // the geometry by changing the associated coordinate frame. // It also makes it easy to re-position a whole group of // primitives by inserting PUSHCOORDS and POPCOORDS events // into the list. // ********************************************************* // **************************************************************************** // === TAGS =================================================================== // **************************************************************************** enum CSG_TAG { CSG_TAG_X = 1, CSG_TAG_Y, CSG_TAG_Z, CSG_TAG_X1, CSG_TAG_X2, CSG_TAG_Y1, CSG_TAG_Y2, CSG_TAG_Z1, CSG_TAG_Z2 }; struct tag_t { char s1[4]; char s2[4]; int id; }; static tag_t CSG_TAG_LIST[] = { {"x", "", CSG_TAG_X}, {"y", "", CSG_TAG_Y}, {"z", "", CSG_TAG_Z}, {"bot", "y1", CSG_TAG_Y1}, {"top", "y2", CSG_TAG_Y2}, {"lft", "x1", CSG_TAG_X1}, {"rgt", "x2", CSG_TAG_X2}, {"fnt", "z1", CSG_TAG_Z1}, {"bak", "z2", CSG_TAG_Z2}, {"", "", -1} }; // **************************************************************************** // === COMMANDS =============================================================== // **************************************************************************** enum CSG_COMMAND_FORMAT { CSG_COMMAND_FORMAT_UNSPECIFIED = 0, CSG_COMMAND_FORMAT1, //CSG_COMMAND_FORMAT2, // = CSG_COMMAND_FORMAT3, // =( ... ) CSG_COMMAND_FORMAT4, // {" ",*} CSG_COMMAND_FORMAT5 // {" ",*}=( ... ) }; enum CSG_COMMAND { CSG_COMMAND_NOP = 0, CSG_COMMAND_RUNSCRIPT, // CSG_COMMAND_PUSHCOORDS, CSG_COMMAND_POPCOORDS, CSG_COMMAND_MATRIX, CSG_COMMAND_IDENTITY, CSG_COMMAND_BBOX, CSG_COMMAND_STEPBBOX, CSG_COMMAND_AXIS, CSG_COMMAND_ROTATE, CSG_COMMAND_ROTATEAXIS, CSG_COMMAND_TRANSLATE, CSG_COMMAND_SCALE, CSG_COMMAND_SHEAR, CSG_COMMAND_PREROTATE, CSG_COMMAND_PREROTATEAXIS, CSG_COMMAND_PRETRANSLATE, CSG_COMMAND_PRESCALE, CSG_COMMAND_PRESHEAR, // CSG_COMMAND_SHAPE, CSG_COMMAND_SPIRALSTAIR1, CSG_COMMAND_REGULARPOLY1, CSG_COMMAND_FREESHAPE, // CSG_COMMAND_DEPTH, CSG_COMMAND_CSGADD, CSG_COMMAND_CSGSUB, // CSG_COMMAND_LOOP, CSG_COMMAND_ENDLOOP, CSG_COMMAND_RETURN }; static void CSG_ExecNOP(char *paramstr[]); static void CSG_ExecRUNSCRIPT(char *paramstr[]); // static void CSG_ExecPUSHCOORDS(char *paramstr[]); static void CSG_ExecPOPCOORDS(char *paramstr[]); static void CSG_ExecMATRIX(char *paramstr[]); static void CSG_ExecIDENTITY(char *paramstr[]); static void CSG_ExecBBOX(char *paramstr[]); static void CSG_ExecSTEPBBOX(char *paramstr[]); static void CSG_ExecAXIS(char *paramstr[]); static void CSG_ExecROTATE(char *paramstr[]); static void CSG_ExecROTATEAXIS(char *paramstr[]); static void CSG_ExecTRANSLATE(char *paramstr[]); static void CSG_ExecSCALE(char *paramstr[]); static void CSG_ExecSHEAR(char *paramstr[]); static void CSG_ExecPREROTATE(char *paramstr[]); static void CSG_ExecPREROTATEAXIS(char *paramstr[]); static void CSG_ExecPRETRANSLATE(char *paramstr[]); static void CSG_ExecPRESCALE(char *paramstr[]); static void CSG_ExecPRESHEAR(char *paramstr[]); // static void CSG_ExecSHAPE(char *paramstr[]); static void CSG_ExecSPIRALSTAIR1(char *paramstr[]); static void CSG_ExecREGULARPOLY1(char *paramstr[]); static void CSG_ExecFREESHAPE(char *paramstr[]); // static void CSG_ExecDEPTH(char *paramstr[]); static void CSG_ExecCSGADD(char *paramstr[]); static void CSG_ExecCSGSUB(char *paramstr[]); // static void CSG_ExecLOOP(char *paramstr[]); static void CSG_ExecENDLOOP(char *paramstr[]); typedef void vf_t(char *paramstr[]); struct command_t { char name[24]; int format; int paramcount; // -1 indicates variable-length list int index; vf_t *func; }; static command_t CSG_COMMAND_LIST[] = { {"nop", 1, 0, CSG_COMMAND_NOP, CSG_ExecNOP}, // no-op {"runscript", 4, 0, CSG_COMMAND_RUNSCRIPT, CSG_ExecRUNSCRIPT}, // {"pushcoords", 1, 0, CSG_COMMAND_PUSHCOORDS, CSG_ExecPUSHCOORDS}, {"popcoords", 1, 0, CSG_COMMAND_POPCOORDS, CSG_ExecPOPCOORDS}, {"matrix", 3, 16, CSG_COMMAND_MATRIX, CSG_ExecMATRIX}, {"identity", 1, 0, CSG_COMMAND_IDENTITY, CSG_ExecIDENTITY}, {"bbox", 3, 6, CSG_COMMAND_BBOX, CSG_ExecBBOX}, {"stepbbox", 3, -1, CSG_COMMAND_STEPBBOX, CSG_ExecSTEPBBOX}, {"axis", 2, 1, CSG_COMMAND_AXIS, CSG_ExecAXIS}, {"rotate", 3, -1, CSG_COMMAND_ROTATE, CSG_ExecROTATE}, {"rotateaxis", 3, 4, CSG_COMMAND_ROTATEAXIS, CSG_ExecROTATEAXIS}, {"translate", 3, 3, CSG_COMMAND_TRANSLATE, CSG_ExecTRANSLATE}, {"scale", 3, 3, CSG_COMMAND_SCALE, CSG_ExecSCALE}, {"shear", 3, -1, CSG_COMMAND_SHEAR, CSG_ExecSHEAR}, {"prerotate", 3, -1, CSG_COMMAND_PREROTATE, CSG_ExecPREROTATE}, {"prerotateaxis", 3, 4, CSG_COMMAND_PREROTATEAXIS, CSG_ExecPREROTATEAXIS}, {"pretranslate", 3, 3, CSG_COMMAND_PRETRANSLATE, CSG_ExecPRETRANSLATE}, {"prescale", 3, 3, CSG_COMMAND_PRESCALE, CSG_ExecPRESCALE}, {"preshear", 3, -1, CSG_COMMAND_PRESHEAR, CSG_ExecPRESHEAR}, // {"shape", 5, -1, CSG_COMMAND_SHAPE, CSG_ExecSHAPE}, {"spiralstair1", 5, 4, CSG_COMMAND_SPIRALSTAIR1, CSG_ExecSPIRALSTAIR1}, {"regularpoly1", 5, 3, CSG_COMMAND_REGULARPOLY1, CSG_ExecREGULARPOLY1}, {"freeshape", 4, 0, CSG_COMMAND_FREESHAPE, CSG_ExecFREESHAPE}, // {"depth", 2, 1, CSG_COMMAND_DEPTH, CSG_ExecDEPTH}, {"csgadd", 4, 0, CSG_COMMAND_CSGADD, CSG_ExecCSGADD}, {"csgsub", 4, 0, CSG_COMMAND_CSGSUB, CSG_ExecCSGSUB}, // {"loop", 2, 1, CSG_COMMAND_LOOP, CSG_ExecLOOP}, {"endloop", 1, 0, CSG_COMMAND_ENDLOOP, CSG_ExecENDLOOP}, {"return", 1, 0, CSG_COMMAND_RETURN, CSG_ExecNOP}, {"", -1, -1, -1, NULL} }; // **************************************************************************** // === GLOBALS/PROTOTYPES ===================================================== // **************************************************************************** #define CSG_MAX_COORDS_DEPTH 10 #define CSG_MAX_RECURS_DEPTH 10 #define CSG_MAX_COMMAND_PARAMS 4 #define CSG_MAX_ARRAY_SIZE 200 // allows for 100-sided polygons static coords_t CSG_COORDS_STACK[CSG_MAX_COORDS_DEPTH]; static int CSG_COORDS_DEPTH; static recurs_t CSG_RECURS_STACK[CSG_MAX_RECURS_DEPTH]; static int CSG_RECURS_DEPTH; static int CSG_CURRENT_DEPTH; static shape_t *CSG_CURRENT_SHAPE; static FILE *CSG_CURRENT_FILE; static shape_t *CSG_SHAPE_LIST; static event_t *CSG_EVENT_LIST; static hull_t *CSG_WORLD; // shadow variable static int CSG_SHAPE_ID; // unique id static int CSG_REVERSE_ORDER = 0; // internal flag static int CSG_ParseTag(char *str); static double CSG_ParseScalarValue(char *str); static int CSG_ParseScalarArray(char *str, double *array); static char *CSG_ParseParamString(char **str, char markers[3]); static shape_t *CSG_FindShape(char name[], int create); static void CSG_FreeShape(shape_t *shape); static event_t *CSG_Event(int type, shape_t *shape); static void CSG_Update(coords_t *coords); static hull_t *CSG_Extrude(shape_t *shape); // **************************************************************************** // === FUNCTIONS ============================================================== // **************************************************************************** void CSG_InitializeModeler(hull_t *world) { CSG_PUSHNAME("InitializeModeler"); // ********************************************* // Allocates a new coordinate stack and sets the // stack depth to zero. Clears all shapes and // brushes. // ********************************************* CSG_COORDS_DEPTH = 2; CSG_RECURS_DEPTH = 0; CSG_CURRENT_DEPTH = 0; CSG_CURRENT_SHAPE = NULL; CSG_CURRENT_FILE = NULL; CSG_SHAPE_LIST = NULL; // dynamic pool CSG_EVENT_LIST = NULL; // dynamic pool CSG_WORLD = world; CSG_SHAPE_ID = 0; // ******************************** // Initialize two coordinate frames // ******************************** for(int i = 0; i < 2; i++) { coords_t *coords = &CSG_COORDS_STACK[i]; coords->transform1.identity(); coords->transform2.identity(); coords->axis.identity(); coords->local.identity(); coords->minv = vec3(-1.0, -1.0, -1.0); coords->maxv = vec3(+1.0, +1.0, +1.0); } CSG_POPNAME_; } int CSG_GetMemInUse_Modeler(void) { CSG_PUSHNAME("GetMemInUse_Modeler"); // ******************************************************** // Returns the amount of memory used by the modeler engine // ******************************************************** int mem = 0; if(!CSG_COORDS_STACK) { CSG_POPNAME(0); // assume modeler is uninitialized } for(shape_t *s = CSG_SHAPE_LIST; s; s = s->next) { mem += sizeof(shape_t); mem += sizeof(vec2) * s->vertcount; } for(event_t *e = CSG_EVENT_LIST; e; e = e->next) { mem += sizeof(event_t); } CSG_POPNAME(mem); } void CSG_RunScript(char *fname) { CSG_PUSHNAME("RunScript"); // *********************************************** // Opens a file and parses the contents, executing // commands as specified. // *********************************************** char linebuf[4001]; int brk = 0; FILE *fp = fopen(fname, "r"); if(!fp) { CSG_POPNAME_; // safe } CSG_CURRENT_FILE = fp; while(!brk && fgets(linebuf, 4000, fp)) { char *s1 = linebuf; while(!brk) { char *cmdstr = strtok_r(s1, " =\t\n\r", &s1); int i; if(!cmdstr) break; // no more commands if(cmdstr[0] == '/' && cmdstr[1] == '/') break; // comment (C++ style) if(cmdstr[0] == '#') break; // comment if(cmdstr[0] == ';') break; // comment // *********************** // Search command database // *********************** command_t *command; int len = strlen(cmdstr); for(i = 0; i < len; i++) { cmdstr[i] = tolower(cmdstr[i]); } for(i = 0;; i++) { command = &CSG_COMMAND_LIST[i]; if(!strcmp(cmdstr, command->name)) break; CSG_ASSERT(command->index >= 0, "unknown command"); } // *************** // Find parameters // *************** char *paramstr[CSG_MAX_COMMAND_PARAMS]; switch(command->format) { case CSG_COMMAND_FORMAT1: break; case CSG_COMMAND_FORMAT2: paramstr[0] = strtok_r(s1, " =:\t\n\r", &s1); paramstr[1] = NULL; break; case CSG_COMMAND_FORMAT3: paramstr[0] = CSG_ParseParamString(&s1, "[]"); paramstr[1] = NULL; break; case CSG_COMMAND_FORMAT4: paramstr[0] = CSG_ParseParamString(&s1, "\"\"*"); paramstr[1] = NULL; break; case CSG_COMMAND_FORMAT5: paramstr[0] = CSG_ParseParamString(&s1, "\"\"*"); paramstr[1] = CSG_ParseParamString(&s1, "[]"); break; default: CSG_ASSERT(0, "unknown command format"); } // *************** // Execute command // *************** if(command->index == CSG_COMMAND_RETURN) brk = 1; command->func(paramstr); } } fclose(fp); CSG_POPNAME_; } static int CSG_ParseTag(char *str) { CSG_PUSHNAME("ParseTag"); // ************ // Finds tag id // ************ CSG_ASSERT(str, "parse error"); int i, len = strlen(str); for(i = 0; i < len; i++) { str[i] = tolower(str[i]); } for(i = 0;; i++) { if(CSG_TAG_LIST[i].id < 0) break; if(!strcmp(CSG_TAG_LIST[i].s1, str) || !strcmp(CSG_TAG_LIST[i].s2, str) ) { CSG_POPNAME(CSG_TAG_LIST[i].id); } } CSG_POPNAME(0); } static double CSG_ParseScalarValue(char *str) { CSG_PUSHNAME("ParseScalarValue"); // ************************************************ // Parses a scalar value. Currently, we handle only // the forms , , and . // ************************************************ CSG_ASSERT(str, "parse error"); char *symbolstr; if(symbolstr = strchr(str, '*')) { CSG_POPNAME(atof(str)*atof(symbolstr+1)); } if(symbolstr = strchr(str, '/')) { CSG_POPNAME(atof(str)/atof(symbolstr+1)); } CSG_POPNAME(atof(str)); } static int CSG_ParseScalarArray(char *str, double *array) { CSG_PUSHNAME("ParseScalarArray"); CSG_ASSERT(str, "parse error"); char *s1 = str; char *s2 = strtok_r(s1, " ,:(){}\t\n\r", &s1); int k = 0; while(s2) { CSG_ASSERT(k < CSG_MAX_ARRAY_SIZE, "array too big"); array[k++] = CSG_ParseScalarValue(s2); s2 = strtok_r(s1, " ,:(){}\t\n\r", &s1); } CSG_POPNAME(k); } static char *CSG_ParseParamString(char **str, char markers[3]) { CSG_PUSHNAME("ParseParamString"); // ******************************************************* // Isolates a parameter list or name surrounded by markers // markers[0] and markers[1]. If markers[2] is specified, // the function returns NULL if marker[2] is found first. // ******************************************************* CSG_ASSERT(*str, "parse error"); CSG_ASSERT(strlen(markers) >= 2, "invalid marker string"); char *s1 = strchr(*str, markers[0]); char *s2 = strchr(*str, markers[2]); if(markers[2] && s2 && (s2 < s1 || !s1)) { // check for garbage //FIXME: check?? //CSG_ASSERT(!strtok(*str, " =\t\n\r"), "parse error"); *str = s2+1; CSG_POPNAME(NULL); } CSG_ASSERT(s1, "parse error"); // no beginning marker s1[0] = '\0'; s1++; CSG_ASSERT(!strtok(*str, " =\t\n\r"), "parse error"); // garbage s2 = strchr(s1, markers[1]); CSG_ASSERT(s2, "parse error"); // no ending marker s2[0] = '\0'; s2++; *str = s2; CSG_POPNAME(s1); } static shape_t *CSG_FindShape(char name[], int create) { CSG_PUSHNAME("FindShape"); // ********************************************** // Finds a shape with the specified name. If none // exists, a new shape is created if create=1. If // name is NULL and create=1, a uniquely-named // shape is created. If name is NULL and create=0 // the current shape is returned. // ********************************************** shape_t *sh = NULL; if(!name) { if(!create) { CSG_POPNAME(CSG_CURRENT_SHAPE); } } else for(sh = CSG_SHAPE_LIST; sh; sh = sh->next) { if(!strcmp(name, sh->name)) break; } if(!sh) { CSG_ASSERT(create, "can't find shape"); // ****************** // Allocate new shape // ****************** sh = (shape_t*)CSG_AllocMem(sizeof(shape_t), 1); if(name) { strncpy(sh->name, name, 23); } else { sprintf(sh->name, "____________SHAPE%i", CSG_SHAPE_ID++); } // ******************* // Link into main list // ******************* sh->next = CSG_SHAPE_LIST; CSG_SHAPE_LIST = sh; } CSG_POPNAME(sh); } static event_t *CSG_Event(int type, shape_t *shape) { CSG_PUSHNAME("Event"); // ************************************************* // Creates a new event with the specified type and // brush and copies the current coordinate frame // into it. The event is appended to the event list. // ************************************************* event_t *event = (event_t*)CSG_AllocMem(sizeof(event_t), 1); event->type = type; event->shape = shape; event->depth = CSG_CURRENT_DEPTH; event->coords = CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; event->next = CSG_EVENT_LIST; CSG_EVENT_LIST = event; CSG_POPNAME(event); } static void CSG_Copy(shape_t *dst, shape_t *src) { CSG_PUSHNAME("Copy[SHAPE]"); // *************** // Copy shape data // *************** shape_t *next = dst->next; // save pointer if(dst->vert) CSG_FreeMem(dst->vert); memcpy(dst, src, sizeof(shape_t)); if(src->vert) { dst->vert = (vec2*)CSG_AllocMem(sizeof(vec2), src->vertcount); memcpy(dst->vert, src->vert, sizeof(vec2) * src->vertcount); } dst->next = next; CSG_POPNAME_; } static void CSG_FreeShape(shape_t *shape) { CSG_PUSHNAME("FreeShape"); if(CSG_SHAPE_LIST == shape) { CSG_SHAPE_LIST = shape->next; } else { for(shape_t *s = CSG_SHAPE_LIST; s; s = s->next) { if(s->next == shape) { s->next = shape->next; break; } } CSG_ASSERT(0, "shape missing from list"); } CSG_FreeMem(shape->vert); CSG_FreeMem(shape); CSG_POPNAME_; } static void CSG_Update(coords_t *coords) { CSG_PUSHNAME("Update[COORDS]"); coords_t *coprev = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-2]; CSG_ASSERT(CSG_COORDS_DEPTH > 1, "coordinate stack underflow"); CSG_ASSERT(coords == coprev+1, "tried to modify non-current matrix"); mat4 bbox; bbox.identity(); for(int i = 0; i < 3; i++) { bbox[i][i] = (coords->maxv[i] - coords->minv[i]) / 2.0; bbox[i][3] = (coords->maxv[i] + coords->minv[i]) / 2.0; } coords->transform1 = coprev->transform1 * coords->local; coords->transform2 = coords->transform1 * bbox * coords->axis; CSG_POPNAME_; } static hull_t *CSG_Extrude(shape_t *shape) { CSG_PUSHNAME("Extrude"); // *************************************************** // Extrude a specified shape to a HCSG hull (using the // current coordinate frame). // *************************************************** coords_t *coords = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; vref_t vref[CSG_MAX_VERTS_PER_HULL]; sref_t sref[CSG_MAX_FACES_PER_HULL]; int n, vc = shape->vertcount; // **************** // Compute vertices // **************** for(n = 0; n < vc; n++) { vec3 v1 = vec3(shape->vert[n].x, shape->vert[n].y, -1.0); vec3 v2 = vec3(shape->vert[n].x, shape->vert[n].y, +1.0); v1 = coords->transform2*v1; v2 = coords->transform2*v2; vref[n] = CSG_AddToVertPool(v1); vref[n+vc] = CSG_AddToVertPool(v2); } // **************** // Compute surfaces // **************** for(n = 0; n < vc; n++) { sref[n] = CSG_AddToSurfPool(vref, (n 0?n-1:vc-1]); } CSG_Update(face); face = CSG_Alloc(hull, vc, sref[vc+1]); for(n = vc-1; n >= 0; n--) { CSG_AddToPoly(face, vref[vc+n], sref[n]); } CSG_Update(face); for(n = 0; n < vc; n++) { int n1 = (n < vc-1) ? n+1 : 0; int n2 = (n > 0) ? n-1 : vc-1; face = CSG_Alloc(hull, 4, sref[n]); CSG_AddToPoly(face, vref[n1], sref[n1]); CSG_AddToPoly(face, vref[n], sref[vc]); CSG_AddToPoly(face, vref[vc+n], sref[n2]); CSG_AddToPoly(face, vref[vc+n1], sref[vc+1]); CSG_Update(face); } CSG_Update(hull); hull->shape = shape; CSG_POPNAME(hull); } static void CSG_ExecNOP(char *paramstr[]) { CSG_PUSHNAME("ExecNOP"); CSG_POPNAME_; } static void CSG_ExecRUNSCRIPT(char *paramstr[]) { CSG_PUSHNAME("ExecRUNSCRIPT"); // **************** // Run a new script // **************** FILE *save = CSG_CURRENT_FILE; CSG_RunScript(paramstr[0]); CSG_CURRENT_FILE = save; CSG_POPNAME_; } static void CSG_ExecPUSHCOORDS(char *paramstr[]) { CSG_PUSHNAME("ExecPUSHCOORDS"); // ****************************************************** // Create a new coordinate frame (identity) and push this // onto the coordinate stack. // // Note that the preferred axis is reset to Z by this // function (this is slightly counter-intuitive, if the // preferred axis is thought of as a global state). // ****************************************************** CSG_ASSERT(CSG_COORDS_DEPTH > 1, "coordinate stack underflow"); CSG_ASSERT(CSG_COORDS_DEPTH < CSG_MAX_COORDS_DEPTH, "coordinate stack overflow"); CSG_Event(CSG_PUSH, NULL); CSG_COORDS_DEPTH++; coords_t *coprev = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-2]; coords_t *coords = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; memcpy(coords, coprev, sizeof(coords_t)); coords->local.identity(); CSG_Update(coords); CSG_POPNAME_; } static void CSG_ExecPOPCOORDS(char *paramstr[]) { CSG_PUSHNAME("ExecPOPCOORDS"); // **************************************************** // Pop a coordinate frame from the stack. Note that the // stack depth must never fall below 2 (not 1). // **************************************************** CSG_ASSERT(CSG_COORDS_DEPTH > 1, "coordinate stack underflow"); CSG_Event(CSG_POP, NULL); CSG_COORDS_DEPTH--; CSG_POPNAME_; } static void CSG_ExecMATRIX(char *paramstr[]) { CSG_PUSHNAME("ExecMATRIX"); coords_t *coords = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; double tmp[CSG_MAX_ARRAY_SIZE]; int count = CSG_ParseScalarArray(paramstr[0], tmp); CSG_ASSERT(count == 16, "incorrect array size"); memcpy(&coords->local, tmp, sizeof(mat4)); CSG_Update(coords); CSG_POPNAME_; } static void CSG_ExecIDENTITY(char *paramstr[]) { CSG_PUSHNAME("ExecIDENTITY"); coords_t *coords = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; coords->local.identity(); CSG_Update(coords); CSG_POPNAME_; } static void CSG_ExecBBOX(char *paramstr[]) { CSG_PUSHNAME("ExecBBOX"); // ************************************************* // Change the current coordinate frame so that the // coordinates (-1,-1,-1) to (1,1,1) fall within the // specified bounding box. // ************************************************* coords_t *coords = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; double tmp[CSG_MAX_ARRAY_SIZE]; int count = CSG_ParseScalarArray(paramstr[0], tmp); CSG_ASSERT(count == 6, "incorrect array size"); CSG_ASSERT(tmp[0] < tmp[3], "invalid coordinates"); CSG_ASSERT(tmp[1] < tmp[4], "invalid coordinates"); CSG_ASSERT(tmp[2] < tmp[5], "invalid coordinates"); coords->minv = vec3(tmp[0], tmp[1], tmp[2]); coords->maxv = vec3(tmp[3], tmp[4], tmp[5]); CSG_Update(coords); CSG_POPNAME_; } static void CSG_ExecSTEPBBOX(char *paramstr[]) { CSG_PUSHNAME("ExecSTEPBBOX"); char *s1 = paramstr[0]; char *s2 = strtok_r(s1, " ,:[]{}\t\n\r", &s1); coords_t *coords = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; while(s2) { int side; double delta; side = CSG_ParseTag(s2); s2 = strtok_r(s1, " ,:[]{}\t\n\r", &s1); delta = CSG_ParseScalarValue(s2); s2 = strtok_r(s1, " ,:[]{}\t\n\r", &s1); switch(side) { case CSG_TAG_X1: coords->minv.x += delta; break; case CSG_TAG_Y1: coords->minv.y += delta; break; case CSG_TAG_Z1: coords->minv.z += delta; break; case CSG_TAG_X2: coords->maxv.x += delta; break; case CSG_TAG_Y2: coords->maxv.y += delta; break; case CSG_TAG_Z2: coords->maxv.z += delta; break; default: CSG_ASSERT(0, "invalid tag"); } } CSG_Update(coords); CSG_POPNAME_; } static void CSG_ExecAXIS(char *paramstr[]) { CSG_PUSHNAME("ExecAXIS"); coords_t *coords = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; coords->axis.zero(); coords->axis.w.w = 1.0; switch(CSG_ParseTag(paramstr[0])) { case CSG_TAG_X: coords->axis.x.z = -1.0; coords->axis.y.y = +1.0; coords->axis.z.x = +1.0; break; case CSG_TAG_Y: coords->axis.x.x = +1.0; coords->axis.y.z = +1.0; coords->axis.z.y = -1.0; break; case CSG_TAG_Z: coords->axis.x.x = +1.0; coords->axis.y.y = +1.0; coords->axis.z.z = +1.0; break; default: CSG_ASSERT(0, "invalid axis; must be X, Y, or Z"); } CSG_Update(coords); CSG_POPNAME_; } static void CSG_ExecROTATE(char *paramstr[]) { CSG_PUSHNAME("ExecROTATE"); coords_t *coords = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; char *s1 = paramstr[0]; char *s2 = strtok_r(s1, " ,:[]{}\t\n\r", &s1); mat4 m; m.identity(); while(s2) { int axis; double theta; axis = CSG_ParseTag(s2); s2 = strtok_r(s1, " ,:[]{}\t\n\r", &s1); theta = CSG_ParseScalarValue(s2); s2 = strtok_r(s1, " ,:[]{}\t\n\r", &s1); // ********************************************************** // NOTE: Successive rotations can introduce floating-point // errors, so avoid unecessary rotations when possible (i.e., // use PUSHCOORDS/POPCOORDS instead of rotating backwards). // ********************************************************** while(theta > +180.0) theta -= 360.0; while(theta <= -180.0) theta += 360.0; if(theta == 0.0) continue; double c, s; if(theta == -90.0) c = 0.0, s = -1.0; else if(theta == +90.0) c = 0.0, s = +1.0; else if(theta == 180.0) c = -1.0, s = 0.0; else { c = cos(theta*PI/180.0); s = sin(theta*PI/180.0); } switch(axis) { case CSG_TAG_X: m = m*mat4::rotX(c, s); break; case CSG_TAG_Y: m = m*mat4::rotY(c, s); break; case CSG_TAG_Z: m = m*mat4::rotZ(c, s); break; default: CSG_ASSERT(0, "invalid tag"); } } if(CSG_REVERSE_ORDER) coords->local = m * coords->local; else coords->local = coords->local * m; CSG_REVERSE_ORDER = 0; CSG_Update(coords); CSG_POPNAME_; } static void CSG_ExecROTATEAXIS(char *paramstr[]) { CSG_PUSHNAME("ExecROTATEAXIS"); CSG_POPNAME_; } static void CSG_ExecTRANSLATE(char *paramstr[]) { CSG_PUSHNAME("ExecTRANSLATE"); coords_t *coords = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; double tmp[CSG_MAX_ARRAY_SIZE]; int count = CSG_ParseScalarArray(paramstr[0], tmp); CSG_ASSERT(count == 3, "incorrect array size"); mat4 m(1.0, 0.0, 0.0, tmp[0], 0.0, 1.0, 0.0, tmp[1], 0.0, 0.0, 1.0, tmp[2], 0.0, 0.0, 0.0, 1.0); if(CSG_REVERSE_ORDER) coords->local = m * coords->local; else coords->local = coords->local * m; CSG_REVERSE_ORDER = 0; CSG_Update(coords); CSG_POPNAME_; } static void CSG_ExecSCALE(char *paramstr[]) { CSG_PUSHNAME("ExecSCALE"); coords_t *coords = &CSG_COORDS_STACK[CSG_COORDS_DEPTH-1]; double tmp[CSG_MAX_ARRAY_SIZE]; int count = CSG_ParseScalarArray(paramstr[0], tmp); CSG_ASSERT(count == 3, "incorrect array size"); mat4 m(tmp[0], 0.0, 0.0, 0.0, 0.0, tmp[1], 0.0, 0.0, 0.0, 0.0, tmp[2], 0.0, 0.0, 0.0, 0.0, 1.0); if(CSG_REVERSE_ORDER) coords->local = m * coords->local; else coords->local = coords->local * m; CSG_REVERSE_ORDER = 0; CSG_Update(coords); CSG_POPNAME_; } static void CSG_ExecSHEAR(char *paramstr[]) { CSG_PUSHNAME("ExecSHEAR"); CSG_POPNAME_; } static void CSG_ExecPREROTATE(char *paramstr[]) { CSG_REVERSE_ORDER = 1; CSG_ExecROTATE(paramstr); } static void CSG_ExecPREROTATEAXIS(char *paramstr[]) { CSG_REVERSE_ORDER = 1; CSG_ExecROTATEAXIS(paramstr); } static void CSG_ExecPRETRANSLATE(char *paramstr[]) { CSG_REVERSE_ORDER = 1; CSG_ExecTRANSLATE(paramstr); } static void CSG_ExecPRESCALE(char *paramstr[]) { CSG_REVERSE_ORDER = 1; CSG_ExecSCALE(paramstr); } static void CSG_ExecPRESHEAR(char *paramstr[]) { CSG_REVERSE_ORDER = 1; CSG_ExecSHEAR(paramstr); } static void CSG_ExecSHAPE(char *paramstr[]) { CSG_PUSHNAME("ExecSHAPE"); shape_t *sh = CSG_FindShape(paramstr[0], 1); double tmp[CSG_MAX_ARRAY_SIZE]; int count = CSG_ParseScalarArray(paramstr[1], tmp); sh->type = CSG_SHAPETYPE_POLYGON; sh->vertcount = count/2; CSG_ASSERT(sh->vertcount >= 3, "too few vertices"); CSG_FreeMem(sh->vert); sh->vert = (vec2*)CSG_AllocMem(sizeof(vec2), sh->vertcount); memcpy(sh->vert, tmp, sizeof(vec2)*sh->vertcount); CSG_CURRENT_SHAPE = sh; double maxcomp = sh->vert[0].x; int i; for(i = 0; i < sh->vertcount; i++) { if(maxcomp < sh->vert[i].x) maxcomp = sh->vert[i].x; if(maxcomp < sh->vert[i].y) maxcomp = sh->vert[i].y; } if(maxcomp < 1.0) maxcomp = 1.0; //FIXME: use _min for(i = 0; i < sh->vertcount; i++) { sh->vert[i] /= maxcomp; } CSG_POPNAME_; } static void CSG_ExecSPIRALSTAIR1(char *paramstr[]) { CSG_PUSHNAME("ExecSPIRALSTAIR1"); // THETA0, THETASTEP, INNERRADIUS, OUTERPOINTS // Useful for making spiral staircases shape_t *sh = CSG_FindShape(paramstr[0], 1); double tmp[CSG_MAX_ARRAY_SIZE]; int count = CSG_ParseScalarArray(paramstr[1], tmp); CSG_ASSERT(count == 4, "incorrect array size"); CSG_ASSERT(tmp[3] == floor(tmp[3]), "integer required"); CSG_ASSERT(tmp[3] >= 2.0, "too few outer points"); CSG_ASSERT(tmp[2] >= 0.0 && tmp[2] < 1.0, "invalid inner radius"); CSG_ASSERT(tmp[1] > 0.0 && tmp[1] < 180.0, "invalid angle"); sh->vertcount = (int)tmp[3]+1; if(tmp[2] > 0.0) sh->vertcount++; CSG_ASSERT(sh->vertcount >= 3, "too few vertices"); sh->vert = (vec2*)CSG_AllocMem(sizeof(vec2), sh->vertcount); double angle = tmp[0]*PI/180.0; double delta = tmp[1]*PI/180.0; int n; for(n = 0; n < (int)tmp[3]; n++) { sh->vert[n].x = cos(angle); sh->vert[n].y = -sin(angle); angle += delta/(tmp[3]-1.0); } sh->vert[n] = sh->vert[n-1]*tmp[2]; if(tmp[2] > 0.0) { sh->vert[n+1] = sh->vert[0]*tmp[2]; } CSG_CURRENT_SHAPE = sh; CSG_POPNAME_; } static void CSG_ExecREGULARPOLY1(char *paramstr[]) { CSG_PUSHNAME("ExecREGULARPOLY1"); // THETA0, THETASTEP, NPOINTS shape_t *sh = CSG_FindShape(paramstr[0], 1); double tmp[CSG_MAX_ARRAY_SIZE]; int count = CSG_ParseScalarArray(paramstr[1], tmp); CSG_ASSERT(count == 3, "incorrect array size"); CSG_ASSERT(tmp[2] == floor(tmp[2]), "integer required"); sh->vertcount = (int)tmp[2]; CSG_ASSERT(sh->vertcount >= 3, "too few vertices"); sh->vert = (vec2*)CSG_AllocMem(sizeof(vec2), sh->vertcount); double angle = tmp[0]*PI/180.0; double delta = tmp[1]*PI/180.0; for(int n = 0; n < sh->vertcount; n++) { sh->vert[n].x = cos(angle); sh->vert[n].y = -sin(angle); angle += delta; } CSG_CURRENT_SHAPE = sh; CSG_POPNAME_; } static void CSG_ExecFREESHAPE(char *paramstr[]) { CSG_PUSHNAME("ExecFREESHAPE"); shape_t *sh = CSG_FindShape(paramstr[0], 0); CSG_FreeShape(sh); CSG_POPNAME_; } static void CSG_ExecDEPTH(char *paramstr[]) { CSG_PUSHNAME("ExecDEPTH"); // ************************************* // Set current depth for HCSG operations // ************************************* double val = CSG_ParseScalarValue(paramstr[0]); CSG_ASSERT(val == floor(val), "depth must be integer"); CSG_CURRENT_DEPTH = (int)val; CSG_POPNAME_; } struct _hull_entry_t{ hull_t *hull; int depth; }; static _hull_entry_t _HULL_LIST[1000]; static int _HULL_COUNT = 0; int _CSG_AddHull(void) { static int count = 0; if(count >= _HULL_COUNT) return 1; // finished CSG_BooleanOp(CSG_WORLD, _HULL_LIST[count].hull, _HULL_LIST[count].depth, CSG_ADD, 0); count++; return 0; } static void CSG_ExecCSGADD(char *paramstr[]) { CSG_PUSHNAME("ExecCSGADD"); shape_t *shape = CSG_FindShape(paramstr[0], 0); hull_t *hull = CSG_Extrude(shape); CSG_Event(CSG_ADD, shape); // --RECENT CHANGE-- // CSG_BooleanOp(CSG_WORLD, hull, CSG_CURRENT_DEPTH, CSG_ADD, 0); // CSG_Free(hull); // BooleanOp CSG_ADD clones hull // Instead of adding the hull to the world, we append it to a list // of hulls to add. This makes it easier to watch the b-rep // updating in realtime, as we can do the hull addition in the // main event loop. _HULL_LIST[_HULL_COUNT].hull = hull; _HULL_LIST[_HULL_COUNT].depth = CSG_CURRENT_DEPTH; _HULL_COUNT++; // --RECENT CHANGE-- CSG_POPNAME_; } static void CSG_ExecCSGSUB(char *paramstr[]) { CSG_PUSHNAME("ExecCSGSUB"); CSG_POPNAME_; } static void CSG_ExecLOOP(char *paramstr[]) { CSG_PUSHNAME("ExecLOOP"); // *************************************************** // WARNING: Loop declarations MUST be at the end of a // line, otherwise the marker will be incorrectly set! // *************************************************** double counter = CSG_ParseScalarValue(paramstr[0]); CSG_ASSERT(CSG_RECURS_DEPTH < CSG_MAX_RECURS_DEPTH, "recursion overflow"); CSG_ASSERT(counter == floor(counter), "counter must be integer"); CSG_ASSERT((int)counter > 0, "counter must be positive"); recurs_t *recurs = &CSG_RECURS_STACK[CSG_RECURS_DEPTH++]; recurs->counter = (int)counter; recurs->marker = (int)ftell(CSG_CURRENT_FILE); CSG_POPNAME_; } static void CSG_ExecENDLOOP(char *paramstr[]) { CSG_PUSHNAME("ExecENDLOOP"); // *************************************************** // WARNING: Endloop declarations MUST be at the end of // a line, otherwise the loop will not work properly! // *************************************************** CSG_ASSERT(CSG_RECURS_DEPTH > 0, "recursion underflow"); recurs_t *recurs = &CSG_RECURS_STACK[CSG_RECURS_DEPTH-1]; if(--recurs->counter > 0) { fseek(CSG_CURRENT_FILE, recurs->marker, SEEK_SET); } else { CSG_RECURS_DEPTH--; } CSG_POPNAME_; }
This page © Bernie Freidin, 2000.