/* Popeye Plugin * Copyright (C) 1999 Rick Haines * * 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 */ #include #include #include "config.h" #include #include #include #include #include #include #include "fishmatic_plugin.h" #include "fishmatic.h" // Include the image files... #include "back.c" #include "bubble.c" #include "lamprologus.c" #include "angel_altum.c" extern VisPlugin fishmatic_vplugin; extern GLfloat heights[16]; extern pthread_mutex_t height_mutex; extern gboolean paused; #define C(x) ((float)(x) / 128.0) // GLTV: gl Texture-coord + gl Vertex #define GLTV(_x,_y,_z) glTexCoord2f(C(_x), C(_y)); glVertex3f(C(_x), C(_y), C(_z) * mirror); App_Data_t *init_gl(void) { int i; float mirror; App_Data_t *app_data; app_data = g_malloc(sizeof (App_Data_t)); if (!app_data) { return(app_data); } app_data->frame = 0; app_data->bubble_burst = 0; glViewport(0, 0, 640, 480); glMatrixMode(GL_PROJECTION); glLoadIdentity(); // gluPerspective(60.0, (GLfloat) 2, 1.0, 30.0); glRotatef(180, 0,1,0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // glTranslatef(0.0, 0.0, -3.6); glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); // Bind the various textures // The backdrop texture glGenTextures(1, (GLuint*)(&app_data->texture[TEXTURE_BACK])); glBindTexture(GL_TEXTURE_2D, app_data->texture[TEXTURE_BACK]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, backdrop.bytes_per_pixel, backdrop.width, backdrop.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, backdrop.pixel_data); // The bubble texture glGenTextures(1, (GLuint*)(&app_data->texture[TEXTURE_BUBBLE])); glBindTexture(GL_TEXTURE_2D, app_data->texture[TEXTURE_BUBBLE]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, bubble.bytes_per_pixel, bubble.width, bubble.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bubble.pixel_data); // Lamprologus fishy glGenTextures(1, (GLuint*)(&app_data->texture[TEXTURE_LAMPROLOGUS])); glBindTexture(GL_TEXTURE_2D, app_data->texture[TEXTURE_LAMPROLOGUS]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, lamprologus.bytes_per_pixel, lamprologus.width, lamprologus.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, lamprologus.pixel_data); // Angel fishee! glGenTextures(1, (GLuint*)(&app_data->texture[TEXTURE_ANGELALTUM])); glBindTexture(GL_TEXTURE_2D, app_data->texture[TEXTURE_ANGELALTUM]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, angel_altum.bytes_per_pixel, angel_altum.width, angel_altum.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, angel_altum.pixel_data); glClearColor (0.0, 0.0, 0.0, 0.0); glShadeModel(GL_FLAT); for(i=0;ibubble[i].y = 1.1; } // Generate the lamprologus display list app_data->lamprologus_list = glGenLists (1); glNewList (app_data->lamprologus_list, GL_COMPILE); glPushMatrix(); glScalef(0.3, -0.3, 0.3); glTranslatef(-42.0/128, -38.0/128, 0); mirror = -1; for(i=0;i<2;i++) { mirror = mirror * -1; glBegin(GL_TRIANGLE_STRIP); GLTV(15, 24, 0); GLTV(0, 39, 0); GLTV(16, 36, 6); GLTV(0, 48, 0); GLTV(16, 48, 6); GLTV(16, 58, 0); glEnd(); glBegin(GL_TRIANGLE_STRIP); GLTV(15, 24, 0); GLTV(33, 13, 0); GLTV(16, 36, 6); GLTV(34, 28, 9); GLTV(16, 48, 6); GLTV(35, 51, 9); GLTV(16, 58, 0); GLTV(35, 62, 0); glEnd(); glBegin(GL_TRIANGLE_STRIP); GLTV(57, 14, 0); GLTV(33, 13, 0); GLTV(58, 27, 9); GLTV(34, 28, 9); GLTV(58, 50, 9); GLTV(35, 51, 9); GLTV(58, 60, 0); GLTV(35, 62, 0); glEnd(); glBegin(GL_TRIANGLE_STRIP); GLTV(57, 14, 0); GLTV(79, 21, 0); GLTV(58, 27, 9); GLTV(79, 29, 8); GLTV(58, 50, 9); GLTV(78, 49, 8); GLTV(58, 60, 0); GLTV(78, 57, 0); glEnd(); glBegin(GL_TRIANGLE_STRIP); GLTV(79, 21, 0); GLTV(90, 32, 0); GLTV(79, 29, 8); GLTV(90, 49, 0); GLTV(78, 49, 8); GLTV(78, 57, 0); glEnd(); } // Do the flat tails // Bottom two fins glBegin(GL_TRIANGLE_STRIP); GLTV(58, 60, 0); GLTV(29, 63, 0); GLTV(66, 72, 0); GLTV(33, 89, 0); glEnd(); // Bottom rear fin glBegin(GL_TRIANGLE_STRIP); GLTV(66, 60, 0); GLTV(72, 69, 0); GLTV(93, 51, 0); GLTV(114, 70, 0); glEnd(); // Tail fin glBegin(GL_TRIANGLE_STRIP); GLTV(90, 32, 0); GLTV(120, 16, 0); GLTV(90, 49, 0); GLTV(127, 69, 0); glEnd(); // Top (Dorsal?) fin glBegin(GL_TRIANGLE_STRIP); GLTV(32, 0, 0); GLTV(30, 15, 0); GLTV(72, 0, 0); GLTV(69, 18, 0); GLTV(94, 10, 0); GLTV(91, 31, 0); glEnd(); glPopMatrix(); glEndList(); // Build the Angel Altum list app_data->angel_altum_list = glGenLists (1); glNewList (app_data->angel_altum_list, GL_COMPILE); glPushMatrix(); glScalef(0.4, -0.4, 0.4); glTranslatef(-25.0/128, -60.0/128, 0); mirror = -1; for(i=0;i<2;i++) { mirror = mirror * -1; glBegin(GL_TRIANGLE_STRIP); GLTV( 4, 53, 0); GLTV( 0, 56, 0); GLTV( 4, 59, 2); GLTV( 0, 61, 0); GLTV( 4, 66, 0); glEnd(); glBegin(GL_TRIANGLE_STRIP); GLTV(11, 48, 0); GLTV( 4, 53, 0); GLTV(11, 55, 4); GLTV( 4, 59, 2); GLTV(11, 62, 4); GLTV( 4, 66, 0); GLTV(11, 74, 0); glEnd(); glBegin(GL_TRIANGLE_STRIP); GLTV(18, 40, 0); GLTV(11, 48, 0); GLTV(19, 53, 6); GLTV(11, 55, 4); GLTV(19, 69, 6); GLTV(11, 62, 4); GLTV(16, 80, 0); GLTV(11, 74, 0); glEnd(); glBegin(GL_TRIANGLE_STRIP); GLTV(18, 40, 0); GLTV(26, 32, 0); GLTV(19, 53, 6); GLTV(29, 50, 4); GLTV(19, 69, 6); GLTV(29, 67, 4); GLTV(16, 80, 0); GLTV(27, 80, 0); glEnd(); glBegin(GL_TRIANGLE_STRIP); GLTV(38, 27, 0); GLTV(26, 32, 0); GLTV(43, 52, 5); GLTV(29, 50, 4); GLTV(44, 78, 0); GLTV(29, 67, 4); GLTV(37, 92, 0); GLTV(27, 80, 0); glEnd(); glBegin(GL_TRIANGLE_STRIP); GLTV(38, 27, 0); GLTV(52, 48, 0); GLTV(43, 52, 5); GLTV(51, 61, 0); GLTV(44, 78, 0); glEnd(); } // The rest are all flat, so only need one copy. // Tail glBegin(GL_TRIANGLE_STRIP); GLTV(59, 47, 0); GLTV(52, 48, 0); GLTV(59, 60, 0); GLTV(51, 61, 0); glEnd(); glBegin(GL_TRIANGLE_STRIP); GLTV(59, 47, 0); GLTV(79, 30, 0); GLTV(59, 60, 0); GLTV(80, 75, 0); glEnd(); // Top fin glBegin(GL_TRIANGLE_STRIP); GLTV(26, 32, 0); GLTV(50, 0, 0); GLTV(38, 27, 0); GLTV(59, 0, 0); GLTV(52, 48, 0); glEnd(); // bottom-rear fin glBegin(GL_TRIANGLE_STRIP); GLTV(72,133, 0); GLTV(50,115, 0); GLTV(52, 99, 0); GLTV(37, 92, 0); GLTV(54, 78, 0); GLTV(44, 78, 0); GLTV(51, 61, 0); glEnd(); // bottom danglies glBegin(GL_TRIANGLE_STRIP); GLTV(16, 80, 0); GLTV(25,127, 0); GLTV(27, 80, 0); GLTV(44,127, 0); glEnd(); glPopMatrix(); glEndList(); // Initialize the fishes! for(i=0;ifishes[i]), app_data->lamprologus_list); } else { Init_Fish(&(app_data->fishes[i]), app_data->angel_altum_list); } } return(app_data); } void Bubbles(App_Data_t *app_data) { int i,j; Bubble_t *bubble; float f; float y; float w; pthread_mutex_lock(&height_mutex); app_data->bubble_burst += heights[0] + 0.1; if (app_data->bubble_burst > 10) { app_data->bubble_burst = 0; // Add a new bubble for(i=0;ibubble[i].y > (1.0 + BUBBLE_WIDTH)) { // Set up a new bubble app_data->bubble[i].y = -1.0 - BUBBLE_WIDTH; app_data->bubble[i].x = 0; app_data->bubble[i].size = 0.01; app_data->bubble[i].size += heights[0] / 100.0; app_data->bubble[i].sep = Stereo_Sep(app_data, 0); break; } } } pthread_mutex_unlock(&height_mutex); // Move and draw all the other bubbles glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBindTexture(GL_TEXTURE_2D, app_data->texture[TEXTURE_BUBBLE]); glBegin(GL_QUADS); bubble = &app_data->bubble[0]; j = 0; for(i=0;iy < (1.0 + BUBBLE_WIDTH)) { bubble->x += (heights[j] - heights[j+1]) / 300; j = (j+1) % 15; bubble->y += BUBBLE_SPEED; f = bubble->x - 1.0; y = bubble->y; w = bubble->size; while(f < 1.0) { glTexCoord2f(0.0, 0.0); glVertex3f(f-w, y + w, 0); glTexCoord2f(0.0, 1.0); glVertex3f(f-w, y - w, 0); glTexCoord2f(1.0, 1.0); glVertex3f(f+w, y - w, 0); glTexCoord2f(1.0, 0.0); glVertex3f(f+w, y + w, 0); f += bubble->sep; } } bubble++; } glEnd(); glDisable(GL_BLEND); } void Fish(App_Data_t *app_data) { Fish_t *fish; int i, j; float theta; float omega; float sep; glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); fish = app_data->fishes; for(i=0;icall_list == app_data->lamprologus_list) { glBindTexture(GL_TEXTURE_2D, app_data->texture[TEXTURE_LAMPROLOGUS]); } else { glBindTexture(GL_TEXTURE_2D, app_data->texture[TEXTURE_ANGELALTUM]); } theta = atan2(-fish->dz, fish->dx); omega = fish->dx * fish->dx + fish->dz * fish->dz; omega = sqrt(omega); omega = atan2(fish->dy, omega); sep = Stereo_Sep(app_data, fish->z); j = -2 - fishmatic_cfg.stereo_repeat / 2; while(jx, -fish->y, -fish->z); glRotatef(10 * j + theta * 180/3.14159264, 0, 1, 0); glRotatef(omega * 10, 0,0,1); glCallList(fish->call_list); glPopMatrix(); j++; } fish->t += 0.001; if (fish->t > 1.0) { Advance_Fish(fish); } fish++; } glDisable(GL_BLEND); }; void draw_gl(App_Data_t *app_data) { glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); // Draw the background image glBindTexture(GL_TEXTURE_2D, app_data->texture[TEXTURE_BACK]); glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-1, -1, 0.9); glTexCoord2f(0.0, 4.0); glVertex3f(-1, 1.0, 0.9); glTexCoord2f(fishmatic_cfg.stereo_repeat - 1, 4.0); glVertex3f(1.0, 1.0, 0.9); glTexCoord2f(fishmatic_cfg.stereo_repeat - 1, 0.0); glVertex3f(1.0, -1, 0.9); glEnd(); glEnable(GL_DEPTH_TEST); Bubbles(app_data); Fish(app_data); // Now start drawing objects, sorted by depth. glFlush(); app_data->frame++; } void kill_gl(App_Data_t *app_data) { g_free(app_data); } // This will set up a fish structure. void Init_Fish(Fish_t *f, GLuint call_list) { f->call_list = call_list; f->x = drand48()-0.5; f->y = drand48()-0.5; f->z = drand48()-0.5; Advance_Fish(f); Advance_Fish(f); f->t = 0; }; // This will change the fish's two velocity vectors void Advance_Fish(Fish_t *f) { f->dx1 = f->dx2; f->dy1 = f->dy2; f->dz1 = f->dz2; f->dx2 = (drand48() - 0.5)/1000; f->dy2 = (drand48() - 0.5)/1000; f->dz2 = (drand48() - 0.5)/1000; f->t = f->t - 1.0; } #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) // Function to calculate how far apart to draw stereo copies, // based upon their Z value float Stereo_Sep(App_Data_t *app_data, float z) { z = 2 / (fishmatic_cfg.stereo_repeat - 0.5 - z); return(z); } // Reposition a fish, based on time t, and the two velocity vectors void Evaluate_Fish(App_Data_t *app_data, Fish_t *f) { float sep; float speed; speed = fishmatic_cfg.fish_speed; f->dx = speed * (f->dx2 * f->t + f->dx1 * (1-f->t)); f->dy = speed * (f->dy2 * f->t + f->dy1 * (1-f->t)); f->dz = speed * (f->dz2 * f->t + f->dz1 * (1-f->t)); f->y = max(-0.9, min(0.9, f->y + f->dy)); f->z = max(-0.8, min(0.9, f->z + f->dz)); f->x += f->dx; sep = Stereo_Sep(app_data, f->z); // There are up to six fishes across if (f->x > sep) { f->x -= sep; } if (f->x < -sep) { f->x += sep; } }