/* vigilance, Copyright (c) 2017-2018 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * Draws surveillance cameras, taking an interest in their surroundings. */ #define DEFAULTS "*delay: 20000 \n" \ "*count: 5 \n" \ "*showFPS: False \n" \ "*wireframe: False \n" \ "*bodyColor: #666666" "\n" \ "*capColor: #FFFFFF" "\n" \ "*hingeColor: #444444" "\n" \ "*mountColor: #444444" "\n" \ "*lensColor: #000000" "\n" \ "*groundColor: #004400" "\n" \ # define release_camera 0 #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) #define DEF_SPEED "1.0" #define DEF_CAMERA_SIZE "1.0" #include "xlockmore.h" #include "gltrackball.h" #include "ximage-loader.h" #include "normals.h" #include #ifdef USE_GL /* whole file */ #undef ABS #define ABS(x) ((x)<0?-(x):(x)) #undef MAX #define MAX(A,B) ((A)>(B)?(A):(B)) #undef MIN #define MIN(A,B) ((A)<(B)?(A):(B)) #undef BELLRAND #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3) #include "gllist.h" extern const struct gllist *seccam_body, *seccam_cap, *seccam_hinge, *seccam_pipe, *seccam_lens; static struct gllist *ground = 0; static const struct gllist * const *all_objs[] = { &seccam_body, &seccam_cap, &seccam_hinge, &seccam_pipe, &seccam_lens, (const struct gllist * const *) &ground }; #define CAMERA_BODY 0 #define CAMERA_CAP 1 #define CAMERA_HINGE 2 #define CAMERA_MOUNT 3 #define CAMERA_LENS 4 #define GROUND 5 #define BEAM_ZOFF 0.185 /* distance from origin to lens in model */ typedef enum { IDLE, WARM_UP, ZOT, COOL_DOWN } camera_state; typedef struct { XYZ pos; GLfloat facing; /* rotation around vertical axis, degrees */ GLfloat pitch; /* front/back tilt, degrees */ GLfloat velocity; /* most recent angular velocity, degrees */ long focus_id; /* pedestrian or camera of interest */ XYZ focus; /* point of interest */ camera_state state; GLfloat tick; } camera; typedef struct pedestrian pedestrian; struct pedestrian { long id; XYZ pos; GLfloat length; GLfloat frequency, amplitude; GLfloat ratio; GLfloat speed; pedestrian *next; }; typedef struct { GLXContext *glx_context; trackball_state *user_trackball; Bool button_down_p; GLuint *dlists; GLfloat component_colors[countof(all_objs)][4]; int ncameras; camera *cameras; pedestrian *pedestrians; } camera_configuration; static camera_configuration *bps = NULL; static GLfloat speed_arg; #ifdef DEBUG static int debug_p; #endif static XrmOptionDescRec opts[] = { { "-speed", ".speed", XrmoptionSepArg, 0 }, #ifdef DEBUG {"-debug", ".debug", XrmoptionNoArg, "True" }, {"+debug", ".debug", XrmoptionNoArg, "False" }, #endif }; static argtype vars[] = { {&speed_arg, "speed", "Speed", DEF_SPEED, t_Float}, #ifdef DEBUG {&debug_p, "debug", "Debug", "False", t_Bool}, #endif }; ENTRYPOINT ModeSpecOpt camera_opts = { countof(opts), opts, countof(vars), vars, NULL}; ENTRYPOINT void reshape_camera (ModeInfo *mi, int width, int height) { GLfloat h = (GLfloat) height / (GLfloat) width; int y = 0; if (width > height * 5) { /* tiny window: show middle */ height = width * 9/16; y = -height/2; h = height / (GLfloat) width; } glViewport (0, y, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective (30.0, 1/h, 1.0, 200); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt( 0, 0, 30, 0, 0, 0, 0, 1, 0); # ifdef HAVE_MOBILE /* Keep it the same relative size when rotated. */ { int o = (int) current_device_rotation(); if (o != 0 && o != 180 && o != -180) glScalef (1/h, 1/h, 1/h); } # endif glClear(GL_COLOR_BUFFER_BIT); } ENTRYPOINT Bool camera_handle_event (ModeInfo *mi, XEvent *event) { camera_configuration *bp = &bps[MI_SCREEN(mi)]; if (gltrackball_event_handler (event, bp->user_trackball, MI_WIDTH (mi), MI_HEIGHT (mi), &bp->button_down_p)) return True; else if (event->xany.type == KeyPress) { KeySym keysym; char c = 0; XLookupString (&event->xkey, &c, 1, &keysym, 0); if (c == ' ' || c == '\t') { int i; if (bp->cameras[0].state == IDLE && bp->pedestrians) for (i = 0; i < bp->ncameras; i++) { bp->cameras[i].state = WARM_UP; bp->cameras[i].tick = 1.0; } return True; } } return False; } static int draw_ground (ModeInfo *, GLfloat color[4]); static void parse_color (ModeInfo *mi, char *key, GLfloat color[4]) { XColor xcolor; char *string = get_string_resource (mi->dpy, key, "CameraColor"); if (!XParseColor (mi->dpy, mi->xgwa.colormap, string, &xcolor)) { fprintf (stderr, "%s: unparsable color in %s: %s\n", progname, key, string); exit (1); } free (string); color[0] = xcolor.red / 65536.0; color[1] = xcolor.green / 65536.0; color[2] = xcolor.blue / 65536.0; color[3] = 1; } ENTRYPOINT void init_camera (ModeInfo *mi) { camera_configuration *bp; int wire = MI_IS_WIREFRAME(mi); int i; MI_INIT (mi, bps); bp = &bps[MI_SCREEN(mi)]; bp->glx_context = init_GL(mi); reshape_camera (mi, MI_WIDTH(mi), MI_HEIGHT(mi)); glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_CULL_FACE); if (!wire) { GLfloat pos[4] = {0.4, 0.2, 0.4, 0.0}; GLfloat amb[4] = {0.2, 0.2, 0.2, 1.0}; GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0}; GLfloat spc[4] = {1.0, 1.0, 1.0, 1.0}; glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glLightfv(GL_LIGHT0, GL_POSITION, pos); glLightfv(GL_LIGHT0, GL_AMBIENT, amb); glLightfv(GL_LIGHT0, GL_DIFFUSE, dif); glLightfv(GL_LIGHT0, GL_SPECULAR, spc); } bp->user_trackball = gltrackball_init (False); bp->dlists = (GLuint *) calloc (countof(all_objs)+1, sizeof(GLuint)); for (i = 0; i < countof(all_objs); i++) bp->dlists[i] = glGenLists (1); for (i = 0; i < countof(all_objs); i++) { const struct gllist *gll = *all_objs[i]; char *key = 0; GLfloat spec1[4] = {1.00, 1.00, 1.00, 1.0}; GLfloat spec2[4] = {0.40, 0.40, 0.70, 1.0}; GLfloat *spec = spec1; GLfloat shiny = 20; glNewList (bp->dlists[i], GL_COMPILE); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMatrixMode(GL_TEXTURE); glPushMatrix(); glMatrixMode(GL_MODELVIEW); glRotatef (-90, 1, 0, 0); glRotatef (180, 0, 0, 1); glScalef (6, 6, 6); glBindTexture (GL_TEXTURE_2D, 0); switch (i) { case CAMERA_BODY: key = "bodyColor"; break; case CAMERA_CAP: key = "capColor"; break; case CAMERA_HINGE: key = "hingeColor"; break; case CAMERA_MOUNT: key = "mountColor"; break; case CAMERA_LENS: key = "lensColor"; spec = spec2; break; case GROUND: key = "groundColor"; spec = spec2; shiny = 128; break; default: abort(); break; } parse_color (mi, key, bp->component_colors[i]); glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, spec); glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shiny); switch (i) { case GROUND: if (! ground) ground = (struct gllist *) calloc (1, sizeof(*ground)); ground->points = draw_ground (mi, bp->component_colors[i]); break; default: renderList (gll, wire); /* glColor3f (1, 1, 1); renderListNormals (gll, 100, True); */ /* glColor3f (1, 1, 0); renderListNormals (gll, 100, False); */ break; } glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glEndList (); } bp->ncameras = MI_COUNT(mi); if (bp->ncameras <= 0) bp->ncameras = 1; bp->cameras = (camera *) calloc (bp->ncameras, sizeof (camera)); { GLfloat range = (MI_COUNT(mi) <= 2) ? 4 : 5.5; GLfloat extent; GLfloat spacing = range / bp->ncameras; if (spacing < 0.7) spacing = 0.7; extent = spacing * (bp->ncameras - 1); for (i = 0; i < bp->ncameras; i++) { camera *c = &bp->cameras[i]; c->state = IDLE; c->pos.x = i*spacing - extent/2; c->pos.z += 0.7; if (spacing < 1.6) c->pos.z = (i & 1 ? 1.1 : -0.3); c->focus.x = c->pos.x; c->focus.y = c->pos.y + 1; c->focus.z = c->pos.z + BEAM_ZOFF; c->pitch = -50; } } # ifdef DEBUG if (!debug_p) # endif /* Let's tilt the floor a little. */ gltrackball_reset (bp->user_trackball, -0.70 + frand(1.58), -0.30 + frand(0.40)); } static XYZ normalize (XYZ p) { GLfloat d = sqrt(p.x*p.x + p.y*p.y * p.z*p.z); if (d < 0.0000001) p.x = p.y = p.z = 0; else { p.x /= d; p.y /= d; p.z /= d; } return p; } static GLfloat vector_angle (XYZ a, XYZ b) { double La = sqrt (a.x*a.x + a.y*a.y + a.z*a.z); double Lb = sqrt (b.x*b.x + b.y*b.y + b.z*b.z); double cc, angle; if (La == 0 || Lb == 0) return 0; if (a.x == b.x && a.y == b.y && a.z == b.z) return 0; /* dot product of two vectors is defined as: La * Lb * cos(angle between vectors) and is also defined as: ax*bx + ay*by + az*bz so: La * Lb * cos(angle) = ax*bx + ay*by + az*bz cos(angle) = (ax*bx + ay*by + az*bz) / (La * Lb) angle = acos ((ax*bx + ay*by + az*bz) / (La * Lb)); */ cc = (a.x*b.x + a.y*b.y + a.z*b.z) / (La * Lb); if (cc > 1) cc = 1; /* avoid fp rounding error (1.000001 => sqrt error) */ angle = acos (cc); return (angle); } static int draw_component (ModeInfo *mi, int i, GLfloat color[4]) { camera_configuration *bp = &bps[MI_SCREEN(mi)]; if (! color) color = bp->component_colors[i]; glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); glCallList (bp->dlists[i]); return (*all_objs[i])->points / 3; } static int draw_double_component (ModeInfo *mi, int i) { int count = draw_component (mi, i, 0); glFrontFace(GL_CCW); glScalef (1, 1, -1); count += draw_component (mi, i, 0); glScalef (1, 1, -1); glFrontFace(GL_CW); return count; } static int draw_ray (ModeInfo *mi, camera *c) { int wire = MI_IS_WIREFRAME(mi); int count = 0; GLfloat length = 10000; GLfloat thickness = 0.08; int i; glPushMatrix(); glTranslatef (c->pos.x, c->pos.y, c->pos.z + BEAM_ZOFF); glRotatef (-c->facing, 0, 0, 1); glRotatef ( c->pitch, 1, 0, 0); glRotatef (frand(90), 0, 1, 0); glScalef (thickness, length, thickness); glDisable (GL_LIGHTING); for (i = 0; i < 5; i++) { glColor4f (1, 0, 0, 0.1 + (i * 0.18)); glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glVertex3f (0, 0, -0.5); glVertex3f (0, 0, 0.5); glVertex3f (0, 1, 0.5); glVertex3f (0, 1, -0.5); glEnd(); count++; glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glVertex3f (-0.5, 0, 0); glVertex3f ( 0.5, 0, 0); glVertex3f ( 0.5, 1, 0); glVertex3f (-0.5, 1, 0); glEnd(); count++; glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glVertex3f (0, 1, -0.5); glVertex3f (0, 1, 0.5); glVertex3f (0, 0, 0.5); glVertex3f (0, 0, -0.5); glEnd(); count++; glBegin (wire ? GL_LINE_LOOP : GL_QUADS); glVertex3f (-0.5, 1, 0); glVertex3f ( 0.5, 1, 0); glVertex3f ( 0.5, 0, 0); glVertex3f (-0.5, 0, 0); glEnd(); count++; glScalef (0.7, 1, 0.7); } if (!MI_IS_WIREFRAME(mi)) glEnable (GL_LIGHTING); glPopMatrix(); return count; } static int draw_camera_1 (ModeInfo *mi, camera *c) { int count = 0; GLfloat scale = 0.01; GLfloat color[4] = { 1, 0, 0, 1 }; glPushMatrix(); glTranslatef (c->pos.x, c->pos.y, c->pos.z); glScalef (scale, scale, scale); glRotatef (90, 1, 0, 0); glRotatef (-90, 0, 1, 0); count += draw_double_component (mi, CAMERA_MOUNT); glRotatef (-c->facing, 0, 1, 0); glRotatef (-c->pitch, 0, 0, 1); count += draw_double_component (mi, CAMERA_HINGE); if (c->state == WARM_UP) { if (c->tick < 0.2) glTranslatef ((0.2 - c->tick) / (scale * 3), 0, 0); } if (c->state == ZOT) { glTranslatef ((0.005 - frand(0.01)) / scale, (0.005 - frand(0.01)) / scale, (0.005 - frand(0.01)) / scale); } count += draw_double_component (mi, CAMERA_BODY); if (c->state == ZOT) { glTranslatef ((0.005 - frand(0.01)) / scale, (0.005 - frand(0.01)) / scale, (0.005 - frand(0.01)) / scale); } count += draw_double_component (mi, CAMERA_CAP); switch (c->state) { case IDLE: break; case WARM_UP: color[0] = 1.0 - c->tick; break; case ZOT: color[0] = 1.0; break; case COOL_DOWN: color[0] = c->tick; break; default: abort(); break; } count += draw_component (mi, CAMERA_LENS, (c->state == IDLE ? 0 : color)); # ifdef DEBUG if (debug_p && c->state != ZOT) { glDisable (GL_LIGHTING); glColor3f (1, 1, 0); glBegin (GL_LINES); glVertex3f (0, BEAM_ZOFF / scale, 0); glVertex3f (-100 / scale, BEAM_ZOFF / scale, 0); glEnd(); if (!MI_IS_WIREFRAME(mi)) glEnable (GL_LIGHTING); } # endif glPopMatrix(); return count; } /* The position this pedestrian would appear at during the given ratio through its life cycle. */ static XYZ pedestrian_position (pedestrian *p, GLfloat ratio) { XYZ pos = p->pos; if (p->speed < 0) ratio = 1-ratio; pos.x += p->length * ratio; pos.z += sin (M_PI * ratio * p->frequency * 2) * p->amplitude + p->amplitude/2; return pos; } static int draw_pedestrian (ModeInfo *mi, pedestrian *p) { int count = 0; # ifdef DEBUG if (debug_p) { GLfloat r; GLfloat step = 0.001; glDisable (GL_LIGHTING); glColor3f (0, 0, 1); glBegin (GL_LINE_STRIP); glVertex3f (p->pos.x, p->pos.y, p->pos.z + p->amplitude); glVertex3f (p->pos.x, p->pos.y, 0); glVertex3f (p->pos.x + p->length, p->pos.y, 0); glVertex3f (p->pos.x + p->length, p->pos.y, p->pos.z + p->amplitude); glEnd(); glBegin (GL_LINE_STRIP); for (r = 0; r <= 1; r += step) { XYZ pos = pedestrian_position (p, r); glVertex3f (pos.x, pos.y, pos.z); count++; if (p->ratio >= r && p->ratio < r + step) { glVertex3f (pos.x, pos.y, pos.z - p->amplitude); glVertex3f (pos.x, pos.y, pos.z + p->amplitude); glVertex3f (pos.x, pos.y, pos.z); count++; } } glEnd(); if (!MI_IS_WIREFRAME(mi)) glEnable (GL_LIGHTING); } # endif return count; } /* Start someone walking through the scene. */ static void add_pedestrian (ModeInfo *mi) { camera_configuration *bp = &bps[MI_SCREEN(mi)]; pedestrian *p = (pedestrian *) calloc (1, sizeof(*p)); static int id = 100; p->id = id++; p->length = 35; p->ratio = 0; p->pos.x = -p->length/2; p->pos.y = 3 + frand(10); p->pos.z = -1.5 + frand(4) + ((random() % 10) ? 0 : frand(8)); p->frequency = 4 + frand(4); p->amplitude = 0.1 + ((random() % 10) ? BELLRAND(0.45) : BELLRAND(1.5)); p->ratio = 0; p->speed = ((4 + frand(4) + ((random() % 10) ? 0 : frand(10))) * (random() & 1 ? 1 : -1) * speed_arg); if (bp->pedestrians) { pedestrian *p2; /* add it to the end */ for (p2 = bp->pedestrians; p2->next; p2 = p2->next) ; p2->next = p; } else { p->next = bp->pedestrians; bp->pedestrians = p; } } static void free_pedestrian (ModeInfo *mi, pedestrian *p) { camera_configuration *bp = &bps[MI_SCREEN(mi)]; Bool ok = False; if (!p) abort(); if (p == bp->pedestrians) { bp->pedestrians = p->next; ok = True; } else { pedestrian *p0; for (p0 = bp->pedestrians; p0; p0 = p0->next) if (p0->next == p) { p0->next = p0->next ? p0->next->next : 0; ok = True; break; } } if (!ok) abort(); p->next = 0; free (p); } static void tick_pedestrian (ModeInfo *mi, pedestrian *p) { p->ratio += 0.001 * (p->speed > 0 ? p->speed : -p->speed); if (p->ratio >= 1) free_pedestrian (mi, p); } /* Extract the position of the thing this camera would like to look at. */ static void set_camera_focus (ModeInfo *mi, camera *c) { camera_configuration *bp = &bps[MI_SCREEN(mi)]; if (c->focus_id > 0) /* Look at pedestrian with id N */ { long id = c->focus_id; pedestrian *p; for (p = bp->pedestrians; p; p = p->next) if (p->id == id) break; if (p) c->focus = pedestrian_position (p, p->ratio); else c->focus_id = 0; /* that pedestrian has escaped */ } else if (c->focus_id < 0) /* Look at camera -N-1 */ { long n = -c->focus_id - 1; if (bp->ncameras > n) c->focus = bp->cameras[n].pos; } } static void tick_camera (ModeInfo *mi, camera *c) { camera_configuration *bp = &bps[MI_SCREEN(mi)]; GLfloat X, Y, Z; set_camera_focus (mi, c); X = c->focus.x - c->pos.x; Y = c->focus.y - c->pos.y; Z = c->focus.z - c->pos.z - BEAM_ZOFF; if (X != 0 || Y != 0) { GLfloat ofacing = c->facing; GLfloat opitch = c->pitch; GLfloat target_facing = atan2 (X, Y) * (180 / M_PI); GLfloat target_pitch = atan2 (Z, sqrt(X*X + Y*Y)) * (180 / M_PI); /* Adjust the current velocity. If we are nearing the target, slow down (but don't stop). Otherwise, speed up (but don't break the speed limit). */ { GLfloat accel = 0.5 * speed_arg; GLfloat decel_range = 20; GLfloat max_velocity = 5 * speed_arg; GLfloat close_enough = 0.5 * speed_arg; GLfloat dx = target_facing - c->facing; GLfloat dy = target_pitch - c->pitch; GLfloat angular_distance = sqrt (dx*dx + dy*dy); /* Split the velocity into vx and vy components. This isn't quite right since this treats them as Cartesian rather than polar, but it's close enough. */ GLfloat r = (dx == 0) ? 1 : ABS(dy) / ABS(dx); GLfloat vx = 1-r; GLfloat vy = r; if (angular_distance < decel_range) /* Nearing target, slow down */ { c->velocity -= accel; if (c->velocity <= 0) c->velocity = accel; } else { c->velocity += accel; if (c->velocity > max_velocity) c->velocity = max_velocity; } /* Don't overshoot */ if (vx > ABS(dx)) vx = ABS(dx); if (vy > ABS(dy)) vy = ABS(dy); /* Rotate toward target by current angular velocity. */ c->facing += vx * c->velocity * (target_facing > c->facing ? 1 : -1); c->pitch += vy * c->velocity * (target_pitch > c->pitch ? 1 : -1); /* If we are pointed *really close* to the target, just lock on, to avoid twitchy overshoot rounding errors. */ if (angular_distance < close_enough) { c->facing = target_facing; c->pitch = target_pitch; } /* Constrain to hinge's range of motion */ c->facing = MAX (-90, MIN (90, c->facing)); c->pitch = MAX (-90, MIN (55, c->pitch)); /* After this motion, our prevailing velocity (for next time) is the angular distance we actually moved. */ dx = c->facing - ofacing; dy = c->pitch - opitch; c->velocity = sqrt (dx*dx + dy*dy); } } # ifdef DEBUG if (debug_p && 1) /* Mark the point on which this camera is focusing. */ { glPushMatrix(); glDisable (GL_LIGHTING); glColor3f(0.3, 0.3, 0.3); glBegin (GL_LINES); glVertex3f (c->pos.x, c->pos.y, c->pos.z + BEAM_ZOFF); glVertex3f (c->focus.x, c->focus.y, c->focus.z); glEnd(); glColor3f(1,1,1); glBegin (GL_LINES); glVertex3f (c->focus.x-0.25, c->focus.y, c->focus.z); glVertex3f (c->focus.x+0.25, c->focus.y, c->focus.z); glVertex3f (c->focus.x, c->focus.y-0.25, c->focus.z); glVertex3f (c->focus.x, c->focus.y+0.25, c->focus.z); glVertex3f (c->focus.x, c->focus.y, c->focus.z-0.25); glVertex3f (c->focus.x, c->focus.y, c->focus.z+0.25); glEnd(); if (!MI_IS_WIREFRAME(mi)) glEnable (GL_LIGHTING); glPopMatrix(); } # endif /* If this camera is looking at another camera, get shy and look away if the target sees us looking. */ if (c->focus_id < 0) { camera *c2 = &bp->cameras[-1 - c->focus_id]; XYZ a, b; GLfloat aa = c->facing / (180 / M_PI); GLfloat bb = c->pitch / (180 / M_PI); GLfloat angle; a.y = cos(aa)* cos(bb); a.x = sin(aa)* cos(bb); a.z = sin(bb); aa = c2->facing / (180 / M_PI); bb = c2->pitch / (180 / M_PI); b.y = cos(aa)* cos(bb); b.x = sin(aa)* cos(bb); b.z = sin(bb); angle = vector_angle (normalize(a), normalize(b)) * (180 / M_PI); if (angle > 100) { c->focus_id = 0; /* Look "away" which means in the same direction. */ c->focus.x = c->pos.x + (c2->focus.x - c2->pos.x); c->focus.y = c->pos.y + (c2->focus.y - c2->pos.y); c->focus.z = c->pos.z + (c2->focus.z - c2->pos.z); /* Look at either the sky or the ground.*/ c->focus.z = c->focus.x * (random() & 1 ? 1 : -1) * 5; c->velocity = c2->velocity * 3; } } /* Randomly pick some other things to look at. */ if (c->state == IDLE && (c->focus_id <= 0 ? !(random() % (int) MAX (1, (50 / speed_arg))) : !(random() % (int) MAX (1, (1000 / speed_arg))))) { /* Shiny! Start paying attention to something else. */ if ((bp->ncameras > 1 && !(random() % 20))) /* look at a camera */ { int which = random() % 4; long i; for (i = 0; i < bp->ncameras; i++) if (c == &bp->cameras[i]) break; /* Look at cameras 1 or 2 away in each direction, but not farther. Since we arrange them in 2 staggered lines, those are the only four that are in line of sight. */ if (i >= 2 && which == 0) which = i-2; else if (i >= 1 && which == 1) which = i-1; else if (i < bp->ncameras-2 && which == 2) which = i+2; else if (i == bp->ncameras-1) which = i-1; else /* (i < bp->ncameras-2 && which == 3) */ which = i+1; c->focus_id = -1 - which; } else /* look at a pedestrian */ { int n = 0; pedestrian *p; for (p = bp->pedestrians; p; p = p->next) n++; if (n > 0) { n = random() % n; p = bp->pedestrians; while (n > 0 && p) p = p->next; if (p) c->focus_id = p->id; } } } /* Run the animation */ if (c->state != IDLE) { pedestrian *p = bp->pedestrians; /* first one */ if (p) c->focus_id = p->id; switch (c->state) { case WARM_UP: c->tick -= 0.01 * speed_arg; break; case ZOT: c->tick -= 0.006 * speed_arg; if (p) p->speed *= 0.995; /* target takes 1d6 HP damage */ break; case COOL_DOWN: c->tick -= 0.02 * speed_arg; break; default: abort(); break; } if (c->tick <= 0) { c->tick = 1.0; switch (c->state) { case WARM_UP: c->state = ZOT; break; case ZOT: c->state = COOL_DOWN; c->focus_id = 0; if (p) p->ratio = 1.0; /* threat eliminated */ break; case COOL_DOWN: c->state = IDLE; break; default: abort(); break; } } } if (bp->cameras[0].state == IDLE && bp->pedestrians && bp->pedestrians[0].ratio < 0.3 && !(random() % MAX (1, (int) (50000 / speed_arg)))) { /* CASE NIGHTMARE RED detected, engage SCORPION STARE by authority of MAGINOT BLUE STARS. */ int i; for (i = 0; i < bp->ncameras; i++) { bp->cameras[i].state = WARM_UP; bp->cameras[i].tick = 1.0; } } } static int draw_ground (ModeInfo *mi, GLfloat color[4]) { int wire = MI_IS_WIREFRAME(mi); GLfloat i, j, k, h; /* When using fog, iOS apparently doesn't like lines or quads that are really long, and extend very far outside of the scene. Maybe? If the length of the line (cells * cell_size) is greater than 25 or so, lines that are oriented steeply away from the viewer tend to disappear (whether implemented as GL_LINES or as GL_QUADS). So we do a bunch of smaller grids instead of one big one. */ int cells = 20; GLfloat cell_size = 0.4; int points = 0; int gridsw = 10; int gridsh = 2; glPushMatrix(); if (!wire) { GLfloat fog_color[4] = { 0, 0, 0, 1 }; glLineWidth (2); glEnable (GL_LINE_SMOOTH); glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable (GL_BLEND); glFogi (GL_FOG_MODE, GL_EXP2); glFogfv (GL_FOG_COLOR, fog_color); glFogf (GL_FOG_DENSITY, 0.017); glFogf (GL_FOG_START, -cells/2 * cell_size * gridsh); glEnable (GL_FOG); } glColor4fv (color); glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color); glTranslatef (-cells * gridsw * cell_size / 2, 0, 0); for (h = 0; h < 2; h++) { glPushMatrix(); glTranslatef (0, cells * cell_size / 2, 0); for (j = 0; j < gridsh; j++) { glPushMatrix(); for (k = 0; k < gridsw; k++) { glBegin (GL_LINES); for (i = -cells/2; i < cells/2; i++) { GLfloat a = i * cell_size; GLfloat b = cells/2 * cell_size; glVertex3f (a, -b, 0); glVertex3f (a, b, 0); points++; glVertex3f (-b, a, 0); glVertex3f (b, a, 0); points++; } glEnd(); glTranslatef (cells * cell_size, 0, 0); } glPopMatrix(); glTranslatef (0, cells * cell_size, 0); } glPopMatrix(); glRotatef (90, 1, 0, 0); } if (!wire) glDisable (GL_LINE_SMOOTH); glPopMatrix(); return points; } ENTRYPOINT void draw_camera (ModeInfo *mi) { camera_configuration *bp = &bps[MI_SCREEN(mi)]; Display *dpy = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); GLfloat camera_size; int i; if (!bp->glx_context) return; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix (); # ifdef HAVE_MOBILE glRotatef (current_device_rotation(), 0, 0, 1); /* right side up */ # endif gltrackball_rotate (bp->user_trackball); # ifdef HAVE_MOBILE { GLfloat s = 0.6; glScalef (s, s, s); } # endif # ifdef DEBUG if (debug_p) { GLfloat s = 0.2; glScalef (s, s, s); glRotatef (30, 0, 1, 0); glRotatef (15, 1, 0, 0); glTranslatef (0, 0, -80); } # endif mi->polygon_count = 0; camera_size = 5; if (MI_COUNT(mi) <= 2) /* re-frame the scene a little bit */ glTranslatef (0, -1, 7); if (MI_COUNT(mi) >= 20) glTranslatef (0, -1.5, -5); if (MI_COUNT(mi) >= 40) glTranslatef (0, 2, -15); glScalef (camera_size, camera_size, camera_size); /* +Z is toward sky; +X is toward the right along the back wall; +Y is toward the viewer. */ glRotatef (-90, 1, 0, 0); glScalef (1, -1, 1); glPushMatrix(); glScalef (1/camera_size, 1/camera_size, 1/camera_size); glTranslatef (0, -2.38, -8); /* Move the ground down and back */ glCallList (bp->dlists[GROUND]); mi->polygon_count += ground->points; glPopMatrix(); { pedestrian *p, *p2; for (p = bp->pedestrians, p2 = p ? p->next : 0; p; p = p2, p2 = p2 ? p2->next : 0) { mi->polygon_count += draw_pedestrian (mi, p); tick_pedestrian (mi, p); /* might free p */ } if (!bp->pedestrians || !(random() % MAX (1, (int) (200 / speed_arg)))) add_pedestrian (mi); } for (i = 0; i < bp->ncameras; i++) { camera *c = &bp->cameras[i]; mi->polygon_count += draw_camera_1 (mi, c); tick_camera (mi, c); } for (i = 0; i < bp->ncameras; i++) { camera *c = &bp->cameras[i]; if (c->state == ZOT) /* Do this last, for alpha blending */ mi->polygon_count += draw_ray (mi, c); } glPopMatrix (); if (mi->fps_p) do_fps (mi); glFinish(); glXSwapBuffers(dpy, window); } ENTRYPOINT void free_camera (ModeInfo *mi) { camera_configuration *bp = &bps[MI_SCREEN(mi)]; pedestrian *p; int i; if (!bp->glx_context) return; glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *bp->glx_context); if (bp->user_trackball) gltrackball_free (bp->user_trackball); if (bp->cameras) free (bp->cameras); p = bp->pedestrians; while (p) { pedestrian *p2 = p->next; free (p); p = p2; } if (bp->dlists) { for (i = 0; i < countof(all_objs); i++) if (glIsList(bp->dlists[i])) glDeleteLists(bp->dlists[i], 1); free (bp->dlists); } } XSCREENSAVER_MODULE_2 ("Vigilance", vigilance, camera) #endif /* USE_GL */