Code Search for Developers
 
 
  

exspline.c from Allegro game programming library at Krugle


Show exspline.c syntax highlighted

/*
 *    Example program for the Allegro library, by Shawn Hargreaves.
 *
 *    This program demonstrates the use of al_draw_spline curves to create smooth 
 *    paths connecting a number of node points. This can be useful for 
 *    constructing realistic motion and animations.
 *
 *    The technique is to connect the series of guide points p1..p(n) with
 *    al_draw_spline curves from p1-p2, p2-p3, etc. Each al_draw_spline must pass though
 *    both of its guide points, so they must be used as the first and fourth
 *    of the al_draw_spline control points. The fun bit is coming up with sensible
 *    values for the second and third al_draw_spline control points, such that the
 *    al_draw_spline segments will have equal gradients where they meet. I came
 *    up with the following solution:
 *
 *    For each guide point p(n), calculate the desired tangent to the curve
 *    at that point. I took this to be the vector p(n-1) -> p(n+1), which 
 *    can easily be calculated with the inverse tangent function, and gives 
 *    decent looking results. One implication of this is that two dummy 
 *    guide points are needed at each end of the curve, which are used in 
 *    the tangent calculations but not connected to the set of splines.
 *
 *    Having got these tangents, it becomes fairly easy to calculate the
 *    al_draw_spline control points. For a al_draw_spline between guide points p(a) and
 *    p(b), the second control point should lie along the positive tangent
 *    from p(a), and the third control point should lie along the negative
 *    tangent from p(b). How far they are placed along these tangents 
 *    controls the shape of the curve: I found that applying a 'curviness'
 *    scaling factor to the distance between p(a) and p(b) works well.
 *
 *    One thing to note about splines is that the generated points are
 *    not all equidistant. Instead they tend to bunch up nearer to the
 *    ends of the al_draw_spline, which means you will need to apply some fudges
 *    to get an object to move at a constant speed. On the other hand,
 *    in situations where the curve has a noticable change of direction 
 *    at each guide point, the effect can be quite nice because it makes
 *    the object slow down for the curve.
 */


#include <stdio.h>

#include "allegro.h"



typedef struct NODE
{
   int x, y;
   fixed tangent;
} NODE;


#define MAX_NODES    1024

NODE nodes[MAX_NODES];

int node_count;

fixed curviness;

int show_tangents;
int show_control_points;



/* calculates the distance between two nodes */
fixed node_dist(NODE n1, NODE n2)
{
   #define SCALE  64

   fixed dx = al_int_to_fix(n1.x - n2.x) / SCALE;
   fixed dy = al_int_to_fix(n1.y - n2.y) / SCALE;

   return al_fix_sqrt(al_fix_mul(dx, dx) + al_fix_mul(dy, dy)) * SCALE;
}



/* constructs nodes to go at the ends of the list, for tangent calculations */
NODE dummy_node(NODE node, NODE prev)
{
   NODE n;

   n.x = node.x - (prev.x - node.x) / 8;
   n.y = node.y - (prev.y - node.y) / 8;

   return n;
}



/* calculates a set of node tangents */
void calc_tangents(void)
{
   int i;

   nodes[0] = dummy_node(nodes[1], nodes[2]);
   nodes[node_count] = dummy_node(nodes[node_count-1], nodes[node_count-2]);
   node_count++;

   for (i=1; i<node_count-1; i++)
      nodes[i].tangent = al_fix_atan2(al_int_to_fix(nodes[i+1].y - nodes[i-1].y),
				  al_int_to_fix(nodes[i+1].x - nodes[i-1].x));
}



/* draws one of the path nodes */
void draw_node(int n)
{
   char b[8];

   al_draw_circle_fill(al_screen, nodes[n].x, nodes[n].y, 2, al_palette_color[1]);

   sprintf(b, "%d", n);
   al_text_mode(-1);
   al_put_text(al_screen, al_font_8x8, b, nodes[n].x-7, nodes[n].y-7, al_palette_color[255]);
}



/* calculates the control points for a al_draw_spline segment */
void get_control_points(NODE n1, NODE n2, int points[8])
{
   fixed dist = al_fix_mul(node_dist(n1, n2), curviness);

   points[0] = n1.x;
   points[1] = n1.y;

   points[2] = n1.x + al_fix_to_int(al_fix_mul(al_fix_cos(n1.tangent), dist));
   points[3] = n1.y + al_fix_to_int(al_fix_mul(al_fix_sin(n1.tangent), dist));

   points[4] = n2.x - al_fix_to_int(al_fix_mul(al_fix_cos(n2.tangent), dist));
   points[5] = n2.y - al_fix_to_int(al_fix_mul(al_fix_sin(n2.tangent), dist));

   points[6] = n2.x;
   points[7] = n2.y;
}



