/* xd - X11 demo framefork Copyright (C) 2002 Ian Cowburn (ianc@noddybox.demon.co.uk) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------- */ static const char id[]="$Id$"; #include #include #include #include #include #include #include #include #include #include #include #include #include /* ---------------------------------------- HANDY MACROS */ #ifndef TRUE #define TRUE True #endif #ifndef FALSE #define FALSE False #endif #ifndef M_PI # define M_PI 3.14159265358979323846 #endif #define RAD(x) ((x)*M_PI/180.0) #define DEG(x) ((x)/(M_PI/180.0)) #define MIN(a,b) ((a)<(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b)) #define LIMIT(v,m,l) MIN((m),MAX((v),(l))) #define RND(x) (rand()%(x)) #define RND2(h,l) ((rand()%((h)-(l)))+(l)) #define XY(x,y) ((x) + (y) * width) /* ---------------------------------------- TYPES */ typedef enum {UsesImage, UsesPixmap} DType; typedef enum {MousePress, MouseRelease, MouseMove} Mouse; typedef struct { const char *name; void (*draw)(void); DType type; } DemoInfo; typedef struct { double x,y,z; } Point3D; #define MAX_POLY_POINTS 32 typedef struct { int p[MAX_POLY_POINTS]; int no; int col; } Poly3D; typedef struct { int no_points; int no_poly; Point3D origin; Point3D *ps; Poly3D *poly; Point3D *workp3; XPoint *workxp; int x_rot; int y_rot; int z_rot; int mode; int col; int cull; } Obj3D; #define MODE_SOLID 0 #define MODE_WIRE 1 #define MODE_EDGE 2 typedef struct { Point3D origin; int radius; int mode; unsigned long col; } Sphere3D; typedef struct { Point3D origin; int w; int h; int mode; unsigned long col; } Rect3D; typedef struct { Point3D origin; unsigned long col; } Particle3D; typedef enum {eObj, eSphere, eRect, eParticle} ObjType; typedef struct { ObjType type; void *p; } WorldObj; /* ---------------------------------------- DEMO PROTOS */ static void AutoDropDemo(void); static void DropDemo(void); static void FastDropDemo(void); static void TrailDemo(void); static void TunnelDemo(void); static void StarDemo(void); static void StarSphereDemo(void); static void Test3DDemo(void); static void ColScapeDemo(void); static void FluidDemo(void); static void ParticleDropDemo(void); static void PolyDropDemo(void); static void CraterDemo(void); static void TriangleDemo(void); static void TriangleDemo2(void); static void FollowDemo(void); static void CatwalkDemo(void); static void GravityDemo(void); static void SnowDemo(void); static void RoadDemo(void); static void CoasterDemo(void); static void FireworkDemo(void); static void RainDemo(void); /* ---------------------------------------- GLOBAL VARS */ #define NO_DEMOS 23 static int demo=NO_DEMOS-1; static int cycle=FALSE; static DemoInfo demotbl[NO_DEMOS]= { { "Z-Buffered Pyramids", TrailDemo, UsesPixmap }, { "Auto Drops", AutoDropDemo, UsesPixmap }, { "Drops (LMB to drop, RMB to toggle clear)", DropDemo, UsesPixmap }, { "Fast Drops (LMB to drop, RMB to toggle clear)", FastDropDemo, UsesPixmap }, { "Tunnel (RMB to toggle clear)", TunnelDemo, UsesPixmap }, { "Starfield (RMB to toggle warp)", StarDemo, UsesPixmap }, { "Star Sphere", StarSphereDemo, UsesPixmap }, { "Test3DDemo (LMB to change mode, RMB to change culling)", Test3DDemo, UsesPixmap }, { "Column Landscape", ColScapeDemo, UsesPixmap }, { "Fluid Particles", FluidDemo, UsesPixmap }, { "Fluid Drops", ParticleDropDemo, UsesPixmap }, { "Polygon Water", PolyDropDemo, UsesPixmap }, { "Crater Landscape (LMB to enter view mode, RMB to change draw mode)", CraterDemo, UsesPixmap }, { "Triangle Trench", TriangleDemo, UsesPixmap }, { "Triangle Wavy Trench", TriangleDemo2, UsesPixmap }, { "Following swarm", FollowDemo, UsesPixmap }, { "Catwalk trench (LMB to toggle bounce. RMB to change draw mode)", CatwalkDemo, UsesPixmap }, { "Gravity Demo (LMB to reset, RMB to move central mass)", GravityDemo, UsesPixmap }, { "Snow Demo (LMB reset)", SnowDemo, UsesPixmap }, { "Road Demo (LMB toggle culling, RMB toggle mouselook)", RoadDemo, UsesPixmap }, { "Coster Demo (RMB toggle mouselook)", CoasterDemo, UsesPixmap }, { "Firework Demo", FireworkDemo, UsesPixmap }, { "Rain Demo", RainDemo, UsesPixmap }, }; /* ---------------------------------------- X11 VARS */ static int use_root=FALSE; static Window win=None; static Pixmap pix=None; static Pixmap saved=None; static GC gc=None; static Display *disp=NULL; static XFontStruct *font=NULL; static int width = 640; static int height = 480; static int centre_x; static int centre_y; static unsigned long black; static unsigned long white; static unsigned long red; static unsigned long green; static unsigned long blue; static int mouse_x; static int mouse_y; static unsigned int mouse_b; static KeySym last_key = XK_VoidSymbol; static double sintab[3600]; static double costab[3600]; #define FONTW (font->max_bounds.rbearing - \ font->min_bounds.lbearing) #define FONTH (font->max_bounds.descent + \ font->max_bounds.ascent + 1) /* ---------------------------------------- PROTOS */ static void OpenX(void); static unsigned long GetCol(unsigned long, unsigned long, unsigned long); static void SetCol(unsigned long col); static void Cls(void); static void ClsCol(unsigned long col); static void Redraw(void); static void UpdateDisplay(void); static void Line(int x1,int y1,int x2,int y2,int col); static void LineP(XPoint *p1,XPoint *p2,int col); static void Plot(int x,int y,int col); static void Circle(int x,int y,int r,int col); static void FCircle(int x,int y,int r,int col); static void Ellipse(int x,int y,int rx,int ry,int col); static void FEllipse(int x,int y,int rx,int ry,int col); static void Print(int x,int y,int col,char *fmt,...); static void Rect(int x,int y,int w,int h,int col); static void FRect(int x,int y,int w,int h,int col); static void Polygon(XPoint pts[], int no, int col); static void FPolygon(XPoint pts[], int no, int col); static int AngToCol(int ang, int no_col); static double Distance(Point3D *a, Point3D *b); static int Project(double x,double y,double z,XPoint *xp); static void RotateX(double cx, double cy, double cz, double ix, double iy, double iz, double *x, double *y, double *z, int a); static void RotateY(double cx, double cy, double cz, double ix, double iy, double iz, double *x, double *y, double *z, int a); static void RotateZ(double cx, double cy, double cz, double ix, double iy, double iz, double *x, double *y, double *z, int a); static void DrawObj(Obj3D *o, unsigned long cmap[]); static void DrawList(WorldObj *w, int no, unsigned long cmap[], int inv_z_order, int do_sort); static void DrawSubList(Obj3D *o, int obj_no, Sphere3D *s, int sphere_no, Rect3D *r, int rect_no, Particle3D *p, int particle_no, unsigned long cmap[], int inv_z_order, int do_sort); static void HandleKey(XKeyEvent *key); static void HandleMouse(int x, int y, int b, Mouse mode); /* ---------------------------------------- MAIN */ int main(int argc, char *argv[]) { XEvent ev; int f; f = 1; while(argv[f] && argv[f][0] == '-') { switch(argv[f++][1]) { case 'r': use_root = TRUE; break; case 'c': cycle = TRUE; break; case 'w': if (argv[f]) { width = atoi(argv[f++]); } break; case 'h': if (argv[f]) { height = atoi(argv[f++]); } break; default: break; } } if (argv[f]) { demo=atoi(argv[f]); demo=MAX(0,MIN(demo,NO_DEMOS-1)); } OpenX(); XMapWindow(disp,win); black=GetCol(0,0,0); white=GetCol(255,255,255); red=GetCol(255,0,0); green=GetCol(0,255,0); blue=GetCol(0,0,255); for(f=0;f<3600;f++) { sintab[f]=sin(RAD(f*0.10)); costab[f]=cos(RAD(f*0.10)); } srand(getpid()); while(TRUE) { int update; Window w1,w2; int i1,i2; if (XPending(disp)) { XNextEvent(disp,&ev); switch(ev.type) { case KeyPress: HandleKey(&ev.xkey); break; case KeyRelease: last_key = XK_VoidSymbol; break; /* case ButtonPress: HandleMouse(ev.xbutton.x,ev.xbutton.y, ev.xbutton.button,MousePress); break; case ButtonRelease: HandleMouse(ev.xbutton.x,ev.xbutton.y, ev.xbutton.button,MouseRelease); break; case MotionNotify: HandleMouse(ev.xbutton.x,ev.xbutton.y, ev.xbutton.button,MouseMove); break; case Expose: if (ev.xexpose.count==0) Redraw(); break; */ default: break; } } XQueryPointer(disp,win,&w1,&w2,&i1,&i2,&mouse_x,&mouse_y,&mouse_b); demotbl[demo].draw(); Redraw(); } return EXIT_SUCCESS; } /* ---------------------------------------- UTILS */ static void Error(char *p) { fprintf(stderr,"%s\n",p); exit(EXIT_FAILURE); } static void SysError(char *p) { perror(p); exit(EXIT_FAILURE); } static void *Grab(size_t s) { void *p; if (!(p=malloc(s))) { SysError("malloc()"); } return p; } static void Release(void *p) { free(p); } static void Bounce(int *x, int *y, int *ix, int *iy) { *x+=*ix; *y+=*iy; if (*x<0 || *x>=width) { *ix=-(*ix); *x+=*ix; } if (*y<0 || *y>=height) { *iy=-(*iy); *y+=*iy; } } static double SgnF(double a) { if (a < 0.0) return -1.0; if (a > 0.0) return 1.0; return 0.0; } /* ---------------------------------------- X11 UTILS */ static void OpenX(void) { /* XVisualInfo visual_info; */ Visual *visual; int depth; XGCValues gc_val; XSizeHints hint; int f; /* Open display and find an appropriate visual */ if (!(disp=XOpenDisplay(NULL))) { Error("Couldn't open X display"); } /* if (!XMatchVisualInfo(disp,DefaultScreen(disp),32,TrueColor,&visual_info) && !XMatchVisualInfo(disp,DefaultScreen(disp),24,TrueColor,&visual_info) && !XMatchVisualInfo(disp,DefaultScreen(disp),16,TrueColor,&visual_info) && !XMatchVisualInfo(disp,DefaultScreen(disp),15,TrueColor,&visual_info)) { Error("Couldn't find a visual"); } */ visual = DefaultVisual(disp, DefaultScreen(disp)); depth = DefaultDepth(disp, DefaultScreen(disp)); font=XLoadQueryFont(disp,"fixed"); if (!font) { Error("Couldn't load font fixed"); } if (!use_root) { win=XCreateWindow (disp, DefaultRootWindow(disp), 0, 0, width, height, 0, depth, InputOutput, visual, 0, NULL); } else { win=DefaultRootWindow(disp); width=DisplayWidth(disp,DefaultScreen(disp)); height=DisplayHeight(disp,DefaultScreen(disp)); } centre_x=width/2; centre_y=height/2; /* XSelectInput (disp,win, KeyPressMask|ButtonPressMask|ButtonReleaseMask| PointerMotionMask|ExposureMask); */ XSelectInput(disp,win,KeyPressMask|KeyReleaseMask); XStoreName(disp,win,demotbl[demo].name); hint.width=hint.min_width=hint.max_width=width; hint.height=hint.min_height=hint.max_height=height; hint.flags=PSize|PMinSize|PMaxSize; XSetWMNormalHints(disp,win,&hint); pix=XCreatePixmap(disp,win,width,height,depth); saved=XCreatePixmap(disp,win,width,height,depth); gc_val.function=GXcopy; gc_val.plane_mask=AllPlanes; gc_val.line_width=0; gc_val.line_style=LineSolid; gc_val.graphics_exposures=False; gc_val.foreground=WhitePixel(disp,DefaultScreen(disp)); gc_val.background=BlackPixel(disp,DefaultScreen(disp)); gc=XCreateGC(disp,pix, GCFunction|GCPlaneMask|GCLineWidth|GCForeground|GCBackground| GCLineStyle|GCGraphicsExposures, &gc_val); if (use_root) { /* XSetCloseDownMode(disp,RetainTemporary); XSetWindowBackgroundPixmap(disp,win,pix); */ } /* TODO: XImage creation */ Cls(); Redraw(); } static void Cls() { SetCol(black); XFillRectangle(disp,pix,gc,0,0,width,height); } static void ClsCol(unsigned long col) { SetCol(col); XFillRectangle(disp,pix,gc,0,0,width,height); } static void Redraw(void) { if (demotbl[demo].type==UsesPixmap) { XCopyArea(disp,pix,win,gc,0,0,width,height,0,0); } else { /* TODO: XImage copy */ } /* XFlush(disp); */ XSync(disp,FALSE); } static void Line(int x1,int y1,int x2,int y2,int col) { SetCol(col); XDrawLine(disp,pix,gc,x1,y1,x2,y2); } static void LineP(XPoint *p1, XPoint *p2, int col) { SetCol(col); XDrawLine(disp,pix,gc,p1->x,p1->y,p2->x,p2->y); } static void Plot(int x,int y,int col) { SetCol(col); XDrawPoint(disp,pix,gc,x,y); } static void Circle(int x,int y,int r,int col) { if (r<1) { Plot(x,y,col); return; } SetCol(col); XDrawArc(disp,pix,gc,x-r,y-r,r*2,r*2,0,64*360); } static void FCircle(int x,int y,int r,int col) { if (r<1) { Plot(x,y,col); return; } SetCol(col); XFillArc(disp,pix,gc,x-r,y-r,r*2,r*2,0,64*360); } static void Ellipse(int x,int y,int rx,int ry,int col) { if (rx < 1 && ry < 1) { Plot(x,y,col); return; } SetCol(col); XDrawArc(disp,pix,gc,x-rx,y-ry,rx*2,ry*2,0,64*360); } static void FEllipse(int x,int y,int rx,int ry,int col) { if (rx < 1 && ry < 1) { Plot(x,y,col); return; } SetCol(col); XFillArc(disp,pix,gc,x-rx,y-ry,rx*2,ry*2,0,64*360); } static void Print(int x,int y,int col,char *fmt,...) { char s[1024]; va_list va; size_t len; va_start(va,fmt); vsprintf(s,fmt,va); va_end(va); len=strlen(s); y+=FONTH-1; SetCol(col); XDrawString(disp,pix,gc,x,y,s,len); } static void Rect(int x,int y,int w,int h,int col) { if (w==0 && h==0) { Plot(x,y,col); return; } SetCol(col); XDrawRectangle(disp,pix,gc,x,y,w,h); } static void FRect(int x,int y,int w,int h,int col) { if (w==0 && h==0) { Plot(x,y,col); return; } SetCol(col); XFillRectangle(disp,pix,gc,x,y,w,h); } static void Polygon(XPoint p[], int no, int col) { int f; for(f=0;f1800) { ang=1800-(ang-1800); } d=(int)((no_col/1800.0)*(double)ang); return MIN(d,no_col-1); } static double Distance(Point3D *a, Point3D *b) { double x,y,z; x=a->x-b->x; y=a->y-b->y; z=a->z-b->z; return sqrt((x*x)+(y*y)+(z*z)); } static int Project(double x,double y,double z,XPoint *xp) { if (z<10.0) return False; z=z/800.0; xp->x=centre_x+(int)(x/z); xp->y=centre_y+(int)(y/z); return True; } static void RotateX(double cx, double cy, double cz, double ix, double iy, double iz, double *x, double *y, double *z, int a) { double dy,dz,si,co; while(a<0) { a+=3600; } a%=3600; if (a==0) { *x=ix; *y=iy; *z=iz; return; } dy=iy-cy; dz=iz-cz; *x=ix; si=sintab[a]; co=costab[a]; *y=cy+(dz*si+dy*co); *z=cz+(dz*co-dy*si); } static void RotateY(double cx, double cy, double cz, double ix, double iy, double iz, double *x, double *y, double *z, int a) { double dx,dz,si,co; while(a<0) { a+=3600; } a%=3600; if (a==0) { *x=ix; *y=iy; *z=iz; return; } dx=ix-cx; dz=iz-cz; *y=iy; si=sintab[a]; co=costab[a]; *x=cy+(dz*si+dx*co); *z=cz+(dz*co-dx*si); } static void RotateZ(double cx, double cy, double cz, double ix, double iy, double iz, double *x, double *y, double *z, int a) { double dy,dx,si,co; while(a<0) { a+=3600; } a%=3600; if (a==0) { *x=ix; *y=iy; *z=iz; return; } dy=iy-cy; dx=ix-cx; *z=iz; si=sintab[a]; co=costab[a]; *y=cy+(dx*si+dy*co); *x=cx+(dx*co-dy*si); } static void DrawObj(Obj3D *o, unsigned long cmap[]) { int f; for(f=0;fno_points;f++) { RotateX(0,0,0, o->ps[f].x,o->ps[f].y,o->ps[f].z, &o->workp3[f].x,&o->workp3[f].y,&o->workp3[f].z, o->x_rot); RotateY(0,0,0, o->workp3[f].x,o->workp3[f].y,o->workp3[f].z, &o->workp3[f].x,&o->workp3[f].y,&o->workp3[f].z, o->y_rot); RotateZ(0,0,0, o->workp3[f].x,o->workp3[f].y,o->workp3[f].z, &o->workp3[f].x,&o->workp3[f].y,&o->workp3[f].z, o->z_rot); Project(o->origin.x+o->workp3[f].x, o->origin.y+o->workp3[f].y, o->origin.z+o->workp3[f].z, o->workxp+f); } for(f=0;fno_poly;f++) { static XPoint p[MAX_POLY_POINTS]; int r; int c=-1; int col; for(r=0;rpoly[f].no;r++) { p[r]=o->workxp[o->poly[f].p[r]]; } if (o->cull) { c=(p[0].x-p[1].x)*(p[2].y-p[1].y)-(p[0].y-p[1].y)*(p[2].x-p[1].x); } if (o->col!=-1) { col=cmap[o->col]; } else { col=cmap[o->poly[f].col]; } if (c<0) { switch(o->mode) { case MODE_SOLID: FPolygon(p,o->poly[f].no,col); break; case MODE_EDGE: FPolygon(p,o->poly[f].no,black); Polygon(p,o->poly[f].no,col); break; case MODE_WIRE: default: Polygon(p,o->poly[f].no,col); break; } } } } static int ZSort(const void *p1, const void *p2) { double z1,z2; const WorldObj *o1, *o2; o1=p1; o2=p2; switch(o1->type) { case eObj: z1=((const Obj3D *)o1->p)->origin.z; break; case eSphere: z1=((const Sphere3D *)o1->p)->origin.z; break; case eRect: z1=((const Rect3D *)o1->p)->origin.z; break; case eParticle: z1=((const Particle3D *)o1->p)->origin.z; break; default: z1=-1; break; } switch(o2->type) { case eObj: z2=((const Obj3D *)o2->p)->origin.z; break; case eSphere: z2=((const Sphere3D *)o2->p)->origin.z; break; case eRect: z2=((const Rect3D *)o2->p)->origin.z; break; case eParticle: z2=((const Particle3D *)o2->p)->origin.z; break; default: z2=-1; break; } return (int)(z2-z1); } static int InvZSort(const void *p1, const void *p2) { double z1,z2; const WorldObj *o1, *o2; o1=p1; o2=p2; switch(o1->type) { case eObj: z1=((const Obj3D *)o1->p)->origin.z; break; case eSphere: z1=((const Sphere3D *)o1->p)->origin.z; break; case eRect: z1=((const Rect3D *)o1->p)->origin.z; break; case eParticle: z1=((const Particle3D *)o1->p)->origin.z; break; default: z1=-1; break; } switch(o2->type) { case eObj: z2=((const Obj3D *)o2->p)->origin.z; break; case eSphere: z2=((const Sphere3D *)o2->p)->origin.z; break; case eRect: z2=((const Rect3D *)o2->p)->origin.z; break; case eParticle: z2=((const Particle3D *)o2->p)->origin.z; break; default: z2=-1; break; } return (int)(z1-z2); } static void DrawList(WorldObj *w, int no, unsigned long cmap[], int inv_z_order,int do_sort) { int f; XPoint xp,xp2; Particle3D *p; Sphere3D *s; Rect3D *r; if (do_sort) { if (inv_z_order) { qsort(w,no,sizeof(WorldObj),InvZSort); } else { qsort(w,no,sizeof(WorldObj),ZSort); } } for(f=0;forigin.x,s->origin.y,s->origin.z,&xp)) { if (Project(s->origin.x+s->radius,s->origin.y, s->origin.z,&xp2)) { switch(s->mode) { case MODE_SOLID: FCircle(xp.x,xp.y,xp2.x-xp.x,cmap[s->col]); break; case MODE_WIRE: Circle(xp.x,xp.y,xp2.x-xp.x,cmap[s->col]); break; case MODE_EDGE: FCircle(xp.x,xp.y,xp2.x-xp.x,black); Circle(xp.x,xp.y,xp2.x-xp.x,cmap[s->col]); break; } } } break; case eRect: r=w[f].p; if (Project(r->origin.x,r->origin.y,r->origin.z,&xp)) { if (Project(r->origin.x+r->w,r->origin.y+r->h, r->origin.z,&xp2)) { int w,h; w=xp2.x-xp.x; h=xp2.y-xp.y; switch(r->mode) { case MODE_SOLID: FRect(xp.x,xp.y,w,h,cmap[r->col]); break; case MODE_WIRE: Rect(xp.x,xp.y,w,h,cmap[r->col]); break; case MODE_EDGE: FRect(xp.x,xp.y,w,h,black); Rect(xp.x,xp.y,w,h,cmap[r->col]); break; } } } break; case eParticle: p=w[f].p; if (Project(p->origin.x,p->origin.y,p->origin.z,&xp)) { Plot(xp.x,xp.y,cmap[p->col]); } break; default: break; } } } static void DrawSubList(Obj3D *o, int obj_no, Sphere3D *s, int sphere_no, Rect3D *r, int rect_no, Particle3D *p, int particle_no, unsigned long cmap[], int inv_z_order, int do_sort) { WorldObj *w; int no; int f; int i; no=obj_no+sphere_no+rect_no+particle_no; w=Grab(sizeof(WorldObj)*no); i=0; for(f=0;fx=mouse_x; new->y=mouse_y; new->r=3; new->c=0; new->next=NULL; if (!head) { head=tail=new; } else { tail->next=new; tail=new; } last=time(NULL); } if (clr) { Cls(); } d=head; while(d) { if (d->c>=256) { Drop *t; if (d->next) { head=d->next; } else { head=tail=NULL; } t=d; d=d->next; Release(t); } else { Circle(d->x,d->y,d->r,col[d->c]); d->r+=scale; d->c+=speed; d=d->next; } } } /* ---------------------------------------- AUTO DROP DEMO */ static void AutoDropDemo(void) { static const int speed=1; static const int scale=1; static const int num=10; static int init=FALSE; static unsigned long col[256]; static time_t last=0; typedef struct Drop { int x,y,r,c; int speed; int scale; struct Drop *next; } Drop; static Drop *head=NULL; static Drop *tail=NULL; Drop *d; int f; if (!init) { int f; for(f=0;f<256;f++) { col[f]=GetCol(MAX(255-f*2,0),MAX(255-f*2,0),255-f); } init=TRUE; } if (last!=time(NULL) && !head) { int x,y,sc; x=RND(width); y=RND(height); sc=scale; for(f=0;fx=x+RND(num); new->y=y+RND(num); new->r=3; new->c=0; new->scale=sc; new->speed=speed; new->next=NULL; if (!head) { head=tail=new; } else { tail->next=new; tail=new; } } last=time(NULL); } /* Cls(); */ d=head; while(d) { if (d->c>=256) { Drop *t; if (d->next) { head=d->next; } else { head=tail=NULL; } t=d; d=d->next; Release(t); } else { Circle(d->x,d->y,d->r,col[d->c]); d->r+=d->scale; d->c+=d->speed; d=d->next; } } } /* ---------------------------------------- FAST DROP DEMO */ static void FastDropDemo(void) { static const int speed=30; static const int scale=2; static int init=FALSE; static int clr=TRUE; static unsigned long col[256]; static time_t last=0; typedef struct Drop { int x,y,r,c; struct Drop *next; } Drop; static Drop *head=NULL; static Drop *tail=NULL; Drop *d; if (!init) { int f; for(f=0;f<256;f++) { col[f]=GetCol(255-f,255-f,255-f); } init=TRUE; } if (mouse_b&Button3Mask) { static time_t bounce=0; if (bounce!=time(NULL)) { clr=!clr; bounce=time(NULL); } } if (mouse_b&Button1Mask /*&& (last!=time(NULL))*/) { Drop *new; new=Grab(sizeof *new); new->x=mouse_x; new->y=mouse_y; new->r=3; new->c=0; new->next=NULL; if (!head) { head=tail=new; } else { tail->next=new; tail=new; } last=time(NULL); } if (clr) { Cls(); } d=head; while(d) { if (d->c>=256) { Drop *t; if (d->next) { head=d->next; } else { head=tail=NULL; } t=d; d=d->next; Release(t); } else { Rect(d->x-d->r/2,d->y-d->r/2,d->r,d->r,col[d->c]); d->r+=scale; d->c+=speed; d=d->next; } } } /* ---------------------------------------- TRAIL DEMO */ static void TrailDemo(void) { static const int NO=20; static const int speed=20; static const int scale=10; static int init=FALSE; static unsigned long col[256]; static struct { int x,y,xi,yi; } *p; typedef struct Drop { int x,y,r,c; struct Drop *next; } Drop; static Drop *head=NULL; static Drop *tail=NULL; Drop *new; Drop *d; int f; if (!init) { for(f=0;f<256;f++) { col[f]=GetCol(255-f,255-f,255-f); } p=Grab(sizeof(*p)*NO); for(f=0;fx=p[f].x; new->y=p[f].y; new->r=3; new->c=0; new->next=NULL; if (!head) { head=tail=new; } else { tail->next=new; tail=new; } Bounce(&p[f].x,&p[f].y,&p[f].xi,&p[f].yi); } /* Cls(); */ d=head; while(d) { if (d->c>=256) { Drop *t; if (d->next) { head=d->next; } else { head=tail=NULL; } t=d; d=d->next; Release(t); } else { FRect(d->x-d->r/2,d->y-d->r/2,d->r,d->r,col[d->c]); d->r+=scale; d->c+=speed; d=d->next; } } } /* ---------------------------------------- TUNNEL DEMO */ static void TunnelDemo(void) { static const int speed=100; static const int radius=1000; static const int points=10; static const int max_z=50000; static const int delay=20; static double *si; static double *co; static int init=FALSE; static int clr=TRUE; static unsigned long col[256]; static time_t last=0; static int create=0; typedef struct Drop { double x[points]; double y[points]; double z; int r,c; int ang; struct Drop *next; } Drop; static Drop *head=NULL; static Drop *tail=NULL; Drop *d; int f; if (!init) { for(f=0;f<256;f++) { col[f]=GetCol(255-f,255-f,255-f); } si=Grab(sizeof(double)*points); co=Grab(sizeof(double)*points); for(f=0;fx[f]=(RND(radius/2)+radius/2)*si[f]; new->y[f]=(RND(radius/2)+radius/2)*co[f]; } new->r=radius; new->z=max_z; new->c=255; new->ang=RND(20)-10; if (new->ang<0) { new->ang+=3600; } new->next=NULL; if (!head) { head=tail=new; } else { tail->next=new; tail=new; } create=delay; } else { create--; } if (clr) { Cls(); } d=head; while(d) { if (d->z<10) { Drop *t; if (d->next) { head=d->next; } else { head=tail=NULL; } t=d; d=d->next; Release(t); } else { XPoint p[points]; if (d->c) { d->c=MAX(0,d->c-10); } for(f=0;fz, d->x[f],d->y[f],d->z, &d->x[f],&d->y[f],&d->z, d->ang); Project(d->x[f],d->y[f],d->z,p+f); } for(f=0;fc]); } d->z-=speed; d=d->next; } } } /* ---------------------------------------- STAR DEMO */ static void StarDemo(void) { # define TYPE Particle3D # undef ISSPHERE static const int no_points=4; static const int no_poly=4; static Point3D pset[4]= { {-100,50,100}, {100,50,100}, {0,-50,100}, {0,0,-300}, }; static Poly3D plist[4]= { { {0,3,1}, 3, 0 }, { {0,1,2}, 3, 0 }, { {0,2,3}, 3, 0 }, { {1,3,2}, 3, 0 }, }; static Obj3D *obj; #ifdef ISSPHERE static const int no=200; #else static const int no=2000; #endif static const int no_obj=3; static const int radius=25000; static const int max_z=50000; static const double max_speed=50.0; static const double min_speed=1.0; static const double speed_inc=0.1; static int init=FALSE; static unsigned long col[256]; static int clr=TRUE; static time_t last=0; static double speed=1.0; static TYPE *star; int fg,bg; int f; if (!init) { for(f=0;f<256;f++) { col[f]=GetCol(255-f,255-f,255-f); } star=Grab(sizeof(TYPE)*no); for(f=0;fmin_speed) { speed-=speed_inc; } } Cls(); for(f=0;fmin_speed) { XPoint p; XPoint p2; Project(star[f].origin.x,star[f].origin.y,star[f].origin.z,&p); Project(star[f].origin.x,star[f].origin.y,star[f].origin.z+speed*speed,&p2); LineP(&p,&p2,col[star[f].col]); } star[f].origin.z-=20*speed; if (star[f].col) { star[f].col--; } } } for(f=0;fmin_speed) { speed-=speed_inc; } } Cls(); for(f=0;fMODE_EDGE) { obj.mode=MODE_SOLID; } bounce=time(NULL); } } Cls(); for(f=0;fradius) { p[f].origin.y=-radius; } } switch(rot) { case 0: Print(0,20,white,"Y rotation"); obj.y_rot+=10; break; case 1: Print(0,20,white,"X rotation"); obj.x_rot+=10; break; case 2: Print(0,20,white,"Z rotation"); obj.z_rot+=10; break; default: break; } Print(0,30,white,"Mode : %d Culling: %s",obj.mode,obj.cull ? "on":"off"); DrawList(w,no_particle+1,col,False,False); } /* ---------------------------------------- COL SCAPE DEMO */ static void ColScapeDemo(void) { static const int gap=200; static const int no_pt=8; static const int no_poly=6; static const int no_x=20; static const int no_z=20; static const int no=400; static const int z_off=1000; static const double y_range=200.0; static const double y_base=100.0; static Point3D pset[8]= { {-50,-20,50}, {50,-20,50}, {50,-20,-50}, {-50,-20,-50}, {50,20,50}, {-50,20,50}, {-50,20,-50}, {50,20,-50}, }; static Poly3D plist[6]= { { {0,1,2,3}, 4, 0 }, { {4,5,6,7}, 4, 0 }, { {0,5,4,1}, 4, 0 }, { {2,1,4,7}, 4, 0 }, { {3,2,7,6}, 4, 0 }, { {0,3,6,5}, 4, 0 }, }; static Obj3D *obj; static WorldObj *w; static int ang=0; static int init=FALSE; static unsigned long col[4]; int f,a; if (!init) { int x,z; col[0]=white; col[1]=red; col[2]=green; col[3]=blue; init=TRUE; obj=Grab(sizeof(Obj3D)*no); w=Grab(sizeof(WorldObj)*no); x=-(no_x/2)*gap; z=(no_z)*gap+z_off; a=0; for(f=0;fy_base) { amp=init_amp; ang=0; dist=1; ctr=drop.origin; drop.origin.x=RND2(max_x,min_x); drop.origin.z=RND2(max_z,min_z); drop.origin.y=drop_y; } if (amp>0.0) { int a; if (ang<0) { ang+=3600; } a=ang; for(f=0;fy_base) { amp=init_amp; ang=0; dist=1; ctr=drop.origin; drop.origin.x=RND2(max_x,min_x); drop.origin.z=RND2(max_z,min_z); drop.origin.y=drop_y; } if (amp>0.0) { int a; if (ang<0) { ang+=3600; } a=ang; for(f=0;fMODE_EDGE) { obj.mode=MODE_SOLID; } bounce=time(NULL); } } Cls(); if (view) { obj.x_rot=MAX(2700,MIN(3599,2700+mouse_y*3)); obj.y_rot=(centre_x-mouse_x)*3; obj.y_rot=MAX(-900,MIN(obj.y_rot,900)); } else { drop.origin.y+=drop_speed*speed; speed+=0.1; if (drop.origin.y>y_base) { int a; speed=1.0; ctr=drop.origin; drop.origin.z=RND2(max_z,min_z)/gap*gap; drop.origin.x=RND2((int)drop.origin.z,(int)-drop.origin.z)/gap*gap; drop.origin.y=drop_y; for(f=0;f=gap) { for(f=no-1;f>=no_x;f--) { pt[f].y=pt[f-no_x].y; } for(f=0;f=gap) { for(f=no-1;f>=no_x;f--) { pt[f].y=pt[f-no_x].y; } for(f=0;f fabs(d_y)) { d = fabs(d_x); } else { d = fabs(d_y); } d_x = d_x / d * FOLLOW_WEIGHT; d_y = d_y / d * FOLLOW_WEIGHT; dx[f] += d_x; dy[f] += d_y; if (dx[f] < -FOLLOW_SPEED) { dx[f] = -FOLLOW_SPEED; } if (dx[f] > FOLLOW_SPEED) { dx[f] = FOLLOW_SPEED; } if (dy[f] < -FOLLOW_SPEED) { dy[f] = -FOLLOW_SPEED; } if (dy[f] > FOLLOW_SPEED) { dy[f] = FOLLOW_SPEED; } x[f] += dx[f]; y[f] += dy[f]; Line(x[f], y[f], x[f]-dx[f], y[f]-dy[f], red); } } /* ---------------------------------------- CATWALK DEMO */ #define TRENCH_X 300 #define TRENCH_Y_TOP -200 #define TRENCH_Y_BOT 100 #define TRENCH_Z_MAX 20000 #define TRENCH_Z_MIN 10 #define TRENCH_CWH 50 static void CatwalkDemo(void) { static const int speed=5; static unsigned long col[3]; static Point3D trench_point[8] = { {-TRENCH_X, TRENCH_Y_TOP, TRENCH_Z_MAX}, /* 0 */ {TRENCH_X, TRENCH_Y_TOP, TRENCH_Z_MAX}, /* 1 */ {-TRENCH_X, TRENCH_Y_BOT, TRENCH_Z_MAX}, /* 2 */ {TRENCH_X, TRENCH_Y_BOT, TRENCH_Z_MAX}, /* 3 */ {-TRENCH_X, TRENCH_Y_TOP, TRENCH_Z_MIN}, /* 4 */ {TRENCH_X, TRENCH_Y_TOP, TRENCH_Z_MIN}, /* 5 */ {-TRENCH_X, TRENCH_Y_BOT, TRENCH_Z_MIN}, /* 6 */ {TRENCH_X, TRENCH_Y_BOT, TRENCH_Z_MIN} /* 7 */ }; static Poly3D trench_poly[3] = { {{2, 3, 7, 6}, 4, 0}, {{1, 5, 7, 3}, 4, 0}, {{0, 2, 6, 4}, 4, 0}, }; static Point3D cw_point[4] = { {-TRENCH_X, 0, 0}, {TRENCH_X, 0, 0}, {TRENCH_X, TRENCH_CWH, 0}, {-TRENCH_X, TRENCH_CWH, 0} }; static Poly3D cw_poly[1] = { {{0, 1, 2, 3}, 4, 1} }; static Point3D trench_work_3d[8]; static XPoint trench_work_xp[8]; static Obj3D trench = {8,3,{0,0,0},trench_point,trench_poly,trench_work_3d,trench_work_xp, 0,0,0,MODE_WIRE,-1,True}; static const int no_catwalk = 10; static Obj3D *catwalk; static int drawmode = MODE_EDGE; static int init=FALSE; static int move = FALSE; static int move_y = 0; static int move_yi = 0; static int *catwalk_y; int f,r; if (!init) { col[0]=GetCol(255, 255, 255); col[1]=GetCol(255, 255, 0); col[2]=GetCol(0, 0, 0); catwalk = Grab(sizeof *catwalk * no_catwalk); catwalk_y = Grab(sizeof *catwalk_y * no_catwalk); for(f = 0; f < no_catwalk; f++) { catwalk[f].no_points = 4; catwalk[f].no_poly = 1; catwalk[f].origin.x = 0; catwalk_y[f] = RND2(TRENCH_Y_BOT - TRENCH_CWH, TRENCH_Y_TOP + TRENCH_CWH); catwalk[f].origin.z = TRENCH_Z_MAX - 1500 * f; catwalk[f].ps = cw_point; catwalk[f].poly = cw_poly; catwalk[f].workp3 = Grab(sizeof *catwalk[f].workp3 * 4); catwalk[f].workxp = Grab(sizeof *catwalk[f].workxp * 4); catwalk[f].x_rot = 0; catwalk[f].y_rot = 0; catwalk[f].z_rot = 0; catwalk[f].mode = MODE_EDGE; catwalk[f].col = -1; catwalk[f].cull = True; } init=TRUE; } if (mouse_b&Button1Mask) { static time_t bounce=0; if (bounce!=time(NULL)) { move = !move; if (move) { move_y = 0; move_yi = 1; } else { move_y = 0; move_yi = 0; } bounce=time(NULL); } } if (mouse_b&Button3Mask) { static time_t bounce=0; if (bounce!=time(NULL)) { drawmode = (drawmode + 1) % 3; switch(drawmode) { case MODE_WIRE: trench.mode = MODE_WIRE; for(f = 0; f < no_catwalk; f++) { catwalk[f].mode = MODE_WIRE; } break; case MODE_EDGE: trench.mode = MODE_WIRE; for(f = 0; f < no_catwalk; f++) { catwalk[f].mode = MODE_EDGE; } break; case MODE_SOLID: trench.mode = MODE_SOLID; for(f = 0; f < no_catwalk; f++) { catwalk[f].mode = MODE_SOLID; } break; } bounce=time(NULL); } } Cls(); trench.origin.y = move_y; DrawObj(&trench,col); if (drawmode == MODE_SOLID) { trench.mode = MODE_WIRE; for(f = 0; f < 3; f++) { trench.poly[f].col = 2; } DrawObj(&trench,col); trench.mode = MODE_SOLID; for(f = 0; f < 3; f++) { trench.poly[f].col = 0; } } for(f = 0; f < no_catwalk; f++) { catwalk[f].origin.y = catwalk_y[f] + move_y; DrawObj(catwalk + f,col); catwalk[f].origin.z -= speed; } while(catwalk[no_catwalk-1].origin.z < TRENCH_Z_MIN) { Obj3D t = catwalk[no_catwalk-1]; for(f = no_catwalk-2; f >= 0; f--) { catwalk[f+1] = catwalk[f]; catwalk_y[f + 1] = catwalk_y[f]; } catwalk[0] = t; catwalk[0].origin.z = TRENCH_Z_MAX; catwalk_y[0] = RND2(TRENCH_Y_BOT - TRENCH_CWH, TRENCH_Y_TOP + TRENCH_CWH); } move_y += move_yi; if (move_y >= 200 || move_y <= -90) { move_yi = -move_yi; move_y += move_yi; } } #undef TRENCH_X #undef TRENCH_Y_TOP #undef TRENCH_Y_BOT #undef TRENCH_Z_MAX #undef TRENCH_Z_MIN /* ---------------------------------------- GRAVITY DEMO */ #define GRAV_OBJ 10 #define GRAV_TRAIL 200 #define GRAV_SIZE 100 static void GravityDemo(void) { static int init = FALSE; typedef struct { double x,y,dx,dy; int col; int mass; double tx[GRAV_TRAIL]; double ty[GRAV_TRAIL]; int alive; } GravObj; static GravObj gravobj[GRAV_OBJ]; int f,r; if (!init || mouse_b&Button1Mask) { for(f = 0; f < GRAV_OBJ; f++) { if (f == 0) { gravobj[f].x = centre_x; gravobj[f].y = centre_y; gravobj[f].dx = 0; gravobj[f].dy = 0; gravobj[f].mass = 1000; gravobj[f].alive = TRUE; } else { int a; int d; a = RND(3600); d = RND2(400,200); gravobj[f].x = centre_x + sintab[a] * d; gravobj[f].y = centre_y + costab[a] * d; a = (a + 900) % 3600; gravobj[f].dx = sintab[a] * 0.1; gravobj[f].dy = costab[a] * 0.1; gravobj[f].mass = 10; gravobj[f].alive = TRUE; } gravobj[f].col = GetCol(RND2(255,128), RND2(255,100), RND2(255,100)); for(r = 0; r < GRAV_TRAIL; r++) { gravobj[f].tx[r] = gravobj[f].x; gravobj[f].ty[r] = gravobj[f].y; } } init=TRUE; } Cls(); if (mouse_b & Button3Mask) { gravobj[0].x = mouse_x; gravobj[0].y = mouse_y; } for(f = 0; f < GRAV_OBJ; f++) { if (gravobj[f].alive) { for(r = 0; r < GRAV_OBJ; r++) { if (f != r && gravobj[r].alive) { double d; double g; double dx; double dy; dx = gravobj[f].x - gravobj[r].x; dy = gravobj[f].y - gravobj[r].y; d = sqrt(dx*dx + dy*dy); if (d < (gravobj[r].mass / GRAV_SIZE)) { gravobj[f].alive = FALSE; gravobj[r].mass += gravobj[f].mass; } g = (0.000001 * gravobj[f].mass * gravobj[r].mass) / d*d; dx /= d; dy /= d; g /= gravobj[f].mass; dx *= g; dy *= g; gravobj[f].dx -= dx; gravobj[f].dy -= dy; } } } } for(f = 0; f < GRAV_OBJ; f++) { if (gravobj[f].alive) { gravobj[f].x += gravobj[f].dx; gravobj[f].y += gravobj[f].dy; for(r = GRAV_TRAIL - 1; r > 0; r--) { gravobj[f].tx[r] = gravobj[f].tx[r-1]; gravobj[f].ty[r] = gravobj[f].ty[r-1]; } gravobj[f].tx[0] = gravobj[f].x; gravobj[f].ty[0] = gravobj[f].y; for(r = 0; r < GRAV_TRAIL - 1; r++) { Line(gravobj[f].tx[r], gravobj[f].ty[r], gravobj[f].tx[r+1], gravobj[f].ty[r+1], gravobj[f].col); } } } for(f = 0; f < GRAV_OBJ; f++) { if (gravobj[f].alive) { FCircle(gravobj[f].x, gravobj[f].y, gravobj[f].mass/GRAV_SIZE, gravobj[f].col); } } } #undef GRAV_OBJ #undef GRAV_TRAIL #undef GRAV_SIZE /* ---------------------------------------- SNOW DEMO */ #define SNOW_LINES 20 #define SNOW_FLAKES 10000 static void SnowDemo(void) { static int init = FALSE; static int *map; typedef struct { int x,y; } Flake; static Flake *snow; int f,r; int x,y; int xi, yi; if (!init || mouse_b&Button1Mask) { Cls(); map = Grab(sizeof *map * width * height); snow = Grab(sizeof *snow * SNOW_FLAKES); Line(0, 0, 0, height - 1, red); Line(width - 1, 0, width - 1, height - 1, red); Line(0, height - 1, width - 1, height - 1, red); for(x = 0; x < width; x++) { for(y = 0; y < height; y++) { if (x == 0 || x == width - 1 || y == height - 1) { map[XY(x,y)] = 1; } else { map[XY(x,y)] = 0; } } } for(f = 0; f < SNOW_LINES; f++) { x = RND(width); y = RND(height); do { xi = RND2(1, -1); } while (xi == 0); yi = 0; for(r = RND(50) + 10; r > 0; r--) { if (x > 0 && x < width && y > 0 && y < height) { Plot(x, y, red); map[XY(x,y)] = 1; x += xi; y += yi; } } } for(f = 0; f < SNOW_FLAKES; f++) { do { snow[f].x = RND(width - 2) + 1; snow[f].y = RND(height); } while (map[XY(snow[f].x, snow[f].y)]); } init=TRUE; } for(f = 0; f < SNOW_FLAKES; f++) { int dir; if (RND(10) > 5) { dir = 1; } else { dir = -1; } if (map[XY(snow[f].x, snow[f].y + 1)] == 0) { Plot(snow[f].x, snow[f].y, black); snow[f].y++; Plot(snow[f].x, snow[f].y, white); } else if (map[XY(snow[f].x + dir, snow[f].y + 1)] == 0) { Plot(snow[f].x, snow[f].y, black); snow[f].x += dir; snow[f].y++; Plot(snow[f].x, snow[f].y, white); } else if (map[XY(snow[f].x - dir, snow[f].y + 1)] == 0) { Plot(snow[f].x, snow[f].y, black); snow[f].x -= dir; snow[f].y++; Plot(snow[f].x, snow[f].y, white); } else { Plot(snow[f].x, snow[f].y, white); map[XY(snow[f].x, snow[f].y)] = 1; snow[f].x = RND(width - 2) + 1; snow[f].y = 0; } } } #undef SNOW_LINES #undef SNOW_FLAKES /* ---------------------------------------- ROAD DEMO */ #define ROAD_NUM 5000 #define ROAD_WIDTH 100 #define ROAD_Z_INC 10 static void RoadDemo(void) { static int init = FALSE; typedef struct { double dx, dy; } Road; static Road road[ROAD_NUM]; static double dx, dy; static double dxi, dyi; static int dcount; static int cull = TRUE; static int mouselook = FALSE; int f,r; double x, y, z; double rx, ry, rz; double rx2, ry2, rz2; int max_y; double ang_y; double ang_x; if (!init) { init=TRUE; for(f = 0; f < ROAD_NUM; f++) { if (dcount == 0 && dx == 0 && dy == 0) { dcount = RND2(200, 500); dx = 0; dy = 0; dxi = RND(10) / 40.0 - 0.125; dyi = RND(10) / 80.0 - 0.065; } else { if (dcount) { dcount--; if (dx < ROAD_Z_INC / 2.0 && dx > -ROAD_Z_INC / 2.0) { dx += dxi; } if (dy < ROAD_Z_INC / 2.0 && dy > -ROAD_Z_INC / 2.0) { dy += dyi; } } else { double sdx = SgnF(dx); double sdy = SgnF(dy); if (dx != 0.0) { dx -= dxi; } if (dy != 0.0) { dy -= dyi; } if (SgnF(dx) != sdx) { dx = 0; } if (SgnF(dy) != sdy) { dy = 0; } } } road[f].dx = dx; road[f].dy = dy; } } if (mouse_b&Button1Mask) { static time_t bounce=0; if (bounce!=time(NULL)) { cull = !cull; bounce=time(NULL); } } if (mouse_b&Button3Mask) { static time_t bounce=0; if (bounce!=time(NULL)) { mouselook = !mouselook; bounce=time(NULL); } } Cls(); x = -ROAD_WIDTH / 2; y = 20.0; z = 10.0; max_y = height; if (mouselook) { ang_y = -(mouse_x - centre_x) * 2; ang_x = (mouse_y - centre_y) * 2; } else { ang_x = atan(fabs(road[0].dy) / ROAD_Z_INC); if (isnan(ang_x)) { ang_x = 0; } else { ang_x = DEG(ang_x) * 10.0; if (road[0].dy > 0) { ang_x = -ang_x; } } ang_y = atan(fabs(road[0].dx) / ROAD_Z_INC); if (isnan(ang_y)) { ang_y = 0; } else { ang_y = DEG(ang_y) * 10.0; if (road[0].dx > 0) { ang_y = -ang_y; } } } for(f = 0; f < ROAD_NUM; f++) { XPoint xp; x += road[f].dx; y += road[f].dy; z += ROAD_Z_INC; RotateY(0, 0, 0, x, y, z, &rx, &ry, &rz, (int)ang_y); RotateX(0, 0, 0, rx, ry, rz, &rx2, &ry2, &rz2, (int)ang_x); if (Project(rx2, ry2, rz2, &xp)) { if (!cull || xp.y < max_y) { Plot(xp.x, xp.y, white); Project(rx2 + ROAD_WIDTH, ry2, rz2, &xp); Plot(xp.x, xp.y, white); max_y = xp.y; } } if (f < ROAD_NUM - 1) { road[f] = road[f + 1]; } else { if (dcount == 0 && dx == 0 && dy == 0) { dcount = RND2(200, 500); dx = 0; dy = 0; dxi = RND(10) / 40.0 - 0.125; dyi = RND(10) / 80.0 - 0.065; } else { if (dcount) { dcount--; if (dx < ROAD_Z_INC / 2.0 && dx > -ROAD_Z_INC / 2.0) { dx += dxi; } if (dy < ROAD_Z_INC / 2.0 && dy > -ROAD_Z_INC / 2.0) { dy += dyi; } } else { double sdx = SgnF(dx); double sdy = SgnF(dy); if (dx != 0.0) { dx -= dxi; } if (dy != 0.0) { dy -= dyi; } if (SgnF(dx) != sdx) { dx = 0; } if (SgnF(dy) != sdy) { dy = 0; } } } road[f].dx = dx; road[f].dy = dy; } } } #undef ROAD_NUM #undef ROAD_WIDTH #undef ROAD_Z_INC /* ---------------------------------------- COASTER DEMO */ #define COASTER_NUM 5000 #define COASTER_WIDTH 30 #define COASTER_Z_INC 10 #define COASTER_BAR 20 #define COASTER_POLE 200 #define MIN_SPEED 2 #define MAX_SPEED COASTER_Z_INC static void CoasterDemo(void) { static int init = FALSE; typedef struct { double dx, dy; int bar, pole; } Coaster; static Coaster coaster[COASTER_NUM]; static double dx, dy; static double dxi, dyi; static int dcount; static int mouselook = FALSE; static int counter = 0; static double speed = MAX_SPEED; static double start_z = 20.0; int f,r; double x, y, z; double rx, ry, rz; double rx2, ry2, rz2; double ang_y; double ang_x; XPoint last_xp1, last_xp2; if (!init) { init=TRUE; for(f = 0; f < COASTER_NUM; f++) { if (dcount == 0 && dx == 0 && dy == 0) { dcount = RND2(300, 1000); dx = 0; dy = 0; dxi = RND(10) / 40.0 - 0.125; dyi = RND(10) / 80.0 - 0.065; } else { if (dcount) { dcount--; if (dx < COASTER_Z_INC / 2.0 && dx > -COASTER_Z_INC / 2.0) { dx += dxi; } if (dy < COASTER_Z_INC / 2.0 && dy > -COASTER_Z_INC / 2.0) { dy += dyi; } } else { double sdx = SgnF(dx); double sdy = SgnF(dy); if (dx != 0.0) { dx -= dxi; } if (dy != 0.0) { dy -= dyi; } if (SgnF(dx) != sdx) { dx = 0; } if (SgnF(dy) != sdy) { dy = 0; } } } coaster[f].dx = dx; coaster[f].dy = dy; if ((counter % COASTER_BAR) == 0) { coaster[f].bar = TRUE; } else { coaster[f].bar = FALSE; } if ((counter % COASTER_POLE) == 0) { coaster[f].pole = TRUE; } else { coaster[f].pole = FALSE; } counter++; } } if (mouse_b&Button3Mask) { static time_t bounce=0; if (bounce!=time(NULL)) { mouselook = !mouselook; bounce=time(NULL); } } Cls(); x = -COASTER_WIDTH / 2; y = 20.0; z = start_z; if (mouselook) { ang_y = -(mouse_x - centre_x) * 2; ang_x = (mouse_y - centre_y) * 2; } else { ang_x = atan(fabs(coaster[20].dy) / COASTER_Z_INC); if (isnan(ang_x)) { ang_x = 0; } else { ang_x = DEG(ang_x) * 10.0; if (coaster[20].dy > 0) { ang_x = -ang_x; } } ang_y = atan(fabs(coaster[20].dx) / COASTER_Z_INC); if (isnan(ang_y)) { ang_y = 0; } else { ang_y = DEG(ang_y) * 10.0; if (coaster[20].dx > 0) { ang_y = -ang_y; } } } last_xp1.x = -1; last_xp1.y = -1; last_xp2.x = -1; last_xp2.y = -1; start_z -= speed; /* speed += coaster[0].dy / 10.0; if (speed < MIN_SPEED) { speed = MIN_SPEED; } if (speed > MAX_SPEED) { speed = MAX_SPEED; } */ for(f = 0; f < COASTER_NUM; f++) { XPoint xp1, xp2; x += coaster[f].dx; y += coaster[f].dy; z += COASTER_Z_INC; RotateY(0, 0, 0, x, y, z, &rx, &ry, &rz, (int)ang_y); RotateX(0, 0, 0, rx, ry, rz, &rx2, &ry2, &rz2, (int)ang_x); if (Project(rx2, ry2, rz2, &xp1) && Project(rx2 + COASTER_WIDTH, ry2, rz2, &xp2)) { if (last_xp1.x != -1) { Line(last_xp1.x, last_xp1.y, xp1.x, xp1.y, white); } if (last_xp2.x != -1) { Line(last_xp2.x, last_xp2.y, xp2.x, xp2.y, white); } last_xp1 = xp1; last_xp2 = xp2; if (coaster[f].bar) { Line(xp1.x, xp1.y, xp2.x, xp2.y, white); } if (coaster[f].pole) { Line(xp1.x, xp1.y, xp1.x, height, white); Line(xp2.x, xp2.y, xp2.x, height, white); } } if (start_z < 10.0) { if (f < COASTER_NUM - 1) { coaster[f] = coaster[f + 1]; } else { if (dcount == 0 && dx == 0 && dy == 0) { dcount = RND2(300, 1000); dx = 0; dy = 0; dxi = RND(10) / 40.0 - 0.125; dyi = RND(10) / 80.0 - 0.065; } else { if (dcount) { dcount--; if (dx < COASTER_Z_INC / 2.0 && dx > -COASTER_Z_INC / 2.0) { dx += dxi; } if (dy < COASTER_Z_INC / 2.0 && dy > -COASTER_Z_INC / 2.0) { dy += dyi; } } else { double sdx = SgnF(dx); double sdy = SgnF(dy); if (dx != 0.0) { dx -= dxi; } if (dy != 0.0) { dy -= dyi; } if (SgnF(dx) != sdx) { dx = 0; } if (SgnF(dy) != sdy) { dy = 0; } } } coaster[f].dx = dx; coaster[f].dy = dy; if ((counter % COASTER_BAR) == 0) { coaster[f].bar = TRUE; } else { coaster[f].bar = FALSE; } if ((counter % COASTER_POLE) == 0) { coaster[f].pole = TRUE; } else { coaster[f].pole = FALSE; } counter++; } } } if (start_z < 10.0) { start_z += COASTER_Z_INC; } } #undef COASTER_NUM #undef COASTER_WIDTH #undef COASTER_Z_INC #undef COASTER_BAR #undef COASTER_POLE /* ---------------------------------------- FIREWORK DEMO */ #define MAX_FIREWORKS 10 #define FIREWORK_SPARKS 100 #define FIREWORK_COL 4 #define DY_ADJ 0.0005 static void FireworkDemo(void) { static int init = FALSE; typedef struct { double x,y; double dx,dy; int life; } Spark; typedef struct { int alive; double x,y; double dx,dy; int life; Spark spark[FIREWORK_SPARKS]; } Firework; static Firework firework[MAX_FIREWORKS]; static int colour[FIREWORK_COL]; int f,r; if (!init) { init=TRUE; colour[0] = white; colour[1] = GetCol(255, 128, 128); colour[2] = GetCol(128, 255, 128); colour[3] = GetCol(128, 128, 255); } Cls(); if (RND(100) > 95) { for(f = 0; f < MAX_FIREWORKS && firework[f].alive; f++) { } if (f < MAX_FIREWORKS) { firework[f].alive = TRUE; firework[f].life = RND2(400, 250); firework[f].y = height; firework[f].x = RND2(width - 100, 100); firework[f].dx = (RND(100) / 100.0) - 0.5; firework[f].dy = -(RND(100) / 100.0) - 0.4; } } for(f = 0; f < MAX_FIREWORKS; f++) { if (firework[f].alive) { if (firework[f].life > 0) { firework[f].x += firework[f].dx; firework[f].y += firework[f].dy; firework[f].dy += DY_ADJ; firework[f].life--; FCircle(firework[f].x, firework[f].y, 2, colour[RND(FIREWORK_COL)]); if (firework[f].life == 0) { for(r = 0; r < FIREWORK_SPARKS; r++) { firework[f].spark[r].life = RND2(400, 200); firework[f].spark[r].x = firework[f].x; firework[f].spark[r].y = firework[f].y; firework[f].spark[r].dx = (RND(100) / 100.0) - 0.5; firework[f].spark[r].dy = (RND(100) / 100.0) - 0.5; } } } else { int all_dead = TRUE; for(r = 0; r < FIREWORK_SPARKS; r++) { if (firework[f].spark[r].life > 0) { all_dead = FALSE; firework[f].spark[r].x += firework[f].spark[r].dx; firework[f].spark[r].y += firework[f].spark[r].dy; firework[f].spark[r].dy += DY_ADJ; firework[f].spark[r].life--; Plot(firework[f].spark[r].x, firework[f].spark[r].y, colour[RND(FIREWORK_COL)]); } } if (all_dead) { firework[f].alive = FALSE; } } } } } #undef MAX_FIREWORKS #undef FIREWORK_SPARKS #undef FIREWORK_COL #undef DY_ADJ /* ---------------------------------------- RAIN DEMO */ #define MAX_DROPS 40 #define MAX_DRIBBLES 5 #define DRIBBLE_LEN 30 #define MAX_RADIUS 40 static void RainDemo(void) { static int init = FALSE; static unsigned cycle; typedef struct { int alive; int x,y; int r; int life; } Drop; static Drop drop[MAX_DROPS]; typedef struct { int alive; int x,y; int lastx[DRIBBLE_LEN]; int life; } Dribble; static Dribble dribble[MAX_DRIBBLES]; int f, r; if (!init) { init=TRUE; for(f = 0; f < MAX_DRIBBLES; f++) { dribble[f].alive = TRUE; dribble[f].y = RND(height); dribble[f].life = (height - dribble[f].y) + DRIBBLE_LEN; dribble[f].x = RND(width); for(r = 0; r < DRIBBLE_LEN; r++) { dribble[f].lastx[r] = dribble[f].x; } } } Cls(); if (RND(100) > 95) { for(f = 0; f < MAX_DROPS && drop[f].alive; f++) { } if (f < MAX_DROPS) { drop[f].alive = TRUE; drop[f].life = RND2(height-20, height/2); drop[f].y = 0; drop[f].r = 0; drop[f].x = RND(width); } } if (RND(100) > 98) { for(f = 0; f < MAX_DRIBBLES && dribble[f].alive; f++) { } if (f < MAX_DRIBBLES) { dribble[f].alive = TRUE; dribble[f].life = height + DRIBBLE_LEN; dribble[f].y = 0; dribble[f].x = RND(width); for(r = 0; r < DRIBBLE_LEN; r++) { dribble[f].lastx[r] = dribble[f].x; } } } for(f = 0; f < MAX_DRIBBLES; f++) { if (dribble[f].alive) { for(r = 0; r < DRIBBLE_LEN; r++) { FCircle(dribble[f].lastx[r], dribble[f].y - DRIBBLE_LEN + r, r/4, blue); } } } if ((cycle % 5) == 0) { for(f = 0; f < MAX_DRIBBLES; f++) { if (dribble[f].alive) { if (dribble[f].life > 0) { dribble[f].y++; dribble[f].x += RND(3) - 1; dribble[f].life--; for(r = 0; r < DRIBBLE_LEN; r++) { if (r < DRIBBLE_LEN - 1) { dribble[f].lastx[r] = dribble[f].lastx[r+1]; } else { dribble[f].lastx[r] = dribble[f].x; } } } else { dribble[f].alive = FALSE; } } } } for(f = 0; f < MAX_DROPS; f++) { if (drop[f].alive) { if (drop[f].life > 0) { drop[f].y++; drop[f].life--; FCircle(drop[f].x, drop[f].y, 3, blue); Line(drop[f].x, drop[f].y, drop[f].x, drop[f].y - 5, blue); } else { if (drop[f].r < MAX_RADIUS) { drop[f].r++; Ellipse(drop[f].x, drop[f].y, drop[f].r, drop[f].r/3, blue); } else { drop[f].alive = FALSE; } } } } cycle++; } #undef MAX_DROPS #undef MAX_DRIBBLES #undef DRIBBLE_LEN #undef MAX_RADIUS /* END OF FILE */