
| 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, (n0?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.