/* draws a al_draw_spline curve connecting two nodes */
void draw_spline(NODE n1, NODE n2)
{
   int points[8];
   int i;

   get_control_points(n1, n2, points);
   al_draw_spline(al_screen, points, al_palette_color[255]);

   if (show_control_points)
      for (i=1; i<=2; i++)
	 al_draw_circle_fill(al_screen, points[i*2], points[i*2+1], 2, al_palette_color[2]);
}



/* draws the al_draw_spline paths */
void draw_splines(void)
{
   char b[80];
   int i;

   al_acquire_screen();

   al_clear_to_color(al_screen, al_make_color(255, 255, 255));

   al_text_mode(al_palette_color[0]);
   al_put_text_centre(al_screen, al_font_8x8, "Spline curve path", AL_SCREEN_W/2, 8, al_palette_color[255]);
   sprintf(b, "Curviness = %.2f", al_fix_to_(curviness));
   al_put_text_centre(al_screen, al_font_8x8, b, AL_SCREEN_W/2, 32, al_palette_color[255]);
   al_put_text_centre(al_screen, al_font_8x8, "Up/down keys to alter", AL_SCREEN_W/2, 44, al_palette_color[255]);
   al_put_text_centre(al_screen, al_font_8x8, "Space to walk", AL_SCREEN_W/2, 68, al_palette_color[255]);
   al_put_text_centre(al_screen, al_font_8x8, "C to display control points", AL_SCREEN_W/2, 92, al_palette_color[255]);
   al_put_text_centre(al_screen, al_font_8x8, "T to display tangents", AL_SCREEN_W/2, 104, al_palette_color[255]);

   for (i=1; i<node_count-2; i++)
      draw_spline(nodes[i], nodes[i+1]);

   for (i=1; i<node_count-1; i++) {
      draw_node(i);

      if (show_tangents) {
	 al_draw_line(al_screen, nodes[i].x - al_fix_to_int(al_fix_cos(nodes[i].tangent) * 24),
		      nodes[i].y - al_fix_to_int(al_fix_sin(nodes[i].tangent) * 24),
		      nodes[i].x + al_fix_to_int(al_fix_cos(nodes[i].tangent) * 24),
		      nodes[i].y + al_fix_to_int(al_fix_sin(nodes[i].tangent) * 24),
		      al_palette_color[1]);
      }
   }

   al_release_screen();
}



/* let the user input a list of path nodes */
void input_nodes(void)
{
   al_clear_to_color(al_screen, al_make_color(255, 255, 255));

   al_text_mode(al_palette_color[0]);
   al_put_text_centre(al_screen, al_font_8x8, "Click the left mouse button to add path nodes",
		  AL_SCREEN_W/2, 8, al_palette_color[255]);
   al_put_text_centre(al_screen, al_font_8x8, "Right mouse button or any al_key to finish",
		  AL_SCREEN_W/2, 24, al_palette_color[255]);

   node_count = 1;

   al_show_mouse(al_screen);

   do {
      al_poll_mouse();
   } while (al_mouse_b);

   al_clear_keybuf();

   for (;;) {
      al_poll_mouse();

      if (al_mouse_b & 1) {
	 if (node_count < MAX_NODES-1) {
	    nodes[node_count].x = al_mouse_x;
	    nodes[node_count].y = al_mouse_y;

	    al_show_mouse(NULL);
	    draw_node(node_count);
	    al_show_mouse(al_screen);

	    node_count++;
	 }

	 do {
	    al_poll_mouse();
	 } while (al_mouse_b & 1);
      }

      if ((al_mouse_b & 2) || (al_key_pressed())) {
	 if (node_count < 3)
	    al_show_alert("You must enter at least two nodes", NULL, NULL, "OK", NULL, 13, 0);
	 else
	    break;
      }
   }

   al_show_mouse(NULL);

   do {
      al_poll_mouse();
   } while (al_mouse_b);

   al_clear_keybuf();
}



/* moves a sprite along the al_draw_spline path */
void walk(void)
{
   #define MAX_POINTS    256

   int points[8];
   int x[MAX_POINTS], y[MAX_POINTS];
   int n, i;
   int npoints;
   int ox, oy;

   al_acquire_screen();

   al_clear_to_color(al_screen, al_make_color(255, 255, 255));

   for (i=1; i<node_count-1; i++)
      draw_node(i);

   al_release_screen();

   do {
      al_poll_mouse();
   } while (al_mouse_b);

   al_clear_keybuf();

   ox = -16;
   oy = -16;

   al_drawing_mode(DRAW_MODE_XOR, NULL, 0, 0);

   for (n=1; n < node_count-2; n++) {
      npoints = (al_fix_to_int(node_dist(nodes[n], nodes[n+1]))+3) / 4;
      if (npoints < 1)
	 npoints = 1;
      else if (npoints > MAX_POINTS)
	 npoints = MAX_POINTS;

      get_control_points(nodes[n], nodes[n+1], points);
      al_calc_spline(points, npoints, x, y);

      for (i=1; i<npoints; i++) {
	 al_vsync();
	 al_acquire_screen();
	 al_draw_circle_fill(al_screen, ox, oy, 6, al_palette_color[2]);
	 al_draw_circle_fill(al_screen, x[i], y[i], 6, al_palette_color[2]);
	 al_release_screen();
	 ox = x[i];
	 oy = y[i];

	 al_poll_mouse();

	 if ((al_key_pressed()) || (al_mouse_b))
	    goto getout;
      }
   }

   getout:

   al_drawing_mode(DRAW_MODE_SOLID, NULL, 0, 0);

   do {
      al_poll_mouse();
   } while (al_mouse_b);

   al_clear_keybuf();
}



/* main program */
int main(void)
{
   int c;

   allegro_init();
   al_install_keyboard();
   al_install_mouse();
   al_install_timer();
   if (al_set_gfx_mode(AL_GFX_SAFE, 640, 480, 0, 0) != 0) {
      al_set_gfx_mode(AL_GFX_NONE, 0, 0, 0, 0);
      al_show_message("Unable to set any graphic mode\n%s\n", al_error);
      return 1;
   }
   al_set_palette(al_desktop_palette);

   input_nodes();
   calc_tangents();

   curviness = al_float_to_fix(0.25);
   show_tangents = FALSE;
   show_control_points = FALSE;

   draw_splines();

   for (;;) {
      if (al_key_pressed()) {
	 c = al_read_key() >> 8;
	 if (c == AL_KEY_ESC)
	    break;
	 else if (c == AL_KEY_UP) {
	    curviness += al_float_to_fix(0.05);
	    draw_splines();
	 }
	 else if (c == AL_KEY_DOWN) {
	    curviness -= al_float_to_fix(0.05);
	    draw_splines();
	 }
	 else if (c == AL_KEY_SPACE) {
	    walk();
	    draw_splines();
	 }
	 else if (c == AL_KEY_T) {
	    show_tangents = !show_tangents;
	    draw_splines();
	 }
	 else if (c == AL_KEY_C) {
	    show_control_points = !show_control_points;
	    draw_splines();
	 }
      }
   }

   return 0;
}

AL_END_OF_MAIN();




See more files for this project here

Allegro game programming library

Allegro is a cross-platform library intended for use in computer games and other types of multimedia programming.

Project homepage: http://sourceforge.net/projects/alleg
Programming language(s): Assembly,C,Shell Script
License: other

  allegro.pcx
  ex12bit.c
  ex3buf.c
  ex3d.c
  exaccel.c
  exalpha.c
  example.dat
  example.h
  examples.txt
  exbitmap.c
  exblend.c
  excamera.c
  excolmap.c
  excustom.c
  exdata.c
  exdbuf.c
  exdodgy.c
  exexedat.c
  exfixed.c
  exflame.c
  exflip.c
  exgui.c
  exhello.c
  exjoy.c
  exkeys.c
  exlights.c
  exmem.c
  exmidi.c
  exmouse.c
  expal.c
  expat.c
  exquat.c
  exrgbhsv.c
  exsample.c
  exscale.c
  exscn3d.c
  exscroll.c
  exshade.c
  exspline.c
  exsprite.c
  exstars.c
  exstream.c
  exswitch.c
  extimer.c
  extrans.c
  extruec.c
  exunicod.c
  exupdate.c
  exxfade.c
  exzbuf.c
  mysha.pcx
  planet.pcx
  running.dat
  running.h
  unifont.dat